Agent

Full API reference for the Agent class — construct an agent, run it, register tools and output validators, and inspect its configuration.


Agent is the core user-facing class. You configure it once — its name, model, instructions, tools, and limits — then call run or arun to drive the agentic loop. The Agent itself is configuration; runtime state lives on the Context that each run creates.

Agent is a Pydantic BaseModel with extra="forbid", so passing an unknown keyword raises a validation error.

from agentroute import Agent

Constructor

Agent(
    name: str,
    *,
    model: str | Model | None = None,
    api_key: str | None = None,
    base_url: str | None = None,
    instructions: str | None = None,
    description: str | None = None,
    tools: list[Tool | Callable] | None = None,
    max_turns: int = 10,
    max_cost: float | None = None,
    retries: int = 1,
    output: type | None = None,
    output_mode: str = "auto",
    memory: MemoryProto | None = None,
    history: History | None = None,
)
ParameterTypeDefaultDescription
namerequiredstrHuman-readable identifier for the agent. Used in error messages and (in a later phase) deployment.
modelstr | Model | NoneNoneThe model to drive the loop. A string such as "claude-sonnet-4" is passed to resolve_model; you can also pass a concrete Model instance. If None, a model must be resolvable from configuration at run time.
api_keystr | NoneNoneAPI key for the resolved model. Falls back to environment variables or ~/.agentroute/config when omitted. See Models for the full resolution order.
base_urlstr | NoneNoneOverride the endpoint for an OpenAI-compatible provider. Leave unset to use the default routing (OpenRouter for bare model names).
instructionsstr | NoneNoneSystem prompt that defines the agent's behavior. Sent as the leading system message on every turn.
descriptionstr | NoneNoneShort description of what the agent does. Metadata only; not sent to the model.
toolslist[Tool | Callable] | NoneNoneTools to register at construction. Each item is either a Tool or a plain callable, which is wrapped with tool automatically. Registering two tools with the same name raises ValueError.
max_turnsint10Maximum number of model turns before the loop aborts with ErrorMaxTurns. Each turn is one model call plus any tool executions it requests.
max_costfloat | NoneNoneOptional spend ceiling in USD. When accumulated cost exceeds it, the loop raises ErrorBudget. None means no cost limit.
retriesint1How many times a Retry signal — raised from a tool body or an output validator — is honored before giving up.
outputtype | NoneNoneA type (typically a Pydantic model) describing the structured result. When set, result.output is an instance of this type instead of a string. See Structured output.
output_modestr"auto"How structured output is produced: "auto" (use tool mode when output is set, otherwise text), "tool", or "text".
memoryMemoryProto | NoneNoneLong-lived memory backend for conversation and facts. Pass Memory or MemorySQLite. Exposed to tools as ctx.memory.
historyHistory | NoneNoneConversation compaction policy applied as the transcript grows, e.g. HistorySlidingWindow. None keeps the full transcript.
Configuration vs. state

The Agent holds configuration only. Per-run state — usage, message transcript, retry counter, deps — lives on a fresh Context created for each call to run / arun. The same agent can be reused across many concurrent runs.

Methods

run

Run the agent synchronously. Wraps arun in asyncio.run.

def run(self, prompt: str, *, deps: Any = None, **kwargs: Any) -> Result
ParameterTypeDefaultDescription
promptrequiredstrThe user message that starts the conversation.
depsAnyNoneArbitrary dependencies made available to tools as ctx.deps — a database handle, an API client, request-scoped config.
**kwargsAnyForwarded to the underlying run loop.
ReturnsResult
The run result. result.output is the final text, or an instance of output when structured output is configured. print(result) shows the text.
Raises
ErrorMaxTurnswhen the loop exceeds max_turns.
ErrorBudgetwhen accumulated cost exceeds max_cost.
Use arun inside async code

run calls asyncio.run, which fails if an event loop is already running. Inside an async function, await agent.arun(...) instead.

arun

The async entry point. run is a thin synchronous wrapper around it.

async def arun(self, prompt: str, *, deps: Any = None, **kwargs: Any) -> Result
ParameterTypeDefaultDescription
promptrequiredstrThe user message that starts the conversation.
depsAnyNoneArbitrary dependencies exposed to tools as ctx.deps.
**kwargsAnyForwarded to the underlying run loop.
ReturnsResult
The run result, same shape as run.
Raises
ErrorMaxTurnswhen the loop exceeds max_turns.
ErrorBudgetwhen accumulated cost exceeds max_cost.
import asyncio
from agentroute import Agent
 
agent = Agent(name="assistant", model="claude-sonnet-4")
 
async def main():
    result = await agent.arun("Write a haiku about routing.")
    print(result)
 
asyncio.run(main())

tool

Decorator that registers fn as a tool on this agent. Works both bare (@agent.tool) and with arguments (@agent.tool(name="...")). See Tools for the full model and tool for the standalone decorator.

