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

RAG 系统设计封面

RAG(Retrieval-Augmented Generation,检索增强生成) 是让 LLM 用好私有数据的主流方案:在生成回答前,先从你的知识库里检索出相关片段,塞进 prompt 让模型「看着资料答题」。它解决了 LLM 的三个硬伤——知识截止、幻觉、无法访问私有数据。

本文脉络:

  1. 为什么需要 RAG:LLM 的三个硬伤
  2. 为什么不直接微调:RAG vs Fine-tuning
  3. RAG 的完整架构:检索 → 增强 → 生成
  4. 文档处理:chunking 是 RAG 成败的关键
  5. Embedding:把文本变成向量
  6. 向量检索:相似度 + Top-K
  7. Reranking:把最相关的排到前面
  8. Prompt 拼装与生成
  9. 评估 RAG 系统
  10. 生产实践与常见陷阱
  11. 常见问题

一、为什么需要 RAG:LLM 的三个硬伤

直接用 LLM 回答私有领域问题,会撞上三个硬伤:

  1. 知识截止:模型的训练数据有截止日期,问它「昨天发布的 API」它不知道。公司内部 API 文档、最新操作手册、刚更新的客服政策,模型训练时往往根本没见过。
  2. 幻觉:不知道却硬编。比如问「我们公司的报销流程」,它可能给出一套看似合理的通用流程,但细节全错。
  3. 无法访问私有数据:模型看不到你的数据库、文档库、内部 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 分三个阶段,对应名字的三个字母:

RAG 系统完整架构

Retrieval(检索):从知识库找出和问题相关的文档片段。典型链路是「问题 → Embedding → 向量检索 → Top-K」。

Augmented(增强):把检索到的片段塞进 prompt,作为模型回答时的参考资料。

Generation(生成):LLM 看着资料生成答案,并尽可能标注引用来源。

但这只是在线推理时的链路。一个完整的 RAG 系统,还需要一条离线的「建库」链路:原始文档先经过清洗、分块、向量化,再写入向量库;用户查询时,再用同一个 Embedding 模型把问题转成向量,检索、重排、拼 prompt,最后生成答案。

下面六章按这个链路顺序展开。


四、文档处理:chunking 是 RAG 成败的关键

很多人以为 RAG 的难点在检索算法,其实真正决定效果的是分块(chunking)——分得不好,检索再准也白搭。

4.1 为什么必须分块

不直接把整篇文档向量化,主要有三个原因:

  1. 长度限制:Embedding 模型有输入长度上限,长文档必须拆开处理。
  2. 精度问题:整篇文档一个向量,语义会被稀释。一篇文档讲了 10 件事,向量很难表达清楚哪一段才和问题相关。
  3. 窗口约束:检索回来的内容最终要塞进 prompt,整篇文档太大,既贵又容易引入噪声。

所以更合理的做法是把文档切成小块,每块单独向量化、单独检索。在线查询时只取最相关的几块,既精准,也省 token。

4.2 四种分块策略

RAG 文档分块策略

策略 做法 优点 风险 适合场景
固定长度分块 每 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 模板

典型模板:

你是一个严谨的助手,必须根据提供的资料回答问题。
如果资料里没有答案,明确说「资料中未提及」,不要编造。

【参考资料】
[1] 来源:退货政策.pdf,第 3 页
内容:30 天内可退货,需保留购物凭证。退款 3-5 工作日到账。
[2] 来源:售后 FAQ.md
内容:退货商品需保持完好,不影响二次销售。
...

【问题】
退货多久能到账?

【要求】
1. 只根据资料回答,不要添加资料外的信息
2. 标注引用(如「据[1]」)
3. 资料没有就说没有

8.2 关键设计:防幻觉的指令

RAG 的核心收益之一是减少幻觉,但前提是 prompt 写得足够克制。

写法 效果
「只根据资料回答」「资料未提及就说没有」 强约束,降低编造空间
「根据资料和你的知识回答」 给了模型使用外部常识补全的口子,幻觉会回来
「标注引用来源」 让答案可追溯,也倒逼模型遵守资料边界

实践上,RAG prompt 的核心是约束大于引导

8.3 上下文塞不下怎么办

如果检索回来 10 个片段,加起来超过上下文窗口,可以从四个方向处理:

  1. 调小 Top-K,减少候选片段数。
  2. 让 Reranker 精筛,只保留最相关的 3~5 条。
  3. 对超长片段二次切短,只保留命中关键词附近的段落。
  4. 换用支持长上下文的模型,比如 128K 或 200K 窗口。

经验是:宁可少给几条高质量片段,也不要塞一堆低相关内容。噪声片段会分散模型注意力,反而降低答案质量。


九、评估 RAG 系统

RAG 上线前必须评估,否则你不知道它到底比「直接问 LLM」强多少。

9.1 三层评估指标

层级 指标 关注问题
检索质量 Recall、Precision 能不能找对资料
生成质量 Faithfulness、Answer Relevance 答案是否忠于资料,是否回答问题
端到端质量 用户满意度、人工评分、标准答案对比 整体效果是否真的变好

9.2 RAGAS:自动评估框架

手工评几千条问答太贵,工业界常用 RAGAS 这类框架做自动评估。它的输入通常包括问题、标准答案、检索到的片段、生成的答案;输出是忠实度、答案相关性、上下文精确率、上下文召回率等分数。

核心技巧是 LLM-as-judge:用一个更强的模型评估另一个模型的答案质量。

9.3 评估数据集怎么来

评估数据集可以来自四个渠道:

  1. 人工标注:最准,但最贵,几百条起步。
  2. 真实用户问题:上线后收集、清洗、打标。
  3. LLM 生成:让模型基于文档生成「问题-答案」对,便宜但有偏差。
  4. 混合方式: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 约束得严不严