LLMStart|持续课程 · 不追玄学
模块 4 · 4.3
进阶级70 分钟

4.3RAG 系统设计

本课只解决一个主问题:本单元只解决一个主问题:

如果要做一个真正可用的 RAG 问答系统,应该如何设计?

学习目标

学完本单元后,学习者应该能够:

  • 画出一个基础 RAG 系统架构。
  • 理解文档清洗、切分、索引、检索、重排、生成和引用的关系。
  • 解释查询改写和多轮对话检索为什么重要。
  • 识别 RAG 系统设计中的关键取舍。
  • 为一个小型知识库问答产品设计第一版方案。

先给直觉

很多人以为 RAG 系统就是:

文档丢进向量数据库,用户提问,模型回答。

这只是最粗糙的想象。

一个更真实的 RAG 系统像一个资料助理团队:

  1. 有人负责整理资料。
  2. 有人负责给资料建索引。
  3. 有人负责理解问题。
  4. 有人负责找相关资料。
  5. 有人负责筛掉无关内容。
  6. 有人负责根据资料写回答。
  7. 有人负责标注引用和检查质量。

RAG 的质量不是由某一个组件决定,而是由整条链路决定。

基础架构

一个基础 RAG 系统可以分成两条链路:

离线链路:资料进入系统
在线链路:用户提问并得到回答

离线链路

原始文档
  ↓
文档解析
  ↓
清洗与结构化
  ↓
Chunk 切分
  ↓
生成 Embedding
  ↓
存入索引

在线链路

用户问题
  ↓
查询理解 / 改写
  ↓
检索候选 Chunk
  ↓
重排
  ↓
组装上下文
  ↓
LLM 生成回答
  ↓
引用与质量检查
  ↓
返回用户

文档解析

原始资料可能来自:

  • Markdown
  • PDF
  • Word
  • 网页
  • 代码仓库
  • 表格
  • 产品文档
  • 企业制度
  • 聊天记录

文档解析要把这些资料变成系统能处理的文本和结构。

难点:

  • PDF 顺序错乱。
  • 表格丢失结构。
  • 网页有导航和广告噪音。
  • 扫描件需要 OCR。
  • 代码和注释需要保留层级。

如果解析阶段就错了,后面检索再努力也只是认真地找错资料。

文档清洗

清洗不是把内容删干净,而是去掉干扰信息,保留有价值结构。

常见清洗动作:

  • 删除页眉页脚。
  • 删除重复导航。
  • 去除广告和无关按钮。
  • 保留标题层级。
  • 保留列表结构。
  • 标准化空格和换行。
  • 标记表格、代码块和引用。

清洗目标是让文档更适合检索和生成。

Chunk 切分策略

切分不是按固定字数一刀切。

更好的切分应考虑:

  • 标题层级。
  • 段落边界。
  • 条款完整性。
  • 代码块完整性。
  • 表格结构。
  • 概念是否完整。

常见策略:

固定长度切分

简单,但容易切断语义。

适合快速原型。

按结构切分

按标题、段落、条款、列表切分。

更适合课程、文档、制度、说明书。

滑动窗口切分

相邻 Chunk 保留一定重叠,避免重要信息被切断。

代价是索引体积和重复内容增加。

元数据设计

元数据决定系统能不能做过滤、权限、引用和调试。

建议至少包含:

  • 文档 ID
  • 文档标题
  • 来源路径
  • 章节标题
  • 创建或更新时间
  • 内容类型
  • 权限等级
  • 语言
  • 课程模块或业务领域
  • Chunk 序号

元数据不是可有可无的装饰。没有元数据,系统出了问题很难定位。

检索策略

检索可以分几类。

向量检索

适合语义相似。

例如:

“模型为什么会胡说”

可以找到:

“幻觉与可靠性”

关键词检索

适合精确匹配。

例如:

  • 错误码
  • 文件名
  • 法条编号
  • 产品型号
  • 人名

混合检索

把向量检索和关键词检索结合起来。

很多实际系统会使用混合检索,因为用户问题既可能包含语义表达,也可能包含精确实体。

重排

初次检索会返回一批候选结果,但候选不一定排序最好。

重排的目标是:在候选结果中重新判断哪些最适合回答当前问题。

为什么需要重排?

  • 向量相似不等于答案相关。
  • 关键词命中不等于上下文正确。
  • 用户问题可能需要多个片段组合。
  • 一些片段看起来相似,但实际不支持结论。

重排可以使用专门模型、规则、元数据或 LLM 辅助。

上下文组装

检索到资料后,不能随便塞进 Prompt。

要考虑:

  • 按相关性排序。
  • 保留来源信息。
  • 控制总 Token 数。
  • 去除重复片段。
  • 合并同一章节相邻片段。
  • 明确告诉模型如何使用资料。

一个基础上下文模板:

