mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Update Workflow Input/Output Redesign (#881)
* feat: Make Executor id field mandatory When checkpointing is involved, it is critical to keep executor ids consistent between runs, even when recreating a new object tree for the workflow. The default id-setting mechanism generated a guid for part of the id, making it not work when restoring from a checkpoint. This change prevents this situation from arising. * feat: Enable running untyped Workflows With the change to enable delay-instantiation of executors and support for async Executor factory methods, we must instantiate the starting executor to know what are the valid input types for the workflow. To avoid forcing instantiation every time, and to better support workflows with multiple input types, we enable support for build and interacting with the base Workflow type without type annotations, and remove the requirement to know a valid input type when initiating a run. * feat: Support Output from any executor and multiple outputs.
This commit is contained in:
@@ -40,14 +40,15 @@ public static class Program
|
||||
var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient();
|
||||
|
||||
// Create the executors
|
||||
var sloganWriter = new SloganWriterExecutor(chatClient);
|
||||
var feedbackProvider = new FeedbackExecutor(chatClient);
|
||||
var sloganWriter = new SloganWriterExecutor("SloganWriter", chatClient);
|
||||
var feedbackProvider = new FeedbackExecutor("FeedbackProvider", chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
var workflow = new WorkflowBuilder(sloganWriter)
|
||||
.AddEdge(sloganWriter, feedbackProvider)
|
||||
.AddEdge(feedbackProvider, sloganWriter)
|
||||
.Build<string>();
|
||||
.WithOutputFrom(feedbackProvider)
|
||||
.Build();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Create a slogan for a new electric SUV that is affordable and fun to drive.");
|
||||
@@ -59,9 +60,9 @@ public static class Program
|
||||
Console.WriteLine($"{evt}");
|
||||
}
|
||||
|
||||
if (evt is WorkflowCompletedEvent completedEvent)
|
||||
if (evt is WorkflowOutputEvent outputEvent)
|
||||
{
|
||||
Console.WriteLine($"{completedEvent}");
|
||||
Console.WriteLine($"{outputEvent}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,8 +120,9 @@ internal sealed class SloganWriterExecutor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SloganWriterExecutor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for the executor.</param>
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public SloganWriterExecutor(IChatClient chatClient)
|
||||
public SloganWriterExecutor(string id, IChatClient chatClient) : base(id)
|
||||
{
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
|
||||
{
|
||||
@@ -189,8 +191,9 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FeedbackExecutor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for the executor.</param>
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public FeedbackExecutor(IChatClient chatClient)
|
||||
public FeedbackExecutor(string id, IChatClient chatClient) : base(id)
|
||||
{
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
|
||||
{
|
||||
@@ -219,13 +222,13 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
|
||||
if (feedback.Rating >= this.MinimumRating)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"The following slogan was accepted:\n\n{message.Slogan}"));
|
||||
await context.YieldOutputAsync($"The following slogan was accepted:\n\n{message.Slogan}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._attempts >= this.MaxAttempts)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"The slogan was rejected after {this.MaxAttempts} attempts. Final slogan:\n\n{message.Slogan}"));
|
||||
await context.YieldOutputAsync($"The slogan was rejected after {this.MaxAttempts} attempts. Final slogan:\n\n{message.Slogan}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public static class Program
|
||||
var workflow = new WorkflowBuilder(frenchAgent)
|
||||
.AddEdge(frenchAgent, spanishAgent)
|
||||
.AddEdge(spanishAgent, englishAgent)
|
||||
.Build<ChatMessage>();
|
||||
.Build();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!"));
|
||||
|
||||
@@ -38,7 +38,7 @@ public static class Program
|
||||
var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient();
|
||||
|
||||
// Create the workflow and turn it into an agent
|
||||
var workflow = WorkflowHelper.GetWorkflow(chatClient);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync(chatClient).ConfigureAwait(false);
|
||||
var agent = workflow.AsAgent("workflow-agent", "Workflow Agent");
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ internal static class WorkflowHelper
|
||||
/// </summary>
|
||||
/// <param name="chatClient">The chat client to use for the agents</param>
|
||||
/// <returns>A workflow that processes input using two language agents</returns>
|
||||
internal static Workflow<List<ChatMessage>> GetWorkflow(IChatClient chatClient)
|
||||
internal static ValueTask<Workflow<List<ChatMessage>>> GetWorkflowAsync(IChatClient chatClient)
|
||||
{
|
||||
// Create executors
|
||||
var startExecutor = new ConcurrentStartExecutor();
|
||||
@@ -29,7 +29,8 @@ internal static class WorkflowHelper
|
||||
return new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent])
|
||||
.Build<List<ChatMessage>>();
|
||||
.WithOutputFrom(aggregationExecutor)
|
||||
.BuildAsync<List<ChatMessage>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,7 +85,7 @@ internal static class WorkflowHelper
|
||||
if (this._messages.Count == 2)
|
||||
{
|
||||
var formattedMessages = string.Join(Environment.NewLine, this._messages.Select(m => $"{m.Text}"));
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent(formattedMessages));
|
||||
await context.YieldOutputAsync(formattedMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user