案例:OpenAI 完整 Tool Loop
这一页给一个最完整、最接近真实生产逻辑的 OpenAI tool calling 闭环。
目标
用户输入:“帮我查一下上海天气,并告诉我是否适合出门。”
系统提供一个工具:get_weather(city)。
完整示例
python
from openai import OpenAI
import json
client = OpenAI()
def get_weather(city: str):
# 这里通常会调用真实 API
if city == "Shanghai":
return {
"city": "Shanghai",
"temperature_c": 24,
"condition": "cloudy",
"aqi": 68
}
return {"error": f"unsupported city: {city}"}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市天气和空气质量信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市英文名,例如 Shanghai"
}
},
"required": ["city"]
}
}
}]
messages = [
{"role": "user", "content": "帮我查一下上海天气,并告诉我是否适合出门。"}
]
# 第一轮:让模型决定是否调用工具
resp = client.chat.completions.create(
model="gpt-4.1",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = resp.choices[0].message
if not msg.tool_calls:
print("模型没有调用工具,直接回答:", msg.content)
raise SystemExit
# 把 assistant 带 tool_calls 的消息加回对话
messages.append({
"role": "assistant",
"content": msg.content,
"tool_calls": [tc.model_dump() for tc in msg.tool_calls]
})
# 执行所有工具调用
for tc in msg.tool_calls:
tool_name = tc.function.name
try:
args = json.loads(tc.function.arguments)
except json.JSONDecodeError as e:
result = {
"ok": False,
"error_code": "INVALID_JSON_ARGUMENTS",
"message": str(e)
}
else:
if tool_name == "get_weather":
result = get_weather(**args)
else:
result = {
"ok": False,
"error_code": "UNKNOWN_TOOL",
"message": f"unknown tool: {tool_name}"
}
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 第二轮:让模型基于工具结果生成最终自然语言答复
final_resp = client.chat.completions.create(
model="gpt-4.1",
messages=messages,
tools=tools
)
print(final_resp.choices[0].message.content)这个例子体现了什么
tool_calls是模型请求,不是执行结果- 工具执行在宿主侧完成
- 结果必须通过
tool_call_id关联回去 - 第二轮请求才通常拿到面向用户的最终自然语言输出
生产环境还应补什么
- 参数 schema 校验
- 超时与重试
- 审计日志
- 并行执行控制
- 幂等设计