chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:25 +08:00
commit db620d33df
5151 changed files with 925932 additions and 0 deletions
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
namespace WorkflowGroupChatToolApprovalSample;
/// <summary>
/// Custom GroupChatManager that selects the next speaker based on the conversation flow.
/// </summary>
/// <remarks>
/// This simple selector follows a predefined flow:
/// 1. QA Engineer runs tests
/// 2. DevOps Engineer checks staging and creates rollback plan
/// 3. DevOps Engineer deploys to production (triggers approval)
/// </remarks>
internal sealed class DeploymentGroupChatManager : GroupChatManager
{
private readonly IReadOnlyList<AIAgent> _agents;
public DeploymentGroupChatManager(IReadOnlyList<AIAgent> agents)
{
this._agents = agents;
}
protected override ValueTask<AIAgent> SelectNextAgentAsync(
IReadOnlyList<ChatMessage> history,
CancellationToken cancellationToken = default)
{
if (history.Count == 0)
{
throw new InvalidOperationException("Conversation is empty; cannot select next speaker.");
}
// First speaker after initial user message
if (this.IterationCount == 0)
{
AIAgent qaAgent = this._agents.First(a => a.Name == "QAEngineer");
return new ValueTask<AIAgent>(qaAgent);
}
// Subsequent speakers are DevOps Engineer
AIAgent devopsAgent = this._agents.First(a => a.Name == "DevOpsEngineer");
return new ValueTask<AIAgent>(devopsAgent);
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Foundry\Microsoft.Agents.AI.Foundry.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to use GroupChatBuilder with tools that require human
// approval before execution. A group of specialized agents collaborate on a task, and
// sensitive tool calls trigger human-in-the-loop approval.
//
// This sample works as follows:
// 1. A GroupChatBuilder workflow is created with multiple specialized agents.
// 2. A custom manager determines which agent speaks next based on conversation state.
// 3. Agents collaborate on a software deployment task.
// 4. When the deployment agent tries to deploy to production, it triggers an approval request.
// 5. The sample simulates human approval and the workflow completes.
//
// Purpose:
// Show how tool call approvals integrate with multi-agent group chat workflows where
// different agents have different levels of tool access.
//
// Demonstrate:
// - Using custom GroupChatManager with agents that have approval-required tools.
// - Handling ToolApprovalRequestContent in group chat scenarios.
// - Multi-round group chat with tool approval interruption and resumption.
using System.ComponentModel;
using System.Text.Json;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
namespace WorkflowGroupChatToolApprovalSample;
/// <summary>
/// This sample demonstrates how to use GroupChatBuilder with tools that require human
/// approval before execution.
/// </summary>
/// <remarks>
/// Pre-requisites:
/// - An Azure OpenAI chat completion deployment must be configured.
/// </remarks>
public static class Program
{
private static async Task Main()
{
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
// 1. Create AI client
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
// 2. Create specialized agents with their tools
ChatClientAgent qaEngineer = aiProjectClient.AsAIAgent(
model: deploymentName,
instructions: "You are a QA engineer responsible for running tests before deployment. Run the appropriate test suites and report the results clearly in your response, including pass/fail counts.",
name: "QAEngineer",
description: "QA engineer who runs tests",
tools: [AIFunctionFactory.Create(RunTests)]);
ChatClientAgent devopsEngineer = aiProjectClient.AsAIAgent(
model: deploymentName,
instructions: "You are a DevOps engineer responsible for deployments. Call CheckStagingStatus, then CreateRollbackPlan, then DeployToProduction — in that order. Do not ask for confirmation before deploying; deployment approval is handled automatically by the system. After all tools complete, summarize each step and its result in your text response.",
name: "DevOpsEngineer",
description: "DevOps engineer who handles deployments",
tools:
[
AIFunctionFactory.Create(CheckStagingStatus),
AIFunctionFactory.Create(CreateRollbackPlan),
new ApprovalRequiredAIFunction(AIFunctionFactory.Create(DeployToProduction))
]);
// 3. Create custom GroupChatManager with speaker selection logic
DeploymentGroupChatManager manager = new([qaEngineer, devopsEngineer])
{
MaximumIterationCount = 4
};
// 4. Build a group chat workflow with the custom manager
Workflow workflow = AgentWorkflowBuilder
.CreateGroupChatBuilderWith(_ => manager)
.AddParticipants(qaEngineer, devopsEngineer)
.Build();
// 5. Start the workflow
Console.WriteLine("Starting group chat workflow for software deployment...");
Console.WriteLine($"Agents: [{qaEngineer.Name}, {devopsEngineer.Name}]");
Console.WriteLine(new string('-', 60));
List<ChatMessage> messages = [new(ChatRole.User, "We need to deploy version 2.4.0 to production. Please coordinate the deployment.")];
await using StreamingRun run = await InProcessExecution.Lockstep.RunStreamingAsync(workflow, messages);
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
string? lastExecutorId = null;
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
switch (evt)
{
case RequestInfoEvent e:
{
if (e.Request.TryGetDataAs(out ToolApprovalRequestContent? approvalRequestContent))
{
Console.WriteLine();
Console.WriteLine($"[APPROVAL REQUIRED] From agent: {e.Request.PortInfo.PortId}");
Console.WriteLine($" Tool: {((FunctionCallContent)approvalRequestContent.ToolCall).Name}");
Console.WriteLine($" Arguments: {JsonSerializer.Serialize(((FunctionCallContent)approvalRequestContent.ToolCall).Arguments)}");
Console.WriteLine();
// Approve the tool call request
Console.WriteLine($"Tool: {((FunctionCallContent)approvalRequestContent.ToolCall).Name} approved");
await run.SendResponseAsync(e.Request.CreateResponse(approvalRequestContent.CreateResponse(approved: true)));
}
break;
}
case AgentResponseUpdateEvent e:
{
if (e.ExecutorId != lastExecutorId)
{
if (lastExecutorId is not null)
{
Console.WriteLine();
}
Console.WriteLine($"- {e.ExecutorId}: ");
lastExecutorId = e.ExecutorId;
}
Console.Write(e.Update.Text);
break;
}
case WorkflowErrorEvent workflowError:
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.WriteLine(workflowError.Exception?.ToString() ?? "Unknown workflow error occurred.");
Console.ResetColor();
break;
case ExecutorFailedEvent executorFailed:
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.WriteLine($"Executor '{executorFailed.ExecutorId}' failed with {(executorFailed.Data == null ? "unknown error" : $"exception {executorFailed.Data}")}.");
Console.ResetColor();
break;
}
}
Console.WriteLine();
Console.WriteLine(new string('-', 60));
Console.WriteLine("Deployment workflow completed successfully!");
Console.WriteLine("All agents have finished their tasks.");
}
// Tool definitions - These are called by the agents during workflow execution
[Description("Run automated tests for the application.")]
private static string RunTests([Description("Name of the test suite to run")] string testSuite)
=> $"Test suite '{testSuite}' completed: 47 passed, 0 failed, 0 skipped";
[Description("Check the current status of the staging environment.")]
private static string CheckStagingStatus()
=> "Staging environment: Healthy, Version 2.3.0 deployed, All services running";
[Description("Deploy specified components to production. Requires human approval.")]
private static string DeployToProduction(
[Description("The version to deploy")] string version,
[Description("Comma-separated list of components to deploy")] string components)
=> $"Production deployment complete: Version {version}, Components: {components}";
[Description("Create a rollback plan for the deployment.")]
private static string CreateRollbackPlan([Description("The version being deployed")] string version)
=> $"Rollback plan created for version {version}: Automated rollback to v2.2.0 if health checks fail within 5 minutes";
}
@@ -0,0 +1,70 @@
# Group Chat with Tool Approval Sample
This sample demonstrates how to use `GroupChatBuilder` with tools that require human approval before execution. A group of specialized agents collaborate on a task, and sensitive tool calls trigger human-in-the-loop approval.
## What This Sample Demonstrates
- Using a custom `GroupChatManager` with agents that have approval-required tools
- Handling `FunctionApprovalRequestContent` in group chat scenarios
- Multi-round group chat with tool approval interruption and resumption
- Integrating tool call approvals with multi-agent workflows where different agents have different levels of tool access
## How It Works
1. A `GroupChatBuilder` workflow is created with multiple specialized agents
2. A custom `DeploymentGroupChatManager` determines which agent speaks next based on conversation state
3. Agents collaborate on a software deployment task:
- **QA Engineer**: Runs automated tests
- **DevOps Engineer**: Checks staging status, creates rollback plan, and deploys to production
4. When the deployment agent tries to deploy to production, it triggers an approval request
5. The sample simulates human approval and the workflow completes
## Key Components
### Approval-Required Tools
The `DeployToProduction` function is wrapped with `ApprovalRequiredAIFunction` to require human approval:
```csharp
new ApprovalRequiredAIFunction(AIFunctionFactory.Create(DeployToProduction))
```
### Custom Group Chat Manager
The `DeploymentGroupChatManager` implements custom speaker selection logic:
- First iteration: QA Engineer runs tests
- Subsequent iterations: DevOps Engineer handles deployment tasks
### Approval Handling
The sample demonstrates continuous event-driven execution with inline approval handling:
- The workflow runs in a single event loop.
- When an approval-required tool is invoked, the loop surfaces an approval request, processes the (simulated) human response, and then continues execution without starting a separate phase.
## Prerequisites
- Azure OpenAI or OpenAI configured with the required environment variables
- `AZURE_OPENAI_ENDPOINT` environment variable set
- `AZURE_OPENAI_DEPLOYMENT_NAME` environment variable (defaults to "gpt-5.4-mini")
## Running the Sample
```bash
dotnet run
```
## Expected Output
The sample will show:
1. QA Engineer running tests
2. DevOps Engineer checking staging and creating rollback plan
3. An approval request for production deployment
4. Simulated approval response
5. DevOps Engineer completing the deployment
6. Workflow completion message
## Related Samples
- [Agent Function Tools with Approvals](../../../02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals) - Basic function approval pattern
- [Agent Workflow Patterns](../../_StartHere/03_AgentWorkflowPatterns) - Group chat without approvals
- [Human-in-the-Loop Basic](../../HumanInTheLoop/HumanInTheLoopBasic) - Workflow-level human interaction