loading

Loading

探索与发现

首页 Agent 工程

工具返回结果被截断之后,Agent 为什么还在找

分类:Agent 工程
字数: (2230)
阅读: (8)
0
摘要:工具返回超长结果时,问题不在截断本身,而在 Agent 不知道眼前是残片。结合 QwenPaw 反复调用 recall_history 的案例,拆解四类截断点、三条压缩路径的代价,以及截断提示该给出哪些具体信息。

工具返回超长结果时,最容易踩的坑不是截断本身,而是截断之后 Agent 不知道自己看到的是残片。QwenPaw 的 issue #5946 记录了一个很具体的现场:execute_shell_command 的输出被截断后,上下文里留下 <<<TRUNCATED ~18948 tokens>>,后面跟一段 system-info,说完整输出已经持久化,可以调用 recall_history(op="recall_tool", tool_call_id="...") 取回。结果 Agent 在那之后对着一份仍在上下文里的内容反复发起 recall_history,做了一堆无效调用。

这个问题值得单独拿出来讲,因为它把“截断处理”从一条数据处理规则,变成了 Agent 状态管理问题。

截断点不止一处,静默的那个最麻烦

[3] 那篇排查记给的链路拆解是可以直接拿来做检查表的:数据源层可能自己只返回了前 20 条;编排层可能给单条工具结果设了硬上限;消息序列化和接入层(比如 OpenAI 兼容接口一侧)可能在请求体大小上再做一次裁剪;模型服务端也可能按自己的窗口二次处理。四层里任何一层做了截断而没留下标记,上层就无从判断信息是否完整。

工程上真正要防的是第三种:截断发生,但没有元数据。用户在页面上看到的和模型看到的不是同一份东西,日志里也看不出少了什么。等症状出来,“50 条数据只剩 20 条”这种表象已经走了好几层。

所以第一步不是选压缩算法,而是在链路每层插桩:记录原始长度、字符数、估算 token、实际进入上下文 token、是否截断、截断在哪一段、截断策略名。有 trace 之后再谈策略,否则调参全是盲调。

三条压缩路径的取舍

[1] 把工具结果进上下文前的路径归成三类:直接截断、抽取式摘要、结构化解析。这个分类不新,但落地时的代价差别很大。

  • 直接截断:最便宜、最确定。头部或尾部保留 N 个字符,加一条明确的截断标记。问题是信息密度不由你控制,一个大 HTML 页面截下来可能全是导航栏和 script。
  • 抽取式摘要:用规则或小模型抽关键句、关键字段。比截断贵,但更适合文本类结果(抓取正文、长日志)。风险是摘要本身可能给出错误的完整性暗示,让 Agent 误以为这就是全部。
  • 结构化解析:先把结果解析成结构(JSON 字段、表格、行号索引),再按槽位填。最可靠,也最贵,且依赖于工具输出格式稳定。网页抓取一旦 DOM 变了,解析器静默挂掉比截断还糟。

我的默认选择是:结构化解析优先,解析失败退到截断加显式标记,抽取式摘要只用于明确的文本类工具。三层都失败时不要硬塞,直接返回“结果过大,已引导按块读取”,然后把分页/增量的控制权交给 Agent。

分页比压缩更根本

很多工具的返回值本来就是可切片的,只是我们懒,一次把全部拉了回来。比如日之类文件读取、数据库查询、网页抓取,完全可以按行区间或字节偏移给工具加参数,让 Agent 自己决定 `offset` 和 `limit`,失败的第二步再考虑“小切片 + 多次调用”。这样上下文里永远是 Agent 主动选择的范围,而不是被动截断后的半截。

代价是调用轮次变多,token 账单和延迟都会涨。所以分页只对大结果生效,而且要设一个上限:最多几次切片,否则一次抓取能拖成十几轮。

截断标记要能定位,不要只写“已截断”

回到 QwenPaw 那个 bug。按 issue 里给的两个方向,方案 B 是改提示文案,让 Agent 明白截断 ≠ 丢失,先在上下文里找,再决定要不要 recall。这看起来只是措辞,实际上是信息结构问题:只告诉 Agent“被截断了”,它会倾向于用工具去补;告诉它“已截断,截断点为字节 X,原文第 1-40 行仍在上下文,第 41 行之后需按 offset 读取”,它才有可执行的下一步。

截断提示不是一个告知,是一份路线图。缺少位置和恢复路径的截断提示,会诱发 Agent 的空转。

实践上至少要有四项:截断阈值、原始长度、保留区间、恢复方式(token 数还是偏移量)。恢复方式最好校验:如果引用的 tool_call_id 不存在或早已被淘汰,要明确返回失败而不是继续重试。

别把恢复成本藏起来

截断相关的问题在观测上应该单列。一笔请求因为截断而增加的工具调用、因此多花的 token 和延迟,算在“截断成本”还是“工具成本”里,决定了你下一步优化哪块。至少要有两个指标:带截断标记的工具结果占比,以及截断之后额外发起的补全类调用次数。前一个不动后一个涨,基本说明提示写得不够好。

另一个容易被忽略的边界是保留完整原文的存储。窗口替 Agent 背下了任务,它的生命周期、检索权限、是否能被重复引用,都要和上下文生命周期对齐,否则就出现“原文还在,引用已经失效”的静默失配。

回到现场

工具输出截断本身不是难点。难的是让 Agent 对“它看到的是否完整”有正确的内部模型。一旦这一点没对齐,无论你用截断、摘要还是结构化,Agent 都会用一个错误的前提去行动。QwenPaw#5946 反复无效 recall 的根因不在 recall 工具,而在于上下文没有被清楚地标成一个有边界的视图标记。这也决定了策略选型的优先级:先给截断加上可执行的位置信息和恢复信息,再谈如何能把大输出压入更低成本、更精准的上下文。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/agent-engineering/641.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图