使用微软代理框架构建多代理人工智能系统

· 6 分钟阅读

简介

我们已经进入了多智能体人工智能系统的时代。该行业不再是单一的单一人工智能处理所有事情,而是转向协作解决复杂问题的专业代理——就像组织良好的专家团队一样。一名代理人进行研究,另一名代理人进行分析,第三名代理人进行写作,一名协调员让每个人都步入正轨。

如果您使用过大型语言模型,您可能已经达到了单个提示功能的上限。上下文窗口被填满,指令变得混乱,质量下降。多代理架构通过将复杂的任务分解为集中的职责来解决这个问题,其中每个代理都是一件事的专家。

Microsoft 的代理框架是更广泛的语义内核生态系统的一部分,它为 .NET 开发人员提供了用于构建此类系统的一流工具包。在这篇文章中,我们将从零开始构建一个完全工作的多代理管道,涵盖核心概念、编排模式和入门所需的实用代码。

什么是微软的代理框架?

Agent Framework 是 Microsoft 在 .NET 中构建、编排和部署 AI 代理和多代理系统的解决方案。它与语义内核并存,并与之深度集成,语义内核自 2023 年以来一直是微软用于 AI 编排的开源 SDK。

可以这样想:语义内核为您提供原语(内核、插件、内存、规划器),而代理框架为您提供专门为代理到代理通信和协调而设计的更高级别的抽象。

该框架支持多个模型提供程序,包括 Azure OpenAI、OpenAI 以及 Azure AI Foundry 上托管的模型。它在设计上与模型无关,但与 Azure 生态系统深度集成,这使得它对于企业场景特别有吸引力。

主要能力包括:

  • 多种代理类型:针对不同后端的 ChatCompletionAgentOpenAIAssistantAgentAzureAIAgent
  • 编排模式:顺序、并发、切换和群聊工作流程
  • 插件生态系统:使用本机 C# 函数、OpenAPI 规范和模型上下文协议 (MCP) 工具扩展代理
  • 会话管理:内置线程、历史管理和终止策略
  • 可观察性:与 OpenTelemetry 集成以跟踪代理交互

关键概念

在编写任何代码之前,让我们先建立词汇表。代理框架围绕一些核心抽象展开。

代理

代理是由 AI 模型支持的实体,配置有特定指令(系统提示)、名称以及可选的一组插件或工具。每个代理都是专家——您可以定义它知道什么、可以做什么以及应该如何表现。

聊天完成代理最直接的代理类型。它包装聊天完成端点(Azure OpenAI、OpenAI 等)并维护对话。它在调用之间是无状态的——您提供历史记录,它就会做出响应。这使得它变得轻量且易于推理。

ChatCompletionAgent agent = new()
{
    Name = "Reviewer",
    Instructions = "You are a senior code reviewer. Analyze the provided code for bugs, security issues, and style violations. Be concise and actionable.",
    Kernel = kernel
};

OpenAIAssistantAgent

此代理类型利用 OpenAI Assistants API,该 API 提供服务器端对话状态、文件处理和代码解释。它更重量级,但为您提供持久线程和内置工具,例如代码解释器和文件搜索。

OpenAIAssistantAgent agent = await OpenAIAssistantAgent.CreateAsync(
    clientProvider: clientProvider,
    definition: new OpenAIAssistantDefinition("gpt-4o")
    {
        Name = "DataAnalyst",
        Instructions = "You analyze datasets and produce statistical summaries."
    }
);

代理群组聊天

这是协调器。 AgentGroupChat 管理多个代理之间的多轮对话,控制下一个发言者、对话何时结束以及如何共享历史记录。这就是多智能体协作的神奇之处。

编排模式

该框架支持四种主要的编排模式,每种模式适合不同的问题。

顺序

代理按照定义的顺序依次执行。代理 A 的输出馈送到代理 B,代理 B 的输出馈送到代理 C。这对于管道来说是理想的:草稿 → 审查 → 编辑 → 发布。

// Conceptual flow
var draft = await writerAgent.InvokeAsync("Write a blog post about .NET 9");
var reviewed = await reviewerAgent.InvokeAsync($"Review this: {draft}");
var edited = await editorAgent.InvokeAsync($"Edit based on feedback: {reviewed}");

并发

多个代理同时处理同一输入。您将工作展开,然后汇总结果。非常适合获得不同的观点——比如让三个审阅者查看同一个拉取请求。

切换

代理根据对话上下文决定将控制权转移给另一个代理。这模仿了客户服务团队的工作方式:一线代理处理基本查询并在需要时升级为专家。

