Building Agents
Purpose and Scope
This page explains how to define a reusable AI SDK agent with ToolLoopAgent, then run it with model configuration, instructions, tools, runtime state, and per-call execution context. An agent in this part of the AI SDK is not just a prompt wrapper: it is a configured tool loop that can let a language model call tools multiple times in sequence while the SDK manages the iteration. The documented goal is to define behavior once and reuse it across application routes or workflows instead of recreating the same model, prompt, and tool settings at every call site.
Sources: content/docs/03-agents/02-building-agents.mdx
ToolLoopAgent is useful when an application needs consistency across several entry points. The source documentation names four practical motivations: reusing configuration, maintaining consistent behavior, simplifying API routes, and preserving TypeScript support for tool inputs and outputs. Those goals shape the design of the page: put the long-lived behavior into the agent constructor, then provide request-specific values to generate(), stream(), or related helpers. This separation keeps business logic, server credentials, and model behavior from being scattered through the application.
Sources: content/docs/03-agents/02-building-agents.mdx
Relevant Source Files
content/docs/03-agents/02-building-agents.mdx— first-party documentation for creatingToolLoopAgentinstances, configuring models and instructions, defining tools with schemas, passingruntimeContextandtoolsContext, and providing an experimental sandbox per call.
Core Primitives
The central primitive is ToolLoopAgent from the ai package. You instantiate it with a configuration object that includes a model, optional instructions, and optional tools. The model can be supplied through whatever provider setup the application uses, while instructions define the agent’s standing behavior, such as “You are a helpful assistant” or “You are an expert software engineer.” The important distinction is that these values belong to the reusable agent definition, not to a single request, so they become the stable operating profile for that agent.
Sources: content/docs/03-agents/02-building-agents.mdx
Tools are declared through the AI SDK tool helper. A tool has a description, an inputSchema, and usually an execute function. The source example uses zod to validate a runCode tool whose input contains a code string and whose executor returns an object with an output message. The language model can choose to call a tool as part of its loop, but the developer defines the tool boundary, validation schema, and server-side execution logic. This is where agent behavior becomes application-specific rather than only conversational.
Sources: content/docs/03-agents/02-building-agents.mdx
Two context primitives support request-specific behavior. runtimeContext is shared runtime state for the agent loop and is available in prepareStep, lifecycle callbacks, and final results. toolsContext is for server-side values needed by tools, such as credentials, account identifiers, scoped permissions, or default settings. A tool declares the shape it expects with contextSchema, and the caller supplies matching values under that tool’s name. This keeps sensitive execution details out of prompts while still making them available to trusted tool code.
Sources: content/docs/03-agents/02-building-agents.mdx
Define an Agent
Start by importing ToolLoopAgent from ai and creating an instance. The minimal form provides a model and instructions. In a real application, __PROVIDER_IMPORT__ and __MODEL__ are replaced by the provider package and model reference chosen for the project. The constructor is the right place for durable behavior: model selection, system-level instructions, common tools, and callbacks that should be shared wherever this agent is used. Once created, the agent can be called from routes, background jobs, or UI stream handlers without rebuilding its policy each time.
import { ToolLoopAgent } from 'ai';
__PROVIDER_IMPORT__;
const myAgent = new ToolLoopAgent({
model: __MODEL__,
instructions: 'You are a helpful assistant.',
tools: {
// Your tools here
},
});The same constructor accepts settings aligned with generateText and streamText, so developers can carry familiar model-call configuration into an agent definition. For example, a software engineering agent can be declared with an expert instruction string and the selected model, then extended with tools later. This compatibility matters because an agent is an evolution of direct model calls, not a separate programming model. You keep the same AI SDK mental model while adding multi-step tool use and reusable behavior.
Sources: content/docs/03-agents/02-building-agents.mdx
Add Tools and Execution Context
A tool gives the agent a controlled action it can request during the loop. The documented runCode example shows the essential contract: describe the tool for the model, validate the model-provided input with inputSchema, and implement execute as trusted application code. In production, the executor is where you would call an API, query a database, run a command, or perform another bounded action. The schema is not decorative; it is the typed boundary between model-generated arguments and application execution.
import { ToolLoopAgent, tool } from 'ai';
__PROVIDER_IMPORT__;
import { z } from 'zod';
const codeAgent = new ToolLoopAgent({
model: __MODEL__,
tools: {
runCode: tool({
description: 'Execute Python code',
inputSchema: z.object({
code: z.string(),
}),
execute: async ({ code }) => {
return { output: 'Code executed successfully' };
},
}),
},
});For tools that need private or request-scoped values, use toolsContext rather than embedding those values into instructions or user messages. The source example defines a searchTickets tool with contextSchema requiring apiKey and accountId. The tool executor receives validated user input, such as a search query, and separately receives trusted context. This pattern is especially important for credentials and authorization boundaries: the model can request a search, but the server decides which account and credential scope are available for that call.
Sources: content/docs/03-agents/02-building-agents.mdx
Runtime State and Per-Step Behavior
runtimeContext represents shared state for a particular agent run. The documentation example passes a requestId and an escalated flag to agent.generate(). The agent’s prepareStep callback can inspect that state before a model step and adjust settings, returning a lower temperature when runtimeContext.escalated is true. This makes step behavior conditional without changing the reusable agent definition. Use this mechanism for request metadata, escalation state, tenant choices, or other values that should influence the loop but are not tool secrets.
const result = await agent.generate({
prompt: 'Find open billing tickets for this account.',
runtimeContext: {
requestId: 'req_abc',
escalated: false,
},
toolsContext: {
searchTickets: {
apiKey: process.env.SUPPORT_API_KEY!,
accountId: 'acct_123',
},
},
});This split between constructor configuration, runtimeContext, and toolsContext is the main design rule for building maintainable agents. Put stable behavior in the ToolLoopAgent constructor. Put loop-wide request state in runtimeContext. Put sensitive, tool-specific execution values in toolsContext and validate them with each tool’s contextSchema. The source documentation also points readers to the broader Runtime and Tool Context guide for sensitive context filtering and availability rules, which is the natural next step once an agent starts carrying real application state.
Sources: content/docs/03-agents/02-building-agents.mdx
Running, Streaming, and Sandboxes
The page’s execution model is call-oriented: after defining an agent, invoke it with methods such as generate() or stream(). generate() is appropriate when the caller wants a completed result, while stream() fits interfaces that need incremental output or progress. The source describes the experimental sandbox as a per-call value supplied when an agent tool needs a command or code execution environment. Because the sandbox is passed to the invocation rather than stored globally, each request can choose the correct isolation boundary for its tools.
Sources: content/docs/03-agents/02-building-agents.mdx
That per-call sandbox rule is important for security and deployment architecture. A code-execution tool may be part of a reusable agent definition, but the actual execution environment should be selected at the moment the request runs. This lets a server decide whether a given user, tenant, route, or job receives sandbox access, and it avoids accidentally sharing an execution environment across unrelated calls. The documented placement on generate(), stream(), or an agent UI stream helper reinforces that sandboxing belongs to execution context, not static prompt design.
Sources: content/docs/03-agents/02-building-agents.mdx
Compact API Reference
| Component | Where it is used | Source-backed behavior |
|---|---|---|
ToolLoopAgent | Imported from ai and instantiated with a configuration object | Encapsulates model configuration, instructions, tools, and reusable behavior for a managed tool loop |
model | new ToolLoopAgent({ model }) | Selects the language model used by the agent |
instructions | new ToolLoopAgent({ instructions }) | Defines standing system behavior for the agent |
tools | new ToolLoopAgent({ tools }) | Provides callable capabilities the model can use during the loop |
tool() | Tool declaration helper imported from ai | Defines tool description, input validation, optional context validation, and execution |
inputSchema | Inside a tool definition | Validates model-provided tool arguments, shown with z.object(...) |
contextSchema | Inside a tool definition | Declares trusted server-side context required by that tool |
prepareStep | Agent configuration callback | Can inspect runtimeContext and return per-step model settings |
agent.generate() | Agent invocation | Runs the agent for a prompt with optional runtimeContext and toolsContext |
agent.stream() | Agent invocation | Runs the agent in streaming mode |
experimental_sandbox | Per-call invocation option | Supplies a command or code execution environment for tools that require one |
Next Steps
After building a first ToolLoopAgent, review the adjacent agent topics in this wiki. Use Loop Control to understand multi-step stopping behavior, Configuring Call Options for per-call model and provider settings, Runtime and Tool Context for context boundaries, Tool Approvals for human-in-the-loop safety, and Sandbox for command or code execution environments. If the agent is being exposed through a UI, connect this page with the UI stream and chatbot pages so the same reusable agent can power both server-side calls and interactive streaming experiences.