Structured Output
Return schema-constrained JSON from an Agent with outputSchema
Set outputSchema on an Agent to make it return JSON that matches your schema.
Quick Start
- Simplest — one outputSchema
typescript import { Agent } from 'praisonai'; const agent = new Agent({ instructions: 'Extract person info as JSON.', llm: 'gpt-4o-mini', outputSchema: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' }, email: { type: 'string' } }, required: ['name', 'age'] } // outputSchemaName defaults to 'response' }); const result = await agent.start( 'John Doe is 30 years old, email john@example.com' ); console.log(result); // result is a JSON string matching the schema: // {"name":"John Doe","age":30,"email":"john@example.com"} - With outputSchemaName and a nested schema
typescript import { Agent } from 'praisonai'; const agent = new Agent({ instructions: 'Extract an order as JSON.', llm: 'gpt-4o-mini', outputSchemaName: 'order', outputSchema: { type: 'object', properties: { id: { type: 'string' }, customer: { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } }, required: ['name'] }, items: { type: 'array', items: { type: 'object', properties: { sku: { type: 'string' }, qty: { type: 'number' } }, required: ['sku', 'qty'] } } }, required: ['id', 'customer', 'items'] } }); const json = await agent.start('Order 42 for Ada (ada@example.com): 2x SKU-1, 1x SKU-9'); console.log(JSON.parse(json));
How It Works
On OpenAI, the agent forwards outputSchema as response_format: { type: 'json_schema', json_schema: { name, schema } }. On other providers it routes the schema through the AI SDK backend’s generateObject({ schema }). Both paths return the raw JSON string.
| Field | Type | Default | Description |
|---|---|---|---|
outputSchema | Record<string, any> | undefined | JSON Schema for the response. |
outputSchemaName | string | 'response' | Name used in response_format.json_schema.name. |
Provider support
outputSchema works on every provider PraisonAI TS supports today.
| Provider prefix | How the schema is enforced |
|---|---|
openai/... | Native response_format: { type: 'json_schema', json_schema: { ... } } |
anthropic/..., google/..., groq/..., mistral/..., ollama/..., and other AI SDK providers | AI SDK backend.generateObject({ schema }) |
The agent returns a JSON string that matches your schema in both paths — JSON.parse(result) for the object.
Cross-provider structured output landed in PraisonAI PR #4412. If you are on an earlier release, the agent logs outputSchema is not yet supported with non-OpenAI providers ... — proceeding without structured output and returns unstructured text.
Multi-turn behaviour
Structured output goes through the agent’s full message history, so follow-up prompts see prior turns and stay consistent.
const agent = new Agent({
instructions: 'Return the running total as JSON.',
llm: 'gpt-4o-mini',
outputSchema: {
type: 'object',
properties: { total: { type: 'number' } },
required: ['total']
}
});
await agent.chat('Add 5'); // {"total":5}
await agent.chat('Add 3'); // {"total":8} — remembers the previous turnHow this differs from generateObject
import { resolveBackend } from 'praisonai';
import { z } from 'zod';
const { provider } = await resolveBackend('openai/gpt-4o-mini');
const result = await provider.generateObject({
messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
schema: z.object({ location: z.string(), temperature: z.number() })
});
console.log(result.object); // { location: "Paris", temperature: 18 }See the Zod docs for schema construction.
Common Patterns
Extraction
const agent = new Agent({
instructions: 'Extract the invoice fields as JSON.',
llm: 'gpt-4o-mini',
outputSchema: {
type: 'object',
properties: {
invoiceNumber: { type: 'string' },
total: { type: 'number' },
dueDate: { type: 'string' }
},
required: ['invoiceNumber', 'total']
}
});
const json = await agent.start('Invoice INV-7 for $240, due 2026-01-15');Classification
const agent = new Agent({
instructions: 'Classify the email as JSON.',
llm: 'gpt-4o-mini',
outputSchema: {
type: 'object',
properties: {
category: { type: 'string', enum: ['spam', 'ham'] },
confidence: { type: 'number' }
},
required: ['category', 'confidence']
}
});
const json = await agent.start('You won $1,000,000! Click here!');
// {"category":"spam","confidence":0.96}Sentiment
const agent = new Agent({
instructions: 'Return sentiment as JSON.',
llm: 'gpt-4o-mini',
outputSchema: {
type: 'object',
properties: {
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
score: { type: 'number' }
},
required: ['sentiment']
}
});
const json = await agent.start('The service was fast and friendly.');Best Practices
Mark required fields
List every field you always expect in required. It nudges the model to include them and keeps the JSON predictable.
Use enums for fixed choices
Add enum on classification fields so the model can only pick valid values.
Parse the result
The agent returns a JSON string. Call JSON.parse(result) to get an object.
Works on every provider
Switching the llm string from openai/... to anthropic/..., google/..., or any other supported provider does not change your outputSchema code. Structured output is guaranteed on every provider from PraisonAI PR #4412 onward.