09. Multi-Tool Agents
Chapter 9 of 16 · 15 min
When an agent has access to multiple tools, it must decide which tool to use and in what order. This introduces the tool selection problem: the model must reason about tool capabilities and pick the right one for each sub-task.
Multi-tool routing
import ollama
from typing import List, Dict, Any
def multi_tool_agent(task: str, tools: List[Tool], max_turns: int = 10) -> str:
tool_map = {t.name: t for t in tools}
tool_schemas = [t.to_openai_schema() for t in tools]
messages = [
{"role": "system", "content": (
"You have access to multiple tools. Choose the right tool for each step. "
"You may call multiple tools if they are independent. "
"When you have all the information needed, respond with your final answer."
)},
{"role": "user", "content": task}
]
for turn in range(max_turns):
response = ollama.chat(
model="llama3.2",
messages=messages,
tools=tool_schemas
)
if not response.message.tool_calls:
messages.append({"role": "assistant", "content": response.message.content})
return response.message.content
for call in response.message.tool_calls:
fn = call.function
if fn.name not in tool_map:
result = f"Error: Unknown tool '{fn.name}'"
else:
result = tool_map[fn.name].invoke(**fn.arguments)
messages.append({"role": "assistant", "content": "", "tool_calls": [call]})
messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
return "Max turns exceeded"
Parallel tool calls
Ollama supports parallel tool calls in a single response. When the model returns multiple tool_calls, execute them concurrently:
import concurrent.futures
if response.message.tool_calls:
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {
executor.submit(tool_map[call.function.name].invoke, **call.function.arguments): call
for call in response.message.tool_calls
if call.function.name in tool_map
}
for future in concurrent.futures.as_completed(futures):
call = futures[future]
try:
result = future.result()
except Exception as e:
result = f"Error: {e}"
messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
Tool selection failures
Models sometimes call the wrong tool. A web_search tool cannot answer "calculate 15% of 200" because the model chose search instead of calculator. Mitigate this by:
- Writing distinct, non-overlapping tool descriptions
- Adding explicit lists of tool capabilities in the system prompt
- Providing a
retrymechanism that asks the model to reconsider
EXERCISE
Register three tools (web search, calculator, file reader) and test the agent with a compound query that requires all three. Log which tool was called at each step and verify the execution order makes sense.