Overhaul orchestration library with new approach (#199)

This commit is contained in:
Stephen Toub
2025-07-23 12:29:37 -04:00
committed by GitHub
Unverified
parent 27f7af2160
commit 5472d6e996
104 changed files with 1212 additions and 5084 deletions
@@ -1,21 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using System.Text.Json.Serialization;
using HelloHttpApi.ApiService;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
/// <summary>
/// Source-generated JSON type information for use by all Agents implementations.
/// </summary>
[JsonSourceGenerationOptions(
JsonSerializerDefaults.Web,
UseStringEnumConverter = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = false)]
[JsonSerializable(typeof(ChatMessage))]
[JsonSerializable(typeof(List<ChatMessage>))]
[JsonSerializable(typeof(ChatClientAgentThread))]
[JsonSerializable(typeof(ChatClientAgentRunRequest))]
[JsonSerializable(typeof(AgentRunResponseUpdate))]
internal sealed partial class AgentsJsonContext : JsonSerializerContext;
@@ -8,7 +8,7 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
internal sealed class ChatClientAgentActor(ChatClientAgent agent, JsonSerializerOptions jsonSerializerOptions, IActorRuntimeContext context, ILogger<ChatClientAgentActor> logger) : IActor
internal sealed class ChatClientAgentActor(AIAgent agent, JsonSerializerOptions jsonSerializerOptions, IActorRuntimeContext context, ILogger<ChatClientAgentActor> logger) : IActor
{
private string? _etag;
private ChatClientAgentThread? _thread;
@@ -36,7 +36,7 @@ internal sealed class ChatClientAgentActor(ChatClientAgent agent, JsonSerializer
}
}
this._thread ??= (ChatClientAgentThread)agent.GetNewThread();
this._thread ??= agent.GetNewThread() as ChatClientAgentThread ?? throw new InvalidOperationException("The agent did not provide a valid thread instance.");
Log.ThreadStateRestored(logger, context.ActorId.ToString(), response.Results[0] is GetValueResult { Value: not null });
while (!cancellationToken.IsCancellationRequested)
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.Agents.Orchestration\Microsoft.Agents.Orchestration.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.Extensions.AI.Agents.Abstractions\Microsoft.Extensions.AI.Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.Extensions.AI.Agents.Runtime.Abstractions\Microsoft.Extensions.AI.Agents.Runtime.Abstractions.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.Extensions.AI.Agents.Runtime\Microsoft.Extensions.AI.Agents.Runtime.csproj" />
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using Microsoft.Agents.Orchestration;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
@@ -9,20 +10,28 @@ namespace HelloHttpApi.ApiService;
public static class HostApplicationBuilderAgentExtensions
{
public static IHostApplicationBuilder AddChatClientAgent(this IHostApplicationBuilder builder, string name, string instructions, string? chatClientKey = null)
public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, string instructions, string? chatClientKey = null)
{
var agentKey = $"agent:{name}";
builder.Services.AddKeyedSingleton(agentKey, (sp, key) =>
builder.Services.AddKeyedSingleton<AIAgent>(agentKey, (sp, key) =>
{
var chatClient = chatClientKey is null ? sp.GetRequiredService<IChatClient>() : sp.GetRequiredKeyedService<IChatClient>(chatClientKey);
return new ChatClientAgent(chatClient, instructions, name);
ChatClientAgent triage = new(chatClient, "You are a triage agent. You will determine which agent to hand off the conversation to based on the user's input.", $"{name}_triageAgent");
ChatClientAgent target = new(chatClient, instructions, $"{name}_targetAgent");
ChatClientAgent customerService = new(chatClient, "You are a customer service agent. You will handle rude, angry, or upset customer inquiries, asking them to be more calm and polite.", $"{name}_customerServiceAgent");
return new HandoffOrchestration(OrchestrationHandoffs
.StartWith(triage)
.Add(triage, target, "Hand off to the target agent for handling normal customer requests.")
.Add(triage, customerService, "Hand off to the customer service agent for handling rude customer inquiries."));
});
var actorBuilder = builder.AddActorRuntime();
actorBuilder.AddActorType(
new ActorType(agentKey),
(sp, ctx) => new ChatClientAgentActor(
sp.GetRequiredKeyedService<ChatClientAgent>(agentKey),
sp.GetRequiredKeyedService<AIAgent>(agentKey),
sp.GetService<JsonSerializerOptions>() ?? JsonSerializerOptions.Web,
ctx,
sp.GetRequiredService<ILogger<ChatClientAgentActor>>()));
@@ -14,7 +14,7 @@ builder.Services.AddProblemDetails();
// Configure the chat model and our agent.
builder.AddKeyedChatClient("chat-model");
builder.AddChatClientAgent(
builder.AddAIAgent(
name: "pirate",
instructions: "You are a pirate. Speak like a pirate.",
chatClientKey: "chat-model");