.NET開發上手Microsoft Agent Framework(一)從開發一個AI美女聊天群組開始
前言
在AI快速發展的今天,微軟推出了多個AI開發框架,從早期的AutoGen到Semantic Kernel,再到最新的Microsoft Agent Framework。很多開發者可能會有疑問:為什么微軟要推出這么多框架?它們之間有什么區別?本文將通過一個實際的AI美女聊天群組項目,帶你深入理解Microsoft Agent Framework,掌握多智能體開發的核心概念。
本文的示例代碼已開源:

為什么微軟要推出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 //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"] ?? "//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": "//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": "//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會自動打開(//localhost:15220),顯示:
- agenthost:后端API服務
- webfrontend:Blazor前端
直接點擊webfrontend的URL即可訪問應用。
方式B:獨立啟動各服務
終端1 - 啟動后端:
cd agent-groupchat/AgentGroupChat.AgentHost
dotnet run
# 記下端口,如 //localhost:7390
終端2 - 啟動前端(需先配置后端地址):
編輯AgentGroupChat.Web/wwwroot/appsettings.json:
{
"AgentHostUrl": "//localhost:7390"
}
然后啟動:
cd agent-groupchat/AgentGroupChat.Web
dotnet run
訪問前端地址(如//localhost:5001)
方式二:使用DeepSeek

- 獲取API密鑰
訪問
- 配置
{
"DefaultModelProvider": "OpenAI",
"OpenAI": {
"BaseUrl": "//api.deepseek.com/v1",
"ModelName": "deepseek-chat",
"ApiKey": "sk-your-api-key"
}
}
方式三:使用Azure AI Foundry

- 創建Azure OpenAI資源
訪問 ,創建Azure OpenAI服務。
- 部署模型
在AI Foundry中部署 gpt-4o-mini 模型。
- 配置
{
"DefaultModelProvider": "AzureOpenAI",
"AzureOpenAI": {
"Endpoint": "//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持久化
參考資源
官方文檔
國內平臺
示例代碼
如果這篇文章對你有幫助,歡迎點贊收藏!有任何問題也歡迎在評論區討論。
