MCP Agent 日耗百元?实测 6 种 Token 成本压降方案:最高省 83%
本文分析了MCP构建AI Agent时Token成本暴涨5-10倍的原因,指出多步任务中上下文滚雪球式增长是核心问题。基于真实数据,作者测试了6种优化方案:提示词缓存(节省61%)、按需裁剪工具列表(节省11%)、分层模型路由(节省46%)、上下文修剪(节省39%)、语义缓存(节省23%)和批处理合并(节省50%)。最佳组合方案可将任务费用降至原来的17%。这些优化措施针对性地解决了MCP Age
用 MCP 构建 AI Agent 后,Token 账单往往比预期高出 5-10 倍。本文基于真实生产数据,拆解成本暴涨的根本原因,并实测 6 种优化方案的实际效果——最极端的组合可以把相同任务的费用压到原来的 17%。
问题从一张账单说起
上个月,一个内部工具的 LLM 账单让我愣了一下。
原型阶段估算:日均 500 次任务 × $0.01/次 = 日均 $5,月均 $150。
实际账单:日均 $43,月均 $1290。差了 8.6 倍。
工具本身不复杂——一个用 MCP 连接了数据库、Jira、Slack 的内部助手,每次任务平均走 6-8 个工具调用。定睛一看 Token 明细才发现问题所在。
MCP 的 Token 成本结构
传统单次问答的 Token 消耗很直观:prompt 长度 + 回答长度。但 MCP Agent 的结构完全不同。
一次典型的多步 MCP 任务,上下文是滚雪球式增长的:
每一步都会把之前所有步骤的内容完整重发一遍——这不是 Bug,是多轮对话模型的工作方式。
假设每步工具调用返回约 1,500 tokens 的数据,完成一个 8 步任务的总输入 Token 数约为:
| 步骤 | 本步输入 Token | 累计输入 Token |
|---|---|---|
| Step 1 | 2,800 | 2,800 |
| Step 2 | 4,300 | 7,100 |
| Step 3 | 5,800 | 12,900 |
| Step 4 | 7,300 | 20,200 |
| Step 5 | 8,800 | 29,000 |
| Step 6 | 10,300 | 39,300 |
| Step 7 | 11,800 | 51,100 |
| Step 8 | 13,300 | 64,400 |
| 合计 | — | ~64,400 输入 + ~2,200 输出 |
用 Claude Sonnet 4 的价格($3/$15 per 1M token)算:64,400 × $3/1M + 2,200 × $15/1M = $0.226/次。
500 次/天 = $113/天,和我账单对上了。
6 种优化方案实测
我在同一批 200 个测试任务上,逐一验证了以下方案。基线:Claude Sonnet 4,无任何优化,平均费用 $0.226/次。
方案一:Prompt Caching(提示词缓存)
最直接的方案。System Prompt 和 Tool List 在每步调用里都是重复的,缓存这部分可以大幅削减输入费用。
Claude Sonnet 4 缓存写入 $3.75/1M,缓存读取 $0.30/1M,读写比大约 1:20 就能回本。
实测结果:
from anthropic import Anthropic
client = Anthropic()
# 把 system prompt 和 tool definitions 标记为 cache_control
response = client.messages.create(
model="claude-sonnet-4-5",
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # 缓存 system
}
],
tools=[
{
**tool_def,
"cache_control": {"type": "ephemeral"} # 缓存 tool 定义
}
for tool_def in TOOL_DEFINITIONS
],
messages=conversation_history,
max_tokens=1024,
)
# 检查缓存命中情况
usage = response.usage
print(f"缓存读取: {usage.cache_read_input_tokens} tokens")
print(f"缓存写入: {usage.cache_creation_input_tokens} tokens")
print(f"普通输入: {usage.input_tokens} tokens")
| 指标 | 无缓存 | 启用缓存 |
|---|---|---|
| 平均单次费用 | $0.226 | $0.089 |
| 节省比例 | — | 61% |
| Step 3+ 缓存命中率 | — | ~94% |
方案二:工具列表按需裁剪
我的 MCP 配置里连了 23 个工具,但处理不同类型任务时实际用到的工具从不超过 4 个。把完整 Tool List 每次都塞进 context 是个巨大的浪费——23 个工具的 JSON schema 大约占 3,500 tokens。
解法:在 Agent Orchestrator 层做任务类型识别,只传入相关工具子集。
TOOL_GROUPS = {
"data_query": ["sql_query", "data_export", "chart_generate"],
"project_mgmt": ["jira_create", "jira_update", "jira_comment", "jira_search"],
"communication": ["slack_send", "email_compose", "calendar_check"],
"code": ["github_search", "github_pr", "code_review", "run_test"],
}
def select_tools(task_description: str) -> list[dict]:
"""用小模型快速分类任务,返回对应工具子集"""
category = classify_task(task_description) # 便宜的分类调用
tool_names = TOOL_GROUPS.get(category, list(ALL_TOOLS.keys()))
return [ALL_TOOLS[name] for name in tool_names]
| 指标 | 全量 23 工具 | 按需 3-4 工具 |
|---|---|---|
| Tool List Token 占用 | ~3,500 | ~500 |
| 每步节省 | — | ~3,000 tokens |
| 8 步任务节省 | — | ~24,000 tokens |
| 费用节省 | — | 约 11%(叠加缓存后效果更明显) |
方案三:分层模型路由
不是每一步都需要最聪明的模型。工具调用结果解析、格式转换、简单判断——这些完全可以用便宜模型完成;只有需要复杂推理的步骤才动用旗舰模型。
def route_model(step_type: str, context_complexity: float) -> str:
"""
step_type: "tool_call" | "reasoning" | "synthesis" | "format"
context_complexity: 0-1 评分,基于上下文长度和任务难度
"""
if step_type == "format" or context_complexity < 0.3:
return "deepseek/deepseek-chat" # $0.27/$1.10 per 1M
elif step_type == "tool_call" and context_complexity < 0.6:
return "anthropic/claude-haiku-4" # $0.80/$4.00 per 1M
else:
return "anthropic/claude-sonnet-4" # $3/$15 per 1M
实测在 8 步任务里,约 40% 的步骤可以降级到便宜模型而不影响质量:
| 模型分配 | 平均单次费用 | vs 全用 Sonnet |
|---|---|---|
| 全用 Claude Sonnet 4 | $0.226 | 基线 |
| 40% Haiku + 60% Sonnet | $0.158 | -30% |
| 40% DeepSeek + 20% Haiku + 40% Sonnet | $0.121 | -46% |
方案四:上下文窗口修剪
Agent 运行多步之后,早期工具返回的原始数据往往已经不需要了。把原始 Tool Response 替换成摘要,可以有效抑制滚雪球。
实现上,在每次调用前扫描 message history,把超过最近 3 轮的 tool_result 消息替换成 [已摘要] 数据查询完成,关键字段:xxx 这样的精简版本。LLM 仍能理解任务脉络,但不再携带完整的原始 JSON 响应。
| 步骤数 | 无修剪平均输入 | 修剪后平均输入 | 节省 |
|---|---|---|---|
| 4 步 | 20,200 | 17,800 | 12% |
| 6 步 | 39,300 | 28,500 | 27% |
| 8 步 | 64,400 | 39,200 | 39% |
方案五:语义缓存(相似请求复用)
同一个系统里,相似任务反复执行是很常见的场景。比如每天早上查昨天的销售数据——prompt 不完全一样,但意图相同,结果也高度相似。
语义缓存在 embedding 空间里匹配历史请求,命中时直接返回缓存结果,整个 LLM 调用都省掉了。
逻辑不复杂:请求进来时先用一个轻量 embedding 模型算相似度,阈值设 0.92,命中就直接返回上次结果,整个 LLM 调用都省了。Redis 或 Chroma 都可以做存储后端。
实测在我们的使用场景(重复率约 28%):
| 指标 | 值 |
|---|---|
| 缓存命中率 | 23% |
| 命中任务费用 | $0(跳过 LLM) |
| 整体费用节省 | 约 23% |
方案六:批处理合并
如果你的 Agent 有大量独立的短任务,逐一发出请求的开销远高于批量处理。Anthropic 的 Message Batches API 可以在非实时场景下按 50% 折扣批量处理。
调用方式和普通 API 相同,只是把请求打包成数组一次性提交,最长 24 小时内返回结果,价格打五折。适合夜间批处理场景,无代码改造成本。
适合:报告生成、数据分析、内容审核等非交互式任务。
方案叠加效果对比
六种方案可以叠加使用。实测不同组合在相同任务集上的总费用:
| 方案组合 | 平均单次费用 | vs 基线 |
|---|---|---|
| 无优化(基线) | $0.226 | — |
| 仅 Prompt Caching | $0.089 | -61% |
| Caching + 工具裁剪 | $0.071 | -69% |
| Caching + 裁剪 + 模型路由 | $0.052 | -77% |
| 以上三项 + 上下文修剪 | $0.041 | -82% |
| 全部六项(含语义缓存+批处理) | $0.038 | -83% |
最优组合把日均费用从 $113 降到了 $19,月费从 $3,390 降到 $570。
适用场景选择建议
| 你的场景 | 优先用这几种方案 |
|---|---|
| 同一 Agent 重复调用(System Prompt 固定) | Prompt Caching(ROI 最高) |
| 工具集大(>10 个 MCP Server) | 工具列表裁剪 |
| 不同步骤难度差异大 | 分层模型路由 |
| 任务步骤多(>5 步) | 上下文修剪 |
| 有大量相似请求 | 语义缓存 |
| 非实时批量任务 | Message Batches API |
多模型路由的工程实现
方案三(分层模型路由)在多模型间动态切换,如果每家供应商 SDK 单独维护,维护成本不低。实践中用统一路由网关更省心——一个 API Key,同一套代码接口,按需换模型名即可:
from openai import OpenAI
client = OpenAI(base_url="https://api.therouter.ai/v1", api_key="tr-xxxx")
for step in agent_steps:
model = route_model(step.type, step.complexity)
response = client.chat.completions.create(
model=model, # "deepseek/deepseek-r1" / "qwen/qwen-turbo" / 其他 30+ 模型
messages=step.messages,
)
国内直连,支持 Prompt Caching 参数透传,不用配多个密钥或处理各家的 API 差异。
小结
MCP Agent 的 Token 成本问题本质上是上下文滚雪球 + 工具列表冗余 + 全程旗舰模型三个问题叠加的结果。六种方案里,Prompt Caching 是 ROI 最高的单项优化,一行配置省 61%;组合使用可以把成本压到原来的 17%。
成本优化没有银弹,但也不复杂。挑 2-3 个最契合你场景的方案,按优先级落地,通常 1 个月就能把账单降到可接受的范围。
TheRouter — 一个 API Key,统一访问 30+ 主流 AI 模型,支持 Prompt Caching 透传,国内直连。
官网:https://therouter.ai
更多推荐

所有评论(0)