基于MCP的代码执行:构建更高效率的智能体

本文是A社2025年11月4日博客文章《Code execution with MCP: Building more efficient agents》的学习总结。

引言

直接工具调用会为每个定义和结果消耗上下文。而智能体(Agents)通过编写代码来调用工具,可实现更好的扩展。以下将介绍其在MCP中的工作原理。

模型上下文协议(Model Context Protocol,简称 MCP)是一种用于将 AI 智能体连接到外部系统的开放式标准。传统方式下,将智能体与工具、数据连接时,每一组配对都需要单独定制集成方案,这会导致系统碎片化且存在大量重复工作,难以实现真正可扩展的互联系统。而 MCP 提供了一种通用协议 ------ 开发者只需在智能体中实现一次MCP,就能解锁一整套集成生态。

自 2024 年 11 月 MCP 推出以来,其采用率增长迅速:社区已搭建了数千台 MCP 服务器,所有主流编程语言均已支持对应的SDK(软件开发工具包),并且 MCP 已成为行业内将智能体与工具、数据连接的事实标准(de-facto standard)。

如今,开发者构建的智能体通常能访问数十台 MCP 服务器上的数百甚至数千个工具。然而,随着所连接工具数量的增加,提前加载所有工具定义以及通过上下文窗口传递中间结果这两个操作,会导致智能体运行速度变慢,同时增加成本。

在本篇博客中,我们将探讨如何通过代码执行让智能体更高效地与 MCP 服务器交互 ------ 在支持更多工具的同时,消耗更少的 tokens(令牌,指 AI 模型处理文本的基本单位)。

一、 工具导致的token过度消耗降低智能体效率

随着模型上下文协议(MCP)使用规模的扩大,出现了两种常见情况,可能导致智能体的成本增加、延迟升高:

  • 工具定义使上下文窗口(context window)负载过高;
  • 工具中间结果消耗额外的令牌。

1. 工具定义使上下文窗口负载过高

大多数 MCP 客户端会将所有工具定义提前直接加载到上下文中,并通过直接工具调用语法将这些定义提供给模型。这些工具定义可能如下所示:

text 复制代码
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.
text 复制代码
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

工具描述占用过多上下文窗口空间,导致响应时间延长、成本增加。当智能体(Agent)连接了数千个工具时,其在读取请求之前,就需要处理数十万个token。

2. 工具中间结果消耗额外token

大多数模型上下文协议(MCP)客户端允许模型直接调用 MCP 工具。例如,你可能会向智能体(Agent)下达指令:"从谷歌云端硬盘(Google Drive)下载我的会议记录,并将其附加到 Salesforce 潜在客户的信息中。" 此时模型会发起如下调用:

text 复制代码
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)

所有中间结果都必须经过模型处理。在这个示例中,完整的会议记录会两次流转经过模型。对于一场时长 2 小时的销售会议,这可能意味着要额外处理 5 万个令牌(token)。而对于篇幅更长的文档,甚至可能超出上下文窗口(context window)的限制,导致整个工作流程中断。

当处理大型文档或复杂数据结构时,模型在工具调用之间复制数据的过程中,出现错误的可能性可能会更高。

MCP 客户端会将工具定义加载到模型的上下文窗口(context window)中,并协调一个消息循环(message loop)------ 在该循环中,每次工具调用及相应结果都会在操作之间传递给模型。

二、 基于MCP的代码执行提升上下文效率

随着代码执行环境在智能体(Agent)中的应用日益普遍,一种解决方案是将 MCP 服务器以代码 API 的形式呈现,而非直接工具调用。智能体随后可通过编写代码与 MCP 服务器交互。该方案能同时解决上述两大痛点:智能体仅需加载所需工具,并可在执行环境中先处理数据,再将结果返回给模型。

实现方式有多种,其中一种是从已连接的 MCP 服务器生成所有可用工具的文件树(file tree)。以下是基于 TypeScript 的实现示例:

TypeScript 复制代码
servers
├── google-drive
│   ├── getDocument.ts
│   ├── ... (other tools)
│   └── index.ts
├── salesforce
│   ├── updateRecord.ts
│   ├── ... (other tools)
│   └── index.ts
└── ... (other servers)

随后,每个工具都对应一个文件,格式类似如下:

TypeScript 复制代码
// ./servers/google-drive/getDocument.ts
import { callMCPTool } from "../../../client.js";

interface GetDocumentInput {
  documentId: string;
}

