Producing Structured Output with Agents
Table of Contents
- Introduction
- Prerequisites
- Why Use Structured Output?
- Core Concepts
- Step-by-Step: Creating a Structured Output Agent
- Demo 1 — Product Information Extraction
- Demo 2 — Event Planning
- Demo 3 — Recipe Parsing
- Demo 4 — Streaming with Structured Output
- Demo 5 — Complex Nested Structures
- Best Practices
- Debugging and Troubleshooting
- Summary
1. Introduction
Not all agent types support structured output. This lesson uses a ChatClientAgent, which does support structured output. The ChatClientAgent is built on top of any IChatClient implementation and uses the support for structured output that is provided by the underlying chat client.
When creating the agent, you have the option to provide a default ChatOptions instance to use for the underlying chat client. This ChatOptions instance allows you to pick a preferred ChatResponseFormat.
Getting structured output from AI agents you develop with the Microsoft Agent Framework enables you to obtain consistent and processable data in your applications. In this lesson, you will learn how to get outputs from agents that conform to a specific JSON schema. Instead of parsing free-form text, you will directly deserialize agent responses into strongly-typed C# objects.
2. Prerequisites
Before you begin, make sure you have:
- .NET 10 SDK installed
- An OpenAI API key (set the
OPEN_AI_KEYenvironment variable) - The following NuGet packages:
Microsoft.Agents.AIMicrosoft.Extensions.AI.OpenAI
3. Why Use Structured Output?
- Type Safety — AI outputs are directly deserialized into C# objects.
- Consistency — You always receive data in the same format.
- Validation — Data that does not conform to the schema is automatically rejected by the model.
- No Parsing Required — Direct object usage instead of text parsing.
- Easy Integration — Simple integration with databases, APIs, and other systems.
4. Core Concepts
4.1. ChatResponseFormat Types
Various options for ResponseFormat are available:
| Format | Description | Use Case |
|---|---|---|
ChatResponseFormat.Text | The response will be plain text. | Normal chat, explanations, storytelling |
ChatResponseFormat.Json | The response will be a JSON object without any particular schema. | Flexible JSON structures, dynamic data |
ChatResponseFormat.ForJsonSchema() | The response will be a JSON object that conforms to a specific schema. | Structured data, when type safety is needed |
4.2. AIJsonUtilities.CreateJsonSchema
The easiest way to produce the schema is to define a type that represents the structure of the output you want from the agent, and then use the AIJsonUtilities.CreateJsonSchema method to create a schema from the type. This eliminates the need to write JSON schemas manually.
4.3. response.Deserialize<T>()
The agent response can then be deserialized into your class using the Deserialize<T> method on the response object.
5. Step-by-Step: Creating a Structured Output Agent
Step 1 — Define the Model Class
First, define the data structure you want as a C# class:
Note: Use[JsonPropertyName]attributes to control JSON field names. Use nullable types (string?,decimal?) for flexibility. Properties must be public with getters and setters.
Step 2 — Configure Schema with ChatOptions
You can then create a ChatOptions instance that uses this schema for the response format.
Step 3 — Create the Agent
This ChatOptions instance can be used when creating the agent.
Step 4 — Run the Agent and Deserialize
Now you can just run the agent with some textual information that the agent can use to fill in the structured output.
6. Demo 1 — Product Information Extraction
This demo shows how to extract structured product information from unstructured text descriptions. The agent automatically identifies the product name, brand, price, category, and key features.
Use Cases:
- Product cataloging in e-commerce sites
- Price comparison applications
- Product data collection and analysis
The agent is configured with a ProductInfo schema and given two different product descriptions — a laptop and a smartphone. It parses each description into the same consistent structure.
7. Demo 2 — Event Planning
This demo shows how to generate structured event plans from natural language descriptions. The agent extracts title, date, location, expected attendee count, budget, and a list of activities.
Use Cases:
- Corporate event management
- Training and seminar organization
- Social event planning assistants
Given a natural language request like "I need to organize a team building event for 50 people with a $5,000 budget", the agent fills in all the fields consistently.
8. Demo 3 — Recipe Parsing
This demo shows how to parse recipe information from natural language descriptions into a structured format. The agent extracts recipe name, cuisine type, preparation time, servings, a list of ingredients, and cooking steps.
Use Cases:
- Recipe applications
- Diet and nutrition tracking
- Kitchen assistant chatbots
The demo provides two recipe descriptions — an Italian Carbonara and a Thai Green Curry — and the agent parses both into the same consistent structure.
9. Demo 4 — Streaming with Structured Output
When streaming, the agent response is streamed as a series of updates, and you can only deserialize the response once all the updates have been received. You must assemble all the updates into a single response before deserializing it.
Use Cases:
- Long-running data extraction operations
- Showing progress to users
- Real-time data visualization
The key extension method is ToAgentResponseAsync(), which collects all streaming updates into a single AgentResponse that can then be deserialized.
10. Demo 5 — Complex Nested Structures
This demo shows how to work with complex nested structures containing multiple levels of objects and arrays. It extracts book information including nested author details and multiple user reviews.
Use Cases:
- Book and media cataloging
- Product reviews and ratings
- Hierarchical data structures
After deserialization, you can access nested data programmatically:
11. Best Practices
Do's
- Use clear and meaningful property names
- Specify snake_case JSON names with
[JsonPropertyName] - Provide flexibility by using nullable types
- Write detailed schema descriptions
- Add model validations (DataAnnotations)
- Implement error handling (try-catch)
Don'ts
- Don't create overly complex nested structures (more than 3 levels deep)
- Don't write conflicting schema definitions and instructions
- Don't request large lists or arrays (performance issues)
- Don't use the same schema for every agent — customize as needed
12. Debugging and Troubleshooting
Problem: Cannot deserialize the response.
Solution: Ensure JSON property names match your C# properties. Check [JsonPropertyName] usage.
Problem: Some fields are missing or null.
Solution: Write your schema description in more detail. Emphasize the importance of each field in the agent instructions.
Problem: Wrong types are returned.
Solution: Use nullable types and add custom JsonConverters where needed.
13. Summary
In this lesson, we learned the fundamentals of getting structured output with the Microsoft Agent Framework:
- Schema creation with
AIJsonUtilities.CreateJsonSchema - Agent configuration with
ChatResponseFormat.ForJsonSchema - Type conversion with
response.Deserialize<T>() - Streaming with structured output using
ToAgentResponseAsync() - Complex nested structures with multiple levels of objects and arrays
Useful Resources
- Official Documentation — Structured Output
- Microsoft Agent Framework GitHub
- Microsoft.Extensions.AI API Reference