Skip to content

助手 · RAG 从关键词到向量(现状 → 效果 → 升级路径) ​

日期: 2026-08-18 | 作者: sunxin(+ Cursor AI 结对讲解) 关联代码: KeywordKnowledgeRetrievalAdapter · IKnowledgeRetrievalPort · KnowledgeSnippet · resources/assistant-knowledge/destinations.json · AssistantApplicationService.appendKnowledge 关联里程碑: 北极星与终局蓝图 M4a · 建设路线图 M4a 关联决策: ADR-039 姊妹篇: 幻觉与 grounding(RAG 的引用/拒答是防幻觉手段)· 向量检索新手科普(embedding / 切块 / 内存索引)

TL;DR ​

  • RAG(Retrieval-Augmented Generation,检索增强生成)= 先「检索」到相关知识文本,再把它「塞回模型上下文」让模型据此「生成」答案。 核心是「生成」二字,且生成时有真实资料兜底、能拒答、能引用。
  • 本项目现在是「退化版 RAG」:KeywordKnowledgeRetrievalAdapter 从本地 destinations.json(仅 6 条)按关键词重叠打分检索,命中就把内容直接字符串拼接成一句「小贴士」——只检索、不生成、无语义、无引用、无拒答。
  • 它离真 RAG 还差三步:内容太少且不结构化 → 检索是关键词非语义(错别字/别名命不中)→ 检索结果不回喂模型(没有「生成」这步)。
  • 升级路径 L0→L4:现状(关键词拼接) → 扩内容+切块 → 向量语义检索 → 回喂模型生成 + 引用 + 拒答(=真 RAG / M4a) → 混合检索+rerank+防注入。
  • IKnowledgeRetrievalPort 已把检索隔离:关键词换向量不动业务代码,只换 Adapter 实现。原文档可放 OSS,但「向量索引」是另一层(进向量库或内存),别混。

目录 ​


1. 什么是 RAG(以及什么不是) ​

RAG 三步:

用户问题 → ①检索(Retrieval):从知识库找相关片段
         → ②增强(Augment):把片段拼进 Prompt 上下文
         → ③生成(Generation):模型据此生成答案(带出处、无资料则拒答)

它解决的问题:大模型的知识是训练时冻结的、且不含你的私有资料。RAG 让模型「开卷考试」——答题前先翻你给的资料,从而回答私有/最新/垂直领域问题,且减少幻觉(有据可依)。

什么不是 RAG(重要澄清):

场景是不是 RAG为什么
助手查平台活动(searchActivitiesAdvanced)❌ 不是检索结果直接当卡片展示,从不回喂模型;模型只在入口抽意图。这是 Tool Use / 结构化查询
命中后拼「小贴士」(现状)🟡 退化版检索到了,但直接字符串拼接、不回喂模型生成。只有「检索」没有「生成」
「退款规则/景点攻略」问答,检索资料→喂模型→生成带引用答案(M4a)✅ 真 RAG检索的知识回喂模型据此生成

一句话分界:检索到的东西要不要回喂给模型再生成? 要 = RAG;不要(直接展示)= Tool Use。


2. 现状:本项目的「退化版 RAG」做到哪了 ​

2.1 组件 ​

部件位置作用
出站 Portdomain/assistant/port/out/IKnowledgeRetrievalPortretrieve(query, topK) 契约,隔离检索实现
关键词实现infrastructure/.../assistant/knowledge/KeywordKnowledgeRetrievalAdapter从本地 JSON 按关键词打分检索
知识库文件resources/assistant-knowledge/destinations.json6 条:赛里木湖 / 新疆 / 青海 / 北京 / 成都 / 户外徒步
片段模型domain/assistant/model/KnowledgeSnippettitle / content / source / score
调用点AssistantApplicationService.appendKnowledge命中活动后追加「小贴士」

2.2 检索算法(关键词重叠打分) ​

java
// 命中的关键词越长/越多,得分越高(长词更具体,权重更大)
for (String kw : entry.getKeywords()) {
    if (query.contains(kw)) score += kw.length();
}

例:destinations.json 里赛里木湖条目的 keywords 是 ["赛里木湖","赛里木","博尔塔拉","博乐"],用户 query 里出现哪个就加几分,取 top-1。

2.3 触发条件(很窄) ​

只有 SEARCH_ACTIVITIES + 有目的地 + 命中活动≥1 条 时,才去检索并在回复末尾拼一句:

已按「赛里木湖 / ≤2000元」为你找到 4 个活动:
(4 张真活动卡片)
小贴士 · 赛里木湖:6-9 月是最佳季节,7 月环湖野花盛开;海拔约 2070m 早晚温差大…  ← 这句是拼接来的

推荐 / 拒答 / 追问 / 0 条这几条路都不补贴士。


3. 效果与短板 ​

达到的效果:命中目的地时,回复多一句静态、可信、本地维护的小贴士,轻量增强体验;命不中就什么都不加(失败优雅降级)。无 Key、离线、可测——很适合起步。

短板(正是升级方向):

短板表现影响
只检索、不生成内容原样拼接,模型没参与严格说不算 RAG,也没发挥模型「组织成人话」的能力
无语义关键词字符串精确包含才命中「赛里目湖 / 塞木屋河」这种错别字/别名命不中(对应 M1 遗留技术债①)
内容太少就 6 条,且非结构化覆盖面窄,问细节答不上
无引用、无拒答不标出处、命不中静默到了知识问答场景(问「海拔多少」)无法可信作答

4. 升级路径 L0 → L4 ​

每一级都可停、可交付。IKnowledgeRetrievalPort 的存在保证升级只换实现、不动业务。