interface GetDocumentResponse {
  content: string;
}

/* Read a document from Google Drive */
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentResponse> {
  return callMCPTool<GetDocumentResponse>('google_drive__get_document', input);
}

我们上文提到的 "从谷歌云端硬盘(Google Drive)到 Salesforce" 的示例,会转化为如下代码:

TypeScript 复制代码
// Read transcript from Google Docs and add to Salesforce prospect
import * as gdrive from './servers/google-drive';
import * as salesforce from './servers/salesforce';

const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  recordId: '00Q5f000001abcXYZ',
  data: { Notes: transcript }
});

智能体(Agent)通过遍历文件系统发现工具:先列出 ./servers/ 目录以查找可用服务器(如 google-drive 和 salesforce),再读取完成任务所需的特定工具文件(如 getDocument.ts 和 updateRecord.ts),从而了解每个工具的接口。这种方式能让智能体仅加载当前任务所需的工具定义,将令牌(token)用量从 15 万个减少至 2000 个 ------ 时间与成本节省率高达 98.7%。 Cloudflare也发布了类似研究结果,并将基于 MCP的代码执行称为 "代码模式(Code Mode)"。其核心思路一致:大语言模型(LLM)擅长编写代码,开发者应利用这一优势,构建能更高效与 MCP 服务器交互的智能体。

三、 基于MCP的代码执行优势

通过基于MCP的代码执行,智能体(Agent)可按需加载工具、在数据传递至模型前进行筛选、并通过单步执行复杂逻辑,从而更高效地利用上下文。此外,该方式在安全性与状态管理方面也具备优势。

1. 渐进式信息披露(Progressive Disclosure)

大语言模型(LLM)在文件系统导航方面表现出色。将工具以代码形式呈现在文件系统中,能让模型按需读取工具定义,而非一次性加载所有定义。 此外,还可在服务器中添加 search_tools 工具(工具搜索功能)以查找相关定义。例如,在使用前文提及的假设性 Salesforce 服务器时,智能体可搜索 "salesforce" 关键词,仅加载当前任务所需的工具。在 search_tools 工具中加入 "细节级别参数"(detail level parameter),能让智能体选择所需的信息详细程度(如仅名称、名称与描述、或包含模式定义的完整工具定义),这同样有助于智能体节省上下文资源、高效定位工具。

2. 上下文高效的工具结果(Context Efficient Tool Results)

处理大型数据集时,智能体可在代码中先对结果进行筛选与转换,再将其返回给模型。以获取包含 1 万行数据的电子表格(spreadsheet)为例:

TypeScript 复制代码
// Without code execution - all rows flow through context
TOOL CALL: gdrive.getSheet(sheetId: 'abc123')
        → returns 10,000 rows in context to filter manually

// With code execution - filter in the execution environment
const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
const pendingOrders = allRows.filter(row => 
  row["Status"] === 'pending'
);
console.log(`Found ${pendingOrders.length} pending orders`);
console.log(pendingOrders.slice(0, 5)); // Only log first 5 for review

智能体(Agent)看到的将是 5 行数据,而非 10000 行。类似逻辑同样适用于数据聚合、多数据源关联或特定字段提取------ 所有这些操作都不会导致上下文窗口(context window)臃肿。

3. 更强大且上下文高效的控制流(Control Flow)

循环(Loops)、条件判断(conditionals)和错误处理(error handling)可通过常用的代码模式实现,无需串联多个独立的工具调用。例如,若需在 Slack 中发送部署通知,智能体可编写如下代码:

TypeScript 复制代码
let found = false;
while (!found) {
  const messages = await slack.getChannelHistory({ channel: 'C123456' });
  found = messages.some(m => m.text.includes('deployment complete'));
  if (!found) await new Promise(r => setTimeout(r, 5000));
}
console.log('Deployment notification received');

该方式比在智能体(Agent)循环中交替执行 MCP 工具调用与睡眠命令(sleep commands)更高效。 此外,能够编写可执行的条件分支树(conditional tree),还能减少 "首令牌响应时间"(time to first token)延迟:无需等待模型评估条件语句(if-statement),智能体可将该任务交由代码执行环境完成。

4. 隐私保护型操作(Privacy-preserving operations)

当智能体通过 MCP 执行代码时,中间结果默认保留在执行环境中。如此一来,智能体仅能获取你明确记录(log)或返回的数据 ------ 这意味着,你不希望与模型共享的敏感数据可在工作流中流转,且始终不会进入模型的上下文(context)。

