使用 MCP 进行代码执行:构建更高效的 AI Agent

举报
yd_298479158 发表于 2026/09/21 10:22:14 2026/09/21
【摘要】 模型上下文协议(Model Context Protocol,MCP)是一项用于将 AI Agent 连接到外部系统的开放标准。传统上,如果要把 Agent 连接到不同的工具和数据源,开发者往往需要为每一对组合单独编写集成。这种方式会造成碎片化和大量重复工作,也很难扩展到真正大规模、彼此连接的系统。MCP 提供了一套统一协议:开发者只需要在自己的 Agent 中实现一次 MCP,就能够接入整...

模型上下文协议(Model Context Protocol,MCP)是一项用于将 AI Agent 连接到外部系统的开放标准。

传统上,如果要把 Agent 连接到不同的工具和数据源,开发者往往需要为每一对组合单独编写集成。这种方式会造成碎片化和大量重复工作,也很难扩展到真正大规模、彼此连接的系统。

MCP 提供了一套统一协议:开发者只需要在自己的 Agent 中实现一次 MCP,就能够接入整个 MCP 集成生态。

自 2024 年 11 月发布 MCP 以来,它的采用速度非常快。社区已经构建了数千个 MCP Server,几乎所有主流编程语言都已经拥有 SDK,而整个行业也逐渐把 MCP 作为连接 Agent、工具和数据的事实标准。

如今,开发者经常会构建能够访问数百甚至数千个工具的 Agent,而这些工具分布在几十个 MCP Server 上。

但随着连接的工具数量增加,新的问题也出现了:

  • 如果一开始就把所有工具定义全部加载进上下文,会拖慢 Agent,并增加成本;

  • 如果所有中间工具结果都必须经过模型上下文,也会进一步增加 Token 消耗。

本文将讨论如何通过“代码执行”让 Agent 更高效地与 MCP Server 交互,从而在使用更少 Token 的同时处理更多工具。

工具带来的过度 Token 消耗会降低 Agent 效率

随着 MCP 使用规模扩大,有两种常见模式会明显增加 Agent 的成本和延迟:

  • 工具定义占满上下文窗口;

  • 中间工具结果额外消耗大量 Token。

1. 工具定义占满上下文窗口

大多数 MCP Client 会在一开始就把所有工具定义直接加载到上下文中,并通过直接工具调用语法暴露给模型。

这些工具定义可能类似下面这样:

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.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 Client 允许模型直接调用 MCP 工具。

例如,你可能对 Agent 说:

从 Google Drive 下载我的会议转录,然后把它附加到 Salesforce 的潜在客户记录中。

模型可能会执行类似这样的调用:

TOOL CALL:
gdrive.getDocument(documentId: "abc123")

→ returns
"Discussed Q4 goals...
[full transcript text]"

(loaded into model context)

TOOL CALL:
salesforce.updateRecord(
  objectType: "SalesMeeting",
  recordId: "00Q5f000001abcXYZ",
  data: {
    "Notes": "Discussed Q4 goals...
    [full transcript text written out]"
  }
)

(model needs to write entire transcript into context again)

每一个中间结果都必须经过模型。

在这个例子中,完整会议转录需要流经上下文两次。

如果这是一场 2 小时的销售会议,就可能额外产生 50,000 Token 的处理量。

对于更大的文档,内容甚至可能直接超过上下文窗口上限,从而让整个工作流失败。

而且,当中间内容是大型文档或复杂数据结构时,模型在不同工具调用之间复制数据时也更容易出错。

使用 MCP 进行代码执行可以提高上下文效率

随着 Agent 使用代码执行环境逐渐成为常见做法,一种解决方案是:

不要把 MCP Server 暴露成“直接工具调用”,而是把它们作为代码 API 提供给 Agent。

这样,Agent 可以通过编写代码来与 MCP Server 交互。

这种方式能够同时解决前面提到的两个问题:

  • Agent 只加载当前真正需要的工具;

  • Agent 可以先在执行环境中处理数据,再把必要结果返回给模型。

实现这一方案的方法有很多。

一种做法,是把所有已连接 MCP Server 中可用的工具生成为一棵文件树。

例如,可以使用 TypeScript 构建如下结构:

servers
├── google-drive
│   ├── getDocument.ts
│   ├── ... (other tools)
│   └── index.ts
├── salesforce
│   ├── updateRecord.ts
│   ├── ... (other tools)
│   └── index.ts
└── ... (other servers)

每一个工具都对应一个文件,例如:

// ./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”的例子,就可以变成:

// 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/

从而看到有哪些可用 Server,例如:

google-drive
salesforce

然后只读取当前任务需要的工具文件,例如:

getDocument.ts
updateRecord.ts

这样,Agent 只会加载真正需要的工具定义。

在我们的示例中,这种方式把 Token 使用量从 150,000 降低到了 2,000,节省了 98.7% 的时间与成本。

Cloudflare 也发布了类似结果,并把这种使用 MCP 进行代码执行的方式称为“Code Mode”。

其核心洞察是一致的:

LLM 本身非常擅长编写代码,因此开发者应该充分利用这种能力,让 Agent 通过代码更加高效地与 MCP Server 交互。

使用 MCP 进行代码执行的优势

