工具超大结果处理(大输出截断与修剪的 OpenClaw 三道防线)

Agent 调用工具时,返回结果可能远超预期——代码搜索返回 50KB 日志、文件读取吐出 200KB 源码、exec 命令输出 MB 级构建信息。把这些原始数据直接塞进上下文窗口,会导致对话历史被挤掉、推理质量断崖下降、延迟与成本同步飙升。OpenClaw 用三道防线处理这个问题:执行层截断、Agent 端渐进式获取、会话修剪(Pruning)。前两道在工具执行时即时生效,第三道在后续轮次中持续清理。

一、超大工具结果带来的问题

工具返回的数据量和对话上下文的容量不在一个数量级。一次 npm test 可能输出 200KB 日志,一次 grep 可能返回 50KB 匹配结果,而模型的上下文窗口即使有 128K Token,也经不起几次这样的填充。

问题 直接后果 严重程度
上下文被撑满 对话历史被挤出窗口,Agent”失忆” 高
推理质量下降 无关信息占比过高,幻觉增多 高
延迟飙升 Token 处理时间与数量成正比 中
成本飙升 按 Token 计费的 API 费用直线上升 中
缓存失效 提示缓存前缀被大结果破坏,无法复用 中

相关研究表明,当 Prompt 中无关信息占比超过 70% 时,LLM 的准确率可能下降 30% 以上。一条 50KB 的工具结果大约消耗 1.2 万 Token,在 128K 窗口中占比近 10%,几次这样的调用就能把可用空间吃光。

二、OpenClaw 的三道防线

2.1 第一道防线:执行层截断

每个工具调用执行完成后,Gateway 会检查返回结果的 Token 大小。如果超过配置的 maxToolResultTokens 阈值,Gateway 截断结果,保留开头部分,末尾附加截断标记和 Token 统计信息。

{
  "tools": {
    "maxToolResultTokens": 4096,
    "truncationMessage": "[结果已被截断,原始大小: {original} tokens,保留前 {kept} tokens。请基于已有信息回答或使用更精确的查询参数重试。]"
  }
}

截断不是简单的 Token 级切分。OpenClaw 做的是内容格式感知截断,在语义边界处截断,保证返回给 LLM 的片段仍有语义完整性:

内容格式 截断边界 原因
JSON 格式结果 完整 JSON 对象边界 避免破坏结构,模型仍可解析
代码输出 函数或类边界 保留逻辑单元完整性
纯文本 段落边界 保持语义连贯

截断标记通过 truncationMessage 模板配置,包含 {original}(原始大小)和 {kept}(保留 Token 数)两个占位符,同时承担引导功能——提示 Agent “基于已有信息回答或使用更精确的查询参数重试”。

2.2 第二道防线:Agent 端渐进式信息获取

截断标记不只是告知”被截了”,它同时引导 Agent 自主决策。Agent 收到截断提示后,可以采取渐进式策略——使用更精细的查询参数重新调用工具:

# Agent 渐进式获取伪代码
# 第一次调用:范围太大,结果被截断
result = tool_call("search_code", query="auth", scope="entire_repo")
# 结果截断标记:[结果已被截断,原始大小: 12000 tokens,保留前 4096 tokens]

# Agent 自主缩小范围
result = tool_call("search_code", query="auth", scope="src/auth/",
                   limit=20, file_type="*.ts")
# 结果在阈值内,完整返回

这种策略类似”分页查询”的思维模式。Agent 根据截断标记判断结果不完整,主动添加 limit、filter、时间范围、路径约束等参数,逐步获取所需信息,而非一次性拉取全量数据。

2.3 第三道防线:会话修剪(Session Pruning)

前两道防线在工具执行时即时生效,但即使每条结果都被截断到 4096 Token,几十轮对话累积下来仍然可观。会话修剪在后续轮次中持续清理旧工具结果。

修剪以 cache-ttl 模式运行,分两步处理:

  1. 软修剪:当上下文总大小占比超过 softTrimRatio(默认 0.3)时,对超大工具结果保留开头和结尾各 1500 字符(合计最多 4000 字符),中间插入省略号;
  2. 硬清除:若仍超过 hardClearRatio(默认 0.5),将旧工具结果内容替换为占位符 [Old tool result content cleared]。
{
  agents: {
    defaults: {
      contextPruning: {
        mode: "cache-ttl",
        ttl: "5m",
        softTrimRatio: 0.3,
        hardClearRatio: 0.5,
        keepLastAssistants: 3
      }
    }
  }
}

