中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

.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的核心優勢

  1. 原生多智能體支持:無需手動管理智能體間的通信,框架自動處理消息路由
  2. 聲明式工作流:通過AgentWorkflowBuilder構建復雜協作場景
  3. 內置編排模式
    • Handoff模式:智能體通過function calling實現控制權轉移
    • GroupChat模式:通過GroupChatManager選擇下一個發言智能體(支持RoundRobin、Prompt-based等策略)
  4. 狀態管理:支持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"
  }]
}

關鍵點

  1. LLM就是一個普通的HTTP接口
  2. 輸入:對話歷史(messages數組)
  3. 輸出:AI生成的回復(content字段)
  4. 所有復雜的Agent功能都是框架基于這個簡單API構建的

函數調用(Function Calling)

函數調用是讓LLM能夠操作外部工具的關鍵機制。

工作流程

  1. 開發者定義可用的函數(工具)
  2. LLM根據用戶意圖決定調用哪個函數
  3. 框架執行函數并獲取結果
  4. 將結果返回給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的優勢

  1. 標準化接口:不同工具遵循統一協議
  2. 動態工具發現:運行時加載工具
  3. 安全隔離:工具在獨立進程運行

在我們的示例項目中,使用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);
    }
}

國內用戶運行指南

方式一:使用阿里云百煉平臺

img

  1. 獲取API密鑰

訪問 ,創建應用并獲取API Key。

  1. 配置appsettings.json
{
  "DefaultModelProvider": "OpenAI",
  "OpenAI": {
    "BaseUrl": "//dashscope.aliyuncs.com/compatible-mode/v1",
    "ModelName": "qwen-plus",
    "ApiKey": "sk-your-api-key"
  }
}
  1. 配置MCP生圖

需要開通MCP生圖服務

使用的key也是和百煉的模型key一致

img

{
  "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 文生圖服務,用于生成圖像"
      }
    ]
  }
}
  1. 運行項目

方式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

img

  1. 獲取API密鑰

訪問

  1. 配置
{
  "DefaultModelProvider": "OpenAI",
  "OpenAI": {
    "BaseUrl": "//api.deepseek.com/v1",
    "ModelName": "deepseek-chat",
    "ApiKey": "sk-your-api-key"
  }
}

方式三:使用Azure AI Foundry

img

  1. 創建Azure OpenAI資源

訪問 ,創建Azure OpenAI服務。

  1. 部署模型

在AI Foundry中部署 gpt-4o-mini 模型。

  1. 配置
{
  "DefaultModelProvider": "AzureOpenAI",
  "AzureOpenAI": {
    "Endpoint": "//your-resource.openai.azure.com/",
    "DeploymentName": "gpt-4o-mini",
    "ApiKey": "your-api-key"
  }
}

實戰演示

測試場景1:直接提及

輸入@莉子 推薦幾部最近的熱門動漫

路由過程

  1. Triage Agent檢測到 @莉子
  2. 調用 handoff_to_rina 函數
  3. 莉子接收消息并回復

輸出

莉子:哇!最近的新番超棒的呢!?
強烈推薦《葬送的芙莉蓮》,這部番真的是神作級別!
還有《咒術回戰》第二季也超燃的!
如果喜歡輕松搞笑的,《關于我轉生變成史萊姆這檔事》第三季也很有趣哦~ (????)

測試場景2:話題路由

輸入最近在學習機器學習,有什么建議嗎?

路由過程

  1. Triage Agent識別"機器學習"屬于科技話題
  2. 路由到克洛伊(科技專家)

輸出

克洛伊:機器學習入門的話,建議從這幾個方面開始:

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美女聊天群組項目,我們學習了:

  1. 大模型基礎:理解LLM API、Function Calling和MCP協議
  2. 多智能體架構:掌握Handoff模式和Triage智能路由
  3. Agent Framework:使用AgentWorkflowBuilder構建Handoff工作流
  4. Aspire編排:通過.NET Aspire實現服務發現和統一啟動
  5. Blazor WASM:前后端分離架構,完全客戶端渲染
  6. 實戰技巧:動態加載、狀態管理、LiteDB持久化

參考資源

官方文檔

國內平臺

示例代碼


如果這篇文章對你有幫助,歡迎點贊收藏!有任何問題也歡迎在評論區討論。

posted @ 2025-10-28 22:53  綠蔭阿廣  閱讀(523)  評論(2)    收藏  舉報