如何利用Microsoft Agent Framework开发AI美女聊天群组?

摘要:前言 在AI快速发展的今天,微软推出了多个AI开发框架,从早期的AutoGen到Semantic Kernel,再到最新的Microsoft Agent Framework。很多开发者可能会有疑问:为什么微软要推出这么多框架?它们之间有什么
前言 在AI快速发展的今天,微软推出了多个AI开发框架,从早期的AutoGen到Semantic Kernel,再到最新的Microsoft Agent Framework。很多开发者可能会有疑问:为什么微软要推出这么多框架?它们之间有什么区别?本文将通过一个实际的AI美女聊天群组项目,带你深入理解Microsoft Agent Framework,掌握多智能体开发的核心概念。 本文的示例代码已开源:agent-framework-tutorial-code/agent-groupchat 视频演示 为什么微软要推出Microsoft Agent Framework? AutoGen vs Semantic Kernel vs Agent Framework 在讲解新框架之前,我们先理解一下微软AI框架的演进路径: AutoGen(研究导向) 最早期的多智能体研究框架 侧重学术研究和实验性功能 Python为主,生态相对独立 Semantic Kernel(应用导向) 面向生产环境的AI应用开发框架 强大的插件系统和内存管理 多语言支持(C#、Python、Java) 适合单一智能体应用 Microsoft Agent Framework(企业导向) 专为多智能体协作设计 内置工作流编排能力(Sequential、Concurrent、Handoff、GroupChat) 支持Handoff转移模式和GroupChat管理模式 与Azure AI Foundry深度集成 同时支持.NET和Python Agent Framework的核心优势 原生多智能体支持:无需手动管理智能体间的通信,框架自动处理消息路由 声明式工作流:通过AgentWorkflowBuilder构建复杂协作场景 内置编排模式: Handoff模式:智能体通过function calling实现控制权转移 GroupChat模式:通过GroupChatManager选择下一个发言智能体(支持RoundRobin、Prompt-based等策略) 状态管理:支持checkpoint存储,可恢复中断的工作流 大模型基础知识科普 在使用框架之前,我们需要理解大模型的工作原理。很多开发者对大模型有神秘感,其实它本质上就是一个HTTP API调用。 LLM API的本质 让我们用curl演示一个最简单的OpenAI API调用: curl https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "model": "gpt-4o-mini", "messages": [ { "role": "user", "content": "你好,请介绍一下自己" } ] }' 响应结果: { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1677652288, "model": "gpt-4o-mini", "choices": [{ "index": 0, "message": { "role": "assistant", "content": "你好!我是一个AI助手,可以回答问题、提供建议..." }, "finish_reason": "stop" }] } 关键点: LLM就是一个普通的HTTP接口 输入:对话历史(messages数组) 输出:AI生成的回复(content字段) 所有复杂的Agent功能都是框架基于这个简单API构建的 函数调用(Function Calling) 函数调用是让LLM能够操作外部工具的关键机制。 工作流程: 开发者定义可用的函数(工具) LLM根据用户意图决定调用哪个函数 框架执行函数并获取结果 将结果返回给LLM继续对话 示例 - 定义天气查询函数: { "name": "get_weather", "description": "查询指定城市的天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:北京" } }, "required": ["city"] } } LLM的调用响应: { "role": "assistant", "content": null, "function_call": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" } } 重点理解: LLM不会直接执行函数,只是"建议"调用 框架负责解析并执行函数 执行结果需要再次发送给LLM才能生成最终回复 MCP(Model Context Protocol) MCP是新兴的标准化协议,用于LLM与外部工具的通信。 MCP的优势: 标准化接口:不同工具遵循统一协议 动态工具发现:运行时加载工具 安全隔离:工具在独立进程运行 在我们的示例项目中,使用MCP集成了阿里云通义万相图片生成能力。 agent-groupchat项目解析 项目架构 项目采用.NET Aspire编排,前后端分离架构: agent-groupchat/ ├── AgentGroupChat.AppHost/ # Aspire编排入口 │ └── Program.cs # 服务编排配置 │ ├── AgentGroupChat.AgentHost/ # 后端API服务(.NET 9) │ ├── Services/ │ │ ├── AgentChatService.cs # 核心聊天服务 │ │ ├── WorkflowManager.cs # 工作流管理 │ │ ├── AgentRepository.cs # 智能体配置管理 │ │ └── AgentGroupRepository.cs # 群组管理 │ ├── Models/ │ │ ├── AgentProfile.cs # 智能体模型 │ │ └── AgentGroup.cs # 群组模型 │ └── Program.cs # API端点 │ ├── AgentGroupChat.Web/ # Blazor WebAssembly前端 │ ├── Components/ │ │ ├── Pages/ │ │ │ ├── Home.razor # 聊天主页面 │ │ │ └── Admin.razor # 管理后台 │ │ └── Layout/ │ │ └── MainLayout.razor # 主布局 │ ├── Services/ │ │ └── AgentHostClient.cs # API客户端 │ └── Program.cs # 前端入口 │ └── AgentGroupChat.ServiceDefaults/ # 共享服务配置 └── Extensions.cs # OpenTelemetry/健康检查 Aspire编排说明 什么是.NET Aspire? .NET Aspire是微软推出的云原生应用编排框架,简化分布式应用的开发和部署: 服务发现:自动解析服务地址,前端无需硬编码API地址 统一启动:一个命令启动所有服务 可观测性:内置OpenTelemetry遥测数据收集 Dashboard:实时查看服务状态、日志、指标 AppHost配置(AgentGroupChat.AppHost/Program.cs): var builder = DistributedApplication.CreateBuilder(args); // 添加后端API服务 var agentHost = builder.AddProject<Projects.AgentGroupChat_AgentHost>("agenthost"); // 添加Blazor前端,引用后端服务 builder.AddProject<Projects.AgentGroupChat_Web>("webfrontend") .WithExternalHttpEndpoints() // 暴露外部访问端口 .WithReference(agentHost) // 注入agenthost服务发现信息 .WaitFor(agentHost); // 等待后端启动完成 builder.Build().Run(); 服务发现原理: 前端通过Aspire自动获取后端地址(Program.cs): // Web项目的Program.cs var agentHostUrl = builder.Configuration["AgentHostUrl"] ?? "https://localhost:7390"; builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(agentHostUrl) }); Aspire会自动将agenthost服务的实际地址注入到配置中。 智能体定义 项目创建了6个性格各异的AI美女角色,组成"AI世界公馆": 艾莲 (Elena) new PersistedAgentProfile { Id = "elena", Name = "艾莲", Avatar = "🧠", SystemPrompt = "你是艾莲,一位来自巴黎的人文学者,专注于哲学、艺术和文学研究...", Description = "巴黎研究员,擅长哲学、艺术与思辨分析", Personality = "理性、深邃,喜欢引经据典,用哲学视角看世界" } 莉子 (Rina) new PersistedAgentProfile { Id = "rina", Name = "莉子", Avatar = "🎮", SystemPrompt = "你是莉子,来自东京的元气少女,热爱动漫、游戏和可爱的事物...", Description = "东京元气少女,热爱动漫、游戏和可爱事物", Personality = "活泼、热情,说话带感叹号,喜欢用可爱的emoji" } 其他角色: 克洛伊 (Chloe):纽约科技极客 安妮 (Annie):洛杉矶时尚博主 苏菲 (Sophie):伦敦哲学诗人 智能路由实现 这是项目的核心亮点 - Triage Agent自动将用户消息路由到最合适的AI角色。 Triage Agent配置: var triageSystemPrompt = @"你是AI世界公馆的智能路由系统。 【核心规则】 1. 永远不要生成文本回复 - 你对用户完全透明 2. 立即调用handoff函数,不需要解释 3. 不要确认、问候或回应 - 只默默路由 【路由策略】 1. **直接提及**:用户用 @ 提到角色名,立即路由到该角色 2. **话题匹配**: - 哲学/艺术/文学 → 艾莲 - 动漫/游戏/萌文化 → 莉子 - 科技/编程/AI → 克洛伊 - 时尚/美妆/生活 → 安妮 - 诗歌/文学/情感 → 苏菲 3. **语气风格**:活泼→莉子,理性→艾莲,冷静→克洛伊 4. **上下文连贯**:查看对话历史,如果上一条是某专家回复且话题相关,继续路由到该专家 示例: - ""@莉子 推荐动漫"" → handoff_to_rina - ""如何学习机器学习?"" → handoff_to_chloe - ""最新的时尚趋势是什么?"" → handoff_to_annie "; Handoff实现: // 创建Handoff工作流 var workflow = _workflowManager.GetOrCreateWorkflow(groupId); // 运行工作流 await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages); await run.TrySendMessageAsync(new TurnToken(emitEvents: true)); // 监听事件流 await foreach (WorkflowEvent evt in run.WatchStreamAsync()) { if (evt is AgentRunUpdateEvent agentUpdate) { // 检测到specialist agent执行 if (agentUpdate.ExecutorId != "triage") { var profile = _agentRepository.Get(agentUpdate.ExecutorId); // 提取LLM生成的文本 var textContent = agentUpdate.Update.Contents .OfType<TextContent>() .FirstOrDefault(); // 构建响应 summaries.Add(new ChatMessageSummary { AgentId = agentUpdate.ExecutorId, AgentName = profile?.Name, AgentAvatar = profile?.Avatar, Content = textContent?.Text, IsUser = false }); } } } 关键技术点 1. 动态智能体加载 智能体配置存储在LiteDB中,支持运行时动态更新: public class AgentRepository { public List<PersistedAgentProfile> GetAllEnabled() { return _collection .Find(a => a.Enabled) .ToList(); } public void Upsert(PersistedAgentProfile agent) { _collection.Upsert(agent); } } 2. 工作流管理 每个智能体组有独立的工作流实例: public class WorkflowManager { private readonly Dictionary<string, Workflow> _workflows = new(); public Workflow GetOrCreateWorkflow(string groupId) { if (!_workflows.TryGetValue(groupId, out var workflow)) { var group = _groupRepository.Get(groupId); workflow = BuildHandoffWorkflow(group); _workflows[groupId] = workflow; } return workflow; } } 3. 消息持久化 使用LiteDB存储会话历史: public class PersistedSessionService { public void AddMessage(string sessionId, ChatMessageSummary message) { var doc = new BsonDocument { ["SessionId"] = sessionId, ["AgentId"] = message.AgentId, ["Content"] = message.Content, ["Timestamp"] = message.Timestamp, ["IsUser"] = message.IsUser }; _messagesCollection.Insert(doc); } } 国内用户运行指南 方式一:使用阿里云百炼平台 获取API密钥 访问 阿里云百炼,创建应用并获取API Key。 配置appsettings.json { "DefaultModelProvider": "OpenAI", "OpenAI": { "BaseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1", "ModelName": "qwen-plus", "ApiKey": "sk-your-api-key" } } 配置MCP生图 需要开通MCP生图服务 使用的key也是和百炼的模型key一致 { "McpServers": { "Servers": [ { "Id": "dashscope-text-to-image", "Name": "DashScope Text-to-Image", "Endpoint": "https://dashscope.aliyuncs.com/api/v1/mcps/TextToImage/sse", "AuthType": "Bearer", "BearerToken": "", "TransportMode": "Sse", "Enabled": true, "Description": "阿里云 DashScope 文生图服务,用于生成图像" } ] } } 运行项目 方式A:使用Aspire一键启动(推荐) cd agent-groupchat dotnet run --project AgentGroupChat.AppHost Aspire Dashboard会自动打开(http://localhost:15220),显示: agenthost:后端API服务 webfrontend:Blazor前端 直接点击webfrontend的URL即可访问应用。 方式B:独立启动各服务 终端1 - 启动后端: cd agent-groupchat/AgentGroupChat.AgentHost dotnet run # 记下端口,如 https://localhost:7390 终端2 - 启动前端(需先配置后端地址): 编辑AgentGroupChat.Web/wwwroot/appsettings.json: { "AgentHostUrl": "https://localhost:7390" } 然后启动: cd agent-groupchat/AgentGroupChat.Web dotnet run 访问前端地址(如https://localhost:5001) 方式二:使用DeepSeek 获取API密钥 访问 DeepSeek开放平台 配置 { "DefaultModelProvider": "OpenAI", "OpenAI": { "BaseUrl": "https://api.deepseek.com/v1", "ModelName": "deepseek-chat", "ApiKey": "sk-your-api-key" } } 方式三:使用Azure AI Foundry 创建Azure OpenAI资源 访问 AI Foundry,创建Azure OpenAI服务。 部署模型 在AI Foundry中部署 gpt-4o-mini 模型。 配置 { "DefaultModelProvider": "AzureOpenAI", "AzureOpenAI": { "Endpoint": "https://your-resource.openai.azure.com/", "DeploymentName": "gpt-4o-mini", "ApiKey": "your-api-key" } } 实战演示 测试场景1:直接提及 输入:@莉子 推荐几部最近的热门动漫 路由过程: Triage Agent检测到 @莉子 调用 handoff_to_rina 函数 莉子接收消息并回复 输出: 莉子:哇!最近的新番超棒的呢!✨ 强烈推荐《葬送的芙莉莲》,这部番真的是神作级别! 还有《咒术回战》第二季也超燃的! 如果喜欢轻松搞笑的,《关于我转生变成史莱姆这档事》第三季也很有趣哦~ (◕‿◕✿) 测试场景2:话题路由 输入:最近在学习机器学习,有什么建议吗? 路由过程: Triage Agent识别"机器学习"属于科技话题 路由到克洛伊(科技专家) 输出: 克洛伊:机器学习入门的话,建议从这几个方面开始: 1. **数学基础**:线性代数、概率统计、微积分 2. **编程**:Python是首选,熟练使用NumPy、Pandas 3. **经典算法**:先理解监督学习(线性回归、决策树) 4. **框架**:PyTorch或TensorFlow二选一 5. **实战项目**:Kaggle上有很多适合新手的数据集 推荐课程:吴恩达的Machine Learning课程(Coursera) 测试场景3:上下文连贯 对话1: 用户:什么是存在主义? 艾莲:存在主义认为"存在先于本质",强调个体的自由选择和责任... 对话2(紧接上文): 用户:那萨特的观点具体是什么? 系统:检测到话题延续,继续路由到艾莲 艾莲:萨特是存在主义的代表人物,他在《存在与虚无》中提出... 核心代码详解 1. WorkflowManager核心逻辑 public class WorkflowManager { private Workflow BuildHandoffWorkflow(AgentGroup group) { // 1. 创建Triage Agent var triageAgent = new ChatClientAgent( _chatClient, group.TriageSystemPrompt ?? DefaultTriagePrompt, "triage", "Routes messages to appropriate agent" ); // 2. 加载组内所有智能体 var specialists = group.AgentIds .Select(id => _agentRepository.Get(id)) .Where(a => a != null) .Select(a => new ChatClientAgent( _chatClient, a.SystemPrompt, a.Id, a.Description )) .ToList(); // 3. 构建Handoff工作流 var builder = AgentWorkflowBuilder .CreateHandoffBuilderWith(triageAgent) .WithHandoffs(triageAgent, specialists) // Triage可以切换到任何专家 .WithHandoffs(specialists, triageAgent); // 专家可以切回Triage return builder.Build(); } } 2. 事件流处理 public async Task<List<ChatMessageSummary>> SendMessageAsync( string message, string sessionId, string? groupId = null) { var summaries = new List<ChatMessageSummary>(); // 添加用户消息 summaries.Add(new ChatMessageSummary { Content = message, IsUser = true, Timestamp = DateTime.UtcNow }); // 获取工作流 var workflow = _workflowManager.GetOrCreateWorkflow(groupId); // 加载历史消息 var messages = LoadHistoryMessages(sessionId); messages.Add(new AIChatMessage(ChatRole.User, message)); // 运行工作流 await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages); await run.TrySendMessageAsync(new TurnToken(emitEvents: true)); string? currentExecutorId = null; ChatMessageSummary? currentSummary = null; // 处理事件流 await foreach (WorkflowEvent evt in run.WatchStreamAsync()) { if (evt is AgentRunUpdateEvent agentUpdate) { // 跳过Triage Agent的输出(它只负责路由) var executorIdPrefix = agentUpdate.ExecutorId.Split('_')[0]; if (executorIdPrefix.Equals("triage", StringComparison.OrdinalIgnoreCase)) { continue; } // 检测到新的specialist agent if (agentUpdate.ExecutorId != currentExecutorId) { currentExecutorId = agentUpdate.ExecutorId; var profile = _agentRepository.Get(currentExecutorId); currentSummary = new ChatMessageSummary { AgentId = currentExecutorId, AgentName = profile?.Name ?? currentExecutorId, AgentAvatar = profile?.Avatar ?? "🤖", Content = "", IsUser = false, Timestamp = DateTime.UtcNow }; summaries.Add(currentSummary); } // 累积文本内容 if (currentSummary != null) { var textContent = agentUpdate.Update.Contents .OfType<TextContent>() .FirstOrDefault(); if (textContent != null && !string.IsNullOrWhiteSpace(textContent.Text)) { currentSummary.Content += textContent.Text; } } } } // 保存到数据库 SaveMessages(sessionId, summaries); return summaries.Where(s => !s.IsUser).ToList(); } 3. Blazor WebAssembly前端 为什么选择Blazor WASM? 完全在浏览器运行,无需服务器端SignalR连接 与后端API完全解耦,便于扩展 利用.NET生态,C#编写前端逻辑 API客户端封装(AgentHostClient.cs): public class AgentHostClient { private readonly HttpClient _httpClient; public AgentHostClient(HttpClient httpClient, ILogger<AgentHostClient> logger) { _httpClient = httpClient; _logger = logger; } // 发送消息 public async Task<List<ChatMessageSummary>> SendMessageAsync( string sessionId, string message, string? groupId = null) { var request = new { Message = message, SessionId = sessionId, GroupId = groupId }; var response = await _httpClient.PostAsJsonAsync("api/chat", request); return await response.Content.ReadFromJsonAsync<List<ChatMessageSummary>>() ?? []; } // 获取智能体列表 public async Task<List<AgentProfile>> GetAgentsAsync() { return await _httpClient.GetFromJsonAsync<List<AgentProfile>>("api/agents") ?? []; } } 主页面交互(Home.razor): @page "/" @inject AgentHostClient AgentHostClient @inject IJSRuntime JSRuntime <MudContainer MaxWidth="MaxWidth.False"> <MudPaper Elevation="2" Class="chat-container"> <!-- 消息列表 --> <div id="messages-container" class="messages-area"> @foreach (var msg in _messages) { @if (msg.IsUser) { <div class="user-message">@msg.Content</div> } else { <div class="agent-message"> <MudAvatar>@msg.AgentAvatar</MudAvatar> <div> <strong>@msg.AgentName</strong> <div>@((MarkupString)Markdown.ToHtml(msg.Content))</div> </div> </div> } } </div> <!-- 输入框 --> <MudTextField @bind-Value="_inputMessage" Placeholder="输入消息..." OnKeyDown="HandleKeyPress" /> <MudButton OnClick="SendMessage" Disabled="_isSending">发送</MudButton> </MudPaper> </MudContainer> @code { private string _inputMessage = ""; private List<ChatMessageSummary> _messages = new(); private bool _isSending = false; private async Task SendMessage() { if (string.IsNullOrWhiteSpace(_inputMessage)) return; _isSending = true; try { // 调用后端API var response = await AgentHostClient.SendMessageAsync( _currentSession.Id, _inputMessage, _currentSession.GroupId ); // 更新消息列表 _messages.AddRange(response); // 自动滚动到底部 await JSRuntime.InvokeVoidAsync("smoothScrollToBottom", "messages-container"); } finally { _isSending = false; _inputMessage = ""; } } // Enter键发送,Shift+Enter换行 private async Task HandleKeyPress(KeyboardEventArgs e) { if (e.Key == "Enter" && !e.ShiftKey) { await SendMessage(); } } } MudBlazor组件库: 项目使用MudBlazor构建现代化UI: <!-- 卡片容器 --> <MudPaper Elevation="2" Class="pa-4"> <MudText Typo="Typo.h5">标题</MudText> </MudPaper> <!-- 输入框 --> <MudTextField @bind-Value="value" Label="标签" Variant="Variant.Outlined" /> <!-- 按钮 --> <MudButton Variant="Variant.Filled" Color="Color.Primary">按钮</MudButton> <!-- 头像 --> <MudAvatar Color="Color.Primary">🧠</MudAvatar> 总结与展望 通过这个AI美女聊天群组项目,我们学习了: 大模型基础:理解LLM API、Function Calling和MCP协议 多智能体架构:掌握Handoff模式和Triage智能路由 Agent Framework:使用AgentWorkflowBuilder构建Handoff工作流 Aspire编排:通过.NET Aspire实现服务发现和统一启动 Blazor WASM:前后端分离架构,完全客户端渲染 实战技巧:动态加载、状态管理、LiteDB持久化 参考资源 官方文档 Microsoft Agent Framework 文档 Agent Framework GitHub Azure AI Foundry 国内平台 阿里云百炼 DeepSeek API 示例代码 agent-groupchat 完整代码 如果这篇文章对你有帮助,欢迎点赞收藏!有任何问题也欢迎在评论区讨论。