拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Django接入LLM实现自然语言商品搜索:从ORM到向量检索

如果要给 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__lte5000“支持 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.10Django 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 服务的选择有两种主流选择建议都考虑本地 LLM用 Ollama 或 vLLM 部署开源模型。优点是数据不出内网适合本地 ERP、商品数据敏感的电商系统缺点是硬件成本和模型能力需要自己评估。云 API使用 OpenAI 兼容接口。优点是接入快、模型理解能力强缺点是商品数据要发到外部服务需要做脱敏和合规评估。无论选哪种只要支持/v1/chat/completions和/v1/embeddings后面的代码都可以直接复用。我把这些配置放到环境变量里不写死厂商export LLM_API_KEYEMPTY export LLM_BASE_URLhttp://localhost:11434/v1 export LLM_MODELqwen2.5 export LLM_EMBED_MODELbge-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_length255) sku models.CharField(max_length64, uniqueTrue) category models.CharField(max_length128, db_indexTrue) price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0) description models.TextField(blankTrue) attributes models.JSONField(defaultdict, blankTrue) embedding models.JSONField(nullTrue, blankTrue, editableFalse) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) 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, temperature0.0, timeout30): url f{self.base_url}/chat/completions payload { model: self.model, messages: messages, temperature: temperature, } resp requests.post( url, headers{Authorization: fBearer {self.api_key}}, jsonpayload, timeouttimeout, ) 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__icontainstext_query) | Q(description__icontainstext_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}, timeout30, ) 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_k5): question_embedding get_embedding(question) candidates [] for product in Product.objects.exclude(embedding__isnullTrue)[:500]: score cosine_similarity(question_embedding, product.embedding) candidates.append((score, product)) candidates.sort(keylambda x: x[0], reverseTrue) 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.py5.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, skuPHONE-X1, category手机, priceDecimal(4999.00), stock20, description支持 5G 网络4800 万像素适合拍照。, attributes{brand: 示例, color: 黑色, storage: 256GB}, ), Product( name入门手机 A2, skuPHONE-A2, category手机, priceDecimal(1999.00), stock50, description4G 全网通大电池长续航。, attributes{brand: 示例, color: 蓝色, storage: 128GB}, ), Product( name轻薄笔记本 L1, skuLAPTOP-L1, category电脑, priceDecimal(6500.00), stock8, description14 英寸屏幕16GB 内存适合办公。, attributes{brand: 示例, color: 银色, storage: 512GB}, ), ] Product.objects.bulk_create(products, ignore_conflictsTrue) self.stdout.write(self.style.SUCCESS(seed done))执行命令python manage.py seed_products5.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}, status400) if not question: return JsonResponse({code: 400, message: question is required}, status400) 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__icontainsquestion)[: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}, status400) 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__isnullTrue)[: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(fembedded {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: 手机 } ] }判断成功的标准有三个请求没有超时。返回的fallback是false说明 LLM 成功完成了结构化翻译。返回结果确实按价格和品类做了过滤而不是简单的关键词包含。如果 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 modeJSON 解析报错接口走兜底模型在 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必须能转成 Decimalstock必须能转成 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 商品系统我的建议是从一个只读搜索接口开始先把查询体验提升起来再来想内容生成和自动化运营。这条路线风险最小、业务价值也最直接。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门