通过代码执行与 MCP 配合,Agent 可以:

  • 按需加载工具;

  • 在数据进入模型前进行过滤;

  • 在一次代码执行中完成复杂逻辑;

  • 更高效地使用上下文。

除此之外,这种方式在安全性和状态管理方面也存在额外优势。

渐进式披露

模型非常擅长浏览文件系统。

因此,如果把工具以代码文件的形式放在文件系统里,模型就可以按需读取工具定义,而不是一开始就把所有定义全部塞进上下文。

另一种方式,是在 Server 中添加一个 search_tools 工具,用来搜索相关工具定义。

例如,当 Agent 使用前面假设的 Salesforce Server 时,它可以先搜索:

salesforce

然后只加载当前任务所需要的工具。

如果 search_tools 还支持一个详细程度参数,那么 Agent 可以自行决定需要获取多少信息,例如:

  • 只返回工具名称;

  • 返回工具名称与描述;

  • 返回包含 Schema 在内的完整定义。

这样也能够进一步减少上下文消耗。

更节省上下文的工具结果

当处理大型数据集时,Agent 可以先在代码环境中筛选和转换数据,然后只把必要结果返回模型。

假设需要读取一份包含 10,000 行的电子表格。

如果不使用代码执行:

// 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 行数据,而不是 10,000 行。

相同模式也可以用于:

  • 聚合;

  • 跨多个数据源进行 Join;

  • 提取特定字段;

  • 过滤大型结果。

这些操作都不需要把整个数据集塞入上下文窗口。

循环、条件判断和错误处理也可以直接通过熟悉的编程结构完成,而不必让 Agent 一个接一个地链式调用工具。

例如,如果你需要等待 Slack 中出现一条部署完成通知,Agent 可以写:

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 Loop 中反复执行:

  • MCP 工具调用;

  • Sleep;

  • 再调用工具;

  • 再 Sleep;

更加高效。

此外,如果能够直接把条件分支写进代码并交给执行环境运行,还可以减少“首 Token 延迟”。

因为模型不需要每次都亲自判断一个 if 条件,而是可以让代码执行环境直接完成。

隐私保护型操作

当 Agent 通过代码执行与 MCP 交互时,中间结果默认会停留在执行环境中。

只有被显式 logreturn 的内容,才会进入模型上下文。

因此,那些你不希望模型看到的数据,也可以在整个工作流中流转,而无需进入模型。

对于更加敏感的工作负载,Agent Harness 还可以自动对敏感数据进行 Tokenization。

例如,假设你需要把电子表格里的客户联系信息导入 Salesforce。

Agent 可以写:

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 Client 可以在数据真正到达模型之前拦截这些内容,并把个人身份信息替换为 Token。

例如,如果 Agent 打印了 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 Client 可以通过本地映射把 Token 还原成真实值。

这样,真实的:

  • 邮箱地址;

  • 电话号码;

  • 姓名;

可以从 Google Sheets 流向 Salesforce,但始终不会经过模型上下文。

这样可以防止 Agent 意外记录或处理敏感数据。

同样的机制还可以用于定义确定性的安全规则,精确控制数据允许从哪里流向哪里。

状态持久化与 Skills

如果代码执行环境允许访问文件系统,那么 Agent 还可以在不同操作之间持久化状态。

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'
);

Agent 还可以把自己写过的代码保存成可重复使用的函数。

当 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`;
}

之后,任何一次 Agent 执行都可以直接复用:

import {
  saveSheetAsCsv
} from './skills/save-sheet-as-csv';

const csvPath = await saveSheetAsCsv(
  'abc123'
);

这与 Skills 的概念非常接近。

Skill 本质上是由可复用的:

  • 指令;

  • 脚本;

  • 资源;

组成的文件夹,可以帮助模型在某个专业任务上获得更稳定的表现。

如果再为这些保存下来的函数添加一个 SKILL.md 文件,就可以把它们正式组织成结构化 Skill,让模型能够读取和使用。

随着时间推移,Agent 可以逐渐为自己构建出一套更高层级的工具箱。

也就是说,它不只是完成任务,还可以不断进化出一套最适合自己工作的脚手架。

需要注意的是,代码执行本身也会引入额外复杂度。

运行 Agent 生成的代码,需要一个安全的执行环境,并配套:

  • Sandbox;

  • 资源限制;

  • 监控机制。

这些基础设施需求会带来额外运维成本,也会增加安全考虑,而直接工具调用则不存在这些问题。

因此,在决定是否使用代码执行时,需要权衡它带来的收益与实现成本。

主要收益包括:

  • Token 成本更低;

  • 延迟更低;

  • 工具组合能力更强。

总结

MCP 为 Agent 连接大量工具和外部系统提供了基础协议。

但是,当连接的 Server 数量变得过多之后,工具定义和工具结果会消耗大量 Token,从而降低 Agent 效率。

本文讨论的一些问题看起来很新,例如:

  • 上下文管理;

  • 工具组合;

  • 状态持久化。

但它们其实在传统软件工程中已经存在成熟的解决方案。

代码执行的意义,就是把这些已经验证过的软件工程模式应用到 Agent 系统中。

通过代码,Agent 可以使用熟悉的编程结构,更高效地与 MCP Server 交互。

如果你实现了这种方法,也可以把自己的经验分享给 MCP 社区。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。