mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Overhaul orchestration library with new approach (#199)
This commit is contained in:
committed by
GitHub
Unverified
parent
27f7af2160
commit
5472d6e996
@@ -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" />
|
||||
|
||||
+13
-4
@@ -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");
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.AI.Agents;
|
||||
|
||||
@@ -10,11 +9,6 @@ namespace HelloHttpApi.Web;
|
||||
|
||||
public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
{
|
||||
private static readonly JsonSerializerOptions s_jsonOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
public async IAsyncEnumerable<AgentRunResponseUpdate> SendMessageStreamAsync(
|
||||
string agentName,
|
||||
string message,
|
||||
@@ -27,7 +21,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
Messages = [new ChatMessage(ChatRole.User, message)]
|
||||
};
|
||||
|
||||
var content = JsonContent.Create(request, s_jsonOptions.GetTypeInfo<ChatClientAgentRunRequest>(AgentClientJsonContext.Default));
|
||||
var content = JsonContent.Create(request, AgentClientJsonContext.Default.ChatClientAgentRunRequest);
|
||||
|
||||
var requestUri = new Uri($"/invocations/actor/{agentName}/{sessionId}/{requestId}?stream=true", UriKind.Relative);
|
||||
|
||||
@@ -82,7 +76,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
Messages = [new ChatMessage(ChatRole.User, message)]
|
||||
};
|
||||
|
||||
var content = JsonContent.Create(request, s_jsonOptions.GetTypeInfo<ChatClientAgentRunRequest>(AgentClientJsonContext.Default));
|
||||
var content = JsonContent.Create(request, AgentClientJsonContext.Default.ChatClientAgentRunRequest);
|
||||
|
||||
var requestUri = new Uri($"/invocations/actor/{agentName}/{sessionId}/{requestId}?stream=false", UriKind.Relative);
|
||||
|
||||
@@ -96,7 +90,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
|
||||
try
|
||||
{
|
||||
var agentResponse = await response.Content.ReadFromJsonAsync(s_jsonOptions.GetTypeInfo<AgentResponse>(AgentClientJsonContext.Default), cancellationToken);
|
||||
var agentResponse = await response.Content.ReadFromJsonAsync(AgentClientJsonContext.Default.AgentResponse, cancellationToken);
|
||||
return agentResponse ?? new AgentResponse { Content = "No response received", Status = "error" };
|
||||
}
|
||||
catch (JsonException ex)
|
||||
@@ -113,7 +107,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
|
||||
try
|
||||
{
|
||||
var eventData = JsonSerializer.Deserialize(jsonData, s_jsonOptions.GetTypeInfo<EventData>(AgentClientJsonContext.Default));
|
||||
var eventData = JsonSerializer.Deserialize(jsonData, AgentClientJsonContext.Default.EventData);
|
||||
if (eventData?.Event != null)
|
||||
{
|
||||
var eventElement = eventData.Event.Value;
|
||||
@@ -121,7 +115,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
// Try to deserialize as AgentRunResponseUpdate for intermediate updates
|
||||
try
|
||||
{
|
||||
var update = JsonSerializer.Deserialize<AgentRunResponseUpdate>(eventElement.GetRawText(), s_jsonOptions);
|
||||
var update = JsonSerializer.Deserialize<AgentRunResponseUpdate>(eventElement.GetRawText(), AgentAbstractionsJsonUtilities.DefaultOptions);
|
||||
if (update != null)
|
||||
{
|
||||
responseUpdate = update;
|
||||
@@ -171,32 +165,6 @@ public class AgentResponse
|
||||
public string Status { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for JSON serialization with source generation support.
|
||||
/// </summary>
|
||||
internal static class JsonSerializerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the JsonTypeInfo for a type, preferring the one from options if available,
|
||||
/// otherwise falling back to the source-generated context.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to get JsonTypeInfo for.</typeparam>
|
||||
/// <param name="options">The JsonSerializerOptions to check first.</param>
|
||||
/// <param name="fallbackContext">The fallback JsonSerializerContext to use if not found in options.</param>
|
||||
/// <returns>The JsonTypeInfo for the requested type.</returns>
|
||||
public static JsonTypeInfo<T> GetTypeInfo<T>(this JsonSerializerOptions options, JsonSerializerContext fallbackContext)
|
||||
{
|
||||
// Try to get from the options first (if a context is configured)
|
||||
if (options.TypeInfoResolver?.GetTypeInfo(typeof(T), options) is JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
// Fall back to the provided source-generated context
|
||||
return (JsonTypeInfo<T>)fallbackContext.GetTypeInfo(typeof(T))!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source-generated JSON type information for use by AgentClient.
|
||||
/// </summary>
|
||||
@@ -206,10 +174,6 @@ internal static class JsonSerializerExtensions
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = false)]
|
||||
[JsonSerializable(typeof(ChatClientAgentRunRequest))]
|
||||
[JsonSerializable(typeof(ChatMessage))]
|
||||
[JsonSerializable(typeof(List<ChatMessage>))]
|
||||
[JsonSerializable(typeof(EventData))]
|
||||
[JsonSerializable(typeof(AgentRunResponseUpdate))]
|
||||
[JsonSerializable(typeof(AgentResponse))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
internal sealed partial class AgentClientJsonContext : JsonSerializerContext;
|
||||
|
||||
Reference in New Issue
Block a user