Contents
Ollama Tool Calling Tutorial: Call Functions to Get Data and Take Action
Language models don't know today's weather or what's in your database, and they're not good at precise calculations. Tool calling (also called function calling) lets a model "ask your program to run a function," then answer based on the result.
Only models that support tools can use them. Their pages on ollama.com list tools, for example qwen3, llama3.1, gpt-oss, and mistral.
How it works
1. You: question + descriptions of available tools ──────────▶ Model
2. Model: "Call get_weather(city=\"Taipei\")" ◀──
3. Your program runs get_weather("Taipei") → "24°C, partly cloudy"
4. You: send the result back ─────────────────────▶ Model
5. Model: "It's 24°C and partly cloudy in Taipei. Consider bringing a light jacket."
The model itself doesn't execute any code; it only decides which tool to call and what parameters to pass. Your program does the actual work, so you have full control over what it can do.
Python example
The Ollama Python package lets you pass Python functions directly as tools. It automatically generates tool descriptions from the function's type annotations and docstring:
from ollama import chat
def get_weather(city: str) -> str:
"""
Get the current weather in a city.
Args:
city: City name, for example "Taipei"
Returns:
Weather description
"""
fake_data = {"Taipei": "24°C, partly cloudy", "Kaohsiung": "29°C, sunny"}
return fake_data.get(city, "No data found")
tools = {"get_weather": get_weather}
messages = [{"role": "user", "content": "What's the weather like in Taipei and Kaohsiung right now?"}]
response = chat(model="qwen3:8b", messages=messages, tools=[get_weather])
messages.append(response.message)
# The model may request multiple tool calls at once
for call in response.message.tool_calls or []:
fn = tools[call.function.name]
result = fn(**call.function.arguments)
print(f"Call {call.function.name}({call.function.arguments}) → {result}")
messages.append({"role": "tool", "tool_name": call.function.name, "content": str(result)})
final = chat(model="qwen3:8b", messages=messages)
print(final.message.content)
Key points:
- Add the model's response, including
tool_calls, back tomessages. - Add each tool's result with
role: "tool". - Call
chatagain so the model can answer based on the results.
Let the model decide how many times to call tools
Some questions require several consecutive tool calls, such as "check the exchange rate, then calculate the amount." Use a loop until the model stops requesting tool calls:
from ollama import chat
def get_exchange_rate(currency: str) -> float:
"""Get the exchange rate for one unit of foreign currency to New Taiwan dollars. currency, for example USD or JPY."""
return {"USD": 32.1, "JPY": 0.21}.get(currency.upper(), 0.0)
def multiply(a: float, b: float) -> float:
"""Multiply a by b."""
return a * b
tools = {f.__name__: f for f in [get_exchange_rate, multiply]}
messages = [{"role": "user", "content": "About how many New Taiwan dollars is 350 US dollars?"}]
while True:
response = chat(model="qwen3:8b", messages=messages, tools=list(tools.values()))
messages.append(response.message)
if not response.message.tool_calls:
break
for call in response.message.tool_calls:
result = tools[call.function.name](**call.function.arguments)
messages.append({"role": "tool", "tool_name": call.function.name, "content": str(result)})
print(response.message.content)
In practice, set a maximum number of calls to prevent the model from getting stuck in an infinite loop.
Define tools with the REST API
When you aren't using the Python package, describe tools with JSON Schema:
curl http://localhost:11434/api/chat -d '{
"model": "qwen3:8b",
"messages": [{ "role": "user", "content": "What's the weather in Taipei?" }],
"stream": false,
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather in a city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" }
},
"required": ["city"]
}
}
}]
}'
The response's message.tool_calls:
"tool_calls": [
{ "function": { "name": "get_weather", "arguments": { "city": "Taipei" } } }
]
After running the tool, add the result as {"role": "tool", "tool_name": "get_weather", "content": "24°C, partly cloudy"} to messages and send the request again.
Write clear tool descriptions
The model relies entirely on the name and description to decide when to use a tool and how to fill in its parameters:
- Choose function names that clearly describe their purpose:
search_ordersis better thanquery1. - State when to use the tool, the parameter formats, and examples in the description.
- Keep parameters to a minimum and use explicit types. Use an enum when possible.
- Don't offer too many tools. Small models can easily choose the wrong one when given a dozen or more.
Security considerations
- The model can trigger tools based on user input. If users can control the input, they can indirectly call your tools.
- Don't provide tools that can run arbitrary code or SQL, or delete data.
- For operations with side effects (payments, sending email, deletion), ask the user to confirm before execution.
- Validate the parameters the model provides; don't trust them blindly.
Updated October 12, 2026