ToolLoopAgent

Creates a reusable AI agent capable of generating text, streaming responses, and using tools over multiple steps (a reasoning-and-acting loop). ToolLoopAgent is ideal for building autonomous, multi-step agents that can take actions, call tools, and reason over the results until a stop condition is reached.

Unlike single-step calls like generateText(), an agent can iteratively invoke tools, collect tool results, and decide next actions until completion or user approval is required.

import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are a helpful assistant.',
tools: {
weather: weatherTool,
calculator: calculatorTool,
},
});
const result = await agent.generate({
prompt: 'What is the weather in NYC?',
});
console.log(result.text);

To see ToolLoopAgent in action, check out these examples.

Import

import { ToolLoopAgent } from "ai"

Constructor

Parameters

model:

LanguageModel

instructions?:

string | SystemModelMessage | SystemModelMessage[]

allowSystemInMessages?:

boolean

tools?:

Record<string, Tool>

toolChoice?:

ToolChoice

stopWhen?:

StopCondition | StopCondition[]

activeTools?:

Array<string>

output?:

Output

prepareStep?:

PrepareStepFunction

experimental_repairToolCall?:

ToolCallRepairFunction

onStepFinish?:

ToolLoopAgentOnStepFinishCallback

onFinish?:

ToolLoopAgentOnFinishCallback

experimental_context?:

unknown

experimental_telemetry?:

TelemetrySettings

experimental_download?:

DownloadFunction | undefined

maxOutputTokens?:

number

temperature?:

number

topP?:

number

topK?:

number

presencePenalty?:

number

frequencyPenalty?:

number

stopSequences?:

string[]

seed?:

number

maxRetries?:

number

providerOptions?:

ProviderOptions

headers?:

Record<string, string | undefined>

callOptionsSchema?:

FlexibleSchema<CALL_OPTIONS>

prepareCall?:

PrepareCallFunction

id?:

string

Methods

generate()

Generates a response and triggers tool calls as needed, running the agent loop and returning the final result. Returns a promise resolving to a GenerateTextResult.

const result = await agent.generate({
prompt: 'What is the weather like?',
});

prompt:

string | Array<ModelMessage>

messages:

Array<ModelMessage>

abortSignal?:

AbortSignal

timeout?:

number | { totalMs?: number; stepMs?: number; chunkMs?: number }

options?:

CALL_OPTIONS

onStepFinish?:

ToolLoopAgentOnStepFinishCallback

Returns

The generate() method returns a GenerateTextResult object (see generateText for details).

stream()

Streams a response from the agent, including agent reasoning and tool calls, as they occur. Returns a StreamTextResult.

const stream = agent.stream({
prompt: 'Tell me a story about a robot.',
});
for await (const chunk of stream.textStream) {
console.log(chunk);
}

prompt:

string | Array<ModelMessage>

messages:

Array<ModelMessage>

abortSignal?:

AbortSignal

timeout?:

number | { totalMs?: number; stepMs?: number; chunkMs?: number }

options?:

CALL_OPTIONS

experimental_transform?:

StreamTextTransform | Array<StreamTextTransform>

onStepFinish?:

ToolLoopAgentOnStepFinishCallback

Returns

The stream() method returns a StreamTextResult object (see streamText for details).

Types

InferAgentUIMessage

Infers the UI message type for the given agent instance. Useful for type-safe UI and message exchanges.

Basic Example

import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
const weatherAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: { weather: weatherTool },
});
type WeatherAgentUIMessage = InferAgentUIMessage<typeof weatherAgent>;

Example with Message Metadata

You can provide a second type argument to customize the metadata for each message. This is useful for tracking rich metadata returned by the agent (such as createdAt, tokens, finish reason, etc.).

import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
import { z } from 'zod';
// Example schema for message metadata
const exampleMetadataSchema = z.object({
createdAt: z.number().optional(),
model: z.string().optional(),
totalTokens: z.number().optional(),
finishReason: z.string().optional(),
});
type ExampleMetadata = z.infer<typeof exampleMetadataSchema>;
// Define agent as usual
const metadataAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
// ...other options
});
// Type-safe UI message type with custom metadata
type MetadataAgentUIMessage = InferAgentUIMessage<
typeof metadataAgent,
ExampleMetadata
>;

Examples

Basic Agent with Tools

import { ToolLoopAgent, stepCountIs } from 'ai';
import { weatherTool, calculatorTool } from './tools';
const assistant = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are a helpful assistant.',
tools: {
weather: weatherTool,
calculator: calculatorTool,
},
stopWhen: stepCountIs(3),
});
const result = await assistant.generate({
prompt: 'What is the weather in NYC and what is 100 * 25?',
});
console.log(result.text);
console.log(result.steps); // Array of all steps taken by the agent

Streaming Agent Response

const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are a creative storyteller.',
});
const stream = agent.stream({
prompt: 'Tell me a short story about a time traveler.',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}

Agent with Output Parsing

import { z } from 'zod';
const analysisAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
output: {
schema: z.object({
sentiment: z.enum(['positive', 'negative', 'neutral']),
score: z.number(),
summary: z.string(),
}),
},
});
const result = await analysisAgent.generate({
prompt: 'Analyze this review: "The product exceeded my expectations!"',
});
console.log(result.output);
// Typed as { sentiment: 'positive' | 'negative' | 'neutral', score: number, summary: string }

Example: Approved Tool Execution

import { openai } from '@ai-sdk/openai';
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are an agent with access to a weather API.',
tools: {
weather: openai.tools.weather({
/* ... */
}),
},
// Optionally require approval, etc.
});
const result = await agent.generate({
prompt: 'Is it raining in Paris today?',
});
console.log(result.text);