Поделиться через


Оркестрации рабочих процессов Microsoft Agent Framework — групповый чат

Оркестрация группового чата моделирует совместную беседу между несколькими агентами, координируемую менеджером, который определяет выбор говорящего и поток беседы. Этот шаблон идеально подходит для сценариев, требующих итеративного уточнения, совместного решения проблем или анализа с несколькими перспективами.

Различия между групповым чатом и другими шаблонами

Оркестрация группового чата имеет уникальные характеристики по сравнению с другими шаблонами с несколькими агентами:

  • Централизованная координация: в отличие от шаблонов выдачи, в которых агенты напрямую передают управление, групповый чат использует диспетчера для координации того, кто говорит дальше
  • Итеративное уточнение: агенты могут просматривать и развивать ответы друг друга в нескольких раундах
  • Гибкий выбор докладчика: менеджер может использовать различные стратегии (циклический перебор, основанный на запросах, пользовательская логика) для выбора докладчиков.
  • Общий контекст: все агенты видят полную историю бесед, что позволяет улучшить совместную работу.

Цели обучения

  • Создание специализированных агентов для групповой совместной работы
  • Настройка стратегий выбора говорящего
  • Создание рабочих процессов с помощью пошаговой оптимизации агентов
  • Настройка потока беседы с помощью пользовательских управляющих

Настройка клиента Azure OpenAI

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.Workflows;
using Microsoft.Extensions.AI;
using Microsoft.Agents.AI;

// Set up the Azure OpenAI client
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ??
    throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
var client = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
    .GetChatClient(deploymentName)
    .AsIChatClient();

Определение агентов

Создайте специализированные агенты для разных ролей в беседе группы:

// Create a copywriter agent
ChatClientAgent writer = new(client,
    "You are a creative copywriter. Generate catchy slogans and marketing copy. Be concise and impactful.",
    "CopyWriter",
    "A creative copywriter agent");

// Create a reviewer agent
ChatClientAgent reviewer = new(client,
    "You are a marketing reviewer. Evaluate slogans for clarity, impact, and brand alignment. " +
    "Provide constructive feedback or approval.",
    "Reviewer",
    "A marketing review agent");

Настройка группового чата с помощью диспетчера Round-Robin

Создание рабочего процесса группового чата с помощью AgentWorkflowBuilder:

// Build group chat with round-robin speaker selection
// The manager factory receives the list of agents and returns a configured manager
var workflow = AgentWorkflowBuilder
    .CreateGroupChatBuilderWith(agents => 
        new RoundRobinGroupChatManager(agents) 
        { 
            MaximumIterationCount = 5  // Maximum number of turns
        })
    .AddParticipants(writer, reviewer)
    .Build();

Запуск рабочего процесса группового чата

Выполните рабочий процесс и просмотрите итеративную беседу:

// Start the group chat
var messages = new List<ChatMessage> { 
    new(ChatRole.User, "Create a slogan for an eco-friendly electric vehicle.") 
};

StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages);
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
{
    if (evt is AgentRunUpdateEvent update)
    {
        // Process streaming agent responses
        AgentRunResponse response = update.AsResponse();
        foreach (ChatMessage message in response.Messages)
        {
            Console.WriteLine($"[{update.ExecutorId}]: {message.Text}");
        }
    }
    else if (evt is WorkflowOutputEvent output)
    {
        // Workflow completed
        var conversationHistory = output.As<List<ChatMessage>>();
        Console.WriteLine("\n=== Final Conversation ===");
        foreach (var message in conversationHistory)
        {
            Console.WriteLine($"{message.AuthorName}: {message.Text}");
        }
        break;
    }
}

Пример взаимодействия

[CopyWriter]: "Green Dreams, Zero Emissions" - Drive the future with style and sustainability.

[Reviewer]: The slogan is good, but "Green Dreams" might be a bit abstract. Consider something 
more direct like "Pure Power, Zero Impact" to emphasize both performance and environmental benefit.

[CopyWriter]: "Pure Power, Zero Impact" - Experience electric excellence without compromise.

[Reviewer]: Excellent! This slogan is clear, impactful, and directly communicates the key benefits. 
The tagline reinforces the message perfectly. Approved for use.

