Token预算制:给长期运行Agent装上「油耗表」与「熔断阀」

📅 2026/4/26 ✍️ 小文 📖 约 1 分钟

Agent跑久了账单失控,根因是没有预算概念。给出一套三级预算(会话/任务/租户)、预算传递机制、超限降级策略与成本归因的埋点设计。

失控不是模型贵,是没有刹车

一个数据分析 Agent 上线两周,账单从每天 200 美元涨到 1800 美元。查日志发现:一个用户的提问触发了 47 轮工具调用,每轮都把完整的历史和查询结果重新发给模型,最后一轮上下文已经超过 20 万 token。

模型没变贵,是没有人为这次会话设上限。这篇文章讲怎么把 Token 从”事后账单”变成”事前预算”。

三级预算模型

预算要在三个层级上分别设定,各自解决不同问题:

层级覆盖范围解决什么越限动作
会话预算一次对话单用户暴走优雅终止 + 建议新开会话
任务预算一次工作流/批处理流程设计失控暂停待审批
租户/日预算一个客户或整个产品预算保护与商业模型降级或限流

三者是包含关系,任何一级耗尽都必须停。关键是每个请求要携带”剩余预算”这个上下文。

预算传递:让每层都知道自己还剩多少

不要在各处硬编码 max_tokens。正确做法是让预算随请求传递:

@dataclass
class Budget:
    remaining: int          # 剩余 token
    deadline_ts: float      # 剩余时间
    level: str              # session / task / tenant

def call_model(messages, budget, reserve=800):
    # 预留输出空间,避免"输入塞满导致无法输出"
    usable = budget.remaining - reserve
    trimmed = trim_context(messages, max_tokens=usable)
    resp = llm.invoke(trimmed, max_tokens=reserve)
    budget.remaining -= (resp.usage.total_tokens)
    return resp

两个细节决定成败:

  1. 必须预留输出空间。常见 bug:输入刚好用满预算,模型返回空字符串,重试一次又花一次钱。
  2. 预算要在工具调用之间精确扣减。工具返回的大段文本(比如一次 SQL 查询返回 5000 行)在进入上下文前就要被压缩或摘要,否则它会把剩余预算一次吃光。

超限时怎么办:四种降级而非报错

预算耗尽直接抛异常是最差的设计——用户看到”出错了”,但其实你可以给他一部分价值。

按优先级:

  1. 压缩上下文继续:把早期历史摘要化,重新计算预算,继续执行
  2. 降档模型:剩余预算不足以支撑旗舰模型时,切到小模型完成收尾
  3. 收窄任务范围:“我已完成前 3 项分析,剩余 2 项需要更多资源,是否继续?”
  4. 优雅终止并交付中间结果:给用户已得到的内容 + 明确的继续路径

第 4 条尤其重要。一个跑到一半的报告加上”已完成 A、B,C 待补充”,比一个错误页面有价值得多。

上下文经济学:每轮都在为历史付费

在多轮 Agent 里,成本是超线性增长的,因为每轮都要重发全部历史。三种削减手段:

  • 摘要压缩:超过 N 轮后,把最早 K 轮压成一段摘要(保留结论,丢弃过程)
  • 工具结果瘦身:工具返回的原始 JSON 只保留模型需要的字段,别原样塞回上下文
  • 提示词缓存:把稳定的系统提示和知识块放在前缀,用供应商的缓存计费,通常可省 50–90% 的输入成本

第三点需要配合前缀稳定性设计:不要在系统提示里插入时间戳、随机 ID、会话相关变量,否则缓存永不命中。把动态内容挪到用户消息里。

埋点:让成本可归因

只有能归因,才能优化。每次模型调用至少记录:

{
  "trace_id": "tr_8f2a",
  "tenant": "acme",
  "session_id": "s_9921",
  "step": 12,
  "step_name": "sql_query",
  "model": "gpt-5",
  "input_tokens": 18234,
  "output_tokens": 420,
  "cached_tokens": 16000,
  "cost_usd": 0.031,
  "budget_remaining": 4100
}

然后做三张表,每周看一次:

  • 按步骤分组的 token 分布:通常会发现某一个环节吃掉 60%(常见是”读文件/查数据库”)
  • 单任务成本分布:看 P95 而非平均值。P95 是 5 美元、均值是 0.3 美元,说明有长尾异常
  • 缓存命中率趋势:突然下降通常意味着有人改了系统提示的前缀

三个真实的反模式

  1. 在工具里做 LLM 调用但不记账:工具内部的模型调用容易被漏计,导致预算体系和实际账单对不上。所有模型调用必须走同一个 client 封装。
  2. 无限重试:工具失败后自动重试,每次重试都是一次完整上下文。必须给重试次数设上限(建议 2 次),且重试时收缩上下文。
  3. 预算只按 token 不按时间:一个 Agent 可能 token 很少但跑了 10 分钟。时间预算和 token 预算要同时设。

落地顺序

  1. 第一周:所有模型调用收敛到一个 client,加埋点,先看清账
  2. 第二周:加会话级预算与四条降级策略
  3. 第三周:加任务与租户级预算,接告警
  4. 第四周起:按步骤分布做针对性优化(工具瘦身、摘要、缓存)

先度量再优化。没有归因数据的成本优化,基本等于凭感觉砍功能。

📤 分享到