| name | output-dev-agent-class |
| description | Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when building agents with skills, structured output, stateful conversations, or streaming callbacks. |
| allowed-tools | ["Read","Write","Edit"] |
Using the Agent Class
Overview
The Agent class extends AI SDK's ToolLoopAgent with Output prompt files and the skills system. Use it when you need multi-step tool execution, conversation history, or a reusable agent instance. For single-shot LLM calls without tools, generateText is simpler.
When to Use This Skill
- Building multi-step agents that call tools in a loop
- Using skills (lazy-loaded instructions) with an agent
- Creating agents with structured output via
aiSdk.Output.object()
- Implementing stateful conversations with
messageStore
- Streaming Agent progress with
onChunk
- Deciding between
Agent and generateText
Import Pattern
import { Agent, aiSdk } from '@outputai/llm';
import type { MessageStore } from '@outputai/llm';
import { z } from '@outputai/core';
Agent comes from @outputai/llm. Use aiSdk.Output for structured output. Import z from @outputai/core (never from zod directly). MessageStore is the type for a pluggable getMessages / addMessages store; implement it yourself.
Construction
The prompt file is loaded and rendered at construction time. Variables and tools are fixed at construction. Skills and maxSteps come from the prompt file. The agent is ready to call generate(), generateWithStreaming(), or stream() immediately.
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: {
content_type: input.contentType,
focus: input.focus,
content: input.content
},
output: aiSdk.Output.object( { schema: reviewSchema } )
} );
Constructor Options
| Option | Type | Default | Description |
|---|
prompt | string | (required) | Prompt file name (e.g. 'writing_assistant@v1') |
promptDir | string | - | Override the stack-resolved prompt directory |
variables | PromptVariables | - | Template variables rendered at construction |
tools | AI SDK tools | - | Caller tools; merged with prompt YAML tools (load_skill last) |
stopWhen | function or function[] | - | Custom stop condition (overrides prompt maxSteps when tools exist) |
output | aiSdk.Output | - | Structured output spec (e.g. aiSdk.Output.object({ schema })) |
messageStore | MessageStore | - | Pluggable store for multi-turn history |
generate()
Run the agent and return when complete:
const result = await agent.generate();
console.log( result.text );
console.log( result.output );
console.log( result.usage );
The result has the same shape as generateText: text, result (alias for text), output, usage, finishReason, toolCalls, etc.
Passing Additional Messages
Extend the conversation with extra messages:
const result = await agent.generate( {
messages: [ { role: 'user', content: 'Focus on the introduction section.' } ]
} );
Messages are appended after the initial prompt messages (and any message-store history). You can also pass abortSignal and toolChoice.
generateWithStreaming()
Use generateWithStreaming() when you need progress callbacks and a complete result:
const result = await agent.generateWithStreaming( {
onChunk( { chunk } ) {
if ( chunk.type === 'text-delta' ) {
process.stdout.write( chunk.text );
}
}
} );
The method behaves like generate() while using streaming internally. It returns the complete response, rejects on stream errors, and automatically appends messages to the configured message store. It accepts the same messages, abortSignal, and toolChoice as generate(), plus onChunk. Prefer it over stream() in Temporal activity steps unless direct access to the stream result is required.
stream()
Use stream() when direct control over textStream or fullStream is required. It accepts the same messages, abortSignal, and toolChoice as generate(), plus onChunk, onFinish, and onError:
const stream = await agent.stream();
for await ( const chunk of stream.textStream ) {
process.stdout.write( chunk );
}
Like streamText, the stream result provides textStream and fullStream iterables, plus promise-based properties (text, usage, finishReason) that resolve on completion.
stream() appends messages to the message store in its wrapped onFinish when finishReason is not 'error'. See output-dev-llm-streaming for streaming and error-handling guidance.
Structured Output
Use aiSdk.Output.object() to get typed responses:
const reviewSchema = z.object( {
issues: z.array( z.string() ).describe( 'List of issues found' ),
suggestions: z.array( z.string() ).describe( 'Actionable suggestions' ),
score: z.number().describe( 'Quality score 0-100' ),
summary: z.string().describe( 'Brief overall assessment' )
} );
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: { content_type: 'documentation', focus: 'clarity', content: markdownContent },
output: aiSdk.Output.object( { schema: reviewSchema } )
} );
const { output } = await agent.generate();
Use .describe() on schema fields instead of .min()/.max() for number constraints. Anthropic does not support minimum/maximum JSON Schema constraints in tool definitions.
Message Store
By default, Agent is stateless. Each generate() call starts fresh with only the initial prompt messages. Pass a messageStore to maintain history across calls:
import { Agent } from '@outputai/llm';
import type { MessageStore } from '@outputai/llm';
const messages: Parameters<MessageStore['addMessages']>[0] = [];
const messageStore: MessageStore = {
getMessages: () => messages,
addMessages: incoming => {
messages.push( ...incoming );
}
};
const chatbot = new Agent( {
prompt: 'chatbot@v1',
messageStore
} );
const r1 = await chatbot.generate( {
messages: [ { role: 'user', content: 'Hello, tell me about Output.' } ]
} );
const r2 = await chatbot.generate( {
messages: [ { role: 'user', content: 'How does it handle retries?' } ]
} );
MessageStore is:
interface MessageStore {
getMessages(): ModelMessage[] | Promise<ModelMessage[]>;
addMessages( messages: ModelMessage[] ): void | Promise<void>;
}
ModelMessage is an AI SDK type (aiSdk / ai). There is no built-in store. Implement the interface in memory for a single process, or with your database for durable history.
Using Agent in Workflow Steps
In workflow steps, construct a new Agent per invocation. Variables come from the step input:
import { step, z } from '@outputai/core';
import { Agent, aiSdk } from '@outputai/llm';
const reviewSchema = z.object( {
summary: z.string().describe( 'Brief assessment' ),
issues: z.array( z.string() ).describe( 'Problems found' ),
suggestions: z.array( z.string() ).describe( 'Improvements' ),
score: z.number().describe( 'Quality score 0-100' )
} );
export const reviewContent = step( {
name: 'reviewContent',
description: 'Review technical content using Agent with structured output',
inputSchema: z.object( {
content: z.string().describe( 'The content to review' ),
content_type: z.string().describe( 'Type of content' ),
focus: z.string().describe( 'Review focus areas' )
} ),
outputSchema: reviewSchema,
: input => {
agent = ( {
: ,
: input,
: aiSdk..( { : reviewSchema } )
} );
{ output } = agent.();
output;
}
} );
This is the standard pattern. Each step invocation is independent, and Agent construction is cheap.
Using Agent with Skills
List skill paths in the prompt frontmatter. See output-dev-skill-file for the full skills guide.
When to Use Agent vs generateText
| generateText | Agent |
|---|
| Best for | Single-shot LLM calls | Multi-step tool loops |
| Tools | Supported | Supported |
| Skills | Supported | Supported |
| Conversation history | Manual | Built-in with messageStore |
| Reusable instance | No (function call) | Yes (construct once, call many) |
| Structured output | aiSdk.Output.object() | aiSdk.Output.object() |
Start with generateText. Move to Agent when you need conversation state or a reusable instance with a fixed configuration.
generateText Example (for comparison)
import { generateText } from '@outputai/llm';
const { result } = await generateText( {
prompt: 'generate_summary@v1',
variables: {
company_name: input.name,
website_content: input.websiteContent
}
} );
Verification Checklist
Related Skills
output-dev-skill-file - Creating skill files for agents
output-dev-llm-streaming - Streaming progress and Temporal-safe error handling
output-dev-prompt-file - Creating .prompt files used by agents
output-dev-step-function - Using agents in step functions
output-dev-types-file - Defining Zod schemas for structured output
output-dev-workflow-function - Orchestrating agent-powered steps