1. 为什么知识获取管道是 AI Agent 的第一道生死关
做 AI Agent 开发的人,十有八九都经历过这样一个阶段:模型选型纠结了半天,工具调用调通了,提示词也打磨得差不多了,结果一上真实业务场景,Agent 给出的答案就开始胡编乱造。不是它不够聪明,而是它根本不知道你的业务里那些私有知识。这就是知识获取管道要解决的问题。
所谓知识获取管道,说白了就是让 Agent 在回答问题之前,先去“查资料”。这个“查资料”的过程,在业界有一个大家更熟悉的叫法——RAG(Retrieval-Augmented Generation,检索增强生成)。它的核心逻辑不复杂:用户提问 → 系统从知识库里检索相关内容 → 把检索结果和原始问题一起塞给大模型 → 模型基于这些材料生成回答。听起来简单,但真正落地的时候,从文档解析、分块策略、嵌入模型选型、向量库搭建到检索排序,每一步都有坑。
我见过太多团队在这一步翻车。有的把 PDF 直接丢进去解析,表格全乱;有的分块大小拍脑袋定,检索出来的内容驴唇不对马嘴;有的嵌入模型选了个通用款,结果在垂直领域里检索命中率惨不忍睹。更常见的是,检索回来的内容明明是对的,但模型就是不用,或者用错了——这又涉及到提示词编排和上下文窗口管理的问题。
这篇文章面向的是正在搭建 AI Agent、准备接入知识库的开发者,不管你是用 LangChain、Spring AI 还是自己手搓管道,RAG 的基础逻辑和实操细节都是绕不过去的。我会从整体架构讲到具体参数,从嵌入模型选型讲到检索策略优化,尽量把每一步的“为什么”说清楚,让你不只是抄配置,而是真正理解这套管道怎么跑起来的。
2. RAG 管道的整体架构与核心环节拆解
2.1 从文档到向量:一条完整的数据流水线
RAG 的离线部分本质上是一条数据处理流水线。原始文档进来,经过解析、清洗、分块、嵌入、存储五个阶段,最终变成向量数据库里的一条条记录。每个阶段都有它的脾气。
文档解析是第一道坎。PDF、Word、HTML、Markdown、Excel,每种格式的解析难度完全不同。PDF 最麻烦,尤其是扫描件和复杂排版,纯文本提取工具经常把表格和正文混在一起。我的经验是,如果文档里有大量表格,优先用能保留结构信息的解析器,比如把 PDF 转成 Markdown 再处理,比直接提取纯文本效果好得多。
清洗阶段要做的事情包括去页眉页脚、去重复段落、修正 OCR 错误、统一标点符号。这一步看起来不起眼,但直接影响后续分块质量。我试过跳过清洗直接分块,结果检索出来的内容里夹杂着一堆“第 3 页 共 12 页”这种噪音,模型被干扰得很厉害。
分块是整条管道里最需要动脑子的环节。块太大,检索精度下降,因为一个块里可能混了好几个主题;块太小,上下文不完整,模型拿到手也不知道前因后果。业界常见的做法是设置一个基础块大小(比如 512 个 token),再叠加一定的重叠区域(比如 50 到 100 个 token),保证相邻块之间有上下文衔接。但这不是万能公式,具体数值要根据你的文档类型来调。
嵌入阶段就是把文本块转成向量。这里涉及到稠密嵌入和稀疏嵌入两条技术路线,后面会详细展开。存储阶段则是把向量和原始文本、元数据一起写进向量数据库,元数据包括来源文件、页码、章节标题等,方便后续检索时做过滤。
2.2 在线检索:从用户提问到上下文组装
在线部分是从用户提问开始的。用户的问题进来,先经过查询理解模块——有时候需要做查询改写,把口语化的提问转成更适合检索的形式;有时候需要做查询扩展,把同义词、相关概念加进去,提高召回率。
然后就是检索环节。向量检索是基础操作,用嵌入模型把查询转成向量,在向量库里做相似度搜索,返回 Top-K 个最相似的块。但纯向量检索有个问题:它对关键词匹配不敏感。比如用户搜一个产品型号“XR-2000”,向量检索可能返回一堆语义相似但型号不同的内容。这时候就需要混合检索,把稠密向量和稀疏向量(比如 BM25)的结果融合起来,兼顾语义理解和关键词精确匹配。
检索回来之后,通常还要做重排序。因为向量相似度高不代表内容真的相关,用一个交叉编码器或者专门的重排序模型对候选结果做精排,能显著提升最终上下文的质量。重排序的代价是增加延迟,所以一般只对 Top-20 到 Top-50 的结果做精排,再取 Top-3 到 Top-5 送给模型。
最后是上下文组装。把检索到的内容按一定格式拼接,加上系统提示词,一起塞给大模型。这里要注意上下文窗口的限制,不能无限堆砌检索结果,否则要么超长被截断,要么模型注意力被分散。我的做法是给每个检索块加上来源标注,让模型知道每段话出自哪里,生成回答时也方便引用。
2.3 为什么选择 RAG 而不是微调
很多人会问:为什么不直接微调模型,把知识灌进去?这个问题我踩过坑之后才想明白。微调适合教模型“怎么说话”,比如调整语气、格式、推理风格;RAG 适合教模型“说什么”,也就是注入事实性知识。微调的成本高、周期长,而且知识更新一次就要重新训练一次。RAG 的知识库可以随时增删改查,今天加一份新文档,明天就能被检索到。
另一个现实问题是,微调后的模型仍然可能胡编乱造,而且你很难追溯它为什么编。RAG 的检索结果是可以审计的,你能看到模型是基于哪些材料生成的回答,出了问题也能定位是检索错了还是生成错了。对于企业级应用来说,可追溯性有时候比准确率还重要。
3. 稠密嵌入与稀疏嵌入:两条技术路线的选型与配合
3.1 稠密嵌入:语义理解的基石
稠密嵌入的核心思想是把文本映射到一个低维稠密向量空间,语义相近的文本在空间中的距离也相近。比如“如何重置密码”和“忘记密码怎么办”这两句话,字面重叠很少,但稠密嵌入能把它们映射到相近的位置,检索时就能匹配上。
稠密嵌入模型的选择直接决定检索质量。目前主流的选择包括 OpenAI 的 text-embedding 系列、BGE 系列、GTE 系列、Jina 系列等。选型时要考虑几个维度:向量维度、最大输入长度、多语言支持、推理速度、以及在你所在领域的实际表现。
向量维度不是越高越好。高维度理论上能表达更丰富的信息,但存储成本和检索延迟也会上升。768 维和 1024 维在实际检索效果上差距可能很小,但存储成本差了不少。我的建议是先用一个中等维度的模型跑 baseline,如果检索命中率不达标再考虑升级。
最大输入长度很关键。很多嵌入模型只支持 512 个 token,超过部分会被截断。如果你的文本块设得比较大,就要选支持更长输入的模型,比如支持 8192 个 token 的模型。但要注意,支持长输入不代表长文本的嵌入质量就好,有些模型在长文本上表现会下降。
多语言支持对于中文场景特别重要。有些模型在英文上表现很好,但中文语义理解能力一般。选型时一定要用你自己的业务数据做测试,不能只看论文里的 benchmark 分数。
3.2 稀疏嵌入:关键词精确匹配的保障
稀疏嵌入的代表是 BM25 和 SPLADE。BM25 是经典的信息检索算法,基于词频和逆文档频率计算相关性。它的优势是关键词匹配精确,比如用户搜“Spring AI RAG”,包含这些词的文档会被优先返回。缺点是它不理解语义,“密码重置”和“重置密码”在 BM25 看来可能是两个不同的查询。
SPLADE 是一种学习式的稀疏表示方法,它通过模型预测每个词的重要性权重,既能保留关键词匹配的能力,又引入了一定的语义扩展。比如它可能会给“密码”这个词很高的权重,同时给“重置”“忘记”“修改”等相关词也分配一定权重,提高召回率。
稀疏嵌入的存储方式和稠密嵌入不同。稠密向量用浮点数组存储,稀疏向量用倒排索引或者键值对存储。很多向量数据库现在同时支持两种索引,比如 Milvus、Qdrant、Weaviate 都提供了混合检索能力。
3.3 混合检索:1+1 大于 2 的实践策略
纯稠密检索和纯稀疏检索各有短板,混合检索就是把两者的结果融合起来。融合策略主要有两种:一种是加权求和,给稠密和稀疏的相似度分数各分配一个权重,然后相加排序;另一种是倒数排名融合(RRF),不看具体分数,只看排名,把两个列表的排名做加权融合。
RRF 的好处是不需要调分数权重,对不同检索器的分数尺度不敏感。公式很简单:对于每个文档,它的融合分数等于所有检索器中该文档排名的倒数之和。比如一个文档在稠密检索里排第 3,在稀疏检索里排第 5,那它的 RRF 分数就是 1/3 + 1/5 = 0.533。按这个分数重新排序,就能得到融合后的结果。
实际使用中,我通常先用混合检索召回 Top-50 左右的结果,再用重排序模型精排到 Top-5。这个流程在多个项目里验证下来,检索命中率比纯向量检索提升了 15% 到 30%,具体提升幅度取决于文档类型和查询特点。
4. 从零搭建 RAG 管道的实操步骤与参数计算
4.1 环境准备与工具选型
搭建 RAG 管道的第一步是选工具。如果你用 Python,LangChain 和 LlamaIndex 是两个主流框架,前者生态更丰富,后者在检索策略上更灵活。如果你用 Java,Spring AI 提供了完整的 RAG 支持,LangChain4j 也是一个选择。如果不想依赖框架,直接调嵌入模型 API 加向量数据库客户端也能跑通,代码量并不大。
向量数据库的选择要考虑数据规模、部署方式和检索性能。小规模数据(百万级向量以内)用 Chroma 或 FAISS 就够了,本地文件存储,零运维成本。中等规模(千万级)可以考虑 Qdrant 或 Milvus,支持分布式部署和混合检索。大规模(亿级以上)通常需要专门的向量数据库集群,比如 Milvus 集群版或者云服务。
嵌入模型方面,如果预算充足且数据可以出外网,OpenAI 的 text-embedding-3-small 性价比很高。如果要求本地部署,BGE-M3 是一个很稳的选择,支持多语言、长文本,而且有稠密和稀疏两种输出。中文场景下,BGE 系列和 GTE 系列都有不错的表现。
4.2 文档解析与分块策略的确定
文档解析我推荐用 unstructured 库,它支持多种格式,而且能保留一定的结构信息。对于 PDF,如果排版复杂,可以先用 PyMuPDF 提取文本,再用正则表达式做清洗。对于 HTML,BeautifulSoup 是标配。对于 Markdown,直接按标题层级切分就很自然。
分块策略我通常分两步走。第一步按文档的自然结构切分,比如 Markdown 按标题切,PDF 按段落切。第二步对过长的段落做二次切分,用递归字符分割器,按段落、句子、词的优先级依次尝试,直到块大小符合要求。
块大小的确定需要做实验。我的做法是准备一组典型查询,然后测试不同块大小下的检索命中率。通常 256 到 512 个 token 是一个合理的起点。重叠区域设为基础块大小的 10% 到 20%,比如块大小 512,重叠 64 到 100 个 token。
这里有一个容易被忽略的细节:分块时要保留元数据。每个块都要记录它来自哪个文件、哪个章节、在原文中的位置。这些元数据在检索时可以用来做过滤,比如只检索某个产品线的文档,或者在生成回答时标注引用来源。
4.3 嵌入计算与向量入库的完整流程
嵌入计算本身不复杂,调 API 或者本地跑模型都行。但有几个实操细节要注意。第一是批处理,不要一条一条调,把多个文本块打包成一个批次,能显著提高吞吐量。第二是错误处理,网络请求可能失败,要有重试机制。第三是速率限制,很多 API 有 QPS 限制,要控制并发数。
向量入库时,除了向量本身,还要存原始文本和元数据。原始文本用于后续组装上下文,元数据用于过滤和引用。如果向量数据库支持,可以同时建立稠密索引和稀疏索引,为混合检索做准备。
入库之后要验证数据完整性。我通常会随机抽几条记录,检查向量维度是否正确、原始文本是否完整、元数据是否齐全。这一步看起来多余,但实际项目中经常出现向量维度不匹配或者文本被截断的问题,早发现早解决。
4.4 检索参数调优:Top-K、阈值与重排序
检索参数直接影响最终效果。Top-K 决定了召回多少候选,太小可能漏掉相关内容,太大则引入噪音并增加延迟。我的经验是,如果后面有重排序,Top-K 可以设大一些,比如 20 到 50;如果没有重排序,Top-K 设 3 到 5 就够了。
相似度阈值是另一个重要参数。低于某个阈值的结果直接丢弃,避免不相关内容干扰模型。但阈值设多少合适,取决于你的嵌入模型和数据类型。我通常先用一个较低的阈值(比如 0.5)跑一遍,观察检索结果的分数分布,再确定一个合理的截断点。
重排序模型的选择也很关键。BGE-reranker 系列在中文场景下表现不错,Cohere 的 rerank 接口也很好用。重排序的输入是查询和候选文档对,输出是相关性分数。对 Top-50 做重排序,再取 Top-5,这个流程在延迟和效果之间取得了比较好的平衡。
5. 常见问题排查与避坑经验实录
5.1 检索命中率低的排查思路
检索命中率低是最常见的问题。排查时我通常按这个顺序走:先看查询本身有没有问题,比如是不是太短、太模糊、或者包含错别字;再看分块是否合理,是不是把相关内容切散了;然后看嵌入模型是否适合当前领域;最后看检索参数是否调优。
一个容易被忽略的点是查询改写。用户的问题往往很口语化,直接拿去检索效果不好。加一个查询改写步骤,把“那个新出的产品怎么用”改写成“XX 产品使用指南”,检索命中率能提升不少。查询改写可以用小模型来做,成本很低。
另一个坑是嵌入模型的领域适配。通用嵌入模型在医疗、法律、金融等垂直领域可能表现不佳。如果预算允许,可以用领域数据对嵌入模型做微调,或者选用在相关领域预训练过的模型。如果不想微调,至少要用领域数据做测试,确认检索效果达标。
5.2 模型不按检索结果回答的应对方法
有时候检索结果明明是对的,但模型就是不用,或者用自己的知识胡编。这个问题通常出在提示词上。系统提示词要明确告诉模型:只基于提供的上下文回答,如果上下文里没有相关信息,就说不知道,不要自己编。
另一个技巧是在上下文里加上来源标注,让模型知道每段话的出处。生成回答时要求模型引用来源,这样既能提高可信度,也能倒逼模型认真使用检索结果。如果模型仍然不听话,可以尝试降低温度参数,减少随机性。
还有一种情况是检索结果太多,模型注意力被分散。这时候要减少送入模型的块数量,或者对块做摘要压缩。我试过把 Top-10 的检索结果用一个小模型做摘要,再送给大模型,效果比直接送原文好很多。
5.3 性能与成本优化的实战技巧
RAG 系统的延迟主要来自嵌入计算、向量检索和模型生成三个环节。嵌入计算可以缓存,相同的查询不用重复计算。向量检索的延迟和索引类型、数据规模有关,HNSW 索引比 IVF 索引查询更快,但内存占用更高。
成本方面,嵌入 API 的调用费用和 token 数量成正比。优化分块策略,减少冗余内容,能直接降低嵌入成本。检索时控制 Top-K,避免送入过多上下文,也能减少生成模型的 token 消耗。
还有一个容易被忽略的成本是向量数据库的存储。高维向量占用的存储空间不小,如果数据量很大,要考虑用降维或者量化技术来压缩向量。比如把 float32 量化成 int8,存储空间能减少 75%,检索速度也能提升,代价是轻微的精度损失。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方案 |
|---|---|---|---|
| 检索结果不相关 | 嵌入模型不适配 | 用领域数据测试嵌入质量 | 换模型或微调 |
| 检索结果不相关 | 分块不合理 | 检查块大小和重叠 | 调整分块策略 |
| 检索结果不相关 | 查询太模糊 | 分析用户查询特点 | 加查询改写步骤 |
| 模型不用检索结果 | 提示词不明确 | 检查系统提示词 | 明确要求基于上下文回答 |
| 模型不用检索结果 | 上下文太长 | 检查送入模型的块数量 | 减少 Top-K 或做摘要 |
| 回答包含过时信息 | 知识库未更新 | 检查文档更新时间 | 建立定期更新机制 |
| 检索延迟高 | 索引类型不合适 | 检查向量索引配置 | 换 HNSW 或优化参数 |
| 嵌入成本高 | 分块冗余 | 分析块内容重复度 | 优化分块和去重 |
6. 知识获取管道的进阶方向与个人实践体会
RAG 的基础管道跑通之后,还有很多可以优化的方向。比如 GraphRAG,把知识图谱引入检索流程,能处理多跳推理和实体关系查询。再比如 Agentic RAG,让 Agent 自己决定什么时候检索、检索什么、要不要多轮检索,而不是固定的一次性检索。这些进阶方案在复杂场景下能显著提升效果,但前提是基础管道已经跑稳了。
我在多个项目里落地 RAG 的经验是,不要一上来就追求完美。先用最简单的方案跑通全流程,用真实数据做测试,找到瓶颈再针对性优化。很多时候,问题不在模型上,而在数据质量和分块策略上。把文档解析和清洗做好,把分块调合理,检索效果自然就上来了。
还有一个体会是,RAG 系统的评估很重要但容易被忽视。没有评估就没有优化方向。我通常会准备一组问答对,覆盖常见查询类型,然后定期跑评估,看检索命中率和回答准确率的变化。评估集不用很大,几十条到上百条就够用,关键是要有代表性。
最后分享一个小技巧:在检索结果里加上“相邻块”。因为分块可能把一段完整的内容切断了,检索到其中一个块时,把它的前后块也一起取出来,能提供更完整的上下文。这个操作成本很低,但效果提升很明显,尤其是在处理长文档的时候。