第 4 章 / 共 12 章

RAG:让 AI 使用私有知识

RAG 是 AI 学习路线的第四章。

前三章分别解决了三个基础能力:Prompt 让需求表达更清楚,Structured Output 让输出可以被程序处理,Tool Calling 让 AI 可以调用外部工具。到了第四章,我们要解决一个更贴近真实项目的问题:如何让 AI 基于你的私有资料回答问题。

通用模型知道很多公开知识,但它并不了解你的公司文档、项目代码、内部接口、产品需求、测试用例、运维手册和历史决策。直接让模型回答这些问题,很容易出现猜测、漏答或编造。RAG 的作用,就是把真实资料检索出来,放进模型上下文里,让回答尽量基于可追溯的资料。

这一章要解决的问题是:

如何让 AI 基于文档、代码和知识库回答问题,并给出可检查的来源。

学完这一章,你应该能做到三件事:

  • 把文档切分、向量化并建立可检索知识库
  • 根据用户问题检索相关内容并拼接上下文
  • 评估回答是否基于资料、是否引用准确、是否降低幻觉

1. RAG 是什么#

RAG 全称是 Retrieval-Augmented Generation,意思是“检索增强生成”。

它的基本思路是:先从知识库中检索相关资料,再把资料交给模型生成回答。

典型流程如下:

用户提问
把问题转换成检索查询
从知识库中检索相关片段
筛选、排序、拼接上下文
把问题和上下文一起发给模型
模型基于上下文生成回答
输出答案和引用来源

示例:

用户:登录接口密码错误时会返回什么错误码?
RAG 系统检索:
- docs/api-login.md
- tests/auth/login-cases.md
- src/routes/auth.ts
模型基于检索结果回答:
密码错误时返回 401,错误码为 INVALID_PASSWORD,响应体包含 message 字段。来源:docs/api-login.md。

RAG 的核心价值,是让 AI 回答时有资料依据。

2. 为什么需要 RAG#

在真实项目里,很多问题只有内部资料里才有答案。

常见场景包括:

  • 根据产品需求回答功能规则
  • 根据接口文档生成调用示例
  • 根据代码库解释实现逻辑
  • 根据测试用例查覆盖情况
  • 根据运维手册定位部署问题
  • 根据公司制度回答流程问题
  • 根据历史会议纪要追溯决策原因

如果没有 RAG,模型只能依赖训练知识和当前对话上下文。对于私有资料、最新资料、项目细节,它很容易给出不可靠答案。

RAG 能带来几个直接收益:

  • 回答更贴近真实资料
  • 可以引用来源,方便核查
  • 知识库可以持续更新
  • 减少模型编造
  • 支持企业内部文档问答
  • 支持代码库理解和项目助手
  • 降低每次手动粘贴资料的成本

RAG 很适合需要“有据可查”的 AI 场景。

3. RAG 的基本组成#

一个基础 RAG 系统通常包含这些部分:

  • 原始资料
  • 文档解析
  • chunking
  • embedding
  • 向量数据库
  • vector search
  • rerank
  • context 拼接
  • 回答生成
  • 引用来源
  • 检索质量评估

可以把它分成两个阶段:

离线阶段:资料入库
原始文档 → 解析 → 切分 → embedding → 存入向量库
在线阶段:用户问答
用户问题 → embedding → 检索 → rerank → 拼接上下文 → 模型回答

离线阶段决定知识库质量,在线阶段决定回答质量。

4. 原始资料#

RAG 的效果首先取决于资料质量。

常见资料来源包括:

  • Markdown 文档
  • Word / PDF 文档
  • 接口文档
  • 产品需求文档
  • 设计稿说明
  • 代码文件
  • 测试用例
  • FAQ
  • 会议纪要
  • 工单记录
  • 数据库导出的知识条目

资料入库前要先整理几个问题:

  • 哪些资料允许被 AI 使用
  • 哪些资料需要脱敏
  • 哪些资料已经过期
  • 哪些资料有版本差异
  • 哪些资料需要保留来源路径
  • 哪些资料需要按权限隔离

