Skip to content

Using Tools with Agents

Tools let your agent take actions — call APIs, search databases, run calculations. This page covers how to attach tools to agents and how the tool-calling loop works. For deep dives on each tool type, see the Tools section.

Passing Tools to an Agent

Pass tools to an agent — the agent handles the entire tool-calling loop:

from fastaiagent import Agent, FunctionTool, RESTTool, LLMClient

agent = Agent(
    name="assistant",
    system_prompt="Use tools to answer questions.",
    llm=LLMClient(provider="openai", model="gpt-4.1"),
    tools=[
        FunctionTool(name="calculate", fn=lambda expr: str(eval(expr))),
        RESTTool(name="weather", url="https://api.weather.com/v1", method="GET"),
    ],
)

result = agent.run("What is 15% of 230, and what's the weather in Tokyo?")
# Agent calls both tools and combines results

How the Tool-Calling Loop Works

  1. Agent sends messages + tool schemas to the LLM
  2. LLM decides to call one or more tools (or respond directly)
  3. SDK executes the tools and sends results back to the LLM
  4. LLM generates a final response using the tool results
  5. This loop repeats up to max_iterations times

By default the tool calls in a single turn run sequentially. See Parallel tool execution to opt into concurrency.

Parallel Tool Execution

When the LLM emits several tool calls in one turn, you can run them concurrently instead of one after another. It's opt-in (default off) via AgentConfig:

from fastaiagent import Agent, AgentConfig, LLMClient

agent = Agent(
    name="assistant",
    llm=LLMClient(provider="openai", model="gpt-4o"),
    tools=[get_weather, get_flights],
    config=AgentConfig(
        parallel_tools=True,    # run a turn's tool calls concurrently (default: False)
        max_parallel_tools=4,   # cap concurrency with a semaphore (default: 4)
    ),
)

A turn's wall-clock becomes the slowest tool instead of the sum of all of them. Results are re-ordered by call index, so message history and result.tool_calls stay deterministic regardless of which tool finishes first.

When it stays sequential

For correctness, the parallel path is used only when none of these order/identity-sensitive features are engaged; otherwise the agent transparently falls back to sequential execution:

  • a checkpointer is configured (per-tool checkpoints / crash recovery / HITL),
  • middleware is attached (wrap_tool sees calls in order),
  • managed governance is enrolled (agent_id is set for policy/approvals).

Tools that share mutable RunContext state are your responsibility to make concurrency-safe when you enable this.

The @tool Decorator

For quick tool creation:

from fastaiagent.tool import tool

@tool(name="calculate")
def calculate(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

# Use directly — it's a FunctionTool
result = calculate.execute({"expression": "2 + 2"})

Tool Types Overview

Type Use Case Example
FunctionTool Wrap any Python function FunctionTool(name="calc", fn=my_func)
RESTTool Call an HTTP API RESTTool(name="weather", url="https://api.weather.com/v1", method="GET")
MCPTool Connect to MCP server MCPTool(name="search", server_url="http://localhost:3000")

All three (and @tool) accept an optional replay_class (read_only / idempotent / side_effecting, default side_effecting) that controls inject-vs-execute during Agent Replay. See Tools → Replay safety.

ToolResult

Every tool execution returns a ToolResult:

Field Type Description
output Any The tool's return value
error str \| None Error message if execution failed
success bool True if no error
metadata dict Extra info (e.g., HTTP status code for REST tools)
result = tool.execute({"query": "test"})
if result.success:
    print(result.output)
else:
    print(f"Error: {result.error}")

Controlling Tool Usage

Configure how the agent uses tools via AgentConfig:

from fastaiagent import Agent, AgentConfig

agent = Agent(
    name="configured-agent",
    llm=LLMClient(provider="openai", model="gpt-4.1"),
    tools=[my_tool],
    config=AgentConfig(
        max_iterations=5,       # Max tool-calling loop iterations (default: 10)
        tool_choice="auto",     # "auto", "required", "none"
        parallel_tools=False,   # Run a turn's tool calls concurrently (default: False)
        max_parallel_tools=4,   # Concurrency cap when parallel_tools is on (default: 4)
    ),
)
tool_choice Behavior
"auto" LLM decides whether to use tools (default)
"required" LLM must call at least one tool
"none" LLM cannot call tools

Context & Dependency Injection

Tools that need runtime dependencies (DB connections, API clients, user sessions) can use RunContext for clean, type-safe dependency injection. See Context & Dependency Injection for the full guide.


Next Steps