智能体工具调用的实现方式(Function Calling和MCP)

前言

智能体(AI Agent)本身运行在云端或受控的沙箱环境中,出于安全考虑,它不能直接访问或执行你本地电脑上的程序(如打开计算器、读取本地文件、运行 Python 脚本等)。

要实现“调用本地程序”,必须通过一个中间桥梁

这个桥梁通常是一个运行在你本地电脑上的客户端程序服务

核心机制是:Function Calling (函数调用) + 本地服务器/代理

智能体(AI Agent)本身运行在云端或受控的沙箱环境中,出于安全考虑,它不能直接访问或执行你本地电脑上的程序(如打开计算器、读取本地文件、运行 Python 脚本等)。

要实现“调用本地程序”,必须通过一个中间桥梁。这个桥梁通常是一个运行在你本地电脑上的客户端程序服务

核心机制是:Function Calling (函数调用) + 本地服务器/代理

以下是具体的实现流程和三种主流方案:

核心流程

它是如何工作的?

  1. 定义工具 (Define Tools)
    开发者在代码中定义一个“工具”(Tool/Function),告诉大模型:“我有一个叫 run_local_script 的功能,你可以调用它,需要传入参数 script_name。”
    注意:此时大模型只知道有这个功能,但还不知道怎么执行。
  2. 模型决策 (Decision)
    当你问智能体:“帮我运行本地的 backup.py 脚本”,大模型分析后决定:“我需要调用 run_local_script 工具”,并生成一个结构化的调用请求(通常是 JSON 格式),包含函数名和参数。
  3. 拦截与转发 (Intercept & Forward)
    你的应用程序(宿主代码)接收到这个大模型的调用请求。它不会让大模型直接执行,而是由本地代码去执行真正的逻辑。
  4. 本地执行 (Local Execution)
    本地代码(运行在你的电脑上)收到指令,使用系统命令(如 Python 的 subprocess、Node.js 的 child_process)真正地去启动那个本地程序。
  5. 返回结果 (Return Result)
    本地程序运行完毕,将输出结果(stdout)或错误信息返回给宿主代码,宿主代码再将其格式化后发给大模型。大模型根据结果回答你:“脚本运行成功,备份已完成。”

三种主流实现方案

使用 Function Calling

这是最基础也是最灵活的方式,适用于你自己开发的应用。

  • 原理:你在本地写一个后端服务(Python/Node.js等),将本地程序封装成 API 或函数,并通过 SDK 注册给大模型。

  • 流程:

    1. 注册:在代码中定义 tools 列表,描述函数签名。
    2. 循环:
      • 发送用户问题 + tools 定义给 LLM。
      • LLM 返回 {"name": "open_app", "arguments": {"app": "notepad"}}
      • 你的代码检测到这个请求,执行 subprocess.run(["notepad.exe"])
      • 将执行结果返回给 LLM。
  • 适用场景:构建专属的桌面助手、自动化运维脚本。

代码逻辑示例 (Python):

1
2
3
4
5
6
7
8
# 伪代码逻辑
if llm_response.tool_name == "open_calculator":
import subprocess
# 真正在本地执行
subprocess.Popen(["calc.exe"])
result = "计算器已打开"
# 把结果告诉 LLM
send_to_llm(result)

使用 MCP

MCP (Model Context Protocol) 是最近兴起的一个开放标准,专门用于解决 AI 模型连接本地数据和工具的问题。

  • 原理:MCP 定义了一套标准协议。你只需要在本地运行一个 MCP Server(它可以是任何语言写的),这个 Server 负责暴露本地资源(文件系统、数据库、本地命令行工具)。支持 MCP 的 AI 客户端(如 Cursor, Claude Desktop, 或自研 Agent)会自动连接这个本地 Server。
  • 优势:解耦。你不需要为每个 AI 应用重写连接代码,只要写好 MCP Server,任何支持 MCP 的客户端都能调用你的本地程序。
  • 如何调用本地程序:
    1. 编写一个 MCP Server(例如用 TypeScript 或 Python)。
    2. 在 Server 中定义一个 Tool,内部逻辑调用 child_process 执行本地命令。
    3. 在 AI 客户端配置中指向这个 Server。
    4. AI 自动发现该工具并调用。

本地代理模式 (Local Proxy / Sidecar)

很多现成的桌面端 AI 软件(如某些本地运行的 LLM 启动器)采用这种模式。

  • 原理:软件在安装时会在本地启动一个微型 HTTP 服务器(Sidecar)。
  • 流程:
    • AI 模型(云端或本地)认为它在调用一个远程 API。
    • 实际上,这个 API 的地址是 http://localhost:8080/execute
    • 请求发送到本地端口,本地服务接收后执行系统命令。
  • 适用场景:开源项目(如 Open Interpreter, Dify 本地版),它们通常自带这种机制来执行代码。

Function Calling原理

在使用 vLLM 启用工具调用(Tool Calling)功能时,“能调用哪些工具”并不是由 vLLM 本身决定的,而是由你在发起 API 请求时动态传入的 tools 列表定义的

