Skip to main content
📖 You are viewing: Conceptual Guide - Learn how tools work and create your ownSee also: API Specification
What You’ll Learn: How the tool system works under the hood, how LLMs “see” and choose which tools to use, how to write tools that LLMs understand correctly, best practices for parameters, return values, and error handling, and common pitfalls and how to avoid them.

What Are Tools?

LLMs are brilliant at language but can’t interact with the real world. Tools are the bridge: Tools give your agent hands to interact with the world.

The Tool Contract: What the LLM Actually Sees

Here’s the key insight: The LLM never sees your Python code. It only sees a “contract” describing the tool. When you write this:
The decorator registers an entry in the _TOOL_REGISTRY dictionary. Today the registry entry looks like this (source: src/gaia/agents/base/tools.py:79-87):
What’s actually captured: The function name, the full docstring (as description), and a {type, required} entry per parameter inferred from type hints (only str, int, float, bool, tuple, dict are recognised — other annotations fall back to "unknown"). Per-argument descriptions from the Args: docstring block and parameter default values are not parsed into the registry today, so write concise, self-contained docstrings to give the LLM all the context it needs.

How LLMs Choose Tools

When a user asks a question, the LLM goes through a decision process: This is why clear docstrings matter! If your description doesn’t match user intent, the LLM won’t choose your tool.

Building Effective Tools

Step 1: Start Simple

Begin with the most basic tool structure:
What makes this effective:
  • Clear function name (calculate) matches what it does
  • Type hint (str) tells LLM what to pass
  • Return type (float) sets expectations
  • Docstring explains when to use it (“when the user asks to calculate”)
  • Args section describes the expected format

Step 2: Add Parameters with Defaults

Make tools flexible with optional parameters:
How defaults work with LLMs:

Step 3: Handle Complex Types

For structured data, use type hints to guide the LLM:
The LLM understands:
  • List[str] → needs to pass a list of strings
  • Optional[List[str]] → can be omitted or set to null
  • The Args descriptions show example formats

Return Value Patterns

What you return matters—the LLM uses it to form responses.

Pattern 1: Success with Data

LLM receives: {"status": "success", "user": {"id": 123, "name": "Alice", ...}} LLM responds: “Alice ([email protected]) has the Admin role.”

Pattern 2: Error with Guidance

LLM receives: {"status": "error", "error": "No user found...", "suggestion": "..."} LLM responds: “I couldn’t find a user with ID 999. Would you like to search by name instead?”

Pattern 3: Partial Results

Why this matters: Prevents context overflow while telling the LLM there’s more.

Standard Response Format

All GAIA tools should return responses in a standardized format that helps the LLM understand the result and how to use it. This is especially important for tools that return structured JSON data.

The GAIA Response Pattern

Why This Format Matters

When tools return raw JSON without context, the LLM may:
  • Echo the JSON directly instead of summarizing it
  • Return structured data in its answer instead of human-readable text
  • Misunderstand how to interpret the data
The instruction field explicitly tells the LLM what to do with the data.

Field Reference

Complete Example

With this format, the LLM responds:
“Your system is running well. CPU usage is at 23%, memory is using 8.2 GB of 16 GB (51%), and you have 245 GB of free disk space.”
Instead of echoing the JSON:
{"cpu_percent": 23, "memory_gb_used": 8.2, ...}
MCP Tools: When using MCPClientMixin, external MCP tool responses are automatically wrapped in this GAIA format with status, message, data, and instruction fields.

The Power of Good Docstrings

Your docstring teaches the LLM when and how to use your tool. Compare:
Problems: LLM doesn’t know what kind of search, q parameter name is unclear, no guidance on when to use it, might conflict with other search tools.

Docstring Anatomy


Error Handling: Return, Don’t Raise

Critical Rule: Tools should never raise exceptions. Always return error information as data.

Why?

When a tool raises an exception:
  1. The agent’s reasoning loop may crash
  2. The LLM doesn’t get useful error information
  3. The user sees a technical error instead of helpful guidance

The Pattern

Now the LLM can respond helpfully:

Common Pitfalls and Solutions

Symptom: User asks to search code, but LLM calls search_web.Cause: Tool descriptions are too similar or vague.Solution: Add explicit differentiation:
Symptom: Tool expects integer, receives string like “5”.Cause: Missing or unclear type hints.Solution: Always use type hints and validate:
Symptom: Agent becomes slow or gives inconsistent answers.Cause: Tool returns massive amounts of data that overflow context.Solution: Limit and summarize output:
Symptom: LLM says “I don’t have a tool for that” when you do.Cause: Tool is defined but not registered with the agent.Solution: Make sure tool is inside _register_tools():
Symptom: Emails sent, files deleted, or data modified when user was just asking a question.Cause: Destructive tools need safeguards.Solution: Add confirmation or dry-run modes:
Now the LLM will preview first:

Practice Challenge

Build a Database Query Tool

Create a tool that: (1) Accepts a natural language query about users, (2) Translates it to a database operation, (3) Returns structured results, (4) Handles errors gracefully.Requirements: Clear docstring with usage examples, type hints on all parameters, graceful error handling, reasonable result limits.
Use a dict to simulate database records. Include examples in the docstring to guide the LLM. Return both data and metadata (count, any filtering applied).
Why this solution works:
  1. Clear docstring with examples: LLM knows exactly how to map user requests to parameters
  2. Optional parameters: LLM can use any combination of filters
  3. Input validation: Invalid values return helpful error messages
  4. Graceful limits: Prevents returning too much data
  5. Rich metadata: LLM knows total count, filters used, and has suggestions
  6. Error handling: Catches unexpected errors with helpful messages

Deep Dive: Tool Schema Generation

When you decorate a function with @tool, Python introspection extracts a minimal schema (see src/gaia/agents/base/tools.py:19-98 for the authoritative implementation):
Notable implementation details:
  • The full docstring is stored once as description. There’s no per-argument docstring parsing today — the LLM sees the whole docstring together with a {type, required} map.
  • Default values are not stored on the parameter entries. required is a boolean derived from whether the parameter has a default.
  • Generic annotations like list[str] or Optional[int] are not specialised — only the bare primitives (str, int, float, bool, tuple, dict) map to a known JSON type; anything else is reported as "unknown".
  • @tool(atomic=True) marks a tool as non-decomposable so the planner will not try to split it further.
  • @tool(display_label=...) sets a user-facing label for UI progress strips, and @tool(timeout=...) overrides the global per-tool execution limit (seconds). Both default to None and are stored on the registry entry.

Key Takeaways

LLM Sees the Contract

Function name, type hints, and docstring are all the LLM knows. Write them for the LLM, not just humans.

Docstrings Drive Selection

“Use this tool when…” phrases directly influence when the LLM chooses your tool.

Return Errors as Data

Never raise exceptions. Return structured error info so the LLM can respond helpfully.

Limit Output Size

Large returns overflow context. Truncate, summarize, and indicate when there’s more.

Next Steps

Agent System

Understand how agents use tools in the reasoning loop

Tool Mixins

Use pre-built tool collections for common tasks

Best Practices

Advanced patterns for production tools