投稿
![[07cf09d432524716b483015d9a24cc6a.png]]
Milvus x 智能客服 :从“找对商品”到“答对细节”的检索体系升级
我们熠坤AI初创公司推出的基于⼤模型RPA 和 RAG 技术的智能客服系统,⽀持淘宝、京东、拼多多等 5+ 电商平台,并已接入头部领域店铺,旨在帮助客服自动回复客户的各种问题。
在早期版本中,我们的系统完全采用 Ragflow 架构,通过将商品的全部内容直接进行粗放式切片(Chunk)处理。但随着接入店铺数据量的增长和咨询场景的复杂化,该架构暴露出了严重的工程与业务痛点: 一方面,检索粒度完全不可控,导致大量无关噪音混入上下文;另一方面,Ragflow系统本身日益臃肿,维护难度大,系统脆弱性增加。
于是在经过大量的调研与测试以后,我们将语义搜索的向量数据库迁移到了milvus,与postgre传统数据库的精确匹配形成互补。
1. 核心需求拆解
我们在落地智能客服 RAG 的过程中,最核心的挑战集中在五类:
- 对象定位难
用户大量问题是“这个怎么样/耗电吗/怎么安装”,如果没先定位“这个”到底指哪一个商品,后面一切都无意义。只要商品检索错了,后面生成再好也没用。 - 语义理解与词法匹配的冲突
商品检索里既有强语义(“制热快不快”),也有强词法(型号、系列名、SKU、简称)。单靠一种检索策略,很容易要么“语义对了商品错了”,要么“商品对了细节不相关”。 - 知识源多且形态不一
既有通用 QA(售后、物流、保修),也有商品知识(参数、卖点、说明书内容),还可能有结构化表格(活动价、规格清单、库存/渠道数据)。如果进同一个知识库,后期很难治理、也难迭代。 - 存在完全无法通过语义检索的场景
用户可能会查询@暖风机表格中价格小于100的商品,依托于相似度的语义检索无法精确解决类似问题。 - 时延、成本与稳定性约束
- 客服是高并发、强实时,必须在可控的 token 开销与可预测的时延下运行。能规则化解决的问题要前置拦截;需要检索的问题要尽量并行;检索结果要尽量“少而准”。
基于这些挑战,我们把检索与数据链路拆成可演进的模块,并采用Milvus 这个开源的向量检索中间件来解决这些问题。
2. 整体方案:一个面向客服的“分层检索 + 证据生成”链路
我们把一次对话请求拆成四段:
- Query 理解:把口语化对话句转成可检索表达(后面会讲“问题重写 + 焦点商品提取”)
- 分层并行检索:通用知识库(QA)与商品知识库(Product)并行召回并使用rerank重排
- 两级回填拼装:Milvus 做召回与排序,Postgres 做权威字段与内容拼装
- 基于证据生成:把检索结果分层注入 prompt,约束 LLM 在证据范围内回答
如果对话中出现语义检索无法完成的任务,我们用Text2SQL技术进行表格检索:让模型能查结构化数据,并保持确定性。
同时我们还在探索agent检索用户自己维护的任意表格,也打算利用milvus来实现
![[Pasted image 20251224185136.png]]
3.核心技术实现设计
3.1. 数据收集与预处理:单商品单记录策略
构建知识库首先要有数据。与业界通用的“将文档切分为碎片化 Chunks 分散存储”的方案不同,我们发现商品数据具有极强的内聚性。如果把一个商品的参数、描述、评价拆得太散,检索时很难拼凑出全貌。
因此,我们采取了“单商品单向量记录”的策略 。 我们在同步数据到 Milvus 时,将一个商品下的所有内容块预先拼接合并,存为一条 Milvus 记录,ChunkID 统一格式为 product_{productId} 。
这种设计让 Milvus 中的记录数从 $N \times M$ 降低到了 $N$($N$=商品数),不仅显著降低了存储成本,更简化了后续的召回拼装逻辑——一次命中,即可获得该商品的完整上下文。
1 | public void syncProductToMilvus(ProductEntity product, List<ProductChunk> chunks) { |
3.2. Embedding 与索引架构:两级存储设计
有了清洗好的数据,接下来是构建索引。我们采用了一套“Milvus + PostgreSQL”的两级检索架构,以此来兼顾检索速度与数据的权威性。
向量库(Milvus)负责“找得准”: 我们将数据向量化后存入 Milvus,作为“索引指针”。这里并不存储冗余的业务字段,只存储用于计算相似度的向量和主键 ID(
product_{productId})。使用IVF_FLAT索引,平衡检索速度和存储成本。1
2
3
4
5
6
7
8CreateIndexParam goodNameIndexParam =
CreateIndexParam.newBuilder()
.withCollectionName(collectionName)
.withFieldName("goodnamevector")
.withIndexType(IndexType.IVF_FLAT)
.withMetricType(MetricType.COSINE)
.withExtraParam("{\"nlist\":1024}")
.build();关系库(PostgreSQL)负责“信息全”: 商品的实时价格、高清图片 URL、上下架状态等结构化信息存储在 Postgres 中。
3.3. 查询前置处理:问题重写与焦点提取
如前面困境所说,用户的 Query 实际是多种多样的,有简单的 Query,也有指代不明的 Query。比如用户进线看了“美的取暖器”,然后问:“这个制热快吗?”。
如果直接拿“这个制热快吗”去检索,召回率几乎为零。为此,我们在检索前引入了 Query 转换模块 。 通过 QuestionRewriteUtil,利用 LLM 对用户问题进行重写,提取出“焦点商品” (focusProduct),并将问题转化为“最新问题” (latestQuestion) 。
1 | conversationText.append( |
例如:
- 原始问题:“这个制热快吗?”
- 重写后:“美的取暖器制热效果如何?”
这一步把“对话语句”转成了“检索语句”,对后续的召回准确率起到了决定性作用。
3.4. 向量查询
这是整个系统的核心。为了解决用户千奇百怪的提问方式,我们设计了多维度的检索策略。根据用户输入情况,自动选择最合适的检索策略。
3.4.1 双层知识库并行检索+postgre回填
客服场景对响应速度极其敏感。我们的知识库分为两层:
- QA 通用库:存储通用的售后政策、退换货流程 。
- 商品专属库:存储具体的商品参数、规格 。
在 Workflow.run() 中,我们利用线程池进行并行执行 。
QA 检索:使用原始问题。
1
2
3
4
5
6
7
8
9
10
11// QA Milvus检索任务
if (args.getUid() != null && args.getAiSolutionId() != null)
// 直接使用QaMilvusClient进行QA向量检索(使用原始问题)
List<org.com.aicaresupport.entity.QaSearchResult> qaResults =
qaMilvusClient.searchQaVectors(
args.getUid(),
args.getAiSolutionId(),
finalOriginalQuestion,
qaTopK);
商品混合检索:使用重写后的“焦点商品+问题”。
1
2
3
4
5
6
7
8if (args.getUid() != null && args.getAiSolutionId() != null)
List<MilvusClient.SearchResult> searchResults =
milvusService.hybridSearch(
args.getUid(),
args.getAiSolutionId(),
finalFocusProduct,
finalLatestQuestion,
goodsTopK);其中在milvus的商品混合检索中我们面临一个经典冲突:用户搜“HP21-K6”时需要精确的对象定位,搜“适合老人的取暖器”时需要模糊的语义理解。单一的向量索引很难同时满足。 为此,我们在 Milvus 中为每条记录维护了两个向量字段,并赋予不同权重:
**商品名称向量 (
goodnamevector)**:权重 **70%**。侧重对象定位,保证搜出来的是“正确的那个商品”。**描述向量 (
describevector)**:权重 **30%**。侧重语义理解,提升回答细节的相关性。 系统并行检索这两个字段,确保了“先找对商品,再匹配细节”。
系统并行检索这两个字段,合并得分后进行加权融合重排序。
1 | // 合并结果并重排序(简单实现:商品名称权重70%,描述权重30%) |
返回的主键 ID(product_{productId}),会到PostgreSQL进一步查询,返回最精准的商品数据。
1 | // 批量从PostgreSQL获取完整内容 |
最后,大模型会根据QA检索以及商品检索返回的topK 个数据生成回复。
3.4.2 Text2SQL表格检索 + 语义补充
上面所说的算是一个比较标准的 RAG 流程,但是企业内部有大量的结构化数据(如 Excel 价格表、参数对比表)。对于“查询价格小于 100 的商品”这类涉及数值比较的问题,纯向量检索几乎不可用。
为此,我们在 Agent 模式下引入了 search_table_data 工具,采用了 Text2SQL + 语义补充 的方案 :
- Text2SQL:利用 LLM 将自然语言转译为 SQL 语句(如
WHERE price < 100)去postgreSQL检索,保证数值查询的绝对精确 。 - 语义补充:同时在milvus中进行向量检索,防止 SQL 生成失败或用户意图模糊 。
- 结果融合:优先采信 SQL 的结构化结果,用向量检索结果做兜底补充。
最后,大模型会根据Text2SQL表格检索 + 语义补充返回的数据生成回复。
1 | public TableSearchResult searchTableData( |
4.实施效果与验证
2025年1月-5月:我们完成Milvus商品搜索功能调研、技术验证与方案设计;2025年6月:完成精确匹配功能开发、测试并部署上线。
比起单纯使用RAGflow的技术框架,我们在Milvus基础上设计了更加灵活集中功能组件的方案,让商品在多种业务场景下检索的召回率大大提升。milvus对检索精度极高的掌控力以及原生支持的混合检索的能力,以及极高的性能扩展性好社群活力,挖掘出了我们产品更大的潜力。


