Agent with tools
This recipe creates an order-status agent that calls typed application code and uses the validated result in its answer.
1. Install the dependencies
pnpm add @anvia/core @anvia/openai zod
pnpm add --save-dev tsx typescript @types/nodeSet OPENAI_API_KEY in the server environment.
2. Define the application service
// orders.ts
import { z } from 'zod'
export const orderStatusSchema = z.object({
orderId: z.string(),
status: z.enum(['processing', 'shipped']),
estimatedShipDate: z.iso.date(),
})
export async function findOrder(orderId: string) {
console.log(`Order service called for ${orderId}`)
return {
orderId,
status: 'processing' as const,
estimatedShipDate: '2026-08-15',
}
}This fixture makes the example runnable without a database. A production service must also accept trusted user and tenant scope.
3. Expose a narrow tool
// tools.ts
import { createTool } from '@anvia/core'
import { z } from 'zod'
import { findOrder, orderStatusSchema } from './orders.js'
export const lookupOrder = createTool({
name: 'lookup_order',
description: 'Look up the current status of one order by its ID.',
inputSchema: z.object({
orderId: z.string().min(1),
}),
outputSchema: orderStatusSchema,
execute: ({ orderId }) => findOrder(orderId),
})inputSchema validates model-supplied arguments before execute runs. outputSchema validates application data before it returns to the model.
4. Run the agent
// agent.ts
import { Agent } from '@anvia/core'
import { OpenAIClient } from '@anvia/openai'
import { lookupOrder } from './tools.js'
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) throw new Error('Set OPENAI_API_KEY.')
const agent = new Agent({
id: 'order-status',
model: new OpenAIClient({ apiKey }).completionModel({
modelId: 'gpt-5.6-sol',
api: "responses"
}),
instructions: 'Use lookup_order before answering. Never invent order data.',
maxTurns: 3,
tools: [lookupOrder],
})
const result = await agent.generate({
prompt: 'Look up order ord_123 and tell me its status and ship date.'
})
if (result.type === 'interaction') {
throw new Error(`Agent requested ${result.interaction.type}`)
}
if (result.type === 'blocked') throw new Error(`Agent blocked at ${result.stage}: ${result.reason}`)
console.log(result.output)Run it with pnpm tsx agent.ts. The console first shows the service lookup, then the agent's final answer.
Secure the real boundary
A valid orderId does not authorize access. Capture authenticated scope when constructing the tool and enforce it inside execute before reading data. Return only fields the caller may see.
Keep side-effecting tools narrow, authorized, audited, and idempotent where practical. Add tool approval for sensitive actions.