ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MCP返回50条模型只见20条:静默截断排查

MCP返回50条模型只见20条:静默截断排查 MCP返回50条模型只看到20条Agent静默截断排查与防御有一类 Agent 问题特别难排查不报错、不超时日志里甚至全是成功模型也正常返回了一段很完整的答案但答案的基础其实少了一大截。一篇社区复盘记录了这样一个案例数据源有 50 条记录真正进入模型上下文的只有前 20 条剩下 30 条没有报错、没有空值也没有显眼异常。模型基于自己看到的 20 条继续回答。如果只看最终文本很难意识到它根本没看全。说明本文事实依据来自一篇社区复盘community_signal属于作者第一人称的排查经历与工程总结并非官方文档、官方公告或官方一手资料。下文统一以“来源作者”“这篇复盘”归因不将社区经验升级为官方结论。问题不在模型而在 LLM 之前来源作者描述的链路是权威数据源 ↓ 读取 / 过滤 / 配额限制 ↓ 准备模型输入 ↓ LLM 总结或转述 ↓ 最终回答问题发生在 LLM 之前中间层有一个条目上限只取了 20 条。对模型来说这 20 条就是它的全部世界。它既不知道后面还有 30 条也没有理由主动说“我可能没看全”。这类问题一开始很像 LLM 波动同样一批资料有时总结不错有时明显漏掉后面的内容。自然反应是怀疑上下文太长、注意力不够、换模型、把 prompt 写得更明确。来源作者往前查后发现模型根本没机会看到那些数据。真正需要检查的是源端这次有多少条、调用方希望处理多少条、真正进入模型输入的是多少条、有没有因字节限制被截断、有没有记录在解析或过滤时被跳过。把“模型看到了多少”变成显式字段来源作者给出的简化结构为{ requested: 50, loaded: 20, byteComplete: true, filteredCount: 0, inputComplete: false, reason: entry_limit }重点不是字段名而是系统能明确说这次送给模型的输入不是完整集合。如果inputCompletefalse后续回答不能继续假装基于全集。但requested50 loaded50 filtered0 byteCompletetrue也不能证明一切。它最多证明按当前系统记录50 条目标记录都进入了模型输入没有被已知的数量、字节或过滤规则截掉。它没有证明模型真的充分利用了 50 条也没有证明最终结论正确。来源作者把“完整”拆成三层源集合是否定义完整目标数据是否完整进入模型最终回答是否正确覆盖这些数据。第一层如用户 37 个持仓、数据库 120 条、文档 16 章节、API 分页 8 页第二层是本文主要问题37 个持仓是否全部进入链路120 条有没有只拿前 100 条8 页是否第 7 页失败后仍当完成第三层是模型质量需要独立评测。不能用inputCompletetrue给模型质量背书。数量一致不等于身份一致来源作者举了一个例子源端目标是A B C D E中间层实际拿到A B C D D。数量仍然是 5requestedloaded但 E 丢了D 重复了一次。只看数量会误判为完整。对有稳定身份的有限集合应加一层 identity 对账例如{ expectedCount: 5, loadedCount: 5, expectedIdsHash: sha256:..., loadedIdsHash: sha256:..., inputComplete: false, reason: identity_mismatch }实际实现不一定非要 hash也可以是 item ID 集合、page cursor、snapshot version、offset 范围、continuation token 或数据源自己的 revision。关键点是数量一致是必要信号不是充分证据。三种静默丢数据来源作者总结了三种静默丢数据的方式。1. 条目数被截断源端 50 条配置只允许 20 条剩下 30 条在进入模型之前就消失了。如果系统不记录原始数量后面基本没人知道发生过什么。2. 字节数被截断条目数量没超过限制但某几条特别长累计输入超过 byte budget 后尾部被截掉。这时甚至可能看到requested20 loaded20数量完全正常但第 20 条其实只剩一半。数量和字节要分开看。3. 过滤阶段偷偷丢记录解析和过滤阶段某条数据可能格式异常、字段为空、schema 不兼容或转换函数抛错后被 catch 掉。系统可能选择 skip 然后继续处理剩余内容最终依然能产生漂亮的模型输入只不过分母已经变了。“读取成功”这种状态过于粗糙。读取成功到底是请求成功、至少拿到一条、所有目标记录都拿到还是所有字节都完整这几个含义差得很远。配额不能简单地一路调大发现 50 条只进了 20 条以后最直接的修复当然是把 20 改成 60。来源作者表示在当前案例里这确实解决了问题。但如果把它总结成“上下文越大越好”很快又会踩另一个坑。输入越大延迟越高、token 成本越高、模型注意力更分散、单次失败的重试成本更高极端数据更容易把整个请求拖死。配额真正要解决的不是“尽可能塞进去”而是超出单次可靠处理范围时系统应该知道自己没处理完。如果数据太多更合理的选择可能是分批读取、每批记录覆盖信息、中间结果结构化、最后汇总而不是发现会截断就把限制继续调大直到某天再次截断。配额本身不是问题静默配额才是问题。尾部丢失测试来源作者提到后来专门写了一个“尾部丢失”测试上限是 60就准备 65 条记录并把真正关键的数据放在最后几条这样旧链路一定会暴露问题。测试不再只检查函数返回了结果而是检查requested 65 loaded 60 inputComplete false reason entry_limit如果是字节超限则应该得到类似byteComplete false inputComplete false reason byte_truncated如果有过滤filteredCount 0 inputComplete false reason filtered正常的小数据很难覆盖“不完整”分支。如果一直只用 5 条、10 条数据测系统可能几年都不会真正走一次截断路径然后第一次走就是线上。部分结果不等于不能回答来源作者早期做法比较保守只要输入不完整回答就只能是草稿不能给肯定结论。方向没错但写得太死也会有问题因为部分数据并不意味着所有结论都失效。例如 100 个持仓里只拿到了 80 个。当然不能说“这就是你的完整持仓分布”。但如果问题只是“这 80 个已读取持仓里有没有黄金 ETF”那么在明确范围之后仍然可以回答在当前已读取的 80 个持仓里没有发现黄金 ETF另有 20 个持仓未读取不能据此判断完整组合没有黄金敞口。真正应该被限制的是断言范围不是文风。完整输入可以对完整目标范围作答部分输入只能对已覆盖范围作答同时披露缺失范围禁止把局部结论升级成全集结论。一个很小的检查层来源作者最后落下来的机制并不复杂。对于有明确全集的读取任务在进入模型前保留几类信息{ sourceSnapshot: snapshot-id, requestedCount: 50, loadedCount: 50, filteredCount: 0, byteComplete: true, identityComplete: true, inputComplete: true }如果其中任何关键项不成立{ requestedCount: 65, loadedCount: 60, filteredCount: 0, byteComplete: true, identityComplete: false, inputComplete: false, reason: entry_limit }后续逻辑就知道不能把当前输入描述成全集回答中的断言范围要缩小UI 可以展示缺失Runtime 可以选择补拉、分页或停止测试和日志能准确知道丢在了哪里。模型本身不需要理解这些状态是怎么计算的它只需要得到明确事实当前输入覆盖 60/65 条缺少 5 条。边界开放搜索不存在天然全集上面这套做法很适合持仓列表、数据库查询、文件清单、API 分页、指定文档、明确范围内的工具结果因为我们至少知道“完整”大概是什么意思。但如果任务是“搜索互联网上所有关于某公司的重要信息”就不能套同一套逻辑然后说returned requested complete。开放搜索没有一个天然可枚举的全集搜索引擎返回 20 条不代表世界上只有 20 条相关信息。这种场景下更合适的说法是“当前检索计划已经执行完成”而不是“相关信息已经完整覆盖”。流程完成、输入完整、世界知识完整是三件完全不同的事。来源作者自述的受控验证结果来源作者在受控窗口里自述观察到50 条源数据只进入 20 条的静默截断被复现65 条尾部丢失用例能稳定触发不完整分支截断专项从初次 6 失败、2 通过变成当次 8 项全部通过相关回归测试当次通过。这些结果说明把一种原本静默的数据丢失变成了可以检测和测试的状态。它没有说明模型从此不会漏读最终答案一定正确所有线上输入规模都适合当前配额开放搜索也能被证明“完整”当前参数就是最佳生产参数。来源作者也提到当时还有线上质量样本和超大体积真实负载没有采集。工程复盘最容易犯的错误之一就是修了一个确定的问题顺手把旁边五个没验证的问题也一起宣布解决。写在最后以前更关注“模型答得对不对”。现在做 Agent可以往前多问一句它回答之前到底看到了什么一段语言完整、逻辑顺畅的回答可能只是对一个残缺输入做出的合理总结。这时候模型甚至没有做错什么真正的问题发生在更前面系统丢了数据却没有告诉任何人。这篇社区复盘最后留下的原则是对有明确全集的数据任务先证明输入覆盖再讨论回答质量。但也要记住另一半输入覆盖完整只证明模型有机会看到全部不证明它真的理解了全部更不证明答案一定正确。把这两个边界同时守住才不会从一种“假完整”走到另一种“假确定”。
返回列表