Advanced

Tool calling

A model can't touch the world by itself. Give it tools, and it learns to ask for them, your code does the real work and feeds the result back into the loop.

What you will see

I could not find that order. Can you double-check the number?

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

A model cannot touch the world on its own, so I give it a named tool, run the real function it asks for, and feed the result back into the loop. When the tool fails, I explain instead of crashing.

1

I describe one tool with its arguments and print which one the model picked and the value it sent along.

tools = [{"name": "get_order", "params": {"order_id": "string"}}] response = '{"tool": "get_order", "args": {"order_id": "123"}}' pick = json.loads(response) print("Tool called: " + pick["tool"]) print("Arguments: " + pick["args"]["order_id"])

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Tool called: get_order Arguments: 123
The model never needs the data; it only requests the tool. You always run the real work, never the request itself.
2

I run the real function for the given order id and print the status it returns back into the conversation.

class Order: def __init__(self, status): self.status = status def get_order(order_id): return Order("shipped") args = {"order_id": "123"} result = get_order(args["order_id"]) print("Order status: " + str(result.status))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Order status: shipped
Feed the tool result back into the loop so the model can decide the next step instead of stopping mid-task.
3

Tools fail sometimes. I let the lookup raise, catch it, and print a clear message instead of letting the conversation break.

class OrderNotFound(Exception): pass def get_order(order_id): raise OrderNotFound() try: result = get_order("123") print("Status: " + str(result.status)) except OrderNotFound: print("I could not find that order. Can you double-check the number?")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
I could not find that order. Can you double-check the number?
A good agent catches the error and explains it. Never guess the answer, and never let a tool crash stop the whole flow.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

The model selects a tool, your code runs it safely, and the result feeds back into the loop.
1 Model chooses a tool with arguments
↓
2 Validate the call against permissions
↓
3 Run the tool and capture the result
↓
4 Send the result back to the model
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

tools = [{"name": "get_order", "params": {"order_id": "string"}}] sets nothing on screen
response = '{"tool": "get_order", "args": {"order_id": "123"}}' sets nothing on screen
pick = json.loads(response) sets nothing on screen
print("Tool called: " + pick["tool"]) Tool called: get_order
print("Arguments: " + pick["args"]["order_id"]) Arguments: 123
Why this works
The function-calling loop

Tools turn a model into an agent. The loop is:

1. Model sees tools
2. Model asks for one
3. You run it safely
4. Send result back
5. Repeat until done

Only allow tools the user would actually want. A model with a delete_database tool is a serious risk.

Sandboxing tool calls

Run every tool behind a permission check:

if tool.name in TOOL_PERMISSIONS[role]:
    run(tool)
else:
    escalate()

Tools are power. Scope each tool to who can use it.

Log every call with arguments so you can review what the agent did and why.

Examples

Describing a tool

Tell the model a tool exists

tools = [
  {"name": "get_order",
   "params": {"order_id": "string"}}
]
response = model.choose_tool(prompt, tools)
# {"tool": "get_order", "args": {"order_id": "123"}}

The model picks a tool and passes arguments. Your code runs the real function and sends the result back.

Why it works: the model never needs the data; it just requests it.

Running the result back

Feed the tool answer into the loop

result = get_order(args["order_id"])
continue_conversation(f"Order status: {result.status}")

This closes the agent loop: think, call tool, observe result, think again.

Each tool call is one step toward the goal.

Handling a tool error

Handle a tool error gracefully

try:
    result = get_order(args['order_id'])
    continue_conversation('Status: ' + str(result.status))
except OrderNotFound:
    continue_conversation('I could not find that order. Can you double-check the number?')

Tools fail sometimes. A good agent catches the error and explains it instead of crashing or guessing.

Why it works: graceful failures keep the conversation going.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Intent & entity extraction
Previous Lesson
Intent & entity extraction
Next Lesson
Multi-agent systems
Multi-agent systems →