智能体经济学:多 Agent 环境下的 Token 预算分配
What(本文讲什么)
这篇文章讲“token 预算”在多 agent 系统里到底是什么:
- 成本预算: 你愿意为一个任务、一次会话、一个团队付多少钱。
- 容量预算: 长上下文会占用推理侧的 KV cache 与显存资源,直接影响并发与延迟。
- 行为预算: 预算决定 agent 是否允许深思、允许重试、允许调用高风险工具。
我们要做的不是“省 token”,而是把 token 从不可控的消耗,变成可执行的工程机制。
Problem(要解决的工程问题)
多 agent 系统的 token 失控通常来自三种形态:
- 重试风暴: 一个错误反复重试,反而把故障放大成账单事故。
- 规则漂移: 每轮 prompt 前缀都在变化,缓存命中率低,延迟与成本都不可控。
- 无观测: 你只知道“贵”,不知道贵在哪一段(规则?上下文?检索?输出?反复失败?)。
Principle(预算是一套“可执行约束”)
1) 三层预算:全局、任务、回合
- 全局硬限(Global Quota): 团队/月度的支出红线。
- 任务预算(Task Budget): 一个任务生命周期允许消耗的上限(可按美元,也可按 tokens)。
- 回合上限(Turn Cap): 单次调用的最大输入/输出 token,防止“幻觉长文”或无意义展开。
这三层的区别在于作用域不同,但执行方式一致:都需要在 runtime 层可被强制执行,而不是写在文档里。
2) 记账点必须落在“副作用边界”
工具调用产生副作用,所以每个 tool call 都是记账点:
- 记录输入 token 与输出 token(以及缓存命中信息)。
- 记录这次调用属于哪个任务、哪个 agent、哪个 step。
- 记录失败原因与是否重试。
这样你才能回答: “是模型贵,还是我的流程在重复做无效工作”。
Usage(落地:账户系统 + 降级策略 + 缓存策略)
1) 一个最小可用的 Token 账户系统
下面的示例刻意只做一件事:把预算变成可熔断的机制。价格只是占位,工程上应以你实际的模型价格表为准。
class BudgetExceededError(RuntimeError):
pass
class TokenAccountant:
"""
Token 记账器:
记录每次调用的 token 消耗,并在超预算时熔断。
"""
def __init__(self, *, task_limit_usd: float, input_usd_per_m: float, output_usd_per_m: float):
self._limit = task_limit_usd
self._spent = 0.0
self._in_price = input_usd_per_m / 1_000_000
self._out_price = output_usd_per_m / 1_000_000
def record(self, *, input_tokens: int, output_tokens: int) -> float:
cost = (input_tokens * self._in_price) + (output_tokens * self._out_price)
self._spent += cost
if self._spent > self._limit:
raise BudgetExceededError(f"task over budget: spent={self._spent:.6f} limit={self._limit:.6f}")
return cost
@property
def spent(self) -> float:
return self._spent
你应该把这个熔断“接到控制回路上”,而不是让异常冒泡变成 500:
- 超预算: 切换到小模型、缩短上下文、减少检索、或要求人工介入。
- 连续失败: 降低动作强度(只读诊断,不再执行写操作)。
- 高风险工具: 在预算接近上限时,禁止触发副作用工具。
2) 模型路由:把“思考强度”也做成预算的一部分
不是每个步骤都需要旗舰模型。你可以把步骤分类,再路由到不同层级:
- L1: 规则/脚本(正则、静态分析、确定性转换)。
- L2: 小模型(摘要、分类、提取结构化字段、初步诊断)。
- L3: 大模型(架构权衡、复杂推理、跨文件修改计划)。
路由不是为了炫技,而是为了让预算“可预测”。这要求你能观测每个步骤的真实消耗与成功率。
3) Prompt Caching:让稳定前缀变成真实收益
缓存不是魔法,它要求“前缀稳定”。两条一手文档给了最重要的工程事实:
-
OpenAI 的 Prompt Caching 会缓存最长公共前缀,并从 1024 tokens 起按固定粒度增长。你的规则/工具定义如果稳定复用,就可能减少成本与延迟。
参考: -
Anthropic 的 Prompt Caching 通过对 prompt 前缀设置缓存断点,后续相同前缀可以命中缓存;文档也明确了 TTL 与缓存读取的计量方式。
参考:
注意: 缓存收益取决于你的系统形态。你需要用日志来验证命中率,而不是凭感觉下结论。
4) 并发与容量:token 预算也影响推理服务吞吐
在推理服务侧,长上下文会显著占用 KV cache,从而降低并发。已有研究提出按 token 预算做路由和分池,以减少长上下文对整体吞吐的拖累。
参考: https://arxiv.org/abs/2604.08075
这类观点对 agent 系统的启发是:预算不仅是钱,还是“系统容量调度”。
Pitfall(常见坑与防错)
- 只设上限不设策略: 超预算后做什么?切模型还是停止?必须写清楚并实现。
- 只统计总量不统计分布: 你需要知道“哪类步骤”最费钱,才能优化。
- 不记录缓存命中: 你无法验证“稳定前缀”是否真的带来收益。
- 没有失败原因标签: 预算优化最终会变成盲目压缩上下文,反而降低成功率。
Debug(如何把预算系统调到可用)
- 先做账本: 每次调用记录 task_id/agent_id/step_id/input/output/cache_hit/retry_reason。
- 再做熔断: 超预算必须可控地停止或降级,而不是直接崩溃。
- 最后做优化: 先提高命中率(稳定前缀),再减少无效重试,再做路由与压缩。
Source(资料来源)
- OpenAI Prompt Caching: https://platform.openai.com/docs/guides/prompt-caching
- OpenAI Prompt Caching 说明: https://openai.com/index/api-prompt-caching/
- Anthropic Prompt Caching: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
- Token-Budget Routing(分池与路由观点): https://arxiv.org/abs/2604.08075
一张表把“预算策略”写清楚
预算不是一个数字,而是一组在不同状态下触发的策略。下面给一个可直接搬进设计文档/实现的策略表。
| 场景 | 触发条件 | 允许的动作 | 禁止的动作 | 退出条件 |
|---|---|---|---|---|
| 正常 | spent < 60% 且连续失败次数低 |
正常路由与检索 | 无 | N/A |
| 节流 | spent >= 60% 或 cache miss 上升 |
稳定前缀、减少 retrieval、压缩 summary | 大段展开输出 | 命中率回升或任务收敛 |
| 降级 | spent >= 85% 或连续失败 |
路由小模型、只读诊断、让人确认下一步 | 高风险写操作、外网抓取 | 人类介入或成功收敛 |
| 熔断 | spent > 100% |
停止、写审计、保存状态 | 任何副作用 | 重新授权预算 |
真正的价值是“禁止列表”。多 agent 系统里,最贵的事故通常不是一次调用贵,而是“预算快没了还在做高风险副作用”。
可观测性(不做这一步,预算永远调不对)
你至少要记录这些字段,且能按 task/agent/step 维度聚合:
- token:
input_tokens,output_tokens。 - cache:
cache_read_tokens(或等价字段),cache_create_tokens(或等价字段)。 - latency: 模型调用延迟与工具调用延迟。
- retries: 每个 step 的失败次数与失败原因标签。
- context: prompt 的主要块占比(rules/workspace/recent/retrieval/summary)。
这些字段不是为了“看起来专业”,而是为了回答两个最重要的问题:
- 我该先省哪一块?(规则?检索?输出?)
- 我该先修哪一类失败?(解析失败?权限拒绝?超时?网络失败?)
补充: 为什么“稳定前缀”既省钱又省事故
稳定前缀的收益有两面:
- 缓存: 规则/工具定义复用更容易命中 prompt caching。
- 行为: agent 行为更稳定,减少“上一轮能过、下一轮忽然不守规矩”的漂移。
如果你的规则写得很长,且每轮都在变,那么你不仅贵,而且会更不稳定。工程上通常需要把“变化”放到可控的上下文块里,把“规则与工具契约”固定下来。
典型成本杀手(优先修这些,收益最大)
- 无意义重试: 同一个错误原因不变,重试只是在烧 token。
- 输出膨胀: 模型在解释和“自我说服”上输出很长,但对下一步动作没有帮助。
- 检索堆叠: RAG 返回十几段相似片段,既贵又污染注意力。
- 规则重复: 每个 agent 都带一份变体规则,导致缓存命中率低,行为还不一致。
- 失败不归因: 没有失败原因标签,你无法把优化聚焦到真正的瓶颈上。
这份列表的意义是把优化顺序固定下来:先止血(重试与输出膨胀),再做结构优化(稳定前缀与检索去噪),最后才是高级路由与分池策略。
小结(把“预算”变成系统能力)
做到这一步,你的预算系统应该具备三个性质:
- 可解释: 你能说清楚钱花在哪个 task/agent/step 上,以及为什么花。
- 可执行: 你能在超预算时确定性地降级或停止,而不是随机崩溃。
- 可迭代: 你能通过缓存命中、失败归因、重试分布来决定下一步优化点。
当预算变成系统能力后,多 agent 才有资格谈规模化。否则“更多 agent”只会把随机性放大成账单事故与生产事故。
下一步阅读
预算系统落地后,你会自然遇到两个问题:
- 发生事故时我如何快速定位与复盘(tracing / shadow mode / 审计日志)。
- 我如何在不中断业务的情况下做灰度与回放(可观测性驱动的迭代)。
这些会在 15-可观测性与运维调试 模块里展开。