2022 年我整理过一篇 RAG:检索增强生成的收益与天花板,谈了它解决什么问题,又有哪些结构性的限制。这几年 RAG 的工程实践往前走了很多,切分、检索、重排每个环节都沉淀出了更细的方法。这篇把”怎么把知识库从能用调到好用”整理一遍,算是上一篇的续篇。
知识库在 Agent 里的位置
在 Agent 的语境下,知识库是一个外部记忆系统,存放不在模型参数里的信息,对应 RAG 原始论文里的非参数化记忆。RAG 的基本流程没有变过:
Query → Retriever → Top-K Documents → Context Augmentation → Generator → Response剥掉各种版本和理论的包装,知识库对外的核心接口只有两个:上传和召回。各家方案的差别,集中在召回什么内容、按什么顺序排。
用不用知识库,可以回到 LLM 的固有局限来判断:
- 训练结束后知识不再更新,新内容进不了参数。
- 模型会生成看似合理但实际错误的内容。
- 企业内部文档、私有数据不在训练语料里。
- 有些场景要求答案能附来源,方便核对。
知识更新快、内容量大、需要引用依据的场景适合知识库;反之,内容少且基本不变的场景不必引入这套设施。
另外一个越来越现实的对比是 Long Context。不同模型的上下文窗口差异很大,支持长度也会随版本和接口变化。长上下文让一部分原来需要 RAG 的场景可以直接提供文档,但能否这样做还要看输入长度、成本、延迟和模型在长文本上的实际效果。我的经验是:数据量较小、更新频率低且可以接受一次性处理成本时,可以直接用 Long Context;数据量大、更新频繁或需要精确召回某几条时,更适合用 RAG;介于两者之间的,可以先用 RAG 粗筛,再交给长上下文精读。50K tokens 只能作为某个项目的试验阈值,不能当成通用分界线。
先定义”好”:评估体系
想构建更好的知识库,得先能衡量好坏,否则优化无从谈起。
RAGAS(2023)是常见的 RAG 评估框架之一。它提供了一组基于参考答案或 LLM 判断的指标,部分指标可以在缺少人工标注时使用,但自动评分仍可能受评估模型和数据集影响,不能替代人工标注与端到端测试。它把评估拆成检索和生成两组指标。
检索侧:
- Context Precision:相关文档有没有排在不相关文档前面。同样召回 5 个 chunk,相关的 2 个排在第 1、2 位,得分就高于排在第 4、5 位的情况。
- Context Recall:回答问题所需的信息有多少被真的检索到了。做法是把参考答案拆成多个 claim,逐个判断能否归因到检索上下文,算被支持的比例。注意这个指标需要参考答案,不是完全的 reference-free。
生成侧:
- Faithfulness:答案里的每个声明能否从检索上下文中推断出来。用 LLM 从答案里抽 claim,再逐条对着上下文验证。这个指标的本质是检测幻觉。
- Answer Relevance:答案是否直接回应了问题。做法是反过来,从答案生成可能的提问,再看它和原问题的相似度。答案如果答非所问,反推出来的问题会和原问题差很远。
传统的 IR 指标(Precision、Recall、F1、NDCG)在检索侧依然适用。F1 是 Precision 和 Recall 的调和平均,两者差异大时会偏向较小的那个,所以 F1 高意味着两边都不太差。
有一个 RAG 场景特有的问题值得单独说:语义相似不等于对 LLM 有用。一段文字和 query 语义接近,不代表模型能基于它生成正确答案。ICLERB 提了一个端到端的评估思路:把检索候选注入 LLM 生成答案,用答案的准确性反推检索器和重排器的效果。选检索组件时只看 NDCG 容易选错,这个思路更接近真实体验。
另外,RAG 的幻觉可以拆成三类来定位:检索没召回相关内容、召回了但答案超出了上下文支撑、上下文充足但答案和问题不相关。三类问题分别对应检索、忠实度、相关性三个环节,拆开评估才知道该修哪里。不同场景对幻觉检测的要求差别很大,医疗和法律场景尤其严格,这篇 2025 年的综述 对检测方法有比较全的梳理。
构建流程的八个环节
知识库构建分离线索引和在线查询两个阶段:
离线:Load → Split → Embed → Store
在线:Query → Retrieve → Rerank → GenerateLoad 是把各种来源和格式的原始数据抽出来。Embed 是用 embedding 模型把文本块转成稠密向量。Store 把向量和元数据写入向量库建索引。这三步相对直白,真正影响效果的是剩下的环节,下面逐个说。
切分:影响最大也最容易被忽视
切分有个两难:chunk 切得太碎,语义不完整,检索回来时上下文不够用;切得太大,噪声多,embedding 的语义被稀释,还浪费上下文窗口。目标是在粒度和完整性之间找平衡。
常见策略可以按切分边界和上下文保留方式区分:
-
固定长度切分
- 说明:按固定的 token 或字符长度切块,可配置固定的 overlap。
- 优点:实现和吞吐量最稳定,chunk 大小可控,便于快速建立基线和估算索引成本。
- 缺点:可能在句子、表格或语义单元中间截断;固定 overlap 无法只在真正需要的边界保留上下文。
- 适用场景:文档结构较弱、来源格式杂乱,或需要先以最低成本建立原型的场景。chunk_size 和 overlap 应通过文档结构、模型窗口、召回效果和延迟验证。
-
递归切分
- 说明:按分隔符优先级逐层尝试切分,例如先标题和段落,再句子,最后才按字符或 token 截断。
- 优点:在块大小受控的同时,尽量保留自然语言和 Markdown 的结构边界;不需要额外模型调用,通常可作为通用文档的默认选择。
- 缺点:分隔符规则需要适配语料;超长段落、表格和代码块仍可能被拆散,且无法解决跨段依赖。
- 适用场景:以说明文、Markdown、网页正文和普通 PDF 解析文本为主的知识库。
-
结构化切分(document-aware chunking)
- 说明:利用标题层级、章节、列表、表格、代码块、页面或字段等文档结构确定边界,并将标题路径、页码等作为 chunk 元数据或正文前缀。
- 优点:chunk 的业务语义更完整,来源定位清楚;章节标题会进入检索表示,有助于区分同名概念在不同模块中的含义。
- 缺点:依赖可靠的文档解析;扫描件、格式混乱的 Office 文档和网页会降低结构提取质量,解析规则也需要维护。
- 适用场景:产品文档、规范、手册、Markdown、带稳定标题层级的 Wiki,以及表格和代码块不能被任意拆开的语料。
-
段落窗口切分(windowed chunking,也可称滑动窗口或重叠切分)
- 说明:先按自然段确定主内容,再为每个 chunk 拼入前一段的结尾和后一段的开头,或带上相邻的一到两个完整段落。
- 优点:保留段落边界两侧的上下文,能缓解定义、前提和结论跨段展开时的信息丢失。
- 缺点:相邻 chunk 的内容会重复,索引体积、embedding 成本和送入模型的 token 数都会增加;窗口过大也会引入无关文本。
- 适用场景:段落短、论述连续且跨段问答较多的文档。重叠范围应根据段落长度和 bad case 调整。
-
语义切分
- 说明:计算相邻句子或候选片段的 embedding 相似度,在语义变化较大的位置设置边界。
- 优点:边界可以随内容主题变化,而不完全受字符数或排版限制。
- 缺点:需要额外的 embedding 计算和阈值选择。Is Semantic Chunking Worth the Computational Cost? 的实验显示,在文档检索、证据检索、答案生成三个任务上,它相对固定长度切分的提升有限,计算成本却增加。
- 适用场景:章节结构弱但主题转换明确的长文本;应先和固定长度或递归切分在自己的评估集上比较,不能默认收益覆盖成本。
进阶一点的:
-
Late Chunking(Jina AI,2024)。传统流水线先按边界把文档切成 chunk,再分别调用 embedding 模型。例如一篇产品文档把“权限配置”的前提写在第一段、具体操作写在第二段,第二段单独编码时,模型只能根据这段文字生成向量;即使检索命中了操作步骤,也可能缺少它依赖的角色、版本或限制条件。
Late Chunking 调换了编码和切分的顺序。先将整篇文档作为一个序列输入长上下文 embedding 模型,取模型为每个 token 输出的最后一层表示;然后按预先确定的字符或 token 边界,把属于同一 chunk 的 token 表示做 mean pooling,得到该 chunk 的检索向量。Transformer 的自注意力在编码阶段允许 token 关注文档中的其他 token,因此第二段中每个 token 的表示已经吸收了前文的相关信息。最终索引的仍是 chunk 级向量,向量库和在线检索流程不需要改成按整篇文档检索。
它解决的是“局部块本身短、但理解它需要跨段上下文”的表示问题,不会自动补齐未写进文档的事实,也不保证每个查询都优于普通切分。工程上要额外注意三个约束:文档必须在模型的有效上下文窗口内,超长文档仍需先分为较大的窗口;切分边界要能映射回 token 位置,避免在 token 中间错误截断;一次编码整篇文档的显存、延迟和批处理方式与逐 chunk 编码不同。是否采用应在自己的跨段问答样本上比较 Context Recall、端到端答案质量以及索引成本。
-
Small-to-Big。检索用小粒度 chunk 保证匹配精度,命中后把它所在的父段落或前后窗口取出来喂给 LLM,解决”匹配准但上下文不够”的问题。
-
意图驱动切分。更新的一个方向,切分边界不由文档结构决定,而是由”用户可能问什么问题”决定,先预测信息需求再组织 chunk。对长文档和异构文档比较有吸引力,我还在观察它的工程成本。
实践上的建议:先用递归切分建立 baseline,重叠比例作为待验证参数。可以结合 RAGAS 的 Context Precision 和 Context Recall,以及人工抽样评估切分效果,重点检查切分是否切断了答案和实体。切分时保留来源、章节、页码等元数据,然后按 bad case 迭代。
召回:稀疏、稠密与混合
召回是核心环节。这一阶段漏掉的文档,后面的 Rerank 和生成都补不回来。
稀疏检索的代表是 BM25,基于词频统计打分:词在文档里出现越多、在全局语料里越稀有,得分越高,同时用文档长度做归一化。它擅长精确匹配,专有名词、错误码、型号这类查询效果很好,但处理不了语义等价,查”怎么重置密码”匹配不到”找回账户口令”。
稠密检索用 embedding 模型把文本映射到向量空间,按余弦或内积找近邻,正好补上语义匹配这块,但对没见过的专有名词和长尾词反而不如 BM25 稳定。
所以工程上一般是混合检索,两路各召回一批再融合。融合常用两种方法:
- Reciprocal Rank Fusion(RRF):score = Σ 1 / (k + rank_i),rank_i 是文档在第 i 路召回里的排名,k 通常取 60。不需要调权重,鲁棒性好。
- 加权线性组合:对两路分数做归一化后加权求和,权重需要用验证集或离线评估调节,不能预设一个跨数据集通用的范围。可调空间更大,但需要数据。
查询侧也有很大的优化空间,因为用户的原始提问经常不适合直接检索:
- 查询改写。让 LLM 把口语化的提问改写成规范的检索语句,多轮对话里还要把指代补全。
- HyDE(Precise Zero-Shot Dense Retrieval without Relevance Labels)。先让 LLM 生成一段假设性答案,再用这段文本的 embedding 去检索。它的假设是生成文本可能比原始问题更接近相关文档,但生成内容本身也可能引入错误,需要用数据验证。
- Multi-Query。生成多个查询变体分别检索,合并去重,缓解单一问法的偏科。
- EAR(Expand, Rerank, and Retrieve)。它提出先生成多个查询扩展,再对扩展排序并进行检索,用来缓解单个扩展不理想的问题。论文中的提升来自特定数据集、任务和指标,不能直接外推到所有知识库。采用前应在自己的数据上复现或评估。
语料规模特别大的时候还有层次化检索(Dense Hierarchical Retrieval):先在文档或章节级别粗筛,再在命中的范围内做段落级精排,避免短段落脱离全局上下文,也能利用标题和章节结构。
索引实现上,HNSW 是常用的近似最近邻索引,通常需要结合数据规模、更新方式、召回率目标和资源约束选择,不能在所有场景直接作为默认。
重排:补上双编码器缺的那块交互
先看双编码器怎么工作。离线阶段,系统会把库里的每个 document 单独编码成一个固定长度的向量并写入向量库;用户提问时,query 也单独编码成向量,再通过向量距离从海量 document 中找最近的候选。document 向量可提前计算并建立索引,所以一次查询只需要编码 query 和搜索向量库,速度足以覆盖大规模语料。
它的限制在于:document 在生成向量时还不知道未来会收到什么 query,query 在生成向量时也看不到具体候选文档。模型只能用两个独立向量的距离来表示“整体上是否相近”。例如 query 是“管理员如何重置密码”,一个候选讲“管理员重置密码的操作步骤”,另一个讲“普通用户如何找回密码”。两者都可能与 query 接近,但仅比较独立向量时,模型较难准确判断“管理员”和“普通用户”这个限定条件的重要性。
Reranker 通常采用交叉编码器。它对每个候选分别输入 [query, document] 这一对文本,直接输出相关性分数。自注意力在编码时可以让 query 里的“管理员”关注 document 中对应的角色描述,也可以判断“重置”和“找回”在当前上下文中是否等价,因此排序通常更细。代价是每个 query-document 对都要重新跑一次模型,document 不能提前算好一个可复用的向量,也不能直接在向量库中完成搜索。
所以两类模型分工使用:双编码器先从大量文档中快速召回候选,交叉编码器只对这批候选逐个打分后重排。
典型流程是:向量检索召回 Top-100,Reranker 精排,取 Top-5 送入 LLM。
几个实践要点:
- 召回数 K 和精排数 N 要权衡。K 太小会漏相关文档,太大会推高延迟,因为交叉编码器需要处理更多候选。K 和 N 应根据语料规模、召回率、延迟预算和端到端效果调节,50 到 100 与 3 到 10 只能作为某些项目的起点。
- Reranker 输出的分数通常只在同一模型、同一批候选和相近任务设置下有可比性。是否设阈值截断要用验证集校准,不能直接把某个固定分数当成通用标准。
- 混合检索时,先用 RRF 融合多路排名,再送 Reranker,比各自精排再合并更顺。
模型选型上,开源的 bge-reranker 系列和 Qwen3-Reranker 可以自部署,Cohere Rerank 和 Jina Reranker 走 API,中文语料为主的话优先看前两者。选型别只看公开榜单,用自己的 query 和语料跑一遍 ICLERB 式的端到端评估更可靠。
几个值得看的项目
把视野放宽一点,社区里有几个项目代表了不同的优化方向。
AutoRAG(GitHub)解决的是组合爆炸问题。RAG 系统里分块策略、embedding 模型、检索方式、Reranker 每个模块都有很多选项,不同组合在不同数据集上表现差异很大,手动调既慢又找不到最优。AutoRAG 把 pipeline 的各模块参数化,在自己的数据集上自动跑评估、搜出最优组合。适合需要为特定领域调配置、又缺乏调优经验的团队。
QuIM-RAG 的思路是把”query 对 document 的匹配”转成”query 对 query 的匹配”:离线为每个文档块预生成它可能回答的问题并建倒排索引,在线拿用户问题去匹配这些预生成的问题,再取回对应文档。论文或项目报告中的部署案例和指标结果应放在其数据集、配置和基线条件下理解。即使某个案例在 BERT-Score 和 RAGAS 指标上优于传统 RAG,也不能直接推断在其他语料上同样成立。它缓解的是直接用 query 撞文档时语义匹配不准、信息被稀释的问题。
OpenViking(字节跳动火山引擎)走得更激进,用文件系统范式重构上下文管理。它针对的痛点和我上一篇结尾说的结构问题是一类:记忆、资源、技能分散在各处,扁平存储缺少全局视角,隐式检索链出错难调试,记忆只是被动记录。它的做法是把上下文组织成带目录层级的虚拟文件系统,检索变成可解释的路径导航加语义匹配,并且让 Agent 在任务过程中主动整理和更新记忆。适合长时间运行的 Agent 和对检索可解释性要求高的场景。
收尾
把上面的内容压缩成几条原则:
- 评估先行。先把 RAGAS 之类的评估跑起来,再谈优化,否则每次改动都不知道是变好还是变坏。
- 从简单开始。递归切分加混合检索加一个轻量 Reranker,是性价比很高的起点,大部分场景够了。
- 用 bad case 定位瓶颈。幻觉是检索没召回、还是生成超出了上下文,对应的修法完全不同。
- 看端到端指标。单个环节的 NDCG 好看不代表最终答案好,语义相似和对 LLM 有用是两回事。
- 处理知识的时序。大多数 RAG 索引把文档视为静态快照,但企业知识会更新、失效甚至相互矛盾。索引需要保留生效时间、版本、来源和更新时间;查询时还要根据问题的时间范围过滤或排序,避免把已经失效的规则和当前规则一起交给模型。
- 把关系可视化。文档、实体、章节、版本和引用之间的关系仅靠 chunk 元数据很难检查。知识图谱可以把这些节点和边展示出来,用于发现孤立文档、重复知识、冲突版本和错误关联;它更适合作为知识治理与检索调试的界面,是否参与在线检索仍要用端到端评估验证。
知识库构建没有标准答案,能做的是根据数据特点、业务场景和资源约束把每个环节的 trade-off 想清楚。至于上一篇结尾提的结构问题,文件系统范式和知识图谱提供了两种组织与检查关系的路径:前者强调可导航的层级,后者强调实体与文档之间的关联。时序则要求这些结构能表达知识在什么时间有效。OpenViking 这类文件系统范式是一个值得关注的尝试,但目前还没有成为主流做法;时序建模和图谱如何进入实际检索链路,也还需要更多实践验证。

