Showing the first 18000 characters. Load the complete file to read the remainder.
# AI SDK documentation
> The TypeScript toolkit for building AI applications and agents.
## When to use AI SDK
- Category: Developer tools
- Audience: JavaScript and TypeScript developers
Common use cases:
- Generate text and structured data
- Build agents and chat interfaces
- Integrate language model providers
## Documentation
---
title: Building Agents
description: Complete guide to creating agents with the ToolLoopAgent.
url: "https://ai-sdk.dev/docs/agents/building-agents"
docs_index: /llms.txt
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
The ToolLoopAgent provides a structured way to encapsulate LLM configuration, tools, and behavior into reusable components. It handles the agent loop for you, allowing the LLM to call tools multiple times in sequence to accomplish complex tasks. Define agents once and use them across your application.
## Why Use the ToolLoopAgent Class?
When building AI applications, you often need to:
- **Reuse configurations** - Same model settings, tools, and prompts across different parts of your application
- **Maintain consistency** - Ensure the same behavior and capabilities throughout your codebase
- **Simplify API routes** - Reduce boilerplate in your endpoints
- **Type safety** - Get full TypeScript support for your agent's tools and outputs
The ToolLoopAgent class provides a single place to define your agent's behavior.
## Creating an Agent
Define an agent by instantiating the ToolLoopAgent class with your desired configuration:
```ts
import { ToolLoopAgent } from 'ai';
const myAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: 'You are a helpful assistant.',
tools: {
// Your tools here
},
});
```
## Configuration Options
The ToolLoopAgent accepts all the same settings as `generateText` and `streamText`. Configure:
### Model and System Instructions
```ts
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: 'You are an expert software engineer.',
});
```
### Tools
Provide tools that the agent can use to accomplish tasks:
```ts
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const codeAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
tools: {
runCode: tool({
description: 'Execute Python code',
inputSchema: z.object({
code: z.string(),
}),
execute: async ({ code }) => {
// Execute code and return result
return { output: 'Code executed successfully' };
},
}),
},
});
```
### Context and Agent State
Use `runtimeContext` as the agent's shared runtime state. It flows through the
agent loop and is available in `prepareStep`, lifecycle callbacks, and final
results. If a tool needs server-side values such as credentials, scoped
permissions, or default settings, pass them through `toolsContext` and declare
them with the tool's `contextSchema`.
Set `runtimeContext` and `toolsContext` in the `ToolLoopAgent` constructor.
```ts
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
tools: {
searchTickets: tool({
description: 'Search support tickets',
inputSchema: z.object({
query: z.string(),
}),
contextSchema: z.object({
apiKey: z.string(),
accountId: z.string(),
}),
execute: async ({ query }, { context }) =>
searchTickets(query, context.accountId, context.apiKey),
}),
},
runtimeContext: {
requestId: 'req_abc',
escalated: false,
},
toolsContext: {
searchTickets: {
apiKey: process.env.SUPPORT_API_KEY!,
accountId: 'acct_123',
},
},
prepareStep: async ({ runtimeContext }) => {
if (runtimeContext.escalated) {
return { temperature: 0.1 };
}
return {};
},
});
const result = await agent.generate({
prompt: 'Find open billing tickets for this account.',
});
```
For context that changes with each invocation, use `callOptionsSchema` and
`prepareCall`. See [Configuring Call Options](/docs/agents/configuring-call-options).
Model call settings returned from `prepareStep`, such as `temperature`, apply
only to the current step. Later steps use the agent's top-level setting unless
they return another override.
For the full model, including sensitive context filtering and where each context
value is available, see [Runtime and Tool
Context](/docs/ai-sdk-core/runtime-and-tool-context).
### Tools That Use Experimental Sandboxes
Pass `experimental_sandbox` when an agent tool needs a command or code execution
environment. The experimental sandbox is a per-call value, so provide it to
`generate()`, `stream()`, or the agent UI stream helper that invokes the agent.
```ts {13,19-23,31}
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: 'You are a coding assistant. Use the shell tool when needed.',
tools: {
shell: tool({
description: 'Execute shell commands in the experimental sandbox.',
inputSchema: z.object({
command: z.string(),
workingDirectory: z.string().optional(),
}),
execute: async (
{ command, workingDirectory },
{ abortSignal, experimental_sandbox },
) => {
if (!experimental_sandbox) {
throw new Error('Experimental sandbox is not available');
}
return experimental_sandbox.run({
command,
workingDirectory,
abortSignal,
});
},
}),
},
});
const result = await agent.generate({
prompt: `Run the tests.\n\nSandbox:\n${experimental_sandbox.description}`,
experimental_sandbox,
});
```
The experimental sandbox description is not added to the model prompt automatically. Include
it in the prompt or instructions when the model needs to know environment
details. Passing an experimental sandbox does not sandbox the tool itself; the tool must
explicitly delegate operations to the experimental sandbox. See the
[Experimental Sandbox section in Tool Calling](/docs/ai-sdk-core/tools-and-tool-calling#experimental-sandbox)
for more details.
You can also require approval before a tool executes. Configure approval on the
`ToolLoopAgent` with `toolApproval`:
```ts
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
tools: {
runCode: tool({
description: 'Execute Python code',
inputSchema: z.object({
code: z.string(),
}),
execute: async ({ code }) => ({ output: code }),
}),
},
toolApproval: {
runCode: 'user-approval',
},
});
```
For manual approvals, automatic approvals and denials, dynamic policy functions,
and `useChat` integration, see [Tool
Approvals](/docs/agents/tool-approvals).
### Loop Control
By default, agents run for 20 steps (`stopWhen: isStepCount(20)`). In each step, the model either generates text or calls a tool. If it generates text, the agent completes. If it calls a tool, the AI SDK executes that tool.
You can configure `stopWhen` differently to allow more steps. After each tool execution, the agent triggers a new generation where the model can call another tool or generate text:
```ts
import { ToolLoopAgent, isStepCount } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
stopWhen: isStepCount(50), // Increase default from 20 to 50.
});
```
Each step represents one generation (which results in either text or a tool call). The loop continues until:
- A finish reasoning other than tool-calls is returned, or
- A tool that is invoked does not have an execute function, or
- A tool call needs approval, or
- A stop condition is met
You can combine multiple conditions:
```ts
import { ToolLoopAgent, isStepCount } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
stopWhen: [
isStepCount(20), // Maximum 20 steps
yourCustomCondition(), // Custom logic for when to stop
],
});
```
Learn more about [loop control and stop conditions](/docs/agents/loop-control).
### Tool Choice
Control how the agent uses tools:
```ts
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
tools: {
// your tools here
},
toolChoice: 'required', // Force tool use
// or toolChoice: 'none' to disable tools
// or toolChoice: 'auto' (default) to let the model decide
});
```
You can also force the use of a specific tool:
```ts
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
tools: {
weather: weatherTool,
cityAttractions: attractionsTool,
},
toolChoice: {
type: 'tool',
toolName: 'weather', // Force the weather tool to be used
},
});
```
### Structured Output
Define structured output schemas:
```ts
import { ToolLoopAgent, Output } from 'ai';
import { z } from 'zod';
const analysisAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
output: Output.object({
schema: z.object({
sentiment: z.enum(['positive', 'neutral', 'negative']),
summary: z.string(),
keyPoints: z.array(z.string()),
}),
}),
});
const { output } = await analysisAgent.generate({
prompt: 'Analyze customer feedback from the last quarter',
});
```
## Define Agent Behavior with System Instructions
System instructions define your agent's behavior, personality, and constraints. They set the context for all interactions and guide how the agent responds to user queries and uses tools.
### Basic System Instructions
Set the agent's role and expertise:
```ts
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions:
'You are an expert data analyst. You provide clear insights from complex data.',
});
```
### Detailed Behavioral Instructions
Provide specific guidelines for agent behavior:
```ts
const codeReviewAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: `You are a senior software engineer conducting code reviews.
Your approach:
- Focus on security vulnerabilities first
- Identify performance bottlenecks
- Suggest improvements for readability and maintainability
- Be constructive and educational in your feedback
- Always explain why something is an issue and how to fix it`,
});
```
### Constrain Agent Behavior
Set boundaries and ensure consistent behavior:
```ts
const customerSupportAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: `You are a customer support specialist for an e-commerce platform.
Rules:
- Never make promises about refunds without checking the policy
- Always be empathetic and professional
- If you don't know something, say so and offer to escalate
- Keep responses concise and actionable
- Never share internal company information`,
tools: {
checkOrderStatus,
lookupPolicy,
createTicket,
},
});
```
### Tool Usage Instructions
Guide how the agent should use available tools:
```ts
const researchAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: `You are a research assistant with access to search and document tools.
When researching:
1. Always start with a broad search to understand the topic
2. Use document analysis for detailed information
3. Cross-reference multiple sources before drawing conclusions
4. Cite your sources when presenting information
5. If information conflicts, present both viewpoints`,
tools: {
webSearch,
analyzeDocument,
extractQuotes,
},
});
```
### Format and Style Instructions
Control the output format and communication style:
```ts
const technicalWriterAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
instructions: `You are a technical documentation writer.
Writing style:
- Use clear, simple language
- Avoid jargon unless necessary
- Structure information with headers and bullet points
- Include code examples where relevant
- Write in second person ("you" instead of "the user")
Always format responses in Markdown.`,
});
```
## Using an Agent
Once defined, you can use your agent in three ways:
### Generate Text
Use `generate()` for one-time text generation:
```ts
const result = await myAgent.generate({
prompt: 'What is the weather like?',
});
console.log(result.text);
```
### Stream Text
Use `stream()` for streaming responses:
```ts
const result = await myAgent.stream({
prompt: 'Tell me a story',
});
for await (const chunk of result.textStream) {
console.log(chunk);
}
```
### Respond to UI Messages
Use `createAgentUIStreamResponse()` to create API responses for client applications:
```ts
// In your API route (e.g., app/api/chat/route.ts)
import { createAgentUIStreamResponse } from 'ai';
export async function POST(request: Request) {
const { messages } = await request.json();
return createAgentUIStreamResponse({
agent: myAgent,
uiMessages: messages,
});
}
```
### Lifecycle Callbacks
Agents provide lifecycle callbacks that let you hook into different phases of the agent execution.
These are useful for logging, observability, debugging, and custom telemetry.
```ts
const result = await myAgent.generate({
prompt: 'Research and summarize the latest AI trends',
onStart({ modelId }) {
console.log('Agent started', { modelId });
},
onStepStart({ stepNumber, modelId }) {
console.log(`Step ${stepNumber} starting`, { modelId });
},
onToolExecutionStart({ toolCall }) {
console.log(`Tool call starting: ${toolCall.toolName}`);
},
onToolExecutionEnd({ toolCall, toolExecutionMs, toolOutput }) {
console.log(
`Tool call finished: ${toolCall.toolName} (${toolExecutionMs}ms)`,
{
success: toolOutput.type === 'tool-result',
},
);
},
onStepEnd({ stepNumber, usage, performance, finishReason, toolCalls }) {
console.log(`Step ${stepNumber} completed:`, {
inputTokens: usage.inputTokens,
outputTokens: usage.outputTokens,
outputTokensPerSecond: performance.effectiveOutputTokensPerSecond,
stepTimeMs: performance.stepTimeMs,
finishReason,
toolsUsed: toolCalls?.map(tc => tc.toolName),
});
},
onEnd({ usage, steps }) {
console.log('Agent finished:', {
totalSteps: steps.length,
totalTokens: usage.totalTokens,
});
},
});
```
The available lifecycle callbacks are:
- **`onStart`**: Called once when the agent operation begins, before any LLM calls. Receives model info, messages, settings, and `runtimeContext`.
- **`onStepStart`**: Called before each step (LLM call). Receives the step number, model, messages being sent, tools, and prior steps.
- **`onToolExecutionStart`**: Called right before a tool's `execute` function runs. Receives the tool call object, messages, and `toolContext`.
- **`onToolExecutionEnd`**: Called right after a tool's `execute` function completes or errors. Receives the tool call, `toolExecutionMs`, and a `toolOutput` discriminated union (`type: 'tool-result'` with `output`, or `type: 'tool-error'` with `error`).
- **`onStepEnd`**: Called after each step finishes. Receives step results including usage, performance, finish reason, and tool calls.
- **`onEnd`**: Called when all steps are finished and the response is complete. Receives all step results, total usage, and `runtimeContext`.
For the full event data reference, see [Lifecycle Callbacks](/docs/ai-sdk-core/lifecycle-callbacks). `ToolLoopAgent` uses the same generation lifecycle event types for `onStart`, `onStepStart`, `onToolExecutionStart`, `onToolExecutionEnd`, `onStepEnd`, and `onEnd`.
#### Constructor vs. Method Callbacks
All lifecycle callbacks can be defined in the constructor for agent-wide tracking, in the `generate()`/`stream()` call for per-call tracking, or both. When both are provided, both are called (constructor first, then the method callback):
```ts
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-5.5",
onStepEnd: async ({ stepNumber, usage }) => {
// Agent-wide logging
console.log(`Agent step ${stepNumber}:`, usage.totalTokens);
},
});
// Method-level callback runs after constructor callback
const result = await agent.generate({
prompt: 'Hello',
onStepEnd: async ({ stepNumber, usage }) => {
// Per-call tracking (e.g., for billing)
await trackUsage(stepNumber, usage);
},
});
```
## End-to-end Type Safety
You can infer types for your agent's `UIMessage`s:
```ts
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
const myAgent = new ToolLoopAgent({
// ... configuration
});
// Infer the UIMessage type for UI components or persistence
export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent>;
```
Use this type in your client components with `useChat`:
```tsx title="components/chat.tsx"
'use client';
import { useChat } from '@ai-sdk/react';
import type { MyAgentUIMessage } from '@/agent/my-agent';
export function Chat() {
const { messages } = useChat<MyAgentUIMessage>();
// Full type safety for your messages and tools
}
```
## Next Steps
Now that you understand building agents, you can:
- Explore [workflow patterns](/docs/agents/workflows) for structured patterns using core functions
- Learn about [loop control](/docs/agents/loop-control) for advanced execution control
- See [manual loop examples](/cookbook/node/manual-agent-loop) for custom workflow implementations
---
For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)
For an index of all available documentation, see [/llms.txt](/llms.txt)
For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)
---
title: Configuring Call Options
description: Pass type-safe runtime inputs to dynamically configure agent behavior.
url: "https://ai-sdk.dev/docs/agents/configuring-call-opt
Measured from the saved file
Links and embedded code
UTF-8 text size
4.7 MB4891507 bytes
Token count
1147747o200k_base encoding
Markdown links
58751757 distinct URLs
Fenced code blocks
4272134042 lines in the file
Size measures the captured UTF-8 text, not compressed transfer size. Tokens are counted with a named tokenizer; other models can count differently. Neither measure establishes context compatibility or answer quality.
Project H1
AI SDK documentation
Opening blockquote
The TypeScript toolkit for building AI applications and agents.
Context architecture
How the content is divided
2273 H2 sections organize 5875 parsed Markdown links. 1079 links point to .md or .markdown paths; 1578 link occurrences use another hostname. Linked destinations have not been recursively validated.
H2 section
Links
UTF-8 bytes
Tokens
When to use AI SDKLine 5
0
206
42
DocumentationLine 15
1
550
121
Why Use the ToolLoopAgent Class?Line 28
0
491
99
Creating an AgentLine 39
0
307
81
Configuration OptionsLine 55
3
7640
1841
Define Agent Behavior with System InstructionsLine 331
0
2768
606
Using an AgentLine 431
1
4301
1034
End-to-end Type SafetyLine 563
0
656
157
Next StepsLine 592
7
1015
223
Why Use Call Options?Line 619
0
579
107
How It WorksLine 630
0
296
68
Basic ExampleLine 638
0
995
244
Modifying Agent SettingsLine 677
0
2631
648
Advanced PatternsLine 789
0
1957
487
Using with createAgentUIStreamResponseLine 867
0
444
108
Next StepsLine 889
6
1182
273
Show 64 more sections
H2 section
Links
UTF-8 bytes
Tokens
Stop ConditionsLine 920
1
4236
1022
Prepare StepLine 1048
0
7104
1669
Access Step InformationLine 1296
0
728
170
Forced Tool CallingLine 1322
0
1776
403
Manual Loop ControlLine 1365
5
1886
428
Three ApproachesLine 1429
3
628
114
Provider-Defined ToolsLine 1439
2
1708
390
Memory ProvidersLine 1475
9
7427
1792
Custom ToolLine 1678
5
1939
420
Agent State and ContextLine 1716
0
607
135
ToolLoopAgent ClassLine 1731
0
1527
351
Why Use the ToolLoopAgent?Line 1781
0
487
101
HarnessAgent ClassLine 1791
2
540
126
Terminal UILine 1804
1
483
121
Structured WorkflowsLine 1822
1
471
85
Next StepsLine 1833
10
1716
409
What you can enforceLine 1867
0
2092
433
InstallLine 1891
0
309
86
How it worksLine 1903
0
465
107
Quick startLine 1912
0
738
192
Writing the Rego policyLine 1939
0
2619
668
Loading the policyLine 2013
0
1166
304
Bring in external data and integrationsLine 2050
2
1196
273
Roll out safely with shadow modeLine 2067
0
1647
390
Send decisions to your observability platformLine 2107
1
1131
264
Capability scoping at the model boundaryLine 2132
0
886
211
Scoping a discovered tool surfaceLine 2152
0
1082
254
Allow-all when no policy is configuredLine 2179
0
889
227
Transitive enforcement: composite toolsLine 2200
1
1478
348
Example applicationLine 2226
3
892
224
API referenceLine 2237
0
1256
308
RelatedLine 2257
8
1028
249
How It WorksLine 2283
0
330
83
When to Use SubagentsLine 2291
0
598
100
Why Use Subagents?Line 2302
0
1352
261
Basic Subagent Without StreamingLine 2330
1
2363
543
Streaming Subagent ProgressLine 2404
1
1890
422
Controlling What the Model SeesLine 2455
0
2213
504
Rendering Subagents in the UI (with useChat)Line 2519
0
2536
506
CaveatsLine 2605
4
2189
477
InstallationLine 2663
0
121
39
Running an AgentLine 2671
0
834
221
Connecting to a Remote AgentLine 2707
0
555
128
SandboxLine 2728
0
826
177
Display OptionsLine 2757
0
1161
278
Tool ApprovalsLine 2788
0
518
131
CompatibilityLine 2809
0
501
102
ControlsLine 2820
0
205
69
Next StepsLine 2829
7
1287
310
StatusesLine 2862
0
751
170
Require Approval for a ToolLine 2884
0
543
148
Decide Based on Tool InputLine 2908
0
1029
244
Use One Policy for All ToolsLine 2941
0
1160
295
Configure Approval per RequestLine 2980
0
814
210
Handle Manual ApprovalsLine 3010
0
1405
327
Use with `useChat`Line 3055
0
2602
521
Security ConsiderationsLine 3131
0
2839
600
Related APIsLine 3187
9
1364
330
Why Durable Agents?Line 3213
0
710
146
When to Use WorkflowAgent vs ToolLoopAgentLine 3224
1
1198
205
InstallationLine 3239
0
261
71
Creating a WorkflowAgentLine 3247
0
884
227
Using the Agent in a WorkflowLine 3287
0
3335
749
Resumable Streaming with WorkflowChatTransportLine 3396
2
3097
746
2193 additional H2 sections are present in the source Markdown. This table previews the first 80 sections.
Link counts exclude parsed fenced and inline code and resolve relative destinations against the final endpoint. Section sizes cover the text below each H2 until the next H2.
287 fenced lines match API-key, Authorization, Bearer, OAuth or access-token patterns
HTTP request examples
4 fenced lines match request-method patterns
Authentication links
53 links have authentication-related labels
Embedded example text and links to further documentation are counted separately. A code block does not establish that a sample runs or that an authentication flow succeeds.
No H2 section named “Optional” was detected in this file.
The community proposal uses Optional by convention for secondary links that can be skipped for a shorter context. This label does not establish that everything outside it is critical, or that the material inside it is deprecated.
Machine-readable routes
MCP references and configuration text
MCP-related text was detected in the captured file. 91 links have MCP-related labels or paths. 1 fenced lines match mcpServers, mcp_servers or an mcp add command.
Whole-file size not established by the bounded response
Token count
Not measured for the whole file
Retrieval scope
Bounded prefix inspected; the whole body was not downloaded
A verified text response establishes a companion at the checked URL. It does not prove the bundle contains all product documentation, that generation is automated, or that it fits an assistant’s context window.
Change evidence
What can be said about freshness
This capture establishes a baseline. Update frequency cannot be determined from one observation.
Linked release and migration material
56 links have release, changelog, deprecation or migration terms in their labels or paths. These provide places to investigate product changes separately.
File changes can reveal documentation edits or navigation changes. They do not by themselves measure product release velocity, accuracy, agent adoption or AI visibility.
Counts use a deterministic parser, not a spec-compliance grade. Unusual Markdown and indented code can fall outside its recognized patterns. See the llms.txt proposal and tokenizer implementation.