RAG 不能自动修复脏资料。资料本身混乱,回答也会跟着混乱。

5. 文档解析#

文档解析的目标,是把不同格式的资料转换成可处理文本。

常见解析内容包括:

  • 标题
  • 正文
  • 表格
  • 列表
  • 代码块
  • 图片说明
  • 页码
  • 文件路径
  • 元数据

不同格式需要不同策略:

  • Markdown:保留标题层级、代码块和列表
  • PDF:注意分页、页眉页脚、表格错位
  • Word:注意标题样式、表格和批注
  • HTML:去掉导航、广告、脚本等无关内容
  • 代码:保留文件路径、函数名、类名和注释

解析质量会直接影响 chunking 和检索效果。

示例元数据:

{
"source": "docs/api-login.md",
"title": "登录接口说明",
"section": "错误码",
"page": null,
"updated_at": "2026-05-28"
}

元数据越完整,后续引用和过滤越方便。

6. chunking#

chunking 指的是把长文档切成适合检索的小片段。

模型上下文有限,向量检索也不适合直接处理整本文档。所以需要把资料切成 chunk。

常见切分方式:

  • 按固定字数切分
  • 按段落切分
  • 按标题层级切分
  • 按语义完整单元切分
  • 按代码函数或类切分
  • 按表格行或业务条目切分

6.1 固定长度切分#

固定长度切分实现简单。

示例:

每 800 字切一段,相邻段落重叠 100 字。

优点:

  • 实现简单
  • 适合快速原型
  • chunk 大小容易控制

缺点:

  • 可能切断句子
  • 可能切断表格或代码
  • 可能破坏语义完整性

6.2 按标题切分#

Markdown、Word、HTML 文档通常适合按标题切分。

示例:

# 登录接口
## 请求参数
## 响应字段
## 错误码

每个标题下的内容可以成为一个 chunk,并保留标题路径:

{
"source": "docs/api-login.md",
"heading_path": ["登录接口", "错误码"],
"content": "密码错误时返回 401,错误码 INVALID_PASSWORD。"
}

这种方式更容易保留语义边界。

6.3 按代码结构切分#

代码库 RAG 适合按文件、函数、类、接口切分。

示例:

{
"source": "src/routes/auth.ts",
"symbol": "loginHandler",
"kind": "function",
"content": "async function loginHandler(...) { ... }"
}

代码 chunk 最好保留:

  • 文件路径
  • 符号名
  • 类型
  • 注释
  • 导入关系
  • 调用关系

如果只是按固定长度切代码,容易切断函数和上下文。

6.4 chunk 大小#

chunk 太小,容易丢上下文。chunk 太大,容易带入无关内容。

常见经验值:

  • FAQ:100-300 字
  • 产品文档:400-1000 字
  • 技术文档:500-1200 字
  • 代码函数:按函数或类边界
  • 表格:按业务行或主题分组

可以先从 500-800 字开始,再根据检索效果调整。

7. embedding#

embedding 指的是把文本转换成向量。

向量可以理解成文本在语义空间里的坐标。意思相近的文本,向量距离通常更近。

示例:

文本 A:如何重置密码?
文本 B:忘记密码怎么办?

这两句话字面不同,但语义接近,embedding 后的向量距离应该比较近。

RAG 中通常会对两类内容生成 embedding:

  • 文档 chunk
  • 用户问题

检索时,把用户问题的向量和知识库中 chunk 的向量做相似度比较,找出最相关的资料。

embedding 需要注意:

  • 使用同一个 embedding 模型处理文档和问题
  • 入库后记录 embedding 模型版本
  • 文档更新后重新生成对应向量
  • 多语言场景要选择支持中文的模型
  • 长文本要先切分再生成 embedding

embedding 是语义检索的基础。

vector search 指的是向量检索。

常见流程:

用户问题 → 生成问题向量 → 在向量库中查相似 chunk → 返回 topK 结果

常见相似度指标包括:

  • cosine similarity
  • dot product
  • Euclidean distance

