Structured output lets you define exactly how you want your AI responses formatted. Instead of parsing free-form text, you get clean, predictable data structures that are ready to use in your application.
Type-Safe Responses
Define schemas and get responses that match your expected structure
No Parsing Required
Skip the regex and string parsing - data comes pre-formatted
Provider Validation
Providers like OpenAI enforce schema compliance at generation time
Works With Tools
Combine structured output with tool calls for powerful workflows
To enable structured output, override the getSchema() method in your agent:
app/Agents/MovieReviewAgent.php
Copy
<?phpnamespace App\Agents;use Prism\Prism\Contracts\Schema;use Prism\Prism\Schema\ObjectSchema;use Prism\Prism\Schema\StringSchema;use Prism\Prism\Schema\NumberSchema;use Vizra\VizraADK\Agents\BaseLlmAgent;class MovieReviewAgent extends BaseLlmAgent{ protected string $name = 'movie_review_agent'; protected string $description = 'Generates structured movie reviews'; protected string $instructions = 'You are a movie critic. Analyze films and provide detailed reviews.'; protected string $model = 'gpt-4o'; public function getSchema(): ?Schema { return new ObjectSchema( name: 'movie_review', description: 'A structured movie review', properties: [ new StringSchema('title', 'The movie title'), new NumberSchema('rating', 'Rating from 1 to 10'), new StringSchema('summary', 'Brief review summary'), new StringSchema('recommendation', 'Who should watch this movie'), ], requiredFields: ['title', 'rating', 'summary', 'recommendation'] ); }}
When you run this agent, instead of free-form text you’ll get structured data:
Using the Structured Agent
Copy
$agent = app(MovieReviewAgent::class);$response = $agent->run('Review the movie Inception', $context);// The response text contains the JSON, but you can also access it structured// via the underlying Prism response when using afterLlmResponse hook
use Prism\Prism\Schema\ArraySchema;use Prism\Prism\Schema\StringSchema;new ArraySchema( name: 'tags', description: 'List of relevant tags', items: new StringSchema('tag', 'A single tag'))
For flexible data that can match one of several schemas.
Copy
use Prism\Prism\Schema\AnyOfSchema;use Prism\Prism\Schema\StringSchema;use Prism\Prism\Schema\NumberSchema;new AnyOfSchema( schemas: [ new StringSchema('text', 'A text value'), new NumberSchema('number', 'A numeric value'), ], name: 'flexible_value', description: 'Can be either text or a number')
Provider Compatibility: AnyOfSchema works with OpenAI and Gemini, but is not supported by Anthropic. Design alternative schema patterns when targeting Anthropic.
<?phpnamespace App\Agents;use Prism\Prism\Contracts\Schema;use Prism\Prism\Schema\ArraySchema;use Prism\Prism\Schema\ObjectSchema;use Prism\Prism\Schema\StringSchema;use Prism\Prism\Schema\BooleanSchema;use Vizra\VizraADK\Agents\BaseLlmAgent;class ContactExtractorAgent extends BaseLlmAgent{ protected string $name = 'contact_extractor'; protected string $description = 'Extracts contact information from text'; protected string $instructions = 'Extract all contact information from the provided text. Be thorough but only include information explicitly mentioned.'; protected string $model = 'gpt-4o'; public function getSchema(): ?Schema { return new ObjectSchema( name: 'extracted_contacts', description: 'Extracted contact information', properties: [ new ArraySchema( name: 'contacts', description: 'List of extracted contacts', items: new ObjectSchema( name: 'contact', description: 'A single contact', properties: [ new StringSchema('name', 'Full name'), new StringSchema('email', 'Email address', nullable: true), new StringSchema('phone', 'Phone number', nullable: true), new StringSchema('company', 'Company name', nullable: true), new StringSchema('role', 'Job title or role', nullable: true), ], requiredFields: ['name', 'email', 'phone', 'company', 'role'] ) ), new NumberSchema('confidence', 'Confidence score from 0 to 1'), ], requiredFields: ['contacts', 'confidence'] ); }}
<?phpnamespace App\Agents;use Prism\Prism\Contracts\Schema;use Prism\Prism\Schema\ArraySchema;use Prism\Prism\Schema\EnumSchema;use Prism\Prism\Schema\NumberSchema;use Prism\Prism\Schema\ObjectSchema;use Prism\Prism\Schema\StringSchema;use Vizra\VizraADK\Agents\BaseLlmAgent;class SentimentAnalysisAgent extends BaseLlmAgent{ protected string $name = 'sentiment_analysis'; protected string $description = 'Analyzes sentiment in text'; protected string $instructions = 'Analyze the sentiment of the provided text. Consider overall tone, specific aspects mentioned, and provide reasoning for your analysis.'; protected string $model = 'gpt-4o'; public function getSchema(): ?Schema { return new ObjectSchema( name: 'sentiment_analysis', description: 'Detailed sentiment analysis results', properties: [ new EnumSchema( name: 'overall_sentiment', description: 'The overall sentiment', options: ['very_negative', 'negative', 'neutral', 'positive', 'very_positive'] ), new NumberSchema('confidence', 'Confidence score from 0 to 1'), new ArraySchema( name: 'aspects', description: 'Sentiment by aspect', items: new ObjectSchema( name: 'aspect', description: 'Sentiment for a specific aspect', properties: [ new StringSchema('aspect', 'The aspect being analyzed'), new EnumSchema('sentiment', 'Sentiment for this aspect', options: ['negative', 'neutral', 'positive'] ), new StringSchema('evidence', 'Quote or evidence from the text'), ], requiredFields: ['aspect', 'sentiment', 'evidence'] ) ), new StringSchema('reasoning', 'Explanation of the analysis'), ], requiredFields: ['overall_sentiment', 'confidence', 'aspects', 'reasoning'] ); }}
Mark fields as nullable when they might not have a value:
Nullable Fields
Copy
new ObjectSchema( name: 'user_profile', description: 'User profile information', properties: [ new StringSchema('name', 'User name'), new StringSchema('email', 'Email address'), new StringSchema('bio', 'User biography', nullable: true), new StringSchema('website', 'Personal website', nullable: true), ], requiredFields: ['name', 'email', 'bio', 'website'])
OpenAI Strict Mode: When using OpenAI’s strict mode, all fields must be listed in requiredFields. Use nullable: true to indicate optional fields that can have null values.
Structured output works seamlessly with tools. The agent can call tools to gather information, then return a structured response:
app/Agents/WeatherReportAgent.php
Copy
<?phpnamespace App\Agents;use App\Tools\GetWeatherTool;use App\Tools\GetForecastTool;use Prism\Prism\Contracts\Schema;use Prism\Prism\Schema\ArraySchema;use Prism\Prism\Schema\NumberSchema;use Prism\Prism\Schema\ObjectSchema;use Prism\Prism\Schema\StringSchema;use Vizra\VizraADK\Agents\BaseLlmAgent;class WeatherReportAgent extends BaseLlmAgent{ protected string $name = 'weather_report'; protected string $description = 'Generates structured weather reports'; protected string $instructions = 'Use the weather tools to gather data, then compile a structured weather report.'; protected string $model = 'gpt-4o'; protected int $maxSteps = 5; protected array $tools = [ GetWeatherTool::class, GetForecastTool::class, ]; public function getSchema(): ?Schema { return new ObjectSchema( name: 'weather_report', description: 'Comprehensive weather report', properties: [ new StringSchema('location', 'Location name'), new ObjectSchema( name: 'current', description: 'Current conditions', properties: [ new NumberSchema('temperature', 'Temperature in Fahrenheit'), new StringSchema('conditions', 'Weather conditions'), new NumberSchema('humidity', 'Humidity percentage'), ], requiredFields: ['temperature', 'conditions', 'humidity'] ), new ArraySchema( name: 'forecast', description: '5-day forecast', items: new ObjectSchema( name: 'day', description: 'Daily forecast', properties: [ new StringSchema('day', 'Day of week'), new NumberSchema('high', 'High temperature'), new NumberSchema('low', 'Low temperature'), new StringSchema('conditions', 'Expected conditions'), ], requiredFields: ['day', 'high', 'low', 'conditions'] ) ), new StringSchema('recommendation', 'Clothing/activity recommendation'), ], requiredFields: ['location', 'current', 'forecast', 'recommendation'] ); }}
When using tools with structured output, set $maxSteps to at least 2. The agent needs multiple steps: one to call tools, and another to return the structured result.
Always use ObjectSchema as your top-level schema. Providers like OpenAI require this in strict mode
Write Clear Descriptions
Descriptive field descriptions help the LLM understand what data to provide
Mark Optional Fields Nullable
Use nullable: true for fields that might not have values
Validate Responses
Even with structured output, validate the response in your application
Schema Limitations: While structured output provides schema enforcement at generation time, it doesn’t guarantee semantic correctness. Always validate that the data makes sense for your use case.