RAG 系统设计:让 LLM 用好你的私有数据

RAG(Retrieval-Augmented Generation,检索增强生成) 是让 LLM 用好私有数据的主流方案:在生成回答前,先从你的知识库里检索出相关片段,塞进 prompt 让模型「看着资料答题」。它解决了 LLM 的三个硬伤——知识截止、幻觉、无法访问私有数据。
本文脉络:
- 为什么需要 RAG:LLM 的三个硬伤
- 为什么不直接微调:RAG vs Fine-tuning
- RAG 的完整架构:检索 → 增强 → 生成
- 文档处理:chunking 是 RAG 成败的关键
- Embedding:把文本变成向量
- 向量检索:相似度 + Top-K
- Reranking:把最相关的排到前面
- Prompt 拼装与生成
- 评估 RAG 系统
- 生产实践与常见陷阱
- 常见问题
一、为什么需要 RAG:LLM 的三个硬伤
直接用 LLM 回答私有领域问题,会撞上三个硬伤:
- 知识截止:模型的训练数据有截止日期,问它「昨天发布的 API」它不知道。公司内部 API 文档、最新操作手册、刚更新的客服政策,模型训练时往往根本没见过。
- 幻觉:不知道却硬编。比如问「我们公司的报销流程」,它可能给出一套看似合理的通用流程,但细节全错。
- 无法访问私有数据:模型看不到你的数据库、文档库、内部 wiki。更现实的是,你也不可能把公司全部资料塞进训练集:成本高,更新慢,权限也很难控制。
RAG 的解法很朴素:既然模型不知道,那就把相关资料翻出来给它看,让它「开卷答题」。
不用 RAG 时,用户问「我们公司的退货政策是什么?」,LLM 没见过内部文档,就很容易套用通用经验,回答「通常 7 天无理由退货……」。这类答案看起来顺,但业务上可能完全错误。
用 RAG 时,系统会先从知识库检索「退货政策」相关片段,再把片段塞进 prompt:根据以下资料回答:[片段] 问题是……。此时模型是在看着资料回答,例如「根据《售后政策 v3》,30 天内可退……」。
RAG 的本质:给 LLM 装上一个「外挂知识库 + 检索入口」,让它每次回答前先查资料。这比把所有知识塞进模型参数(微调)灵活得多——资料更新了,改知识库就行,不用重新训练。
二、为什么不直接微调:RAG vs Fine-tuning
很多人会问:让模型学会私有知识,为什么不直接微调?这是 RAG 必须先回答的问题。
| 维度 | RAG(检索增强) | Fine-tuning(微调) |
|---|---|---|
| 解决的问题 | 知识的「可访问性」(给资料让它查) | 能力的「内化」(让它学会某种风格/能力) |
| 数据更新 | 改知识库即可,秒级生效 | 要重新训练,成本高 |
| 知识可追溯 | ✅ 能引用来源(哪段资料说的) | ❌ 知识融进权重,无法溯源 |
| 幻觉控制 | 好(有资料约束) | 差(还是会编) |
| 成本 | 检索 + 推理,每次请求都付 | 训练贵,推理便宜一点 |
| 适合 | 事实性知识、文档问答、私有数据 | 风格、格式、特定任务的能力 |
一句话区分:
RAG 解决「知道什么」:给它资料库,让它查。微调解决「怎么做」:让它内化某种能力或风格。
举个例子:让模型用你们公司的术语和风格写文档,更像微调;让模型回答你们公司的具体政策,更像 RAG。两者也经常组合使用:微调改风格,RAG 给知识。
常见误区是以为「微调能让模型记住所有私有知识」。实际情况是,微调更擅长学习风格和能力,事实性知识一旦变化就会过期,而且无法溯源。知识类需求优先考虑 RAG。
结论:本文聚焦 RAG——它是「让 LLM 用好私有数据」的主流且首选方案。
三、RAG 的完整架构:检索 → 增强 → 生成
RAG 分三个阶段,对应名字的三个字母:
Retrieval(检索):从知识库找出和问题相关的文档片段。典型链路是「问题 → Embedding → 向量检索 → Top-K」。
Augmented(增强):把检索到的片段塞进 prompt,作为模型回答时的参考资料。
Generation(生成):LLM 看着资料生成答案,并尽可能标注引用来源。
但这只是在线推理时的链路。一个完整的 RAG 系统,还需要一条离线的「建库」链路:原始文档先经过清洗、分块、向量化,再写入向量库;用户查询时,再用同一个 Embedding 模型把问题转成向量,检索、重排、拼 prompt,最后生成答案。
下面六章按这个链路顺序展开。
四、文档处理:chunking 是 RAG 成败的关键
很多人以为 RAG 的难点在检索算法,其实真正决定效果的是分块(chunking)——分得不好,检索再准也白搭。
4.1 为什么必须分块
不直接把整篇文档向量化,主要有三个原因:
- 长度限制:Embedding 模型有输入长度上限,长文档必须拆开处理。
- 精度问题:整篇文档一个向量,语义会被稀释。一篇文档讲了 10 件事,向量很难表达清楚哪一段才和问题相关。
- 窗口约束:检索回来的内容最终要塞进 prompt,整篇文档太大,既贵又容易引入噪声。
所以更合理的做法是把文档切成小块,每块单独向量化、单独检索。在线查询时只取最相关的几块,既精准,也省 token。
4.2 四种分块策略
| 策略 | 做法 | 优点 | 风险 | 适合场景 |
|---|---|---|---|---|
| 固定长度分块 | 每 N 个字符或 token 切一刀 | 实现简单 | 可能切断句子、段落,破坏语义 | 格式统一的纯文本 |
| 语义边界分块 | 按标题、段落、句子切 | 语义完整,检索精度高 | 块大小不均,需要做上限控制 | 大多数文章、文档站 |
| 滑动窗口 + 重叠 | 相邻块保留一部分 overlap | 避免边界信息丢失 | 存储和检索成本略增 | 代码、步骤说明 |
| 递归 / 层级分块 | 章节 → 段落 → 句子,多层切分 | 兼顾精准检索和上下文完整 | 实现复杂 | 手册、论文、长文档 |
4.3 chunk 大小怎么选
chunk 太大时,一个块里会混进多件事,向量语义变得混杂,检索不准;同时塞进 prompt 的 token 也更多,成本更高。
chunk 太小时,语义可能不完整。检索虽然命中了,但拿到的是半句话、半个步骤,模型仍然答不好,还需要检索更多块才能凑齐上下文。
实践上可以从这些经验值开始:
- 一般文档:200-500 字,或 256-512 tokens。
- 代码和步骤说明:按完整逻辑单元切,比如一个函数、一个配置段、一个操作步骤。
- overlap 通常设为 10%~20%,用来减少边界信息丢失。
4.4 chunking 是 RAG 最容易被忽视的优化点
很多团队一看到检索效果差,就先换更强的 Embedding 模型、换更贵的向量库。但真实原因往往更基础:分块没切好。
例如文档里有一句「退货政策:30 天内可退。需要保留小票。」如果固定长度切块,把这句和后面无关内容拼在一起,检索「退货」时向量会被无关内容稀释。按语义边界切,把「退货政策」单独成块,检索就会精准很多。
经验是:RAG 调优,先调 chunking,再调检索,最后才考虑换模型。
五、Embedding:把文本变成向量
分块后,要把每个 chunk 变成向量,才能做相似度检索。
5.1 Embedding 是什么
Embedding 会把文本映射成高维向量,比如 768 维或 1536 维。语义相近的文本,向量也会更接近。
| 文本 | 向量示意 | 语义关系 |
|---|---|---|
| 退货政策 | [0.21, -0.34, 0.88, ..., 0.12] |
和 return policy 接近 |
| return policy | [0.19, -0.31, 0.85, ..., 0.10] |
和退货政策接近 |
| 今天天气不错 | [-0.50, 0.22, -0.11, ..., 0.03] |
和退货政策距离远 |
5.2 相似度怎么算
| 方法 | 直觉 | 适用建议 |
|---|---|---|
| 余弦相似度(Cosine) | 衡量向量方向是否一致,值越大越相似 | 文本检索最常用 |
| 欧氏距离(L2) | 衡量绝对距离,值越小越相似 | 常见于通用向量搜索 |
| 点积(Dot Product) | 和余弦相似,但会受向量长度影响 | 取决于模型训练方式 |
实践中,文本检索默认优先考虑余弦相似度。
5.3 Embedding 模型选型
| 类型 | 例子 | 特点 |
|---|---|---|
| 通用闭源 | OpenAI text-embedding-3、Cohere | 效果好,但数据要发给外部 |
| 开源 | BGE、E5、GTE | 可本地部署,数据不出域;中文场景 BGE 表现好 |
| 垂直微调 | 用自己领域数据微调过的 | 特定领域效果最佳,但成本高 |
选型经验:
- 中文为主:BGE-M3 / bge-large-zh,开源且中文表现好。
- 多语言:BGE-M3,同时支持中英、多语言和长文本。
- 数据敏感:优先开源本地部署,避免数据出域。
- 效果优先且数据不敏感:可以考虑 OpenAI text-embedding-3-large 这类闭源模型。
注意:查询和文档必须用同一个 Embedding 模型,否则向量空间不一致,相似度无意义。
六、向量检索:相似度 + Top-K
向量都存好后,检索就是把问题也变成向量,找最近的 K 个。
6.1 检索流程
以「退货要多久能到账?」为例,系统会先把问题做 Embedding,得到查询向量 q;再到向量库里查找和 q 最相似的 K 个 chunk;最后返回 Top-K 片段,例如 K=5。
这一步追求的是召回:宁可先多拿一些候选,也不要一开始就漏掉关键证据。后面的 Rerank 再负责把真正相关的片段排到前面。
6.2 向量库(Vector DB)
专门的向量数据库,存储海量向量并支持快速近邻检索:
| 向量库 | 特点 |
|---|---|
| Milvus | 国产开源,分布式,大规模首选 |
| Qdrant | Rust 写的,性能好,云原生 |
| Weaviate | 内置混合检索(向量 + 关键词) |
| pgvector | PostgreSQL 扩展,复用现有 PG,轻量场景好用 |
| Chroma | 轻量,本地开发和小项目 |
| Elasticsearch(dense_vector) | 你已经用 ES,加个向量字段即可 |
选型经验:已有 PostgreSQL 时,可以先用 pgvector,不引入新组件;已有 ES 时,可以复用 dense_vector;大规模生产更适合 Milvus / Qdrant;本地开发和小项目用 Chroma 会更轻。
6.3 近似最近邻(ANN):为什么不全量比
朴素做法是把查询向量和库里每个向量都算一次相似度。数据量到百万级时,每次查询都要算百万次,延迟很难接受。
实际系统会使用 ANN(Approximate Nearest Neighbor,近似最近邻)算法,比如 HNSW、IVF、PQ。它牺牲一点点精度,换来巨大的速度提升,常见效果是把百万级数据的查询从秒级降到毫秒级。
这就像查字典不会逐页翻,而是用索引跳着查。向量库内部通常已经内置 ANN 索引,使用者一般不需要自己实现。
本博客的「Elasticsearch 基础知识」一文讲过 HNSW 算法的原理,那是 ES 用的向量索引方式,RAG 场景同样适用。
6.4 纯向量检索的局限
向量检索不擅长所有问题。遇到专有名词、型号、编号时,关键词检索往往更准。比如查「iPhone 15 Pro Max」,向量检索可能返回「iPhone 14」或「手机推荐」这类语义相近但型号不对的内容。
数字和日期也类似。「2024 年 Q3 财报」可能被匹配到其他季度的财报。解决办法是混合检索(Hybrid Search):向量负责语义相似,关键词负责精确匹配。
七、Reranking:把最相关的排到前面
向量检索快但粗(为了速度用了近似算法),返回的 Top-K 里常有「语义相关但其实不对」的。Reranking 用更精确的模型重排。
7.1 为什么需要 Rerank
向量检索是召回阶段:快,能从百万 chunk 里快速捞出 Top-50,但会混入一些「语义相关但答非所问」的片段。
Rerank 是精排阶段:慢一些,但会用更强的模型对「问题 + 候选片段」逐对打分,把真正最相关的几条排到前面,再取 Top-5 进入 prompt。
所以常见设计是两阶段:先粗筛,保证快和广;再精排,保证准。
7.2 Reranker 模型
Embedding 和 Reranker 的区别在于:
| 模型 | 输入 | 输出 | 特点 |
|---|---|---|---|
| Embedding | 单条文本 | 向量 | 快,但相关性判断较粗 |
| Reranker | 问题 + 候选片段 | 相关性分数 | 慢,但判断更准 |
常见 Reranker 包括 BGE-Reranker、Cohere Rerank、Cross-Encoder 等。典型流程是:向量检索拿 Top-50,Reranker 对每个候选打分,再取 Top-5 进入 prompt。
7.3 Rerank 划算吗
Rerank 的成本是多一次模型调用,收益是答案质量显著提升,尤其当知识库很大、噪声很多时。
经验上,知识库小于 1 万 chunk 且噪声少,可以先不做 Rerank;知识库大、精度要求高时,强烈建议加 Rerank。Rerank 往往是进阶 RAG 和朴素 RAG 的分水岭之一。
八、Prompt 拼装与生成
检索 + 重排后拿到相关片段,接下来拼 prompt 让 LLM 生成。
8.1 Prompt 模板
典型模板: |
8.2 关键设计:防幻觉的指令
RAG 的核心收益之一是减少幻觉,但前提是 prompt 写得足够克制。
| 写法 | 效果 |
|---|---|
| 「只根据资料回答」「资料未提及就说没有」 | 强约束,降低编造空间 |
| 「根据资料和你的知识回答」 | 给了模型使用外部常识补全的口子,幻觉会回来 |
| 「标注引用来源」 | 让答案可追溯,也倒逼模型遵守资料边界 |
实践上,RAG prompt 的核心是约束大于引导。
8.3 上下文塞不下怎么办
如果检索回来 10 个片段,加起来超过上下文窗口,可以从四个方向处理:
- 调小 Top-K,减少候选片段数。
- 让 Reranker 精筛,只保留最相关的 3~5 条。
- 对超长片段二次切短,只保留命中关键词附近的段落。
- 换用支持长上下文的模型,比如 128K 或 200K 窗口。
经验是:宁可少给几条高质量片段,也不要塞一堆低相关内容。噪声片段会分散模型注意力,反而降低答案质量。
九、评估 RAG 系统
RAG 上线前必须评估,否则你不知道它到底比「直接问 LLM」强多少。
9.1 三层评估指标
| 层级 | 指标 | 关注问题 |
|---|---|---|
| 检索质量 | Recall、Precision | 能不能找对资料 |
| 生成质量 | Faithfulness、Answer Relevance | 答案是否忠于资料,是否回答问题 |
| 端到端质量 | 用户满意度、人工评分、标准答案对比 | 整体效果是否真的变好 |
9.2 RAGAS:自动评估框架
手工评几千条问答太贵,工业界常用 RAGAS 这类框架做自动评估。它的输入通常包括问题、标准答案、检索到的片段、生成的答案;输出是忠实度、答案相关性、上下文精确率、上下文召回率等分数。
核心技巧是 LLM-as-judge:用一个更强的模型评估另一个模型的答案质量。
9.3 评估数据集怎么来
评估数据集可以来自四个渠道:
- 人工标注:最准,但最贵,几百条起步。
- 真实用户问题:上线后收集、清洗、打标。
- LLM 生成:让模型基于文档生成「问题-答案」对,便宜但有偏差。
- 混合方式:LLM 先生成,再人工审核修正。
十、生产实践与常见陷阱
10.1 进阶 RAG 的几个方向
朴素 RAG 的主流程是「文档 → chunking → Embedding → 检索 → 生成」。在生产环境里,常见的进阶方向有:
| 方向 | 做法 | 解决的问题 |
|---|---|---|
| 查询改写 | 先让 LLM 把口语化问题改写成更适合检索的查询 | 用户问题模糊、表达不稳定 |
| 混合检索 | 向量检索 + BM25 关键词检索 | 兼顾语义相似和精确匹配 |
| HyDE | 先让 LLM 生成假设答案,再用答案向量检索 | 问题太短或语义不完整 |
| 父子分块 | 检索用小块,返回时带父块上下文 | 同时追求精准和上下文完整 |
| 多路召回 + Rerank | 向量、关键词、HyDE 多路召回后统一重排 | 提升复杂知识库的召回和排序质量 |
10.2 常见陷阱
| 陷阱 | 症状 | 对策 |
|---|---|---|
| chunk 切得太糙 | 检索到了但答案不全 | 按语义边界切,加 overlap |
| 查询和文档向量化模型不一致 | 检索全是噪声 | 两端必须用同一 Embedding 模型 |
| Top-K 设太大 | prompt 塞满噪声,模型分心 | K 控制在 3~5,配合 Rerank |
| 没做 Rerank | 相关资料排不到前面 | 加 Reranker 精排 |
| prompt 没约束幻觉 | 模型还是乱编 | 明确「只根据资料」「资料没有就说没有」 |
| 知识库不更新 | 答案陈旧 | 增量更新机制 + 文档 TTL |
| 只评生成、不评检索 | 不知道瓶颈在检索还是生成 | 分层评估,先保证检索召回 |
| 没做评估就上线 | 效果全靠「感觉」 | 至少跑一轮 RAGAS 自动评估 |
10.3 什么时候不适合 RAG
RAG 不是银弹。实时数据(股票、库存)直接查 API 更合适;高度结构化查询(如「订单 #12345 的状态」)直接查 DB 更准;需要大量推理或计算的任务,应该交给模型能力或工具调用;数据量极小(几十条)时,直接全塞 prompt 可能比建向量库更简单。
RAG 最适合的是文档问答、知识库检索、客服、企业内部搜索这类「答案在资料里,只是要找出来」的场景。
十一、常见问题
| 问题 | 速答要点 |
|---|---|
| RAG 是什么?解决什么问题? | 检索增强生成:先从知识库检索相关片段,塞进 prompt 让 LLM 开卷答题。解决知识截止、幻觉、无法访问私有数据 |
| RAG 和微调怎么选? | RAG 解决「知道什么」(给资料查),适合事实/私有数据;微调解决「怎么做」(内化能力/风格)。知识类首选 RAG |
| RAG 的完整链路? | 离线:文档→chunking→Embedding→存向量库;在线:问题→Embedding→检索→Rerank→拼prompt→生成 |
| chunking 为什么重要? | 分块决定检索精度。切太大语义混杂,切太小不完整;要按语义边界切,配 overlap,一般 200~500 字 |
| 向量检索的局限? | 关键词/型号/数字精确匹配弱;解法是混合检索(向量 + BM25) |
| 为什么需要 Rerank? | 向量检索快但粗,Reranker 用强模型精排,把最相关的排到前面;先粗筛再精排 |
| 怎么防 RAG 幻觉? | prompt 强约束(只根据资料、资料没有就说没有、标注引用);Rerank 提升资料质量 |
| RAG 怎么评估? | 三层:检索(召回/精确)+ 生成(忠实度/相关性)+ 端到端;用 RAGAS 框架 + LLM-as-judge 自动评 |
| 向量库怎么选? | 已有 PG 用 pgvector;已有 ES 用 dense_vector;大规模用 Milvus/Qdrant;小项目用 Chroma |
| 什么时候不用 RAG? | 实时数据、结构化精确查询、数据量极小、推理计算类任务 |
一句话总结:RAG 的本质是「让 LLM 开卷答题」——离线把文档切成块、向量化建库,在线把问题向量化检索、拼进 prompt 让模型看着资料答。成败的关键不在算法多花哨,而在 chunking 切得准不准、检索的资料相不相关、prompt 约束得严不严。