对于敏感度更高的任务场景,智能体框架(agent harness)可自动对敏感数据进行令牌化处理(tokenize sensitive data)。例如,若需将电子表格(spreadsheet)中的客户联系方式导入 Salesforce,智能体将编写如下代码:

TypeScript 复制代码
const sheet = await gdrive.getSheet({ sheetId: 'abc123' });
for (const row of sheet.rows) {
  await salesforce.updateRecord({
    objectType: 'Lead',
    recordId: row.salesforceId,
    data: { 
      Email: row.email,
      Phone: row.phone,
      Name: row.name
    }
  });
}
console.log(`Updated ${sheet.rows.length} leads`);

MCP客户端会拦截数据,并在数据传递至模型前,对个人身份信息(PII)进行令牌化处理(tokenize)。

TypeScript 复制代码
// 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 客户端会通过查找映射关系对其进行解令牌化处理(untokenize)。真实的电子邮箱地址、电话号码和姓名会从谷歌表格(Google Sheets)流转至 Salesforce,但全程不会经过模型。这能防止智能体(Agent)意外记录或处理敏感数据。你还可以借助此功能定义确定性安全规则(deterministic security rules),指定数据的可流入和流出路径。

5. 状态持久化与技能扩展(State persistence and skills)

具备文件系统访问权限的代码执行能力,让智能体能够在多个操作之间维持状态(maintain state)。智能体可将中间结果写入文件,从而支持恢复工作进度(resume work)和跟踪任务进展(track progress):

TypeScript 复制代码
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');

智能体(Agent)还能将自身代码持久化为可复用函数(reusable functions)。一旦智能体开发出可完成某项任务的可用代码,便可将该实现方案保存起来,以备后续使用:

TypeScript 复制代码
// 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)" 概念紧密相关 ------"技能" 指包含可复用指令、脚本和资源的文件夹,供模型用于提升特定任务的执行性能。在这些已保存的函数中添加一个 SKILL.md 文件,即可创建结构化的技能模块,供模型参考和使用。长此以往,你的智能体(Agent)将能构建一个包含更高级能力的 "工具箱",逐步完善高效工作所需的基础架构。

需注意的是,代码执行也会带来其自身的复杂性。运行智能体生成的代码,需要一个具备适当沙箱隔离(sandboxing)、资源限制(resource limits)和监控(monitoring)功能的安全执行环境。这些基础设施需求会增加运营开销,同时带来直接工具调用所无需考虑的安全问题。因此,在采用代码执行方式时,需权衡其优势(降低令牌成本、减少延迟、提升工具组合能力)与实现成本之间的关系。

四、总结

模型上下文协议(MCP)为智能体提供了连接众多工具与系统的基础协议。然而,当连接的服务器数量过多时,工具定义与结果会消耗过量令牌(token),导致智能体效率下降。 尽管本文中探讨的诸多问题(上下文管理、工具组合、状态持久化)看似新颖,但在软件工程领域早已存在成熟的解决方案。代码执行将这些既定模式应用于智能体,使其能够通过熟悉的编程结构,更高效地与 MCP 服务器交互。

相关推荐
bcbnb4 小时前
如何解析iOS崩溃日志:从获取到符号化分析
后端
许泽宇的技术分享4 小时前
当AI学会“说人话“:Azure语音合成技术的魔法世界
后端·python·flask
大模型真好玩4 小时前
Gemini3.0深度解析,它在重新定义智能,会是前端工程师噩梦吗?
人工智能·agent·deepseek
用户69371750013844 小时前
4.Kotlin 流程控制:强大的 when 表达式:取代 Switch
android·后端·kotlin
用户69371750013844 小时前
5.Kotlin 流程控制:循环的艺术:for 循环与区间 (Range)
android·后端·kotlin
vx_bisheyuange4 小时前
基于SpringBoot的宠物商城网站的设计与实现
spring boot·后端·宠物
bcbnb4 小时前
全面解析网络抓包工具使用:Wireshark和TCPDUMP教程
后端
leonardee4 小时前
Spring Security安全框架原理与实战
java·后端
AAA修煤气灶刘哥4 小时前
从Coze、Dify到Y-Agent Studio:我的Agent开发体验大升级
人工智能·低代码·agent
回家路上绕了弯4 小时前
包冲突排查指南:从发现到解决的全流程实战
分布式·后端