1. 为什么知识获取管道是 AI Agent 落地的第一道坎做 AI Agent 的人迟早会撞上一堵墙模型本身很聪明但你问它公司内部的报销标准、上周刚更新的产品参数、某个客户的特殊约定它要么一本正经地胡说要么干脆告诉你我没有这方面的信息。这不是模型不行而是它的知识边界被训练数据锁死了。知识获取管道要解决的就是把这个边界打开让 Agent 在回答问题之前先去外部知识源里把相关材料捞出来再基于材料组织答案。这套机制的核心实现方式就是RAG检索增强生成。我在过去一年里帮几个团队搭过 Agent 的知识层从最初用现成框架跑通 Demo到后来自己拆开每一层做调优踩过的坑比想象中多。很多人以为 RAG 就是把文档切一切、塞进向量库、检索出来拼进 Prompt真上手才发现切分策略不对检索出来的全是半截话嵌入模型选错语义相近的问题匹配不到召回率看着挺高但真正有用的片段排在第十几位模型根本用不上。这些问题不会在 Demo 阶段暴露一旦上真实数据就集中爆发。这篇内容适合三类人正在从零搭建 AI Agent、需要给它接上私有知识库的开发者已经跑通了 RAG 流程、但效果不稳定的工程师以及想搞清楚 RAG 到底怎么回事、不想被各种框架名词绕晕的技术负责人。我会从知识获取管道的整体结构讲起把稠密嵌入、向量检索、上下文组装这几个关键环节拆开配上我实际调参的经验和踩坑记录。读完你应该能自己判断我的场景该用多大的切分粒度、嵌入模型怎么选、检索结果怎么过滤才不浪费上下文窗口。先说一个反直觉的结论RAG 的效果瓶颈八成不在生成模型而在检索质量。你换一个更强的 LLM回答可能更流畅但如果检索回来的材料本身就是错的、残缺的、不相关的再强的模型也只能基于垃圾输出垃圾。所以这篇的重点会放在管道的前半段——知识怎么进来、怎么存、怎么被准确捞出来。2. 知识获取管道的完整链路拆解2.1 从原始文档到可检索片段的四个阶段一条完整的知识获取管道我习惯把它拆成四个阶段摄取、切分、嵌入、索引。这四个阶段环环相扣任何一个环节偷懒后面都要加倍还回来。摄取阶段负责把各种格式的原始资料读进来。PDF、Word、Markdown、网页、数据库导出、甚至聊天记录格式五花八门。这一步最容易被低估很多人直接拿个库把 PDF 转成纯文本就完事结果表格结构全丢了、页眉页脚混进正文、多栏排版读成了乱序。我的做法是对结构化程度高的文档比如产品手册、API 文档优先保留其原有的层级结构用 Markdown 作为中间格式对扫描件或排版复杂的 PDF宁可多花点时间做版面分析也不要图省事直接抽文本。切分阶段是把长文档拆成适合检索和嵌入的小块。这里有个核心矛盾块太小语义不完整检索出来是断章取义块太大噪声多嵌入向量被稀释匹配精度下降。业界常见的做法是 256 到 512 个 token 一块配合一定的重叠overlap。但我不建议你直接抄这个数字后面会专门讲怎么根据自己的数据定切分粒度。嵌入阶段是把每个文本块转成一个高维向量也就是稠密嵌入。这个向量捕捉的是文本的语义语义相近的文本向量在空间里的距离就近。嵌入模型的选择直接决定了检索的天花板选错了后面怎么调都费劲。索引阶段是把这些向量存进向量数据库建立高效的相似度检索结构。常见的有 HNSW、IVF 这类近似最近邻算法目的是在百万级甚至亿级向量里快速找到最相近的若干个。2.2 检索与生成之间那道容易被忽略的缝很多人把管道理解到检索出 Top-K 个片段就结束了其实检索和生成之间还有一道关键的缝上下文组装。检索回来的片段不能原封不动全塞进 Prompt你得考虑几件事。第一是去重和排序。同一个知识点可能在多个片段里重复出现全塞进去既浪费上下文窗口又可能让模型过度关注某个重复信息。我一般会做一次相似度去重把高度重叠的片段合并或丢弃。第二是相关性阈值过滤。不是所有检索结果都值得用。如果某个片段的相似度分数明显低于其他片段它大概率是噪声硬塞进去反而干扰模型判断。设一个动态阈值比固定取 Top-5 要靠谱得多。第三是上下文预算分配。模型的上下文窗口是有限的检索片段、系统提示、对话历史、用户问题都要占地方。你得算清楚留给检索材料多少 token超了就按相关性从低到高砍。我见过有人检索回来二十个片段全塞进去结果把对话历史挤没了多轮对话直接失忆。提示上下文组装这一步没有标准答案但有一个原则——宁可少而精不要多而杂。三个高度相关的片段效果通常好过十个良莠不齐的片段。2.3 一个最小可用的管道长什么样如果你现在就想动手我给你一个最小可用的管道结构不依赖任何重型框架也能跑起来用文档解析库把原始资料转成带结构的文本Markdown 优先按语义边界切分成块块之间保留 10% 到 20% 的重叠用嵌入模型把每个块转成向量连同原文和元数据一起存进向量库用户提问时把问题也转成向量在向量库里做相似度检索对检索结果做去重、阈值过滤、重排序把筛选后的片段拼进 Prompt交给 LLM 生成答案这个结构看起来简单但每一步都有讲究。接下来我逐个拆开讲重点讲那些看起来能跑、实际会出问题的地方。3. 稠密嵌入检索质量的天花板由它决定3.1 嵌入模型到底在做什么稠密嵌入这个词听起来唬人本质其实很朴素把一段文本映射成一个固定长度的数字数组比如 768 维或 1024 维。这个数组就是这段文本在语义空间里的坐标。语义相近的文本坐标就靠近语义无关的坐标就离得远。检索的时候把用户问题也映射成同样的坐标然后找离它最近的那些文本块。关键在于语义相近这四个字。传统的关键词检索只能匹配字面相同的词你搜如何退款它匹配不到写着申请退货流程的文档。而稠密嵌入能捕捉到这两者在语义上的关联因为它们表达的是同一件事。这就是为什么 RAG 比传统搜索更适合处理自然语言提问。但嵌入模型不是万能的。它对训练数据覆盖的领域表现好对完全陌生的领域会退化。比如一个主要用通用语料训练的嵌入模型拿去处理法律条文或医疗术语效果可能还不如关键词检索。所以选嵌入模型时领域匹配度比模型大小更重要。3.2 选嵌入模型时我实际看的几个指标市面上的嵌入模型很多从开源的到商业 API 都有。我选型时主要看这几个维度维度说明我的取舍向量维度维度越高表达能力越强但存储和计算成本也越高768 到 1024 维是甜点区超过 1536 收益递减明显最大输入长度单次能嵌入多长的文本至少要覆盖你的切分块大小否则会被截断领域适配是否在你的领域语料上训练或微调过通用模型够用就用通用专业领域优先找适配版多语言能力是否支持中英文混合中文场景必须实测中文语义匹配效果推理成本本地部署还是 API 调用延迟和费用如何数据量大且敏感就本地部署追求快速上线用 API我踩过的一个坑是早期图省事用了一个英文为主的嵌入模型处理中文文档结果中文语义匹配一塌糊涂两个意思完全不同的中文句子向量相似度却很高。后来换成中文优化过的模型检索准确率立刻上了一个台阶。中文场景一定要用中文语料训练或优化过的嵌入模型这一点没有捷径。3.3 嵌入不是一劳永逸数据变了要重建有个容易被忽略的问题嵌入是静态的。你把文档嵌入成向量存进库里的那一刻这个向量就固定了。如果嵌入模型升级了或者你的文档内容更新了旧向量和新向量就不在同一个语义空间里混在一起检索会出问题。我的做法是给向量库里的每条记录都带上嵌入模型版本和文档版本的元数据。模型升级或文档大改时触发一次全量重建。增量更新也要小心新文档用新模型嵌入旧文档还是旧模型两者相似度计算就不可比了。所以要么全量重建要么保证新旧文档用同一个模型版本。注意向量库不是数据库它不擅长频繁的增删改。如果你的知识更新很频繁建议设计成批量重建 版本切换的模式而不是实时逐条更新。4. 切分策略决定检索精度的隐形手4.1 固定长度切分为什么经常翻车最简单的切分方式是按固定字符数或 token 数切比如每 500 个字符一块。这种方式实现简单但问题很明显它会在句子中间、段落中间甚至词语中间切断导致每个块的首尾都是残缺的语义。我做过一个对比实验同一批产品文档一组按固定 500 字符切一组按段落和标题层级切。检索同一个问题时固定切分的组经常召回一些上半句在讲 A、下半句在讲 B的块模型拿到这种材料回答要么含糊要么跑偏。而按语义边界切的组召回的块基本都是完整表达一个意思的回答质量明显更稳。所以我的原则是优先按文档的自然结构切结构不明确时再退回到固定长度加重叠。Markdown 的标题、段落、列表项HTML 的标签层级代码的函数边界这些都是天然的切分点。4.2 重叠窗口设多少才合适块之间保留重叠是为了防止一个完整的语义被切分点劈成两半导致两边都检索不到。比如一句话跨越了两个块如果没有重叠这句话在两个块里都是残缺的检索时可能都匹配不上。重叠比例我一般设在10% 到 20%。太小起不到保护作用太大则会造成大量重复内容浪费存储和检索开销。具体怎么定可以这样想如果你的块是 500 token重叠 50 到 100 token 基本够用。如果文档里长句、跨段逻辑特别多可以适当加大到 25%。但重叠不是越多越好。我见过有人设 50% 重叠结果检索出来的 Top-5 里有三个块内容高度重复等于白白浪费了上下文窗口。重叠是为了兜底不是为了堆量。4.3 按标题层级切分的实操细节对于结构清晰的文档我最推荐按标题层级切分。具体做法是把文档解析成树状结构每个叶子节点最细一级的标题下的内容作为一个基础块。如果某个叶子节点内容太长再在内部按段落二次切分如果太短就和相邻的兄弟节点合并。这样做的好处是每个块都自带上下文路径。比如一个块来自产品手册 第三章 计费规则 3.2 退款政策检索出来的时候你可以把这个路径作为元数据一起带上模型看到路径就知道这段内容属于哪个部分理解起来更准确。我在实际项目里会给每个块存这些元数据来源文档、标题路径、块在文档中的位置、创建时间、文档版本。这些信息在检索后过滤和结果展示时非常有用。比如用户问的是最新政策你就可以按创建时间过滤掉旧版本的内容。4.4 特殊内容的切分要单独处理表格、代码、公式这几类内容用通用的文本切分方式处理会出大问题。表格如果被按行切开表头和数据行分离检索出来就是一堆没有意义的数字。我的做法是把表格整体作为一个块或者转成字段名: 值的键值对形式再嵌入。如果表格特别大就按行分组但每组都要重复带上表头。代码要按函数或类切分保持语法完整。把函数从中间切断嵌入出来的向量语义是混乱的。同时代码块最好带上它所在的文件路径和函数签名作为元数据。公式建议转成 LaTeX 或自然语言描述再嵌入直接嵌入公式符号嵌入模型基本理解不了。5. 向量检索与重排序的配合打法5.1 相似度计算方式的选择向量检索的核心是计算两个向量的相似度。常见的有余弦相似度、点积、欧氏距离。余弦相似度只看方向不看长度对文本嵌入最常用点积受向量长度影响如果嵌入模型输出的是归一化向量点积和余弦等价欧氏距离衡量的是空间直线距离对绝对位置敏感。大多数嵌入模型配套的检索库会默认用余弦相似度你直接用就行。但要注意一点不同嵌入模型的相似度分数不可比。A 模型算出来 0.85 算高度相关B 模型算出来 0.85 可能只是中等相关。所以阈值过滤的数值必须针对你实际用的模型来标定不能照搬别人的经验值。5.2 为什么需要重排序这一步向量检索是粗筛它快但不够准。它把语义相近的候选捞出来但排序未必符合真正的相关性。这时候就需要重排序Rerank用一个更精细但更慢的模型对粗筛出来的候选重新打分排序。重排序模型通常是交叉编码器Cross-Encoder它把问题和候选片段拼在一起输入模型直接输出相关性分数。这种方式比向量点积精确得多因为它能捕捉问题和片段之间的细粒度交互。代价是慢所以只用在粗筛后的少量候选上。我的标准流程是向量检索召回 Top-20 到 Top-50重排序后取 Top-3 到 Top-5 送给 LLM。实测下来加了重排序之后真正有用的片段排进前三的概率大幅提升模型回答的准确率也跟着涨。这一步的投入产出比非常高强烈建议加上。5.3 混合检索稠密加稀疏的组合拳纯稠密嵌入有个短板对专有名词、产品型号、错误码这类精确匹配需求它不如关键词检索。比如用户搜错误码 E5021稠密嵌入可能把它和错误码 E5022匹配得很近因为语义上都是错误码相关但用户要的是精确的那一个。解决办法是混合检索一路用稠密嵌入做语义召回一路用稀疏检索比如 BM25做关键词召回然后把两路结果融合。融合方式有加权求和、倒数排名融合RRF等。我一般用 RRF因为它不需要调权重对两路结果的分数尺度不敏感比较省心。混合检索在真实业务里几乎是标配。纯语义检索在 Demo 上看着很美一上真实数据遇到型号、编号、专有名词就露馅。5.4 检索结果去重与多样性控制检索回来的片段经常有大量重复。同一个知识点在文档里出现多次或者切分时的重叠导致相邻块内容高度相似都会让 Top-K 里塞满重复信息。我的去重做法是对候选片段两两计算相似度超过阈值的只保留分数最高的那个。另外还可以做多样性控制比如用 MMR最大边际相关性算法在保证相关性的同时让选出的片段之间尽量不重复覆盖不同的信息点。这个在处理总结类问题时特别有用。用户问这个产品的核心功能有哪些如果检索回来的五个片段都在讲同一个功能模型总结出来就只有一个功能。多样性控制能保证检索结果覆盖到不同方面。6. 把管道跑起来之后才会遇到的真实问题6.1 召回率看着高回答却不对这是最典型的数据好看、效果拉胯场景。你测召回率Top-10 里确实有相关片段召回率 90% 以上但用户就是觉得回答不对。问题往往出在排序上相关片段排在第七第八位而模型只用了前三个自然答不好。解决办法就是前面说的重排序把真正相关的顶上来。另一个原因是片段本身质量差虽然相关但内容残缺或表述混乱模型基于它生成也会跑偏。这时候要回头检查切分策略。6.2 多轮对话里检索结果打架单轮问答时检索很准一到多轮对话就乱套。原因是多轮对话里用户的问题经常是省略的、指代的。比如第一轮问退款政策是什么第二轮问那超过七天呢这个那指代的是退款政策但检索时如果只拿那超过七天呢去搜根本搜不到相关内容。我的处理方式是查询改写在检索之前先用 LLM 结合对话历史把当前问题改写成独立完整的查询。上面那个例子会被改写成退款政策中超过七天的规定是什么再去检索就准了。这一步会增加一次 LLM 调用但对多轮场景几乎是必需的。6.3 知识库更新后的检索漂移知识库更新后新文档嵌入了旧文档还在如果两者内容有冲突检索时可能同时召回新旧两个版本模型不知道该信哪个。更隐蔽的问题是新文档的嵌入分布和旧文档不一致导致检索结果整体偏向某一批。我的做法是给文档打上生效时间和版本号检索后按时间过滤只保留当前有效的版本。如果新旧版本需要共存比如查历史政策就在元数据里区分让用户或系统明确指定查哪个版本。6.4 上下文窗口被检索材料挤爆检索回来一堆片段加上系统提示和对话历史直接超出模型上下文窗口。这时候要么报错要么模型自动截断把最重要的信息截掉了。我的做法是预算管理先算好系统提示和对话历史占多少 token剩下的留给检索材料。检索材料按相关性排序从高到低往里塞塞满为止。同时给每个片段设一个最大长度超长的片段先做摘要或截断。这样能保证最重要的信息一定进得去。7. 我在实际项目里沉淀的几条经验第一条先把检索做扎实再考虑花哨的生成。很多人一上来就研究怎么让 LLM 输出更漂亮却忽略了检索回来的材料是不是对的。我建议把 70% 的精力放在管道前半段文档解析、切分、嵌入、检索、重排序。这部分做好了哪怕用普通的生成模型效果也不会差。第二条建立一套自己的评估集。不要凭感觉判断 RAG 好不好要有一批标注好的问题和标准答案每次调整管道后跑一遍看准确率、召回率、回答质量的变化。我一般会准备 50 到 100 个真实用户问题作为评估集覆盖不同难度和类型。没有评估集调优就是盲人摸象。第三条元数据是宝藏别浪费。来源、时间、版本、标题路径、文档类型这些元数据在检索过滤、结果展示、问题排查时都能派上大用场。我见过有人只存了向量和原文后面想按时间过滤都做不到只能重建整个库。第四条别迷信框架理解原理更重要。各种 RAG 框架层出不穷封装得越来越厚但底层逻辑就是这篇讲的这些切分、嵌入、检索、重排序、组装。理解了原理你才知道框架里哪些参数该调、哪些默认值不适合你的场景。框架是工具不是黑盒。第五条从小数据量开始验证。不要一上来就把整个知识库灌进去先拿几十篇文档跑通全流程把每个环节的问题暴露出来再逐步扩大数据量。数据量一大很多在小规模下不明显的问题比如检索延迟、存储成本、更新策略会集中爆发提前验证能省很多返工。最后分享一个我常用的排查思路当 RAG 回答不对时我会按顺序检查——检索回来的片段里有没有正确答案如果有是不是排序太靠后如果排序没问题是不是片段内容残缺如果片段完整是不是上下文组装时被截断了如果都正常那才轮到怀疑生成模型。这个顺序能帮你快速定位问题出在管道的哪一段而不是盲目地换模型、调参数。