连接数据只完成了21%:RAG真正难在切分、检索与评估

📅 2026/8/27 7:57:04
连接数据只完成了21%:RAG真正难在切分、检索与评估
把 LLM 接到自己的数据上听起来是最难的一步。但真正做过一遍后你会发现这只是整条路上最先完成的一小段。我见过不少团队把文档灌进向量库跑通一个问答 demo然后宣布知识库助手已经完成。实际上更准确的说法是连接数据只是那 21% 的解决方案。剩下的 79%藏在数据质量、切分策略、检索效果、上下文组装、权限控制和评估迭代里。如果把这 79% 全部忽略那么接完数据后的系统通常会表现为答案不稳定、引错来源、超窗口、甚至产生看似合理但完全错误的回复。你可以把这个“21%”当成一个经验判断而不是精确统计。它想提醒的事情很简单数据连接解决了“模型能不能看到数据”的问题但没有解决“模型能不能正确使用数据”的问题。看到和用对之间隔着好几个关键环节。就像给实习生发了一柜子资料不等于他能做好分析真正的价值发生在检索、理解、判断、表达这些更靠后的步骤里。理解了这个前提后面所有技术选型和调试优先级都会变得清楚很多。1. 为什么“接上数据”会让大部分团队产生 80% 的错觉1.1 demo 跑通带来的虚假完成感当你能上传 PDF能从向量库检索到相关内容能返回一段带引用的回答时那种正反馈是非常强的。技术团队往往会在这个节点松懈因为“已经看见了成果”。产品团队也会觉得核心功能已经搞定剩下只是优化体验。这种虚假完成感正是 21% 被误认为 80% 的心理机制。从工程经验看demo 用一条文档和一条 query 看起来正常往往是因为问题边界太小。你的文档里只有一层意思切分粒度不会明显影响结果你的 query 和文档关键词高度重合召回结果自然很准。一旦数据量增加到数万条用户问题变成口语化、指代化、多跳式问题就会集中暴露。我通常建议团队在 demo 跑通后立刻做两件事。第一准备 20 到 30 条真实用户可能会问的问题而不是自己设计的问题。第二把检索到的中间结果全部打印出来直接看召回片段到底相不相关。这两件事做完虚假完成感通常就会消退。1.2 “连接”和“可用”之间的距离连接数据在技术实现上通常是这样一条链路加载文档 → 切分 → 做 embedding → 存入向量库 → 用户提问时检索相关片段 → 塞进 prompt → 交给 LLM 生成。在这个流程里“连接”只完成了前几步和最后一步调用。中间的大量决策——切多长、用什么 embedding、检索多少条、怎么排序、上下文怎么组织、冲突怎么处理——都会直接影响最终结果。如果把整个 RAG 流程看作一条链路数据接入只占一个环节。更准确地说连接数据只是把数据从“不可达”变成了“可见”。但“可见”不代表模型能正确理解不代表模型能准确引用也不代表模型在复杂场景下不产生幻觉。很多系统问题都发生在可见之后数据被看到了但看错了重点或者不知道该怎么用。所以我会把“连接”定义为第一步而不是核心。它重要但权重不该超过 21%。1.3 21% 不是坏消息而是定位说“连接数据只是 21% 的解决方案”不是为了否定数据连接的价值。恰恰相反没有这 21%后面所有环节都无法开始。它的意义在于不要把起点当成终点不要因为连接成功就停止投入。把它当成项目的第一阶段你会自然地分配精力先跑通最小流程再补数据治理再调检索再搭评估。如果把它当成项目的全部你会陷入“为什么数据都接进去了回答还是不对”的困惑。这个定位差异决定了后续调试的方法论。2. 数据切分与治理21% 之外的第一块硬骨头2.1 不是所有文档都适合直接进向量库很多团队接数据时把 Word、PDF、Markdown 一股脑切块 embedding很快就会发现表格被切碎代码块被拆开PDF 里的多栏文字错乱同一主题被分散到多个 chunk。结果检索到的片段要么不完整要么边界切在句子中间。这个问题不是 model 能力不行而是输入数据本身就没准备好。数据治理要做的是把原始数据转换成适合模型检索的形态。这包括清洗掉页眉页脚、目录、无效空格统一文档格式对表格、列表、代码块做结构化处理对长文档保留标题层级。这一步不能省因为它直接决定后续 embedding 和检索的质量。在实操里常见做法是先对文档类型做分类。规范类文档可以按章节切问答类文档可以按条目切表格类文档要转成 Markdown 表格或文本摘要代码类文档要保留代码块边界。如果原始材料格式杂乱宁可先用脚本清洗也不要盲目喂进向量库。文档类型建议处理方式常见问题规范手册按章节和标题层级切分标题和正文脱节常见问题按问题条目切分答案和问题被拆开表格数据转成 Markdown 表格或文本摘要切块后行列错乱代码示例保留代码块边界关键字被切断网页抓取去掉导航、广告、重复正文大量无关文本进向量库2.2 chunk 大小与重叠不是玄学是检索质量的开关chunk 的粒度影响两点一是 embedding 的语义密度二是上下文窗口的利用率。chunk 太小单个片段信息量不足模型缺少上下文chunk 太大向量表示容易被多个主题稀释而且检索命中后可能把大量无关内容带进 prompt。常见经验值是 300 到 800 token 左右重叠 50 到 100 token。但这个值必须结合文档类型和实际检索效果调整。更通用的做法是先跑一批代表性 query比较不同切分参数下的召回和回答质量而不是直接套用某个社区的默认值。我曾经遇到一个场景技术文档里经常出现“这个变量”“上述函数”这类指代表达。如果 chunk 太小模型只看到局部完全不知道“这个”指的是什么。后来把切分窗口调大同时保留章节路径问题就明显缓解了。这说明切分策略必须服务于语义单位而不是机械地按字数切。2.3 版本更新与去重数据是活的不是一次性的如果文档是静态的一次接入就够了。但真实场景里文档会更新、会被删除、会有多个版本同时存在。很多系统只做了导入没有更新机制导致模型总是引用旧版本。要长期使用就需要给文档加元数据例如来源、更新时间、版本号、权限范围。这样检索时才能按条件过滤也可以在建索引时对已删除或过期文档做同步清理。这里的思路和普通后端系统没有本质区别先保证数据层可追溯才能保证上层输出可解释。否则出现问题后你甚至无法判断模型是从哪一个旧版本里找到的答案。更麻烦的是用户问“为什么答案和最新规则不一致”时你只能去翻数据源而不是直接看检索日志。我建议在数据库表结构设计时就预留几个字段文档来源、上传时间、生效时间、版本号、权限组。这些字段在后续检索过滤和评估归因时非常有用。3. 检索和上下文组装从“查得到”到“答得对”3.1 Top-K 只是把候选捞回来排序才是真正的筛选多数初版 RAG 实现只会做语义相似度检索取 Top-K然后直接塞进 prompt。但 embedding 的相似度不一定等于问题相关性。用户问的是“这个功能怎么配置”候选片段可能包含大量同名但不同场景的段落靠向量相似度很难区分。常见的补强手段是加一层重排序。让一个专门的排序模型或更强的 LLM对召回的候选片段再打分再从结果里取最优几条。这样虽然多一次调用和更多耗时但对回答准确率的提升非常明显。如果不想引入额外模型也可以用工程手段改善给每个 chunk 加上标题、章节路径、摘要等元数据检索时按元数据过滤候选范围或者采用混合检索把关键词匹配和向量召回的结果合并去重再统一排序。每条路径都有成本所以建议先用小样本评估再决定是否引入。这里有一条很实用的经验不要只看排序结果还要看召回的候选里是否包含正确答案。如果正确答案根本没被召回那无论重排序多强都无用。这个阶段要回到数据层和切分策略去解决问题。3.2 上下文组装不是把片段拼起来而是帮模型建立“信息秩序”即使检索结果都正确prompt 里片段之间的顺序、格式、引用方式也会影响回答。模型需要知道哪些片段是核心依据哪些是补充说明哪些可能与问题无关但被召回。一种常见做法是给每个片段编号并在 prompt 中明确要求模型“优先引用编号片段如果片段没有足够信息直接说不知道”。还可以在片段前标注来源、篇名、章节让模型在回答时能引用出处。这些细节看着小却能把“看似有个答案”提升到“答案有依据”。我平时会用一个很简单的 prompt 结构你是一个知识库助手。请根据下面提供的资料回答问题。 资料片段 doc id1 source产品手册-第3章.../doc doc id2 sourceFAQ-常见问题#12.../doc 回答要求 1. 优先引用编号片段作为依据。 2. 如果片段之间存在矛盾请指出矛盾并列出各自来源。 3. 如果资料中没有足够信息直接回答“当前资料不足”不要编造。这样做的价值是让模型在生成前就知道自己有哪些可用信息、信息边界在哪里。很多幻觉问题其实能在上下文组装阶段就提前规避一部分。3.3 冲突信息与幻觉数据越多越要处理“不确定”当多个片段同时存在而且彼此矛盾时模型最容易犯错。很多情况下模型不会主动告诉你数据冲突而是选择其中一个片段的内容生成回答或者把冲突信息融合成一个看似正确的答案。要处理这个问题可以在 prompt 里加入“如果多个片段存在矛盾请指出并列出各自来源”。也可以通过后处理规则在模型回答后检查引用来源是否真的支持结论。这些内容属于“如何用数据”的范畴距离 21% 的数据连接已经有明显距离但恰恰是决定用户是否信任系统的关键。用户不会因为你接入了数据就满意他们只会因为每次回答都准确、可追溯、可验证而信任系统。4. 评估体系才是那 79% 里的中枢4.1 没有评估就无法判断剩下 79% 到底做完没有很多团队在数据接入和检索优化之后面临一个尴尬系统看起来能用但不确定它“够好了”。要判断优化方向就要建立评估集和评估指标。建议先准备 50 到 100 条有代表性的真实问题覆盖常规回答、边界情况、跨章节问题、找不到答案、有明显干扰信息等。然后定义几个维度正确性、完整性、引用相关性、拒绝率不知道答案时是否如实说、超时率。不一定要做成多复杂的评分系统哪怕先用人工标注打分也能帮你定位问题出在哪一层。评估维度说明可能的问题来源正确性回答的事实是否正确检索、生成、数据源完整性是否漏掉了关键信息点切分、检索 Top-K 不足引用相关性回答是否真的基于给出的片段Prompt 指令、生成层拒绝率不知道时是否如实回答Prompt、模型能力超时率响应是否在预期时间内上下文长度、重排序、模型调用4.2 从评估结果回溯到问题层如果评估结果显示多数错误是“答案与检索到的内容不一致”问题大概率在生成层需要改 prompt 或后处理。如果错误是“检索到的内容本身不相关”问题在检索层或 embedding 层。如果问题是“检索到的内容相关但缺失关键信息”问题可能在切分策略或数据完整性。这就是一个可复用的排查框架先看输出再判断归因到数据、切分、检索、上下文、生成哪一层。这个框架比盲目调参数更有效。举个例子某次评估里系统对于“退款政策是什么”的回答总是只提到网页抓取到的摘要没有提到原始 PDF 里的完整政策。后来检查发现PDF 的表格被切碎了退款比例和条件被分到不同 chunk。问题不在生成层也不在检索层而在数据切分。没有评估这类问题可能要等用户投诉才会暴露。4.3 把评估变成常规动作而不是上线前的一次性检查数据更新、embedding 模型升级、prompt 调整都会让系统行为发生变化。建议在每次调整后跑一遍评估集保留对比记录。如果评估集足够稳定还可以在 CI 流程中加入回归检查。这样系统在迭代过程中不会出现“改好一个 case坏掉一片 case”的情况。评估体系不会直接提升效果但它让其他 79% 的优化工作有了方向所以它是这套解决方案里的中枢。没有评估你只能靠感觉判断“好像变好了”有了评估你才能说“这个参数提升了 3 个百分点的正确性”。5. 从单点实验到工程化编排框架、MCP 与 Agent 的定位5.1 为什么单点跑通不等于能长期使用单点实验通常是在脚本或 Notebook 里完成的。你写了加载、切分、检索、生成输入一个 query得到输出。但真实产品还需要服务化接口、日志、监控、重试、限流、权限控制、并发管理。这些都不属于“连接数据”本身而是工程化的一部分。从实践看如果系统只是内部工具、数据量小、用户少脚本方案也可以接受。但如果要面向团队或外部用户就需要引入更完整的服务框架。这也是为什么现在很多项目会使用 LLM 编排框架它们不只解决数据连接还解决了多步流程、工具调用、记忆管理、权限校验和可观测性。5.2 编排框架解决的是“流程控制”不是“数据连接”LangChain、LlamaIndex、Dify、FastGPT 这类工具的核心价值是把“数据加载 → 切分 → 检索 → 生成 → 工具调用”这个过程变成可配置、可复用、可观测的流程。你不需要自己处理每个环节的细节但必须理解每个环节的语义。不过框架也带来新的问题版本更新快、抽象层次高、调试时不容易看清底层发生了什么。所以更稳妥的方式是先用最小代码量跑通再渐进式引入框架。如果一开始就套大量高级抽象遇到问题反而不好排查。我自己的习惯是第一版尽量少用框架让数据链路清晰可见当问题稳定后再引入框架来统一管理流程。这样既能享受框架的工程能力又不至于被框架的抽象挡住视线。5.3 MCP 和 Agent让数据成为可被主动调用的资源MCP 的意义在于给模型和外部工具之间定了一个标准接口。模型不再需要针对每个数据源写独立适配器而是通过统一协议访问工具、文件、数据库、API 等资源。这进一步降低了“连接”的技术成本也再次带出那个判断连接本身正在变得越来越便宜真正值钱的是如何组织、选择和校验数据。Agent 场景也一样。当模型可以主动调用工具时数据不是预先塞进 prompt而是在需要时通过工具获取。这一下把上下文长度、实时性、数据范围的问题都放大了。比如一个 Agent 需要先查订单再查库存再决定回复每一步的中间结果都要可控、可追踪。没有权限校验和数据质量保障Agent 越快越容易把错误放大。所以把 LLM 连接到数据只是开始当这个连接变成可被模型主动调用、动态选择的资源时工程复杂度会更高也更需要清晰的权限边界和评估反馈。5.4 选型时先问三个问题面对编排框架和 Agent 工具时我建议先问三个问题一是你的流程是否真的有多个步骤需要编排二是每一步之间是否需要传递状态三是你是否需要完整的日志和权限体系如果答案都是“是”那引入框架是合理的。如果只是简单问答不一定要上重型框架。这个判断逻辑和 21% 的思路一脉相承不要为了一个只占 21% 的问题搭建一个覆盖 200% 的方案。先把核心痛点找准确再决定投入多少工程成本。6. 落地时的排查链路和适用边界6.1 一套可复用的排查链路如果系统已经上线但回答质量不稳定不要一上来就调 prompt。按下面的顺序排查看现象错误是答案错误、引用错误、拒答错误还是超时、无响应看输入用户 query 是否被正确解析是否有噪音或指代不清看数据候选文档是否最新、是否完整、是否被错误切分看检索召回的 Top-K 是否相关相关片段是否排在前面看上下文最终进入 prompt 的片段是否完整是否发生了截断或冲突看生成prompt 指令是否明确模型是否有足够信息有没有被要求输出没有依据的内容每个环节都有对应的中间产物数据源列表、切分结果、embedding 向量、检索结果、prompt 内容、生成文本。把这些中间日志打出来问题就很难滑走。我见过不少“模型回答错误”的 case最后定位到是上游把日期字段格式弄错了这种问题光调 prompt 永远解决不了。6.2 什么情况下 21% 已经足够也有一些场景数据连接真的占了大头。比如问题非常结构化、文档非常简单、用户 query 与文档关键词高度重合、模型只做摘要或抽取、不要求实时更新。这种时候一个简单的数据接入加上默认参数就可能够用。判断依据可以总结成三个问题数据是否稳定用户问题是否规范答案是否允许模糊如果三个答案都偏向“稳定、规范、允许模糊”就可以先交付不必过度工程化。例如一个内部系统的操作手册用户通常会问“某按钮在哪”“某参数怎么填”这类问题关键词集中检索相对容易。这时候把数据接好、切分合理效果就可能已经接近可用。6.3 不适用和需要注意的边界相反如果数据更新频繁、用户问题发散、答案必须可验证那就必须把重心从连接挪到数据治理、检索优化、评估和权限上。只把数据连进去远远不够。另外也要注意成本和延迟。重排序、长上下文、多轮 Agent 调用都会增加成本和耗时。每个优化方案都要问一句它带来的效果提升是否值得增加的时间与费用这个权衡没有统一答案但应该在 79% 的投入过程中反复校验。最后提醒一点不要把“连接数据”这项工作做成了“为连接而连接”。如果你的数据根本不需要被模型访问或者数据质量差到会误导模型那还不如不接。连接之前先确认数据值不值得接、能不能被理解、更新机制是否可持续。这比急着做大而全的知识库更重要。回到开头那个被反复提到的 21%它不是一个精确的数字而是一个提醒。把 LLM 连接到数据是进入这个系统的最短路径也是最容易让人误判进度的地方。真正有价值的是在连接之后建立一套让数据变得可治理、可检索、可验证、可迭代的机制。如果你现在正准备做这类系统我建议你先跑通一个最小流程然后用 50 条真实问题去做一轮评估看看错误到底集中在数据层、检索层还是生成层。从那里开始补齐会比反复调 prompt 更有用。