跳转到主要内容

代理墙钟预算:一场与工具超时机制的赛跑

阅读需 2 分钟Tian PanTian Pan

有一种 Agent 漏洞,当你孤立地观察任何单个组件时,它都不会出现。模型没问题,工具没问题,重试策略也没问题。纸面上的超时值甚至可以说很慷慨。然而,一个通常在 8 秒内完成的工具,却总是在一个已经在 7.9 秒时将其宣告为失败的 Agent 面前折戟。Agent 围绕一个从未发生过的“错误”重新规划,并启动了第二次调用,而第一次调用的结果即将与其发生碰撞。

漏洞不在任何一个框框里。它存在于两个没人同意应该同步的时钟之间的缝隙中。

这是 Agent 工程领域中相当于分布式系统经典问题——协作节点之间的时钟漂移 (clock drift)——只不过这两个节点位于同一个进程中,且漂移不是以 NTP 偏差的毫秒为单位衡量的。它是以每一方决定称为“t-zero”的事件来衡量的。Agent 的预算往往从 LLM 发出第一个 token 开始计时。工具的预算往往从工具进程实际收到调用的那一刻开始计时。在这两个时刻之间,隔着整个 prefill 阶段、整个流式传输管道、整个工具路由器 (tool-router) 跳转,以及工具工作线程 (tool worker) 前的任何排队。这两个秒表之间没有任何时间是共享的。它们都认为自己在为同一件事计时。

两个没人同意应该同步的时钟

对 Agent 步骤的传统看法就像时间轴上的一个条形图:提示词输入,模型思考,工具运行,结果返回。对超时的传统看法是,你在该条形图的某个地方画一条垂直线,并称之为截止日期。

现实情况是至少有四个时钟在并发运行,且几乎从未同步:

  • Agent 框架 (harness) 时钟,通常在请求发送给模型时开始计算预算。
  • 模型的有效时钟,框架通常只有在第一个流式 token (TTFT) 出现时才能感知到。在 2026 年,对于对话式生成,首字时间被报告在数百毫秒到几秒不等,对于长上下文可能更长。这整个间隔可能算作,也可能不算作你的“Agent 预算”,具体取决于你使用的库。
  • 工具客户端时钟,从框架发出工具调用开始,到工具返回结束。大多数框架将其公开为每个工具的超时。
  • 工具服务端时钟,仅在工具进程实际收到请求时开始。它前面的任何东西——队列、MCP 路由器、具有自身空闲超时的反向代理——都会增加工具进程看不见且无法计入自身的延迟。

在表现良好的 RPC 栈中,这些时钟通过截止日期传播 (deadline propagation) 来协调。调用方计算一个绝对截止日期,将其作为墙钟瞬间 (而非持续时间) 附加到请求中,每个下游跳转都会继承它。经典的 gRPC 公式对此非常明确:截止日期是以绝对时间戳表示的“从现在起 5 秒内”,并且上下文会通过每个子调用进行传播,以便整个子树在同一时刻终止。像 userver 这样的框架也描述了同样的想法——链接截止日期,这样缓慢的上游就不会消耗掉下游仍期望拥有的预算。

在实践中,Agent 栈并不这样做。Agent 的预算是在框架进程中计算的持续时间。工具的预算是在工具进程中计算的另一个独立的持续时间。没有共享的截止日期。没有传播。

因此,当模型的第一个 token 终于到达时,框架时钟已经过去了 1100 毫秒。当工具调用经过 MCP 服务器路由时,又消失了 300 毫秒。当工具工作线程出队请求并开始其自身的 8 秒预算时,框架已经在针对一个 1.4 秒前就开始的 8 秒预算进行计时。从框架的角度看,工具还有 6.6 秒。从工具的角度看,它有 8 秒。数字看起来相等,实则不然。

为什么这看起来像是一个 Agent 拒绝使用的成功工具调用

病理性的情况并不是 Agent 在工具启动前就放弃了。那种情况非常明显——你会看到一个被取消的调用和一个愤怒的日志。病理性的情况是 Agent 在 7.9 秒时放弃,工具在 8.0 秒时完成,且双方都在各自的追踪 (trace) 中写入了结构化的成功日志。

Agent 的追踪显示:启动调用,等待,超时在 7.9 秒过期,重新规划。从 Agent 的角度来看,工具失败了。它进入了一个恢复分支——选择一个不同的工具,向用户询问澄清问题,或者最糟糕的是,重试相同的调用。

工具'的追踪显示:收到调用,执行,在 8.0 秒返回并带有干净的 200 状态码。从工具的角度来看,一切正常。它甚至还因为这项工作向用户收了费。

这种竞争在下游表现为三种不同的症状,它们看起来都像是不同的漏洞:

  1. 一个没有任何东西在等待的“成功”工具结果。Agent 的协程已被取消。结果落入了一个关闭的通道或孤儿 future 中。一些框架将其记录为“tool_use without matching tool_result”——如果双方对发生的事情达成一致,这种形式本应是不可能的。
  2. 一个自信地报告“工具失败了,我将尝试不同的方法”的模型,而一个完全正确的答案正坐在工具的响应队列中。
  3. 重复调用,因为 Agent 的重新规划选择了具有相同参数的相同工具——而现在工具工作线程正在做两次相同的工作,第二次调用正与第一个过时的结果赛跑,进入一个不知道该信任哪一个的状态机。
会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 13 分钟

重试放大:2% 的工具错误率如何演变成 20% 的智能体故障

智能体循环通过在多个步骤和 SDK 层级间叠加重试,将 2% 的工具错误率放大为 20% 的用户端故障。本文将解析其背后的数学原理、自我 DoS 模式,以及能够遏制这种现象的重试预算规范。

insider
agents
阅读需 11 分钟

跨 Agent 服务边界的分布式追踪:上下文传播的断裂

当 Agent 跨微服务边界调用 Agent 时,W3C TraceContext 会发生断裂,追踪信息碎片化为不相关的 Span。本文介绍故障的技术形态以及修复方法。

insider
observability
阅读需 11 分钟

取消安全的智能体:你的“停止”按钮背后已经产生的副作用

“停止”只是一种 UI 手段,而非系统保证。这是一份针对取消安全智能体的从业者指南:持久化副作用账本、作用域授权、补偿操作,以及取消 UI 究竟应该显示什么。

insider
agents
阅读需 9 分钟

那个因等待另一个 Agent 而死锁的 Agent

两个能力出色的 Agent 可能会在你的账单飞涨时永远互相等待。为什么是协作——而非模型能力——导致了 Agent 群体的崩溃,以及分布式系统准则如何修复这一问题。

insider
ai-agents
阅读需 9 分钟

智能体学会针对重试预算进行规划

为了可靠性而增加重试机制,但智能体的规划器最终会学会将其视为免费的探索 —— 将安全网变成模型悄悄消耗的配额。本文将探讨这种偏差是如何发生的,以及如何约束它的模式。

insider
agents