Day 19:RAG 问答链路 v1

学习目标

经过前三天的学习,我们已经掌握了 RAG 系统的各个组件:文档解析与清洗、文本切分、Embedding 和向量数据库。今天是把这些组件串起来的一天——构建一个端到端的 RAG 问答链路。

这个 v1 版本不追求完美,追求的是”跑通”。从用户提问到获得答案,整个链路能走通、能回答基本问题、能显示引用来源。有了这个基线版本,明天才能在上面做优化。

学完今天,你将拥有一个可以演示的 RAG Demo。它可能还不够好,可能有些问题回答不了,但它是一个真实的、可运行的系统。拿这个 Demo 给别人看,别人能理解 RAG 是什么、能做什么。


核心概念

一、用户问题处理

用户问题处理是 RAG 在线管线的第一个环节。它的任务不只是”拿到问题文本”,还包括一些必要的预处理。

问题清洗。 用户的输入可能包含多余的空格、特殊字符、表情符号等。虽然这些问题不影响 Embedding 的效果(模型本身有一定的鲁棒性),但在展示和日志记录时,干净的问题文本更易读。

问题分类。 不是所有用户输入都是适合 RAG 回答的问题。有些是闲聊(“你好”),有些是命令(“帮我生成一份报告”),有些是超出知识库范围的提问(“今天天气怎么样”)。在 MVP 阶段可以不做分类,但在生产系统中,需要判断用户输入是否是”知识库问答”类型的请求。如果不是,直接走其他处理路径。

问题补全。 在对话场景中,用户的提问可能包含代词或省略。比如用户先问了”退货政策是什么”,接着问”那换货呢?“。第二个问题的”换货”需要结合上下文理解。在 v1 版本中可以不处理多轮对话,但至少要意识到这个问题的存在。

工程建议。 v1 阶段保持简单:接收问题文本,做基本清洗(去除首尾空白、合并多余空格),然后直接进入向量化环节。对话上下文补全、问题分类等高级功能留到 v2。

二、问题向量化

把用户的问题通过 Embedding 模型转换为向量。这和 Day 18 离线阶段对 Chunk 做的向量化是同一个操作,使用同一个模型。

需要注意的点:

  • 模型一致性。 必须使用和文档入库时相同的 Embedding 模型。这个在 Day 18 已经强调过了,但它是如此重要,值得再提醒一次。
  • 单条 vs 批量。 在线查询通常一次只处理一个问题,不需要批量。但如果你要支持高并发(多个用户同时提问),需要考虑 Embedding API 的并发能力和速率限制。
  • 缓存优化。 如果多个用户问了相同或非常相似的问题,可以缓存问题的向量,避免重复调用 Embedding API。这是一个可选优化,v1 可以不做。
  • 延迟记录。 记录问题向量化的耗时,作为系统性能监控的基础数据。通常 Embedding API 的响应时间在 50-200ms。

三、检索 top-k

用问题向量在向量数据库中检索最相似的 k 个 Chunk。

参数选择。 v1 阶段建议 k=3 或 k=5。这个值不需要过度优化,先用一个中间值,后面根据测试效果调整。

相似度阈值。 top-k 检索总是会返回 k 个结果,即使这些结果和问题一点也不相关。你需要设定一个相似度阈值(比如余弦相似度 0.5),低于阈值的结果视为”未检索到相关内容”。这样当知识库中确实没有答案时,系统可以诚实地回答”未找到相关信息”,而不是硬凑一个不相关的答案。

检索结果的格式。 每个检索结果应该包含:

  • Chunk 的原始文本
  • 相似度分数
  • 元数据(来源文档、章节、页码等)

这些信息在后续环节都会用到。

检索失败的判断。 什么情况算”检索失败”?

  • 所有 top-k 结果的相似度分数都低于阈值
  • 检索到的 Chunk 和问题明显不相关(可以通过人工抽样判断)
  • 向量数据库返回错误(技术故障)

v1 阶段对检索失败的处理可以简单一些:告诉用户”未找到相关信息”。后续版本可以更智能地处理,比如扩大搜索范围、换一种检索方式等。

四、上下文拼接

把检索到的 k 个 Chunk 的文本拼接起来,作为 Prompt 的上下文部分。

拼接格式。 常见的做法是给每个 Chunk 加上序号和来源标注,然后用分隔符隔开。比如:

[参考资料 1](来源:《产品手册》第 3 章"退货政策")
退货申请提交后,客服团队将在 1 个工作日内审核...

[参考资料 2](来源:《售后服务指南》第 2 章"退款流程")
审核通过后,退款将在 3 个工作日内原路退回...

