1. 什么是RAG
RAG 全称是 Retrieval-Augmented Generation,就是检索增强生成。LLM 的知识在训练完之后就固定了,遇到私有数据或者最新的信息它就答不上来。RAG 的做法是在生成答案之前,先去外部知识库里检索相关内容,然后把检索结果和用户的问题一起交给 LLM,让它基于这些上下文来回答。
RAG两个阶段:
- 离线阶段
离线阶段的目标很明确:在用户提问之前,就把知识库建好。这一阶段只做一次,建好了后面反复用。
主要实现有以下几步:
(1)文档加载:
把各种格式的原始数据读取进来,可以是 PDF、Word、Markdown、网页、数据库记录等。这一步通常用 LlamaIndex 或 LangChain 提供的 DocumentLoader 来做
(2)文档切割
为什么不把整篇文档直接存进去检索,非要切成一块一块的?原因有两个。 - 一是向量模型有输入长度限制,一般最多几百到几千个 token,整篇文档根本塞不进去。。
- 二是更关键的,如果把一整篇文章压缩成一个向量,细节信息会被「平均掉」。这就好比你问「这道菜怎么样」,对方回答「中国菜整体偏咸」,具体哪道菜咸、咸到什么程度,全丢失了。
文档切割实践中通常 500~1000 token 一个 chunk,同时做一定的重叠(比如前后各重叠 100 token),避免把一段完整的语义从中间切断。
(3)Embedding(向量化)
Embedding 模型会把一段文字转成一个高维数字向量,比如一个 1536 维的浮点数列表。这东西听起来很玄,但其实你可以把它理解成一个「语义坐标系」。 语义相似的文本,它们在这个坐标系里的位置就靠近;语义不相关的,位置就离得远。
(4) 入库
把每个 chunk 的向量和原始文本一起存进向量数据库。向量数据库专门优化了高维向量的存储和相似度搜索,常见的有 Chroma、Milvus、Qdrant、Weaviate 等,
支持在千万量级的向量里快速找到最相近的几条。
- 在线阶段:
在线阶段是每次用户提问时实时执行的,对响应速度有要求。主要分为以下几步
(1) Query 处理
让 LLM 把用户的问题改写成更适合检索的形式,或者从对话历史里补充必要的上下文。举个例子, 比如用户说"上次那个问题怎么样",这里检索的时候根本不知道上次具体指的是什么,所以需要LLM对于用户提的问题进行修改,然后用修改后的问题进行检索.
(2) 向量检索(粗排)
把用户的问题也转成向量,然后去向量库里做相似度搜索,找出向量距离最近的 Top-K 个 chunk。
(3) Rerank(精排)
Rerank 模型(通常是 Cross-Encoder 结构)会把用户问题和每个候选 chunk 拼在一起,深度理解它们之间的相关性,然后重新排序,把不相关的结果过滤掉。
(4) 生成
把用户问题 + 精排后的 chunk 拼成 prompt,交给 LLM 生成最终答案。Prompt 里通常会明确告诉 LLM「只根据提供的资料回答,资料里没有就说不知道」. 这样能有效抑制 LLM 瞎编的倾向。

