“帮我查一下明天的天气。”
对着本地部署的AI对话框敲完这句话,看着回复,才发现它根本做不到。
不是不想做。是没有"查"这个能力。
AI能写诗、能翻译、能讨论哲学——为什么不能查个天气?为什么对话框里那个看似无所不能的东西,连这么简单的事都做不了?
答案比想象中简单:它没有手。
AI 需要一双手
大语言模型的本质是一台预测引擎。输入一段文字,它预测下一段文字最可能是什么。它没有眼睛、连不上网、打不开文件。它的全部世界,就是塞进对话框的那段文字。
“查天气"这三个字对它来说,跟"写首诗"没有区别——都是在预测下一个token。但它预测出来的天气,可能是去年的,可能是编的。
所以问题变成了:怎么给这台预测引擎装上一双能伸出去的手?
这就是工具调用。
工具调用就三个动作
不需要理解代码。就三个动作在循环:
动作一:告诉AI有哪些工具可用。 提前写好一份清单,列出AI能调用的资源——比如"网页搜索"“读文件"“发邮件”。每个工具附带一份说明书,写清楚叫什么、能干嘛、需要什么参数。
动作二:AI判断该调哪个、怎么调。 AI收到消息后,扫一眼清单。如果发现聊天解决不了,而清单上有工具能解决——就决定调动那个工具,并填好参数。
动作三:系统执行,把结果喂回来。 AI只负责"说要用哪个工具”,真正去搜索、读文件、跑命令的,是电脑或服务器。执行完把结果塞回对话,AI基于结果继续回答。
工具调用的三种场景
工具调用不是一个"功能开关”,取决于AI"站在哪里"。
网页端。 最常见的形态。ChatGPT网页版能搜索、能画图、能跑代码——这些就是被预设好的工具。但能看到哪些、能调哪些,是平台定好的,没法自己加。
API调用。 开发者通过API接入。工具完全自己定义——想给AI什么能力,就写什么工具。最灵活,但需要写代码。
AI Agent。 跑在本地或服务器上的AI客户端。能调用shell命令、读写文件、操作软件。装什么工具就能调什么,权限边界自己控制。
三种场景一层比一层自由。要彻底搞懂工具调用,本地Agent是最短路径——每一步都看得见。
一个真实的例子。复盘上周微信公众号时,我让AI帮我看一眼我已经记录在本地笔记中的数据,:
“查看《上下文工程》的阅读量”
AI没有直接回答。它在后台调用了shell命令,在我电脑上执行了文件搜索。拿到搜索结果,再告诉我"23"。看起来是一问一答,背后是一次完整的工具调用循环。
工具调用的四个组件
| 组件 | 做什么 | 谁负责 |
|---|---|---|
| 工具定义 | 描述工具叫啥、能干嘛、要什么参数 | 人提前写好 |
| 工具选择 | AI判断该用哪个工具 | AI自己判断 |
| 工具执行 | 真正去搜索、读文件、跑命令 | 系统执行 |
| 结果整合 | AI把返回结果消化,继续对话 | AI自己 |
四个组件里,最核心也最容易困惑的是第一个——工具定义。
一个工具,就两部分:
① JSON说明书——给AI看的菜单。告诉AI:我叫什么、我能干什么、我需要什么参数。
主流格式就两种,本质一模一样:
OpenAI风格——
{
"name": "get_weather",
"description": "查询指定城市的当前天气",
"parameters": {
"city": {
"type": "string",
"description": "城市名称,例如北京、上海"
}
}
}
MCP风格——换个字段名,结构不变:
{
"name": "get_weather",
"description": "查询指定城市的当前天气",
"input_schema": {
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
}
}
}
② 执行代码——真正干活的。AI不看它,也不执行它:
def get_weather(city):
"""收到调用指令后,系统执行这个函数"""
import requests
response = requests.get(f"https://api.weather.com?city={city}")
return response.json()["weather"]
一句话:JSON说明书是菜单,代码是厨师。AI对着菜单点菜,厨师在厨房做菜。AI不知道厨房长什么样,厨师不关心AI怎么点的。
MCP:给工具配一个标准接口
理解了工具调用,MCP就好讲了。
不同平台定义工具的方式不一样。OpenAI有一套格式,Anthropic有另一套。给Codex写的工具换到Claude Code上,描述格式得重写——这就是碎片化。
MCP做了一件事:制定统一的工具描述标准。像USB接口——不管什么设备,插上去就能用。
MCP不是工具调用本身,是工具调用的标准化实现。它在技术栈里的位置:
AI模型层 ← 只知道"工具调用"这个能力,不知道MCP
↓
Agent/应用层 ← MCP在这一层:发现工具、执行、返回结果
↓
MCP工具服务器 ← 用MCP协议包装的具体工具
一个极简的MCP工具服务器示例——完整可运行的天气查询:
# ===== 真正干活的函数(跟MCP无关) =====
def get_weather(city):
"""模拟天气查询,实际项目替换为真实API"""
weather_db = {
"北京": "多云,25°C",
"上海": "晴,30°C",
"深圳": "小雨,22°C",
}
return weather_db.get(city, f"找不到{city}的天气数据")
# ===== MCP包装(固定模板) =====
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server") # 创建MCP服务器
@mcp.tool() # 声明:这是一个工具
def query_weather(city: str) -> str: # city: str → 参数自动变成工具定义
"""查询指定城市的当前天气。参数:city 城市名称"""
return get_weather(city) # 调用真正干活的函数
# ===== 启动 =====
if __name__ == "__main__":
mcp.run() # 启动,等AI来调用
然后在配置文件里加三行:
[mcp_servers.weather]
command = "python"
args = ["weather_server.py"]
重启。AI就能调用这个工具了。
一个普通函数,加上 @mcp.tool(),再注册到配置——就是一个即插即用的工具。格式固定,照抄就行。
提示工程回答"怎么说"。上下文工程回答"怎么管记忆"。工具调用回答"怎么让AI动手"。
从说到管到做,一条递进线。
对话AI的边界不在它"知道"什么,在它"能够得着"什么。提示词写得再好,也只能在对话框里优化;工具调用把墙推倒了——让AI的手伸出去,摸到真实的世界。
这是从聊天工具到生产力的跨越。