Build your first Anvia agent
Anvia is a TypeScript runtime for adding AI agents to an application without handing over the rest of its architecture. You create the provider model and define the agent's behavior; Anvia runs the model and tool loop.
The tutorial below builds a small support agent with the stable v1 API. Add each snippet to the same TypeScript file in order.
1. Install the runtime and a provider
@anvia/core contains the provider-neutral agent runtime. @anvia/openai adapts OpenAI and OpenAI-compatible models to that runtime. Install both from the stable release line.
pnpm add @anvia/core @anvia/openaiKeep credentials in your application's configuration. Anvia clients receive credentials explicitly and do not read environment variables on their own.
export OPENAI_API_KEY=...2. Create a provider model
The provider client handles the provider-specific API. Calling completionModel() creates the model object that the rest of Anvia depends on.
import { OpenAIClient } from '@anvia/openai'
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) {
throw new Error('OPENAI_API_KEY is required')
}
const client = new OpenAIClient({ apiKey })
const model = client.completionModel({
modelId: 'gpt-5.6-sol',
api: "responses"
})This separation is what keeps the agent provider-neutral. To change providers later, create a different model and leave the agent behavior unchanged.
3. Define the agent
An agent combines a model with reusable behavior. Its ID is useful for tracing and memory, while its instructions and turn limit define how it should handle a run.
import { Agent } from '@anvia/core'
const supportAgent = new Agent({
id: 'support',
model,
instructions: 'Answer support questions clearly. Ask for missing details.',
maxTurns: 4,
})The dependencies remain visible at construction time. Tools, memory, context, guardrails, and observers can be added to the same options object as the application grows.
4. Generate an answer
generate() starts the agent loop and resolves to a response, interaction, or guardrail block. Checking type keeps that boundary explicit, even though this first agent has no tools yet.
const response = await supportAgent.generate({
prompt: 'A customer cannot reset their password. What should I check first?'
})
if (response.type === 'interaction') throw new Error(`Interaction required: ${response.interaction.type}`)
if (response.type === 'blocked') throw new Error(`Blocked at ${response.stage}: ${response.reason}`)
console.log(response.output)The response outcome includes the final output, normalized messages, token usage, and run metadata. Your application decides how that result is stored or presented to a user.
Stream the same agent
Use stream() when a CLI or interface should update while the model is responding. It emits normalized events, so application code does not need to translate each provider's streaming format.
for await (const event of supportAgent.stream({
prompt: 'Draft a short support reply.'
})) {
if (event.type === 'text_delta') {
process.stdout.write(event.delta)
}
if (event.type === 'response' || event.type === 'interaction' || event.type === 'blocked') {
process.stdout.write('\n')
console.log(event.usage)
}
}What changed from v0
The v0 API builds agents through AgentBuilder and sends work through prompt().send(). In v1, construction uses one explicit options object and execution happens directly on the agent.
In v0, the builder accumulates configuration before creating the agent:
const supportAgent = new AgentBuilder('support', model)
.instructions('Answer support questions clearly.')
.defaultMaxTurns(4)
.build()
const response = await supportAgent.prompt(input).send()In v1, the constructor receives that configuration directly and generate() starts the run:
const supportAgent = new Agent({
id: 'support',
model,
instructions: 'Answer support questions clearly.',
maxTurns: 4,
})
const response = await supportAgent.generate({
prompt: input
})The runtime boundary stays the same: Anvia owns the model and tool loop, while the application owns credentials, authentication, permissions, data access, persistence, deployment, and user-facing responses.
Continue building
- Follow Getting started for direct completions and a deeper walkthrough.
- Learn how agents, tools, context, memory, and events fit together in Core concepts.
- Add application-owned actions in Tools.
- Connect an agent to a server or interface in Build applications.
- Review production boundaries in Production operations.