传统的做法是,将所有可用的工具定义一次性加载到模型的上下文中。比如,一个连接了Google Drive和Salesforce的Agent,其上下文会包含类似这样的工具定义:
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,
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当Agent需要连接数千个工具时,仅仅是这些定义就可能消耗数十万Token,模型还没开始工作,成本就已经产生
第二,中间结果消耗
更致命的是,工作流中的每一个中间结果都必须经过模型的上下文。
设想一个任务:“从Google Drive下载我的会议纪要,并将其附加到Salesforce的潜在客户记录中。”
模型的处理流程是这样的:
1.第一次工具调用:gdrive.getDocument(documentId: "abc123")
2.结果返回:返回完整的会议纪要文本,例如“讨论了Q4目标...\n[完整纪要文本]”,并将其全部加载进模型上下文
3.第二次工具调用:salesforce.updateRecord(...),在其data字段中,模型需要再次写入完整的会议纪要文本
传统MCP客户端工作流
这意味着,一份长达2小时会议、可能包含5万Token的纪要,在整个流程中被模型处理了两次。如果文档更大,甚至可能直接超出上下文窗口的限制,导致任务失败
面对上述挑战,Anthropic提出的新范式是:将MCP服务器呈现为代码API,而不是直接的工具调用接口
Agent的任务不再是选择工具并填充参数,而是编写一小段代码来完成整个工作流。
具体实现上,系统可以将所有可用的工具生成一个文件树结构,例如用TypeScript实现:
servers/
├── google-drive/
│ ├── getDocument.ts
│ └── ... (other tools)
├── salesforce/
│ ├── updateRecord.ts
│ └── ... (other tools)
...每个工具文件(如getDocument.ts)内部封装了对MCP工具的实际调用。
现在,对于前面提到的“会议纪要”任务,Agent生成的不再是工具调用指令,而是这样一段代码:
// 从Google Docs读取纪要并添加到Salesforce
import*asgdrivefrom'./servers/google-drive';
import*assalesforcefrom'./servers/salesforce';
consttranscript = (awaitgdrive.getDocument({documentId:'abc123'})).content;
awaitsalesforce.updateRecord({
objectType:'SalesMeeting',
recordId:'00Q5f000001abcXYZ',
data: {Notes: transcript }
});变化是颠覆性的:
按需加载:Agent可以通过浏览文件系统(例如ls ./servers/)来发现可用的服务,然后只读取它完成当前任务所需的文件(getDocument.ts和updateRecord.ts)来理解接口。这避免了开局就加载所有工具定义
数据本地流转:getDocument返回的transcript内容被存储在一个代码变量中,直接传递给updateRecord函数。整个纪要文本从未进入模型的上下文窗口
结果就是文章开头提到的惊人数据:Token消耗从15万骤降至2000,效率提升98.7%。Anthropic还提到,Cloudflare也独立发现了类似的模式,并称之为“代码模式”(Code Mode)。这证明了其核心洞察的普适性:LLM天生擅长编写代码,我们应当利用这一优势
这种新范式不仅节省了Token,还带来了一系列深刻的优势,重塑了Agent的能力边界。
1. 渐进式披露
模型无需预知一切。它们可以像人类程序员一样,通过探索文件系统或使用一个search_tools工具来按需发现和学习工具的用法。
2. 上下文高效的工具结果
在处理海量数据时,Agent可以在代码执行环境中进行过滤、转换和聚合,只将最终的、小规模的结果返回给模型。例如,处理一个包含10000行数据的电子表格:
// 传统方式:返回10000行数据到上下文
TOOLCALL: gdrive.getSheet(sheetId:'abc123')
// 代码执行方式:在环境中过滤,只返回摘要
constallRows =awaitgdrive.getSheet({sheetId:'abc123'});
constpendingOrders = allRows.filter(row=>row["Status"] ==='pending');
console.log(`发现${pendingOrders.length}个待处理订单`);
console.log(pendingOrders.slice(0,5));// 只记录前5个供模型审查Agent最终看到的可能只是5行样本数据,而不是全部10000行。
3. 更强大的控制流
循环、条件判断、错误处理等复杂的逻辑,现在可以用标准代码模式实现,而不是笨拙地串联多个工具调用。例如,需要轮询Slack等待一条部署完成的消息:
letfound =false;
while(!found) {
constmessages =awaitslack.getChannelHistory({channel:'C123456'});
found = messages.some(m=>m.text.includes('deployment complete'));
if(!found)awaitnewPromise(r=>setTimeout(r,5000));
}
console.log('部署完成通知已收到');这远比“调用工具-休眠-调用工具”的循环更高效,也减少了模型的“首个Token”延迟。
4. 保护隐私的操作
默认情况下,所有中间数据都保留在代码执行环境中。更进一步,执行环境可以自动识别并“令牌化”敏感数据
例如,Agent写的代码是处理row.email和row.phone,但如果它尝试打印这些数据,模型实际看到的会是[EMAIL_1]和[PHONE_1]。而真实数据则在执行环境中安全地从Google Sheets流向Salesforce,全程不经过模型,有效防止了敏感信息泄露
5. 状态持久化与技能
通过文件系统访问,Agent可以将中间结果写入文件,从而实现任务的中断和恢复。
constleads =awaitsalesforce.query(...);
constcsvData = leads.map(l=>...).join('\n');
awaitfs.writeFile('./workspace/leads.csv', csvData);更重要的是,Agent可以将一段成功的代码保存为可复用的函数,也就是一项“技能”(Skill)
// In ./skills/save-sheet-as-csv.ts
exportasyncfunctionsaveSheetAsCsv(sheetId:string) { ... }
// Later, in any agent execution:
import{ saveSheetAsCsv }from'./skills/save-sheet-as-csv';
constcsvPath =awaitsaveSheetAsCsv('abc123');通过不断积累这样的技能,Agent可以构建起一个强大的、可复用的高级能力工具箱
Anthropic认为,尽管上下文管理、工具组合、状态持久化这些问题在AI Agent领域显得很新颖,但它们在传统软件工程中都有成熟的解决方案。
代码执行范式,正是将这些经过时间检验的工程模式应用于AI Agent,让Agent以其最擅长的方式——编写代码——来更高效地与世界互动
当然,这也带来了新的挑战:运行Agent生成的代码需要一个安全的沙箱环境、资源限制和监控机制。但这是一种权衡,其换来的是Token成本的大幅降低、延迟的缩短以及工具组合能力的极大提升
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |