> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: Agents
description: Configure LLM agents with instructions, models, tools, handoffs, and loop limits.
last_verified: 2026-07-24
---



An **Agent** is an LLM that runs in a loop: it reads your instructions, calls tools when it needs to, and keeps going until it has a final answer. You give it a model, a purpose, and optionally a set of tools. The SDK handles the loop, tool dispatch, and result collection.

---

## Creating an Agent



**Python:**

Three things are required: a name, a model, and instructions.

```python
from agnt5 import Agent

agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="You are a research assistant. Answer questions with cited sources.",
)
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | `str` | yes | Identifier for this agent |
| `model` | `str` | yes | Model to use, as `"provider/model-name"` (e.g. `"openai/gpt-4o"`, `"anthropic/claude-3-5-sonnet-20241022"`) |
| `instructions` | `str` | yes | System prompt that tells the agent its role and behavior |
| `tools` | `list` | no | Tools the agent can call (see [Tools](#tools)) |
| `built_in_tools` | `list[BuiltInTool]` | no | Provider-hosted tools like web search (see [Built-in Tools](#built-in-tools)) |
| `handoffs` | `list` | no | Agents to delegate to (see [Handoffs](#handoffs)) |
| `max_iterations` | `int` | no | Max reasoning loops before stopping. Default: `10` |
| `temperature` | `float` | no | Sampling temperature (0–1). Lower = more deterministic. Default: `0.7` |
| `max_tokens` | `int` | no | Maximum tokens in the model response |
| `top_p` | `float` | no | Top-p nucleus sampling (0–1) |
| `model_config` | `ModelConfig` | no | Custom endpoint config: `base_url`, `api_key`, `timeout`, `headers` |

**Example with generation settings:**

```python
agent = Agent(
    name="analyst",
    model="openai/gpt-4o-mini",
    instructions="You are a precise data analyst.",
    temperature=0.2,      # more focused, less creative
    max_tokens=4096,
    max_iterations=5,
)
```





**TypeScript:**

Three things are required: a name, a model, and instructions. `model` takes an `LM` provider client; the specific model string goes in `modelName`.

```typescript
const agent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You are a research assistant. Answer questions with cited sources.',
});
```

| Option | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | yes | Identifier for this agent |
| `model` | `LM \| LanguageModel` | yes | Provider client, e.g. `LM.openai()`, `LM.anthropic()` |
| `modelName` | `string` | no | Model to use, as `"provider/model-name"` (e.g. `"openai/gpt-4o"`, `"anthropic/claude-3-5-sonnet-20241022"`) |
| `instructions` | `string` | yes | System prompt that tells the agent its role and behavior |
| `tools` | `Tool[]` | no | Tools the agent can call (see [Tools](#tools)) |
| `builtInTools` | `BuiltInTool[]` | no | Provider-hosted tools like web search (see [Built-in Tools](#built-in-tools)) |
| `handoffs` | `(Agent \| Handoff)[]` | no | Agents to delegate to (see [Handoffs](#handoffs)) |
| `maxIterations` | `number` | no | Max reasoning loops before stopping. Default: `10` |
| `temperature` | `number` | no | Sampling temperature (0–1). Lower = more deterministic. Default: `0.7` |
| `sandbox` | `Sandbox` | no | Isolated file/code execution workspace (see [Tools](/docs/build/tools.md)) |

`maxTokens`, `topP`, and a custom `modelConfig` (base URL / API key overrides) aren't exposed on `AgentOptions` yet — configure those through the `LM` provider client instead.

**Example with generation settings:**

```typescript
const agent = new Agent({
  name: 'analyst',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You are a precise data analyst.',
  temperature: 0.2, // more focused, less creative
  maxIterations: 5,
});
```





**Go:**

Three things are required: a name, a model, and instructions, passed as `NewAgent` options.

```go
console.log(result.output);
```

`result.output` is the agent's final text response. `result.toolCalls` lists every tool call made along the way.





**Go:**

`Run` is the only execution method — call it with the invocation's `*agnt5.Context` and an `AgentInput`.

```go
result, err := agent.Run(ctx, agnt5.AgentInput{Message: "What causes northern lights?"})
if err != nil {
    return Output{}, err
}
fmt.Println(result.Response)
```

`result.Response` is the agent's final text response. `result.ToolCalls` is the number of tool calls made along the way; `result.ToolCallDetails` lists each one.



### `stream()`: receive events as they happen



**Python:**

Use `stream()` when you want to show output progressively or react to tool calls in real time.

```python
async for event in agent.stream("What causes northern lights?"):
    if event.event_type == "lm.content_block.delta":
        print(event.content, end="", flush=True)
```

Key event types:

| Event type | When it fires |
|---|---|
| `agent.started` | Agent loop begins |
| `lm.content_block.delta` | A chunk of the LLM response arrives |
| `lm.content_block.completed` | LLM response block is complete |
| `tool_call.started` | A tool call begins |
| `tool_call.completed` | A tool call finishes |
| `agent.completed` | Agent loop ends. Final answer is available |





**TypeScript:**

Use `stream()` when you want to react to agent lifecycle and tool-call events in real time. The TypeScript SDK streams lifecycle events, not token-by-token output — the final answer arrives on the `agent.completed` event.

```typescript
for await (const event of agent.stream('What causes northern lights?')) {
  if ('eventType' in event && event.eventType === 'tool_call.started') {
    console.log(`Calling tool: ${event.toolName}`);
  } else if (!('eventType' in event)) {
    // Final value from the generator is the AgentResult, not an event
    console.log(event.output);
  }
}
```

Key event types:

| Event type | When it fires |
|---|---|
| `agent.started` | Agent loop begins |
| `agent.iteration.started` | A reasoning iteration begins |
| `agent.iteration.completed` | A reasoning iteration finishes |
| `tool_call.started` | A tool call begins |
| `tool_call.completed` | A tool call finishes |
| `tool_call.failed` | A tool call raised an error |
| `agent.completed` | Agent loop ends. Final `AgentResult` is available |





**Go:**

`Agent.Run` has no separate streaming method yet — it emits the same lifecycle events (`agent.started`, `agent.iteration.started`, `tool_call.started`/`completed`/`failed`, `agent.completed`) internally via `ctx.Emit`, but there's no public iterator to consume them from `Run` the way Python's `stream()` or TypeScript's `stream()` do. For token-by-token output from inside a function or workflow, use `ctx.Output(delta string)` directly, which streams as `output.delta` events.



---

## Tools



**Python:**

Tools give your agent the ability to take actions: call an API, run code, or search the web. Pass them via `tools` (custom functions or other agents) or `built_in_tools` (provider-hosted tools like web search).





**TypeScript:**

Tools give your agent the ability to take actions: call an API, run code, or search the web. Pass them via `tools` (custom functions or other agents) or `builtInTools` (provider-hosted tools like web search).





**Go:**

Tools give your agent the ability to take actions: call an API, run code, or search the web. Pass them via `WithAgentTools`. There are no provider-hosted built-in tools (web search, code interpreter) in the Go SDK yet — every tool needs a handler you write.



See [Tools](/docs/build/tools.md) for the full reference. Covers custom tools, sandbox workspaces, built-in tools, and human-in-the-loop tools.

---

## Multi-Agent Patterns

### Agents as tools

The calling agent invokes a specialist, gets the result back, and continues its own reasoning. Good for workflows where the coordinator needs to synthesize results from multiple specialists.



**Python:**

```python
from agnt5 import Agent

research_agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions="You research topics and return concise summaries.",
)

analyst_agent = Agent(
    name="analyst",
    model="openai/gpt-4o-mini",
    instructions="You analyze data and identify trends.",
)

coordinator = Agent(
    name="coordinator",
    model="openai/gpt-4o-mini",
    instructions="Use the researcher and analyst to answer complex questions.",
    tools=[research_agent, analyst_agent],   # each becomes ask_researcher / ask_analyst
)

result = await coordinator.run("What are the latest trends in renewable energy?")
print(result.output)
```





**TypeScript:**

```typescript
const researchAgent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You research topics and return concise summaries.',
});

const analystAgent = new Agent({
  name: 'analyst',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You analyze data and identify trends.',
});

const coordinator = new Agent({
  name: 'coordinator',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Use the researcher and analyst to answer complex questions.',
  tools: [researchAgent, analystAgent], // exposed to the model as tools named `researcher` / `analyst`
});

const result = await coordinator.run('What are the latest trends in renewable energy?');
console.log(result.output);
```





**Go:**

`WithAgentTools` only accepts `Tool` values, not `*Agent` directly — wrap the specialist's `Run` call in `agnt5.NewTool` to expose it as a tool.

```go
researchAgent, err := agnt5.NewAgent("researcher",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("You research topics and return concise summaries."),
)

researchTool, err := agnt5.NewTool("ask_researcher", func(c context.Context, input map[string]any) (any, error) {
    question, _ := input["question"].(string)
    result, err := researchAgent.Run(ctx, agnt5.AgentInput{Message: question})
    return result.Response, err
}, agnt5.WithToolDescription("Ask the research specialist a question"))

coordinator, err := agnt5.NewAgent("coordinator",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Use the researcher to answer complex questions."),
    agnt5.WithAgentTools(researchTool),
    agnt5.WithAgentMaxTurns(3), // NewAgent defaults to 1, which leaves no turn to answer after a tool call
)