[参考资料 3](来源:《产品手册》第 3 章"退货政策")
特殊情况下,退款可能延长至 7 个工作日...

这样拼接的好处是:模型知道每段内容的来源(有助于引用标注),用户也能看到答案参考了哪些资料。

上下文长度控制。 检索到的 k 个 Chunk 的总字符数可能很长。如果超过模型上下文窗口的一半,需要截断。为什么是一半而不是全部?因为 Prompt 中还需要留空间给问题和生成指令,以及模型的回答本身。

截断策略:按相似度分数排序,从最相似的开始保留,直到总字符数达到上限。低相似度的 Chunk 被优先截断。

去重。 有时检索到的多个 Chunk 来自同一篇文档的相邻段落,内容高度重复(因为有 Overlap)。在拼接前可以做一次简单的去重:如果两个 Chunk 的文本重复率超过某个阈值(比如 80%),只保留相似度更高的那个。

五、Prompt 组装

把拼接好的上下文和用户问题组装成完整的 Prompt。

Prompt 的结构设计。 一个标准的 RAG Prompt 通常包含以下部分:

系统指令。 告诉模型它的角色和回答规则。比如:

“你是一个知识库问答助手。你的任务是基于提供的参考资料回答用户的问题。请遵循以下规则:1. 只基于参考资料中的信息回答,不要使用你自己的知识。2. 如果参考资料中没有相关信息,请明确说明。3. 在回答中标注信息来源。”

参考资料。 上一环节拼接好的上下文文本。

用户问题。 用户提出的问题。

回答指令。 引导模型按特定格式回答。

Prompt 设计的原则:

  • 明确约束。 告诉模型”只基于参考资料回答”,这是减少幻觉的关键指令。虽然模型不一定 100% 遵守,但明确约束比不约束效果好很多。
  • 明确失败处理。 告诉模型”如果没有相关信息,说’未找到相关信息’“。这比让模型自由发挥要好。
  • 格式指引。 如果需要特定格式(比如分点回答、标注引用编号),在 Prompt 中给出格式模板和示例。

和 Week 2 的衔接。 Week 2 我们学了 Prompt Contract 和结构化输出。这些能力在 RAG Prompt 中同样适用。你可以要求模型以 JSON 格式输出,包含 answer(答案正文)和 citations(引用列表)两个字段。这样下游系统可以程序化地处理答案。

六、答案生成

调用大模型,传入组装好的 Prompt,获取答案。

模型选择。 RAG 答案生成对模型的要求:

  • 需要好的中文理解能力
  • 需要能严格遵循指令(“只基于参考资料回答”)
  • 不需要特别强的推理能力(因为推理主要基于提供的上下文)

通常中等能力的模型就够用了(比如 GPT-4o-mini、Claude Haiku 级别)。用最强的模型当然更好,但成本也更高。在 v1 阶段先用性价比高的模型,后续根据效果决定是否升级。

Temperature 设置。 RAG 问答的答案应该基于事实,不需要创意发挥。建议 Temperature 设为 0 或很低(0.1-0.3)。低 Temperature 让模型的输出更确定、更稳定、更贴近参考资料。

流式输出。 如果前端支持,建议使用流式输出(Streaming)。RAG 的答案通常比较长,用户等待完整答案生成可能需要几秒。流式输出让用户可以逐步看到答案,体验更好。

七、引用来源

在每个答案中标注信息来源,是 RAG 系统区别于普通问答的重要特征。

引用的两种形式:

内联引用。 在答案正文中直接标注。比如:“退货申请提交后,客服团队将在 1 个工作日内审核[参考资料 1]。审核通过后,退款将在 3 个工作日内原路退回[参考资料 2]。”

引用列表。 在答案末尾列出所有引用的资料。比如:“参考来源:[1]《产品手册》第 3 章;[2]《售后服务指南》第 2 章。”

两种形式可以结合使用:答案正文中用编号引用,末尾附完整的来源列表。

引用的实现方式。

如果 Prompt 要求模型标注引用,大多数模型能在答案中加入引用标记。但引用的准确性不是 100%,模型可能标注了不相关的来源,或者遗漏了实际使用的来源。

对于 v1 版本,接受引用可能不完全准确的现实。后续可以通过后处理来校验引用的准确性。

八、答案保存

答案生成后需要保存,用于日志记录、效果评估和后续优化。

应该保存的内容:

  • 用户问题
  • 检索到的 top-k Chunk(文本、分数、元数据)
  • 完整的 Prompt(包含上下文和指令)
  • 模型生成的答案
  • 引用来源
  • 时间戳
  • 处理耗时(每个环节的耗时)

