mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Workflow getting started samples in .Net (Second batch) (#650)
* Add loop and agents in workflows samples * Add foundry agent and workflow as agent samples * Checkpoint sample WIP * Checkpoint sample 1 Done * Add HIL samples * Fix formatting * Force folder name change step 1 * Force folder name change step 2 * Fix formatting * Fix formatting * Add checkpoint and rehydrate sample * _Foundational * Fix formatting
This commit is contained in:
committed by
GitHub
Unverified
parent
2393351a03
commit
89c8418705
+16
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.Workflows\Microsoft.Agents.Workflows.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
|
||||
namespace WorkflowHumanInTheLoopBasicSample;
|
||||
|
||||
/// <summary>
|
||||
/// This sample introduces the concept of InputPort and ExternalRequest to enable
|
||||
/// human-in-the-loop interaction scenarios.
|
||||
/// An input port can be used as if it were an executor in the workflow graph. Upon receiving
|
||||
/// a message, the input port generates an RequestInfoEvent that gets emitted to the external world.
|
||||
/// The external world can then respond to the request by sending an ExternalResponse back to
|
||||
/// the workflow.
|
||||
/// The sample implements a simple number guessing game where the external user tries to guess
|
||||
/// a pre-defined target number. The workflow consists of a single JudgeExecutor that judges
|
||||
/// the user's guesses and provides feedback.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Pre-requisites:
|
||||
/// - Foundational samples should be completed first.
|
||||
/// </remarks>
|
||||
public static class Program
|
||||
{
|
||||
private static async Task Main()
|
||||
{
|
||||
// Create the workflow
|
||||
var workflow = WorkflowHelper.GetWorkflow();
|
||||
|
||||
// Execute the workflow
|
||||
StreamingRun handle = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in handle.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case RequestInfoEvent requestInputEvt:
|
||||
// Handle `RequestInfoEvent` from the workflow
|
||||
ExternalResponse response = HandleExternalRequest(requestInputEvt.Request);
|
||||
await handle.SendResponseAsync(response).ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case WorkflowCompletedEvent workflowCompleteEvt:
|
||||
// The workflow has completed successfully
|
||||
Console.WriteLine($"Workflow completed with result: {workflowCompleteEvt.Data}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ExternalResponse HandleExternalRequest(ExternalRequest request)
|
||||
{
|
||||
if (request.Port.Request == typeof(NumberSignal))
|
||||
{
|
||||
var signal = (NumberSignal)request.Data;
|
||||
switch (signal)
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: ");
|
||||
return request.CreateResponse<int>(initialGuess);
|
||||
case NumberSignal.Above:
|
||||
int lowerGuess = ReadIntegerFromConsole("You previously guessed too large. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(lowerGuess);
|
||||
case NumberSignal.Below:
|
||||
int higherGuess = ReadIntegerFromConsole("You previously guessed too small. Please provide a new guess: ");
|
||||
return request.CreateResponse<int>(higherGuess);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Request {request.Port.Request} is not supported");
|
||||
}
|
||||
|
||||
private static int ReadIntegerFromConsole(string prompt)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Console.Write(prompt);
|
||||
string? input = Console.ReadLine();
|
||||
if (int.TryParse(input, out int value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
Console.WriteLine("Invalid input. Please enter a valid integer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowHumanInTheLoopBasicSample;
|
||||
|
||||
internal static class WorkflowHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a workflow that plays a number guessing game with human-in-the-loop interaction.
|
||||
/// An input port allows the external world to provide inputs to the workflow upon requests.
|
||||
/// </summary>
|
||||
internal static Workflow<NumberSignal> GetWorkflow()
|
||||
{
|
||||
// Create the executors
|
||||
InputPort numberInputPort = InputPort.Create<NumberSignal, int>("GuessNumber");
|
||||
JudgeExecutor judgeExecutor = new(42);
|
||||
|
||||
// Build the workflow by connecting executors in a loop
|
||||
var workflow = new WorkflowBuilder(numberInputPort)
|
||||
.AddEdge(numberInputPort, judgeExecutor)
|
||||
.AddEdge(judgeExecutor, numberInputPort)
|
||||
.Build<NumberSignal>();
|
||||
|
||||
return workflow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals used for communication between guesses and the JudgeExecutor.
|
||||
/// </summary>
|
||||
internal enum NumberSignal
|
||||
{
|
||||
Init,
|
||||
Above,
|
||||
Below,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JudgeExecutor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="targetNumber">The number to be guessed.</param>
|
||||
public JudgeExecutor(int targetNumber) : this()
|
||||
{
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!"))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Below).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Above).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user