Guides
Tool calling
Tool calling (function calling) lets the model ask your code to run functions you define, such as looking up the weather, querying a database or running code. The model only decides which tool to call and with what arguments; your code does the actual work.
How it works
- Describe the available tools in the request with JSON Schema.
- When the model needs a tool, it returns the tool name and arguments instead of an answer.
- Your code runs the tool and sends the result back as a new message.
- The model uses the result to answer. If it needs another tool, it asks again, looping until it's done.
OpenAI-compatible format
import json
from openai import OpenAI
client = OpenAI(api_key="sk-...", base_url="https://api.flyrouter.ai/v1")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name, e.g. London"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What's the weather in Shanghai right now?"}]
resp = client.chat.completions.create(model="gpt-6.1-sol", messages=messages, tools=tools)
msg = resp.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = {"city": args["city"], "weather": "cloudy", "temperature": 22} # replace with your implementation
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
resp = client.chat.completions.create(model="gpt-6.1-sol", messages=messages, tools=tools)
print(resp.choices[0].message.content)Anthropic native format
Claude tools are defined with input_schema. When the model wants a tool it returns a tool_use content block, and you send the result back in a tool_result block:
from anthropic import Anthropic
client = Anthropic(api_key="sk-...", base_url="https://api.flyrouter.ai")
tools = [{
"name": "get_weather",
"description": "Get the current weather for a city",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
messages = [{"role": "user", "content": "What's the weather in Shanghai right now?"}]
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=4096, tools=tools, messages=messages)
while resp.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": resp.content})
results = []
for block in resp.content:
if block.type == "tool_use":
output = '{"weather": "cloudy", "temperature": 22}' # replace with your implementation
results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
messages.append({"role": "user", "content": results})
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=4096, tools=tools, messages=messages)
print(next(b.text for b in resp.content if b.type == "text"))Tips
- Write clear descriptions: what the tool does, when to use it and what the arguments look like matter more than any parameter.
- Parallel calls: the model may request several tools at once. Run them all and return every result in a single message.
- Return failures too: in the OpenAI format, return an error description; in the Anthropic format, set
"is_error": trueon thetool_result. Don't drop them. - Always parse arguments as JSON: use
json.loads/JSON.parserather than matching on the raw string.
Newer Claude models don't support forced tool use
claude-opus-5-5, claude-sonnet-5-5 and claude-fable-5-1 reject tool_choice set to any or a specific tool (400). Use the default auto and say in the prompt which tool to call. To guarantee arguments match the schema exactly, add "strict": true to the tool definition.