vLLM 的作用是:解析模型输出,并按照你指定的格式(如 OpenAI 协议)返回结构化的工具调用信息

请求示例

POST http://localhost:8000/v1/chat/completions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [
{"role": "user", "content": "今天北京天气怎么样?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称,如 '北京'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "search_web",
"description": "搜索网络信息",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
}
],
"tool_choice": "auto"
}

注意

每次请求都要把要使用的工具放在请求中。

核心属性是"tool_choice": "auto"tools

工具的选择是模型依赖description属性进行推断。

返回的数据是怎样使用的呢?

我们在模型启动的时候会添加类似这样的参数

1
--enable-auto-tool-choice --tool-call-parser qwen3_coder 

并且在 API 请求中传入了 tools 和 tool_choice=”auto”,那么当模型判断需要调用工具时,其返回的数据结构将遵循 OpenAI Chat Completions API 的 Tool Calling 格式

--tool-call-parser的作用就是大模型把返回的数据转为OpenAI Chat Completions API 的 Tool Calling 对应的JSON格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1710000000,
"model": "your-model-name",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null, // 注意:content 为 null 或空字符串
"tool_calls": [
{
"id": "call_abc123xyz", // 工具调用唯一 ID
"type": "function",
"function": {
"name": "get_weather", // 调用的函数名
"arguments": "{\"location\": \"San Francisco, CA\", \"unit\": \"fahrenheit\"}"
}
}
]
},
"finish_reason": "tool_calls" // 关键!表示因工具调用结束
}
]
}

这样处理逻辑就能调用了

1
2
3
4
5
6
7
# 提取工具调用
if response.choices[0].finish_reason == "tool_calls":
tool_call = response.choices[0].message.tool_calls[0]
func_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f"Call {func_name} with args: {args}")
# 输出: Call get_weather with args: {'location': 'Tokyo', 'unit': 'fahrenheit'}

MCP的原理

MCP(Model Context Protocol,模型上下文协议)服务被大模型“发现”的过程,并不是由大模型主动扫描网络或注册中心,而是通过客户端(AI Agent 或 MCP 客户端)在运行时动态获取工具列表,并将这些能力以结构化提示词(Prompt)的形式注入给大模型,从而让大模型“知道”有哪些外部工具可用。整个过程是客户端驱动、协议标准化、提示工程引导的协同机制。

下面从三个核心阶段详细说明 MCP 服务如何被大模型“发现”:

核心阶段

阶段一:获取工具清单

客户端连接 MCP 服务器,获取工具清单(Tool Discovery)

这是“发现”的起点,但发现者是客户端,不是大模型本身

用户或系统配置好一个或多个 MCP 服务器地址(如 https://mcp.amap.com)。

客户端(如 Cursor、Claude Desktop、Azure AI Agent 等)在会话开始前或需要时,向 MCP 服务器发送 /tools/list 请求(通常为 HTTP GET 或 SSE 连接)。

1
GET https://mcp.amap.com/tools/list

MCP 服务器返回一个 JSON 格式的工具清单,包含每个工具的:

  • name(唯一标识)
  • description(功能描述)
  • inputSchemaparameters(参数结构,符合 JSON Schema)

示例响应:

1
2
3
4
5
6
7
8
9
{
"tools": [
{
"name": "maps_weather",
"description": "查询指定城市的实时天气数据",
"parameters": { "city": "string" }
}
]
}

✅ 此时,客户端拥有了“服务菜单”,但大模型对此仍一无所知

阶段二:合成提示词

客户端将工具信息注入 Prompt,引导大模型决策

这是“发现”对大模型生效的关键环节——通过提示词工程(Prompt Engineering)实现能力透传

客户端将上一步获取的工具列表,格式化后嵌入到系统提示词(System Prompt)中,与用户问题一起发送给大模型 。

例如,构造如下 Prompt:

1
2
3
4
5
6
7
8
9
10
11
你是一个智能助手,可以使用以下工具帮助用户:

- get_weather: 获取指定城市的当前天气信息。
参数: city (必需, 字符串)

请根据用户需求选择是否调用工具。若需调用,请严格按以下格式输出:
<use_mcp_tool>
<server_name>weather-server</server_name>
<tool_name>get_weather</tool_name>
<arguments>{"city": "北京"}</arguments>
</use_mcp_tool>

或者使用 OpenAI 兼容的 tools 字段(在支持的平台如阿里云百炼中):

1
2
3
4
5
6
7
8
9
10
{
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "...",
"parameters": { ... }
}
}]
}

注意

大模型本身不具备主动发现能力,它只是在看到这个“菜单”后,基于语义理解判断是否需要调用某个工具 。

阶段三:客户端执行

