用 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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