Agent成本优化:降低API支出的实用指南
Agent成本优化:降低API支出的实用指南
你的Agent运行良好,每天处理200个请求,用户反馈也很满意。然后你查看API账单:本月$3,400。深入分析数据后,你发现Agent平均每个请求发起12次API调用,每次调用包含4,000个token的系统提示词。仅系统提示词每天就消耗960万个输入token。按每百万token $3计算,仅重复内容每月就要花费$864。
成本是生产环境Agent部署被缩减或彻底终止的首要原因。优化并非过早之举——而是生存之道。好消息是:大多数Agent部署通过一些直接的改动就能降低50–80%的成本,无需重新设计整个系统。
本指南将介绍七种按投资回报率(ROI)排序的降本策略。从第一条开始,逐步推进,直到达到预算目标。每个部分都包含具体数字,让你在编写一行代码之前就能估算出节省的费用。
1. Token计费分析:了解资金流向
无法衡量的东西无从优化。在做任何改动之前,先全面梳理Agent工作流中每一分钱的去向。
输入Token构成
每次调用Claude的API,输入端都包含以下几类token:
- 系统提示词 — 指令、角色设定、约束条件。通常为1,000–5,000个token,且每次调用都会重复发送。
- 工具定义 — Agent可调用的每个工具的JSON schema。10个工具轻松消耗2,000–3,000个token。
- 对话历史 — 会话中的所有历史消息,随每个步骤不断增长。
- 工具调用结果 — 上一次工具调用的输出,重新注入上下文中。可能非常庞大(完整网页、数据库查询结果等)。
输出Token构成
输出token的费用是输入token的3–5倍,是关键的优化目标:
- Agent推理过程 — 内部思维链(尤其是启用扩展思考时)。
- 工具调用生成 — 工具调用的JSON内容。
- 最终响应 — 面向用户的答案。
单任务成本计算
单个Agent任务的总成本计算公式:
总成本 = Σ (input_tokens × input_price + output_tokens × output_price) (对任务中每次API调用求和)成本构成示例
以下是使用Claude Sonnet(输入$3/百万token,输出$15/百万token)执行一个10步研究型Agent任务的实际成本分解:
| 步骤 | 组件 | 输入Token | 输出Token | 输入成本 | 输出成本 | 合计 |
|---|---|---|---|---|---|---|
| 1 | 初始规划 | 4,200 | 800 | $0.0126 | $0.0120 | $0.025 |
| 2 | 网络搜索调用 | 4,800 | 200 | $0.0144 | $0.0030 | $0.017 |
| 3 | 处理搜索结果 | 8,500 | 600 | $0.0255 | $0.0090 | $0.035 |
| 4 | 深度阅读(页面1) | 12,000 | 500 | $0.0360 | $0.0075 | $0.044 |
| 5 | 深度阅读(页面2) | 15,200 | 500 | $0.0456 | $0.0075 | $0.053 |
| 6 | 追加搜索 | 16,800 | 200 | $0.0504 | $0.0030 | $0.053 |
| 7 | 处理结果 | 20,100 | 600 | $0.0603 | $0.0090 | $0.069 |
| 8 | 综合分析 | 22,500 | 1,200 | $0.0675 | $0.0180 | $0.086 |
| 9 | 核实验证 | 24,000 | 400 | $0.0720 | $0.0060 | $0.078 |
| 10 | 最终响应 | 25,500 | 1,500 | $0.0765 | $0.0225 | $0.099 |
| 合计 | 153,600 | 6,500 | $0.461 | $0.098 | $0.558 |
注意输入成本占主导地位,且随着对话历史的积累,每个步骤的成本都在增长。第8–10步仅占总步骤数的30%,却贡献了47%的总成本。
成本追踪代码
从第一天起就实现一个追踪封装器:
import anthropicimport timefrom dataclasses import dataclass, fieldfrom typing import Optional
@dataclassclass CostRecord: step: int model: str input_tokens: int output_tokens: int cache_read_tokens: int = 0 cache_creation_tokens: int = 0 input_cost: float = 0.0 output_cost: float = 0.0 total_cost: float = 0.0 duration_ms: float = 0.0
# Pricing per million tokens (as of early 2026)MODEL_PRICING = { "claude-haiku": {"input": 0.25, "output": 1.25, "cache_read": 0.025, "cache_write": 0.30}, "claude-sonnet": {"input": 3.00, "output": 15.00, "cache_read": 0.30, "cache_write": 3.75}, "claude-opus": {"input": 15.00, "output": 75.00, "cache_read": 1.50, "cache_write": 18.75},}
@dataclassclass TaskCostTracker: task_id: str budget_limit: Optional[float] = None records: list = field(default_factory=list) total_cost: float = 0.0
def record_call(self, step: int, model: str, usage) -> CostRecord: pricing = MODEL_PRICING.get(model, MODEL_PRICING["claude-sonnet"])
input_cost = (usage.input_tokens / 1_000_000) * pricing["input"] output_cost = (usage.output_tokens / 1_000_000) * pricing["output"] cache_read_cost = (getattr(usage, 'cache_read_input_tokens', 0) / 1_000_000) * pricing["cache_read"] cache_write_cost = (getattr(usage, 'cache_creation_input_tokens', 0) / 1_000_000) * pricing["cache_write"]
total = input_cost + output_cost + cache_read_cost + cache_write_cost
record = CostRecord( step=step, model=model, input_tokens=usage.input_tokens, output_tokens=usage.output_tokens, cache_read_tokens=getattr(usage, 'cache_read_input_tokens', 0), cache_creation_tokens=getattr(usage, 'cache_creation_input_tokens', 0), input_cost=input_cost + cache_read_cost + cache_write_cost, output_cost=output_cost, total_cost=total, )
self.records.append(record) self.total_cost += total
print(f" Step {step} [{model}]: {usage.input_tokens} in / {usage.output_tokens} out = ${total:.4f} (cumulative: ${self.total_cost:.4f})")
if self.budget_limit and self.total_cost > self.budget_limit: raise BudgetExceededError( f"Task {self.task_id} exceeded budget: ${self.total_cost:.4f} > ${self.budget_limit:.4f}" )
return record
def summary(self) -> dict: return { "task_id": self.task_id, "total_steps": len(self.records), "total_input_tokens": sum(r.input_tokens for r in self.records), "total_output_tokens": sum(r.output_tokens for r in self.records), "total_cost": self.total_cost, "cost_by_model": self._cost_by_model(), }
def _cost_by_model(self) -> dict: by_model = {} for r in self.records: if r.model not in by_model: by_model[r.model] = {"calls": 0, "cost": 0.0} by_model[r.model]["calls"] += 1 by_model[r.model]["cost"] += r.total_cost return by_model
class BudgetExceededError(Exception): pass即使还没开始优化,也要从今天起就记录数据。你需要基准数字来衡量改进效果。
2. 按任务选择模型
这是你手中最大的成本调节杠杆。并非每个Agent步骤都需要最强大的模型。
模型层级与定价
以Claude系列为参考:
| 模型 | 输入(每百万token) | 输出(每百万token) | 适用场景 |
|---|---|---|---|
| Haiku | $0.25 | $1.25 | 分类、路由、提取、简单格式化 |
| Sonnet | $3.00 | $15.00 | 复杂工具调用、研究、综合分析、通用场景 |
| Opus | $15.00 | $75.00 | 复杂规划、细致推理、高风险决策 |
Haiku的输入和输出成本均比Sonnet便宜12倍,Sonnet全面比Opus便宜5倍。
各角色模型推荐
将每个Agent角色匹配到满足质量要求的最低成本模型:
- 路由/分类Agent → Haiku。“这是计费问题还是技术问题?“不需要Sonnet。对于定义明确的类别,Haiku的分类准确率超过95%。
- 数据提取Agent → Haiku。从文本中提取结构化字段、解析日期、识别实体——Haiku在这方面表现出色。
- 工作Agent(工具调用) → Sonnet。复杂的多步骤工具编排、研究综合和细致的响应生成都能受益于Sonnet的能力。
- **编排/规划Agent