别让模型搬运数据
让 LLM 做理解意图的事,让系统做精确传递的事:大结果强制外置、同一数据单一表示、跨步骤用参数绑定注入。
让 LLM 做理解意图的事,让系统做精确传递的事。这条分工一旦写进架构,很多诡异的线上问题会直接消失——因为它们的共同成因是同一个:你让一个概率系统去搬运一个字节都不能错的东西。
数据搬运谬误
模型搬运 UUID 会截断、混淆、幻觉。这不是调参能修的毛病,而是任务类型错配:识别"用户想要改哪个订单"是理解意图,模型擅长;把 a3f2-...-9c1d 这串字符一字不差地从第 3 步传到第 9 步,是精确传递,模型不该被安排在这件事上。
数组更明显。LLM 搬运大数组时会做一种"无意识摘要"——它不会老老实实传 30 个元素,而是挑 3–5 个"代表性"元素传下去,还自认为完成了任务。中间丢了 25 个,日志里看不出任何异常。qwen-harness
强制外置的阈值
所以要有硬阈值,而不是"提示模型别省略":超过约 8000 字符或 10 个元素的工具结果,必须存到外部,上下文里只留引用加一个有界摘要。强制外置从根上消除了上面那种数据丢失模式——数据根本没进过上下文,也就没有"被无意识摘要"的机会。
数据生命周期因此是条明确的链路:Tool 结果 → 分类 → Inline 或 ArtifactRef → 有界摘要 → 索引 → 按需检查 → 过期或归档。外置时要一并保留来源、Hash、Schema、权限和过期策略,并且给摘要、截断和不完整结果打标记——下游必须知道它拿到的是不是全量。
配套的还有局部检查接口:outline、search、context、head、tail。有了这些,模型才能"看一眼结构再决定读哪一段",而不是要么全读要么瞎猜。
同一数据只能有一种形态
单一表示原则:同一来源在一次模型调用中只保留一种形态。禁止同时出现完整结果加摘要、摘要加预览、完整结果加 assistant 复述、或多个不同截断版本。
原因很实际:多形态共存会让模型花大量 token 交叉验证两个说法哪个对,甚至因为两处措辞差异直接产生幻觉。这不是理论担忧——它是长链路任务劣化的主要来源之一。
检查要放在 Prompt 组装阶段,而不是运行后祈祷。检测到同一 refId 以多种形态进入待组装的 prompt,就拒绝组装并告警。这是编译期检查的思路:宁可让这一步失败,也不要让模型在脏输入上给出看起来正常的输出。
预览必须是原始文本的 substring
有一个细节教训值得单独说:LLM 重写 preview 会改变字段名和前缀,导致下游的前缀匹配恢复失败、脏数据一路流到最末端。
结论是 preview 类字段必须用原始文本 substring 生成,不经任何模型改写。它背后的原则是通用的:在数据管道里,确定性永远优先于智能性。过度智能反而有害——你以为让模型"顺手优化一下展示",实际是让它在无人察觉的地方改了数据的身份。
参数绑定与上游一次采集
跨步骤传递数据的正确姿势是 parameterBindings:由运行时从上下文直接注入,跳过模型搬运环节。模型只负责表达"这一步需要上一步的那个订单 ID",系统负责把值取出来放进去。
同一条原则往上推一层,就是上游一次采集、下游复用:同一事实源在链路上只采集一次,Source Connector → Raw Artifact → Structured Projection → Verified Summary → Downstream Reference。原始数据存成受控 Artifact,下游需要的字段转成结构化 Projection。不要让多个 Agent 分别去取同一份外部数据,也不要把完整外部 Payload 灌进长生命周期的协调 Agent——它会在那里住上几十步,持续占预算、持续被稀释。
事实也不能由模型供给
往执行端再推一步:模型不该搬运数据,它连政策事实都不该提供。τ-bench 航空场景里的取消工具是一个可以照抄的样板——舱位、是否有保险、预订时间、航段使用、航班状态全部由服务端查库得到,时间取服务端时钟 now = server_clock.now(),没有任何一项事实来自模型自报 ai-agent-book。
模型自报的 expected_cabin_class、expected_has_insurance 照样保留,但用途变了:填这两个字段的过程逼它先查订单、逐条核对取消政策,这是一份强制性 checklist。查到经济舱又没买保险,模型往往在准备参数时就发现了违规,根本不会发起调用。服务端只把它当自我陈述,不当事实;一旦自报值与库里的真值不一致就记一条 mismatch 告警——这既是模型错误认知的探测器,也是提示注入的探测器。
三层各管一段:自然语言规则帮它理解,工具参数逼它核对,服务端真值校验保证错误不变成不可逆损失。前两层减少错误发生,第三层负责守门。这里有一条容易被忽略的推论:**独立性不只是换一个模型来审,首先是换一个数据来源。**让模型自己填 cabin_class,再让另一个模型检查它填得对不对,守门员仍然形同虚设。
代价与检查
代价是你要自己建这些设施:外置存储、过期与归档策略、索引与源文件版本关联、索引缺失或低置信时回退到源搜索。单步任务、短链路不值得。
检查清单四条:dump 一次长任务,看有没有 ID 或数组是被模型从上下文里抄出来的;看同一个 refId 有没有两种形态同时出现;看 preview 字段是不是和原文逐字节相同;再看每一条政策事实——舱位、保险、时间——是查库来的还是模型填的。任何一条不过,你的数据就在靠运气传递。
