Agents and function calls
Create an agent class, give it application functions and run it from a route or job.
On this page
Source-backed MarkdownCreate an agent
Create server/agents/HelpAgent.ts. An agent describes its task in instructions() and exposes functions from tools(). This example reads a help guide through your app’s Storage service.
Install zod in your app for tool argument schemas. The framework validates the model’s arguments before invoking your function. The route selects the guide path, user and scope; the model cannot choose another user’s file.
import { z } from 'zod';
import { Agent, tool, type AgentTool, type BaseAgentContext } from '@db3.ai/app/ai';
import { app } from '@db3.ai/app/server';
/** Trusted context supplied by your route or job, never selected by the model. */
export interface HelpAgentContext extends BaseAgentContext {
guidePath: string;
}
/** Answers questions using a help guide saved by your application. */
export class HelpAgent extends Agent<HelpAgentContext> {
/** Gives the agent its task and source of truth. */
async instructions(): Promise<string> {
return 'Answer questions about this app using its saved help guide. Read the guide before answering. If the answer is absent, say so.';
}
/** Exposes one function with validated arguments and a server-selected file path. */
protected tools(): AgentTool[] {
return [Object.assign(tool({
name: 'read_help_guide',
description: 'Read the application help guide.',
parameters: z.object({}),
/** Reads only the file selected by trusted application code. */
execute: async () => app().storage.getText(this.context.guidePath),
}), { title: 'Read help guide', description: 'Read the application help guide.' })];
}
}
Run your agent
Write a guide to your configured disk, then call the agent from trusted server code. run() resolves with the final output and saved IDs, or throws when the run fails. user and scope are application identifiers; choose them from your authenticated request and permission checks.
import { app } from '@db3.ai/app/server';
import { HelpAgent } from './agents/HelpAgent';
await app().storage.put('help.md', 'Change your password on the settings page.');
const result = await new HelpAgent({ user: userId, scope: teamId, guidePath: 'help.md' }).run('How do I change my password?');
console.log(result.finalOutput, result.conversationId);Stream events to your UI
Use stream(message, emit) to forward typed events to your HTTP transport. Events include text.delta, tool.calling, tool.progress, tool.success, tool.error, run.completed and run.error.
stream() reports a failure through run.error and returns null. Your renderer can display tool progress alongside text and later restore the same timeline from saved messages. Keep prompt/debug events behind an authorized diagnostic view.
await new HelpAgent({ user: userId, scope: teamId, guidePath: 'help.md' }).stream(message, async event => {
await sendEvent(event);
});Run an agent in the background
Register AgentRunJob and each concrete agent during app boot, in both your web and worker processes. Add QueuedJob and FailedJob to your migration registry if you have not already configured the database queue.
queue() saves the conversation and prepared run before dispatching. The worker reconstructs your agent from its serialized constructor context. Keep context serializable; registered ActiveRecord references can be restored by the framework serializer. Use stable class names for persisted jobs.
import { AgentRunJob, registerQueuedAgent } from '@db3.ai/app/ai';
application.queue.registerJob(AgentRunJob);
registerQueuedAgent('HelpAgent', HelpAgent);
const run = await new HelpAgent({ user: userId, scope: teamId, guidePath: 'help.md' }).queue('Explain password settings.', { queue: 'help' });
console.log(run.jobId, run.conversationId);Use other AI features in a tool
Call this.generateImage(...), this.generateEmbedding(...) or this.generateStructured(...) from an agent tool. Calls made during the run inherit its conversation and parent request, so the full operation appears in one request tree. Direct app().ai calls inside a tool inherit the same context.
Use the exported SDK tool helpers for supported provider tools such as web search. Tool availability depends on the selected provider and model. Your functions still own authorization, input limits and any application side effects.