MCP是如何工作的?
MCP是如何工作的?
LLM 在调用外部工具(Tool-calling / Function-calling)时,确实经历了“口语化输入 → 结构化解析 → 运行时执行 → 结果回灌”这一闭环。MCP(Model-Calling-Protocol,或更广为人知的“Model-Context-Protocol”)正是把这一闭环标准化、可插拔化之后的产物。
从人话到函数,再从函数回到人话
—— 用 MCP 的视角拆解 LLM 工具调用
想象一下:用户说“今天北京热吗?”
你家的 AI 居然自己打开了天气 API,把温度读出来,还用中文回答:“34℃,挺热的,记得防晒。”
这背后发生了什么?为什么 AI 知道要调哪个函数、填哪些参数?
答案就藏在 MCP 的三次握手 里。
1. 口语 → 结构化:第一次握手(解析)
用户输入:
“今天北京热吗?”
在 MCP 里,这行文本首先被送进 解析器(Parser) 。解析器并不关心天气,它只干一件事:
把自然语言映射成“函数签名 + 参数” 。
// 解析器输出(LLM 第一次返回)
{
"name": "get_weather",
"arguments": {
"city": "北京",
"date": "2025-07-29"
}
}
关键点
- 解析器可以就是 LLM 自己(通过 prompt/fine-tune),也可以是独立 NLU 模型。
- MCP 要求函数声明(JSON Schema)提前注册,LLM 像“查字典”一样匹配最符合意图的函数。
- 对可选参数、枚举值、数组等复杂 schema,MCP 会给出完整描述,减少幻觉。
2. 结构化 → 运行时:第二次握手(执行)
解析器把 JSON 交给 MCP 运行时(Runtime) 。
运行时的职责是“零信任”地执行函数,并捕获三种结果:
| 结果类型 | 示例 | 如何处理 |
|---|---|---|
| 正常值 | {"temp": 34, "unit": "℃"} |
直接返回 |
| 业务错误 | {"error": "城市不存在"} |
包装成 message 回传 |
| 系统异常 | timeout、网络不通 | 转成统一异常格式,避免 LLM 看到堆栈 |
// 运行时返回
{
"tool_call_id": "call_1",
"content": {
"temp": 34,
"unit": "℃"
}
}
MCP 的精妙之处
- 统一错误码,让 LLM 可以“优雅地道歉”或“自动重试”。
- 支持异步调用、批量调用(parallel tool calls),LLM 一次可请求 3~N 个函数。
3. 运行时 → 口语:第三次握手(生成)
现在 LLM 拿到了原始数据,但它不会把 JSON 甩给用户。
它要进行 第二次推理,把结构化结果再翻译成人话:
34℃,体感偏热,紫外线强,出门建议涂 SPF50。
至此,MCP 的三次握手 完成闭环:
- 人话 → 结构化(意图解析)
- 结构化 → 执行(函数调用)
- 执行结果 → 人话(总结回答)
4. 把“握手”做成协议:MCP 架构全景
为了让不同团队、不同语言的模型和工具都能互通,MCP 把上述流程固化为一套协议:
┌────────────┐ MCP Schema ┌───────────┐
│ LLM Core │◄──────────────────►│ Registry │
└────┬───────┘ └────┬──────┘
│ 1. 结构化请求 │ 2. 函数清单
┌────┴───────┐ JSON-RPC ┌────┴───────┐
│ Runtime │◄───────────────►│ Tool A/B │
└────────────┘ └────────────┘
- Registry:函数清单 + JSON Schema,可热更新。
- Runtime:沙箱、鉴权、限流、重试、日志。
- LLM Core:只关心“我要调什么、结果长啥样”,其余全部交给协议层。
5. 开发者如何落地 MCP?
-
定义函数
用 JSON Schema 写一个get_weather.json,放进 registry。 -
接入 Runtime
Python/Java/Go 都有官方 SDK,三行代码起一个本地 server。 -
调教 LLM
在 prompt 里加一句:你只能用以下函数:get_weather, book_restaurant...或者 fine-tune 一个小模型专门做解析器。
6. 小结:记住一句话
MCP 把“人话 ↔ 函数”的黑盒拆成了可观测、可插拔的三次握手。
开发者只需写好函数,剩下的事交给协议。
7. 彩蛋:如果用户问“帮我订今晚 7 点海底捞”?
-
解析器输出:
{ "name": "book_restaurant", "arguments": { "brand": "海底捞", "date": "2025-07-29", "time": "19:00", "people": 2 } } -
运行时返回:
{"order_id": "HN12345", "wait_time": 30} -
LLM 回复:
已为您预订今晚 7 点海底捞,预计等位 30 分钟,订单号 HN12345。
附 Python 代码
工具函数代码
# tools.py
def get_current_weather(location):
"""
获取指定城市的实时天气信息
注意:实际项目中应接入真实天气 API,这里仅为示例
"""
# 模拟返回天气数据(实际项目中应调用高德、OpenWeatherMap 等)
weather_data = {
"location": location,
"temperature": "26°C",
"condition": "多云",
"humidity": "65%"
}
return weather_data
def calculate_temperature(num1, num2, operation):
"""
支持加减乘除四种运算
"""
if operation == "add":
return num1 + num2
elif operation == "subtract":
return num1 - num2
elif operation == "multiply":
return num1 * num2
elif operation == "divide":
if num2 == 0:
raise ValueError("除数不能为零")
return num1 / num2
else:
raise ValueError(f"未知运算类型: {operation}")
if __name__ == "__main__":
print(calculate_temperature(966, 69, "add"))
调用模型代码
# qwq_mcp.py
import os
import json
from openai import OpenAI
from tools import get_current_weather, calculate_temperature
# 初始化客户端
client = OpenAI(
api_key="sk-0f0dac238000000000000000000000", # 替换为你的实际 API Key
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 定义可用工具
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定城市的实时天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市名称,如杭州市"}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取当前时间",
"parameters": {}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "执行两个数字的加减乘除运算",
"parameters": {
"type": "object",
"properties": {
"num1": {"type": "number", "description": "第一个操作数"},
"num2": {"type": "number", "description": "第二个操作数"},
"operation": {
"type": "string",
"description": "运算类型:'add', 'subtract', 'multiply', 'divide'",
"enum": ["add", "subtract", "multiply", "divide"]
}
},
"required": ["num1", "num2", "operation"]
}
}
}
]
# 用户提问
messages = [{"role": "user", "content": "588+222 等于多少?"}]
# 第一次请求:让模型判断是否需要调用工具
completion = client.chat.completions.create(
model="qwen3-235b-a22b-instruct-2507",
messages=messages,
tools=tools,
tool_choice='auto' # 可选:auto, none, 或指定工具
)
response_message = completion.choices[0].message # 获取模型回复
print(" 模型回复:", response_message)
# 检查是否需要调用工具
if response_message.tool_calls:
# 添加工具调用结果到消息历史
messages.append(response_message)
# 遍历所有 tool_calls
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
# 执行对应的工具函数
if function_name == "get_current_weather":
# 这里可以接入真实天气 API
location = function_args.get("location", "未知城市")
# 模拟返回天气数据(实际项目中应调用高德、OpenWeatherMap 等)
weather_data = get_current_weather(location)
tool_response_content = json.dumps(weather_data, ensure_ascii=False)
elif function_name == "get_current_time":
from datetime import datetime
current_time = datetime.now().strftime("北京时间 %Y年%m月%d日 %H:%M:%S")
tool_response_content = json.dumps({"time": current_time}, ensure_ascii=False)
elif function_name == "calculate_temperature":
num1= function_args.get("num1", 0)
num2 = function_args.get("num2", 0)
operation = function_args.get("operation", "add")
result = calculate_temperature(num1, num2, operation)
tool_response_content = json.dumps(result, ensure_ascii=False)
else:
tool_response_content = json.dumps({"error": "未知工具"}, ensure_ascii=False)
# 将工具执行结果以 role="tool" 的形式加入对话
messages.append({
"role": "tool",
"content": tool_response_content,
"tool_call_id": tool_call.id # 必须匹配
})
# 第二次请求:将工具结果传回模型,生成最终回答
final_response = client.chat.completions.create(
model="qwen3-235b-a22b-instruct-2507",
messages=messages
)
print("✅ 最终回答:")
print(final_response.choices[0].message.content)
else:
# 如果模型没有调用工具,直接输出其回复
print(" 模型直接回复:")
print(response_message.content)
执行后的返回如下
(mcp-test) zy@ztg2:~/mycode/MCP_test$ python qwq_mcp.py
模型回复: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_2ba108ca775e465aa1ac0c', function=Function(arguments='{"num1": 588, "num2": 222, "operation": "add"}', name='calculate'), type='function', index=0)])
✅ 最终回答:
588 + 222 = 810。
可以看到第一次请求大模型后,它就把用户的口语完整地解析成工具所需的函数参数格式;运行时(qwq_mcp.py)执行函数后,把结果再次回传给大模型,最终由大模型生成回答。
浙公网安备 33010602011771号