Core Building Blocks / AI Agent
The Agent Builder
Define queue-backed agents with models, tools, skills, execution policies, and HTTP exposure.
AgentQueueBuilder is available directly from ServiceBuilder in @purista/core via getAgentQueueBuilder(...). An attached agent expands into normal PURISTA artifacts: a queue, queue worker, aggregate command, and stream.
Builder workflow
flowchart LR
A[Create ServiceBuilder] --> B[getAgentQueueBuilder]
B --> C[Define Schemas]
C --> D[Declare Models]
D --> E[Pick Execution Type]
E --> F[Configure Tools]
F --> G[Set Policies]
G --> H[Add Agent Definition]
Create the agent builder
import { z } from 'zod'
import { supportV1ServiceBuilder } from '../supportV1ServiceBuilder.js'
export const supportAgentBuilder = supportV1ServiceBuilder
.getAgentQueueBuilder('supportAgent', 'Answers support questions')
Define schemas
supportAgentBuilder
.addPayloadSchema(z.object({
query: z.string().min(1),
conversationId: z.string().optional(),
}))
.addParameterSchema(z.object({
priority: z.enum(['low', 'normal', 'high']).default('normal'),
}))
.addOutputSchema(z.object({
answer: z.string(),
confidence: z.number().min(0).max(1),
sources: z.array(z.string()),
}))
Declare a model alias
supportAgentBuilder.addModel('primary', {
model: 'gpt-4.1-mini',
capabilities: ['object', 'text'],
defaults: {
temperature: 0.2,
},
})
The concrete provider is supplied when the service starts. The handler uses the alias through context.harness.models.primary.
Pick one execution type
Choose exactly one execution definition:
| Method | Use when |
|---|---|
setRunFunction(...) | Normal PURISTA application code with typed resources, model calls, command tools, and child agents. |
setHarnessAgent(...) | You already have a reusable @purista/harness agent definition. |
setHarnessWorkflow(...) | You already have a reusable @purista/harness workflow definition. |
Most application agents should start with setRunFunction(...):
supportAgentBuilder.setRunFunction(async context => {
const result = await context.harness.models.primary.object(
{
messages: [
{
role: 'user',
content: `Answer this customer query: ${context.payload.query}`,
},
],
schema: {
type: 'object',
properties: {
answer: { type: 'string' },
confidence: { type: 'number' },
sources: { type: 'array', items: { type: 'string' } },
},
required: ['answer', 'confidence', 'sources'],
},
},
context.signal,
)
return result.object
})
Configure tools and policies
supportAgentBuilder
.canInvoke('UserService', '1', 'getUser')
.canInvoke('OrderService', '1', 'getOrderHistory')
.canInvokeAgent('escalationAgent', '1')
.useSkills(['customer-support'])
.useBuiltInTools(false)
.setSessionPolicy({
mode: 'conversation',
payloadPath: ['conversationId'],
})
.setExecutionPolicy({
maxAttempts: 3,
maxParallelHandlers: 1,
})
HTTP response shape
supportAgentBuilder.exposeAsHttpEndpoint('POST', 'agents/support', {
streamingMode: 'aggregate', // one JSON response
})
Use streamingMode: 'stream' for an SSE response. Use setResponseMode('accepted', ...) when a long-running agent should return a queue handle instead of holding the HTTP request open.
Add to the service
supportV1ServiceBuilder.addAgentDefinition(await supportAgentBuilder.getDefinition())
At runtime, services with attached agents need a queueBridge and ai.models bindings:
const supportService = await supportV1ServiceBuilder.getInstance(eventBridge, {
queueBridge,
ai: {
models: {
primary: {
provider,
model: 'gpt-4.1-mini',
capabilities: ['object', 'text'],
},
},
},
})
Important constraints
canEmitis not implemented onAgentQueueBuilder.canConsumeStreamis not implemented onAgentQueueBuilder.- Execution types are mutually exclusive; pick exactly one.
- Model capabilities must match the handler calls and runtime provider binding.