def tool(
    self,
    fn: Callable | None = None,
    /,
    *,
    name: str | None = None,
    description: str | None = None,
    needs_approval: bool | Callable[[Context, dict], bool] = False,
    timeout: float | None = None,
) -> Tool | Callable
ParameterTypeDefaultDescription
fnCallable | NoneNoneThe function to register. Supplied automatically when used as a bare decorator; omit it when calling with keyword arguments.
namestr | NoneNoneOverride the tool name. Defaults to the function name.
descriptionstr | NoneNoneOverride the tool description. Defaults to the function docstring.
needs_approvalbool | Callable[[Context, dict], bool]FalseWhether the tool requires approval before running. Pass a callable to decide per call from the context and arguments.
timeoutfloat | NoneNonePer-call timeout in seconds. None means no timeout.
ReturnsTool
The registered Tool. Registering a name that already exists raises ValueError.
from agentroute import Agent, Context
 
agent = Agent(name="weather-bot", model="claude-sonnet-4")
 
@agent.tool
def get_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"{city}: 18C, clear"
 
@agent.tool(needs_approval=True, timeout=10.0)
def send_email(to: str, body: str) -> str:
    """Send an email. Requires approval before running."""
    return f"sent to {to}"

A parameter annotated as Context is auto-injected and excluded from the model-visible schema:

@agent.tool
def save_note(ctx: Context, text: str) -> str:
    """Persist a note to agent memory."""
    # ctx is injected automatically; the model only sees `text`.
    return "saved"

output_validator

Decorator that registers a validator run against the structured output before it is returned. Use it to enforce invariants and push the model to correct itself.

def output_validator(self, fn: Callable) -> Callable

The validator signature is (ctx: Context, output: OutputModel) -> OutputModel. Return the (possibly modified) output to accept it, or raise Retry(message) to send the model a hint and ask for another attempt. Retries are bounded by the agent's retries setting. See Errors and retries.

ParameterTypeDefaultDescription
fnrequiredCallableThe validator function. Receives the Context and the parsed output, returns the validated output.
ReturnsCallable
The same function, so it can be stacked with other decorators.
from pydantic import BaseModel
from agentroute import Agent, Context, Retry
 
class Invoice(BaseModel):
    total: float
 
agent = Agent(name="biller", model="claude-sonnet-4", output=Invoice)
 
@agent.output_validator
def must_be_positive(ctx: Context, output: Invoice) -> Invoice:
    if output.total <= 0:
        raise Retry("total must be a positive amount")
    return output

Properties

tools_map

Read-only view of the tools registered on the agent, keyed by name.

@property
def tools_map(self) -> dict[str, Tool]
The agent's registered tools by name. Treat it as read-only; register tools through the constructor or the tool decorator.
agent = Agent(name="bot", model="claude-sonnet-4")
 
@agent.tool
def ping() -> str:
    """Health check."""
    return "pong"
 
print(list(agent.tools_map))   # ['ping']
print(agent.tools_map["ping"]) # Tool(name='ping', ...)

output_validators

Read-only copy of the registered output validators, in registration order.

@property
def output_validators(self) -> list
Returnslist
A fresh list of the registered validator functions. Mutating the returned list does not affect the agent.

Examples

A minimal agent with one tool and a budget guard:

quickstart.py
from agentroute import Agent, Context
 
agent = Agent(
    name="assistant",
    model="claude-sonnet-4",
    instructions="You are a concise, helpful assistant.",
    max_turns=6,
    max_cost=0.50,
)
 
@agent.tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b
 
result = agent.run("What is 21 + 21?")
print(result)            # 42
print(result.usage)      # Usage(input_tokens=..., output_tokens=..., ...)

Structured output with a validator:

structured.py
from pydantic import BaseModel
from agentroute import Agent, Context, Retry
 
class City(BaseModel):
    name: str
    country: str
    population: int
 
agent = Agent(name="geo", model="claude-sonnet-4", output=City)
 
@agent.output_validator
def sane_population(ctx: Context, output: City) -> City:
    if output.population <= 0:
        raise Retry("population must be greater than zero")
    return output
 
result = agent.run("Give me data for Lisbon.")
city = result.output          # City instance
print(city.name, city.population)

Handling the loop limits:

limits.py
from agentroute import Agent, ErrorMaxTurns, ErrorBudget
 
agent = Agent(name="bounded", model="claude-sonnet-4", max_turns=3, max_cost=0.10)
 
try:
    result = agent.run("Solve this step by step, calling tools as needed.")
    print(result)
except ErrorMaxTurns as e:
    print(f"hit turn limit: {e.turn}/{e.limit}")
except ErrorBudget as e:
    print(f"over budget: ${e.spent:.4f} > ${e.limit:.2f}")

Source: agent.py