保存的用途:

  • 效果评估。 通过历史记录分析系统的回答质量,发现检索不准或生成不佳的案例。
  • 问题诊断。 当用户反馈”回答不对”时,可以回溯查看是检索的问题还是生成的问题。
  • Prompt 优化。 分析 Prompt 和答案的对应关系,找到 Prompt 可以改进的地方。
  • 成本统计。 通过保存的 Token 使用记录,统计系统的运行成本。

存储方式。 v1 阶段可以用 JSON 文件或 SQLite 数据库。生产环境建议用正式的数据库(PostgreSQL 等)。


概念关系图

RAG 问答链路 v1 完整流程
=========================================================

[用户提问]
    |
    v
[问题预处理] -- 清洗、补全(v1 可选)
    |
    v
[问题向量化] -- Embedding API 调用
    |
    v
[向量检索] --- 向量数据库 top-k 查询
    |               |
    |               +-- 相似度分数
    |               +-- Chunk 文本
    |               +-- Chunk 元数据
    v
[检索结果评估]
    |
    +-- 分数低于阈值? --> [返回:未找到相关信息]
    |
    +-- 分数合格? -------> 继续
    |
    v
[上下文拼接] -- Chunk 文本 + 来源标注
    |
    v
[Prompt 组装] -- 系统指令 + 上下文 + 问题 + 格式要求
    |
    v
[LLM 调用] ---- 模型生成答案
    |
    v
[后处理] ------ 引用标注 + 格式化
    |
    v
[返回答案] ---- 答案正文 + 引用来源
    |
    v
[保存记录] ---- 问答日志 + 检索记录 + 耗时统计


Prompt 组装结构
=========================================================

+-----------------------------------------------+
| System: 你是知识库问答助手                      |
| 规则:只基于参考资料回答,标注来源              |
|                                               |
| 参考资料:                                     |
| [1] 来源:XX文档,第X章                        |
|     退款流程如下...                            |
| [2] 来源:XX文档,第X章                        |
|     退货条件是...                              |
| [3] 来源:XX文档,第X章                        |
|     特殊情况...                                |
|                                               |
| 用户问题:退款需要多久?                        |
|                                               |
| 请基于以上参考资料回答,标注引用编号。          |
+-----------------------------------------------+
        |
        v
[模型生成]
        |
        v
"退款将在 3 个工作日内完成[2]。特殊情况可能延长至
 7 个工作日[3]。

 参考来源:
 [2]《售后服务指南》第 2 章"退款流程"
 [3]《产品手册》第 3 章"退货政策""

实战分析

任务一:完成最小 RAG 问答

把前三天的所有组件串起来,完成一个端到端的 RAG 问答。

需要的组件:

  • Day 16 产出的:清洗后的文档文本
  • Day 17 产出的:切分好的 Chunk(带元数据)
  • Day 18 产出的:Chunk 向量(存入向量数据库)
  • 今天新做的:检索 + Prompt + 生成的在线管线

端到端验证流程:

  1. 输入一个问题(比如”退货的流程是什么”)
  2. 问题经过向量化
  3. 在向量数据库中检索 top-3
  4. 检查检索到的 Chunk 是否包含相关信息
  5. 拼接上下文、组装 Prompt
  6. 调用大模型生成答案
  7. 检查答案是否正确、是否引用了正确的来源

如果一切顺利,你应该能获得一个基于文档内容的准确回答。第一次跑通的时候,你会真切感受到 RAG 系统的价值。

任务二:上传 3 篇文档

选择 3 篇不同类型的文档,完成从上传到可检索的全流程。

文档选择建议:

  • 一篇产品手册或技术文档(有明确标题结构,适合按标题切分)
  • 一份 FAQ 文档(问答对格式,天然适合 RAG)
  • 一份制度文件或规范(可能比较枯燥,但是企业知识库的典型内容)

对每篇文档完成:

  1. 解析和清洗(Day 16 的流程)
  2. 切分(Day 17 的流程)
  3. 向量化和入库(Day 18 的流程)
  4. 验证检索效果(用 2-3 个问题测试)

任务三:提问 20 个问题

准备 20 个测试问题,覆盖不同的类型和难度。

问题类型分布建议:

  • 5 个简单事实型问题(有明确答案,如”退款 SLA 是几天”)
  • 5 个流程型问题(需要描述步骤,如”退货流程是什么”)
  • 5 个条件判断型问题(需要结合多个信息判断,如”购买超过 30 天还能退货吗”)
  • 3 个跨文档问题(答案可能在不同文档中)
  • 2 个知识库范围外的问题(测试系统的失败处理能力)