[CopyWriter]: Thank you! The final slogan is: "Pure Power, Zero Impact" - Experience electric 
excellence without compromise.

Настройка клиента чата

from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential

# Initialize the Azure OpenAI chat client
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())

Определение агентов

Создайте специализированные агенты с различными ролями:

from agent_framework import ChatAgent

# Create a researcher agent
researcher = ChatAgent(
    name="Researcher",
    description="Collects relevant background information.",
    instructions="Gather concise facts that help answer the question. Be brief and factual.",
    chat_client=chat_client,
)

# Create a writer agent
writer = ChatAgent(
    name="Writer",
    description="Synthesizes polished answers using gathered information.",
    instructions="Compose clear, structured answers using any notes provided. Be comprehensive.",
    chat_client=chat_client,
)

Настройка группового чата с помощью простого селектора

Создание группового чата с логикой выбора настраиваемого докладчика:

from agent_framework import GroupChatBuilder, GroupChatStateSnapshot

def select_next_speaker(state: GroupChatStateSnapshot) -> str | None:
    """Alternate between researcher and writer for collaborative refinement.

    Args:
        state: Contains task, participants, conversation, history, and round_index

    Returns:
        Name of next speaker, or None to finish
    """
    round_idx = state["round_index"]
    history = state["history"]

    # Finish after 4 turns (researcher → writer → researcher → writer)
    if round_idx >= 4:
        return None

    # Alternate speakers
    last_speaker = history[-1].speaker if history else None
    if last_speaker == "Researcher":
        return "Writer"
    return "Researcher"

# Build the group chat workflow
workflow = (
    GroupChatBuilder()
    .select_speakers(select_next_speaker, display_name="Orchestrator")
    .participants([researcher, writer])
    .build()
)

Настройка группового чата с помощью менеджера Prompt-Based

В качестве альтернативы используйте диспетчер на базе ИИ для динамического выбора динамиков.

# Build group chat with prompt-based manager
workflow = (
    GroupChatBuilder()
    .set_prompt_based_manager(
        chat_client=chat_client,
        display_name="Coordinator"
    )
    .participants(researcher=researcher, writer=writer)
    .build()
)

Запуск рабочего процесса группового чата

Выполните рабочий процесс и обработайте события.

from agent_framework import AgentRunUpdateEvent, WorkflowOutputEvent

task = "What are the key benefits of async/await in Python?"

print(f"Task: {task}\n")
print("=" * 80)

# Run the workflow
async for event in workflow.run_stream(task):
    if isinstance(event, AgentRunUpdateEvent):
        # Print streaming agent updates
        print(f"[{event.executor_id}]: {event.data}", end="", flush=True)
    elif isinstance(event, WorkflowOutputEvent):
        # Workflow completed
        final_message = event.data
        author = getattr(final_message, "author_name", "System")
        text = getattr(final_message, "text", str(final_message))
        print(f"\n\n[{author}]\n{text}")
        print("-" * 80)

print("\nWorkflow completed.")

Пример взаимодействия

Task: What are the key benefits of async/await in Python?

================================================================================

[Researcher]: Async/await in Python provides non-blocking I/O operations, enabling 
concurrent execution without threading overhead. Key benefits include improved 
performance for I/O-bound tasks, better resource utilization, and simplified 
concurrent code structure using native coroutines.

[Writer]: The key benefits of async/await in Python are:

1. **Non-blocking Operations**: Allows I/O operations to run concurrently without 
   blocking the main thread, significantly improving performance for network 
   requests, file I/O, and database queries.

2. **Resource Efficiency**: Avoids the overhead of thread creation and context 
   switching, making it more memory-efficient than traditional threading.

3. **Simplified Concurrency**: Provides a clean, synchronous-looking syntax for 
   asynchronous code, making concurrent programs easier to write and maintain.

4. **Scalability**: Enables handling thousands of concurrent connections with 
   minimal resource consumption, ideal for high-performance web servers and APIs.

--------------------------------------------------------------------------------

Workflow completed.

