主题
02 · 工具调用
核心事实:模型请求调用工具,应用执行工具。工具名、描述、参数 Schema 是给模型看的菜单。
定义工具菜单
先看一个模拟天气工具,不连接真实天气接口:
python
from pydantic import BaseModel, Field
class WeatherArgs(BaseModel):
city: str = Field(min_length=1, max_length=40, description="城市名称")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询演示天气数据,不代表实时天气。支持北京、上海。",
"parameters": WeatherArgs.model_json_schema(),
},
}]required、properties 等 JSON Schema 细节由 Pydantic 生成。工具描述应该说明什么时候用、返回什么、哪些限制。
模型返回的不是调用结果
把 tools 与 messages 一起传给 client.chat.completions.create(),模型可能返回:
json
{
"role": "assistant",
"tool_calls": [{
"id": "call_example",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\"}"
}
}]
}注意 arguments 是 JSON 字符串。应用解析、校验、执行后,再返回:
json
{
"role": "tool",
"tool_call_id": "call_example",
"content": "{\"city\":\"北京\",\"temp_c\":22,\"demo\":true}"
}必须保留 tool_call_id 对应关系。模型一轮可能请求多个工具,每个调用都需要一个对应结果,然后才能继续请求模型。
交互看看消息如何变化
STEP 1 程序启动,先放入 system 消息,告诉模型它是谁、能做什么。
system
你是一个乐于助人的助手,可以使用工具。上面的 UI 是协议概念演示,不是可以直接发到 API 的完整 JSON。真实数组需要每个调用的 ID、type、function;下面手写 Agent 章节会完整保存 SDK 返回对象。
tool_choice
auto:模型自行决定是否调用。none:本轮不允许工具调用。- 指定函数:强制调用某个工具,具体能力与格式以平台文档为准。
仅仅传入工具,不保证模型一定会用。描述差、任务不匹配、平台不支持,都可能导致没有调用。
安全执行的四道门
- 工具名在程序白名单中。
- 参数通过 Pydantic 校验,限制长度、范围和额外字段。
- 程序验证权限和资源范围,例如文件只能来自指定练习目录。
- 可能产生外部影响的动作,执行前明确向用户展示并确认。
不要用 eval / exec 调工具
eval(arguments) 或 eval(function_name) 会把模型输出变成任意代码执行。用 JSON 解析 + 固定工具注册表。能运行 shell 的工具更需要独立沙箱,不能仅靠 Prompt 说「不要危险操作」。
练习
给天气工具加一个 unit 参数,只允许 celsius / fahrenheit。想一想:这是在 Schema 中提示模型就够了,还是执行时也要验证?
答案:两边都要,Schema 帮模型选择正确参数,程序校验保护执行。