你只能根据以下资料回答问题。
如果资料不足,请说明无法确定。

资料 1:
来源:...
内容:...

资料 2:
来源:...
内容:...

用户问题:
...

查询改写

用户问题常常不适合直接检索。

例子:

那它和微调有什么区别?

如果这是多轮对话,系统需要知道“它”指的是 RAG。

查询改写会把问题改成更适合检索的形式:

RAG 和微调有什么区别?

查询改写适合:

  • 多轮对话。
  • 代词很多的问题。
  • 问法口语化的问题。
  • 需要补全上下文的问题。

多轮对话中的检索

多轮对话比单轮问答更复杂。

系统需要判断:

  • 当前问题是否依赖历史对话。
  • 是否要重新检索。
  • 是否可以复用上轮资料。
  • 用户是否改变了主题。
  • 历史对话是否会引入错误上下文。

错误做法是把全部历史对话都塞给模型。

更好的做法是:

  1. 对当前问题做上下文补全。
  2. 判断是否需要检索。
  3. 检索最相关资料。
  4. 只保留必要历史。

引用与可追溯性

RAG 系统应该尽量给出引用。

引用的价值:

  • 用户能检查来源。
  • 系统能调试错误。
  • 内容更可信。
  • 高风险场景更容易审计。

引用要注意:

  • 引用必须真实存在。
  • 引用内容必须支持结论。
  • 不要把不相关资料硬塞成依据。
  • 引用粒度要合适,最好到章节或片段。

权限控制

企业 RAG 不能只考虑“能不能搜到”,还要考虑“用户有没有权看”。

权限控制应该发生在检索前或检索中,而不是最后靠模型自觉不说。

风险例子:

  • 普通员工问制度,检索到高管薪酬文件。
  • 客服问客户问题,检索到其他客户的隐私资料。
  • 学员问课程内容,系统返回内部未发布讲义。

模型不是权限系统。权限必须由系统设计保证。

基础评估

RAG 系统至少要评估两个层面:

检索评估

问题:

  • 正确资料有没有被找回来?
  • 排名靠前吗?
  • 是否有无关资料混入?

回答评估

问题:

  • 回答是否基于资料?
  • 是否遗漏关键点?
  • 是否引用正确?
  • 是否承认资料不足?
  • 是否出现幻觉?

没有评估的 RAG 系统,很容易只在 Demo 时看起来不错。

案例:课程问答系统第一版

目标:

让学员基于 LLMCourse 课程内容提问。

第一版设计:

  1. 文档来源:只读取课程 lesson.md 和模块 README.md
  2. 文档解析:保留标题层级。
  3. Chunk:按二级/三级标题切分,必要时保留重叠。
  4. 元数据:模块、单元、标题、路径、难度。
  5. 检索:向量检索 + 标题关键词匹配。
  6. 重排:优先同模块内容,优先课程单元正文。
  7. 生成:要求只根据课程资料回答。
  8. 引用:返回课程文件路径和小标题。
  9. 失败处理:资料不足时明确说明。

这比“把所有 Markdown 塞进模型”更可维护。

常见误区

误区 1:RAG 就是向量数据库

向量数据库只是一个组件,不是整个系统。

误区 2:切分越细越好

太细会丢上下文。切分要服务问题和文档结构。

误区 3:检索到资料就一定能回答

还要看资料是否支持结论、模型是否正确理解、引用是否准确。

误区 4:权限可以交给模型判断

不能。权限必须在系统层控制。

误区 5:Demo 能回答几个问题,就说明系统可靠

真实系统要有评估集、日志、错误分析和持续优化。

动手练习

为本课程设计一个 RAG 问答系统第一版。

填写:

项目 你的设计
文档范围
Chunk 切分方式
元数据字段
检索方式
是否需要重排
引用粒度
权限规则
资料不足时怎么回答
如何评估
检查题(自测)
  1. RAG 的离线链路和在线链路分别做什么?
  2. 为什么元数据设计很重要?
  3. 为什么权限控制不能交给模型自己处理?
参考答案
  1. 离线链路:资料收集、清洗、切分、向量化、入库,把知识变成可检索的资产;在线链路:查询改写、检索、重排、组装 Prompt、生成回答、标注引用。
  2. 元数据(来源、时间、权限、类型)支撑三件事:过滤检索范围、生成可追溯引用、控制访问权限;没有元数据的知识库难以治理。
  3. 模型会幻觉、可能被 Prompt Injection 影响,无法可靠地执行权限逻辑。权限必须在系统层用代码强制,而不是指望模型"守规矩"。

Takeaway

RAG 系统不是“文档 + 向量库 + LLM”这么简单。真正可用的 RAG 需要文档处理、切分、元数据、检索、重排、上下文组装、引用、权限和评估一起工作。哪一环偷懒,哪一环就可能在用户面前替你表演。