☰
Django接入LLM实现自然语言商品搜索:从ORM到向量检索
2026/9/29 14:51:20 网站建设 项目流程

如果要给 Django 后端找一个最适合接大模型的场景,我的答案不是智能客服,也不是代码生成,而是那个看起来最普通的“产品目录”——catalogue produit。在电商、ERP、供应链、自营商城里面,Product 表往往是查询需求最复杂、用户抱怨最多、数据质量最难维护的地方。过去做商品搜索,基本靠 SQL LIKE 和一组多层筛选器:用户输入“5000 以内的 5G 手机”,系统能识别价格区间,却未必能把“5G”落到正确的字段上;用户输入“适合送女生的香水”,关键词检索基本无能为力。

把 LLM 接进 Django 之后,这个场景会发生一个实质变化:LLM 不再是一个“替你写 SQL 的魔法黑盒”,而是作为语言解析层,把自然语言转成受约束的结构化指令,Django ORM 继续负责权限、过滤、分页和审计。这篇文章我不会讲那种大而全的企业级 AI 中台方案,而是给你一条最小可复用的接入链路:从 Product 建模开始,到封装一个 OpenAI 兼容的 LLM 客户端,再到自然语言转 ORM 查询,最后加上一个可选的向量语义检索增强方案。读完之后,你可以直接在自己的 Django 项目里复刻这条链路,也能知道生产环境真正容易踩坑的地方在哪里。

1. 这篇文章真正要解决的问题

先讲一个具体场景。你负责维护一个面向法务、采购或销售团队的商品目录系统,里面可能有几万条产品记录,字段包括名称、SKU、品类、价格、库存、描述和自定义属性。传统搜索框的局限很明显:

  • 用户需要掌握系统的筛选条件,否则只能全表 LIKE。
  • 同义词和口语表达无法统一,比如“手机”和“智能电话”在数据库里可能是两个分类。
  • 价格区间、尺码、颜色这类属性写进描述里,常规查询无法拆出来。
  • 非技术用户不会写“价格小于 5000 且库存大于 0”这类条件。

这些痛点在内部 ERP、B2B 电商和跨境电商项目里尤其突出。因为商品数据一旦多起来,普通的“分词 + LIKE + 筛选项”就很难兼顾召回率和精确度。于是很多团队开始考虑:能不能让用户直接问一句话,系统自动把这句话拆成查询条件?

把 LLM 接入 Django,本质上就是要回答这个问题。但我必须先给一个判断:不要指望 LLM 直接生成 SQL 然后执行,也不要把整个搜索逻辑交给大模型自由发挥。更稳妥、也更符合 Django 工程习惯的做法是,让 LLM 输出一个非常有限的结构化 JSON,这个 JSON 只描述字段、操作符和值,然后由后端代码把它翻译成 QuerySet。这样,LLM 负责“听懂人话”,Django 和数据库负责“安全执行”。

读完这篇文章,你会得到以下实际收益:

  • 知道在 Django 里接入 LLM 的三种主流模式,以及各自适合什么场景。
  • 能实现一个“自然语言 → 结构化条件 → ORM 查询 → JSON 响应”的最小可用链路。
  • 能为商品目录补上向量语义检索,解决关键词不匹配的问题。
  • 知道生产环境里如何控制成本、权限、输出格式和安全边界。

2. 基础概念与核心原理

2.1 目录查询的本质:从精确匹配到意图理解

传统产品目录查询,本质上是“条件过滤”。用户选择品类、价格区间、品牌,系统把这些条件映射成数据库 SQL。这种方式的优点是确定性强,缺点是用户必须先理解系统的分类法。

例如“有没有 5000 以内支持 5G 的手机”,在传统查询里,我们需要知道“手机”属于哪个品类字段,“5000 以内”对应 price__lte=5000,“支持 5G”要么是属性字段,要么只能靠描述 LIKE。如果目录数据很乱,字段没有标准化,这个需求就很难实现。

