
1. 为什么知识获取管道是 AI Agent 的第一道生死关做 AI Agent 开发的人十有八九都经历过这样一个阶段模型接上了工具调通了流程跑起来了但一到真实业务场景就露馅。用户问“我们公司去年Q3的差旅报销标准是多少”Agent 要么胡编一个数字要么支支吾吾说“我无法获取内部信息”。这不是模型不够聪明而是它压根没拿到正确的上下文。知识获取管道要解决的就是这个问题。你可以把它理解成 Agent 的“进食系统”——模型是大脑管道负责把外部世界的知识嚼碎了、分类好、在需要的时候精准投喂。没有这条管道Agent 就是个只会背课本的书呆子管道设计得不好Agent 就是个消化不良的病人喂进去再多也吸收不了。RAG也就是检索增强生成是目前搭建这条管道最主流、最成熟的技术路线。它的核心逻辑不复杂用户提问时系统先从知识库里检索出最相关的片段把这些片段和问题一起塞给大模型让模型基于真实材料来回答。听起来简单但真正落地的时候从文档切分、向量化、索引构建到检索排序每一步都有大量细节决定成败。这篇文章适合三类人看正在从零搭建 AI Agent 的开发者、已经用了 RAG 但效果不理想的工程师、以及想搞清楚 RAG 底层原理再决定技术选型的技术负责人。我会从整体设计思路讲到具体实操细节把稠密嵌入和稀疏嵌入的取舍、检索命中率的优化、常见故障的排查都掰开揉碎讲清楚。读完你至少能搭出一个能用的知识获取管道并且知道哪里容易踩坑。2. 知识获取管道的整体设计与技术选型2.1 从“知识割裂”说起为什么传统方案不够用很多团队一开始的做法很朴素把公司文档全部拼成一个大文本直接塞进模型的上下文窗口。短文档还行一旦知识库上到几百兆这条路就走不通了。上下文窗口再大也有上限而且塞太多无关内容进去模型的注意力会被稀释回答质量反而下降。另一种常见做法是关键词搜索。用户问“报销标准”系统就去文档里找包含“报销”和“标准”的段落。问题是用户可能问的是“出差费用怎么算”字面上跟“报销标准”没有重叠关键词匹配就失效了。这就是所谓的知识割裂——知识明明在库里但检索环节没把它找出来。RAG 的思路是在两者之间找平衡用语义检索代替字面匹配用片段召回代替全文塞入。它把知识库拆成一个个语义完整的片段每个片段转成向量存起来。用户提问时把问题也转成向量在向量空间里找距离最近的片段。这样即使用户用的词跟文档里的词不一样只要意思相近就能被检索到。2.2 稠密嵌入与稀疏嵌入两条腿走路才稳说到向量化就绕不开稠密嵌入和稀疏嵌入这两个概念。它们不是二选一的关系而是互补的。稠密嵌入是把一段文本映射成一个固定长度的浮点数向量比如 768 维或 1024 维。这个向量里的每个维度都参与表达语义没有哪个维度是“空”的所以叫稠密。它的优势是能捕捉深层语义关系“出差费用”和“差旅报销”在稠密向量空间里距离很近。缺点是对于专有名词、产品型号、精确数字这类信息稠密嵌入有时候会“糊”掉把“A100”和“A800”当成差不多的东西。稀疏嵌入则相反它的向量维度跟词表大小一致绝大多数维度是零只有出现过的词对应的维度才有值。传统的关键词权重算法就是典型的稀疏表示。它的优势是精确匹配能力强用户搜“ISO-9001”就一定能找到包含这个字符串的文档。缺点是无法处理同义替换和语义泛化。实际生产环境中我见过效果最好的方案基本都是混合检索同时跑稠密和稀疏两路召回然后用融合算法把两边的结果合并排序。这样既能抓住语义相似的内容又不会漏掉精确匹配的关键信息。具体怎么融合后面实操部分会详细讲。2.3 管道架构的四个核心模块一个完整的知识获取管道不管用什么框架实现本质上都包含四个模块。文档处理模块负责把各种格式的原始材料PDF、Word、HTML、数据库记录统一转成纯文本然后按照语义边界切分成片段。切分策略直接决定了后续检索的质量切得太碎会丢失上下文切得太大会引入噪声。向量化模块把每个文本片段转成向量表示。这里要决定用哪个嵌入模型、要不要做归一化、批量处理的并发度怎么控制。嵌入模型的选择要考虑语言支持、维度大小、推理速度和成本。存储与索引模块负责把向量和原始文本存起来并建立高效的相似度检索索引。向量数据库的选择很多从轻量级的本地库到分布式的云服务都有关键看数据规模和查询延迟要求。检索与重排模块是用户提问时真正跑起来的环节。它把用户问题转成向量在索引里召回候选片段然后可能经过一个重排模型做精排最后把最相关的几个片段返回给生成模块。这四个模块串起来就是一条完整的管道。每个模块都有优化空间但根据我的经验文档切分和检索重排是投入产出比最高的两个环节值得多花时间打磨。3. 核心细节解析与实操要点3.1 文档切分别让一刀切毁掉语义完整性文档切分是很多人最容易忽视的环节。我见过不少项目直接按固定字数切每 500 字一刀结果一个完整的操作步骤被切成两半检索出来的片段缺头少尾模型看了也拼不出完整答案。合理的切分策略应该优先尊重文档的自然结构。Markdown 文档按标题层级切HTML 按 DOM 节点切PDF 如果解析质量好可以按段落切。在自然结构的基础上再控制片段长度在一个合理区间通常 200 到 500 个 token 是比较舒服的范围。注意切分长度没有万能值。技术文档可以短一些因为概念密集叙事性内容可以长一些因为需要上下文连贯。建议先用一批真实用户问题做测试看检索出来的片段是否包含完整答案。还有一个实用技巧是重叠切分。相邻两个片段之间保留 10% 到 20% 的重叠内容这样即使答案刚好落在切分边界上也不会完全丢失。代价是存储和检索时会多一些冗余但相比漏检的风险这点冗余完全值得。对于表格和代码块要特殊处理。表格最好整表保留不要按行切散代码块要保留完整的函数或类定义不要从中间截断。这些结构化内容一旦被破坏检索出来也没法用。3.2 嵌入模型选型维度不是越高越好选嵌入模型的时候很多人第一反应是看排行榜哪个分数高用哪个。但实际落地要考虑的因素远不止分数。语言支持是第一条。如果你的知识库主要是中文就要选中文语料训练充分的模型。有些英文模型在中文上也能跑但语义捕捉能力会打折扣。现在有不少多语言模型表现不错可以优先考虑。维度大小直接影响存储成本和检索速度。768 维和 1536 维的模型存储开销差一倍检索时的计算量也差不少。高维度通常意味着更强的表达能力但边际收益递减很明显。我的经验是对于大多数企业知识库场景768 到 1024 维足够用了没必要盲目追高。推理速度在批量处理大量文档时很关键。有些模型效果好但推理慢处理十万个片段可能要跑好几个小时。如果知识库更新频繁这个时间成本就不可接受了。建议在选型阶段就用真实数据量做一次基准测试算清楚全量处理和增量更新的耗时。成本也是硬约束。商用嵌入 API 按 token 计费知识库大了之后费用不小。开源的本地嵌入模型虽然省了 API 费用但需要 GPU 资源这笔账也要算进去。3.3 向量索引HNSW 与 IVF 的取舍向量存进数据库之后怎么快速找到最相似的 top-k 个结果这就是索引要解决的问题。暴力遍历当然最准但数据量上到百万级就慢得没法用了。近似最近邻算法是必须的。HNSW分层可导航小世界图是目前最常用的索引结构。它构建一个多层图查询时从顶层开始逐层向下搜索每层都快速逼近目标区域。优点是查询速度快、召回率高缺点是内存占用大因为图结构本身要占不少空间。对于千万级以下的数据量HNSW 基本是首选。IVF倒排文件索引先把向量聚类成若干个簇查询时只搜索距离最近的几个簇。优点是内存占用小适合超大规模数据。缺点是如果目标向量刚好落在簇边界上可能会漏掉。通常需要配合 PQ乘积量化来压缩向量进一步降低内存但会损失一些精度。实际选型的时候如果数据量在百万级以内直接上 HNSW省心效果好。如果数据量上亿再考虑 IVF 加 PQ 的组合。参数调优方面HNSW 的M参数控制每个节点的连接数越大召回率越高但内存也越大通常设 16 到 64 之间efConstruction控制构建时的搜索范围设大一些索引质量更好但构建更慢。3.4 混合检索的融合策略稠密和稀疏两路召回的结果怎么合并常见的有两种做法。一种是加权求和。把两路的相似度分数归一化到同一量级然后按权重相加。稠密检索的权重通常设高一些比如 0.7稀疏检索设 0.3。这个权重可以根据实际效果调如果发现精确匹配的需求多就调高稀疏的权重。另一种是倒数排名融合。不看具体分数只看排名。每个结果在两路召回中分别有一个排名把排名的倒数加权求和作为最终分数。这种方法的优势是不需要处理分数归一化的问题对两路分数的量纲差异不敏感。实操心得我一般先用倒数排名融合跑一版基线因为不需要调归一化参数上手快。等基线效果稳定了再尝试加权求和看能不能通过调权重再提升几个百分点。两种方法都试试用真实查询集评估别凭感觉选。融合之后通常还要过一个重排模型。重排模型比嵌入模型更重但精度更高它会把候选片段和用户问题一起编码输出一个精细的相关性分数。因为只对 top-50 或 top-100 的候选做重排计算量可控但效果提升往往很明显。4. 实操过程与核心环节实现4.1 环境准备与依赖安装假设我们用 Python 生态来搭建这条管道。核心依赖包括文档解析库、嵌入模型库、向量数据库客户端和重排模型库。pip install unstructured pdfplumber markdown pip install sentence-transformers pip install chromadb pip install rank-bm25 pip install FlagEmbeddingunstructured负责把各种格式的文档转成纯文本pdfplumber专门处理 PDF 表格提取sentence-transformers提供稠密嵌入模型chromadb是轻量级向量数据库rank-bm25实现稀疏检索FlagEmbedding提供重排模型。如果你打算用 GPU 加速嵌入推理还需要装对应版本的 PyTorch 和 CUDA 工具包。CPU 也能跑但处理大量文档时速度差距很明显。4.2 文档加载与智能切分先写文档加载逻辑。不同格式的文件走不同的解析器统一输出成带元数据的文本块。from unstructured.partition.auto import partition from langchain.text_splitter import RecursiveCharacterTextSplitter def load_and_split(file_path): elements partition(filenamefile_path) text \n.join([str(el) for el in elements]) splitter RecursiveCharacterTextSplitter( chunk_size400, chunk_overlap80, separators[\n## , \n### , \n\n, \n, 。, ] ) chunks splitter.split_text(text) return chunks这里chunk_size设 400 个字符chunk_overlap设 80 个字符重叠比例 20%。分隔符列表按优先级排列优先在二级标题、三级标题处切分其次才是空行和句号。这样能最大程度保留语义完整性。对于表格单独走一条路径。用pdfplumber提取表格后把整个表格转成 Markdown 格式作为一个独立片段不参与常规切分。4.3 稠密向量生成与入库嵌入模型选BAAI/bge-large-zh-v1.5这是目前中文场景下综合表现很稳的一个模型1024 维对中文语义捕捉到位。from sentence_transformers import SentenceTransformer import chromadb model SentenceTransformer(BAAI/bge-large-zh-v1.5) client chromadb.PersistentClient(path./vector_store) collection client.get_or_create_collection( nameknowledge_base, metadata{hnsw:space: cosine} ) def embed_and_store(chunks, source): embeddings model.encode(chunks, normalize_embeddingsTrue) ids [f{source}_{i} for i in range(len(chunks))] metadatas [{source: source, chunk_index: i} for i in range(len(chunks))] collection.add( embeddingsembeddings.tolist(), documentschunks, idsids, metadatasmetadatas )normalize_embeddingsTrue把向量归一化到单位长度这样余弦相似度计算就等价于内积检索时更快。hnsw:space设成 cosine 表示用余弦距离。批量处理的时候不要一条一条 encode把 chunks 攒成 batch 一起跑GPU 利用率高很多。batch size 根据显存大小调一般 32 到 128 之间。4.4 稀疏检索索引构建稀疏检索用 BM25 算法它根据词频和逆文档频率给每个词打分是信息检索领域的经典方法。from rank_bm25 import BM25Okapi import jieba def build_sparse_index(chunks): tokenized [list(jieba.cut(chunk)) for chunk in chunks] bm25 BM25Okapi(tokenized) return bm25, tokenized def sparse_search(bm25, tokenized, query, top_k20): query_tokens list(jieba.cut(query)) scores bm25.get_scores(query_tokens) top_indices sorted(range(len(scores)), keylambda i: scores[i], reverseTrue)[:top_k] return top_indices, [scores[i] for i in top_indices]中文需要先分词jieba是最常用的分词库。BM25 的k1和b参数控制词频饱和度和文档长度归一化默认值 1.5 和 0.75 在大多数场景下够用。如果发现长文档被系统性压低可以把b调小一些。4.5 混合检索与重排的完整流程把稠密和稀疏两路串起来加上重排就是完整的检索流程。from FlagEmbedding import FlagReranker reranker FlagReranker(BAAI/bge-reranker-large, use_fp16True) def hybrid_search(query, top_k5): # 稠密检索 query_embedding model.encode([query], normalize_embeddingsTrue) dense_results collection.query( query_embeddingsquery_embedding.tolist(), n_results20 ) # 稀疏检索 sparse_indices, sparse_scores sparse_search(bm25, tokenized_chunks, query, top_k20) # 倒数排名融合 rrf_scores {} for rank, doc_id in enumerate(dense_results[ids][0]): rrf_scores[doc_id] rrf_scores.get(doc_id, 0) 1 / (60 rank 1) for rank, idx in enumerate(sparse_indices): doc_id fdoc_{idx} rrf_scores[doc_id] rrf_scores.get(doc_id, 0) 1 / (60 rank 1) # 取融合后的 top-20 做重排 candidates sorted(rrf_scores.items(), keylambda x: x[1], reverseTrue)[:20] candidate_texts [get_doc_text(doc_id) for doc_id, _ in candidates] # 重排 pairs [[query, text] for text in candidate_texts] rerank_scores reranker.compute_score(pairs) reranked sorted(zip(candidate_texts, rerank_scores), keylambda x: x[1], reverseTrue) return reranked[:top_k]倒数排名融合里的常数 60 是经验值来自原始论文作用是平滑排名差异让靠前的结果优势不那么极端。重排模型用bge-reranker-large它比嵌入模型大不少但只对 20 个候选做推理延迟可以接受。4.6 参数调优的实测记录我在一个约 5 万片段的知识库上做过一轮参数对比测试用 200 条真实用户问题做评估集看 top-5 命中率。配置稠密 top-20稀疏 top-20融合方式重排top-5 命中率A是否无否72%B是是RRF否81%C是是RRF是89%D是是加权 0.7/0.3是90%从 A 到 B 的提升说明混合检索确实有效稀疏路补充了稠密路漏掉的精确匹配。从 B 到 C 的提升说明重排模型价值很大它能把真正相关的片段从候选池里挑出来。从 C 到 D 的提升很小说明 RRF 已经够用加权求和调参的边际收益有限。实操心得如果你的资源有限优先保证稠密检索加混合融合重排模型可以后面再加。但如果对精度要求高重排是必须的它带来的提升比换更大的嵌入模型更明显。5. 常见问题与排查技巧实录5.1 检索命中率低的排查思路检索效果不好的时候不要急着换模型先按顺序排查这几个环节。第一步看切分质量。把检索出来的片段打印出来人工判断它是否包含完整答案。如果片段明显缺头少尾问题出在切分策略上调整 chunk_size 和分隔符优先级。第二步看嵌入模型是否匹配语言。如果知识库是中文但用了英文为主的嵌入模型语义捕捉会打折扣。换一个中文或多语言模型试试。第三步看查询改写。用户的问题往往口语化、省略多直接拿去检索效果不好。可以在检索前加一步查询改写用大模型把用户问题改写成更适合检索的形式或者生成多个查询变体分别检索再合并。第四步看是否需要领域微调。如果知识库涉及大量专业术语通用嵌入模型可能区分不开。可以用领域数据对嵌入模型做微调让它在你的专业语境下更敏感。5.2 常见故障速查表现象可能原因排查方法解决方向检索结果完全不相关嵌入模型加载错误或维度不匹配检查模型输出维度与索引维度是否一致重新生成向量并重建索引精确匹配查不到稀疏检索未启用或分词错误单独测试 BM25 检索结果检查分词器配置补充自定义词典响应延迟高重排模型推理慢或候选集太大分别计时各环节耗时减小候选集或用更小的重排模型新文档检索不到增量更新未触发索引重建检查文档入库流程确保新增文档走完整的嵌入和索引流程相似文档互相干扰切分过碎导致重复内容多查看 top-k 结果是否高度重复增大 chunk_size或做去重后处理长尾问题效果差训练数据覆盖不足分析失败案例的查询类型补充领域数据微调嵌入模型5.3 增量更新的坑与解法知识库不是一次建好就完事了新文档要加进来旧文档要更新或删除。增量更新有几个容易踩的坑。坑一ID 冲突。如果新文档的 ID 生成规则跟旧文档撞了会覆盖掉原有内容。建议用“来源文件名 内容哈希”作为 ID内容变了哈希就变不会误覆盖。坑二稀疏索引不支持增量删除。BM25 索引通常是全量构建的删文档需要重建整个索引。如果知识库更新频繁可以考虑用支持增删的稀疏索引实现或者定期全量重建。坑三嵌入模型换了之后旧向量作废。如果升级了嵌入模型新旧向量不在同一空间不能混用。必须全量重新嵌入重建索引。所以嵌入模型选型要慎重别频繁换。5.4 评估与监控别等上线了才发现问题RAG 系统需要持续评估。我建议至少维护两套评估集一套是人工标注的标准问答对用来算命中率和准确率另一套是线上真实查询的采样用来发现分布变化。关键指标包括检索命中率top-k 里包含正确答案的比例、检索精确率top-k 里相关片段的比例、端到端回答准确率最终回答正确的比例、平均响应延迟。监控方面把每次查询的检索结果和最终回答都记下来定期抽样人工检查。如果发现某类问题的失败率突然升高可能是知识库内容过期了或者用户查询模式变了需要针对性调整。实操心得我习惯在检索结果里保留相似度分数设一个阈值低于阈值的直接返回“未找到相关信息”而不是硬塞给模型让它编。这个阈值需要根据实际数据调太高会漏答太低会引入噪声。一般从 0.6 开始试根据误答率调整。6. 从基础 RAG 到 Agentic RAG 的演进方向基础 RAG 跑通之后你会很快遇到它的天花板。用户的问题越来越复杂单次检索搞不定需要多步推理、多源检索、甚至主动追问澄清。这就是 Agentic RAG 要解决的问题。Agentic RAG 的核心思路是把检索从一次性的动作变成 Agent 的一个工具。Agent 可以决定什么时候检索、检索什么、检索几次。比如用户问“对比我们公司和竞争对手的产品定价策略”Agent 可以先检索自家定价文档再检索竞品分析报告然后综合两边信息生成对比。如果发现某个信息缺失它还能主动发起补充检索。实现上这需要在 Agent 的决策循环里集成检索工具并且让 Agent 能评估检索结果的质量决定是否需要再次检索。这比基础 RAG 复杂不少但能处理的问题类型也丰富得多。另一个方向是图增强检索。传统 RAG 把知识当成孤立的片段但很多知识之间存在关联。比如“产品A的定价”和“产品A的成本结构”是相关的但作为独立片段检索时可能只召回其中一个。图增强检索把知识片段和它们之间的关系一起建模检索时能沿着关系边扩展召回更完整的上下文。这两个方向都还在快速演进中没有形成绝对标准。我的建议是先把基础 RAG 的每个环节做扎实把检索命中率和回答准确率提上去再考虑往 Agentic 方向扩展。基础不牢上层建筑再花哨也撑不住。最后分享一个我在多个项目里验证过的经验知识获取管道的质量八成取决于文档切分和检索策略两成取决于模型选型。很多人把大量时间花在对比嵌入模型上却忽略了切分策略的优化。实际上把切分做好、把混合检索和重排加上效果提升比换一个更大的模型明显得多。先把这两件事做到位再考虑其他优化。