两条安全规则始终生效:最近 3 个助手轮次绝不被修剪,会话首条用户消息之前的内容(如 SOUL.md、USER.md 引导读取)绝不被修剪。修剪仅在内存中进行,磁盘上的完整历史记录始终保留。

三、ToolResult 的标准化结构

OpenClaw 在工具执行层将所有返回结果统一为标准化的 ToolResult 对象,截断信息通过元数据传递:

interface ToolResult {
  toolCallId: string;
  content: string;           // 给模型看的文本
  isError: boolean;
  metadata: {
    executionTimeMs: number;
    sandbox: boolean;
    approved?: boolean;
    truncated?: boolean;     // 输出是否被截断
  };
}

metadata.truncated 标记告诉 Agent 这条结果不完整,Agent 可以据此决定是否重新获取。不同模型提供商的工具结果格式有差异,OpenClaw 在 ToolResult 层统一抽象,在最后一步才做格式适配:

提供商 格式
Anthropic { type: "tool_result", tool_use_id, content, is_error }
OpenAI { role: "tool", tool_call_id, content }

这个格式差异是多模型 Agent 框架的常见痛点。OpenClaw 在 ToolResult 层统一,避免了 Provider 特定逻辑污染整个工具链路。

四、为什么不在 Agent 层做全量分段处理

一个自然的想法是:把超大结果存到文件,让 Agent 分段读取。OpenClaw 没有采用这种方案,有三个原因:

  1. 状态管理复杂度高:Agent 需要记住当前分段偏移量,引入额外状态管理,违反工具调用的无状态原则;
  2. 前 4K Token 足够:工程实践表明,绝大多数场景下工具返回结果的前 4096 Token 已包含足够信息,无需分段读取;
  3. 保持原子性:每次调用原子化、可重试,不引入”读取到一半”的中间态。
# 不采用的方案:文件存储 + 分段读取(引入状态)
offset = 0
while True:
    chunk = read_file("tool_output.txt", offset=offset, limit=4096)
    if not chunk:
        break
    # Agent 需要记住 offset,违反无状态原则
    offset += len(chunk)

# 采用的方案:截断 + 提示重新查询(无状态)
result = tool_call("exec", cmd="npm test")
if result.metadata.truncated:
    # Agent 自主决定是否用更精确的参数重试
    result = tool_call("exec", cmd="npm test --grep='auth'")

对于确实需要处理海量数据的场景,OpenClaw 推荐将工具封装为 Skill:Skill 脚本在本地处理数据,只把处理后的摘要或结论返回给 Agent。这样即使 Skill 内部处理了 50MB 数据,Agent 上下文中也只有几百 Token 的摘要。

五、实际配置建议

根据工具类型设置差异化阈值,反映各类输出的信息密度:

工具类型 建议阈值 理由
exec(命令执行) 较低(如 4096 Token) 构建日志大量重复,截断损失小
read(文件读取) 较高 + 建议 offset/limit 源码有结构,可分段读取
web(搜索结果) 每条限制摘要长度 搜索引擎已做初步摘要
browser(截图) Base64 或文字描述 视觉信息无法文本截断

启用会话修剪时,对 Anthropic 提供商价值尤其大。修剪能减小提示缓存写入量,缓存 TTL 过期后重新缓存时直接降低成本。非 Anthropic 提供商默认关闭修剪,需手动启用。

常见问题(FAQ)

Q1:截断后的工具结果还能用吗?

能用。截断保留开头部分并标注截断,Agent 可据此决定是否重新查询。

Q2:会话修剪会删除磁盘上的历史吗?

不会。修剪仅在内存中处理,磁盘上的完整历史始终保留。

Q3:如何避免工具频繁返回超大结果?

在工具参数中加 limit/filter 约束,或封装为 Skill 在本地处理。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
命令行手艺人的头像命令行手艺人普通用户

相关推荐

返回顶部