Build an agent ​
Build an agent after a direct completion confirms that the provider credential, endpoint, and model are working.
1. Create the provider model ​
Keep the credential in server-side configuration and validate it when the application starts:
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"
})The agent depends on Anvia's provider-neutral completion-model contract. Changing providers later means supplying a different model at this boundary, not rewriting the agent loop.
2. Construct the agent ​
import { Agent } from '@anvia/core'
export const supportAgent = new Agent({
id: 'support',
name: 'Support assistant',
description: 'Helps investigate customer support requests.',
model,
instructions: [
'Answer support questions clearly.',
'Ask for details when the report is incomplete.',
'Do not invent account-specific information.',
].join('\n'),
maxTurns: 4,
})id is the stable runtime identity used by sessions, traces, evaluations, development tooling, and agent-as-tool integrations. Keep it predictable and do not derive it from a user prompt.
name and description are optional human-readable metadata. maxTurns bounds the model-and-tool loop; when omitted, the runtime default is 20.
3. Generate the first response ​
const result = await supportAgent.generate({
prompt: 'A customer cannot reset their password. What should I verify first?'
})
if (result.type === 'interaction') throw new Error(`Interaction required: ${result.interaction.type}`)
if (result.type === 'blocked') throw new Error(`Blocked at ${result.stage}: ${result.reason}`)
console.log(result.output)
console.log(result.runId)
console.log(result.usage.totalTokens)The status checks remain important as capabilities are added. generate() returns type: 'interaction' when a configured tool needs approval or a structured human answer. That is expected control flow, not a failed run.
4. Stream the same agent ​
Use stream() when an interface should update while the run is active:
const stream = supportAgent.stream({
prompt: 'Draft a short password-reset troubleshooting reply.'
})
for await (const event of stream) {
if (event.type === 'text_delta') {
process.stdout.write(event.delta)
}
if (event.type === 'response') {
process.stdout.write('\n')
console.log(event.runId, event.usage)
}
if (event.type === 'interaction') {
console.log('Interaction required:', event.interaction)
}
}The terminal stream event is the same response | interaction | blocked union returned by generate(). The handle also exposes textStream, text, and result when an application does not need every runtime event. The model must support streaming.
5. Add capabilities deliberately ​
Start with the smallest agent that works. Add tools, context, memory, guardrails, or observers when the product actually needs those boundaries.
Continue with Stable behavior.