result, err := coordinator.Run(ctx, agnt5.AgentInput{Message: "What are the latest trends in renewable energy?"})
fmt.Println(result.Response)
```



### Handoffs

The calling agent transfers control entirely to a specialist. The specialist runs and its output is returned as the final result. Good for routing: when the triage agent's job is done once it knows who should handle the request.



**Python:**

```python
from agnt5 import Agent, handoff

billing_agent = Agent(
    name="billing",
    model="openai/gpt-4o-mini",
    instructions="You handle billing questions and subscription changes.",
)

technical_agent = Agent(
    name="technical",
    model="openai/gpt-4o-mini",
    instructions="You handle technical support and bug reports.",
)

triage_agent = Agent(
    name="triage",
    model="openai/gpt-4o-mini",
    instructions="Route the user to the right specialist.",
    handoffs=[
        handoff(billing_agent, "Transfer when the user has a billing or payment question"),
        handoff(technical_agent, "Transfer when the user has a technical or product issue"),
    ],
)

result = await triage_agent.run("My payment failed but I was still charged.")
print(result.output)          # billing_agent's response
print(result.handoff_to)      # "billing"
```

`handoff()` parameters:

| Parameter | Type | Default | Description |
|---|---|---|---|
| `agent` | `Agent` | required | The target agent |
| `description` | `str` | agent's instructions | What the LLM sees when deciding whether to hand off |
| `tool_name` | `str` | `transfer_to_{name}` | Custom name for the transfer tool |
| `pass_full_history` | `bool` | `True` | Whether to pass the full conversation to the target agent |

To hand off without customization, pass the agent directly (no `handoff()` call needed):

```python
triage_agent = Agent(
    name="triage",
    model="openai/gpt-4o-mini",
    instructions="Route the user to the right specialist.",
    handoffs=[billing_agent, technical_agent],   # simple form
)
```





**TypeScript:**

```typescript
const billingAgent = new Agent({
  name: 'billing',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You handle billing questions and subscription changes.',
});

const technicalAgent = new Agent({
  name: 'technical',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You handle technical support and bug reports.',
});

const triageAgent = new Agent({
  name: 'triage',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Route the user to the right specialist.',
  handoffs: [
    handoff(billingAgent, 'Transfer when the user has a billing or payment question'),
    handoff(technicalAgent, 'Transfer when the user has a technical or product issue'),
  ],
});

const result = await triageAgent.run('My payment failed but I was still charged.');
console.log(result.output);      // billingAgent's response
console.log(result.handoffTo);   // "billing"
```

`handoff(agent, description?, toolName?, passFullHistory?)` is a positional function, not an options object:

| Parameter | Type | Default | Description |
|---|---|---|---|
| `agent` | `Agent` | required | The target agent |
| `description` | `string` | agent's instructions | What the LLM sees when deciding whether to hand off |
| `toolName` | `string` | `transfer_to_{name}` | Custom name for the transfer tool |
| `passFullHistory` | `boolean` | `true` | Whether to pass the full conversation to the target agent |

To hand off without customization, pass the agent directly (no `handoff()` call needed):

```typescript
const triageAgent = new Agent({
  name: 'triage',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Route the user to the right specialist.',
  handoffs: [billingAgent, technicalAgent], // simple form
});
```





**Go:**

```go
billingAgent, err := agnt5.NewAgent("billing",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("You handle billing questions and subscription changes."),
)

technicalAgent, err := agnt5.NewAgent("technical",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("You handle technical support and bug reports."),
)

billingHandoff, err := agnt5.NewHandoff(billingAgent,
    agnt5.WithHandoffDescription("Transfer when the user has a billing or payment question"),
)
technicalHandoff, err := agnt5.NewHandoff(technicalAgent,
    agnt5.WithHandoffDescription("Transfer when the user has a technical or product issue"),
)

triageAgent, err := agnt5.NewAgent("triage",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Route the user to the right specialist."),
    agnt5.WithAgentHandoffs(billingHandoff, technicalHandoff),
)

result, err := triageAgent.Run(ctx, agnt5.AgentInput{Message: "My payment failed but I was still charged."})
fmt.Println(result.Response)   // billingAgent's response
fmt.Println(result.HandoffTo)  // "billing"
```

`NewHandoff(agent *Agent, opts ...HandoffOption) (Handoff, error)` options:

| Option | Type | Default | Description |
|---|---|---|---|
| `WithHandoffDescription` | `string` | `"Transfer to " + agent.Name` | What the LLM sees when deciding whether to hand off |
| `WithHandoffToolName` | `string` | `transfer_to_{name}` | Custom name for the transfer tool |
| `WithHandoffFullHistory` | `bool` | `true` | Whether to pass the full conversation to the target agent |
| `WithHandoffMetadata` | `map[string]any` | none | Arbitrary metadata attached to the handoff |

Unlike Python/TypeScript, there's no simple form — `WithAgentHandoffs` takes `...Handoff`, not `...*Agent`, so every target needs a `NewHandoff` call even to accept all the defaults.


