Microsoft Agent Framework Microsoft.Extensions.AI Created: 12 Jul 2026 Updated: 12 Jul 2026

Getting Structured Output from a Chat Model

Instead of getting a plain-text response from a model, we often want the response to conform to a specific .NET type (an enum, a record, etc.) so it can be used directly in code without brittle text parsing. Microsoft.Extensions.AI supports this via the GetResponseAsync<T> extension method: by passing a type argument, the response is automatically deserialized into that type.

Reference

  1. Structured output quickstart

Step-by-Step Walkthrough

1. Structured Output Types

Two types are defined for this example:

  1. Sentiment: an enum representing the sentiment values the model will classify reviews into (Positive, Negative, Neutral).
  2. SentimentRecord: a record that captures both a free-text response and the analyzed sentiment value together, so the model can return a short explanation alongside the classification.

2. Setup

The API key is read from the OPEN_AI_KEY environment variable, and an IChatClient is created targeting the gpt-4o-mini model.

3. Example 1: Sentiment Analysis of a Single Review (enum)

GetResponseAsync<Sentiment> is called with a prompt asking for the sentiment of a review. The model's answer is deserialized directly into the Sentiment enum, so response.Result is a strongly typed enum value rather than a string that would need to be parsed.

4. Example 2: Sentiment Analysis of Multiple Reviews

The same pattern is applied in a loop over several review strings, showing that structured output works consistently across different, and even ambiguous or short (e.g., "Hello"), inputs.

5. Example 3: Getting Response Text and Sentiment Together (record)

GetResponseAsync<SentimentRecord> is used so the model returns both a natural-language response (ResponseText) and the classified sentiment (ReviewSentiment) in a single structured result, avoiding the need for two separate calls.

Full Code Example

using Microsoft.Extensions.AI;
using OpenAI;

namespace MicrosoftAgentFrameworkLesson.ConsoleApp
{
// StructuredOutput: An example of requesting a structured response from the model
// that conforms to a .NET type we specify (enum, record, etc.) instead of plain text.
// A type argument is passed to the GetResponseAsync<T> extension method so the
// response is deserialized into that type.
// Source: https://learn.microsoft.com/en-us/dotnet/ai/quickstarts/structured-output
public static class StructuredOutputDemo
{
#region Structured Output Types
// The sentiment values the model will classify reviews into
public enum Sentiment
{
Positive,
Negative,
Neutral
}

// A record type to get both the text response and the analyzed sentiment together
public record SentimentRecord(string ResponseText, Sentiment ReviewSentiment);
#endregion

public static async Task RunAsync()
{
#region Setup
var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY")
?? throw new InvalidOperationException("Please set the OPEN_AI_KEY environment variable.");

IChatClient chatClient = new OpenAIClient(apiKey)
.GetChatClient("gpt-4o-mini")
.AsIChatClient();

Console.WriteLine("====== Structured Output Example ======\n");
#endregion

#region Example 1: Sentiment Analysis of a Single Review (enum)
string review = "I'm happy with the product!";
var response = await chatClient.GetResponseAsync<Sentiment>(
$"What's the sentiment of this review? {review}");

Console.WriteLine($"Review: {review}");
Console.WriteLine($"Sentiment: {response.Result}\n");
#endregion

#region Example 2: Sentiment Analysis of Multiple Reviews
string[] inputs =
[
"Best purchase ever!",
"Returned it immediately.",
"Hello",
"It works as advertised.",
"The packaging was damaged but otherwise okay."
];

foreach (var input in inputs)
{
var response2 = await chatClient.GetResponseAsync<Sentiment>(
$"What's the sentiment of this review? {input}");
Console.WriteLine($"Review: {input} | Sentiment: {response2.Result}");
}
Console.WriteLine();
#endregion

#region Example 3: Getting Response Text and Sentiment Together (record)
var review3 = "This product worked okay.";
var response3 = await chatClient.GetResponseAsync<SentimentRecord>(
$"What's the sentiment of this review? {review3}");

Console.WriteLine($"Review: {review3}");
Console.WriteLine($"Response text: {response3.Result.ResponseText}");
Console.WriteLine($"Sentiment: {response3.Result.ReviewSentiment}");
#endregion
}
}
}

Summary

Structured output lets you skip fragile string parsing of model responses. By specifying a .NET type as the generic argument to GetResponseAsync<T>, you get a strongly typed, ready-to-use result—whether it's a simple enum classification or a richer record combining multiple pieces of information from a single model call.


Share this lesson: