4.9 KiB
4.9 KiB
Structured Output Guide
Last Updated: 2025-10-25
Best practices for using JSON schemas with OpenAI's structured outputs feature.
When to Use Structured Outputs
Use structured outputs when you need:
- ✅ Guaranteed JSON format: Response will always be valid JSON
- ✅ Schema validation: Enforce specific structure
- ✅ Type safety: Parse directly into TypeScript types
- ✅ Data extraction: Pull specific fields from text
- ✅ Classification: Map to predefined categories
Schema Best Practices
1. Keep Schemas Simple
// ✅ Good: Simple, focused schema
{
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' },
},
required: ['name', 'age'],
additionalProperties: false,
}
// ❌ Avoid: Overly complex nested structures
// (they work but are harder to debug)
2. Use Enums for Fixed Options
{
type: 'object',
properties: {
category: {
type: 'string',
enum: ['bug', 'feature', 'question'],
},
priority: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
},
},
required: ['category', 'priority'],
}
3. Always Use strict: true
response_format: {
type: 'json_schema',
json_schema: {
name: 'response_schema',
strict: true, // ✅ Enforces exact compliance
schema: { /* ... */ },
},
}
4. Set additionalProperties: false
{
type: 'object',
properties: { /* ... */ },
required: [ /* ... */ ],
additionalProperties: false, // ✅ Prevents unexpected fields
}
Common Use Cases
Data Extraction
const schema = {
type: 'object',
properties: {
person: { type: 'string' },
company: { type: 'string' },
email: { type: 'string' },
phone: { type: 'string' },
},
required: ['person'],
additionalProperties: false,
};
// Extract from unstructured text
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Extract contact information' },
{ role: 'user', content: 'John works at TechCorp, email: john@tech.com' },
],
response_format: { type: 'json_schema', json_schema: { name: 'contact', strict: true, schema } },
});
const contact = JSON.parse(completion.choices[0].message.content);
// { person: "John", company: "TechCorp", email: "john@tech.com", phone: null }
Classification
const schema = {
type: 'object',
properties: {
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
confidence: { type: 'number' },
topics: { type: 'array', items: { type: 'string' } },
},
required: ['sentiment', 'confidence', 'topics'],
additionalProperties: false,
};
// Classify text
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Classify the text' },
{ role: 'user', content: 'This product is amazing!' },
],
response_format: { type: 'json_schema', json_schema: { name: 'classification', strict: true, schema } },
});
const result = JSON.parse(completion.choices[0].message.content);
// { sentiment: "positive", confidence: 0.95, topics: ["product", "satisfaction"] }
TypeScript Integration
Type-Safe Parsing
interface PersonProfile {
name: string;
age: number;
skills: string[];
}
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' },
skills: { type: 'array', items: { type: 'string' } },
},
required: ['name', 'age', 'skills'],
additionalProperties: false,
};
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Generate a person profile' }],
response_format: { type: 'json_schema', json_schema: { name: 'person', strict: true, schema } },
});
const person: PersonProfile = JSON.parse(completion.choices[0].message.content);
// TypeScript knows the shape!
Error Handling
try {
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
response_format: { type: 'json_schema', json_schema: { name: 'data', strict: true, schema } },
});
const data = JSON.parse(completion.choices[0].message.content);
return data;
} catch (error) {
if (error.message.includes('JSON')) {
console.error('Failed to parse JSON (should not happen with strict mode)');
}
throw error;
}
Validation
While strict: true ensures the response matches the schema, you may want additional validation:
import { z } from 'zod';
const zodSchema = z.object({
email: z.string().email(),
age: z.number().min(0).max(120),
});
const data = JSON.parse(completion.choices[0].message.content);
const validated = zodSchema.parse(data); // Throws if invalid
See Also: Official Structured Outputs Guide (https://platform.openai.com/docs/guides/structured-outputs)