1. 为什么知识获取管道是 AI Agent 落地的第一道分水岭
做 AI Agent 的人迟早会撞上同一堵墙:模型本身很聪明,但你问它公司内部的报销标准、上周刚更新的产品参数、某个客户的特殊约定,它要么一本正经地胡说,要么干脆说不知道。这不是模型不行,而是它的知识边界停在了训练数据截止的那一天。知识获取管道要解决的,就是把这个边界往后推、往深挖,让 Agent 在运行时能拿到外部知识。而 RAG(Retrieval-Augmented Generation,检索增强生成)目前是这条管道里最成熟、最通用的一段。
我见过太多项目卡在这里。Demo 阶段用几个 PDF 喂进去,效果惊艳,老板拍板立项;真到了生产环境,几千份文档、几十个数据源、每天还在更新,检索命中率断崖式下跌,Agent 开始答非所问。问题往往不在大模型,而在管道本身没搭对。RAG 听起来简单——检索加生成,但检索什么、怎么检索、检索出来怎么用,每一步都有大量工程细节。
这篇是"走进 AI Agent"系列的第四篇,专门讲知识获取管道的基础设施。我会把 RAG 的核心链路拆开,重点讲清楚稠密嵌入和稀疏嵌入这两条技术路线的差异与配合,以及从 0 到 1 搭建一个能用的 RAG 管道时,哪些环节最容易翻车。适合已经了解 Agent 基本概念、准备动手做知识库的开发者,也适合正在评估 RAG 方案的技术负责人。读完你应该能判断:自己的场景该用哪种检索策略,管道里哪几个环节必须重点投入。
先说一个反直觉的结论:RAG 的效果瓶颈,八成不在生成模型,而在检索质量。很多人一上来就纠结用哪个大模型,其实检索没做好,再强的模型也只能拿着错误的上下文硬编。所以这篇的重心会放在管道的前半段——知识的表示与召回。
2. RAG 管道的完整链路拆解:从原始文档到可用上下文
2.1 一条管道的五个阶段
把 RAG 想象成一条流水线,原料是各种格式的原始文档,成品是喂给大模型的上下文片段。中间大致经过五个阶段:
- 文档加载与解析:把 PDF、Word、网页、数据库记录等统一转成纯文本,同时保留结构信息(标题、表格、层级)。
- 切分(Chunking):把长文档切成适合检索和放入上下文窗口的小块。
- 向量化(Embedding):把每个文本块转成向量,存进向量库。
- 检索(Retrieval):用户提问时,把问题也转成向量,从库里召回最相关的若干块。
- 生成(Generation):把召回的内容拼进提示词,交给大模型生成答案。
这五步里,切分和检索是决定成败的两个环节,也是最容易被低估的。加载解析看起来是脏活累活,但它决定了后面所有环节的信息质量——解析丢了的表格、错乱的层级,后面再怎么优化都补不回来。
2.2 每个阶段的常见坑与判断标准
我按自己的经验,把每个阶段最容易出问题的地方列一下:
| 阶段 | 常见问题 | 判断标准 |
|---|---|---|
| 加载解析 | 表格变乱码、多栏 PDF 顺序错乱、扫描件无文字层 | 随机抽 20 份文档,人工核对解析结果是否可读 |
| 切分 | 块太大检索不准,块太小语义不完整 | 单块 200-800 字,且不切断完整语义单元 |
| 向量化 | 模型选错、维度不匹配、中英文混用效果差 | 用真实问题做召回测试,看 Top5 命中率 |
| 检索 | 只做向量检索,忽略关键词精确匹配 | 混合检索,稠密加稀疏 |
| 生成 | 上下文塞太多、提示词没约束引用来源 | 答案可溯源,无来源时明确说不知道 |
这张表不是让你照抄,而是给你一个自查清单。我踩过最深的坑在切分:早期图省事按固定字数硬切,结果一个完整的操作步骤被切成两半,检索时只召回前半段,Agent 给出的答案缺了关键一步,用户照着做直接出错。后来改成按语义边界切分——优先在段落、标题、列表项处断开,实在超长再按句子切,效果立刻不一样。
2.3 为什么切分策略比嵌入模型更值得花时间
这里要展开讲一个观点:嵌入模型的选择对最终效果的影响,往往小于切分策略。原因很简单,嵌入模型决定的是"能不能把语义相近的内容映射到相近的向量空间",这是模型能力问题,主流模型差距没那么大;而切分决定的是"每个向量到底代表什么语义单元",这是信息组织问题,切错了,再好的模型也救不回来。
举个具体例子。一份产品手册里有这么一段:
故障代码 E05 表示进水异常。排查步骤:1. 检查进水阀是否打开;2. 检查水压是否低于 0.05MPa;3. 若以上正常,更换水位传感器。
如果按 100 字硬切,很可能"故障代码 E05 表示进水异常"和排查步骤被分到两个块。用户问"E05 怎么处理",检索可能只召回排查步骤那块,但缺了"E05 是什么"的上下文,生成时容易张冠李戴。而按语义切分,整段作为一个块,信息完整,召回即用。
所以我的建议是:在切分上多花两天,比在嵌入模型上纠结两周更值。切分策略没有标准答案,但有几个原则可以遵循——保持语义完整、控制块大小在合理区间、给每个块加上来源和标题等元数据,方便后续过滤和溯源。
3. 稠密嵌入与稀疏嵌入:两条检索路线的本质差异
3.1 稠密嵌入到底在做什么
稠密嵌入(Dense Embedding)是把一段文本映射成一个固定长度的实数向量,比如 768 维或 1024 维,向量里每个维度都是一个浮点数,整体表示这段文本的语义。它的核心能力是语义匹配:用户问"怎么退款",文档里写的是"申请退货流程",字面没有重叠,但稠密嵌入能把它们映射到相近的位置,从而召回。
稠密嵌入的底层通常是 Transformer 类模型,经过大量文本对训练,学会把语义相近的句子拉近、无关的推远。它的优势是泛化能力强,能处理同义、近义、换一种说法的查询。缺点是对精确匹配不敏感——产品型号、错误代码、人名这类需要字面精确命中的内容,稠密嵌入经常召回不准,因为它关注的是整体语义,不是具体词。
3.2 稀疏嵌入为什么在 2026 年又火了起来
稀疏嵌入(Sparse Embedding)走的是另一条路。传统做法是 BM25 这类基于词频的算法,一个文档表示成一个高维稀疏向量,绝大多数维度是 0,只有出现的词对应的维度有值。它的核心能力是关键词精确匹配:用户问"E05",文档里有"E05"就能命中,没有就召不回。
这几年稀疏嵌入重新受到重视,是因为出现了学习型稀疏表示,比如 SPLADE 这类方法。它不再单纯依赖词频,而是用模型学习每个词的重要性权重,既保留了关键词精确匹配的能力,又引入了一定的语义泛化。实测下来,在包含大量专有名词、代码、型号的场景里,稀疏嵌入的召回质量明显优于纯稠密方案。
3.3 一张表看清两者该在什么场景用
| 维度 | 稠密嵌入 | 稀疏嵌入 |
|---|---|---|
| 匹配方式 | 语义相似 | 关键词精确 |
| 擅长场景 | 口语化提问、同义改写 | 型号、代码、专有名词 |
| 短板 | 精确词命中差 | 语义泛化弱 |
| 向量形态 | 低维稠密(如 1024 维) | 高维稀疏(词表维度) |
| 典型算法 | 双塔模型、句向量模型 | BM25、SPLADE |
| 存储开销 | 中等 | 视词表大小而定 |
我的实际经验是:别二选一,做混合检索。用户的问题千奇百怪,有人用大白话问,有人直接甩一个错误代码,单一检索路线必然有覆盖不到的地方。混合检索把两路召回的结果融合,用加权或倒数排名融合(RRF)的方式合并,命中率通常比单路高出一截。
3.4 混合检索的融合策略怎么定
融合策略听起来玄,其实核心就一件事:怎么把两路不同尺度的分数合并成一个可排序的分数。稠密检索的相似度分数和稀疏检索的 BM25 分数不在一个量纲上,直接相加没有意义。常见做法有两种:
- 倒数排名融合(RRF):不看具体分数,只看排名。某文档在稠密检索里排第 3,在稀疏检索里排第 5,按
1/(k+rank)累加,k 一般取 60。这种方法简单稳健,不依赖分数校准,我大多数项目默认用它。 - 加权分数融合:把两路分数归一化到 0-1 后加权求和。权重需要根据场景调,比如专有名词多的场景给稀疏路更高权重。调参成本高,但上限可能更高。
提示:融合后的 Top-K 不要直接全塞给大模型。先做一次重排(Rerank),用交叉编码器对候选做精细打分,再取前 3-5 条。这一步对最终答案质量的提升,往往比换嵌入模型更明显。
4. 从零搭一条能用的 RAG 管道:关键决策与实操细节
4.1 向量库选型:别被"专用"两个字绑架
向量库的选择经常被过度讨论。我的观点是:中小规模场景,用你已有的数据库加向量扩展就够了,没必要为了"专用"引入一套新组件。PostgreSQL 的 pgvector、Elasticsearch 的向量检索、甚至 SQLite 的向量扩展,都能撑起百万级向量的场景。
什么时候需要专用向量库?当你的向量规模到千万级以上,或者对检索延迟有极致要求,或者需要复杂的分布式和分片能力。这时候 Milvus、Qdrant 这类专用库才有明显优势。选型时重点看三件事:索引类型是否支持你的规模、是否支持混合检索、运维成本是否可接受。
我见过一个团队,为了一个内部知识库(总共不到 5 万份文档)上了分布式向量集群,结果运维复杂度陡增,检索延迟反而因为网络跳数变高。后来换回 pgvector,性能没降,运维省了一大半。规模没到,别提前上重装备。
4.2 嵌入模型的本地化与成本权衡
嵌入模型分两类:调用云端 API,或本地部署开源模型。怎么选?
- 云端 API:省事,效果稳定,按量付费。适合文档量不大、更新频繁、不想维护模型的场景。缺点是数据要出本地,且长期成本随调用量线性增长。
- 本地开源模型:数据不出本地,无调用成本,但需要 GPU 资源,且要自己处理模型版本、推理优化。适合数据敏感、调用量大、有运维能力的场景。
我的建议是先 API 跑通,再评估是否本地化。很多团队一上来就纠结本地部署,结果卡在环境配置上,项目迟迟出不了 Demo。先用 API 把整条管道跑通,验证效果,再根据成本和合规要求决定要不要换本地模型。切换时注意:换嵌入模型必须重建整个向量库,因为不同模型的向量空间不兼容,这是硬约束。
4.3 一个最小可用的检索代码骨架
下面这段代码展示混合检索的核心逻辑,用伪代码风格写,方便你迁移到自己的技术栈:
# 1. 稠密检索:问题转向量,查向量库 query_dense = dense_model.encode(question) dense_hits = vector_store.search(query_dense, top_k=20) # 2. 稀疏检索:问题做分词/权重,查倒排索引 query_sparse = sparse_model.encode(question) sparse_hits = inverted_index.search(query_sparse, top_k=20) # 3. 融合:用 RRF 合并两路结果 def rrf_fuse(dense_hits, sparse_hits, k=60): scores = {} for rank, doc in enumerate(dense_hits): scores[doc.id] = scores.get(doc.id, 0) + 1 / (k + rank) for rank, doc in enumerate(sparse_hits): scores[doc.id] = scores.get(doc.id, 0) + 1 / (k + rank) return sorted(scores.items(), key=lambda x: -x[1]) fused = rrf_fuse(dense_hits, sparse_hits) # 4. 重排:交叉编码器精排,取前 5 reranked = reranker.rerank(question, [doc for doc, _ in fused[:20]]) final_context = reranked[:5] # 5. 生成:拼上下文,交给大模型 answer = llm.generate(question, context=final_context)这段骨架里,每一步的 top_k 都值得调。稠密和稀疏各召回 20 条,融合后取 20 条重排,最终留 5 条。召回太少会漏,太多会引入噪声且拖慢重排。我一般从"召回 20、重排留 5"起步,再根据实测调整。
4.4 元数据过滤:被低估的召回加速器
很多 RAG 项目忽略了元数据过滤。每个文本块除了向量,还应该带上来源、时间、分类、权限等元数据。检索时先用元数据缩小范围,再做向量匹配,效果和速度都会提升。
比如用户问"2025 年第三季度的销售政策",如果所有块都带时间戳,检索时先过滤出第三季度的块,再在里面做语义匹配,命中率远高于全库盲搜。再比如多租户场景,权限过滤必须在检索阶段做,不能等生成后再筛,否则会泄露不该看的内容。
注意:元数据过滤和向量检索的顺序会影响结果。先过滤再检索,速度快但可能漏掉边界情况;先检索再过滤,召回全但慢。我的做法是先粗过滤(按权限、大类),再向量检索,最后精过滤(按时间、细分类),兼顾两者。
5. 检索命中率上不去时,我会按这个顺序排查
5.1 先确认问题出在检索还是生成
命中率低,第一步是定位问题环节。做法很简单:拿一批真实问题,人工标注每个问题的正确答案应该来自哪些文档块,然后看检索结果里有没有这些块。如果检索没召回,问题在检索;如果召回了但答案还是错,问题在生成或提示词。
这个标注过程很枯燥,但没有它,后面所有优化都是盲猜。我一般标 50-100 个问题就能看出规律。标完你会发现,问题往往集中在某几类查询上,比如带型号的、带时间范围的、口语化严重的,针对性优化这几类,比全局调参有效得多。
5.2 切分粒度与重叠度的重新校准
如果检索召回率低,先回头看切分。常见问题是块太大——一个块里塞了太多主题,向量表示被平均化,反而不精准。或者块太小——语义不完整,召回了也没用。
我的校准方法是:抽一批问题,看召回块的实际内容。如果召回块里只有一半内容和问题相关,说明块太大,需要切细;如果召回块读起来缺头少尾,说明块太小或切断了语义。调整时给相邻块加重叠(overlap),一般重叠 10%-20%,避免边界信息丢失。
5.3 查询改写:让用户的问题更好被检索
用户的提问方式千奇百怪,直接拿原始问题去检索,效果经常不理想。查询改写(Query Rewriting)是提升召回的有效手段,常见做法有几种:
- 同义扩展:把问题里的关键词扩展成多个同义表达,一起检索。
- 假设文档生成(HyDE):先让大模型根据问题生成一个假想的答案文档,再用这个文档去检索。因为答案文档的表述更接近知识库里的内容,召回效果往往更好。
- 多查询生成:让模型把一个问题改写成多个不同角度的查询,分别检索后合并结果。
这几种方法各有适用场景。HyDE 对口语化问题效果好,多查询对复杂问题覆盖全。但都要注意别过度改写导致语义漂移,改完的查询要能追溯到原问题。
5.4 重排模型的引入时机
重排不是万能的,也不是必须的。当你的召回已经不错,但 Top-K 里混了噪声,重排能明显提升精度;但如果召回本身就漏,重排救不了。所以重排应该放在召回优化之后。
重排模型通常是交叉编码器,把问题和候选文档一起输入,输出相关性分数。它比双塔模型的精度高,但速度慢,所以只对少量候选做。我一般对融合后的前 20 条做重排,取前 5 条。实测下来,重排能把最终答案的准确率提升 10-20 个百分点,是性价比很高的一步。
5.5 一个真实的排查案例
之前有个项目,用户反馈"问产品参数经常答错"。我按上面的顺序排查:
- 标注 50 个问题,发现检索召回率其实有 80%,问题出在生成——召回的块里有正确参数,但模型选了另一个块的错误参数。
- 看提示词,发现没有明确要求"优先使用最新版本参数"。
- 检查元数据,发现参数块没有版本和时间标记,模型无法判断哪个更新。
- 修复:给参数块加版本元数据,检索时按版本排序,提示词里明确要求使用最新版本。
改完之后准确率从 60% 提到 90% 以上。这个案例说明:命中率问题不一定是检索问题,元数据和提示词同样关键。排查时别一上来就换模型,先把链路走一遍。
6. RAG 之外:知识获取管道的延伸方向
6.1 GraphRAG 与本体增强的适用边界
普通 RAG 处理的是"块与问题"的匹配,但有些问题需要跨块推理,比如"和 A 产品兼容的所有配件有哪些",答案分散在多个文档里,单块检索拼不出完整答案。这时候GraphRAG这类方案就有价值——它把知识组织成图结构,实体是节点,关系是边,检索时沿着图遍历,能召回关联的多跳信息。
但 GraphRAG 不是银弹。它的构建成本高,需要实体抽取、关系抽取、图维护,而且对抽取质量敏感。我的判断标准是:如果你的问题大量涉及多实体关系、跨文档推理,且普通 RAG 确实拼不出答案,才考虑上图结构。否则,把普通 RAG 的切分和检索做扎实,性价比更高。
6.2 知识更新与增量索引的工程处理
知识库不是一次建好就完事,文档会更新、会新增、会删除。增量索引是生产环境必须解决的问题。常见做法是给每个文档块记录来源文档 ID 和版本,文档更新时,先删除旧块,再插入新块。删除要彻底,否则旧版本会和新版本一起被召回,导致答案矛盾。
我踩过的坑是:早期没做版本管理,文档更新后旧块还在库里,检索时新旧混在一起,模型经常引用过时信息。后来加了版本字段,检索时只取最新版本,问题才解决。增量更新不是可选项,是必选项,设计之初就要考虑。
6.3 评估体系:没有度量就没有优化
最后强调一点:RAG 必须有评估体系。没有度量,你无法判断一次改动是优化还是劣化。评估指标至少包括:
- 召回率:正确答案所在的块,有多少被召回。
- 精确率:召回的块里,有多少是真正相关的。
- 答案准确率:最终生成的答案是否正确。
- 可溯源率:答案是否能对应到具体来源。
评估集要覆盖真实场景的各种问题类型,定期跑,跟踪变化。我一般用 100-200 个标注问题做基线,每次改动后跑一遍,看指标涨跌。这套体系搭起来费点功夫,但它是 RAG 项目从"能用"走向"好用"的分界线。
我个人在实际操作中的体会是:RAG 的难点从来不是某个单点技术,而是整条链路的协同。切分、嵌入、检索、重排、生成,每一环都影响最终效果,而优化时又要能定位到具体是哪一环出了问题。把评估体系建起来,把排查顺序理清楚,比盲目追新框架、新模型有用得多。知识获取管道搭扎实了,上面的 Agent 才能真正跑起来。