群聊

多个代理参与公开对话,根据选择策略轮流进行。 AgentGroupChat 类通过可配置的轮流和终止逻辑来实现此模式。

构建你的第一个代理

让我们实际一点吧。以下是如何逐步构建您的第一个代理。

先决条件

你需要:

  • .NET 9 SDK
  • 具有已部署模型的 Azure OpenAI 资源(例如 gpt-4o
  • Visual Studio 或 VS Code

设置项目

创建一个新的控制台应用程序并安装所需的包:

dotnet new console -n AgentDemo
cd AgentDemo
dotnet add package Microsoft.SemanticKernel --prerelease
dotnet add package Microsoft.SemanticKernel.Agents.Core --prerelease
dotnet add package Azure.AI.OpenAI
dotnet add package Azure.Identity

创建一个简单的代理

首先,使用 Azure OpenAI 配置设置内核:

using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;

var builder = Kernel.CreateBuilder();

builder.AddAzureOpenAIChatCompletion(
    deploymentName: "gpt-4o",
    endpoint: "https://your-resource.openai.azure.com/",
    credentials: new DefaultAzureCredential()
);

Kernel kernel = builder.Build();

现在创建一个代理并调用它:

ChatCompletionAgent agent = new()
{
    Name = "TechWriter",
    Instructions = """
        You are a technical writer specializing in software documentation.
        Write clear, concise content aimed at experienced developers.
        Use code examples when appropriate.
        Always structure your output with headings and bullet points.
        """,
    Kernel = kernel
};

ChatHistory history = new();
history.AddUserMessage("Explain dependency injection in .NET in 200 words.");

await foreach (ChatMessageContent response in agent.InvokeAsync(history))
{
    Console.WriteLine(response.Content);
}

就是这样。你有一个工作代理人。但真正的力量来自于特工们的共同努力。

使用 AgentGroupChat 进行多代理编排

让我们构建一些更有趣的东西——多个代理协作的群聊。 AgentGroupChat 类管理对话流,包括谁接下来发言以及何时停止。

定义代理

我们将创建三个代理:作家、审阅者和编辑。

ChatCompletionAgent writer = new()
{
    Name = "Writer",
    Instructions = """
        You are a content writer. When given a topic, produce a well-structured
        draft. Focus on clarity and technical accuracy.
        When you receive feedback from the Reviewer, incorporate it into a
        revised draft.
        """,
    Kernel = kernel
};

ChatCompletionAgent reviewer = new()
{
    Name = "Reviewer",
    Instructions = """
        You are a content reviewer. Analyze drafts for technical accuracy,
        clarity, and completeness. Provide specific, actionable feedback.
        Do NOT rewrite the content  only provide feedback.
        When the content meets your standards, respond with: APPROVED
        """,
    Kernel = kernel
};

ChatCompletionAgent editor = new()
{
    Name = "Editor",
    Instructions = """
        You are an editor. Once content is approved by the Reviewer,
        polish it for grammar, tone, and formatting.
        Output only the final polished version.
        When you have produced the final version, respond with: COMPLETE
        """,
    Kernel = kernel
};

设置群聊

AgentGroupChat 需要两个关键配置:选择策略(下一个发言者)和终止策略(何时停止)。

AgentGroupChat chat = new(writer, reviewer, editor)
{
    ExecutionSettings = new()
    {
        SelectionStrategy = new SequentialSelectionStrategy(),
        TerminationStrategy = new ApprovalTerminationStrategy()
        {
            Agents = [editor],
            MaximumIterations = 12
        }
    }
};

自定义终止策略终止策略定义对话何时结束。这是一个查找“COMPLETE”关键字的自定义:

class ApprovalTerminationStrategy : TerminationStrategy
{
    protected override Task<bool> ShouldAgentTerminateAsync(
        Agent agent,
        IReadOnlyList<ChatMessageContent> history,
        CancellationToken cancellationToken = default)
    {
        bool isComplete = history
            .Last()
            .Content?
            .Contains("COMPLETE", StringComparison.OrdinalIgnoreCase) ?? false;

        return Task.FromResult(isComplete);
    }
}

运行对话

通过用户消息开始对话并让代理进行协作:

chat.AddChatMessage(
    new ChatMessageContent(AuthorRole.User,
        "Write a 300-word technical overview of gRPC vs REST for microservices.")
);

await foreach (ChatMessageContent message in chat.InvokeAsync())
{
    Console.WriteLine($"[{message.AuthorName}]: {message.Content}");
    Console.WriteLine("---");
}

流程将如下所示:

  1. 作者 起草草稿
  2. 审稿人提供反馈
  3. 作者根据反馈进行修改
  4. 审阅者说“已批准”
  5. 编辑润色并说“完成”
  6. 通话结束

这种来回自动继续,直到满足终止条件或达到 MaximumIterations

插件和工具

当代理能够与外部系统交互时,它们就会变得真正强大。 Agent框架支持三种主要的扩展机制。

本机函数(内核插件)

您可以让代理访问 C# 方法作为工具。当代理确定需要这些函数时,它将调用这些函数:

public class ContentToolsPlugin
{
    [KernelFunction("word_count")]
    [Description("Counts the number of words in the provided text.")]
    public int WordCount([Description("The text to count words in")] string text)
    {
        return text.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
    }

    [KernelFunction("check_readability")]
    [Description("Calculates a readability score for the given text.")]
    public string CheckReadability([Description("The text to analyze")] string text)
    {
        var words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
        var sentences = text.Split(['.', '!', '?'], StringSplitOptions.RemoveEmptyEntries).Length;

        if (sentences == 0) return "Unable to calculate — no sentences found.";

        double avgWordsPerSentence = (double)words / sentences;

        return avgWordsPerSentence switch
        {
            < 15 => $"Easy to read (avg {avgWordsPerSentence:F1} words/sentence)",
            < 25 => $"Moderate difficulty (avg {avgWordsPerSentence:F1} words/sentence)",
            _ => $"Difficult to read (avg {avgWordsPerSentence:F1} words/sentence). Consider shorter sentences."
        };
    }
}

在创建代理之前在内核上注册插件:

kernel.Plugins.AddFromType<ContentToolsPlugin>();

ChatCompletionAgent analyst = new()
{
    Name = "ContentAnalyst",
    Instructions = "Analyze content using available tools. Report word count and readability.",
    Kernel = kernel,
    Arguments = new KernelArguments(
        new PromptExecutionSettings
        {
            FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
        })
};

模型上下文协议 (MCP)

MCP 是一种用于将 AI 模型连接到外部工具和数据源的开放标准。代理框架支持 MCP,这意味着您的代理可以使用任何符合 MCP 的服务器公开的工具。这为文件系统、数据库、API 等打开了大门——所有这些都通过标准化接口实现。

// Example: Adding an MCP server for file operations
kernel.Plugins.AddFromMcpServer("filesystem",
    new Uri("http://localhost:3000/mcp"));

这是特别令人兴奋的,因为这意味着您的代理不仅限于您构建的内容 - 他们可以利用其他人开发和共享的 MCP 工具生态系统。

真实示例:内容审核流程

让我们将所有内容与实际场景结合起来。想象一下,您正在构建一个内部工具,可以自动为文档团队进行内容审查。该管道有四个阶段:

  1. 研究员 — 收集相关技术信息
  2. 作家 — 根据研究撰写草稿
  3. 审阅者 — 检查准确性和完整性
  4. 发布者 — 格式化并准备最终输出

这是一个精简的实现:

using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;

// Build the kernel
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
    deploymentName: "gpt-4o",
    endpoint: "https://your-resource.openai.azure.com/",
    credentials: new DefaultAzureCredential()
);
Kernel kernel = builder.Build();