级别能力关键动作里程碑
L0 现状关键词检索 + 直接拼接已完成(KeywordKnowledgeRetrievalAdapter)M0 骨架
L1 内容工程扩充结构化攻略库 + 切块 + 元数据destinations.json → 每景点结构化(最佳季节/交通/注意/玩法/周边),长文切块 chunk + 打标签(省份/类型)M4a 前置
L2 向量语义检索Embedding + 向量相似度文本转 embedding 存向量库;query 也转 embedding 算余弦相似度 top-K。解决别名/错别字/近义M4a
L3 真 RAG(生成) ⭐检索片段回喂 LLM 生成 + 出处引用 + 无资料拒答把 top-K 片段拼进 Prompt,让模型据此生成答案并标来源;检索不到就拒答M4a 达成
L4 生产级检索混合检索 + rerank + 防注入关键词 + 向量混合召回,再用 rerank 模型精排;检索内容当「数据」不当「指令」(防提示注入)M4a+

L1→L2 是「检索」的质变(关键词→语义);L2→L3 是「有没有 RAG」的质变(加上了「生成」)。别跳级——先把内容和检索做扎实,再上生成。


5. 存储:向量库 / Embedding / OSS 怎么摆 ​

三个常被混为一谈的概念,分清楚:

概念是什么放哪
原始文档攻略 markdown / JSON 原文resources(起步)或 OSS(量大/要热更新)
Embedding把一段文本转成的一串数字向量(语义指纹)由 embedding 模型算出,存进向量库
向量索引 / 向量库存 embedding + 支持相似度检索的库pgvector / ES / Milvus / Qdrant 等

关键澄清(回答「知识库是不是存向量库、能不能放 OSS」):

  • 原文档可以放 OSS,启动时或定时拉下来切块、算 embedding、灌进向量库。OSS 只是「原文仓库」,方便运营改攻略不用发版。
  • 但检索要查的是「向量索引」,不是直接查 OSS 文件。所以「放 OSS」和「上向量库」不是二选一,是两层:OSS 存原文 + 向量库存索引。
  • 起步不必上向量库:现在关键词版连向量都不用(直接读 resources JSON)。真要上语义(L2)再引向量库。小规模甚至可以「内存向量」(启动时把几百条 embedding 加载进内存算余弦),省一个中间件。

选型建议(到 L2 时再定):项目已用 MySQL,pgvector 需要 Postgres(不匹配);可考虑 ES 向量字段(若已有 ES)或轻量 Qdrant/Milvus;规模很小时先「内存向量」。这属于 M4a 的决策点,到时单独出 ADR。


6. 在 TourMate 落地「景点攻略库」的建议 ​

你想自建景点攻略库(而不是用 WebSearch 实时查)——方向对,理由见《幻觉与 grounding》:护城河是真实资料、可控、便宜、不飘。落地建议:

  1. 内容先行(L1):把攻略库从 6 条扩成有价值的量(热门目的地各一篇结构化:最佳季节 / 怎么去 / 注意事项 / 玩法 / 周边)。内容质量 > 检索花哨——这步不需要任何新技术,却决定 80% 的体验。
  2. 顺手解决技术债①:攻略条目的 keywords 里补别名/常见错别字(「赛里木/赛里木胡/乌市」),关键词版立刻能扛一部分别名;语义层面的彻底解决等 L2 向量。
  3. 两个消费场景分清:
    • 给活动补「小贴士」(现状,SEARCH 命中后拼接)→ 保持轻量,不必上生成。
    • 给「出行计划 / 知识问答」当素材(MAKE_TRAVEL_PLAN / M4a)→ 这才需要 L3「回喂模型生成 + 引用 + 拒答」。
  4. 别一上来就向量库:先 L1 把内容做厚、L0 关键词继续用,等「知识问答」真要做了再上 L2/L3。

7. 成本与隔离 ​

  • 检索本身几乎零成本:关键词版读本地文件;向量版主要成本是「算 embedding」(一次性建库 + 每次 query 一次 embedding 调用,很便宜)。
  • 贵的是 L3 的「生成」那次 LLM 调用——和 MAKE_TRAVEL_PLAN 一样属于生成型,要配成本闸门(ADR-040)。
  • 隔离纪律(ADR-039):所有 RAG 代码只在 assistant 包内;IKnowledgeRetrievalPort 是唯一契约,Domain 不认向量库/embedding 具体类型。将来剥离助手 = 删包即可,业务域零改。

8. 附录 ​

8.1 术语对照 ​

术语中文说明
RAG检索增强生成检索资料 → 喂模型 → 生成答案
Retrieval检索从知识库找相关片段
Embedding嵌入向量文本的数字化语义指纹
Vector DB向量数据库存 embedding + 相似度检索
Chunk切块把长文切成适合检索的小段
Rerank重排精排对召回结果再用模型精排
Citation引用答案标明出处,可追溯
Prompt Injection提示注入资料/输入里藏指令篡改模型行为

8.2 常见疑问 ​

Q:查数据库活动算 RAG 吗? 不算。活动查询结果直接展示、不回喂模型,是 Tool Use。RAG 的关键是「检索到的知识回喂模型再生成」。

Q:一定要上向量库才算 RAG 吗? 不是。向量是「检索」的一种升级(语义)。就算用关键词检索,只要把结果回喂模型生成 + 引用 + 拒答,也是 RAG(只是检索精度低)。反过来,用了向量但只拼接不生成,仍不是真 RAG。

Q:原文放 OSS,检索快吗? 检索不查 OSS,查的是向量索引(或内存)。OSS 只存原文,供建库时读取和运营热更新。

8.3 项目内关联 ​

Powered by VitePress