2 RAG解决了什么问题
(1) 知识时效性
大模型在训练完成后,知识就固定在训练对应的时间了, 所以对于最新的知识它根本不知道
(2) 私有知识覆盖
公司内部文档、行业专有数据根本没有机会进训练集,LLM 对这些内容是空白的;
(3) 幻觉
没有知识依据时 LLM 容易「自己发挥」编出一个听起来合理但实际错误的答案,给了它参考资料之后幻觉就少很多。
3 RAG和微调的区别
微调是把新知识直接烧进模型参数里,适合改变模型的行为风格或者培养深度的专业能力;RAG 是在推理的时候实时检索注入知识,适合知识需要频繁更新、或者需要有溯源的场景
RAG的缺点:
(1) 首先是多了检索步骤,整体响应延迟会增加几百毫秒到一秒。
(2) 检索质量上限, 相关的内容没有被找回,那么LLM也不会回答准确的内容
(3) 对复杂推理能力提升有限: RAG 只是给模型提供了「参考资料」,但如果你需要模型对某个领域有更深的推理能力,光靠检索资料是不够的,还是得微调。
4 RAG信息存储为什么不能让文档整篇存
(1) 向量模型有输入长度限制,一般最多几百到几千 token,一篇几千字的文档根本塞不进去。
(2) 就算模型支持超长输入,把整篇文章压缩成一个向量,细节信息会被「平均掉」,你想找「退款政策」,但向量里还混着「配送时效」「积分规则」等内容,最终检索到的就是这篇笼统的文档,而不是精确的那段话。
向量库里每一条记录通常包含三个部分:
(1) 索引卡: 指的是向量,通过向量能找到RAG中语义相似的内容
(2) 书页: 指的是原文, LLM 真正要阅读的内容,检索命中后原封不动地塞进 prompt
(3) 书签(metadata): 记的是来源文件名、页码、章节这些附加信息,用于过滤(「只搜客服部门的文档」)和溯源(「这个答案来自哪个文件的第几页」)。
5 文档切割的策略有哪些
(1) 固定大小切割:
按固定字符数或 token 数切割,不管语义边界在哪。优点是实现简单、chunk 大小可控;缺点是可能在句子中间截断,破坏语义完整性。纯固定大小几乎不单独使用,通常会加上重叠(overlap)来缓解边界截断问题。
(2) 语义边界切割
按文档的自然语义边界来切,比如段落、句子、标题层级。核心思想是:不要在语义中间截断,找到文字天然的「断点」再切。
实际操作时,常见的做法是维护一个分隔符优先级列表,先尝试按段落切,切出来太大再按句子切,还是太大再按标点切,以此类推,直到满足 chunk_size 限制。
(3) 特殊内容专项处理
- 代码应该以函数或类为单位切割,
- 表格则要整块保留,转成 Markdown 格式存储,不能按行截断。表格的每一行都依赖表头才有意义,「2 小时」单独来看完全不知道是什么的 2 小时,但配上列名「响应时间:2 小时」就清晰多了。
(4) 父子切割
检索时用放大镜(小块,精准定位),返回时用全景图(大块,上下文完整)。
存储时,同一段内容存两份。一份是细粒度的小 chunk(比如 200 token),专门用于向量检索,因为小 chunk 语义聚焦,围绕一个小话题,检索精度高。另一份是包含这个小 chunk 前后上下文的大 chunk(比如 1000 token),通过 ID 与对应的小 chunk 关联。检索时用小 chunk 找到精准的命中点,然后根据关联 ID 取出对应的大 chunk,把完整的上下文交给 LLM 阅读,生成质量更好。
(5) Late Chunking
6. 怎么规避语义被切割掉的问题
第一个方向是切的时候就不要在语义中间截断,用重叠切割和语义边界切割来保证每个 chunk 内容是完整的,也就是按句子、段落这些自然的边界来切。
第二个方向是切完之后用检索策略把上下文补回来,核心方案是句子窗口检索,命中一个句子就把周围几句一起返回给 LLM;
另外还有父子切割,小块检索命中、大块内容输出。
Contextual Retrieval: 在做 Embedding 之前先让大模型看着整篇文档为每个 chunk 生成一段背景说明,把这段背景和 chunk 拼在一起再向量化,从根本上解决孤立 chunk 没头没尾的问题。
具体方案:
方案1:重叠切割(兜底策略)
让相邻 chunk 之间有一段内容重叠,这样就算边界切在语义中间,跨边界的内容也一定会完整地出现在其中一个 chunk 里。重叠量通常设为 chunk_size 的 10%~20%,比如 chunk_size=800,overlap=150。
方案2: 按语义边界切割]
找到文本中自然的语义边界再切.
实际操作时,可以用 NLP 工具(比如 spacy 或 nltk)识别句子结束位置,然后以句子为单位填充 chunk:把句子一条条往当前 chunk 里加,加满了就封存这个 chunk 开启新的,不会在句子中间截断。对于段落分明的文档,还可以在句子边界的基础上优先在段落边界处切,效果更好。
方案3: 句子窗口检索
它不是在「切」上做文章,而是在「检索后如何返回上下文」上做文章。类比一下:就像你在图书馆里用关键词找到了某本书里的一句话,但你实际阅读的是这句话所在的整个段落,而不是只拿走那一行文字。
具体做法是:存储时把文档切成单个句子,每个句子单独做向量,用于精准检索;检索命中一个句子后,并不只返回这一个句子,而是把这个句子前后各 N 个句子一起返回,形成一个上下文窗口,交给 LLM 阅读。
方案4: 父子切割
父子切割的思路是「用小 chunk 检索,用大 chunk 生成」:存储时同一段内容存两份,一份是细粒度的小 chunk(比如 200 token),一份是包含这个小 chunk 上下文的大 chunk(比如 1000 token),两者通过 ID 关联。检索时用小 chunk,语义聚焦,召回精度高, 命中后把对应的大 chunk 返回给 LLM,上下文更完整,生成质量更好。
方案5: 命题化切割
用 LLM 把文档分解成一条条独立的「命题」(Proposition)。每个命题是一个完整、自包含的陈述句,包含了表达这个事实所需的全部上下文,单独拿出来就能看懂,不依赖上下文,只包含一个核心事实。
方案6: Contextual Retrieval
不改变 chunk 本身,而是在向量化之前把缺失的上下文补进去。
核心做法分两步:
(1)生成 Context:让 LLM 看着整篇原始文档,为每一个切出来的 chunk 生成一段简短的背景说明(通常 1~2 句话),说清楚这个 chunk 在整篇文档里处于什么位置、讲的是什么。
(2)拼接再向量化:把生成的 Context 前置拼到 chunk 前面,然后把这个「Context + chunk」整体去做 Embedding 和 BM25 索引
成本控制:
每次调用时,full_document(完整文档)在所有 chunk 的请求里是相同的前缀。开启 Prompt Caching 后,第一次调用会把文档内容缓存到 KV Cache,后续同一篇文档的所有 chunk 请求都复用这份缓存,只有 chunk 部分需要重新计算。
7. 什么是Embedding
Embedding 我理解就是模型把一段文本转成一串数字向量的过程。它有一个很关键的特性,就是相同的模型,语义相近的文本,转出来的向量在数学空间里的距离也近。
模型选择的标准:
(1)中文支持,中文场景我会优先选 BGE 系列,效果其实比 OpenAI 的模型还要好
(2)向量维度,维度越高精度越好,但存储成本也越大;
(3)第三是最大输入长度,这个决定了能处理多长的 chunk。
注:语义相近的文本,向量的余弦相似度高。
为什么衡量相似度要用「余弦」而不是直接算两个向量的直线距离?
因为在高维空间里,两段文本的向量长度(模长)会受文本长度、表达强度等非语义因素影响,直接比距离会把「长短」和「语义」混在一起。而余弦只看两个向量的方向(也就是夹角),方向越一致余弦值越接近 1,正好把「意思相近」从「文本长短」里剥离出来。
主流的Embedding 模型有以下几类
(1)OpenAI 的 text-embedding 系列,text-embedding-3-small 是性价比最高的,1536 维,支持降维到 256 维来节省存储,调用方便,英文效果非常好;缺点是 API 调用有费用,而且数据要发到 OpenAI 服务器,有些企业有数据出境合规问题。
(2)BGE 系列(北京智源研究院出品),bge-large-zh-v1.5 是经典的中文开源模型,1024 维,可以本地部署,数据不出境。不过要注意的是,BGE 虽然仍然是很好的选择,但已经不是中文场景的唯一首选了。bge-m3 是 BGE 的多语言版本,同时支持中英日等多种语言,1024 维,而且支持三种检索模式(稠密向量、稀疏向量、ColBERT 式多向量),适合中英文混排的场景。
(3)新一代高性能模型, 这两年涌现了一批在 MTEB 排行榜上超过 BGE 的模型。Qwen3-Embedding(阿里通义出品)在多语言基准上表现突出,中文效果很强;Voyage-3-large 在英文检索上精度超过 OpenAI 的模型;Cohere embed-v4 支持 128K 超长上下文,适合长文档场景;Gemini Embedding(Google 出品)在多个评测中表现均衡。如果你在 2025-2026 年做新项目。
如何选embedding模型
(1)中英文比例:知识库以中文为主,可以选 bge-large-zh-v1.5 或者更新的 Qwen3-Embedding;中英混合,选 bge-m3;纯英文或追求省事,选 text-embedding-3-small。
(2)数据合规要求:数据不能出境,就必须用可以本地部署的开源模型,BGE 系列和 Qwen3-Embedding 都是很好的选择。
(3)向量维度对存储和检索速度的影响:维度越高精度越好,但存储空间和检索时间都会增加。百万量级的知识库,1024 维是个合理的平衡点;如果规模很小,1536 维也无所谓。有些新模型(如 text-embedding-3-small)支持 Matryoshka 降维,可以灵活调整维度来平衡精度和成本。
8. Embedding算法有哪些
算法经历了三代演进
第一代:静态词向量(Word2Vec / GloVe / FastText)
用一个词周围的词来预测这个词,或者反过来用这个词来预测周围的词。通过大量文本训练,语义相近的词自然就会在向量空间里被推到一起。
第二代 [上下文相关向量(ELMo / BERT)
让词的向量随上下文动态变化,同一个词在不同句子里有不同的向量表示。
第三代句子级对比学习 Embedding(SBERT / SimCSE / BGE)
让每个句子独立生成一个向量,然后直接用余弦相似度来比较。
9 什么是向量数据库
向量数据库就是专门用来存储和检索「向量」的数据库。这里的向量,指的是嵌入模型(Embedding Model)把文本、图片、音频这些内容转换成的一串浮点数,
向量数据库支持的核心操作叫做** 近似最近邻搜索(ANN,Approximate Nearest Neighbor)**:给你一个查询向量,在库里找出和它最相似的 K 个向量,返回对应的原始内容。
10. 为什么需要专门的数据库
普通的关系型数据库(MySQL、PostgreSQL)在存储结构化数据时靠 B-tree 索引,查询 WHERE id = 123 这种精确匹配效率极高。但向量检索要做的事完全不同——不是找「等于」的,而是找「最相近」的,没有精确匹配,只有相似度排序。
目前主流的索引算法:
(1) HNSW(Hierarchical Navigable Small World)
它构建的是一个多层图结构,查询时从最上层的稀疏图开始导航,逐层收窄范围,最终在底层找到最近邻。例如在地图上找餐厅,先国家,然后再找省,市,区,街道等等,逐渐收窄查找区域。
- 优点:召回率高,查询速度快
- 缺点:占用内存大
(2)IVF(Inverted File Index)
它先对向量做聚类,把相似的向量分进同一个「桶」里,查询时只搜最相关的几个桶,而不是全量遍历。类似于图书馆分类,找一本书时只需要在对用的分类里边一点点找就可。
- 优点:内存占用小
- 缺点:精度比HNSW 略低,需要调参(聚类数量 nlist、搜索桶数量 nprobe)。
向量数据库的核心能力
(1)Metadata 过滤,也叫混合检索
向量数据库支持给每个向量挂上 metadata 字段,检索时加过滤条件,只在符合条件的子集里做 ANN 搜索。比如有多个部门的文档,可以在检索时过滤自己想要的部门的文档。
(2)实时更新
RAG 的知识库经常需要新增、修改、删除文档,向量数据库需要支持增量更新。
(3)与关键词检索融合
纯向量检索对一些精确词语(比如产品型号「GPT-4o」、专有名词)的效果不好,有些向量数据库同时支持向量检索 + BM25 关键词检索,做混合召回效果更好。
向量数据库选型
(1)Qdrant
中小到大规模、团队小、快速上线,性能好、API 设计简洁、文档完善,Docker 一条命令就能部署,Rust 写的性能很稳。Qdrant 现在也支持分布式模式(分片 + 副本),实际已经有亿级规模的生产案例,覆盖面比较广。
(2)Chroma
上手最快,零配置,现在也支持了 Client-Server 模式和 Chroma Cloud 托管服务,加入了 BM25/SPLADE 稀疏向量的混合检索支持。分布式能力还在成熟中,对于超大规模(千万级以上)的生产场景,它的稳定性和性能还不如 Milvus 和 Qdrant
(3)Milvus
数据规模在千万到亿级,需要分布式,支持多种索引类型,有完整的集群方案,但部署运维复杂度也高,团队需要有足够的人力来维护。
(4)Pinecone
不想运维,数据在云上,全托管 SaaS,按用量付费,适合快速验证商业化产品。不过要注意数据出境的问题,如果业务数据有合规要求,Pinecone 就不合适了。
(5)PostgreSQL
已经在用的,数据量不是特别大,可以直接用 pgvector 插件,,不用引入新组件,查询可以和业务数据做 SQL JOIN,运维成本为零。
总结:快速原型验证用 Chroma,中小到大规模生产环境推荐 Qdrant,超大规模选 Milvus 做分布式,不想运维就上 Pinecone,已有 PostgreSQL 的项目直接用 pgvector 插件就够了。
11. Milvus 核心概念
(1)Collection(集合) 类似关系数据库里的「表」,存储一类向量数据。我们的知识库就是一个 Collection,每条记录包含:文本 chunk 的 ID、向量(embedding),原文内容、来源文档等 metadata。
(2)Segment(段) 是 Milvus 内部管理数据的基本单位,理解它对后面分析性能瓶颈很关键。新写入的数据先进「增量段」,像一个临时的接收缓冲区;积累到一定量后触发合并,变成「封存段」。封存段会建好索引,查询时走索引检索速度很快。但合并这个动作本身会消耗 CPU 和磁盘,就像磁盘碎片整理,把一堆小文件合并成大文件,期间会抢资源。
(3)Index(索引) 是向量检索的关键加速结构。最常用的是 HNSW(Hierarchical Navigable Small World,分层可导航小世界图),一种图结构索引。它的核心思想是:把向量组织成多层图,查询时从稀疏的顶层开始,快速定位到大致区域,再逐层细化找到最近邻,整体复杂度接近 O(log N)。
12. 你使用 RAG 给大模型一个输入,系统是怎样的工作流程?
当你把一个问题输入给 RAG 系统,它不会直接丢给大模型,而是先经历一套「检索 -> 整理 -> 生成」的流水线。
具体来说:系统先对问题做预处理(改写成更适合检索的形式),然后把问题向量化,去向量库里找最相关的文档片段,再经过精排筛掉噪音,最后把筛选出来的片段和问题一起拼成 Prompt 交给大模型,大模型基于这些「参考资料」生成最终答案。具体阶段如下:
(1)Query预处理
用户的提问往往是口语化的,甚至带着指代,比如「上次那个方案怎么样」,这种问题离开对话上下文完全没法检索。另外,就算问题表达清楚,用词和知识库里文档的用词可能完全不一样,直接拿去检索命中率会很低。而为了解决这个问题,常见的方法有几种:
- 简单改写,把口语化问题改写成更正式、独立完整的检索句。
- HyDE(Hypothetical Document Embeddings),让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。
- 多角度扩写,把同一个问题扩展成 3-5 种不同表述,分别检索后合并结果,覆盖面更广。
(2)问题向量化
很多人以为随便选一个 Embedding 模型就行了,其实不然,必须用和离线建库时完全相同的 Embedding 模型。因为不同 Embedding 模型在训练时见过的数据、目标函数、输出维度、向量空间的"形状"都不一样,模型 A 让「苹果手机」和「iPhone」的向量落在空间里的某个方向,,模型 B 完全有可能让它们落在另一个方向,甚至连维度数都对不上(A 是 1024 维,B 是 1536 维,根本没法算距离)。
(3)向量检索(ANN 搜索)+ 多路召回
拿着问题向量,去向量数据库里做近似最近邻搜索(ANN),找出余弦相似度最高的 Top-K 个文档片段。但工程实践中,只用向量检索这一路往往不够。所以这一步通常同时进行多路召回:向量检索负责捕获语义相似性,BM25/全文检索负责捕获关键词精确匹配,两路各有所。
比如用户问「LSTM 和 Transformer 的区别」,向量检索能找到「序列模型对比」相关的语义内容,BM25 能精准命中包含「LSTM」「Transformer」这两个词的文档。多路结果通过 RRF(互倒排名融合)算法合并,最终召回的结果比单路覆盖面更广、质量更高。
(4)Rerank 精排
Rerank 模型(Cross-Encoder 结构)会把用户问题和每个候选片段拼在一起输入,深度理解它们之间的语义匹配程度,重新打分排序。最终只保留 Top-3 到 Top-5 的高质量片段,把噪音过滤掉。
(5)Prompt拼装
精排后的高质量片段拿到了,接下来要把它们和用户的原始问题组装成 Prompt 交给大模型。典型模板大概是这样:
prompt = f"""
你是一个专业助手,请根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请回答「根据现有资料无法回答」,不要自行猜测。
参考资料:
[1] {chunk_1}
[2] {chunk_2}
[3] {chunk_3}
用户问题:{user_query}
"""
(6) 大模型生成 + 溯源
13. 向量检索和关键词检索的区别?
(1)关键词检索:字面匹配,靠统计
关键词检索的代表是 BM25(Best Match 25),这是 Elasticsearch、Lucene 等传统搜索引擎的核心算法。一个词在这篇文档里出现多(词频高),但在整个知识库里出现少(区分度高),说明这个词对这篇文档很具代表性,权重就高。本质上是在问:「这个词有没有『代表』这篇文档?」
中文场景下用 BM25 需要先做分词(比如用 jieba 切词),再对分词后的词列表建索引和检索。实现上就是对每个 chunk 先切词建索引,查询时同样切词后计算 BM25 分数排序。
l
BM25打分机制:
- 第一个是词频(TF):这个词在这篇文档里出现了几次,出现越多说明越相关。
- 第二个是稀缺度(IDF):这个词在所有文档里有多罕见。
- 饱和度限制:词频增加时,加分递增越来越慢,最后趋近一个上限,不会无限涨。
优缺点:
优点:BM25 的优势是对精确词汇命中率极高,产品型号「iPhone 15 Pro Max」、专有名词「LSTM」、缩写「RAG」,只要文档里有这个词,BM25 就能精准找到。
缺点:遇到同义词就束手无策。
(2)向量检索:语义匹配,靠 Embedding
先用一个 Embedding 模型把每段文本转成一个高维数字向量(比如 1024 维的浮点数列表),这个向量可以理解成这段文本在「语义空间」里的坐标。语义相近的文本,坐标就靠近;语义不相关的,坐标就离得远。
优缺点:
优点:语义理解能力强,能跨越同义词、近义词、不同表达方式的障碍
缺点:但它的劣势是对精确词汇不敏感,产品型号「Pro Max 256GB」、人名、版本号这类词,在 Embedding 空间里可能彼此距离并不近,向量检索容易把它们混淆在一起
混合检索(Hybrid Search):同时跑向量检索和 BM25,各自召回一批候选,然后用 RRF(Reciprocal Rank Fusion,互倒排名融合)算法把两路结果合并排序。
RRF(Reciprocal Rank Fusion,倒数排名融合):RRF 用排名的倒数来打分,排名越靠前倒数越大,最终按总分降序排列。这样,两路都认为相关的文档会排在最前面,只有一路认为相关的文档也不会被完全丢掉。

[图片上传中...(image.png-e4a771-1781084202660-0)]
其中 k 是平滑参数(通常取 60),rank 是文档在某一路结果里的排名。
RRF 的直觉很好理解:不管各路分数怎么算(因为向量相似度和 BM25 分数本来就没有可比性),只看排名。一个文档在多路检索里都排名靠前,它的 RRF 综合分就高。就像多位评委都给高分的选手,最后的综合排名就高。这个方法不需要训练、计算量极小,工程落地成本很低。
**值得注意的是,RRF 本质上还是粗排,适合在 Rerank 之前做候选集合并。如果对最终召回精度要求很高,还是要在 RRF 融合之后接一个 Cross-Encoder 结构的精排模型(比如 bge-reranker-v2-m3)做深度打分,把真正相关的内容筛到最前。
以上两者与多 Query 扩展召回是常用的多路召回的三种方法

- 如何润色用户的 Query
(1)直接改写
把口语化、模糊的问题改写成更精准、更书面化的检索表述,这就是最基础的改写方式。一般是将问题发给大模型,让大模型根据上下文对问题进行改写
(2)HyDE(Hypothetical Document Embeddings)
HyDE 的直觉可以这样理解:正常你用问题的影子去找答案的样子,HyDE 先描绘出答案可能长什么样子,再去找长得像它的文档。先让 LLM 根据用户问题生成一段「假设性的答案」,再用这段假设答案的向量去检索文档。假设答案不需要准确,只需要「风格像文档」就够了,它的作用是充当一个向量上更好的「检索代理」。
(3)Step-back Prompting(后退提问)
对于涉及具体细节的问题,有时候知识库里没有直接对应的答案,但有背景原理。Step-back Prompting 把具体问题「后退一步」,提升到更抽象的层次,先检索背景知识,再结合背景知识回答具体问题。
(4)多Query扩展
用 LLM 把用户原始问题改写成 3~5 个不同角度的版本,分别去检索,然后把所有结果合并去重。核心思路是:只要有一个改写版本和文档的表述对上了,就能把正确的内容召回来,就像拦截网越宽,捕到鱼的概率越高。
15 RAG 检索怎么优化
检索的优化可以从以下4个层面来理解
- 索引层决定知识怎么「存」,也就是文档切割的粒度和方式,直接影响向量的语义质量;
- 查询层决定问题怎么「转」,在检索之前对用户的 query 做加工,让它更容易命中知识库;
- 召回层决定知识从哪里「找」,用多条不同的检索路径并行捞取候选,互补各自的盲区;
- 重排序层决定候选里谁「最相关」,对粗召的候选集做精排,保证进入 prompt 的都是真正有用的内容。
(1)索引层:索引优化
索引优化最关键的是chunk怎么切分,一个chunk的切分需要满足以下两个任务
【1】检索时被找到:要求向量语义尽量聚焦。把一篇文章压成一个向量,这个向量里不要混合太多的语义
【2】被 LLM 读懂:要求有完整的上下文。断章取义的几句话 LLM 往往答不好,如果文档里前一段定义了术语、后一段才是真正的解释,只给 LLM 后一段它可能看不明白,所以 LLM 需要大粒度的 chunk,上下文完整。
而满足以上两个任务有三种方法:
- 父子切割:将一个文档切割时,同时切割两部分,小块chunk,和包含该chunk的大块chunk,并且小块的chunk要保存大块的id,然后对于小块的chunk进行创建索引,检索到小块的chunk时,直接返回大块的chunk。
- 摘要索引:让 LLM 为每一段内容生成一段摘要,用摘要来建向量索引。为什么这样做?因为文档原文有时候表述很散,而摘要是对核心意思的提炼,语义更聚焦,在向量空间里和用户的问题会更接近,命中率更高。检索时用摘要的向量匹配,命中后把原始段落塞给 LLM 阅读。
- 多粒度分层索引:同时建章节级、段落级、句子级三层索引。不同类型的问题适合不同粒度:「什么是 RAG」这种宽泛的概念性问题,用章节级就够了;「退款申请需要几个工作日」这种细节性问题,用句子级更精准。
(2)查询层:查询优化
查询优化主要是查询改写,改写的方式有以下四种,前面也提到过,具体内容,去查看前边的文档:
- Query 改写
- 多 Query 扩展
- HyDE
- Step-back Prompting
(3)召回层:召回优化
召回层的优化主要使用多路召回的方式,而多路召回的方式有以下3种
- 向量检索
- BM25精确匹配
- 多Query扩展
以上三种方法的具体原理前边已经提到过,这里不做赘述,而多路召回每一路往往使用以上3种方法,常用的是向量检索+BM25,高质量的场景下可以使用多Query扩展。最后召回的每一路的文档,使用RRF算法返回最满足要求的几个文档。
(4)重排序层
重排序层的作用就是经过前面三步,召回的chunk可能过多,这会让token消耗巨大,也会存在Lost in the Middle(LLM只关注开头和结尾,中间的内容容易被忽略的现象。)
Rerank 用的是 Cross-encoder 结构,把「query + chunk」拼成一对输入,让模型整体看这一对的相关性。chunk 里哪些词最能回答 query,相关性判断精度远高于 Bi-encoder。代价是每一个候选 chunk 都要单独跑一次 Cross-encoder,速度慢,所以只适合对小规模候选集做精排,不适合大规模召回阶段。
常用的开源 Rerank 模型有 BGE-Reranker-v2(BAAI 出品,中英双语效果都很好)、BCE-Reranker;
16 RAG有哪些复杂的范式
首先接受一下RAG的演进
(1)Naive RAG:逻辑非常直白:用户提问 -> 向量检索 -> 拼 prompt -> LLM 生成:
- 优点:简单,很容易跑通
- 确定:用户提问的差返回的差,召回的内容质量有可能很低,容易让大模型出现幻觉
(2)Advanced RAG
核心思路是在「检索前」和「检索后」各加一道工序。检索前加 Query 改写和扩展,把用户口语化的提问打磨成更容易命中知识库的形式;检索后加 Rerank 精排和内容压缩,把召回内容里最相关的几条筛出来,不相关的过滤掉,减少喂给 LLM 的噪音。
(3)Modular RAG
把 RAG 的各个环节拆成可以独立替换的模块,像乐高一样按需组合:检索模块可以选向量检索、BM25 或图检索;改写模块可以选 HyDE、Step-back 或多 Query 扩展;生成模块可以是普通输出,也可以是带引用的结构化输出。不同业务场景挑不同模块组合,灵活性大幅提升。
然后介绍以下几种RAG的高级范式
(1)Self-RAG:LLM 自己决定要不要检索
Self-RAG解决的问题是用户的问题要不要进行RAG检索,它 训练了一个特殊的 LLM,自主决定:
- 当前问题需不需要检索?(Retrieval token)
- 检索回来的内容和问题相不相关?(Relevance token)
- 生成的答案有没有幻觉?(Support token)
- 最终答案质量够不够好?(Utility token)
其执行流程如下:

【1】判断是否需要检索:LLM 先评估这个问题要不要查知识库。如果是常识性问题或者不需要外部信息,直接生成答案。
【2】检索并逐条评估相关性:对每一个召回的 chunk,LLM 独立判断「这段内容和问题相关吗」,不相关的直接跳过。
【3】基于相关 chunk 生成候选答案:每个相关 chunk 各自生成一个候选答案。
【4】评估答案质量:对每个候选答案,LLM 打两个分,有没有文档支撑(防幻觉)、答案对用户有没有用。
【5】选出最优答案返回:综合两个分数,选出最好的那个候选答案。
(2)CRAG(Corrective RAG):检索质量差时自动纠错
CRAG 在检索完之后加了一个质量评估环节:如果检索到的内容质量高,正常走 RAG 流程;如果质量低,自动切换到网络搜索,用搜索结果代替知识库内容来回答问题;如果质量居中,把知识库结果和网络搜索结果都用上。
CRAG 的执行流程如下:
【1】本地检索:先从知识库里召回 top-K 个 chunk。
【2】质量评估:用一个轻量级的检索评估器(可以是专门训练的分类模型,也可以用 Rerank 模型的分数来近似)来判断检索结果和问题的相关程度。
【3】级路由决策:根据评估结果分成三档。评估为「相关」,直接用本地结果生成答案;评估为「不相关」,说明知识库没有覆盖这个问题,丢弃本地结果,降级走网络搜索;;评估为「模糊」,把本地结果和网络搜索结果合并一起用,两者互补。
【4】生成答案:基于最终上下文生成回答。
(3)GraphRAG:用知识图谱增强全局理解
GraphRAG 就是微软在 2024 年 7 月发布论文的一套方案,它把知识图谱、社区发现和层次摘要这几样现成工具组合起来解决这个问题,核心价值是「系统化组合」而不是单点发明。主要是解决需要全局理解的问题(比如「这批文档的核心主题是什么」「A 公司和 B 公司之间有什么关联」)。
核心做法分两步:
【1】预处理阶段,先用 LLM 从文档里抽取实体和关系,建成知识图谱,然后用 Leiden 等社区发现算法对图谱中的实体做聚类,把紧密关联的实体分成一个个「社区」,再对每个社区生成一份 LLM 摘要,描述这个社区里的实体之间是什么关系、整体在讲什么。这些社区摘要会形成多层的层次结构,从细粒度到粗粒度都有覆盖。
【2】检索阶段,GraphRAG 支持两种查询模式。对于局部问题(比如「A 公司的 CEO 是谁」),可以直接在知识图谱中做实体查找和关系遍历;对于全局问题(比如「这些文档的主要主题有哪些」)检索阶段,GraphRAG 支持两种查询模式。对于局部问题(比如「A 公司的 CEO 是谁」),可以直接在知识图谱中做实体查找和关系遍历;对于全局问题(比如「这些文档的主要主题有哪些」)
(4)Agentic RAG:把 RAG 做成 Agent
Agentic RAG 把 RAG 嵌入 Agent 循环,LLM 可以自主决定:要不要再检索一次、用什么关键词检索、检索结果是否足够、什么时候可以生成最终答案。
具体执行流程如下:
【1】接收问题,进入循环:LLM 拿到用户问题,开始 Agent 决策循环。LLM 决定下一步动作:
【2】根据当前已收集到的上下文,LLM 判断,信息够了吗?如果不够,下一步该搜什么关键词?
【3】执行检索,累积上下文:按 LLM 指定的关键词检索,把新召回的 chunk 追加到已有上下文里。重复判断,直到信息充分:
【4】LLM 每轮都重新评估「现在能回答了吗」,不够就继续检索,够了就生成答案。
【5】生成最终答案:信息充分后退出循环,基于所有累积的上下文生成回答。为防止死循环,设置最大迭代次数兜底。
这套机制特别适合需要多步骤推理的复杂问题,比如「帮我分析 A 公司最近三年的财报,找出营收增速放缓的根本原因」,这类问题需要多次、有针对性地检索不同维度的内容,一次检索完全不够。
总结:
Self-RAG 解决「不是所有问题都需要检索」的问题,让 LLM 自主决策;
CRAG 解决「检索质量差时怎么办」的问题,自动降级到网络搜索兜底。
GraphRAG 解决「全局理解和跨文档关联」的问题,用社区发现和层次化摘要补上向量检索只能做局部匹配的短板;
Agentic RAG 解决「复杂问题需要多轮动态检索」的问题,把 RAG 做成 Agent 循环。
17 如何规避RAG系统中大模型的幻觉
RAG 幻觉主要有两类:
- 检索没有召回到相关内容,LLM 没有可用的上下文,
- 检索内容召回了但 LLM 没有严格遵循,在文档内容的基础上加了自己的推断。
解决方法有4个
(1)Prompt 强约束,明确告知 LLM 只能根据提供的资料回答,资料里没有的就说不知道;具体方法是::在 system prompt 里明确立规矩,只能用资料里的内容、资料里没有就说不知道、不准推断和补充。
(2)第二是检索质量门控,Rerank 分数低于阈值就直接拒答,不让 LLM 在低质量上下文上硬撑;
(3)第三是生成后引用核查,每个关键的声明都要在 chunk 里找到来源依据;
实现思路是:LLM 生成完答案,再用另一个 LLM(或者同一个 LLM)回头检查,答案里的每一条关键信息,在 chunk 里有没有对应的依据。没有依据的内容,标注「无法核实」或者直接删掉。
(4)第四是结构化输出强制溯源,让 LLM 输出 JSON,每条结论必须附上来源编号。具体方法是:让 LLM 输出结构化的 JSON,每个结论都必须填写来自哪条参考资料的编号。
18 RAG怎么及逆行量化评估
RAG的量化评估,主要分两层
(1)检索曾评估
不管 LLM 生成什么,先单独评估检索有没有把正确的 chunk 召回来。需要准备一批「问题 + 对应的正确 chunk ID」的测评数据(可以从历史问答里标注,也可以让领域专家整理)。
这一层主要有两个指标:
Hit@K :把 Top-K 的检索结果摆在你面前,你要找的那个在里面吗?如果在,这次就算命中(Hit)。Hit@5 就是说,把前 5 个结果给你,能不能命中,最终统计命中率。一般 Hit@5 低于 0.7 就说明检索层有问题,需要考虑换 Embedding 模型或者优化 Chunking 策略;高于 0.8 说明检索层 OK,如果答案还不好,问题在生成层。
MRR(Mean Reciprocal Rank,平均倒数排名): 关心的是「你要找的东西排在第几名」。计算公式也很直观,就是对每个问题算 1 / 排名,然后对所有问题求平均。。所以第一名找到得 1 分,第二名找到得 0.5 分,第三名得 0.33 分,第五名得 0.2 分,排名越靠后得分越低。MRR 越高,说明正确内容排名越靠前,用户越早看到它。这个指标回答的是「多快找到的」。MRR 低于 0.5 通常说明 Rerank 效果不够好,正确内容召回了但没排到前面。
(2)生成层评估(RAGAs 框架)
是目前使用很广泛的 RAG 端到端评估框架,它的核心思路叫做「LLM-as-a-Judge」,意思是用 LLM 来当裁判,自动给答案打分,不需要人工标注每一条,大幅降低评估成本。它有四个核心指标,分别是 Faithfulness、Answer Relevancy、Context Recall 和 Context Precision,每个都有直观的理解方式,下面挨个讲。
【1】Faithfulness(忠实度):答案里说的每件事,在检索到的 chunk 里有没有出处?这个指标衡量的是幻觉程度。你可以把它理解为「LLM 裁判」在逐句问:这句话你从哪条资料里找到的依据?」没有依据的句子越多,分越低。目标值是 > 0.8。
优化方向:Faithfulness 低,说明 LLM 在编造,幻觉问题多,你回答里说的东西在参考资料里找不到依据,优化方向是加强 Prompt 约束、引入引用核查、或者做检索质量门控,防止低质量上下文进入生成阶段。
【2】Answer Relevancy(答案相关性):答案有没有回答用户问的那个问题?注意这个指标和 Faithfulness 是两回事,很多人会把它们搞混。Faithfulness 是问「说的是不是真的」,Answer Relevancy 是问「说的是不是用户想要的」。一个答案可以字字有据、但完全跑题,Faithfulness 高、Answer Relevancy 低。打个比方,你问「北京天气怎么样」,AI 回答了一篇关于北京历史的资料,内容全是对的,但和天气没有半毛钱关系,这就是 Faithfulness 高、Answer Relevancy 低。目标值是 > 0.8。
优化方向:Answer Relevancy 低,说明答案跑题了,没有聚焦在用户问的问题上,通常是 Prompt 的指令不够明确,告诉 LLM「请严格回答问题本身,不要展开无关内容」往往就能改善。
【3】Context Recall(上下文召回率):要回答这个问题,所需要的信息有多少比例在检索结果里覆盖到了?这个指标需要有「标准答案」作为参照,衡量的是检索层有没有「漏掉该找到的内容」。目标值是 > 0.7。
优化方向:Context Recall 低,你可以把它理解为「检索结果里缺少了答好这道题必要的信息」,说明检索层没召回到正确内容,优化方向是换更强的 Embedding 模型、调整 Chunking 策略、或者加多路召回来补充覆盖面。
【4】Context Precision(上下文精确率):这个指标和 Context Recall 配对出现,衡量的是检索结果里「有用的内容」排名是否靠前。也就是说,如果你召回了 10 个 chunk,相关的那几个有没有被排在前面,而不是混在无关内容的后面。它同样需要 ground_truth 作为参照计算。Context Recall 关注「该找的有没有找全」,Context Precision 关注「找到的里面相关的是不是排在前面」,两个配合能完整刻画检索质量。
优化方向:Context Precision 低,说明检索召回了太多噪音,相关内容是找到了,但不相关的内容也混进来了,把 LLM 的注意力稀释掉了,优化方向是加强 Rerank 模型、调低最终送给 LLM 的 chunk 数量。
需要注意的是,RAGAs 本质上是「LLM-as-a-Judge」,每次评估都要调用 LLM 来打分。如果测试集有几千条,全量跑一遍的 token 消耗和时间成本相当可观。工程上通常有两种缓解方式:一是对核心测试集抽样评估,只跑最有代表性的 200~500 条;二是把评判者模型从 GPT-4o 降级到 GPT-4o-mini,成本降低 10 倍,精度损失在可接受范围内。
以上说的都是离线指标,而线上指标有以下几种:
- 踩率(thumbs_down_rate)是最直接的信号,用户主动点踩,说明这次回答让他不满意,是最真实的负反馈。
- 追问率(followup_rate)反映的是「答非所问」的程度,用户紧接着说「你没回答我的问题」或者追问同一个问题,通常意味着上一次回答没用。
- 转人工率(escalation_rate)衡量的是「RAG 放弃回答」的频率,这个比例太高说明知识库覆盖不足;但如果这个比例因为加了质量门控而上升,不一定是坏事,宁可转人工也不要给用户错误答案。
- 空回答率(answer_empty_rate)就是系统主动说「我不知道」的比例,过高说明知识库亟需扩充。
- 会话解决率(session_resolution_rate)是最综合的指标,衡量「一次对话能不能解决用户的问题」,是最贴近用户体验的衡量维度。
19 RAG 知识库如何实现动态与持续更新
知识库操作方法:
- 新增:走一遍完整的「切割 -> Embedding -> 写入」流程就行
- 修改:检测文档内容是否更新,检测到更新,将原文档和对应的chunk删除,然后重新走新增的流程即可。检测更新的方式为,首先确认文档的最后修改时间是否一直,不一致,计算文档内容的 MD5 或 SHA256 摘要, 然后与旧的文档的MD5或SHA256摘要的hash值对比,改变了就删除旧文档,然后走新增逻辑。新增时把这个 hash 值和文档 ID、对应的 chunk ID 列表一起存下来(存在 Redis、数据库都行)。下次检测到这篇文档时,重新计算 hash 和存储的值对比:
- 删除:删除时只需要将对应的文档和文档所有的chunk删除即可
怎么感知文档有变更呢?有两种方法
- 定时轮询:指定时间,让定时任务在对应的时间轮询检测
- 消息队列:数据源变更时,主动发送一条消息到Kafka,webhook等消息组件,知识库服务订阅对应的消息,收到时间立刻处理。
全量更新:
适用场景:知识库规模很小(几十篇文档,重建几分钟搞定);或者做了重大架构调整(比如换了 Embedding 模型、改了 Chunking 策略),新旧向量不兼容,必须全量重建。平时不推荐依赖这个方案。
灰度更新:稳妥地切换新版本
对于核心的生产知识库,直接删旧数据、写新数据风险还是太大了。万一新切割的内容有问题,想回滚都来不及。那怎么办?更稳妥的做法是不直接删旧数据,而是先并行写入新版本,验证没问题再切换。
具体操作是:把新版本的 chunk 写入时打上 version=new 的标签,旧版本保留 version=old。在验证阶段,用一批测试问题同时跑新旧两个版本,对比答案质量,确认新版本没有引入退化。验证通过后,把检索时的版本过滤条件从 old 切换到 new,最后再清理掉旧版本的 chunk。
20 RAG检索难点
(1)文档预处理
例如,pypdf 这类只适用普通文本,对于表格,双栏,嵌套pdf这种格式的处理不了,会把内容搞乱
表格和复杂版面应该交给 pdfplumber、unstructured 这类专门优化过结构化提取的库。
(2)检索质量调优
文档预处理保证了输入质量,但如果检索这一步不准,前面的努力就全白费了。检索质量是整个 RAG 系统效果的天花板,检索召回不到相关内容,后面的 LLM 再强也没用。但检索质量差的原因可能来自好几个地方,定位起来特别麻烦。
- Chunking 策略是第一个排查点。chunk 切得不好,用户问的问题和知识库里的相关内容语义对不上。比如用户问「退款流程是什么」,但知识库里的文档是按产品分类组织的退款相关的内容被切散在十几个不同的 chunk 里,每个 chunk 单独来看相关度都不高,导致召回的都是些边缘内容。
- Query 和文档的语义鸿沟:用户的提问往往是口语化的,而知识库里的文档是正式的技术或业务语言。比如用户问「这个功能怎么用不了」,文档里的表述是「系统故障排查指南」,向量相似度可能不高,导致正确的文档没被召回。解法是 Query 改写,或者在存文档时也为每个 chunk 生成几个可能的提问形式一起存进去(假设性问题增强)。
- 向量检索对精确词语效果差:向量检索往往是语义检索,对于专有名字,往往检索的效果不好。产品型号「Pro Max 256GB」、专有名词、缩写等,纯向量检索往往不如 BM25 关键词检索。所以通常使用混合检索,向量和BM25同时检索,然后多路召回。
(3)效果评估困难
主要从检索层和生成层两种方式评估
【1】检索层使用Hit@k的方式,主要是评估答案是否在召回的前K条里,比如 Hit@5 = 0.8,意思是 80% 的问题,它对应的答案都出现在了前 5 条检索结果里。、
【2】生成层:主要使用Faithfulness(忠实度)、Answer Relevancy(答案相关性)、Context Recall(上下文召回率)三种方式结合来评估
Faithfulness(忠实度)评估的是回答的每一句是否都有有对应的来源检索文档,
Answer Relevancy(答案相关性)评估的是问题与答案是否有相关性,比如问的是天气,回答的是城市的历史
Context Recall(上下文召回率):评估的是需要的信息有多少比例在检索结果里覆盖到了?