Product Docs

RAG 设计

返回作品总览

RAG 设计

设计目标

外卖客服 RAG(Retrieval-Augmented Generation)的目标不是技术炫技,而是在客户问「能不能少辣」「多远能送」「虾新不新鲜」「怎么退款」时,找到可引用的店铺规则,让 AI 避免凭常识编造

具体的设计目标:

  1. 召回准确:客户问什么,RAG 就找到对应店铺规则
  2. 召回可解释:每条结果可追溯到具体知识条目
  3. 降级可控:找不到时主动承认,不编造
  4. 维护可达:商家能看到命中率、有用率,知道哪条要改
  5. 成本可控:检索本身不占大头开销,单次查询成本忽略不计

知识库特点

外卖客服知识库的几个特点决定了 RAG 设计的取舍:

特点含义设计影响
规模小单店通常 30-80 条不需要高维向量召回,关键词足够
问法多变同一意图有多种表达必须支持同义词与近义召回
更新频繁价格、活动周更不能依赖向量预计算
中文为主客户多用中文分词器和 trigram 必须支持中文
强结构化营业时间、配送范围有固定字段可以用结构化 KV 加速

这些特点意味着外卖场景下不需要复杂的 RAG 工程——纯关键词 + trigram + 同义词扩展就能覆盖 80% 召回,向量是锦上添花。

检索架构

系统采用混合检索 + RRF 融合的轻量架构:

[客户 query]
     │
     ↓
[查询规范化(去标点、分词、同义词扩展)]
     │
     ┌─────────────┬──────────────┬───────────────┐
     ↓             ↓              ↓               ↓
[ILIKE 全文]  [trigram 模糊]  [同义词命中]   [向量召回(可选)]
     │             │              │               │
     │             └──────────────┴───────────────┘
     ↓                            │
[关键词 top-k]                    ↓
     │                       [向量 top-k]
     └──────────┬─────────────────┘
                ↓
        [RRF 融合排序]
                │
                ↓
        [取 top-3 进 prompt]
                │
                ↓
        [回答时标注引用 [1][2][3]]

四路召回各有侧重:

召回方式优势局限
ILIKE 全文精确匹配,零延迟错别字命不中
trigram 模糊容错错别字噪声多
同义词扩展应对问法变化依赖维护质量
向量召回语义相似需要 embedding 服务,成本高

RRF 融合

四路召回结果用 RRF(Reciprocal Rank Fusion)融合排序,公式:

score(d) = Σ 1 / (k + rank_i(d))

其中 k=60 是常用调和参数,rank_i(d) 是文档 d 在第 i 路召回中的排名。

RRF 的优点:

  • 不同路召回的分数分布完全不同(ILIKE 是 0/1,向量是 0-1 浮点),直接相加会被高分路压制
  • RRF 只看排名不看绝对分数,对所有路平等
  • 实现简单,几行代码搞定

例:客户问「虾新不新鲜」

文档ILIKE 排名trigram 排名同义词排名向量排名RRF 总分
海鲜保存说明12130.0612
食品安全条例31-20.0476
退款政策--540.0212

最终取 top-3 进入 Prompt 上下文。

中文分词与 trigram

PostgreSQL 的 pg_trgm 默认对英文优化,对中文需要额外处理:

  1. 客户 query 预处理:jieba 分词 + 去停用词
  2. 知识库索引:标题 + 正文 + 同义词合并字段建 trigram 索引
  3. trigram 阈值:设为 0.3(默认 0.3 偏严,可调到 0.25 提召回)
  4. 单字保留:分词时保留单字(避免「鱼」被吃掉)

中文 trigram 的实际效果:

  • 「皮皮虾」→ 命中"虾""皮虾""皮皮"
  • 「不新鲜」→ 命中"新鲜""不鲜"
  • 「能退款吗」→ 命中"退款""退"

向量召回(可选)

向量召回作为开关:ENABLE_VECTOR_RAG=true/false。当前默认 false,原因:

  • windhub.cc default 分组的 embedding 渠道未启用
  • 单店 30-80 条规模下,关键词 + 同义词召回率已 > 85%
  • 向量召回每次有 embedding 调用成本

启用条件:

  • 知识库扩展到 200+ 条(关键词召回开始遗漏)
  • 客户问法越来越多变(语义相似但词面差异大)
  • embedding 服务成本可接受(每次 query < ¥0.0001)

上下文打包

召回结果进入 Prompt 时的格式:

# 相关知识库条目

[1] 营业时间
   每天 16:00 - 次日 02:00 营业。节假日除外。

[2] 配送范围
   3 公里内全部配送。3-5 公里在订单充足时配送。

[3] 海鲜保存
   海鲜需冷藏保存,开封后 2 小时内食用。

打包要点:

  • 每条条目带编号([1][2][3]),便于回答时引用
  • 标题 + 正文,不带 metadata(如 hit_count)
  • 总长度控制在 800 字以内(避免压缩客户当前问题的注意力)
  • 按 RRF 排序,最相关的放最前

引用展示

AI 回答中自然嵌入引用:

[AI] 我们家每天 16:00 - 次日 02:00 营业 [1],配送范围一般在 3 公里内 [2],
     高峰期 21-23 点 3-5 公里也可以送。

客户在 widget 中看到 [1] [2] 是可点击的,展开后显示对应知识条目标题。这是RAG 可解释性的关键——客户和商家都能追溯每条信息来源。

召回失败的降级

当四路召回都没有合理结果时(top-1 RRF 分数 < 阈值),系统进入降级:

  1. 不传任何知识进 Prompt(避免误引用)
  2. 在 Prompt 中显式告知"未找到相关规则"
  3. 模型回复要短,引导客户补充信息或转人工
  4. 该会话标记为 kb_miss=true,进入 fallback 队列

降级处理详见 02-conversation/04-fallback

运营维护

RAG 的效果完全取决于知识质量。系统每日聚合下列指标:

指标用途
top10 query 命中率是否有高频问题没召回
知识条目命中分布是否有 zombie 条目(30 天 0 命中)
同义词触发频率是否有同义词从未生效
引用后有用率召回是否真的解决问题
RRF 分数分布是否需要调阈值

命中率低的会话回流到知识库待补充列表(参见 03-knowledge/02-lifecycle 新增来源),过期条目会提示复核。RAG 不只是技术模块,而是知识运营流程的一部分。

设计取舍

取舍原因
不用 BM25中文 BM25 调参成本高,trigram + 同义词更直接
不用 reranker 模型单店规模下 RRF 已够用
不用大模型重排成本与延迟代价不值得
默认关闭向量embedding 渠道未稳定,且小库关键词够用
不做 chunking知识条目本身就短(< 200 字),无需切分
引用编号显式展示牺牲一点回复美观度换可解释性

这些取舍让 RAG 在中小商家场景下够用、便宜、可维护。后续如果接入大型知识库或多店共享知识,再升级到向量主路 + reranker。

与其他模块的关系

RAG 与下面三个模块紧密协作:

  • 意图分类(参见 02-conversation/01-intent-taxonomy):意图决定召回过滤(例如订单类不查 KB,直接调工具)
  • 知识库管理(参见 03-knowledge/):知识质量决定 RAG 效果上限
  • Prompt 设计(参见 05-prompt/01-design-principles):Prompt 的"引用规则"决定召回结果如何被呈现

三者协同才能让 RAG 从"召回工具"升级为"客服内容工程"的核心环节。