.Net: Add Structured output ChatClientAgent samples (#250)

* WIP

* Structured Output sample

* Update dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Address xml and comment targeting the Structured Output context

* Update with proposed fix for Persistent ChatClient

* Address PR feedback

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
This commit is contained in:
Roger Barreto
2025-07-31 12:35:29 +01:00
committed by GitHub
Unverified
parent caee8bfa90
commit 139f033b05
5 changed files with 112 additions and 13 deletions
@@ -0,0 +1,75 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
namespace Steps;
/// <summary>
/// Demonstrates how to use structured outputs with <see cref="ChatClientAgent"/>.
/// </summary>
public sealed class Step06_ChatClientAgent_StructuredOutputs(ITestOutputHelper output) : AgentSample(output)
{
/// <summary>
/// Demonstrates processing structured outputs using JSON schemas to extract information about a person.
/// </summary>
[Theory]
[InlineData(ChatClientProviders.AzureAIAgentsPersistent)]
[InlineData(ChatClientProviders.AzureOpenAI)]
[InlineData(ChatClientProviders.OpenAIAssistant)]
[InlineData(ChatClientProviders.OpenAIChatCompletion)]
[InlineData(ChatClientProviders.OpenAIResponses)]
public async Task RunWithCustomSchema(ChatClientProviders provider)
{
var agentOptions = new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
agentOptions.ChatOptions = new()
{
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
schemaName: "PersonInfo",
schemaDescription: "Information about a person including their name, age, and occupation"
)
};
// Create the server-side agent Id when applicable (depending on the provider).
agentOptions.Id = await base.AgentCreateAsync(provider, agentOptions);
using var chatClient = base.GetChatClient(provider, agentOptions);
ChatClientAgent agent = new(chatClient, agentOptions);
var thread = agent.GetNewThread();
const string Prompt = "Please provide information about John Smith, who is a 35-year-old software engineer.";
var updates = agent.RunStreamingAsync(Prompt, thread);
var agentResponse = await updates.ToAgentRunResponseAsync();
var personInfo = agentResponse.Deserialize<PersonInfo>(JsonSerializerOptions.Web);
Console.WriteLine("Assistant Output:");
Console.WriteLine($"Name: {personInfo.Name}");
Console.WriteLine($"Age: {personInfo.Age}");
Console.WriteLine($"Occupation: {personInfo.Occupation}");
// Clean up the server-side agent after use when applicable (depending on the provider).
await base.AgentCleanUpAsync(provider, agent, thread);
}
/// <summary>
/// Represents information about a person, including their name, age, and occupation, matched to the JSON schema used in the agent.
/// </summary>
public class PersonInfo
{
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("age")]
public int? Age { get; set; }
[JsonPropertyName("occupation")]
public string? Occupation { get; set; }
}
}
@@ -15,7 +15,7 @@ namespace Steps;
/// Demonstrates how to use <see cref="ChatClientAgent"/> with file search tools and file references.
/// Shows uploading files to different providers and using them with file search capabilities to retrieve and analyze information from documents.
/// </summary>
public sealed class Step04_ChatClientAgent_UsingFileSearchTools(ITestOutputHelper output) : AgentSample(output)
public sealed class Step07_ChatClientAgent_UsingFileSearchTools(ITestOutputHelper output) : AgentSample(output)
{
[Theory]
[InlineData(ChatClientProviders.AzureAIAgentsPersistent)]