Chapter 4: Tools β Giving the Agent Hands
Why This Matters
By the end of this chapter, you'll be able to design a tool from scratch β write the function, describe it so the LLM knows when to use it, handle errors gracefully, and let the agent choose between multiple tools on its own. You'll also understand the one design principle that separates tools that work from tools that confuse the model: the description is for the LLM, not for a human.
A Tool Is Just a Function
Let's strip away the mystery. A tool is a Python function. It takes arguments, returns a result. That's it. The "tool" part isn't special β it's the description you write alongside it that lets the LLM know it exists and when to call it.
In Chapter 3, our calculator tool was a function called
calculate that took an expression string. Now let's build a
real one for Rosa's bookshop: search_books.
Building a Real Tool
Step 1: Write the function
Step 2: Describe it for the LLM
Now the crucial part. The description is not documentation for a human developer. It's instructions for the LLM β telling it what this tool does, when to use it, and what arguments to provide. Write it like you're explaining it to a smart intern who has never seen your codebase.
The Description Is Everything
Here's the principle that will save you hours of debugging: the LLM decides whether to call a tool based entirely on the description. It can't read your function body. It can't infer what the tool does from its name alone. It reads the description string and decides: "Is this relevant to what the user asked?"
Compare two descriptions for the same function:
With "Search books," the LLM might use it when someone asks "search for the word 'books' in this essay" β wrong tool, wrong time. With the good description, it knows: this is for bookshop inventory queries. The description is the LLM's only map of what the tool does. Make it precise.
Here's a function for a tool that sends an email. Write a good description for it β one that tells the LLM when to use it and what each argument means. Then compare with the answer below.
A good answer: "Send an email to a recipient. Use when the user explicitly asks to send, email, or forward a message. 'to' is the email address, 'subject' is the email subject line, 'body' is the full email content." β Note the word "explicitly." You don't want the agent sending emails on a whim.
Multiple Tools: The Agent Chooses
Here's where it gets fun. An agent can have many tools, and the LLM
decides which to use based on the user's request. Let's give Rosa's agent
two tools: search_books and calculate (for
totalling up a customer's order).
Now watch what happens with different questions. The agent picks the right tool on its own:
The agent didn't need an if-statement saying "if the question is about books, call search_books." The LLM read both tool descriptions, understood the user's intent, and picked the right one. This is the autonomy from Chapter 1, now with real consequences.
When Tools Fail: Error Handling
Tools fail. A database is down. An API returns an error. The LLM passes bad arguments. The question isn't if β it's when, and what you do about it.
The good news: the agent loop is self-correcting by design. If a tool returns an error message, that message goes back into the conversation as the tool result. The LLM reads "Error: connection refused" and can decide what to do β retry, try a different tool, or tell the user something went wrong.
tools list. It can't invent a tool. If it tries to call something that doesn't exist, your else: return f"Unknown tool: {name}" branch catches it, and the error goes back to the LLM, which will try something else.Where People Come Unstuck
Mistake #1: Returning objects instead of strings
Your tool returns a dict, a list, a custom object. The LLM gets... a
stringified Python repr like {'title': 'Dune', 'price': 14.0}.
It can sort of read it, but it's messy and error-prone. Always return a
clean, human-readable string. If you need structure, use JSON β but plain
text is usually better for the LLM to reason about.
Mistake #2: Vague descriptions
We've hit this twice now because it's the #1 cause of tool problems. "Search books" is not a description. "Search the bookshop inventory by author or price, returning matching titles with stock counts" is. The LLM only knows what you tell it.
Mistake #3: Tools that do too much
A tool called do_everything that searches books, calculates
totals, sends emails, and updates inventory. The LLM can't reason about
when to use it because it does everything and nothing. Keep tools
single-purpose. One tool, one job. The agent loop
handles combining them.
Rosa's bookshop needs three more tools. For each, sketch out: the function signature, a good description, and one scenario where the agent would use it.
1. add_to_cart β adds a book to a customer's order.
2. check_stock β returns how many copies of a specific title are in stock.
3. place_order β finalises a cart and returns an order number.
Think about: what arguments does each need? How would you describe each so the LLM knows when to use it? Which of these might the agent chain together β and in what order?
Chapter Summary
- A tool is a Python function plus a description. The function does the work; the description tells the LLM when to use it.
- Tools always return strings. The LLM reads text, not objects. If you need structure, use JSON or plain readable text.
- The description is the most important part of a tool. It tells the LLM what the tool does and when to use it. Write it for the LLM, not for a human developer.
- An agent can have multiple tools. The LLM reads all the descriptions and picks the right one based on the user's request β no if-statements needed.
- Tools fail, and that's fine. Return error messages as strings; the LLM reads them and adapts. Never let a tool crash the loop silently.
- Keep tools single-purpose. One tool, one job. Let the agent loop orchestrate; let tools execute.
The Bookshop Agent. Combine the agent loop from
Chapter 3 with the search_books and
calculate tools from this chapter. Run it against Rosa's
inventory with this conversation:
1. "Do you have any books by Le Guin under β¬15?"
2. "What about Frank Herbert?"
3. "If I buy one Le Guin and one Herbert, what's the total?"
4. "And how many of each are left in stock?"
Watch the agent pick the right tool for each question, chain the
results together, and answer naturally. Then add one more tool β
check_stock(title) β and watch it learn to use it for
question 4 without you changing the loop. That's the power of tools:
add one, and the agent just gets smarter.