Agent 系统的成本工程:Token 消耗不是运营成本,是架构信号
大多数团队发现成本问题的时机都太晚。
Demo 阶段,一次 Agent 运行消耗几分钱,团队觉得 LLM API 成本完全可控。到了生产,加上完整的系统提示、跨会话记忆检索、多轮工具调用之后,同一个任务的成本变成了几块钱。用户量稍微上来,账单就开始让人难受。
这不是”规模化的代价”,这是架构问题——只是在生产环境里才暴露出来。
Token 消耗的结构
解决成本问题之前,先把钱花在哪里说清楚。
一次典型的 Agent API 调用,input token 的组成大概是这样的:
1 | |
Output token 只是这张图里很小的一部分。
大多数团队的直觉是优化 output——让模型说话更简洁。这个方向不错,但收益有限。真正的成本杠杆在 input 侧,尤其是前三行:系统提示、工具定义、对话历史。这三项的共同特点是:它们在大多数实现里是完全静态或线性增长的,没有任何压缩逻辑。
一个工具定义写得粗糙,每个工具的描述塞了 500 个 token,装了 20 个工具,工具定义就要 10,000 token。每次调用都带着这 10,000 token,这是一个固定的架构税,不是用量问题。
让成本失控的三个模式
模式一:System Prompt 膨胀。
系统提示是最容易膨胀的地方。需求来一个加一段,没有人负责裁剪,最终系统提示变成了几千 token 的巨型文档。更隐蔽的是,很多内容已经不再适用,但没有人敢删——不知道删了会不会出问题。
这是技术债的一种形式,只是债主是 token budget,而不是代码维护成本。
模式二:对话历史无压缩。
把完整的对话历史传给每次 API 调用,是最常见的实现方式,也是最容易失控的。一个任务执行了 30 步,历史就有 30 轮工具调用记录,每轮可能带着几百到几千 token 的工具输出。这些历史对 Agent 当前的推理真的全部有用吗?很可能不是。
更常见的情况是:Agent 只需要知道当前目标、最近几步的结果、以及几个关键的历史决策。大量的中间执行细节在当前阶段对推理没有任何贡献,但全部留在 context 里。
模式三:用最强的模型做所有任务。
Orchestrator Agent 需要做复杂的任务分解,用 Opus 级别的模型是合理的。但如果 Executor Agent 做的是格式化输出、简单分类、或者 JSON 提取这类高度结构化的任务,用同样的模型就是资源错配。
成本差异可以是 10 倍到 30 倍。任务复杂度差异可能只有 2 倍。
四条优化路径
1. Prompt Caching
这是 input 侧成本最直接的优化手段,也是被低估最严重的一个。
现代 LLM API 支持对 prompt 的前缀部分进行缓存。系统提示、工具定义、少样本示例——这些在单次会话内甚至跨会话都保持不变的内容,可以被缓存,后续请求命中缓存后,这部分的 token 处理成本大幅降低(通常是 80%-90%)。
要让缓存有效,有几个工程要求:
- 缓存内容必须放在 prompt 的前部,紧跟着才是动态内容。大多数 API 的缓存是前缀匹配,动态内容插在缓存内容之前会导致缓存失效。
- 静态内容要真的稳定。系统提示里如果混了动态信息(当前时间、用户名、会话 ID),每次都不同,缓存永远命中不了。把动态信息单独放在 user turn,而不是 system prompt。
- 工具定义的顺序要固定。工具列表的顺序变化会导致缓存失效,即使内容没有变。
一个实际数字:一个系统提示 2,000 token + 工具定义 8,000 token 的 Agent,每次调用命中缓存后,这 10,000 token 的处理成本几乎可以忽略不计。如果这个 Agent 一天执行 10,000 次任务,仅此一项优化的成本节省就非常可观。
2. 模型路由(Model Routing)
按任务复杂度选择模型,而不是统一用同一个模型。
实现模型路由的第一步是对任务做分类。哪些任务需要深度推理(任务分解、策略判断、代码生成),哪些任务是结构化处理(格式转换、信息提取、分类标注)。这个分类不需要做得很精细,粗粒度的分类就足够驱动路由决策。
1 | |
路由逻辑本身不应该由 LLM 来做——那样又引入了一次 LLM 调用的成本。用规则或者简单的分类器来路由,把 LLM 的调用留给真正需要 LLM 的任务。
3. Context 主动压缩
对话历史不应该被动增长。Harness 层需要主动管理 context 窗口。
几个实用策略:
滚动窗口 + 摘要:只保留最近 N 轮的完整历史,更早的历史用一段结构化摘要替代。摘要包含已完成的目标、关键决策、当前状态——这是 Agent 继续推理所需要的,而不是完整的执行记录。
工具输出截断:很多工具返回的数据远比 Agent 实际需要的多。一个 API 返回了 500 行 JSON,Agent 可能只需要其中 3 个字段。在工具层做截断和提取,而不是把原始输出全部传给模型。
区分”记录”和”推理材料”:执行历史的完整记录应该写入外部存储(可观测性系统),context 里只保留推理所需的摘要。这两件事不要混在一起。
4. 批处理(Batching)
不是所有 Agent 任务都需要实时执行。
如果有一批相似的任务(处理 100 个 PR 的 release note、对 1000 条 issue 做分类),可以用批处理 API,通常有更低的成本和更高的吞吐。代价是延迟——批处理结果可能在几小时后才返回。
对延迟不敏感的后台任务(定时的代码扫描、定期的文档更新、离线的数据处理),批处理是应该默认选择的模式。
把成本变成可观测的信号
优化不能是一次性的手工操作。成本需要变成系统里的可观测指标。
每个任务的 token 成本需要被记录和追踪,按任务类型分组,建立基线。当某类任务的平均成本突然上升 30%,这不只是成本问题——它通常意味着 Agent 在某个场景下开始绕路:多了几轮工具调用,context 膨胀,或者模型在反复重试某个失败的操作。
成本退步和质量退步往往同时发生,因为根本原因一样:Agent 行为异常。把 per-task token 消耗加入 CI 的回归检测,是一个低成本但高价值的质量信号。
追踪的粒度建议到工具调用级别:哪个工具消耗的 token 最多?这个工具的输出有没有压缩空间?工具调用次数有没有异常增多?这些问题的答案往往指向具体的优化点,而不是模糊的”成本太高”。
成本工程不是省钱,是架构可持续性
最后说一个容易被误解的地方。
成本优化不是让团队去抠每一个 token。它的真正意义是:确保系统的成本结构和它提供的价值成比例,让 Agent 系统可以持续运行和迭代,而不是因为账单压力被迫降低功能或者下线。
一个设计良好的 Agent 系统,成本应该随着有价值的任务量线性增长,而不是随着执行步骤数或者历史长度爆炸式增长。如果成本曲线的斜率远高于价值曲线的斜率,说明架构上有浪费,需要找到并修复。
ROI 的计算框架很简单:cost per task vs value per task。前者是工程可以测量的,后者是产品需要定义的。两个数字都清楚,成本优化的优先级就不是一个主观判断,而是一个工程决策。
Token 消耗是一个症状,不是病因。
贵的地方往往就是低效的地方——context 设计冗余、工具输出没有过滤、模型选择没有匹配任务复杂度。把成本当作架构审计的入口,而不是运营部门要控制的开销,往往能找到更根本的改进机会。