Основные понятия

  • Централизованный менеджер: групповый чат использует диспетчер для координации выбора и потока говорящего
  • AgentWorkflowBuilder.CreateGroupChatBuilderWith(): создает рабочие процессы с помощью фабричной функции менеджера
  • RoundRobinGroupChatManager: встроенный менеджер, который чередует участников по кругу
  • MaximumIterationCount: управляет максимальным числом итераций агента перед завершением.
  • Настраиваемые диспетчеры: расширение RoundRobinGroupChatManager или реализация пользовательской логики
  • Итеративное уточнение: агенты просматривают и улучшают вклад друг друга
  • Общий контекст: все участники видят полную историю бесед
  • Гибкие стратегии диспетчера: выбор между простыми селекторами, диспетчерами на основе запросов или пользовательской логикой
  • GroupChatBuilder: создает рабочие процессы с настраиваемым выбором говорящего
  • select_speakers(): определение пользовательских функций Python для выбора говорящего
  • set_prompt_based_manager(): использование управляемой ИИ координации для динамического выбора выступающих
  • GroupChatStateSnapshot: предоставляет состояние беседы для выбора решений
  • Итеративная совместная работа: агенты опираются на вклад друг друга
  • Потоковая передача событий: обновления и выходные данные агента обработки в режиме реального времени

Дополнительно: выбор настраиваемого говорящего

Вы можете реализовать логику пользовательского диспетчера, создав пользовательский диспетчер групповых чатов:

public class ApprovalBasedManager : RoundRobinGroupChatManager
{
    private readonly string _approverName;

    public ApprovalBasedManager(IReadOnlyList<AIAgent> agents, string approverName) 
        : base(agents)
    {
        _approverName = approverName;
    }

    // Override to add custom termination logic
    protected override ValueTask<bool> ShouldTerminateAsync(
        IReadOnlyList<ChatMessage> history, 
        CancellationToken cancellationToken = default)
    {
        var last = history.LastOrDefault();
        bool shouldTerminate = last?.AuthorName == _approverName &&
            last.Text?.Contains("approve", StringComparison.OrdinalIgnoreCase) == true;

        return ValueTask.FromResult(shouldTerminate);
    }
}

// Use custom manager in workflow
var workflow = AgentWorkflowBuilder
    .CreateGroupChatBuilderWith(agents => 
        new ApprovalBasedManager(agents, "Reviewer") 
        { 
            MaximumIterationCount = 10 
        })
    .AddParticipants(writer, reviewer)
    .Build();

Вы можете реализовать сложную логику выбора на основе состояния беседы:

def smart_selector(state: GroupChatStateSnapshot) -> str | None:
    """Select speakers based on conversation content and context."""
    round_idx = state["round_index"]
    conversation = state["conversation"]
    history = state["history"]

    # Maximum 10 rounds
    if round_idx >= 10:
        return None

    # First round: always start with researcher
    if round_idx == 0:
        return "Researcher"

    # Check last message content
    last_message = conversation[-1] if conversation else None
    last_text = getattr(last_message, "text", "").lower()

    # If researcher asked a question, let writer respond
    if "?" in last_text and history[-1].speaker == "Researcher":
        return "Writer"

    # If writer provided info, let researcher validate or extend
    if history[-1].speaker == "Writer":
        return "Researcher"

    # Default alternation
    return "Writer" if history[-1].speaker == "Researcher" else "Researcher"

workflow = (
    GroupChatBuilder()
    .select_speakers(smart_selector, display_name="SmartOrchestrator")
    .participants([researcher, writer])
    .build()
)

Когда следует использовать групповой чат

Оркестрация группового чата идеально подходит для:

  • Итеративное уточнение: несколько раундов обзора и улучшения
  • Совместное решение проблем: агенты с дополняющими знаниями, работающие вместе
  • Создание содержимого: рабочие процессы авторов и рецензентов для создания документа
  • Анализ с несколькими перспективами: получение различных точек зрения для одних и того же входных данных
  • Quality Assurance: автоматизированные процессы проверки и утверждения

Рассмотрите варианты, когда:

  • Требуется строгая последовательная обработка (используйте последовательную оркестрацию)
  • Агенты должны работать полностью независимо (использовать параллельную оркестрацию)
  • Необходимы прямые передачи между агентами (используйте оркестрацию Handoff)
  • Требуется сложное динамическое планирование (использование магентической оркестрации)

Дальнейшие шаги