Agent Tools#
Tools allow agents to interact with the outside world. By providing tools, you enable your agents to perform actions such as executing code, fetching data from APIs, querying databases, or performing complex calculations.
Under the hood, a tool is a standard Python function accompanied by a schema that describes its purpose, inputs, and outputs to the underlying Large Language Model (LLM).
Creating Agent Tools#
The easiest way to create a tool is by using the @tool decorator. This decorator automatically parses your function's signature, type hints, and docstring to generate a schema that the LLM can understand.
For the best results, always provide clear type hints and a descriptive docstring. The LLM relies heavily on these to know when and how to invoke your tool.
Once defined, you can equip an agent with this capability by passing the tool to the tools list during the agent's initialization.
from autogen.beta import Agent
agent = Agent(name="ShippingAssistant", tools=[calculate_shipping_cost])
Note
For simpler use cases, you can pass an undecorated Python function directly to the agent's tools list. The framework will automatically convert it into a fully-fledged tool under the hood, extracting the schema from the signature and docstring just like the decorator would.
Registering Tools via a Decorator#
Alternatively, you can register a tool directly with an agent instance using its @my_agent.tool decorator.
This approach is particularly useful when you need to dynamically add capabilities to an agent after it has been created, or when you are logically organizing your code by attaching specific tools to specific agent instances.
Synchronous and Asynchronous Tools#
Agent interactions are naturally asynchronous. To support this, tools are executed within an asynchronous event loop by default. However, you have the flexibility to define your tool functions as either synchronous (def) or asynchronous (async def).
To ensure that heavy computational tasks or blocking I/O operations do not freeze the entire application, synchronous tools are automatically executed in a separate thread by default.
Disabling Threaded Execution#
If you have a synchronous function that executes very quickly (e.g., simple string manipulation or math) and you want to avoid the minor overhead of thread creation, you can disable threaded execution by passing sync_to_thread=False to the @tool decorator.
Warning
When sync_to_thread=False is set, the synchronous tool runs directly within the asynchronous context. If the function performs time-consuming operations (like network requests or large loops), it will block the entire event loop until it finishes, preventing other agents or asynchronous tasks from making progress.
Customizing Tool Schemas#
LLMs perform best when they have precise constraints and detailed instructions. The framework automatically generates a tool's schema from its function signature and docstring. For more granular control, you can define minimum/maximum values, enforce specific formats, or override the tool's name.
You can override the basic properties directly in the decorator:
Note
Explicitly setting the name and description overrides the automatically generated values.
Deep Schema Customization with Pydantic#
Under the hood, arguments are serialized and validated using Pydantic schemas. This means you can use standard pydantic.Field annotations to deeply customize individual schema parameters, providing the LLM with strict guidelines on what values are acceptable.
Complete Custom Schema Example#
A complete example combining custom tool properties and strict parameter validation looks like this:
This configuration generates the following detailed JSON schema, ensuring the LLM understands exactly what inputs are required and valid:
{
"description": "Creates a new user profile in the database.",
"name": "create_user_profile",
"parameters": {
"properties": {
"username": {
"description": "The chosen username. Must be alphanumeric.",
"maxLength": 20,
"minLength": 3,
"title": "Username",
"type": "string"
},
"age": {
"description": "The user's age. Must be 18 or older.",
"minimum": 18,
"title": "Age",
"type": "integer"
}
},
"required": [
"username",
"age"
],
"type": "object"
}
}
Toolkits#
A Toolkit groups related tools into a single, reusable unit. Instead of passing individual tools one by one, you can bundle them into a toolkit and pass the whole collection to an agent. This is useful for organizing domain-specific capabilities (e.g., all database tools, all file-system tools) and sharing them across multiple agents.
A toolkit accepts both plain functions and @tool-decorated functions in its tools list. Plain functions are automatically converted, just like when passing them directly to an agent.
Registering Tools via a Decorator#
You can also add tools to a toolkit using the @toolkit.tool decorator, following the same pattern as @agent.tool:
The toolkit can then be passed to any number of agents:
Combining Toolkits with Standalone Tools#
You can freely mix toolkits and individual tools in an agent's tools list:
Execution Context#
Tools often need access to the broader execution context, such as injected dependencies, variables, or mechanisms for human-in-the-loop interactions. The AG2 framework supports these features natively.
Note
Under the hood, these contextual capabilities are powered by the FastDepends library, ensuring robust and FastAPI-like dependency management.
To access the execution context from within your tool, you can simply type-hint an argument with the Context object.
For more detailed information on specific context features, see Dependency Injection, Context Variables, Depends, Human-in-the-loop.