你用 Codex 写过一个稍复杂的任务吗?配置数据库、查 API、处理 CSV、发通知。如果你装了五个 MCP 服务器,每个暴露十几个工具,模型还没开始干活,光读懂这些工具的描述就烧掉了上万 Token。更要命的是,它还得在几十个相似的工具名之间反复推敲,“query_database” 和 “db_query” 到底是不是同一个东西?

这是当前 Agent 架构里一个被严重低估的问题:工具箱越膨胀,模型越糊涂

工具箱悖论:工具越多,效率越低#

Agent 的工具生态正在经历一场军备竞赛。一个 MCP 服务器提供 12 个工具,下一个提供 30 个。加上 CLI、沙箱、数据库、CRM、浏览器,模型要花大量上下文窗口来学习接口,而不是完成任务。

有人做过估算:40 个常驻工具的 schema 描述,光 JSON 定义就能吃下约 12,000 Token。这还没算工具之间的语义重叠带来的认知负担。模型需要推断 search_repofind_repository 是不是同一个东西,create_issue 的参数格式跟 open_ticket 有什么不同。

第一代 Agent 确实需要显式的工具,那时候模型还不够可靠,上下文窗口也小,每个操作都需要紧耦合的护栏。但现在已经不一样了。前沿模型在一件事上练得极其出色:写代码。

代码模式:给模型一个它本来就会的界面#

“代码模式”(Code Mode)的核心思想很简单:不要给模型一堆它需要重新学习的工具接口,给它一个类型安全的执行环境,让它写代码。

具体怎么做?你不再定义 tool_create_databasetool_query_databasetool_insert_record 三个独立工具。你暴露一个 Database 类型,它有 create()query()insert() 方法。模型自己写代码调用它们。

这看起来只是换了一种调用方式,但实际影响是质变的:

并行不再是奢望。 三个工具调用需要三次模型往返,但一段代码可以同时发起三个请求,在 JavaScript 里就是三行 await Promise.all([...])

中间结果不用全塞进上下文。 一个工具调用返回 20MB 的 API 响应,如果直接塞给模型,Token 账单立刻爆炸。但一段代码可以先过滤出关键的 10 行,把剩下的写到磁盘,只把精华返回给模型。

控制流回到代码手里。 分支、循环、异常处理、重试,这些是编程语言的基本功。但在工具调用模式里,每一个分支都需要模型重新决策一次。代码模式让所有这些逻辑在 TypeScript(或 Python)里确定性执行,模型只需要做一次决策:写什么程序。

一个工具调用是一次决策,一段代码是一棵决策树#

这是我读 Browserbase 原文时最被打动的一句话:“An individual tool call is a decision, a program is a decision tree.”

工具调用是离散的、原子化的。每一步都要等模型预测下一步。10 步工作流意味着 10 次模型往返,每次都要携带完整的上下文。

代码模式把工作流压缩成一次执行。模型写一段程序,这段程序包含了所有步骤和它们之间的逻辑关系。如果第三步失败了,程序里的 try/catch 会在本地处理重试,不需要再把错误信息传回模型重新推理。

更深一层的好处是复用。一个成功的工具调用序列只是一段对话历史,下次还得重来。但一段成功的程序是一个资产,它可以被保存为脚本、测试、甚至技能。下一次执行直接从工作代码开始,不需要从对话记录里重建工作流。

Browserbase 举了自己的内部 Agent “BB” 的例子。这个 Agent 横跨工程、客服、销售、市场、运维,能调查浏览器会话、查询内部数据、读文件、记录需求、写代码。他们本可以给它一个巨大的扁平工具目录,但它核心只有一个能力:在类型化服务接口上执行 JS 代码。剩下的都交给模型自己写。

浏览器 Agent 是代码模式的天然主场#

Browserbase 做浏览器 Agent 起家,所以他们对这一点感受最深。

每一个网页已经是一个编程接口。DOM 描述了结构,浏览器 API 暴露了状态,JavaScript 可以查询元素、读取应用数据、触发事件、调用页面函数。你根本不需要为"提取搜索结果"、“填写表单”、“点击弹窗"定义独立工具,让模型写一段 JavaScript,在页面上下文里执行就行了。

当然也有边界。页面内的 JS 控制不了浏览器 chrome、绕不过跨域限制、处理不了下载弹窗和原生权限提示。这些仍然需要专门的工具原语。但对于绝大多数页面交互来说,最通用的浏览器工具就是 JavaScript 本身。

安全性:把护栏放在代码下面,不是上面#

如果你看到这里在想"让 AI 执行任意代码,安全怎么办”,这个担心完全合理。

代码执行的爆破半径确实比单个工具调用大得多。“放在沙箱里跑"是必要的,但远远不够。沙箱限制的是代码能碰什么系统资源,但它不会自动阻止 Agent 泄露凭证、调用破坏性 API、把数据发到未经批准的域名,或者在部分失败后重复执行副作用。

Browserbase 的方案是把安全边界放在生成代码的下面,而不是上面:

  • 凭证代理:沙箱拿到的永远是短期引用,不是原始生产密钥
  • 策略层:一个独立的策略层决定这次运行可以调用哪些服务方法,生成的代码不能给自己授权
  • 网络管控:域名白名单和请求拦截限制数据流向
  • 不可逆操作:付款、权限变更、生产部署需要更窄的工具和显式确认
  • 审计日志:保存程序、输入、输出、网络行为和策略决策,每次运行都可追溯

核心原则:让模型在盒子里想写什么写什么,然后让盒子在物理上不可能做它没被授权的事。

从推理到确定性:省下每轮重复烧的 Token#

代码模式还有一个容易被忽略的经济优势。

一个稳定工作流的推理成本不应该每次都是全新的。一旦 Agent 发现了一个能跑的流程,把这个流程保存为代码,之后确定性执行就好。只有当环境变了、程序坏了,再把模型叫回来修复。

这个逻辑放在日常工作中再自然不过:你不会每次发工资都重新发明工资计算逻辑,你写一次脚本然后每月跑。Agent 的工作流也应该如此。

工具调用模式天然倾向于"每次都重新思考”,因为它把每次执行都当作全新的对话。代码模式倾向于"发现规律、固化流程",因为它输出的是可保存的程序。

工具和代码不是非此即彼#

最后要说清楚:代码模式不是要消灭所有工具。

高风险的写操作,付款、权限变更、生产部署,仍然应该走窄工具 + 显式确认的路径。工具适合做"合同":一个定义清晰的、不可逆的、需要人类点头的操作。

代码模式适合做"编排":把一堆读操作和转换操作串起来,过滤、聚合、格式化,然后把结果交给模型或用户。

好的 Agent 架构应该两者都用:少量高风险的专用工具加上一个通用的代码执行面。工具的目录可以继续膨胀,但模型面对的表面不应该膨胀。那些工具应该放在执行层的下面,对模型来说只是一组类型定义。


工具生态的发展方向已经很清楚了:SDK、MCP 服务器、CLI 会继续提供传输、发现、认证、可观测性和人类访问能力。但对强大的 Agent 来说,越来越多的接口会下沉到一个可编程执行层之下。Agent 看到的是类型和代码,运行时看到的是权限和策略,服务看到的是一个正常的认证请求。

每一代新模型都在写更好的代码,调试更长的工作流,更有效地利用执行反馈。我们应该停止把这些能力塞进几百个细碎陌生的 tool schema 里。

给模型一个它本来就会的语言,给语言一个安全运行的地方,然后把严格的护栏放在这两者下面。


参考来源:Code mode is all you need: Why agents writing code > calling tools — Browserbase Blog, July 22, 2026