上周五收到账单邮件,我差点以为邮件发错了。
日均 1,290。
我做的东西不复杂——一个内部用的 MCP 助手,连了数据库、Jira 和 Slack。每天 500 次调用,上线前我估算了半天,觉得每次大概 5 左右。
实际 8.6 倍。
坐下来翻 Token 日志,花了两个小时才搞清楚为什么。
原来是这么烧的
你们有没有认真想过 MCP Agent 的 Token 是怎么消耗的?
我之前没想过。我以为就是 prompt 进去、回答出来,按长度收费。
实际情况是:每一步调用,你都在重复发之前所有步骤的内容。
第 1 步发出去:System + 用户需求 + 23个工具的 JSON schema
→ 约 2,800 tokens
第 2 步发出去:System + 用户需求 + 23个工具的 JSON schema
+ 第1步的工具调用 + 第1步工具的返回结果
→ 约 5,600 tokens
第 3 步发出去:以上所有 + 第2步的 → 约 9,200 tokens
...
第 8 步发出去:以上所有 → 约 64,400 tokens!
8 步任务,发出的 Token 总量是 64,400 输入 + 2,200 输出。
用 DeepSeek-V3 算:贵,但比那些海外大模型贵的多少?
实测还是得用更强的推理模型来保证工具调用的准确性,单次 $0.2+。
500 次一天,就是这 $100+。
我他妈的工具列表里有 23 个 MCP 工具,每步都全发一遍。23 个工具的 JSON schema 大概 3,500 tokens,8 步 × 重复 = 白白烧了 28,000 tokens。
这笔钱冤枉到离谱。
我是怎么把它压下来的
试了一堆方案,有用的有没用的,挑几个说。
第一个招:Prompt Caching
这是投入产出比最高的改动,没有之一。
System Prompt 和 Tool List 每次调用都是一样的。缓存它们,后续调用读缓存价格是普通输入的 1/10。
改法很简单,加几个字段:
response = client.messages.create(
model="deepseek/deepseek-chat",
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # 就这一行
}
],
tools=[
{**tool, "cache_control": {"type": "ephemeral"}}
for tool in TOOLS # tool list 也缓存
],
messages=history,
max_tokens=1024,
)
改完之后,Step 3 之后的调用缓存命中率接近 94%。
单次费用:0.089。省了 61%。
就这一个改动,月费从 503。
第二个招:工具列表别全发
我什么时候需要 23 个工具?从来没有。每次任务顶多用 3-4 个。
但我图省事,直接把全部工具列表丢进去了,让模型自己选。
结果呢,每步多了 3,500 tokens 的废话。
现在的做法:在调用主模型之前,先用便宜模型做一次任务分类,把相关工具子集挑出来:
TOOL_GROUPS = {
"data_query": ["sql_query", "data_export", "chart_gen"],
"project": ["jira_create", "jira_update", "jira_search"],
"comms": ["slack_send", "email_draft", "cal_check"],
"code": ["gh_search", "gh_pr", "code_review"],
}
def get_tools_for_task(task: str) -> list:
# 用 Qwen-Turbo 分类,几乎不花钱
category = quick_classify(task, model="qwen/qwen-turbo")
return TOOL_GROUPS.get(category, list(ALL_TOOLS.keys()))
分类调用很便宜,但每步省 ~3,000 tokens,8 步省 24,000 tokens。算下来追加节省了 ~11%。
第三个招:分层模型路由
说实话这个我之前有抵触:感觉用便宜模型会让质量变差。
实测发现,约 40% 的步骤根本不需要复杂推理——格式转换、结构化解析、简单判断——Qwen-Turbo 就能搞定。
def pick_model(step_type: str, complexity: float) -> str:
if step_type == "format" or complexity < 0.3:
return "qwen/qwen-turbo" # 便宜,够用
elif complexity < 0.6:
return "deepseek/deepseek-chat" # 中档
else:
return "deepseek/deepseek-r1" # 复杂推理
不同组合实测结果:
| 方案 | 单次均价 | 省了多少 |
|---|---|---|
| 全程 DeepSeek-V3 | $0.089(缓存后) | 基线 |
| 40% Qwen-Turbo + 60% DeepSeek | $0.063 | -29% |
| 混合三档 | $0.052 | -42% |
质量有没有影响?复杂任务没有。简单步骤的小模型出错率高了一点,但加个重试逻辑基本解决了。
第四个招:修剪历史上下文
Agent 跑到第 7、8 步的时候,第 1 步工具返回的原始数据大概率用不到了,但还是全扛着。
改成:保留最近 3 步的完整 tool response,更早的只留摘要:
def prune_context(messages: list, keep_full: int = 3) -> list:
tool_msgs = [m for m in messages if m.get("role") == "tool"]
if len(tool_msgs) <= keep_full:
return messages
old_ids = {m["tool_use_id"] for m in tool_msgs[:-keep_full]}
result = []
for msg in messages:
if msg.get("role") == "tool" and msg.get("tool_use_id") in old_ids:
result.append({**msg, "content": f"[摘要] {summarize(msg['content'])}"})
else:
result.append(msg)
return result
8 步任务输入 Token 从 64,400 降到 39,200,省了 39%。
叠加之后是什么效果
前四个方案叠起来,效果如下:
| 组合 | 单次均价 | vs 原始 |
|---|---|---|
| 啥都没做 | $0.226 | 基线 |
| Prompt Caching | $0.089 | -61% |
| + 工具裁剪 | $0.071 | -69% |
| + 模型路由 | $0.052 | -77% |
| + 上下文修剪 | $0.041 | -82% |
日均费用:21。
月费:630。
有没有感觉之前白花了好多钱……
还有两个进阶玩法(暂时没上,有兴趣的可以试)
语义缓存:把历史请求的 embedding 存下来,新请求相似度 > 0.92 就直接返回缓存结果,整个 LLM 调用都省了。我的场景重复率大约 28%,理论上能再省 23%。还没上生产,但测试环境跑起来挺稳的。
批量 API:非实时任务(比如夜间报告生成)改用 Batches API,价格打五折。这个改动成本低,适合有批处理场景的同学。
一个顺便提的工具
做模型路由的时候,我发现在多个供应商之间切换非常麻烦——每家 API 格式不完全一样,key 要分开管,国内访问有的还需要特殊处理。
后来用了 TheRouter,一个 API Key 统一接入 30+ 模型,接口采用标准 REST 格式,Prompt Caching 的参数也能透传。不用自己维护多套 SDK,路由逻辑写起来简单了很多:
# 用标准 HTTP 客户端就行,接口格式统一
import httpx
def call_model(model: str, messages: list) -> str:
resp = httpx.post(
"https://api.therouter.ai/v1/chat/completions",
headers={"Authorization": "Bearer tr-xxxxxx"},
json={
"model": model, # 换模型只改这一个字符串
"messages": messages,
}
)
return resp.json()["choices"][0]["message"]["content"]
# 按步骤类型选模型
call_model("qwen/qwen-turbo", messages) # 格式解析
call_model("deepseek/deepseek-chat", messages) # 工具调用
call_model("deepseek/deepseek-r1", messages) # 复杂推理
总结一下
MCP Agent 的账单暴涨,根子在三个问题叠加:上下文滚雪球、工具列表冗余、全程旗舰模型。
优先级:
- Prompt Caching——改动最小,效果最大,先上这个
- 工具列表裁剪——有 10+ 个 MCP 工具必须做
- 模型路由——不同步骤用不同模型,便宜的步骤没必要用贵的
- 上下文修剪——任务步骤多(>5 步)再考虑
按顺序来,第 1、2 个就能省掉 60-70%。
希望能帮到同样被账单吓到的同学。欢迎评论区聊聊你们的优化经验。
TheRouter — 一个 Key 搞定 30+ AI 模型,国内直连,标准 API 格式兼容。 官网:therouter.ai