大模型输出结构化指令,客户端执行调用

  • 大模型分析用户问题(如“北京天气怎么样?”),匹配工具描述,决定调用 get_weather

  • 不直接执行操作,而是输出一个严格格式化的调用指令(XML 或 JSON)。

    例如:

    1
    2
    3
    4
    <use_mcp_tool>
    <tool_name>get_weather</tool_name>
    <arguments>{"city": "北京"}</arguments>
    </use_mcp_tool>
  • 客户端解析该指令,提取 tool_namearguments,向对应的 MCP 服务器发起 /tools/call 请求 。

  • MCP 服务器执行实际逻辑(如调用高德天气 API),返回结构化结果。

  • 客户端将结果再次送回大模型,生成最终自然语言回复。

请求的格式

/tools/callMCP(Model Context Protocol) 协议中用于客户端调用 MCP 服务端工具的标准接口。

其请求参数格式是严格定义的 JSON 结构,旨在确保跨平台、跨语言的互操作性。

标准请求格式(HTTP POST)

1
2
POST /tools/call
Content-Type: application/json

请求体(Request Body)

1
2
3
4
5
6
7
{
"tool_name": "get_weather",
"arguments": {
"city": "Beijing",
"unit": "celsius"
}
}

字段说明

字段 类型 必填 说明
tool_name string ✅ 是 工具的唯一名称,必须与 MCP Server 通过 /tools/list 返回的 name 完全一致
arguments object ✅ 是 调用该工具所需的参数对象,结构必须符合该工具在 /tools/list 中声明的 inputSchemaparameters

⚠️ 注意:

  • arguments 不是字符串,而是原生 JSON 对象(与 OpenAI Function Calling 的 arguments 字符串不同)。
  • 参数名和类型必须严格匹配工具定义,否则 MCP Server 应返回错误。

完整示例

  1. 假设 MCP Server 的 /tools/list 返回:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"tools": [
{
"name": "search_web",
"description": "Search the web for information",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"max_results": { "type": "integer", "default": 5 }
},
"required": ["query"]
}
}
]
}
  1. 客户端调用 /tools/call
1
2
3
4
5
6
7
{
"tool_name": "search_web",
"arguments": {
"query": "latest AI news 2026",
"max_results": 3
}
}
  1. MCP Server 成功响应(200 OK):
1
2
3
4
5
6
7
8
{
"content": [
{
"type": "text",
"text": "Top AI news: Qwen3 released with native MCP support..."
}
]
}
  1. 若参数错误(如缺少 query),应返回 400 错误:
1
2
3
4
5
6
{
"error": {
"type": "invalid_request_error",
"message": "Missing required argument: query"
}
}

与 OpenAI Function Calling 的关键区别

特性 OpenAI Function Calling MCP /tools/call
arguments 格式 字符串(JSON-encoded) 原生 JSON 对象
调用发起方 大模型生成 → 客户端解析执行 客户端直接构造调用
协议层级 模型 API 层 独立服务协议层
调用方式 LLM 输出调用指令 客户端直接调用

注意:

在 MCP 架构中,大模型只负责决策是否调用及生成参数语义,而参数构造和网络调用由客户端完成,因此 arguments 是结构化对象而非字符串。

安全与扩展字段(可选)

部分 MCP 实现可能支持额外字段,如:

1
2
3
4
5
6
7
8
9
{
"tool_name": "send_email",
"arguments": { ... },
"context": {
"user_id": "u123",
"session_id": "sess_abc"
},
"timeout": 10000
}

但这些属于非标准扩展,使用前需确认 MCP Server 是否支持。

总结

MCP /tools/call 的标准参数格式为

1
2
3
4
{
"tool_name": "string",
"arguments": { /* JSON object matching tool schema */ }
}

它强调结构化、类型安全、与模型解耦,是构建可靠 AI Agent 工具链的核心接口 。

MCP 服务的注册与集中发现机制

虽然单次调用依赖客户端传入,但生态层面已有集中式注册表,便于开发者查找和集成 MCP 服务:

  1. GitHub MCP Registry
    GitHub 官方推出的 MCP 服务器目录,自动聚合托管在 GitHub 上的 MCP 项目,按 Star 数、活跃度排序,支持一键安装 。
  2. AIbase MCP 服务库
    提供超过 12 万个 MCP 服务的排行榜,支持按流行度、更新时间、功能类型筛选,降低选型成本 。
  3. Azure API 中心
    企业可在 API 清单中注册远程 MCP 服务器,并通过 Foundry 工具目录供 AI Agent 发现 。

这些平台解决的是“人类开发者如何找到 MCP 服务”的问题,而非大模型自动发现。

总结

MCP 服务被“发现”的本质

角色 职责
MCP 服务器 托管工具,提供 /tools/list/tools/call 接口
客户端(Agent) 主动连接 MCP 服务,获取工具列表,注入 Prompt,解析并执行调用
大模型(LLM) 被动接收工具信息,在 Prompt 引导下决策是否调用,输出结构化指令

大模型并不“主动发现”MCP 服务,而是通过客户端提供的上下文“被告知”有哪些工具可用

这种设计实现了解耦、安全、灵活的工具扩展机制,是 MCP 协议的核心优势之一 。

因此,MCP 的“发现”本质上是 客户端驱动的动态工具注入 + 大模型的条件性调用,而非传统意义上的服务注册与自动发现。