// Define specialized agents
ChatCompletionAgent researcher = new()
{
    Name = "Researcher",
    Instructions = """
        You are a technical researcher. Given a topic, identify the key
        concepts, recent developments, and important details that should
        be covered. Output a structured research brief.
        """,
    Kernel = kernel
};

ChatCompletionAgent writer = new()
{
    Name = "Writer",
    Instructions = """
        You are a technical writer. Using the research brief provided,
        write a comprehensive, well-structured article. Include code
        examples where relevant. Target audience: experienced developers.
        """,
    Kernel = kernel
};

ChatCompletionAgent reviewer = new()
{
    Name = "Reviewer",
    Instructions = """
        You are a senior technical reviewer. Check the article for:
        - Technical accuracy
        - Completeness relative to the research brief
        - Code correctness
        - Clarity and structure
        If everything looks good, respond with APPROVED.
        Otherwise, provide specific feedback for revision.
        """,
    Kernel = kernel
};

ChatCompletionAgent publisher = new()
{
    Name = "Publisher",
    Instructions = """
        You are a content publisher. Once the article is approved,
        format it with proper markdown, add a summary at the top,
        and ensure consistent formatting throughout.
        End your response with COMPLETE.
        """,
    Kernel = kernel
};

// Configure the group chat
AgentGroupChat chat = new(researcher, writer, reviewer, publisher)
{
    ExecutionSettings = new()
    {
        SelectionStrategy = new SequentialSelectionStrategy(),
        TerminationStrategy = new ApprovalTerminationStrategy()
        {
            Agents = [publisher],
            MaximumIterations = 15
        }
    }
};

