mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
9506fb28f6
* .NET: Delete AgentResponse.{Try}Deserialize<T> methods (#3518)
* delete deserialize method of agent response
* order usings
* Update dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* .NET:[Breaking] Add support for structured output (#3658)
* add support for so
* restore lost xml comment part
* fix using ordering
* Update dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentStructuredOutput.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentStructuredOutput.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_SO_WithFormatResponseTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* addressw pr review comments
* address pr review feedback
* address pr review comments
* fix compilation issues after the latest merge with main
* remove unnecessry options
* remove RunAsync<object> methods
* address code review feedback
* address pr review feedback
* make copy constructor protected
* address pr review feedback
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* .NET: Add decorator for structured output support (#3694)
* add decorator that adds structured output support to agents that don't natively support it.
* Update dotnet/src/Microsoft.Agents.AI/StructuredOutput/StructuredOutputAgentResponse.cs
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
* Update dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
* address pr review feedback
---------
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
* .NET: Support primitives and arrays for SO (#3696)
* wrap primitives and arrays
* fix file encoding
* address review comments
* add adr
* add missed change
* fix compilation issue
* address review comments
* rename adr file name
* reflect decision to have SO decorator as a reference implementation in samples
* .NET: Move SO agent to samples (#3820)
* move SO agent to samples
* change file encoding
* fix files encoding
* .NET: Preserve caller context (#3803)
* fix stuck orchestration
* add previously removed RunAsync<T> method to DurableAIAgent
* suppress IDE0005 warning
* update changelog and remove unused constructor of AgentResponse<T>
* updatge the changelog
* address PR review feedback
* .NET: Disable irrelevant integration test (#3913)
* disable irrelevant integration test
* Update dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* forgotten change
* address pr review feedback
* disable intermittently failing integration test.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
184 lines
8.8 KiB
C#
184 lines
8.8 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
// This sample shows how to configure ChatClientAgent to produce structured output.
|
|
|
|
using System.ComponentModel;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Azure.AI.OpenAI;
|
|
using Azure.Identity;
|
|
using Microsoft.Agents.AI;
|
|
using Microsoft.Extensions.AI;
|
|
using OpenAI.Chat;
|
|
using SampleApp;
|
|
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
|
|
|
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
|
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
|
|
|
// Create chat client to be used by chat client agents.
|
|
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
|
|
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
|
|
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
|
|
ChatClient chatClient = new AzureOpenAIClient(
|
|
new Uri(endpoint),
|
|
new DefaultAzureCredential())
|
|
.GetChatClient(deploymentName);
|
|
|
|
// Demonstrates how to work with structured output via ResponseFormat with the non-generic RunAsync method.
|
|
// This approach is useful when:
|
|
// a. Structured output is used for inter-agent communication, where one agent produces structured output
|
|
// and passes it as text to another agent as input, without the need for the caller to directly work with the structured output.
|
|
// b. The type of the structured output is not known at compile time, so the generic RunAsync<T> method cannot be used.
|
|
// c. The type of the structured output is represented by JSON schema only, without a corresponding class or type in the code.
|
|
await UseStructuredOutputWithResponseFormatAsync(chatClient);
|
|
|
|
// Demonstrates how to work with structured output via the generic RunAsync<T> method.
|
|
// This approach is useful when the caller needs to directly work with the structured output in the code
|
|
// via an instance of the corresponding class or type and the type is known at compile time.
|
|
await UseStructuredOutputWithRunAsync(chatClient);
|
|
|
|
// Demonstrates how to work with structured output when streaming using the RunStreamingAsync method.
|
|
await UseStructuredOutputWithRunStreamingAsync(chatClient);
|
|
|
|
// Demonstrates how to add structured output support to agents that don't natively support it using the structured output middleware.
|
|
// This approach is useful when working with agents that don't support structured output natively, or agents using models
|
|
// that don't have the capability to produce structured output, allowing you to still leverage structured output features by transforming
|
|
// the text output from the agent into structured data using a chat client.
|
|
await UseStructuredOutputWithMiddlewareAsync(chatClient);
|
|
|
|
static async Task UseStructuredOutputWithResponseFormatAsync(ChatClient chatClient)
|
|
{
|
|
Console.WriteLine("=== Structured Output with ResponseFormat ===");
|
|
|
|
// Create the agent
|
|
AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions()
|
|
{
|
|
Name = "HelpfulAssistant",
|
|
ChatOptions = new()
|
|
{
|
|
Instructions = "You are a helpful assistant.",
|
|
// Specify CityInfo as the type parameter of ForJsonSchema to indicate the expected structured output from the agent.
|
|
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<CityInfo>()
|
|
}
|
|
});
|
|
|
|
// Invoke the agent with some unstructured input to extract the structured information from.
|
|
AgentResponse response = await agent.RunAsync("Provide information about the capital of France.");
|
|
|
|
// Access the structured output via the Text property of the agent response as JSON in scenarios when JSON as text is required
|
|
// and no object instance is needed (e.g., for logging, forwarding to another service, or storing in a database).
|
|
Console.WriteLine("Assistant Output (JSON):");
|
|
Console.WriteLine(response.Text);
|
|
Console.WriteLine();
|
|
|
|
// Deserialize the JSON text to work with the structured object in scenarios when you need to access properties,
|
|
// perform operations, or pass the data to methods that require the typed object instance.
|
|
CityInfo cityInfo = JsonSerializer.Deserialize<CityInfo>(response.Text)!;
|
|
|
|
Console.WriteLine("Assistant Output (Deserialized):");
|
|
Console.WriteLine($"Name: {cityInfo.Name}");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static async Task UseStructuredOutputWithRunAsync(ChatClient chatClient)
|
|
{
|
|
Console.WriteLine("=== Structured Output with RunAsync<T> ===");
|
|
|
|
// Create the agent
|
|
AIAgent agent = chatClient.AsAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
|
|
|
|
// Set CityInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke it with some unstructured input.
|
|
AgentResponse<CityInfo> response = await agent.RunAsync<CityInfo>("Provide information about the capital of France.");
|
|
|
|
// Access the structured output via the Result property of the agent response.
|
|
CityInfo cityInfo = response.Result;
|
|
|
|
Console.WriteLine("Assistant Output:");
|
|
Console.WriteLine($"Name: {cityInfo.Name}");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static async Task UseStructuredOutputWithRunStreamingAsync(ChatClient chatClient)
|
|
{
|
|
Console.WriteLine("=== Structured Output with RunStreamingAsync ===");
|
|
|
|
// Create the agent
|
|
AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions()
|
|
{
|
|
Name = "HelpfulAssistant",
|
|
ChatOptions = new()
|
|
{
|
|
Instructions = "You are a helpful assistant.",
|
|
// Specify CityInfo as the type parameter of ForJsonSchema to indicate the expected structured output from the agent.
|
|
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<CityInfo>()
|
|
}
|
|
});
|
|
|
|
// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
|
|
IAsyncEnumerable<AgentResponseUpdate> updates = agent.RunStreamingAsync("Provide information about the capital of France.");
|
|
|
|
// Assemble all the parts of the streamed output.
|
|
AgentResponse nonGenericResponse = await updates.ToAgentResponseAsync();
|
|
|
|
// Access the structured output by deserializing JSON in the Text property.
|
|
CityInfo cityInfo = JsonSerializer.Deserialize<CityInfo>(nonGenericResponse.Text)!;
|
|
|
|
Console.WriteLine("Assistant Output:");
|
|
Console.WriteLine($"Name: {cityInfo.Name}");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static async Task UseStructuredOutputWithMiddlewareAsync(ChatClient chatClient)
|
|
{
|
|
Console.WriteLine("=== Structured Output with UseStructuredOutput Middleware ===");
|
|
|
|
// Create chat client that will transform the agent text response into structured output.
|
|
IChatClient meaiChatClient = chatClient.AsIChatClient();
|
|
|
|
// Create the agent
|
|
AIAgent agent = meaiChatClient.AsAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
|
|
|
|
// Add structured output middleware via UseStructuredOutput method to add structured output support to the agent.
|
|
// This middleware transforms the agent's text response into structured data using a chat client.
|
|
// Since our agent does support structured output natively, we will add a middleware that removes ResponseFormat
|
|
// from the AgentRunOptions to emulate an agent that doesn't support structured output natively
|
|
agent = agent
|
|
.AsBuilder()
|
|
.UseStructuredOutput(meaiChatClient)
|
|
.Use(ResponseFormatRemovalMiddleware, null)
|
|
.Build();
|
|
|
|
// Set CityInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke it with some unstructured input.
|
|
AgentResponse<CityInfo> response = await agent.RunAsync<CityInfo>("Provide information about the capital of France.");
|
|
|
|
// Access the structured output via the Result property of the agent response.
|
|
CityInfo cityInfo = response.Result;
|
|
|
|
Console.WriteLine("Assistant Output:");
|
|
Console.WriteLine($"Name: {cityInfo.Name}");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static Task<AgentResponse> ResponseFormatRemovalMiddleware(IEnumerable<ChatMessage> messages, AgentSession? session, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken)
|
|
{
|
|
// Remove any ResponseFormat from the options to emulate an agent that doesn't support structured output natively.
|
|
options = options?.Clone();
|
|
options?.ResponseFormat = null;
|
|
|
|
return innerAgent.RunAsync(messages, session, options, cancellationToken);
|
|
}
|
|
|
|
namespace SampleApp
|
|
{
|
|
/// <summary>
|
|
/// Represents information about a city, including its name.
|
|
/// </summary>
|
|
[Description("Information about a city")]
|
|
public sealed class CityInfo
|
|
{
|
|
[JsonPropertyName("name")]
|
|
public string? Name { get; set; }
|
|
}
|
|
}
|