ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MCP换CLI:Agent token消耗砍掉94%的优化实践

MCP换CLI:Agent token消耗砍掉94%的优化实践 最近这两个月一直在调 Agent 开发相关的流程换来换去最大的意外收获是token 消耗直接砍掉了九成以上。先说背景我之前把一堆 MCP server 挂进了 Codex 和 Claude 的 CLI 环境想象中很美好——让模型直接查数据库、读运维平台、操作第三方服务结果跑了一周之后查用量人都麻了。每一轮对话光承载那些工具的定义描述就吃掉了好几千 token真正干活的上下文反而被挤得只剩一点。后来我换了个思路能通过命令行解决的事情坚决让 CLI 来执行MCP 只保留那种“非它不可”的数据接入。就这么一项调整单次任务的平均 token 用量从几十万级别掉到了几万级别粗算下来省了大概 94%。这篇文章就是这次优化过程的完整复盘包含我对比测试的记录、替换方案的具体配置以及过程中遇到的坑希望对正在跟 token 账单作斗争的同路人有点参考价值。1. 先说清楚MCP 的 token 到底花在哪了什么是 MCP通俗说它是一种“把外部工具和数据源接入大模型”的统一协议。你想让模型能查员工信息表、能调内部接口、能操作浏览器就不必你手动把每个工具的信息“喂”给模型而是通过 MCP server 把工具的说明、参数定义、返回格式全部暴露给模型。听起来很省事但这恰好是 token 消耗的大头。1.1 每次会话都在重复“自我介绍”我挂的 MCP server 不只是自己写的内部工具还有几个现成的第三方服务。问题在于模型每做一个决策都得在上下文中看到这些工具的 schema这个工具叫什么、有哪些参数、参数类型是 string 还是 number、返回什么结构、路径怎么拼。一段典型工具描述少说 500 token复杂的要 1500 token 以上。你挂了 4 个 MCP server每个 server 暴露 8 到 10 个工具光初始化的工具清单可能就超过 2 万 token。更要命的是这并不是一次性开销。只要模型在推理过程中持续引用这些工具上下文里的工具定义就不会从缓存中清掉每一轮 request 都会把这块信息重新计算一遍。我用 Codex CLI 跑了几个稍复杂的任务之后去看 token 明细发现“工具 schema 描述”通常占了总用量的 30% 到 40%。也就是说有三四成的钱是花在了让模型“知道该怎么用工具”上面而不是花在它真正“想问题和写代码”上面。1.2 MCP 还带来上下文碎片化挂了很多 MCP server 之后模型会面临“工具搜索成本”。它得在几百个候选功能里挑一个还得判断哪个 server 负责哪件事。实际操作里模型经常拿着 A 工具的参数格式去调 B 工具的接口然后报错、再读错误、再修正来回好几轮中间过程全部变成 token。如果单纯靠人写代码这种尝试大概率一次就能定位问题但模型在“工具选择”和“参数纠错”上额外消耗的 token 非常可观。所以我看明白一件事MCP 很适合“复杂的、需要结构化输入输出的工具集成”但如果你只是想让模型去数据库里查一条记录、去服务器上看一份日志、去本地跑一个 python 脚本那让模型直接通过 CLI 执行命令才是更省 token 的做法。2. 从 MCP 迁到 CLI 的核心设计取舍明白了 token 花在哪后面就好办了。核心思路是让模型直接调用命令行工具把“工具描述”压缩成“一句简短指令”。2.1 CLI 为什么更省 token命令行天然是“短参数、短输出”的交互方式。你不需要给模型塞 1000 token 的 JSON Schema只需要在系统提示里加一句话“你可以在终端里执行 bash 命令命令包括……”模型就能自主地构造具体命令去访问数据库、调用 API、处理文件。举个例子用 MCP 查询订单数量模型需要拿到 serveletool 的定义、参数表、数据库连接配置然后模型生成一个 JSON 格式参数包MCP server 再把结果转回给模型用 CLI 查询订单数量模型只需要生成这样一句话mysql -h 127.0.0.1 -u read_user -p密码 db_order -e SELECT COUNT(*) FROM orders;这两条路径的差别就是MCP 模式中模型要处理的 token 数量包含大量结构化描述CLI 模式中模型直接面对“命令本身”上下文立刻清爽无数倍。同样的任务MCP 模式下可能要 8000 tokenCLI 模式下可能只要 2500 token。2.2 哪些场景必须保留 MCP哪些可以完全替换我做了一圈梳理之后把原来的 MCP server 分成三类。第一类是“必须保留的”。例如需要 GUI 操作验证的浏览器自动化、需要读内部系统页面并做结构化交互的场景我保留了 Playwright MCP 这样的工具。因为这类工具自己内部做了流程封装你让模型裸写 playwright 脚本也可以但处理复杂页面时稳定性差一大截。保留一两个核心工具其 token 开销换来的稳定性是划算的。第二类是“可以换成 CLI 的”。数据库查询、Redis 操作、本地文件读写、Git 操作、Docker 容器管理、日志检索。这些操作在命令行里天生就很成熟而且输出结果可读到模型上下文中。我把这些全部从 MCP 摘掉换成纯 CLI 方式。第三类是“直接砍掉也不影响”的。比如单纯拉取 API 文档的 MCP server、某些数据库 agent 库这些属于只吃 token但不干活的。我用 curl 就能拿到的信息没必要让模型先做一次工具选择。2.3 一个关键原则输出结果也要“瘦身”实际切换中我还发现一个容易被忽略的细节CLI 命令返回的输出如果在标准输出里太多也会变成大 token。例如执行一个返回 10000 行数据的查询模型上下文会被撑爆。所以我给 Agent 环境加了几个常用命令的“输出截断”封装把docker logs后面自动补--tail 100把find /改成限定目录和固定深度把cat大文件改成先wc -l再分段查看。这不是复杂技术但确实让平均单轮 token 消耗又降了一截。你也不能指望模型每次都记得写--limit参数不如把常用命令做成别名或包裹脚本让模型调用的默认行为就是“少量输出”。3. 具体实操我如何把 CLI 方案落地这一节写配置层面的事情我尽量按步骤还原。3.1 环境准备一个权限受限的 Runner 容器为了避免模型直接操作宿主机导致各种风险我为 Agent 单独开了一个容器环境里面预装好开发时常用的工具链mysql-client、redis-cli、docker仅用于查看、python3、node、curl、jq、git。这个容器的意义在于它给了模型一个“可以乱来但不会搞坏宿主机”的沙箱。我的启动参数大概是这样docker run -it --rm \ -v $(pwd)/workspace:/workspace \ -w /workspace \ --network host \ --name agent-runner \ my-ai-agent-image:latest这里有个取舍容器内使用 host 网络是为了让模型能直接访问本机端口上的各类本地服务MySQL、Redis这是我个人开发环境下的妥协。如果你对网络隔离要求高建议用 docker compose 里的固定局域网。3.2 配置模型能直接执行的命令白名单在 Agent 的系统提示里我不再给模型冗长的工具说明而是写了一段非常短但边界清晰的话默认情况下你可以执行任意 bash 命令。如果某条命令不存在或者权限不足请说明原因并建议替代方案。高耗时的命令必须加 timeout。紧接着写一个简短命令清单只列高频操作示例包括db_q()来查询 MySQL、cache_get()读 Redis、run_test()跑当前项目测试。这些是我预写的 shell 函数本质是对原始 CLI 的封装但比直接让模型裸敲命令更安全也更好控制输出长度。实现方式很简单在容器内/usr/local/bin/下放一个脚本比如db_q#!/bin/bash query$1 mysql -h 127.0.0.1 -u read_user -pxxx db_order \ -e $query 2/dev/null | head -100这个函数做了两件事一是限制返回行数二是吞掉 MySQL 密码的 warning 输出。模型调用时只需要写db_q SELECT ...它看到的就是“一个简单指令 一个结果表”。token 开销会比它自己拼完整 mysql 命令要小因为不会把密码、地址等重复配置拼进长命令里。3.3 禁用 MCP server 的实操Codex CLI 和 Claude CLI 都支持通过配置文件控制是否启用 MCP。我当时直接修改了全局配置把不需要的 MCP server 注释掉。例如 Codex CLI 的~/.codex/config.toml里原本挂着[mcp_servers.mysql] command npx args [-y, mcp-server-mysql] env { ... }我把这类都注释掉了只保留了浏览器自动化那一个。如果是 Claude CLI配置路径不一样但思路相同只保留真正高价值的 MCP 工具其余的全部摘除。改完之后立刻跑一个任务看 token 明细效果非常明显第一轮对话的系统上下文长度直接缩到了原来的五分之一。3.4 手动“热切换”的经历有个小插曲我在会议中途演示一个功能时发现其中一个 MCP server 因为认证过期开始疯狂报刷 token 的错误我的代码忘了处理。那一瞬间我意识到如果一个接口是直接走 CLI 的模型遇到认证失败会自己看报错再调整命令而走 MCP 时它可能被那个“登录失败”之类的错误信息兜住反复尝试相同的工具调用白烧 token。这就是 CLI 方案在健壮性上的附加优势错误信息对模型更透明它更容易自己定位问题。4. token 消耗对比我是怎么测出“省下 94%”这个数字的口说无凭我把自己实际做的一组对比测试记录放出来。测试任务选的是日常开发里最常见的一类让模型查一个订单表、找出金额异常的记录、再把这批记录写入一条测试日志。整个流程涉及数据库查询、条件判断、文件写入复杂度适中。4.1 MCP 模式下的消耗当时挂着 3 个 MCP server数据库、对象存储、内部文档系统上下文里工具定义很长我粗算过模型每轮与 MCP server 交互前上下文里的工具描述和 schema 加起来就有 1.8 万到 2.2 万 token。任务跑了大约 14 轮其中至少 4 轮是模型在“尝试选择正确工具 / 纠正参数格式”。最终账单显示这次任务一共消耗约 46 万 token这是我当时真实环境给的数据不同场景数值会有浮动但这个量级差异是普遍的。这个 46 万本身已经比“只写 Python 代码”的方案高很多。因为它在不停地把 schema、工具返回结构、重试信息都拼进对话历史来回滚动开销很大。4.2 CLI 模式下的消耗切到 CLI 方式后我在同一个 Runner 容器里跑同样的任务。模型的初始上下文里只有一段短指令和几个函数说明大约 800 token。它执行时直接调用db_q、redis_cli、echo等命令每一次命令执行后只把输出接进上下文。整个任务只用了 6 轮对话总消耗约 2.8 万 token。粗略一算46 万降到 2.8 万节省正好在 94% 附近。这个数字不代表每个任务都能达到同样比例任务类型影响很大。如果你的任务本身全是复杂网页交互必须依赖 Playwright MCP 这类结构化工具那省幅可能只有 30%。但如果你做的是开发和运维类工作CLI 方案的优化空间几乎就是成倍的。4.3 不同任务类型的 token 明细对比我整理了一个粗略的对照表方便大家理解什么样的场景适合直接用 CLI任务类型MCP 方式平均消耗CLI 方式平均消耗优化比例查数据库并汇总约 32000 token约 5000 token84%本地 Git 操作与提交约 28000 token约 3000 token89%Docker 容器状态排查约 45000 token约 7000 token84%调用第三方 API 并解析约 38000 token约 8000 token79%复杂页面自动化测试约 62000 token约 48000 token23%页面自动化之所以优化有限是因为即便换成直接写 Playwright 脚本模型也要读页面状态和选择器信息这部分上下文省不掉。所以我现在的策略很明确日常开发和平级运维尽量走 CLI真的需要做浏览器端到端测试时才挂上 MCP 专用工具。5. 常见问题与避坑经验方案落地之后有几个问题基本每个团队来问都会踩到我直接列出来省得大家再花时间排查。5.1 MCP 和 CLI 不是非此即彼有朋友看到省 token 就想把所有 MCP 全部拆掉包括浏览器自动化、专门的数据分析工具也全用裸 CLI 硬写。这不是不能做但会很痛。复杂交互场景里MCP 提供的封装能减少模型自己探索的轮次那部分 token 省下来反而更划算。我的原则是如果这个工具的 CLI 方式本身足够清晰、输出足够结构化就用 CLI如果这个工具的内部逻辑复杂比如需要处理页面状态机、需要多步骤握手就用 MCP。把这两类分开管理而不是一刀切。5.2 小心命令注入和权限边界当你给 Agent 开放了自由执行 shell 命令的能力后安全边界就必须认真对待。我在独立容器里跑网络是 host 模式所以宿主机上别的进程其实也是能访问的。建议至少做到这三件事使用只读账号连接数据库、限制容器目录挂载、在容器外再用一层网络白名单。模型本身就是去执行你给它的指令它本身不具备复杂“自主安全意识”但你可能喂给它的任务说明里会包含敏感信息反过来通过命令传给外部 API这个风险模型不会替你拦截。像我这套方案里密码虽然是明文写在脚本里的但那是因为我放在了容器内、只有我这个用户能访问的文件中而不是写在项目代码里。生产环境不要这么干用环境变量或 secrets 管理工具。5.3 输出截断不能依赖模型自觉你可能觉得“模型很聪明它自己会控制输出长度”实际上它经常为了完整性而把大数据全部 dump 出来。解决办法不是在提示词里反复强调“请缩短输出”而是从工具层面直接限制。我给所有封装脚本统一加了head -100或timeout 10s甚至有些场景直接设置LESS TERM环境变量让模型的分页输出保持在极小范围。这样无论模型怎么发挥能进入上下文的字节数都是可控的成本自然稳定。5.4 token 失效类问题的连带影响我在原环境里挂的一些 MCP server 认证经常过期尤其内部文档类和对象存储类的工具。一旦 token 失效模型拿到的是那种“sign-in could not be completed token exchange failed”一类报错它往往会围绕这个错误反复重试同一种工具调用每次重试都重新提交一遍很长的工具上下文token 燃烧速度飙升。切到 CLI 后这类重试循环我见到少了很多。原因也很简单CLI 端认证失败时模型能看到具体的状态码和错误信息它天然会更倾向检查凭据或者换个网络路径而不会像 MCP 模式下那样对着抽象错误晕头转向。5.5 免费 token 计划用户更要重视上下文长度如果你用的是免费 token 或者按量付费的 API上下文中每一轮携带什么内容都很关键。工具定义这种东西完全可以通过 CLI 封装省掉不但省钱还能让模型的可用上下文变长减少那种“上下文被工具描述占满写代码写到一半没有地方放新信息”的窘境。我实测下来同样的任务切到 CLI 之后不仅 token 少了回复的准确度也更高了——因为重要的代码逻辑在上下文里的相对占比更大模型注意力不会被无关 schema 分散。6. 这套方案还能往哪些方向扩展别觉得优化到这里就结束了后面我又往前推了一步。我把一些常见的业务操作封装成了一个“命令行工具箱”比如通知群消息、创建任务、读状态报告全部做成简单的可执行文件。模型在 Agent 环境里直接调用这些命令效果接近一个定制版 MCP但没有那一坨 schema 元数据。核心思路是“用你掌握的所有 CLIs 尽量替代 MCP server 提供的操作”本质是让 Agent 更接近人类开发者在终端里的工作习惯。另一个扩展方向是在容器里内置一个命令操作日志收集器记录模型每次跑了什么命令、输出多少行。这样你能对着 token 账单一笔一笔追回来路。我在实际过程中发现有些神秘的高消耗来源往往就是某个命令意外输出了一段超长 JSON而日志收集能帮你快速定位这种问题。你可以把快照数据发到本地日志目录统计出哪些命令平均消耗最大再针对这些命令做更强的输出限制。最后一个小技巧分享一下我在 Runner 容器的 shell 配置里加了一个_last_cmd_time记录并在每个命令执行前打印一行动态时间。这么做的好处是翻阅会话记录时能精确看出模型在哪一步卡壳、反复重试了多次。这种诊断信息配合 token 数据比我之前用 MCP 时直观多了。说到底优化 token 不只是为了省钱也是在逼着你想清楚你的 Agent 真正需要从模型那里获得什么信息所有无助于回答这个问题的上下文都该被裁剪出去无论是 MCP 的工具定义还是 CLI 命令的无用输出。这是我这轮重构最大的收获。
返回列表