核算单位错了
大多数 LLM 系统的成本仪表盘都围绕一个问题构建:这次调用花了多少钱?这个问题可回答、精确,但对 Agent 几乎毫无用处。一次 Agent 运行不是一次调用,而是一条轨迹:一个计划、一系列工具调用、一次或多次反思、参数格式错误后的重试,以及最终的合成。调用是原子,轨迹是分子。为原子定价、为分子做预算,正是团队最终拿到一张无法从日志中解释的月度账单的原因。
本文主张采用轨迹级成本控制:对 token、墙钟时间和工具调用设置运行级上限,在模型之外强制执行,并在运行开始前定义好分级降级与升级策略。这套机制并不光鲜,但它决定了一个 Agent 系统是可运营的,还是只能被演示。
为什么按调用定价会误导
单次调用的成本是有界的。轨迹则不然,因为调用次数是任务、模型自身决策和环境响应的函数。有三个因素占主导,而它们都不会出现在按调用计费的账本里。
重试。 一次工具调用如果未通过 schema 校验、超时或返回含糊的错误,就会被重试。每次重试都会重新发送上下文。在一条轨迹中单调增长的上下文意味着第 n 次重试比第一次更贵,而不是一样贵。即使尝试次数稳定,每次尝试的成本也在上升。
工具扇出。 一个规划步骤可以决定查询一个来源,也可以查询十二个。模型不知道你的单位经济模型,因此没有理由偏好某一个。扇出正是表现良好的中位数案例与病态尾部案例相差一个数量级的地方,而且这种差异是结构性的,不是偶然的。
反思。 自我批判、验证和重新规划循环是大多数 Agent 设计中杠杆最高的质量机制,也是方差最高的成本机制。一次没发现任何问题的反思仍然要付出完整的上下文成本。一次发现了问题的反思则要付出这些,再加上修复和重新验证。
The consequence is a heavy-tailed cost distribution. The median run is cheap and the mean is not. Teams that budget from the median are systematically surprised; teams that budget from the mean over-provision and still get surprised by the tail. The tail is not noise. It is a specific class of task that the agent handles by looping, and it will recur.
还有一个外部维度:随着 Agent 框架和编排模式激增,已部署系统的成本面变化速度超过任何静态的按调用估算所能追踪的范围。一个有用的纪律是:把轨迹——而不是调用——当作被定价、被监控、被设限的对象。

预算原语
运行预算是一组附加到轨迹标识符上的上限,由编排器强制执行,而不是由模型执行。在模型之外强制执行很重要:要求模型保持在预算内,是在做一个软承诺,而软承诺恰恰会在尾部失效。编排器可以计数,而预算需要的正是计数。
上限
三个上限覆盖了大多数失败模式:
- Token 上限。 整条轨迹的输入加输出 token 总量,而不是每次调用。在编排器边界计数,这样重试和反思在结构上就被包含在内。
- 墙钟时间上限。 轨迹的截止时间。这能抓住每步便宜但步数无界的循环,也是唯一能在提供商侧延迟事故中存活的限制。
- 工具调用上限。 对外部调用数量的限制,可选地按工具分别设置。这是对扇出的直接控制,也是最容易推理的。
一个实用的实现会维护一个每次运行的账本,每个步骤在派发之前先写入该账本:
class RunLedger:
def __init__(self, limits):
self.limits = limits
self.tokens = 0
self.tools = 0
self.started = now()
def admit(self, est_tokens, tool_name=None):
if self.tokens + est_tokens > self.limits.tokens:
raise BudgetExceeded("tokens")
if tool_name and self.tools + 1 > self.limits.tools:
raise BudgetExceeded("tools")
if now() - self.started > self.limits.wall_clock:
raise BudgetExceeded("wall_clock")
self.tokens += est_tokens
if tool_name:
self.tools += 1
传给 admit 的估算值刻意保守:它是该步骤的预计成本,再加上一次重试的预留。正是这个预留让上限变得有意义,而不是空想。
分级降级
在上

- 缩减工具集。 去掉可选或高延迟的工具,保留任务没有它们就无法完成的工具。
- 去掉反思。 跳过自我批判和验证,接受较低的答案质量,并记录该运行已被降级。
- 失败关闭。 返回一个附带部分轨迹的结构化失败,而不是无界地尝试成功。
这阶梯要在事前定义,因为耗尽的那一刻是设计回退方案最糟糕的时机。一次降级运行如果返回可用的部分答案和清晰的标记,在运营上远好于一次要么悄悄超预算、要么一无所获地终止的运行。
归因
只有当支出被归因时,预算才具有诊断价值。为每次运行打上任务类别、相关的租户或用户标识符,以及产生它的入口点。然后按标签聚合支出,而不是按模型。有用的视图是每个任务类别的分布:中位数、尾部,以及两者之间的比率。一个任务类别的尾部比其中位数高一个数量级,就说明存在循环病态,而标签会告诉你该去哪里查。
运营后果
成本异常就是循环病态。 当一个任务类别的成本突然上升时,本能反应是归咎于 prompt 效率低下。通常 prompt 没变,变的是循环:某个工具开始返回触发重试的错误,某个规划器开始更宽地扇出,某个反思不再收敛。异常调查应从轨迹形态开始,而不是 prompt 文本。每次运行的步数和工具调用分布会比任何 prompt diff 更快地定位病态。
预算让评估变得可比。 没有运行级的成本约束,两种 Agent 设计之间的基准比较就不是比较。更贵的设计可以仅仅靠尝试更久而赢得成功率。当预算固定在相同的上限时,同等成功率下更便宜的轨迹才是真实结果,而同等成功率下更贵的轨迹则是回归。这就是整个方法的诚实权衡:硬预算偶尔会切断一次如果给更多空间本会成功的运行。这个代价是刻意付出的,换来的是一个最坏情况行为在部署前就已知、而不是在账单上才被发现的系统。无法接受任何截断的团队应该把上限设高,并使用降级而不是失败,但他们仍然应该设置上限。
截止时间是契约的一部分。 墙钟时间上限不仅是成本控制,也是对等待 Agent 的任何东西的延迟保证。超过它的运行应该以部分结果失败关闭,因为已经放弃的调用方不会从迟到的成功中获益。
先构建什么
最小可行版本很小。在编排器上放一个账本,按轨迹统计 token、工具调用和经过时间,强制执行三个上限,并给每次运行打上任务类别标签。在添加第二种降级模式之前,先添加一种。对分布做埋点,而不是对平均值。按调用的视图可以保留作为调试辅助,但它不应是预算所依附的对象。把预算附加到运行上,运行就变成了你可以运营的东西。
封面照片:Miguel Á. Padriñán / Pexels。
