What is an agent?
An agent is not a single model call. It is a loop — a stateful orchestrator that calls a model, executes tools based on the response, feeds results back into the next turn, and repeats until a validated output emerges or a limit is reached.Model
The language model that generates responses. Anything from the Vercel AI SDK:
anthropic(...), openai(...), google(...).System Prompt
A static string or a dynamic function called each turn. Describes the agent’s role and persona.
Tools
Functions the model can call during a run. Each tool has a Zod schema, a description, and an
execute function.Output Schema
A Zod schema that shapes the agent’s return type. When set, Vibes validates and parses the model’s structured response.
Dependencies
Runtime values (databases, API clients, config) injected at
agent.run() time and available everywhere inside the agent.Result Validators
Functions that run after the output is parsed. Throw to reject and retry; return to accept (optionally modifying the output).
Agent<TDeps, TOutput>. Defaults are undefined (no deps) and string (plain text output). TypeScript infers these for you in almost all cases.
A motivating example
Let’s build something concrete: a roulette advisor that rolls a virtual wheel and recommends whether to bet on red or black.tool()creates a typed tool. Theexecutefunction receivesRunContextas its first argument (unused here —_ctx) and the validated args as the second. It returns any serializable value.- The Zod schema defines the shape of
result.output. No schema →result.outputis a plainstring. new Agent(...)is cheap — it just stores config. Do it once at module level, not inside request handlers.nameis optional but makes traces and logs much easier to read.- Pick the smallest model that does the job well.
claude-haiku-4-5-20251001is 3× cheaper than Sonnet for simple tasks. systemPromptcan be a plain string (evaluated once) or a function(ctx) => string(evaluated every turn).toolsis an array — Vibes does not use decorators. Pass all tools the model may need.outputSchematriggers structured output mode. Vibes injects afinal_resulttool under the hood; the model calls it to return the structured data.agent.run()is an ordinaryasyncfunction. Await it.
Running an agent — three ways
Vibes gives you three run methods. They all execute the same loop; they differ only in how you receive the output.1. agent.run() — await the result
The simplest option. Returns a Promise<RunResult<TOutput>> that resolves when the run is complete.
2. agent.stream() — stream text tokens
Returns a StreamResult<TOutput> immediately. Consume textStream for real-time tokens, then await the remaining promises after iteration.
outputMode: "tool"):
3. agent.runStreamEvents() — typed event stream
Returns an AsyncIterable<AgentStreamEvent<TOutput>>. Every turn, text token, tool call, and tool result is a distinct event. Use this when you need full observability or custom UI updates.
System prompts — static vs. dynamic
A system prompt can be a plain string (evaluated once at agent construction) or a function evaluated fresh every turn. Static string — simple, predictable, costs no extra computation:RunContext and can inspect deps, usage, or metadata:
instructions vs systemPrompt — Vibes distinguishes two prompt fields:
| Field | Recorded in result.messages? | Use for |
|---|---|---|
systemPrompt | Yes | Persistent persona, role, core rules |
instructions | No | Per-run ephemeral guidance, user-tier rules |
instructions are injected into each turn alongside systemPrompt but are never stored in the message history. This matters for multi-turn conversations: when you pass result.messages back as messageHistory, the instructions are re-injected fresh rather than accumulating in the history.
Agent reuse
Instantiate agents once at module level. They are stateless — all run-specific state lives inRunContext and the returned RunResult, never on the agent object itself.
agent.override() — creating variants
agent.override(overrides) returns a scoped runner that applies substituted options for a single call. The original agent is never mutated. This is the canonical pattern for testing, A/B model comparisons, and per-request overrides.
override() accepts the same fields as AgentOptions: model, systemPrompt, instructions, tools, toolsets, resultValidators, maxRetries, maxTurns, usageLimits, historyProcessors, modelSettings, endStrategy, telemetry.
Override runs bypass the
setAllowModelRequests(false) guard, so they work cleanly in test environments that disable live model access.Constructor options reference
All options passed tonew Agent(opts: AgentOptions<TDeps, TOutput>):
| Field | Type | Default | Description |
|---|---|---|---|
model | LanguageModel | required | Vercel AI SDK model (e.g. anthropic("claude-sonnet-4-6")) |
name | string? | — | Human-readable label for traces and logs |
systemPrompt | string | (ctx) => string | — | Base system prompt; recorded in message history |
instructions | string | (ctx) => string | — | Per-turn additions; NOT recorded in message history |
tools | ToolDefinition<TDeps>[] | [] | Tools always available to the model |
toolsets | Toolset<TDeps>[] | [] | Dynamic per-turn tool groups |
outputSchema | ZodType | ZodType[] | — | Zod schema for structured output |
outputMode | 'tool' | 'native' | 'prompted' | 'tool' | How structured output is requested from the model |
outputTemplate | boolean | true | Whether to inject the schema description into the system prompt |
resultValidators | ResultValidator[] | [] | Post-parse validators; throw to reject and retry |
maxRetries | number | 3 | Max validation retries before MaxRetriesError |
maxTurns | number | 10 | Max tool-call round trips before MaxTurnsError |
usageLimits | UsageLimits? | — | Cap cumulative token or request usage |
historyProcessors | HistoryProcessor[] | [] | Per-turn message transforms (trim, summarize, filter) |
modelSettings | ModelSettings? | — | Temperature, maxTokens, topP, and other model params |
endStrategy | 'early' | 'exhaustive' | 'early' | When to stop after receiving final_result |
maxConcurrency | number? | unlimited | Max concurrent tool executions per turn |
telemetry | TelemetrySettings? | — | OpenTelemetry settings forwarded to the AI SDK |
Dependencies
Inject runtime values into tools and prompts via RunContext
Tools
Give agents the ability to take actions
Results
RunResult, StreamResult, and output validation