LLM 的意义在于,它可以把自然语言映射成一套标准化的过滤条件。你可以为 LLM 定义一个能力边界:它只负责输出一组filters,每个 filter 包含field、operator、value,不直接碰数据库。这就等于把“非结构化输入”和“结构化查询”之间加了一层可靠的翻译。

2.2 在 Django 中接入 LLM 的三种模式

模式输入输出适用场景实现复杂度
自然语言转结构化查询用户问题字段条件 JSON筛选项固定的商品列表页、后台快速筛选低
向量语义检索 RAG用户问题语义相似的商品列表关键词不匹配、描述模糊、跨语言检索中
内容生成与属性补全商品描述/图片信息商品卖点、属性归类、标题优化商品上架、运营文案、后台管理低到中

这三种模式并不互斥。实际项目里,自然语言转结构化查询适合“用户想按价格、品类、库存筛选”的场景;向量语义检索适合“用户描述的是需求而不是字段”的场景;内容生成与属性补全则更像是后台工具,和用户搜索无关。

2.3 LLM 和 Django 的分工边界

接 LLM 最忌惮的是把模型输出当作可信代码直接执行。这里要建立一个观点:LLM 只能做语言理解,Django 必须做动作控制。

  • LLM 负责:将用户问题解析成有限的 JSON 协议。
  • Django 负责:校验字段白名单、操作符白名单、类型转换,再执行 ORM 查询。
  • 数据库负责:真实过滤、排序、分页。
  • 审计日志负责:记录用户问题、LLM 原始输出、最终查询条件。

这个分层看起来繁琐,但能解决大模型的三个核心问题:输出不稳定、格式幻觉、越权操作。你不需要信任模型的“自觉”,只需要信任自己的校验逻辑。

3. 环境准备与前置条件

3.1 运行环境

本文的示例基于以下环境(版本请以你实际项目为准):

  • Python 3.10+
  • Django 4.2 或更高版本
  • 一个 LLM 服务,优先使用 OpenAI 兼容 API,例如本地 Ollama 的/v1接口,或任意提供兼容接口的云服务
  • 可选:numpy,用于向量相似度计算
  • 可选:requests,用于调用 HTTP 接口

Django 项目推荐使用虚拟环境:

python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install django requests numpy

创建项目和应用:

django-admin startproject config . python manage.py startapp catalog

然后在config/settings.py的INSTALLED_APPS中加入:

