电商智能客服问答系统(RAG)
五层分层架构 RAG 客服系统——BERT 意图分类 + 双路混合检索 + Reranker 精排 + 三级缓存,覆盖八大知识域,端到端 P50 < 2s。
项目概览
| 要素 | 内容 |
|---|---|
| 项目周期 | 2024.12 - 2025.08 |
| 核心技术 | BERT / BGE-M3 / Milvus / vLLM / Redis / FastAPI |
| 角色 | 主导架构设计、检索优化、缓存方案、部署上线 |
| 核心成果 | 意图准确率 95%+ / 缓存命中率 60%+ / 响应延迟降低 70% |
项目背景
公司电商平台客服团队每天处理大量重复咨询(退换货政策、物流时效、尺码推荐等)。分析了近 3 个月的客服咨询日志,退换货、物流、商品参数等高频意图占总咨询量 60% 以上。人工回复存在两大问题:
- 标准不一致:不同客服回答口径不同,用户体验有差异
- 高峰期排队:大促期间咨询量暴增,人工客服响应慢,用户满意度下降
核心挑战
- 如何精准识别用户模糊口语化的查询意图
- 如何在八大知识域中高效检索相关内容
- 如何保证回答准确合规,控制幻觉
- 如何在高并发下保持低延迟
核心架构:五层分层设计
┌─────────────────────────────────────────────┐
│ 第五层:安全审核 │
│ 内容合规 / 事实校验 / 语气规范 │
├─────────────────────────────────────────────┤
│ 第四层:LLM 生成 │
│ 分场景温度控制 / Prompt模板矩阵 / 溯源引用 │
├─────────────────────────────────────────────┤
│ 第三层:Reranker 精排 │
│ BGE-Reranker-v2-m3 Cross-Encoder │
├─────────────────────────────────────────────┤
│ 第二层:混合检索 │
│ 稠密检索(BGE-M3)+ 稀疏检索(BM25) │
│ RRF 融合 + 动态权重调整 │
├─────────────────────────────────────────────┤
│ 第一层:意图识别 │
│ BERT-wwm-base 微调 / 四级置信度策略 │
└─────────────────────────────────────────────┘
↗ 三级缓存体系 ↖
L1 精确匹配 / L2 语义匹配 / L3 全链路模块化可扩展的五层架构,每一层都可以独立优化和替换。
第一层:意图识别(BERT 微调)
BERT-wwm-base 微调做意图分类,覆盖八大核心知识域:退换货、物流、商品参数、比价、活动、教程、尺码、发票支付。
三阶段渐进训练
- 通用预训练微调:在通用中文文本分类数据集上微调,让模型学会基本分类能力
- 领域适应微调:在电商客服领域弱标注数据上继续微调,熟悉电商咨询表述风格
- 精标数据微调:在约 5000 条精标数据上最终微调
为什么分三阶段而不是直接在精标数据上微调?因为精标数据只有约 5000 条,直接微调容易过拟合。渐进训练让模型先有通用分类能力,再适应领域表述,最后精修到具体类别。
标注数据来源
- 客服团队已有粗分类标签约 3000 条 → 清洗标准化
- 从原始日志补充标注约 2000 条 → 重点覆盖模糊表述和长尾场景
- 每个意图最少 300+ 条样本
- 质量控制:随机抽 200 条由客服运营二次审核,一致性 > 90%
四级置信度策略
BERT 输出的是概率分布,不是所有查询都能高置信归到一个类别。
| 级别 | 置信度 | 处理方式 |
|---|---|---|
| 高置信 | > 0.85 | 直接路由到对应意图检索流程 |
| 中等 | 0.6 ~ 0.85 | 路由对应意图 + 附加 Query 扩展 |
| 低置信 | 0.3 ~ 0.6 | 多意图并行检索,LLM 综合判断 |
| 无关 | < 0.3 | 走兜底,礼貌告知并转人工 |
只取最高概率的问题:一个用户问"这个衣服能退吗质量怎么样",BERT 可能给退换货 0.55、商品参数 0.45,硬路由到退换货会丢失"质量"意图。
第二层:混合检索(双路召回)
BGE-M3 稠密检索 + BM25 稀疏检索双路召回,RRF 融合。
为什么需要双路
- 稠密检索(BGE-M3):擅长语义理解——"这款手机续航怎么样"→匹配到电池评测内容
- 稀疏检索(BM25):擅长精确关键词——"7天无理由退货"→精确匹配政策条款
- RRF 融合:让两种优势互补——语义相关 + 关键词精确的文档双路都靠前
动态权重调整
根据意图识别结果动态调整稠密/稀疏权重:
| 意图类型 | 稠密权重 | 稀疏权重 | 原因 |
|---|---|---|---|
| 退换货政策 | 0.4 | 0.6 | 需要精确关键词匹配政策条款 |
| 商品参数 | 0.7 | 0.3 | 语义理解比关键词更重要 |
| 活动促销 | 0.5 | 0.5 | 关键词 + 语义双重需求 |
Query 理解优化
在检索之前做 Query 理解预处理:
- 口语归一化:200+ 条规则映射表("买了多久能退"→"退换货时效 条件")
- 关键词扩展:同义词替换 + 相关词扩展
- 子查询分解:复杂问题拆成多个子查询并行检索
规则覆盖不了的走 LLM Query 改写,但只在中等/低置信度时触发,避免不必要的 LLM 调用增加延迟。
第三层:Reranker 精排
BGE-Reranker-v2-m3 做精排。召回阶段从百万级文档中快速取 Top-50(速度快,精度够用),Rerank 阶段对这 50 条精排(精度高,50 条推理量可控)。
召回粗排 + Rerank 精排是 RAG 系统的标准两阶段检索架构。
BGE-M3 vs BGE-Reranker 的区别
| 模型类型 | 架构 | 速度 | 精度 | 用途 |
|---|---|---|---|---|
| BGE-M3 | 双塔模型(Query/Doc 分别编码) | 快 | 有限 | 粗排召回 |
| BGE-Reranker | Cross-Encoder(Query+Doc 交互推理) | 慢 | 高 | 精排 |
Reranker 输出的分数是相对值不是绝对概率——0.87 不代表 87% 相关,只表示比 0.65 分更相关。只能用来排序,不能设绝对阈值。
效果
Top-1 准确率从单路向量检索的 71% 提升到 82%——因为 Reranker 能捕捉初排无法辨别的细微差异。比如用户问"退货政策",向量检索可能把"退款流程"排在前面(语义接近但意图不同),Reranker 能识别出二者是不同意图。
第四层:LLM 生成
(意图 × 场景)二维 Prompt 模板矩阵
不同意图、不同场景用不同 Prompt 模板,不是所有问题一套模板。
分场景温度控制
| 场景类型 | temperature | 原因 |
|---|---|---|
| 事实类(政策、时效、参数) | 0 | 几乎只输出最高概率答案,抑制幻觉 |
| 策略类(比价、尺码推荐) | 0.3 | 允许轻微多样性,多个选项推荐 |
事实类问题温度>0 会让 LLM 可能"创造性"编造政策条款,这在客服场景是致命的。
为什么温度为 0 还要用 LLM 而不直接返回原文?
- 信息提取和重组:原文可能是长篇政策,LLM 提取关键信息整合成一句精准回答
- 多文档信息整合:一个查询涉及多篇文档,LLM 合并成连贯回答
- 多轮上下文理解:连续提问需要结合历史上下文消歧
温度=0 只是为了抑制创造性,不是否定 LLM 的信息提取、整合和上下文理解能力。
第五层:安全审核
三段式安全审核:
- 内容合规审核:检查是否涉及虚假宣传、价格欺诈等
- 事实校验审核:关键事实(退货天数、受理条件)是否在参考文档中有原文依据
- 语气规范审核:语气是否专业礼貌,避免生硬或冒犯
每段审核由规则引擎+轻量模型组合执行,不合格的回答会被拦截或修正后重新生成。
三级缓存体系
上线运行 2 个月稳定期命中率约 60%。
| 级别 | 缓存方式 | 命中率 | 响应速度 |
|---|---|---|---|
| L1 精确匹配 | Redis 精确匹配键 | ~38% | < 5ms |
| L2 语义匹配 | Redis 语义索引最近邻 | ~18% | ~30ms |
| L3 全链路 | 全链路结果缓存 | ~6% | ~100ms |
L2 缓存实现:用户 Query 经 BGE-M3 编码后,在 Redis 有序集合中找最近邻。覆盖了 L1 漏掉的表述变体(比如"能退货吗"和"退换货政策"语义相同但字面不同)。
冷热分层存储
知识库访问频率差异大,不分层成本高。
| 层级 | 访问频率 | 存储 | 占比 | 覆盖查询量 |
|---|---|---|---|---|
| 热数据 | 近7天高频 | Redis 缓存 + Milvus SSD | ~20% | ~60% |
| 温数据 | 7~30天有访问 | Milvus 向量库 | ~60% | ~38% |
| 冷数据 | 30天+无访问 | 对象存储归档 | ~20% | ~2% |
每天凌晨定时统计各知识条目的近 7 天访问次数,低于阈值降级,被重新启用则升级。
幻觉控制策略
RAG 系统的幻觉有三个来源,针对性控制:
来源一:知识库边界外的查询
用户问了知识库里没有覆盖的问题,LLM 拿一堆不相关结果被迫生成,就会编造。
控制策略:四级置信度路由的 L4 兜底——置信度<0.3 时不触发 RAG,直接回复"该问题暂无法自动回复,已转接人工客服"。关键原则是宁愿说"不知道"也不能编造。
来源二:检索噪声
检索结果看起来相关但实质偏离,LLM 基于"半相关"内容生成答案,细节出错。
控制策略:双路检索 + Reranker 精排把 Top-1 准确率从 71% 拉到 82%;三段式审核的第二段"事实校验"检查关键数字是否在原文中有依据。
来源三:生成阶段固有偏差
即使检索完全正确,LLM 可能用预训练知识"脑补"——比如文档说"支持 7 天无理由退货",LLM 把预训练学的"电商常见规则 15 天"混进去了。
控制策略:分场景温度控制(事实类 0)+ 答案末尾附信息来源,让用户知道信息来源,也方便运营溯源。
核心成果
| 指标 | 数值 |
|---|---|
| 意图分类整体准确率 | > 93% |
| 高频意图准确率 | > 96% |
| 三级缓存命中率 | ~ 60%(上线 2 个月稳定期) |
| 端到端 P50 延迟 | < 2s(本地部署) |
| L1 缓存命中延迟 | ~ 5ms |
| 覆盖知识域 | 8 个(退换货/物流/商品参数/比价/活动/教程/尺码/发票支付) |
| 知识库条目 | 1000+ 条(经运营专家审核) |
| Top-1 准确率(vs 单路向量) | 71% → 82% |
| 人工介入率下降 | ~ 40% |
关键技术决策
为什么 BERT 110M 参数能做到 >93% 准确率
三个因素叠加让小模型在这个场景足够:
- 任务本身不复杂:八大意图的语义边界清晰,不是需要大模型的细粒度区分
- 预训练知识迁移:BERT-wwm-base 在全词掩码预训练时已经学到了电商词汇的语义表示
- 数据质量比模型大小更重要:5000 条高质量数据 + 110M BERT 效果优于 2000 条数据 + 340M BERT
换个角度:如果需要区分 50+ 个细粒度意图,或者意图边界非常模糊,110M 可能不够。但在八分类高区分度场景下,它是"够用且轻量"的选择。
Embedding 模型为什么选 BGE-M3 而不是 bge-large
客服场景有三个特性是 BGE-M3 能提供的:
- 稠密 + 稀疏双向量输出 → 一个模型支撑双路检索
- 多语言支持 → 中英混杂输入(比如"iPhone15 售后政策")
- 长文本支持(8192 tokens) → 政策条款、活动规则等长文档
bge-large-zh 只有 1024 维稠密向量,用了它还要单独跑 BM25,两个模型串联更重。
为什么用 Milvus 而不是其他向量库
| 选型对比 | 差异 |
|---|---|
| vs FAISS | 是检索库不是数据库——不支持持久化、分布式、元数据过滤 |
| vs Qdrant | 更灵活,但 Milvus 社区生态更大、更熟悉索引调优 |
| vs pgvector | 主库是 MySQL,引入 pgvector 增加运维成本,百万级性能不如 Milvus |
选型原则:Milvus 负责"检索快",Redis 负责"查得快",MySQL 负责"记得久",COS 负责"存得省"。各司其职,不互相越界。
遇到的挑战与解决方案
挑战一:缓存雪崩
问题:上线第二周,运营一次性更新退换货政策(约 30 条),触发批量缓存失效。Redis 中大量缓存瞬间过期,QPS 从 50 飙升到 200+,延迟从 300ms 飙到 3s。
根因:缓存失效时间集中 + 没有随机过期偏移。
解决:
- 批量更新时给缓存设置随机过期偏移(TTL=3600±300s),避免集中失效
- 加本地内存 Caffeine 缓存做 L0 兜底——请求先查本地,miss 再走全链路
- 上线缓存预热机制——知识更新后异步重建高频 Query 缓存
挑战二:Reranker 精度退化
问题:上线一个月后运营反馈"最近推荐退换货政策的准确率下降了"。
分析:新增 50+ 条活动促销类知识,语义空间和退换货政策有重叠(都涉及"退款""退货"关键词),旧阈值无法区分新增边界 case。
解决:用新知识的标注数据做 Reranker 微调(约 200 条样本),定向提升区分力。建立 Reranker 月度评估机制——每月用固定 500 条标注 query 做离线评测,追踪各知识域 Recall@5 变化趋势。
挑战三:长尾意图的 LLM 超时
问题:部分用户问超长、超复杂的问题(一口气问 4~5 个不相关问题),BERT 归为"其他/无法识别"进入完整链路。多意图并行检索时,Reranker 候选膨胀到 100+ 条,LLM 处理超长上下文时偶尔超时。
解决:
- 加输入截断——>200 字先做 LLM 拆分成多个子 query 分别处理
- 设置 LLM 调用超时机制(10s 超时走原文返回降级),避免长耗时请求阻塞线程池
挑战四:长文档分块策略优化
问题:最初用固定 512 token 切块,发现政策类文档经常在条款边界切断——标题在一个 chunk、内容在另一个 chunk。
解决:文档类型感知的差异化分块策略——FAQ 按 QA 对切、政策按标题语义边界切、长商品描述用 768 tokens + 128 overlap。每种类型的 splitter 有各自的 separators 优先级。
在此基础上加父块上下文扩展——子 chunk 召回后,找到所属父文档位置,前后各拼接 1 个相邻 chunk,保证信息完整性。
技术选型说明
| 组件 | 选型 | 备选 | 选型理由 |
|---|---|---|---|
| 意图分类 | BERT-wwm-base 微调 | 直接用 LLM / FastText | 110M 参数足够 + 毫秒级响应 |
| Embedding | BGE-M3 | bge-large-zh / M3E | 稠密+稀疏双输出,一次推理双路检索 |
| Reranker | BGE-Reranker-v2-m3 | 无 Reranker 直接 Top-10 | Top-1 准确率从 71% → 82% |
| 向量库 | Milvus | FAISS / Qdrant / pgvector | 生产级 + 熟悉 + 扩展性好 |
| 缓存 | Redis 三级缓存 | 本地内存 | 多实例部署必须共享缓存 |
| LLM 部署 | vLLM + Qwen3:14B | API 调用 | 数据隐私要求 + 成本控制 |
| 服务框架 | FastAPI | Flask | 异步支持 + 高并发 + 自动文档 |
| 部署方式 | Docker + Nginx | 裸机部署 | 环境一致性 + 易扩缩容 |
项目价值与意义
这个项目是从"模型能力"到"系统能力"的进阶——不再是调用一个模型解决单一问题,而是构建完整的检索增强生成系统,涉及意图识别、检索优化、生成控制、缓存体系、部署运维全链路。
核心设计理念:按场景做差异化——不是所有查询都用同一个流程处理,而是根据意图置信度、类型、频率动态选择最合适的处理路径。高频高效、长尾覆盖、精准控本。
从业务价值看:客服回答质量直接影响用户体验和退单率,这个环节用自研全链路而不是商业 SaaS,换来了对八大知识域的深度优化能力和完全可控的迭代速度。