记录每个问题的测试结果:

  • 检索到了哪些 Chunk(文本摘要、相似度分数)
  • 模型生成的答案
  • 答案是否正确
  • 引用是否准确
  • 如果回答错了,分析是哪个环节出了问题

任务四:输出带引用答案

确保每个答案都包含引用来源。

检查引用的准确性。 对于每个答案中的引用,验证引用的 Chunk 确实包含了答案所表述的信息。如果答案说”退款需要 3 个工作日[2]“,检查参考资料 2 中是否确实说了 3 个工作日。

引用标注的完整性。 答案中的每个关键信息点都应该有引用。如果一个答案包含三个关键信息但只有两个有引用,说明有一个信息点可能是模型自行补充的(潜在的幻觉)。


当日产物说明

产物一:《RAG Demo v1》

这是一个可运行的 RAG 问答系统。

应该包含:

  • 文档上传和索引功能(把文档处理成可检索的状态)
  • 问答功能(输入问题,输出带引用的答案)
  • 3 篇已索引的测试文档
  • 20 个测试问题的完整记录

质量标准: Demo 可以演示给他人看。输入一个问题,能在 10 秒内返回带引用的答案。20 个测试问题中,简单事实型问题的正确率不低于 80%。

产物二:《20 个测试问题》

这是一份测试问题集和对应的答案记录。

应该包含:

  • 每个问题的类型标注(事实型/流程型/条件判断型/跨文档型/范围外型)
  • 每个问题的正确答案(人工标注,基于文档内容)
  • 每个问题的系统回答
  • 正确性判断(正确/部分正确/错误/无法回答)
  • 错误分析(如果是错误的,分析哪个环节出了问题)

质量标准: 问题覆盖多种类型,正确性判断客观,错误分析能定位到具体环节。

产物三:《答案引用记录》

这是一份引用准确性的分析记录。

应该包含:

  • 每个答案的引用列表
  • 每个引用的准确性判断(引用的 Chunk 是否确实包含对应信息)
  • 引用遗漏记录(答案中有信息点但没有引用的情况)
  • 引用错误记录(引用的 Chunk 不包含对应信息的情况)

质量标准: 20 个问题中,至少 15 个问题的引用是准确的。不准确的情况有明确的分析。


常见误区与避坑

误区一:直接让模型回答,不管检索结果

有时候检索到的 Chunk 和问题关系不大,模型却还是会基于这些不相关的内容硬编一个答案。这就是”强行回答”的问题。

解决方案在 Prompt 中明确告诉模型:“如果参考资料中没有相关信息,请回答’根据现有资料未能找到相关信息’“。这比硬编一个答案好得多。

有些开发者不敢让系统回答”不知道”,怕影响用户体验。但实际上,一个诚实的”不知道”比一个自信的错误答案要好一万倍。用户可以换一种方式提问,但如果系统给出了错误答案,用户可能会基于错误信息做出决策。

误区二:Prompt 写一次就不管了

RAG Prompt 需要持续迭代。你写了第一版 Prompt,跑了几十个测试问题,会发现有些情况 Prompt 处理不好。比如模型没有严格基于参考资料回答、引用标注不准确、答案格式不统一等等。

每发现一个问题,就调整 Prompt,重新测试。建立一个 Prompt 版本记录,记录每次修改的内容和原因。这和 Week 2 学的 Prompt 管理是一脉相承的。

误区三:不记录中间过程

很多开发者只保存最终的答案,不保存检索到的 Chunk、组装的 Prompt、模型的原始输出。当需要分析问题(“为什么这个问题回答错了”)时,发现没有足够的信息来定位原因。

RAG 系统是一个多环节的管线,任何一个环节都可能是问题所在。只有记录了每个环节的输入和输出,才能有效地排查问题。

误区四:用同一个问题反复测试

用同一组问题反复测试 RAG 系统会让你产生”效果很好”的错觉,因为你可能会不自觉地针对这些问题优化 Prompt 和参数,导致系统在这些特定问题上表现很好,但在新问题上表现很差。

定期更换测试问题集,引入新的、未见过的问题来评估系统的真实水平。最好是让其他人(不了解系统实现的人)来提问,他们的问题表述方式可能和你完全不同。

误区五:忽略响应时间

RAG 问答链路涉及多个 API 调用:Embedding API(问题向量化)、向量数据库查询、LLM API(答案生成)。每个调用都有延迟,叠加起来可能好几秒。