INSTALLED_APPS = [ # ... "catalog", ]

3.2 LLM 服务的选择

有两种主流选择,建议都考虑:

  1. 本地 LLM:用 Ollama 或 vLLM 部署开源模型。优点是数据不出内网,适合本地 ERP、商品数据敏感的电商系统;缺点是硬件成本和模型能力需要自己评估。
  2. 云 API:使用 OpenAI 兼容接口。优点是接入快、模型理解能力强;缺点是商品数据要发到外部服务,需要做脱敏和合规评估。

无论选哪种,只要支持/v1/chat/completions和/v1/embeddings,后面的代码都可以直接复用。我把这些配置放到环境变量里,不写死厂商:

export LLM_API_KEY="EMPTY" export LLM_BASE_URL="http://localhost:11434/v1" export LLM_MODEL="qwen2.5" export LLM_EMBED_MODEL="bge-m3"

这个“OpenAI 兼容”思路是关键。它让你的 Django 服务不绑定任何具体模型,后面换模型只是改环境变量,而不是改代码。

4. 核心流程拆解

这条链路的完整流程是:用户输入问题 → Django 接收请求 → 调用 LLM 客户端 → 得到原始回复 → 解析 JSON → 字段与操作符白名单校验 → 构建 QuerySet → 返回结果。下面拆成五步。

4.1 第一步:定义产品目录模型

先建一个足够典型的 Product 模型。为了兼顾普通关系和灵活属性,我建议在基础字段之外加一个attributesJSON 字段,用来存品牌、颜色、容量等不固定属性。同时可以把商品 embedding 存成一个辅助字段,便于演示向量检索。

# catalog/models.py from django.db import models class Product(models.Model): name = models.CharField(max_length=255) sku = models.CharField(max_length=64, unique=True) category = models.CharField(max_length=128, db_index=True) price = models.DecimalField(max_digits=10, decimal_places=2) stock = models.IntegerField(default=0) description = models.TextField(blank=True) attributes = models.JSONField(default=dict, blank=True) embedding = models.JSONField(null=True, blank=True, editable=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: indexes = [ models.Index(fields=["category", "price"]), ] def __str__(self): return f"{self.sku} - {self.name}"

这里把embedding放在 Product 表里,只是为了演示。生产环境更推荐用 PostgreSQL 的 pgvector 字段或单独的向量库,避免在大表里做 JSON 读取。

4.2 第二步:封装 LLM 客户端

不要在每个视图里直接requests.post,而是封装成服务类。好处是统一超时时间、错误处理、日志记录,以后换 SDK 也只改一个文件。

# catalog/services/llm_client.py import os import requests class OpenAICompatibleClient: def __init__(self): self.api_key = os.getenv("LLM_API_KEY", "EMPTY") self.base_url = os.getenv("LLM_BASE_URL", "http://localhost:11434/v1") self.model = os.getenv("LLM_MODEL", "qwen2.5") def chat(self, messages, temperature=0.0, timeout=30): url = f"{self.base_url}/chat/completions" payload = { "model": self.model, "messages": messages, "temperature": temperature, } resp = requests.post( url, headers={"Authorization": f"Bearer {self.api_key}"}, json=payload, timeout=timeout, ) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"]

温度设置为 0,是刻意为之:我们希望结构化查询的输出尽量稳定,不需要模型发挥创造力。

4.3 第三步:设计结构化输出协议

LLM 的返回结果必须足够窄,窄到后端可以安全校验。下面定义协议:

{ "text_query": "5G", "filters": [ {"field": "price", "operator": "lte", "value": 5000}, {"field": "category", "operator": "eq", "value": "手机"} ], "order_by": "price" }

协议中只允许出现白名单字段:name、sku、category、price、stock、description。操作符只允许eq、lt、lte、gt、gte、contains。order_by也只能从白名单里选。

把这个协议放进 system prompt,可以让 LLM 在绝大多数情况下输出合法 JSON:

# catalog/services/prompt_builder.py SYSTEM_PROMPT = """ 你是产品目录查询引擎的语言理解层。 输入是用户自然语言问题,输出必须是一个 JSON,不要输出任何其他内容。 JSON 结构: { "text_query": "用于关键词兜底检索的字符串,可为空", "filters": [ {"field": "category", "operator": "eq", "value": "手机"} ], "order_by": "price 或 null" } 可选字段仅限:name, sku, category, price, stock, description。 可选 operator 仅限:eq, lt, lte, gt, gte, contains。 价格类比较请输出 float 或 int,不要输出货币符号。 如果用户没有提到价格、品类等硬条件,filters 可以为空数组。 """

这里“text_query”是给关键词兜底用的。因为像“5G”这种属性不一定在数据库里有独立字段,LLM 先把它拎出来,后端再用icontains在名称和描述里做一次检索。

4.4 第四步:把 LLM 输出安全翻译成 ORM 查询

这是整条链路的核心环节。解析 JSON 之后,不要直接执行任何 SQL 字符串,而是遍历 filters,逐个构造 DjangoQ对象。

# catalog/services/query_translator.py import json from django.db.models import Q from .llm_client import OpenAICompatibleClient from .prompt_builder import SYSTEM_PROMPT SAFE_FIELDS = {"name", "sku", "category", "price", "stock", "description"} SAFE_OPERATORS = {"eq", "lt", "lte", "gt", "gte", "contains"} def parse_llm_response(raw_content: str) -> dict: text = raw_content.strip() if text.startswith("```"): text = text.strip("`") if text.startswith("json"): text = text[4:] return json.loads(text) def translate_to_query(question: str): client = OpenAICompatibleClient() raw = client.chat( [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": question}, ] ) parsed = parse_llm_response(raw) filters = parsed.get("filters", []) query = Q() for cond in filters: field = cond.get("field") op = cond.get("operator") value = cond.get("value") if field not in SAFE_FIELDS: continue if op not in SAFE_OPERATORS: continue if op == "eq": query &= Q(**{field: value}) elif op == "contains": query &= Q(**{f"{field}__icontains": value}) elif op in {"lt", "lte", "gt", "gte"}: query &= Q(**{f"{field}__{op}": value}) text_query = parsed.get("text_query", "") if text_query: keyword_query = Q(name__icontains=text_query) | Q(description__icontains=text_query) query &= keyword_query order_by = parsed.get("order_by") if order_by not in SAFE_FIELDS: order_by = None return query, order_by

这段代码有几个关键设计:

  • 不存在的字段直接跳过,而不是报错。
  • 不存在的操作符直接跳过,而不是拼进 ORM。
  • 不使用extra(),不拼接原生 SQL。
  • text_query只在name和description上做包含检索,避免全字段 LIKE。

如果 LLM 输出完全乱掉,视图层还要做异常兜底。

4.5 第五步:向量语义检索增强

自然语言转 ORM 擅长处理“有限字段的硬条件”,但处理不了“推荐”“类似”“适合”这类软描述。这时候需要向量检索:把商品名称、描述、品类、属性拼成一段文本,用 embedding 模型转成向量;用户提问时也转成向量,然后计算余弦相似度。

这里给一个简化实现,把向量直接存在 Product.embedding JSON 字段里。

# catalog/services/embeddings.py import os import numpy as np import requests def get_embedding(text: str): url = f"{os.getenv('LLM_BASE_URL', 'http://localhost:11434/v1')}/embeddings" resp = requests.post( url, json={"model": os.getenv("LLM_EMBED_MODEL", "bge-m3"), "input": text}, timeout=30, ) resp.raise_for_status() return resp.json()["data"][0]["embedding"] def cosine_similarity(a, b): a = np.array(a) b = np.array(b) return float((a @ b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9))
# catalog/services/vector_search.py from ..models import Product from .embeddings import cosine_similarity, get_embedding def vector_search(question: str, top_k=5): question_embedding = get_embedding(question) candidates = [] for product in Product.objects.exclude(embedding__isnull=True)[:500]: score = cosine_similarity(question_embedding, product.embedding) candidates.append((score, product)) candidates.sort(key=lambda x: x[0], reverse=True) return [(product, score) for score, product in candidates[:top_k]]

生产环境不要用这个全表循环方案,数据量几百条可以,几千条以上建议使用 pgvector 索引或独立的向量数据库。这个简化版本的价值是让你理解 RAG 在产品目录里的落地逻辑:数据先索引,问题再检索,检索结果交回给业务层。

5. 完整示例与代码实现

接下来给出一个可以直接跑的 Django 示例。项目结构如下:

catalogue_llm/ ├── manage.py ├── config/ │ ├── settings.py │ └── urls.py └── catalog/ ├── models.py ├── views.py └── services/ ├── __init__.py ├── llm_client.py ├── prompt_builder.py ├── query_translator.py ├── embeddings.py └── vector_search.py

5.1 创建商品数据的命令

为了方便测试,先写一个 management command,插入少量示例商品。

# catalog/management/__init__.py # 空文件
# catalog/management/commands/seed_products.py from decimal import Decimal from django.core.management.base import BaseCommand from catalog.models import Product class Command(BaseCommand): help = "写入测试商品数据" def handle(self, *args, **options): products = [ Product( name="智能手机 X1", sku="PHONE-X1", category="手机", price=Decimal("4999.00"), stock=20, description="支持 5G 网络,4800 万像素,适合拍照。", attributes={"brand": "示例", "color": "黑色", "storage": "256GB"}, ), Product( name="入门手机 A2", sku="PHONE-A2", category="手机", price=Decimal("1999.00"), stock=50, description="4G 全网通,大电池,长续航。", attributes={"brand": "示例", "color": "蓝色", "storage": "128GB"}, ), Product( name="轻薄笔记本 L1", sku="LAPTOP-L1", category="电脑", price=Decimal("6500.00"), stock=8, description="14 英寸屏幕,16GB 内存,适合办公。", attributes={"brand": "示例", "color": "银色", "storage": "512GB"}, ), ] Product.objects.bulk_create(products, ignore_conflicts=True) self.stdout.write(self.style.SUCCESS("seed done"))

执行命令:

python manage.py seed_products

5.2 视图与接口

再写两个视图,一个走自然语言转 ORM,一个走向量检索。

# catalog/views.py import json from django.http import JsonResponse from django.views.decorators.http import require_POST from .models import Product from .services.query_translator import translate_to_query from .services.vector_search import vector_search @require_POST def search_by_llm(request): try: payload = json.loads(request.body) question = payload.get("question", "").strip() except json.JSONDecodeError: return JsonResponse({"code": 400, "message": "invalid json"}, status=400) if not question: return JsonResponse({"code": 400, "message": "question is required"}, status=400) try: query, order_by = translate_to_query(question) qs = Product.objects.filter(query) if order_by: qs = qs.order_by(order_by) products = qs[:10] return JsonResponse({ "code": 200, "fallback": False, "data": list(products.values("sku", "name", "price", "category")), }) except Exception: products = Product.objects.filter(name__icontains=question)[:10] return JsonResponse({ "code": 200, "fallback": True, "data": list(products.values("sku", "name", "price", "category")), }) @require_POST def semantic_search_view(request): try: payload = json.loads(request.body) question = payload.get("question", "").strip() except json.JSONDecodeError: return JsonResponse({"code": 400, "message": "invalid json"}, status=400) results = vector_search(question) data = [ {"sku": p.sku, "name": p.name, "score": round(score, 4), "price": p.price} for p, score in results ] return JsonResponse({"code": 200, "data": data})
# config/urls.py from django.contrib import admin from django.urls import path from catalog.views import search_by_llm, semantic_search_view urlpatterns = [ path("admin/", admin.site.urls), path("api/search/", search_by_llm), path("api/semantic-search/", semantic_search_view), ]

这里为了演示简洁,没有写认证。生产环境必须把 CSRF 校验和用户鉴权补回来,至少在视图中使用 Django REST Framework 的权限类或自研 token 校验。

5.3 构建商品向量的命令

# catalog/management/commands/build_embeddings.py from django.core.management.base import BaseCommand from catalog.models import Product from catalog.services.embeddings import get_embedding class Command(BaseCommand): help = "为商品名称、描述和属性生成向量" def handle(self, *args, **options): qs = Product.objects.filter(embedding__isnull=True)[:1000] for product in qs: source_text = " ".join([ product.name, product.description, product.category, str(product.attributes), ]) embedding = get_embedding(source_text) product.embedding = embedding product.save(update_fields=["embedding"]) self.stdout.write(f"embedded {product.sku}") self.stdout.write(self.style.SUCCESS("build embeddings done"))

注意,如果模型返回的向量维度很大,存进数据库 JSON 字段只是一种演示方式。生产环境优先选择 pgvector 字段类型和真正的向量索引。

6. 运行结果与效果验证

启动 Django 服务:

python manage.py runserver 8000

先用curl测试自然语言转 ORM 查询:

curl -X POST http://127.0.0.1:8000/api/search/ \ -H "Content-Type: application/json" \ -d '{"question": "5000以内支持5G的手机"}'

如果 LLM 服务可用,预期输出类似:

{ "code": 200, "fallback": false, "data": [ { "sku": "PHONE-X1", "name": "智能手机 X1", "price": "4999.00", "category": "手机" } ] }

判断成功的标准有三个:

  1. 请求没有超时。
  2. 返回的fallback是false,说明 LLM 成功完成了结构化翻译。
  3. 返回结果确实按价格和品类做了过滤,而不是简单的关键词包含。

如果 LLM 不可用,或者返回了非法 JSON,视图会走兜底逻辑,返回fallback: true,用name__icontains做一次简单搜索。这保证了接口在模型故障时仍然可用,只是能力降级。

再测试向量检索:

curl -X POST http://127.0.0.1:8000/api/semantic-search/ \ -H "Content-Type: application/json" \ -d '{"question": "适合拍照的手机"}'

如果已经执行过build_embeddings,预期返回带有相似度分数的列表:

{ "code": 200, "data": [ { "sku": "PHONE-X1", "name": "智能手机 X1", "score": 0.82, "price": "4999.00" } ] }

如果返回空列表,第一步先检查数据库里embedding字段是否为空;第二步检查 embedding 接口是否通;第三步检查向量维度是否一致。版本和维度不匹配是最常见的失败原因,建议在日志里把维度打出来。

生产环境要注意:curl测试只是链路验证的第一步。你还需要写单元测试,用固定的 LLM 返回结果覆盖“正常解析”“非法 JSON”“字段越权”“操作符越权”四类情况。尤其是非法 JSON,不能因为 LLM 一次输出错误就让整个接口报 500。

7. 常见问题与排查思路

在实际接入过程中,问题往往不在 Django,而在 LLM 输出和工程假设之间。整理了一份高频问题表:

问题现象可能原因排查方式解决方案
LLM 返回的是解释文字而不是 JSONsystem prompt 不够强硬,或模型不支持严格 JSON 格式打印 LLM 原始输出,确认实际返回内容修改 system prompt,增加“只输出 JSON”约束;开启模型的 JSON mode
JSON 解析报错,接口走兜底模型在 JSON 前后加了 markdown 代码块或注释打日志查看 raw_contentparse_llm_response 兼容 ```json 包裹,去除多余注释
查询条件没生效LLM 输出的 field 不在白名单中打印 parsed 后的 filters检查字段名是否与 models 中一致;必要时让 LLM 参考 Product 字段清单
价格条件查不到数据value 带了货币符号或单位打印 filters 里的 value在 prompt 中明确“价格类比较输出 float 或 int,不要输出货币符号”;后端再做强类型转换
向量检索结果为空embedding 字段未写入,或维度不一致检查 build_embeddings 输出和执行时间重建向量;统一 embedding 模型
接口响应很慢LLM 服务本身慢,或没有设置超时查看请求耗时日志缩短 timeout,加缓存,对超时走兜底;长任务改用异步任务队列
用户写上“删除这个商品”LLM 意图识别不设边界查看 LLM 输出是否包含危险操作彻底禁止在协议中定义 delete/update 动作;只允许只读查询

这些问题的共性其实只有一个:不要默认 LLM 永远输出合法结果。每一层都要有校验和兜底,这是工程化接入大模型的基本修养。

8. 最佳实践与工程建议

8.1 把 LLM 的动作边界写进协议

如果你在采购系统或 ERP 里接入 LLM,最危险的做法是让 LLM 直接“执行操作”。无论是DELETE、UPDATE还是批量修改,都不建议通过自然语言直接触发。正确做法是:LLM 只负责输出意图和参数,最终执行必须由用户确认,并且要记录操作审计日志。哪怕是只读查询,也要把 LLM 可访问的字段限制在最小集合。

8.2 用“降级开关”保证核心功能可用

LLM 是增强功能,不是核心依赖。你的商品目录查询必须保证在模型服务挂掉时仍然可用。我在示例里已经演示了兜底搜索,但这只是最低级兜底。更完整的做法是:

  • 用 Redis 缓存用户问题对应的查询条件。
  • 对高频问题做预置规则。
  • 设置超时上限,比如 3 秒到 5 秒。
  • 如果在生产环境中无法接受同步调用,把 LLM 解析放到 Celery 任务里,前端通过 WebSocket 接收结果。

这里延伸一句:很多团队用“Django + WebSocket 实现后台有数据前端推送”来承接 LLM 长耗时任务,这是非常合理的组合。用户提问后,接口立刻返回“解析中”,后端异步完成解析和检索后推送结果,避免 HTTP 请求长时间挂起。

8.3 字段白名单与类型强校验

我在代码里用了白名单,但还不够。真实项目中还要做类型强校验,比如price必须能转成 Decimal,stock必须能转成 int。这些校验可以放在一个独立的validate_filters()函数里,并在视图抛出校验异常时返回 422 而不是 500。不要让 LLM 的坏输出污染你的业务接口状态码。

8.4 向量检索不是银弹,索引和成本要提前规划

本地 ERP、B2B 电商这些场景很适合“本地化 RAG + LLM 产品检索”,但要注意几个成本点:

  • embedding 模型调用也有成本,商品数据全量更新时开销不小。
  • 不要把向量存进普通 JSON 字段后全表扫描,数据量过千就该用向量数据库或 pgvector 索引。
  • 向量命中的结果不要直接展示,最好再做一轮业务规则过滤,比如库存为 0 的商品要降权或隐藏。

8.5 与 Django 后台的整合

如果你正在用 Django Unfold 这类后台模板,完全可以把 LLM 能力加进后台的管理指令里。常见做法有几种:

  • 在 ProductAdmin 的实例 action 里加“生成商品卖点”,选中多条商品后调用 LLM 生成描述。
  • 在后台详情页加“智能补全属性”,把 LLM 提取出的属性写成草稿,由运营确认后再保存。
  • 在后台查询入口接一个自然语言筛选框,让运营人员可以快速筛选库存、价格和品类组合。

这些做法的核心原则都一样:系统自动生成的内容只能作为建议或草稿,不能绕过人工审核直接写库。

8.6 产品目录的多语言问题

catalogue produit 这个场景在法语项目、跨境电商项目里很常见,而多语言搜索正是 LLM 能发挥价值的地方。传统做法是多语言字段或翻译表,用户输入法语,系统还要做翻译再匹配。用向量语义检索后,法语描述和中文问题可以在语义空间里直接匹配,这是一个显著增强。但要注意:embedding 模型必须支持多语言,否则跨语言相似度会失真。实践建议是在描述字段里同时保留多语言版本,并把语言信息作为 meta 字段参与向量构建,而不是简单地把翻译文本混在一起。

9. 总结与后续学习方向

这条链路真正讲清楚的是:Django 接入 LLM 不等于“让大模型生成 SQL”,而是建立一个受控的语言解析层。产品目录这类业务场景最适合先跑通“自然语言 → 结构化 JSON → ORM 查询”的最小闭环,再逐步扩展向量语义检索和后台内容生成。LLM 负责理解用户意图,Django 负责坚守业务规则和权限边界,数据库负责高效执行查询,三者各司其职。

这里给你一个务实的下一步:先不要急着做复杂的 Agent、多轮对话或工具调用,把你的 Product 模型和筛选条件字段清单列出来,写一个最简单的 system prompt,跑通一次“用户说人话、系统出列表”的流程。之后再去研究向量检索优化、缓存、异步推送和后台管理整合。

值得继续深入的方向包括:

  • 用 pgvector 替换 JSON embedding 字段,支持大规模商品向量的高效检索。
  • 把自然语言转 ORM 的解析结果接入审计日志,追踪每次查询的意图和条件。
  • 用 Django Channels 实现 LLM 长耗时任务的 WebSocket 推送。
  • 针对本地 ERP 场景,做一套“离线索引 + 本地模型 + 权限隔离”的私有化产品检索方案。
  • 探索多语言商品目录的语义检索,让法语、英语、中文目录都被一条查询链路覆盖。

如果你正在维护一个老旧的 Django 商品系统,我的建议是从一个只读搜索接口开始,先把查询体验提升起来,再来想内容生成和自动化运营。这条路线风险最小、业务价值也最直接。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询