一般实现时不用手写底层算法,可以使用现成向量数据库或检索引擎。

常见选择包括:

  • PostgreSQL + pgvector
  • Elasticsearch / OpenSearch
  • Qdrant
  • Milvus
  • Weaviate
  • Chroma
  • SQLite 向量扩展

vector search 适合解决“语义相似”的问题。

示例:

用户问:用户登录失败有哪些原因?
可能检索到:
- 登录接口错误码
- 用户状态说明
- 密码校验逻辑
- 登录测试用例

9. 关键词检索和混合检索#

向量检索擅长语义相似,但对精确关键词有时不够敏感。

例如:

  • 错误码:E10032
  • 接口名:POST /api/v1/login
  • 函数名:createUserSession
  • 配置项:ENABLE_RAG_CACHE
  • 文件名:wrangler.toml

这类内容更适合关键词检索。

所以真实系统里常用 hybrid search,也就是混合检索:

向量检索 + 关键词检索 + 过滤条件

常见过滤条件包括:

  • 文档类型
  • 文件路径
  • 更新时间
  • 业务模块
  • 权限范围
  • 语言
  • 标签

示例:

{
"query": "登录失败错误码",
"filters": {
"module": "auth",
"doc_type": "api"
}
}

混合检索通常比单一向量检索更稳。

10. rerank#

初次检索返回的结果不一定最适合回答问题。rerank 的作用是对候选结果重新排序。

常见流程:

向量库先返回 top 30
reranker 对 top 30 重新评分
选出最相关的 top 5
拼接进模型上下文

rerank 可以改善这些问题:

  • 向量相似但内容不回答问题
  • 关键词命中但上下文无关
  • 多个 chunk 重复
  • 重要资料排名靠后

rerank 策略包括:

  • 使用 rerank 模型
  • 使用关键词命中加权
  • 使用标题和路径加权
  • 使用更新时间加权
  • 使用用户权限过滤
  • 去除重复或高度相似 chunk

RAG 系统复杂后,rerank 对回答质量影响很大。

11. context 拼接#

检索到资料后,需要把资料拼接成模型能理解的上下文。

拼接时要注意:

  • 保留来源路径
  • 保留标题层级
  • 控制总长度
  • 删除重复内容
  • 优先放高相关资料
  • 给每个片段编号
  • 区分资料内容和用户问题

示例:

请只基于以下资料回答问题。如果资料不足,请说明缺少哪些信息。
[资料 1]
来源:docs/api-login.md
标题:登录接口 / 错误码
内容:密码错误时返回 401,错误码 INVALID_PASSWORD。
[资料 2]
来源:tests/auth/login-cases.md
标题:密码错误测试
内容:输入错误密码时,预期返回 401,并提示用户名或密码错误。
用户问题:
登录接口密码错误时会返回什么?

这种格式能让模型更容易引用资料,也方便后续检查答案来源。

12. 引用来源#

RAG 回答最好带来源。

引用来源可以帮助用户:

  • 核对答案
  • 判断资料是否过期
  • 找到原文继续阅读
  • 追溯决策依据
  • 发现知识库缺口

常见引用信息包括:

  • 文件路径
  • 文档标题
  • 章节标题
  • 页码
  • chunk 编号
  • 更新时间
  • URL

示例回答:

密码错误时,登录接口返回 401,错误码为 INVALID_PASSWORD。
来源:
1. docs/api-login.md / 登录接口 / 错误码
2. tests/auth/login-cases.md / 密码错误测试

引用要和实际检索资料一致。不能在没有资料支撑的情况下编造来源。

13. 幻觉控制#

RAG 可以降低幻觉,但不能完全消除幻觉。

常见幻觉来源包括:

  • 检索结果不相关
  • 检索结果不完整
  • 资料本身过期
  • 多份资料互相矛盾
  • 模型忽略上下文
  • Prompt 允许自由发挥过多
  • 用户问题超出资料范围