用户对问答系统的响应时间有心理预期。通常 2-3 秒内是可以接受的,超过 5 秒就会觉得慢,超过 10 秒可能就不想等了。

在开发过程中关注每个环节的耗时:

  • 问题向量化:通常 50-200ms
  • 向量检索:通常 10-100ms(取决于数据规模和索引质量)
  • Prompt 组装:几乎为 0(本地字符串操作)
  • 答案生成:通常 2-10 秒(取决于答案长度和模型速度)

答案生成通常是瓶颈。如果总时间太长,可以考虑:用更快的模型、用流式输出、减少输入上下文的长度。


延伸思考

从 v1 到 v2 的优化方向

今天的 v1 版本是一个基线。跑通之后,你会清晰地看到它的不足:

  • 有些问题检索不到相关内容(检索质量需要优化)
  • 有些问题的答案不够准确(生成质量需要优化)
  • 有些问题的答案缺少上下文(切分策略需要调整)
  • 有些问题的检索结果包含太多噪声(需要 Rerank)
  • 有些问题的表述不够清晰(需要 Query Rewrite)

明天的 Day 20 就是要解决这些问题。但优化之前,你需要有 v1 的基线数据:哪些问题回答对了、哪些回答错了、错在哪里。没有基线,就没有办法量化优化的效果。

RAG 问答和 Week 2 API 调用的关系

今天的 RAG 问答链路,本质上就是在 Week 2 学的 API 调用基础上,增加了一个”检索”环节。模型调用本身没有什么变化,变化的是 Prompt 的内容——不再是简单的”回答这个问题”,而是”基于这些参考资料回答这个问题”。

所以 Week 2 的所有知识(API 调用、错误处理、重试机制、结构化输出、日志记录)在 RAG 系统中全部用得上。RAG 不是全新的东西,而是在已有能力上的系统级组装。

企业 Demo 的演示策略

当你拿着这个 RAG Demo 去给客户演示时,有几个策略:

选好演示文档。 用和客户行业相关的文档做演示。如果客户是制造业,就用制造业的文档;如果是金融,就用金融的文档。相关性越强,说服力越大。

准备好演示问题。 提前准备 5-10 个能完美回答的问题,用来展示系统的能力。但同时准备 2-3个”故意刁钻”的问题,用来展示系统在遇到无法回答的问题时能诚实地说”不知道”。这比只展示成功案例更有说服力,因为它展示了系统的可靠性。

展示引用来源。 引用来源是 RAG 相比普通问答的核心优势。演示时强调”每个答案都可以追溯到具体的文档位置”,这对企业客户非常有吸引力。


自测问题

  1. 描述 RAG 问答链路 v1 的完整流程,从用户提问到返回答案,列出每个步骤的输入和输出。

  2. 为什么 Prompt 中要明确告诉模型”只基于参考资料回答”?不写这条指令会怎样?

  3. 上下文拼接时,如何处理检索到的 Chunk 总长度超过模型上下文窗口的情况?

  4. 引用来源的两种形式是什么?为什么引用来源对 RAG 系统特别重要?

  5. 答案保存应该包含哪些内容?这些内容分别用于什么目的?

  6. 如果检索到的 top-3 Chunk 的相似度分数都很低(低于 0.3),系统应该怎么处理?为什么?

  7. RAG Prompt 的 Temperature 应该怎么设置?为什么和普通对话不同?

  8. 你用 20 个问题测试了 RAG 系统,其中 8 个回答不正确。你怎么判断是检索的问题还是生成的问题?

  9. 为什么说”一个诚实的’不知道’比一个自信的错误答案好一万倍”?

  10. RAG 问答链路的响应时间主要花在哪些环节?怎么优化?


关键词

  • 用户问题处理:对用户输入进行清洗、分类、补全的预处理环节
  • 问题向量化:将用户问题通过 Embedding 模型转换为向量,用于检索
  • 上下文拼接:将检索到的多个 Chunk 文本按格式组装成 Prompt 的上下文部分
  • Prompt 组装:将系统指令、上下文、问题和格式要求组装成完整的 Prompt
  • 答案生成:大模型基于增强后的 Prompt 生成回答的过程
  • 引用来源(Citation):在答案中标注信息来自哪个文档的哪个部分
  • 相似度阈值:用于判断检索结果是否足够相关的最低相似度分数
  • 答案保存:记录问答过程的所有中间数据,用于评估和诊断
  • RAG Demo v1:第一个端到端可运行的 RAG 问答系统,作为后续优化的基线
  • 检索失败处理:当检索不到相关内容时,系统选择诚实告知而非强行回答