最近,在开发 AI Agent 的过程中,我越来越感受到工具集成带来的挑战。AI Agent 通常需要连接到多个外部系统,比如 Google Drive 或 Salesforce,但在传统方式下,每个工具的定义和调用结果都会占用大量上下文窗口空间,导致响应变慢、成本上升。我原本计划通过优化提示来缓解这个问题,但昨天(2025年11月4日)读到 Anthropic 发布的博客后,发现了一个更根本的解决思路:让 AI Agent 通过编写代码来调用工具,而不是直接进行工具调用。
MCP 是由 Anthropic 开发的开放标准,用于连接 AI Agent 与外部系统。传统上,为 AI Agent 对接每一个工具或数据源都需要定制化集成,这不仅导致碎片化,还会带来大量重复工作,难以构建真正可扩展的系统。MCP 则提供了一种通用协议:开发者只需在 AI Agent 中实现一次 MCP,就能接入整个生态的集成。自 2024 年 11 月推出以来,MCP 的采用速度非常快:社区已构建了数千个 MCP 服务器,主流编程语言都有相应的 SDK 支持,整个行业已将其视为连接 AI Agent 到工具和数据的默认标准。如今,开发者可以轻松构建连接数百甚至数千个工具的 AI Agent,这些工具分布在数十个 MCP 服务器上。然而,随着工具数量的增长,将所有工具定义预加载到上下文中,并通过上下文窗口传递中间结果,会显著拖慢 AI Agent 的速度并增加成本。
这篇博客探讨了如何通过代码执行环境,让 AI Agent 更高效地与 MCP 服务器交互,从而处理更多工具,同时使用更少的 token。
工具导致的 token 过度消耗
在大规模使用 MCP 扩展 AI Agent 时,我们通常有两种常见模式,这两种模式会显著增加 AI Agent 的成本和延迟:
工具定义过载上下文窗口
第一种模式是通过工具定义的方式使用。大多数 MCP 客户端会将所有工具定义预先加载到模型上下文中,并使用直接工具调用的语法暴露给模型。例如,一个 Google Drive 工具的定义可能如下:
gdrive.getDocument
Description: Retrieves a document from Google Drive
Parameters:
documentId (required, string): The ID of the document to retrieve
fields (optional, string): Specific fields to return
Returns: Document object with title, body content, metadata, permissions, etc.
另一个 Salesforce 工具定义如下:
salesforce.updateRecord
Description: Updates a record in Salesforce
Parameters:
objectType (required, string): Type of Salesforce object (Lead, Contact, Account, etc.)
recordId (required, string): The ID of the record to update
data (required, object): Fields to update with their new values
Returns: Updated record object with confirmation
尽管这种定义方式可以让大模型轻松理解和使用各种工具,大幅提升扩展能力,但这些描述本身会占用上下文窗口的空间,延长响应时间并提高成本。如果 AI Agent 连接了数千个工具,在处理请求之前就需要处理数十万 token 的定义信息。
中间工具结果消耗额外 token
第二种模式是在执行过程中获取 MCP 工具的结果。大多数 MCP 客户端允许模型直接调用 MCP 工具。例如,你可能让 AI Agent “从 Google Drive 下载会议记录,并附加到 Salesforce 的潜在客户中”。模型会进行类似这样的调用:
TOOL CALL: gdrive.getDocument(documentId: "abc123")
→ returns "Discussed Q4 goals...\n[full transcript text]"
(loaded into model context)
TOOL CALL: salesforce.updateRecord(
objectType: "SalesMeeting",
recordId: "00Q5f000001abcXYZ",
data: { "Notes": "Discussed Q4 goals...\n[full transcript text written out]" }
)
(model needs to write entire transcript into context again)
// What the agent would see, if it logged the sheet.rows:
[
{ salesforceId: '00Q...', email: '[EMAIL_1]', phone: '[PHONE_1]', name: '[NAME_1]' },
{ salesforceId: '00Q...', email: '[EMAIL_2]', phone: '[PHONE_2]', name: '[NAME_2]' },
...
]
然后,在另一个 MCP 工具调用中,通过 MCP 客户端查找解标记化。真实的电子邮件、电话和姓名从 Google Sheets 流动到 Salesforce,但从未经过模型。这防止了 AI Agent 意外日志或处理敏感数据。你也可以用此方式定义确定性安全规则,选择数据流动的来源和去向。
状态持久化和技能
代码执行结合文件系统访问,让 AI Agent 在操作之间维护状态。AI Agent 可以将中间结果写入文件,实现恢复工作和跟踪进度:
const leads = await salesforce.query({
query: 'SELECT Id, Email FROM Lead LIMIT 1000'
});
const csvData = leads.map(l => `${l.Id},${l.Email}`).join('\n');
await fs.writeFile('./workspace/leads.csv', csvData);
// Later execution picks up where it left off
const saved = await fs.readFile('./workspace/leads.csv', 'utf-8');
AI Agent 还能将自己的代码持久化为可复用函数。一旦为任务开发出有效代码,它可以保存以备后用:
// In ./skills/save-sheet-as-csv.ts
import * as gdrive from './servers/google-drive';
export async function saveSheetAsCsv(sheetId: string) {
const data = await gdrive.getSheet({ sheetId });
const csv = data.map(row => row.join(',')).join('\n');
await fs.writeFile(`./workspace/sheet-${sheetId}.csv`, csv);
return `./workspace/sheet-${sheetId}.csv`;
}
// Later, in any agent execution:
import { saveSheetAsCsv } from './skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');
这与 “Skills” 概念紧密相关:Skills 是模型用于提升特定任务性能的可复用指令、脚本和资源文件夹。将 SKILL.md 文件添加到这些保存函数中,即可创建结构化技能,供模型引用使用。随着时间的推移,这让 AI Agent 能够构建一个高级能力工具箱,逐步演进其所需支架。