mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.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:
committed by
GitHub
Unverified
parent
caee8bfa90
commit
139f033b05
@@ -29,12 +29,7 @@
|
||||
<BuildType Solution="Publish|*" Project="Release" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/Samples/">
|
||||
<Project Path="samples/GettingStarted/GettingStarted.csproj">
|
||||
<BuildType Solution="Publish|*" Project="Debug" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/Samples/HelloHttpApi/">
|
||||
<Folder Name="/Demos/HelloHttpApi/">
|
||||
<Project Path="samples/HelloHttpApi/HelloHttpApi.ApiService/HelloHttpApi.ApiService.csproj">
|
||||
<BuildType Solution="Publish|*" Project="Release" />
|
||||
</Project>
|
||||
@@ -48,6 +43,11 @@
|
||||
<BuildType Solution="Publish|*" Project="Release" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/Samples/">
|
||||
<Project Path="samples/GettingStarted/GettingStarted.csproj">
|
||||
<BuildType Solution="Publish|*" Project="Debug" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
<File Path=".gitignore" />
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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)]
|
||||
@@ -371,13 +371,35 @@ namespace Azure.AI.Agents.Persistent
|
||||
{
|
||||
if (options.ResponseFormat is ChatResponseFormatJson jsonFormat)
|
||||
{
|
||||
runOptions.ResponseFormat = jsonFormat.Schema is { } schema ?
|
||||
BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(new Dictionary<string, object?>()
|
||||
if (jsonFormat.Schema is JsonElement schema)
|
||||
{
|
||||
var schemaNode = JsonSerializer.SerializeToNode(schema, AgentsChatClientJsonContext.Default.JsonElement)!;
|
||||
|
||||
var jsonSchemaObject = new JsonObject
|
||||
{
|
||||
["type"] = "json_schema",
|
||||
["json_schema"] = JsonSerializer.SerializeToNode(schema, AgentsChatClientJsonContext.Default.JsonNode),
|
||||
}, AgentsChatClientJsonContext.Default.JsonObject)) :
|
||||
BinaryData.FromString("""{ "type": "json_object" }""");
|
||||
["schema"] = schemaNode
|
||||
};
|
||||
|
||||
if (jsonFormat.SchemaName is not null)
|
||||
{
|
||||
jsonSchemaObject["name"] = jsonFormat.SchemaName;
|
||||
}
|
||||
if (jsonFormat.SchemaDescription is not null)
|
||||
{
|
||||
jsonSchemaObject["description"] = jsonFormat.SchemaDescription;
|
||||
}
|
||||
|
||||
runOptions.ResponseFormat =
|
||||
BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(new()
|
||||
{
|
||||
["type"] = "json_schema",
|
||||
["json_schema"] = jsonSchemaObject,
|
||||
}, AgentsChatClientJsonContext.Default.JsonObject));
|
||||
}
|
||||
else
|
||||
{
|
||||
runOptions.ResponseFormat = BinaryData.FromString("""{ "type": "json_object" }""");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -879,7 +879,9 @@ public class ChatClientAgentThreadTests
|
||||
public void VerifyJsonDeserialization_HandlesMalformedJson()
|
||||
{
|
||||
// Arrange - Invalid JSON structure
|
||||
#pragma warning disable JSON001 // Invalid JSON pattern
|
||||
string invalidJson = "{ invalid json";
|
||||
#pragma warning restore JSON001 // Invalid JSON pattern
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ChatClientAgentThread>(invalidJson));
|
||||
|
||||
Reference in New Issue
Block a user