Knowledge Base

Designing Effective Tools for AI Agents

An AI agent’s performance is fundamentally determined by the quality of its tools. Tools are the functions an agent can invoke to interact with the outside world, retrieve information, or perform actions. Designing these tools effectively is more critical than simply providing a list of functions; it requires careful consideration of how an LLM perceives, selects, and uses them.

The guiding principle is human readability: a tool that is easy for a human developer to understand and use will also be easier for an AI agent to use correctly.

1. Core Principles of Tool Definition

The foundation of a good tool is its definition. The LLM relies entirely on the function signature and docstring to understand a tool’s purpose and how to call it.

Specificity in Naming

Tool names should be unambiguous and clearly state their function.
Bad: search(query) – Is this a keyword search, a vector search, or a web search?
Good: keyword_database_search(query: str) – This name is explicit about the type of search and the data source.

Descriptive Docstrings

The docstring is the primary instruction manual for the agent. It must be comprehensive.
Purpose: A concise, one-sentence summary of what the tool does.
Parameters: List every input parameter, its data type, and a clear description of what it represents.
Return Value: Describe the structure and content of the output, especially if it’s a complex object or a formatted string.

Strongly-Typed Inputs and Outputs

Using explicit types helps the agent construct valid calls and correctly interpret the results. For complex outputs, use a dataclass to define a clear structure.

from dataclasses import dataclass

@dataclass
class KeywordSearchResult:
    """Data structure for a single keyword search result."""
    document_id: str
    filename: str
    content_snippet: str

def keyword_database_search(query: str) -> list[KeywordSearchResult]:
    """
    Performs a keyword search against the document database.

    Args:
        query (str): The keywords to search for.

    Returns:
        list[KeywordSearchResult]: A list of matching documents, each containing its ID, filename, and a content snippet.
    """
    # ... database search logic ...
    results = search_database(query)
    return results
````

## 2. Enhancing Tool Functionality

Beyond the definition, the internal logic of the tool can be optimized for agentic use.

### Single Responsibility Principle

Each tool should have one, clear, singular purpose. Vague or multi-purpose tools confuse the agent, leading it to use them at the wrong time or with incorrect parameters.

### Provide Clean, Parsed Outputs

Never return raw, messy data (like a full JSON dump from an API) directly to the agent. Instead, parse the raw result within the tool and format it into a clean, structured string that highlights only the necessary information.

```python
def _parse_search_results_for_llm(results: list[KeywordSearchResult]) -> str:
    """Parses search results into a clean string for an LLM."""
    if not results:
        return "No results found."

    output_lines = [f"Found {len(results)} documents:"]
    for i, result in enumerate(results):
        output_lines.append(
            f"\nDocument {i+1}:\n"
            f"  ID: {result.document_id}\n"
            f"  Filename: {result.filename}\n"
            f"  Snippet: {result.content_snippet}"
        )
    return "\n".join(output_lines)

# The main tool function would then use this parser before returning
# return _parse_search_results_for_llm(results)

Avoid Context Overload

Tools that can return a large number of items (e.g., database queries) can easily overwhelm the agent’s context window. Always include parameters to limit the output.

  • max_results: Add a parameter to limit the number of items returned (e.g., max_results=10).
  • Sorting: Include sorting options so the agent can request the most relevant items (e.g., sort_by='relevance').

Informative Error Handling

When a tool fails, the error message is a critical piece of feedback for the agent’s reasoning loop. Generic errors are useless; informative errors enable self-correction.

def some_api_tool(api_key: str):
    try:
        # ... logic to call an external API ...
        pass
    except RateLimitError as e:
        raise RuntimeError("API Error: Rate limit exceeded. Wait before retrying the call.")
    except AuthenticationError as e:
        raise ValueError("API Error: Invalid API key provided. Do not try again with the same key.")
    except Exception as e:
        raise RuntimeError(f"An unexpected error occurred: {e}. Check your inputs and try again.")

3. Managing Tool Availability in the Prompt

How and when you present tools to an agent is as important as the tools themselves.

  • Avoid Overwhelming the Agent: Do not provide an exhaustive list of every possible tool for every task. This makes it harder for the agent to choose the correct one.
  • Use Contextual Tools: If possible, dynamically provide only the tools that are relevant to the agent’s current task or state.
  • Structure the Prompt: Clearly separate the tool definitions from the rest of the prompt. Using a dedicated section with markdown headings or XML tags (e.g., <tools>...</tools>) helps the agent focus on them.

About the Author: Adam Bernard

Designing Effective Tools for AI Agents
Adam Bernard is a digital marketing strategist and SEO specialist building AI-powered business intelligence systems. He's the creator of the Strategic Intelligence Engine (SIE), a multi-agent framework that transforms business knowledge into autonomous, AI-driven competitive advantages.

Let’s Connect

Ready to Build Your Own Intelligence Engine?

If you’re ready to move from theory to implementation and build a Knowledge Core for your own business, I can help you design the engine to power it. Let’s discuss how these principles can be applied to your unique challenges and goals.