Lesson 12 / 29

Designing Tools That Agents Use Well

Write tools with clear names, narrow scope and safe behaviour.

Tools are an interface for a model

The model chooses tools from their names, descriptions and argument schemas, so these are your prompt. Good tools are narrow (one clear job), well named (get_order_status, not do_stuff), documented (when to use, what each argument means, an example), return concise, structured results (not megabytes of text), and give actionable error messages the model can recover from ("order_id must be 6 digits"). Prefer a few good tools over many overlapping ones. Validate all arguments in code, enforce permissions per user, make write actions idempotent where possible, and never let a tool run arbitrary code or SQL built from model output without strict checks.

Weak versus strong tool definitions

The second version tells the model when to use the tool and how to recover from errors. Illustrative; not run here.

# weak
@tool
def lookup(x): 
    """Look something up."""
    ...

# strong
@tool
def get_order_status(order_id: str) -> dict:
    """Return the status of ONE customer order.
    Use when the user asks where an order is. order_id is exactly 6 digits, e.g. "481516".
    Returns {"status": "...", "eta": "..."} or {"error": "..."}."""
    if not (order_id.isdigit() and len(order_id) == 6):
        return {"error": "order_id must be exactly 6 digits"}   # recoverable message
    ...

Return errors as data

A clear error message in the tool result lets the model correct itself on the next step instead of crashing the run.

Quick check: Which tool design is better for an agent?

  • One huge tool that does everything
  • One narrow, well-named tool with a clear description and recoverable errors
  • Tools with no descriptions
  • Tools that return raw database dumps
Answer

One narrow, well-named tool with a clear description and recoverable errors — The model relies on names, descriptions and error messages to use tools well.