控制方法:

  • 要求模型只基于资料回答
  • 资料不足时明确说无法确认
  • 回答中标注推断内容
  • 给结论附来源
  • 对关键字段做程序校验
  • 用 Eval 测试回答质量
  • 检测引用是否真实存在
  • 对高风险答案增加人工审核

示例 Prompt:

请只基于提供的资料回答问题。
如果资料中没有答案,请回答“资料不足,无法确认”。
如果需要推断,请单独标注为“推断”。
每个关键结论都要给出来源。

这个规则能明显减少无依据扩写。

14. 知识库更新#

RAG 系统必须处理资料更新。

常见更新场景:

  • 新增文档
  • 修改文档
  • 删除文档
  • 文档改名
  • 权限变化
  • 版本发布
  • 历史资料归档

更新策略包括:

  • 全量重建
  • 增量更新
  • 定时同步
  • 文件变更监听
  • 手动触发重建
  • 按文档版本保留历史

每个 chunk 建议记录:

  • source
  • content_hash
  • embedding_model
  • updated_at
  • version
  • permission_scope

示例:

{
"source": "docs/api-login.md",
"chunk_id": "api-login-error-codes-001",
"content_hash": "a1b2c3",
"embedding_model": "text-embedding-model-x",
"updated_at": "2026-05-28",
"version": "v1.3",
"permission_scope": "internal"
}

这样可以判断哪些 chunk 需要重新生成 embedding。

15. 权限和隐私#

RAG 很容易接触敏感资料,权限和隐私必须提前设计。

需要重点关注:

  • 用户是否有权限访问某份文档
  • 检索结果是否包含敏感信息
  • 回答是否泄露 token、密钥、手机号、身份证号等内容
  • 日志是否保存了敏感资料
  • embedding 数据是否可以被外部服务读取
  • 删除资料后,向量库是否同步删除

常见措施:

  • 入库前脱敏
  • 查询时按用户权限过滤
  • 敏感字段不入库
  • 高敏文档单独隔离
  • 回答前做敏感信息检测
  • 删除资料时同步删除 chunk 和向量
  • 记录访问审计日志

RAG 系统一旦进入企业内部资料,权限控制就是基础能力。

16. 检索质量评估#

RAG 系统需要评估检索质量。只看最终回答好不好,往往难以定位问题。

可以从两个层面评估:

  • 检索是否找到了正确资料
  • 回答是否正确使用了资料

常见指标包括:

  • Recall@K:正确资料是否出现在前 K 个结果里
  • Precision@K:前 K 个结果里有多少真正相关
  • MRR:第一个正确结果排名是否靠前
  • Answer Accuracy:回答是否正确
  • Citation Accuracy:引用是否支撑结论
  • Hallucination Rate:无依据内容比例

示例测试集:

[
{
"question": "登录接口密码错误时返回什么错误码?",
"expected_sources": ["docs/api-login.md"],
"expected_answer_keywords": ["401", "INVALID_PASSWORD"]
},
{
"question": "如何重置用户密码?",
"expected_sources": ["docs/user-password.md"],
"expected_answer_keywords": ["验证码", "新密码", "确认密码"]
}
]

有了测试集,才能判断 chunking、embedding、rerank、Prompt 调整后效果是否变好。

17. 常见 RAG 问题#

17.1 检索不到正确资料#

可能原因:

  • chunk 切得太碎
  • chunk 切得太大
  • embedding 模型不适合中文
  • 关键词内容没有命中
  • 文档没有成功入库
  • 查询改写效果差
  • 权限过滤过严

排查方法:

  • 直接搜索原文关键词
  • 查看 topK 检索结果
  • 检查 chunk 内容和元数据
  • 检查 embedding 模型版本
  • 临时放宽过滤条件
  • 对比向量检索和关键词检索结果

17.2 检索结果相关但回答错误#

可能原因:

  • Prompt 没有限制只基于资料回答
  • 上下文里有互相矛盾的资料
  • 模型忽略了关键片段
  • 引用和结论没有绑定
  • 问题需要多跳推理

处理方式:

  • 把资料编号
  • 要求每个结论附来源
  • 去除重复和过期资料
  • 增加 rerank
  • 对冲突资料提示模型说明差异