// Start the pipeline
chat.AddChatMessage(new ChatMessageContent(
    AuthorRole.User,
    "Create a technical article about implementing health checks in ASP.NET Core microservices."
));

await foreach (ChatMessageContent message in chat.InvokeAsync())
{
    Console.ForegroundColor = message.AuthorName switch
    {
        "Researcher" => ConsoleColor.Cyan,
        "Writer" => ConsoleColor.Green,
        "Reviewer" => ConsoleColor.Yellow,
        "Publisher" => ConsoleColor.Magenta,
        _ => ConsoleColor.White
    };

    Console.WriteLine($"\n{'='new string('=', 60)}");
    Console.WriteLine($"[{message.AuthorName}]");
    Console.WriteLine(new string('=', 60));
    Console.WriteLine(message.Content);
}

Console.ResetColor();

该流程通过代理协作生成一篇经过充分研究、撰写、审查和格式化的文章。每个代理都专注于自己最擅长的事情,AgentGroupChat 协调工作流程。

最佳实践

在构建了多个多代理系统之后,以下是我发现最有价值的模式和实践。

错误处理

始终在终止策略中设置 MaximumIterations。如果没有它,代理可能会进入无限循环——尤其是当审稿人不断发现问题而作者不断修改而没有改进时。

TerminationStrategy = new ApprovalTerminationStrategy()
{
    MaximumIterations = 12 // Safety net
}

将代理调用包装在 try-catch 块中。 API 速率限制、网络问题和模型错误都是生产系统的现实:

try
{
    await foreach (var message in chat.InvokeAsync(cancellationToken))
    {
        // Process messages
    }
}
catch (HttpOperationException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
    // Implement retry with exponential backoff
    await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
}

可观察性代理框架与 OpenTelemetry 集成,这意味着您可以跟踪每个代理交互、工具调用和令牌使用情况。这对于调试多代理工作流程至关重要,因为在这种情况下,哪个代理导致问题并不总是很明显。

通过添加语义内核遥测包并配置您的首选导出器(Application Insights、Jaeger 等)来设置基本遥测:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("Microsoft.SemanticKernel*");
        tracing.AddOtlpExporter();
    });

成本管理

多代理系统会增加您的 API 成本 - 每个代理轮次都是一次 API 调用,而群聊可以产生多次轮次。控制成本的一些策略:

  • 使用更便宜的模型来实现更简单的代理:并非每个代理都需要 GPT-4o。审稿人可能会使用较小的模型来很好地工作,而只有作者需要重磅模型。
  • 限制历史记录:在执行设置中使用 ReducedHistoryCount 来限制每个代理接收的对话上下文数量。
  • 设置严格的迭代限制:使用合理的 MaximumIterations 值防止失控对话。
  • 尽可能缓存:如果代理重复执行相同的查找,则将结果缓存在插件中。

代理设计

  • 保持指令集中:每个代理都应该有一个单一、明确的职责。宽泛的指令会导致所有任务的表现平庸。
  • 明确输出格式:准确地告诉代理如何构建他们的响应。这使得下游解析可靠。
  • 使用终止关键字:定义代理用来表明他们已完成的明确信号(例如“批准”或“完成”)。这使得终止策略变得简单且可预测。

结论

多智能体人工智能系统代表了我们构建智能应用程序方式的根本转变。我们可以将问题分解为专门的角色并让代理进行协作,而不是纠结于单一提示来处理所有事情。

Microsoft 的代理框架使这对于.NET 开发人员来说非常实用。抽象是干净的——代理、群聊、选择和终止策略——而且它们自然组成。结合语义内核的插件生态系统和 Azure 的模型托管,您将拥有用于构建生产级多代理系统的完整堆栈。

该框架仍在不断发展(许多包都处于预览状态),但核心模式很扎实,方向也很明确。如果您正在 .NET 中构建人工智能驱动的应用程序,那么现在是开始尝试多代理架构的时候了。

从小事做起——构建两个代理来协作完成一个简单的任务。一旦您看到单代理方法的质量改进,您就会希望将所有内容分解为代理团队。

快乐编码!