Skip to content

案例: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 校验
  • 超时与重试
  • 审计日志
  • 并行执行控制
  • 幂等设计

聚焦 OpenClaw、tool_calls、function calling 与 agent 实战。