mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Enable more analyzers and various code tweaks/cleanup (#738)
This commit is contained in:
committed by
GitHub
Unverified
parent
f3264966ff
commit
5e5761b288
@@ -44,17 +44,16 @@ public static class Program
|
||||
var feedbackProvider = new FeedbackExecutor(chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(sloganWriter);
|
||||
builder.AddEdge(sloganWriter, feedbackProvider);
|
||||
builder.AddEdge(feedbackProvider, sloganWriter);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(sloganWriter)
|
||||
.AddEdge(sloganWriter, feedbackProvider)
|
||||
.AddEdge(feedbackProvider, sloganWriter)
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow
|
||||
var ask = "Create a slogan for a new electric SUV that is affordable and fun to drive.";
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, ask);
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Create a slogan for a new electric SUV that is affordable and fun to drive.");
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (evt is SloganGeneratedEvent || evt is FeedbackEvent)
|
||||
if (evt is SloganGeneratedEvent or FeedbackEvent)
|
||||
{
|
||||
// Custom events to allow us to monitor the progress of the workflow.
|
||||
Console.WriteLine($"{evt}");
|
||||
@@ -114,10 +113,6 @@ internal sealed class SloganWriterExecutor
|
||||
IMessageHandler<string, SloganResult>,
|
||||
IMessageHandler<FeedbackResult, SloganResult>
|
||||
{
|
||||
private const string Instruction = """
|
||||
You are a professional slogan writer. You will be given a task to create a slogan.
|
||||
""";
|
||||
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
|
||||
@@ -127,13 +122,11 @@ internal sealed class SloganWriterExecutor
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public SloganWriterExecutor(IChatClient chatClient)
|
||||
{
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: Instruction)
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(SloganResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(SloganResult)))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,10 +177,6 @@ internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEve
|
||||
/// </summary>
|
||||
internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, IMessageHandler<SloganResult>
|
||||
{
|
||||
private const string Instruction = """
|
||||
You are a professional editor. You will be given a slogan and the task it is meant to accomplish.
|
||||
""";
|
||||
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
|
||||
@@ -195,7 +184,7 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
|
||||
public int MaxAttempts { get; init; } = 3;
|
||||
|
||||
private int _attempts = 0;
|
||||
private int _attempts;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FeedbackExecutor"/> class.
|
||||
@@ -203,13 +192,11 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public FeedbackExecutor(IChatClient chatClient)
|
||||
{
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: Instruction)
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult)))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -229,11 +216,13 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
var feedback = JsonSerializer.Deserialize<FeedbackResult>(response.Text) ?? throw new InvalidOperationException("Failed to deserialize feedback.");
|
||||
|
||||
await context.AddEventAsync(new FeedbackEvent(feedback));
|
||||
|
||||
if (feedback.Rating >= this.MinimumRating)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"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}"));
|
||||
|
||||
@@ -34,10 +34,10 @@ public static class Program
|
||||
AIAgent englishAgent = await GetTranslationAgentAsync("English", persistentAgentsClient, model);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(frenchAgent);
|
||||
builder.AddEdge(frenchAgent, spanishAgent);
|
||||
builder.AddEdge(spanishAgent, englishAgent);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(frenchAgent)
|
||||
.AddEdge(frenchAgent, spanishAgent)
|
||||
.AddEdge(spanishAgent, englishAgent)
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!"));
|
||||
@@ -71,11 +71,10 @@ public static class Program
|
||||
PersistentAgentsClient persistentAgentsClient,
|
||||
string model)
|
||||
{
|
||||
string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.";
|
||||
var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync(
|
||||
model: model,
|
||||
name: $"{targetLanguage} Translator",
|
||||
instructions: instructions);
|
||||
instructions: $"You are a translation assistant that translates the provided text to {targetLanguage}.");
|
||||
|
||||
return await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public static class Program
|
||||
Dictionary<string, List<AgentRunResponseUpdate>> buffer = [];
|
||||
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread).ConfigureAwait(false))
|
||||
{
|
||||
if (update.MessageId == null)
|
||||
if (update.MessageId is null)
|
||||
{
|
||||
// skip updates that don't have a message ID
|
||||
continue;
|
||||
|
||||
@@ -26,11 +26,10 @@ internal static class WorkflowHelper
|
||||
AIAgent englishAgent = GetLanguageAgent("English", chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(startExecutor);
|
||||
builder.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent]);
|
||||
builder.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent]);
|
||||
|
||||
return builder.Build<List<ChatMessage>>();
|
||||
return new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent])
|
||||
.Build<List<ChatMessage>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,11 +38,8 @@ internal static class WorkflowHelper
|
||||
/// <param name="targetLanguage">The target language for translation</param>
|
||||
/// <param name="chatClient">The chat client to use for the agent</param>
|
||||
/// <returns>A ChatClientAgent configured for the specified language</returns>
|
||||
private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient)
|
||||
{
|
||||
string instructions = $"You're a helpful assistant who always responds in {targetLanguage}.";
|
||||
return new ChatClientAgent(chatClient, instructions, name: $"{targetLanguage}Agent");
|
||||
}
|
||||
private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) =>
|
||||
new(chatClient, instructions: $"You're a helpful assistant who always responds in {targetLanguage}.", name: $"{targetLanguage}Agent");
|
||||
|
||||
/// <summary>
|
||||
/// Executor that starts the concurrent processing by sending messages to the agents.
|
||||
|
||||
+4
-4
@@ -51,7 +51,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -72,9 +72,9 @@ public static class Program
|
||||
|
||||
// Rehydrate a new workflow instance from a saved checkpoint and continue execution
|
||||
var newWorkflow = WorkflowHelper.GetWorkflow();
|
||||
var checkpointIndex = 5;
|
||||
Console.WriteLine($"\n\nHydrating a new workflow instance from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 5;
|
||||
Console.WriteLine($"\n\nHydrating a new workflow instance from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
|
||||
Checkpointed<StreamingRun> newCheckpointedRun = await InProcessExecution
|
||||
.StreamAsync(newWorkflow, NumberSignal.Init, checkpointManager)
|
||||
|
||||
+8
-18
@@ -23,12 +23,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(guessNumberExecutor)
|
||||
return new WorkflowBuilder(guessNumberExecutor)
|
||||
.AddEdge(guessNumberExecutor, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, guessNumberExecutor)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -70,9 +70,9 @@ public static class Program
|
||||
Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}");
|
||||
|
||||
// Restoring from a checkpoint and resuming execution
|
||||
var checkpointIndex = 5;
|
||||
Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 5;
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
|
||||
+8
-18
@@ -23,12 +23,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(guessNumberExecutor)
|
||||
return new WorkflowBuilder(guessNumberExecutor)
|
||||
.AddEdge(guessNumberExecutor, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, guessNumberExecutor)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +92,15 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound));
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +109,7 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -149,17 +143,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -57,7 +57,7 @@ public static class Program
|
||||
// Checkpoints are automatically created at the end of each super step when a
|
||||
// checkpoint manager is provided. You can store the checkpoint info for later use.
|
||||
CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint;
|
||||
if (checkpoint != null)
|
||||
if (checkpoint is not null)
|
||||
{
|
||||
checkpoints.Add(checkpoint);
|
||||
Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}.");
|
||||
@@ -76,9 +76,9 @@ public static class Program
|
||||
Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}");
|
||||
|
||||
// Restoring from a checkpoint and resuming execution
|
||||
var checkpointIndex = 1;
|
||||
Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex];
|
||||
const int CheckpointIndex = 1;
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
@@ -109,13 +109,13 @@ public static class Program
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: ");
|
||||
return request.CreateResponse<int>(initialGuess);
|
||||
return request.CreateResponse(initialGuess);
|
||||
case NumberSignal.Above:
|
||||
int lowerGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too large. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(lowerGuess);
|
||||
return request.CreateResponse(lowerGuess);
|
||||
case NumberSignal.Below:
|
||||
int higherGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too small. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(higherGuess);
|
||||
return request.CreateResponse(higherGuess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -20,12 +20,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(numberInputPort)
|
||||
return new WorkflowBuilder(numberInputPort)
|
||||
.AddEdge(numberInputPort, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, numberInputPort)
|
||||
.Build<SignalWithNumber>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ internal sealed class SignalWithNumber
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
private const string StateKey = "JudgeExecutorState";
|
||||
|
||||
/// <summary>
|
||||
@@ -94,17 +92,13 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// Checkpoint the current state of the executor.
|
||||
/// This must be overridden to save any state that is needed to resume the executor.
|
||||
/// </summary>
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
return context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
}
|
||||
protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
context.QueueStateUpdateAsync(StateKey, this._tries);
|
||||
|
||||
/// <summary>
|
||||
/// Restore the state of the executor from a checkpoint.
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default)
|
||||
{
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ public static class Program
|
||||
var aggregationExecutor = new ConcurrentAggregationExecutor();
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(startExecutor);
|
||||
builder.AddFanOutEdge(startExecutor, targets: [physicist, chemist]);
|
||||
builder.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist]);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [physicist, chemist])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist])
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow in streaming mode
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, "What is temperature?");
|
||||
|
||||
+17
-38
@@ -53,11 +53,11 @@ public static class Program
|
||||
var handleSpamExecutor = new HandleSpamExecutor();
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(spamDetectionExecutor);
|
||||
builder.AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false));
|
||||
builder.AddEdge(emailAssistantExecutor, sendEmailExecutor);
|
||||
builder.AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true));
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(spamDetectionExecutor)
|
||||
.AddEdge(spamDetectionExecutor, emailAssistantExecutor, condition: GetCondition(expectedResult: false))
|
||||
.AddEdge(emailAssistantExecutor, sendEmailExecutor)
|
||||
.AddEdge(spamDetectionExecutor, handleSpamExecutor, condition: GetCondition(expectedResult: true))
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Read a email from a text file
|
||||
string email = Resources.Read("spam.txt");
|
||||
@@ -79,53 +79,34 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <param name="expectedResult">The expected spam detection result</param>
|
||||
/// <returns>A function that evaluates whether a message meets the expected result</returns>
|
||||
private static Func<object?, bool> GetCondition(bool expectedResult)
|
||||
{
|
||||
return detectionResult =>
|
||||
{
|
||||
return detectionResult is DetectionResult result && result.IsSpam == expectedResult;
|
||||
};
|
||||
}
|
||||
private static Func<object?, bool> GetCondition(bool expectedResult) =>
|
||||
detectionResult => detectionResult is DetectionResult result && result.IsSpam == expectedResult;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spam detection agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,7 +169,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._spamDetectionAgent.RunAsync(message);
|
||||
@@ -252,10 +233,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -94,53 +94,33 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <param name="expectedDecision">The expected spam detection decision</param>
|
||||
/// <returns>A function that evaluates whether a message meets the expected result</returns>
|
||||
private static Func<object?, bool> GetCondition(SpamDecision expectedDecision)
|
||||
{
|
||||
return detectionResult =>
|
||||
{
|
||||
return detectionResult is DetectionResult result && result.spamDecision == expectedDecision;
|
||||
};
|
||||
}
|
||||
private static Func<object?, bool> GetCondition(SpamDecision expectedDecision) => detectionResult => detectionResult is DetectionResult result && result.spamDecision == expectedDecision;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a spam detection agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(DetectionResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,7 +193,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._spamDetectionAgent.RunAsync(message);
|
||||
@@ -276,10 +256,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+16
-39
@@ -78,7 +78,7 @@ public static class Program
|
||||
.AddEdge<AnalysisResult>(
|
||||
emailAnalysisExecutor,
|
||||
databaseAccessExecutor,
|
||||
condition: analysisResult => analysisResult is not null && analysisResult.EmailLength <= LongEmailThreshold)
|
||||
condition: analysisResult => analysisResult?.EmailLength <= LongEmailThreshold)
|
||||
// Save the analysis result to the database with summary
|
||||
.AddEdge(emailSummaryExecutor, databaseAccessExecutor);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
@@ -141,61 +141,40 @@ public static class Program
|
||||
/// Create an email analysis agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email analysis</returns>
|
||||
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are a spam detection assistant that identifies spam emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(AnalysisResult)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email assistant agent.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an email assistant that helps users draft responses to emails with professionalism.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailResponse)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates an agent that summarizes emails.
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email summarization</returns>
|
||||
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient)
|
||||
{
|
||||
string instructions = "You are an assistant that helps users summarize emails.";
|
||||
var agentOptions = new ChatClientAgentOptions(instructions: instructions)
|
||||
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.")
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary))
|
||||
)
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema(AIJsonUtilities.CreateJsonSchema(typeof(EmailSummary)))
|
||||
}
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal static class EmailStateConstants
|
||||
@@ -271,7 +250,7 @@ internal sealed class EmailAnalysisExecutor : ReflectingExecutor<EmailAnalysisEx
|
||||
EmailId = Guid.NewGuid().ToString(),
|
||||
EmailContent = message.Text
|
||||
};
|
||||
await context.QueueStateUpdateAsync<Email>(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.QueueStateUpdateAsync(newEmail.EmailId, newEmail, scopeName: EmailStateConstants.EmailStateScope);
|
||||
|
||||
// Invoke the agent
|
||||
var response = await this._emailAnalysisAgent.RunAsync(message);
|
||||
@@ -335,10 +314,8 @@ internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context)
|
||||
{
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context) =>
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"Email sent: {message.Response}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -437,7 +414,7 @@ internal sealed class DatabaseAccessExecutor() : ReflectingExecutor<DatabaseAcce
|
||||
public async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context)
|
||||
{
|
||||
// 1. Save the email content
|
||||
var email = await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope);
|
||||
await Task.Delay(100); // Simulate database access delay
|
||||
|
||||
// 2. Save the analysis result
|
||||
|
||||
@@ -99,8 +99,8 @@ internal sealed class Program
|
||||
{
|
||||
Configuration = this.Configuration
|
||||
};
|
||||
Workflow<string> workflow = DeclarativeWorkflowBuilder.Build<string>(this.WorkflowFile, options);
|
||||
return workflow;
|
||||
|
||||
return DeclarativeWorkflowBuilder.Build<string>(this.WorkflowFile, options);
|
||||
}
|
||||
|
||||
private const string DefaultWorkflow = "HelloWorld.yaml";
|
||||
@@ -164,7 +164,7 @@ internal sealed class Program
|
||||
Debug.WriteLine($"REQUEST #{requestInfo.Request.RequestId}");
|
||||
if (response is not null)
|
||||
{
|
||||
ExternalResponse requestResponse = requestInfo.Request.CreateResponse<InputResponse>(response);
|
||||
ExternalResponse requestResponse = requestInfo.Request.CreateResponse(response);
|
||||
await run.Run.SendResponseAsync(requestResponse).ConfigureAwait(false);
|
||||
response = null;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ internal sealed class Program
|
||||
private static InputResponse HandleExternalRequest(ExternalRequest request)
|
||||
{
|
||||
InputRequest? message = request.Data.As<InputRequest>();
|
||||
string? userInput = null;
|
||||
string? userInput;
|
||||
do
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
|
||||
+4
-5
@@ -52,18 +52,17 @@ public static class Program
|
||||
{
|
||||
if (request.DataIs<NumberSignal>())
|
||||
{
|
||||
var signal = request.DataAs<NumberSignal>();
|
||||
switch (signal)
|
||||
switch (request.DataAs<NumberSignal>())
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: ");
|
||||
return request.CreateResponse<int>(initialGuess);
|
||||
return request.CreateResponse(initialGuess);
|
||||
case NumberSignal.Above:
|
||||
int lowerGuess = ReadIntegerFromConsole("You previously guessed too large. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(lowerGuess);
|
||||
return request.CreateResponse(lowerGuess);
|
||||
case NumberSignal.Below:
|
||||
int higherGuess = ReadIntegerFromConsole("You previously guessed too small. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(higherGuess);
|
||||
return request.CreateResponse(higherGuess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -19,12 +19,10 @@ internal static class WorkflowHelper
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(numberInputPort)
|
||||
return new WorkflowBuilder(numberInputPort)
|
||||
.AddEdge(numberInputPort, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, numberInputPort)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +42,7 @@ internal enum NumberSignal
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JudgeExecutor"/> class.
|
||||
|
||||
@@ -108,7 +108,7 @@ internal sealed class GuessNumberExecutor : ReflectingExecutor<GuessNumberExecut
|
||||
internal sealed class JudgeExecutor : ReflectingExecutor<JudgeExecutor>, IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
private int _tries;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JudgeExecutor"/> class.
|
||||
|
||||
@@ -30,10 +30,10 @@ public static class Program
|
||||
var aggregate = new AggregationExecutor();
|
||||
|
||||
// Build the workflow by connecting executors sequentially
|
||||
WorkflowBuilder builder = new(fileRead);
|
||||
builder.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount]);
|
||||
builder.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount]);
|
||||
var workflow = builder.Build<string>();
|
||||
var workflow = new WorkflowBuilder(fileRead)
|
||||
.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount])
|
||||
.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount])
|
||||
.Build<string>();
|
||||
|
||||
// Execute the workflow with input data
|
||||
Run run = await InProcessExecution.RunAsync(workflow, "Lorem_Ipsum.txt");
|
||||
@@ -63,7 +63,7 @@ internal sealed class FileReadExecutor() : ReflectingExecutor<FileReadExecutor>(
|
||||
string fileContent = Resources.Read(message);
|
||||
// Store file content in a shared state for access by other executors
|
||||
string fileID = Guid.NewGuid().ToString();
|
||||
await context.QueueStateUpdateAsync<string>(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope);
|
||||
await context.QueueStateUpdateAsync(fileID, fileContent, scopeName: FileContentStateConstants.FileContentStateScope);
|
||||
|
||||
return fileID;
|
||||
}
|
||||
|
||||
+4
-10
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
@@ -54,13 +55,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="message">The input text to convert</param>
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
string result = message.ToUpperInvariant();
|
||||
|
||||
// The return value will be sent as a message along an edge to subsequent executors
|
||||
return result;
|
||||
}
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -76,9 +72,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
char[] charArray = message.ToCharArray();
|
||||
System.Array.Reverse(charArray);
|
||||
string result = new(charArray);
|
||||
string result = string.Concat(message.Reverse());
|
||||
|
||||
// Signal that the workflow is complete
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
@@ -53,13 +54,8 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="message">The input text to convert</param>
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
string result = message.ToUpperInvariant();
|
||||
|
||||
// The return value will be sent as a message along an edge to subsequent executors
|
||||
return result;
|
||||
}
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -75,9 +71,7 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context)
|
||||
{
|
||||
char[] charArray = message.ToCharArray();
|
||||
System.Array.Reverse(charArray);
|
||||
string result = new(charArray);
|
||||
string result = string.Concat(message.Reverse());
|
||||
|
||||
// Signal that the workflow is complete
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent(result)).ConfigureAwait(false);
|
||||
|
||||
+7
-9
@@ -40,13 +40,14 @@ public static class Program
|
||||
AIAgent englishAgent = GetTranslationAgent("English", chatClient);
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
WorkflowBuilder builder = new(frenchAgent);
|
||||
builder.AddEdge(frenchAgent, spanishAgent);
|
||||
builder.AddEdge(spanishAgent, englishAgent);
|
||||
var workflow = builder.Build<ChatMessage>();
|
||||
var workflow = new WorkflowBuilder(frenchAgent)
|
||||
.AddEdge(frenchAgent, spanishAgent)
|
||||
.AddEdge(spanishAgent, englishAgent)
|
||||
.Build<ChatMessage>();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!"));
|
||||
|
||||
// Must send the turn token to trigger the agents.
|
||||
// The agents are wrapped as executors. When they receive messages,
|
||||
// they will cache the messages and only start processing when they receive a TurnToken.
|
||||
@@ -66,9 +67,6 @@ public static class Program
|
||||
/// <param name="targetLanguage">The target language for translation</param>
|
||||
/// <param name="chatClient">The chat client to use for the agent</param>
|
||||
/// <returns>A ChatClientAgent configured for the specified language</returns>
|
||||
private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient)
|
||||
{
|
||||
string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.";
|
||||
return new ChatClientAgent(chatClient, instructions);
|
||||
}
|
||||
private static ChatClientAgent GetTranslationAgent(string targetLanguage, IChatClient chatClient) =>
|
||||
new(chatClient, $"You are a translation assistant that translates the provided text to {targetLanguage}.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user