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 creating ToolLoopAgent instances, configuring models and instructions, defining tools with schemas, passing runtimeContext and toolsContext, 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

ComponentWhere it is usedSource-backed behavior
ToolLoopAgentImported from ai and instantiated with a configuration objectEncapsulates model configuration, instructions, tools, and reusable behavior for a managed tool loop
modelnew ToolLoopAgent({ model })Selects the language model used by the agent
instructionsnew ToolLoopAgent({ instructions })Defines standing system behavior for the agent
toolsnew ToolLoopAgent({ tools })Provides callable capabilities the model can use during the loop
tool()Tool declaration helper imported from aiDefines tool description, input validation, optional context validation, and execution
inputSchemaInside a tool definitionValidates model-provided tool arguments, shown with z.object(...)
contextSchemaInside a tool definitionDeclares trusted server-side context required by that tool
prepareStepAgent configuration callbackCan inspect runtimeContext and return per-step model settings
agent.generate()Agent invocationRuns the agent for a prompt with optional runtimeContext and toolsContext
agent.stream()Agent invocationRuns the agent in streaming mode
experimental_sandboxPer-call invocation optionSupplies 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.