Guides
Tool calling
The full function-calling loop — declare tools, read tool_calls, feed results back, then let the model answer.
Tool calling takes two or more rounds: the model never executes a function. It only emits which function it wants and with what arguments; your code runs it, feeds the result back, and the model answers from there.
Round one: declare tools
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a given city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"],
},
},
}]
resp = client.chat.completions.create(
model="gpt-5.4",
messages=[{"role": "user", "content": "How warm is it in Beijing?"}],
tools=tools,
)
msg = resp.choices[0].message
print(msg.tool_calls)
# [ChatCompletionMessageToolCall(id='call_...', function=Function(name='get_weather', arguments='{"city":"Beijing"}'))]Round two: feed results back
import json
messages = [{"role": "user", "content": "How warm is it in Beijing?"}, msg]
for call in msg.tool_calls:
result = get_weather(**json.loads(call.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(model="gpt-5.4", messages=messages, tools=tools)
print(final.choices[0].message.content)tool_call_id must match the id the model returned — that pairing is the only way to associate
results when the model calls several tools in parallel.
Common issues
| Symptom | Cause |
|---|---|
| Model answers without calling a tool | The description is too vague, or the model has no tool support |
arguments is not valid JSON | Wrap json.loads in try/except and feed the error back so the model retries |
| Parallel results get mixed up | Pair by tool_call_id, never by array index |
Tool calling vs structured output
Tool calling is "let the model decide what to call"; structured output is "make the answer fit a schema". See Structured output.