17.3 回答没有引用来源#

可能原因:

  • context 里没有保留来源
  • Prompt 没有要求引用
  • 输出格式没有来源字段
  • 模型把来源省略了

处理方式:

  • 在每个 chunk 前加来源信息
  • 输出格式中固定 sources 字段
  • 对无来源结论进行校验

17.4 知识库过期#

可能原因:

  • 文档更新后没有重新入库
  • 删除文档后向量库仍保留旧 chunk
  • 多版本文档混在一起
  • 没有记录更新时间

处理方式:

  • 增加 content_hash
  • 增加增量同步
  • 增加版本字段
  • 检索时优先使用最新资料
  • 对过期资料做归档或降权

18. RAG Prompt 示例#

基础问答 Prompt:

你是一个项目知识库问答助手。
请只基于提供的资料回答用户问题。
规则:
1. 如果资料中有明确答案,请给出简洁回答。
2. 如果资料不足,请回答“资料不足,无法确认”。
3. 如果资料之间存在冲突,请说明冲突点。
4. 每个关键结论都要标注来源。
5. 不要补充资料中没有出现的功能、字段或规则。
资料:
[资料 1]
来源:...
内容:...
[资料 2]
来源:...
内容:...
用户问题:
...

结构化输出 Prompt:

请根据资料回答问题,并输出 JSON。
输出格式:
{
"answer": "回答内容",
"confidence": "high | medium | low",
"sources": [
{
"source": "文件路径或 URL",
"section": "章节标题",
"evidence": "支撑结论的简短原文摘要"
}
],
"missing_information": ["资料不足时列出缺失信息"]
}
规则:
1. 只基于资料回答。
2. 没有来源支撑的内容不要写入 answer。
3. 资料不足时,confidence 使用 low。

RAG 和 Structured Output 结合后,答案更容易进入后续程序流程。

19. RAG 实战练习#

可以按下面顺序练习:

  1. 做一个本地 Markdown 文档问答系统
  2. 给每个回答附上来源路径
  3. 调整 chunk size,对比检索结果
  4. 给知识库增加关键词检索
  5. 实现向量检索和关键词检索的混合检索
  6. 给 topK 结果增加 rerank
  7. 做一个代码库问答系统
  8. 做一个产品需求知识库
  9. 设计 30 条 RAG 测试问题
  10. 统计回答准确率和引用准确率

每个练习都可以检查三个问题:

  • 是否检索到了正确资料
  • 回答是否基于资料
  • 来源是否能支撑结论

20. 阶段验收标准#

学完这一章,可以用下面的问题检查自己:

  • 我能不能把文档解析成可入库文本?
  • 我能不能选择合适的 chunking 策略?
  • 我能不能为 chunk 生成 embedding?
  • 我能不能使用 vector search 找到相关资料?
  • 我能不能结合关键词检索提升精确匹配能力?
  • 我能不能对检索结果 rerank?
  • 我能不能把检索结果拼接成清晰上下文?
  • 我能不能让回答带来源?
  • 我能不能处理资料不足和资料冲突?
  • 我能不能设计 RAG 测试集评估效果?

如果这些问题大多数都能做到,说明 RAG 阶段已经基本合格。

21. 本章总结#

RAG 的核心是把真实资料检索出来,交给模型基于资料回答问题。

一套可用的 RAG 系统,建议至少包含:

  • 清晰的资料范围
  • 稳定的文档解析
  • 合理的 chunking 策略
  • 可追踪的 embedding 入库流程
  • vector search
  • 关键词检索或混合检索
  • rerank
  • 清晰的 context 拼接
  • 引用来源
  • 幻觉控制
  • 知识库更新机制
  • 权限和隐私控制
  • 检索质量评估

RAG 是 AI 工程化路线中非常重要的一步。它让 AI 可以使用你的项目文档、代码、需求、测试用例和内部知识库。

这一章的目标很明确:让 AI 回答时有资料依据,让用户能检查来源,让系统能持续更新和评估。