Ollama Python Tool Calling
A minimal local tool-calling loop for Ollama: register Python functions, pass schemas to the model, execute selected calls, and feed tool results back into chat.
Problem
Local LLM applications need a controlled way to call real Python functions instead of relying on text-only responses.
Outcome
A small function registry and execution loop for Ollama tool calls.
Implementation evidence
The solution is backed by inspectable code
This solves the local tool-use loop: define trusted Python functions, expose them to Ollama, run only registered calls, and return the function output to the model.
Code
from ollama import chat
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers."""
return int(a) + int(b)
def subtract_two_numbers(a: int, b: int) -> int:
"""Subtract two numbers."""
return int(a) - int(b)
AVAILABLE_FUNCTIONS = {
"add_two_numbers": add_two_numbers,
"subtract_two_numbers": subtract_two_numbers,
}
def run_tool_chat(user_message, model="llama3.2"):
messages = [{"role": "user", "content": user_message}]
response = chat(
model,
messages=messages,
tools=[add_two_numbers, subtract_two_numbers],
)
if not response.message.tool_calls:
return response.message.content
messages.append(response.message)
for tool_call in response.message.tool_calls:
function_to_call = AVAILABLE_FUNCTIONS.get(tool_call.function.name)
if function_to_call is None:
messages.append({
"role": "tool",
"name": tool_call.function.name,
"content": f"Function {tool_call.function.name} is not registered.",
})
continue
output = function_to_call(**tool_call.function.arguments)
messages.append({
"role": "tool",
"name": tool_call.function.name,
"content": str(output),
})
final_response = chat(model, messages=messages)
return final_response.message.content
Usage
print(run_tool_chat("What is 30 plus 12?"))
For HTTP-facing tools, keep a registry rather than dispatching arbitrary function names:
FUNCTIONS = {}
def register_function(func):
FUNCTIONS[func.__name__] = func
return func
@register_function
def square(x: int) -> int:
return int(x) * int(x)
def call_registered_function(function_name, arguments):
if function_name not in FUNCTIONS:
return {"error": f"Function {function_name!r} not found"}, 404
result = FUNCTIONS[function_name](*arguments)
return {"result": result}, 200
Requirements
Install ollama and run a local model that supports tool calls, for example ollama pull llama3.2.
Source
The article points to working examples in ernanhughes/ollama-functions.
Full explanation
For the Flask version, external API examples, and security considerations, read: Beyond Text Generation: Coding Ollama Function Calls and Tools.