资讯动态

Microsoft Agent Framework:.NET智能体系统架构演进

发布时间:2026/10/20 8:36:45 来源:尧图企业网站定制
1. 项目概述这不是一次简单的工具替换而是一场底层思维的迁移“.NET AI 生态系统的重构与演进Microsoft Agent Framework 对 Semantic Kernel 的继承、超越与战略整合”——这个标题里藏着三个关键动作“重构”、“演进”、“整合”。它不是在说“又出了个新库”而是在宣告.NET 平台上的 AI 开发范式正在从“调用模型 API”的辅助阶段正式迈入“构建自主智能体Agent系统”的工程化阶段。我接触过大量基于 Semantic KernelSK的早期项目从某高校实验室的课程 Demo 到某公司内部的知识助手原型它们共同的特点是逻辑清晰、结构规整但一旦业务复杂度上升比如要同时处理用户多轮模糊提问、调用多个异步外部服务、在失败时自动回退并重试、还要把整个决策链路可审计地记录下来——代码就会迅速膨胀成难以维护的“状态机迷宫”。而 Microsoft Agent FrameworkMAF的出现恰恰就是为了解决这个痛点。它没有抛弃 SK 的核心价值比如对 LLM 编排、函数调用Function Calling、记忆管理Memory等能力的抽象而是把这些能力重新组织进一个更贴近真实世界任务流的模型里Agent 不再是被动响应请求的函数集合而是能主动规划Plan、执行Act、反思Reflect、协作Collaborate的软件实体。这背后是微软对 AI 应用开发本质的一次深刻重估开发者真正需要的不是更灵活的 Prompt 工具而是更健壮的“AI 系统架构”。所以如果你现在还在用 SK 写一个需要调用天气、日历、邮件三个插件来帮用户安排行程的 Demo那 MAF 就是你下一步必须认真研究的演进路径如果你正准备启动一个面向企业客户的智能客服中台项目那么 MAF 提供的内置可观测性、生命周期管理、以及与 Azure AI Studio 的深度集成就不是锦上添花而是降低交付风险的刚需。它面向的不是单点功能的实现者而是 AI 系统的架构师与交付负责人。2. 核心设计思路拆解从“编排引擎”到“智能体操作系统”2.1 为什么必须重构Semantic Kernel 的历史功绩与时代局限要理解 MAF 的价值得先看清 SK 的底色。Semantic Kernel 诞生于大模型爆发初期它的核心使命是“让 .NET 开发者能快速上手 LLM”。因此它的设计哲学是“最小可行抽象”提供一个轻量级的 Kernel 对象封装了 Prompt 渲染、插件Plugin加载、函数调用Function Calling的调度逻辑。这种设计极其成功它让无数 .NET 工程师在一周内就能做出一个能调用本地函数的聊天机器人。但它的局限也根植于此——它本质上是一个增强版的 Prompt 编排器。我们来看一个典型 SK 项目的代码骨架var kernel new KernelBuilder() .WithOpenAIChatCompletion(gpt-4, your-key) .Build(); // 加载插件 kernel.ImportPluginFromTypeWeatherPlugin(weather); kernel.ImportPluginFromTypeCalendarPlugin(calendar); // 执行一个复合任务先查天气再根据天气建议日程 var result await kernel.InvokeAsync( You are a helpful assistant. Check todays weather in Beijing, then suggest one outdoor activity if its sunny, or an indoor activity if its rainy., new KernelArguments { [location] Beijing } );这段代码的问题不在于写法而在于责任边界模糊。InvokeAsync这一行承担了意图识别、工具选择、参数提取、调用顺序、错误处理、结果聚合等全部职责。当业务变复杂比如要求“如果天气API超时则降级使用缓存数据并通知用户”这个逻辑就得硬编码在 Prompt 里或 Kernel 的回调中很快就会失控。SK 的 Plugin 模型也缺乏对“状态”的原生支持——一个需要记住用户偏好、历史对话上下文、当前任务进度的智能体其状态散落在Kernel、Memory、ContextVariables等多个对象中没有统一的生命周期管理。这就像给一辆自行车加装了涡轮增压动力是有了但底盘、悬挂、刹车系统都没升级高速行驶时风险陡增。MAF 的重构正是要打造一套完整的“智能体底盘”。2.2 MAF 的核心范式以 Agent 为中心的分层架构MAF 的设计思想非常清晰把“智能体”Agent本身作为一等公民First-Class Citizen来建模和管理。它不再围绕“一次调用”展开而是围绕“一个 Agent 的完整生命周期”展开。这个生命周期被明确划分为四个核心层级每一层都解决一个特定维度的问题Agent 层The Agent Itself这是最顶层的抽象。一个Agent不再是一个函数集合而是一个拥有自己身份ID、角色Role、目标Goal、能力Capabilities和记忆Memory的独立实体。你可以创建ResearchAgent、WritingAgent、CodeReviewAgent它们各自封装了领域知识和行为模式。Agent类型本身就是一个可序列化、可持久化、可监控的对象。Orchestration 层编排层这是 MAF 最具革命性的部分。它引入了Orchestrator的概念一个专门负责“如何让多个 Agent 协同工作”的调度器。Orchestrator不再是 SK 那种隐式的、基于 Prompt 的调度而是显式的、可编程的、支持多种策略的控制器。它内置了三种标准策略Sequential: 严格按顺序执行 Agent 链。Parallel: 同时启动多个 Agent等待全部完成。Router: 基于输入内容动态路由到最合适的 Agent。 更重要的是Orchestrator是可扩展的。你可以轻松实现自己的CustomOrchestrator比如一个基于强化学习的动态路由器或者一个能根据 SLA服务等级协议自动选择模型供应商的智能调度器。这彻底解耦了“业务逻辑”与“调度逻辑”。Runtime 层运行时层这是 MAF 的“操作系统内核”。它提供了 Agent 运行所必需的基础设施State Management状态管理: 每个 Agent 都有一个强类型的State对象其生命周期与 Agent 绑定。你无需再手动管理ContextVariables所有中间状态、临时变量、任务进度都自然地存储在State中并且可以被序列化到 Redis 或 Cosmos DB 中实现跨请求、跨实例的状态恢复。Observability可观测性: MAF 内置了完整的 OpenTelemetry 支持。每一次 Agent 的Plan、Act、Think、Observe操作都会自动生成结构化的 Trace 和 Log。你不需要额外埋点就能在 Azure Monitor 或任何兼容的 APM 工具中看到一个复杂任务是如何被分解、由哪个 Agent 执行、耗时多少、调用了哪些外部服务、是否发生了重试。这对生产环境的故障排查和性能优化是无价的。Lifecycle Management生命周期管理: Agent 的创建、初始化、执行、暂停、恢复、销毁都有明确的 Hook钩子方法。你可以在OnInitialized中加载专属配置在OnExecuting中进行权限校验在OnCompleted中触发后续的 Webhook 通知。这使得 Agent 成为了一个真正可管理、可治理的软件组件。Foundation Layer基础层这一层是 MAF 与 SK 的“继承”所在。它复用了 SK 最成熟、最稳定的部分Kernel、PromptTemplate、FunctionCalling、Memory等核心能力。这意味着你过去为 SK 编写的高质量插件Plugin几乎可以 100% 无缝迁移到 MAF 中作为某个 Agent 的“能力”Capability被调用。MAF 并没有推翻重来而是在 SK 的坚实地基上盖起了一座更高、更稳固的智能体大厦。2.3 战略整合为何不是取代而是升维将 MAF 定位为“对 SK 的取代”是一种严重的误读。微软的战略意图非常明确SK 是“AI 能力的乐高积木”而 MAF 是“用这些积木搭建智能城市的蓝图与施工队”。它们的关系是互补与升维而非替代。这种整合体现在三个层面技术栈整合MAF 的 NuGet 包Microsoft.Extensions.AI是一个统一的、面向未来的 AI 抽象层。它定义了IChatClient、IEmbeddingClient、IRerankerClient等接口这些接口既被 SK 实现也被 MAF 的底层 Runtime 所依赖。这意味着无论你用 SK 写一个简单的文本摘要工具还是用 MAF 构建一个复杂的多 Agent 客服系统你所依赖的底层模型客户端、向量数据库客户端都是同一套标准化的接口。这极大地降低了技术栈碎片化带来的维护成本。工具链整合MAF 与 Azure AI Studio 的集成是深度的、原生的。在 AI Studio 的可视化画布中你不仅可以像以前一样调试一个 Prompt现在还能直接拖拽出一个ResearchAgent、一个SummarizeAgent并用连线定义它们之间的数据流和控制流。AI Studio 会自动生成对应的 MAF 代码框架并为你预置好 Azure Monitor 的监控仪表盘。这种“所见即所得”的低代码体验是 SK 时代无法想象的。它把 AI 系统的开发从纯代码编写提升到了“系统架构设计”的层面。生态位整合微软清晰地划分了两者的适用场景。SK 依然是快速原型Rapid Prototyping、小型工具Small Utilities、以及需要极致轻量级控制的场景如嵌入到一个已有 Web API 的中间件中的首选。而 MAF 则是构建生产级、可扩展、可运维的 AI 应用Production-Grade AI Applications的官方推荐框架。这种“双轨并行”的策略确保了 .NET AI 生态既能保持敏捷创新又能支撑企业级的严肃交付。3. 核心细节解析与实操要点从概念到代码的关键跃迁3.1 Agent 的定义不只是一个类而是一个有生命的组件在 MAF 中定义一个 Agent 远不止是写一个继承Agent的类。它是一个包含身份、目标、能力和状态的完整契约。我们以一个DocumentReviewerAgent为例看其核心构成// 1. 定义强类型 State这是 MAF 的核心创新 public class DocumentReviewerState { public string? DocumentId { get; set; } public string? ReviewerName { get; set; } public Liststring Findings { get; set; } new(); public int CurrentPage { get; set; } 1; public bool IsApproved { get; set; } false; } // 2. 定义 Agent继承自泛型 AgentTState public class DocumentReviewerAgent : AgentDocumentReviewerState { // 构造函数注入依赖遵循 .NET Core 最佳实践 public DocumentReviewerAgent( IChatClient chatClient, IDocumentService documentService, ILoggerDocumentReviewerAgent logger) : base(chatClient, logger) { // 注册该 Agent 的“能力”Capabilities // 这些能力最终会被 Orchestrator 调用 RegisterCapability(new ReviewDocumentCapability(documentService)); RegisterCapability(new ApproveDocumentCapability()); RegisterCapability(new RequestChangesCapability()); } // 3. 定义 Agent 的核心行为Plan规划 // 这是 Agent 的“大脑”决定接下来要做什么 protected override async TaskPlan OnPlanAsync( Plan plan, CancellationToken cancellationToken default) { // 基于当前 State 和输入生成一个执行计划 var prompt $ You are a senior document reviewer. Current document: {State.DocumentId}, Page {State.CurrentPage}. Your goal is to review and approve this document. Based on the findings so far ({string.Join(, , State.Findings)}), what is the next best action? Choose from: ReviewNextPage, Approve, RequestChanges. Output only the action name. ; var response await ChatClient.GetChatMessageContentAsync( new ChatHistory(prompt), cancellationToken); var nextAction response.Content.Trim(); switch (nextAction) { case ReviewNextPage: State.CurrentPage; break; case Approve: State.IsApproved true; break; } return plan; // 返回更新后的 Plan } // 4. 定义 Agent 的核心行为Act执行 // 这是 Agent 的“手脚”调用具体能力 protected override async Task OnActAsync( Plan plan, CancellationToken cancellationToken default) { // 根据 Plan 中的指令调用注册的能力 foreach (var step in plan.Steps) { if (step.Name ReviewNextPage) { await GetCapabilityReviewDocumentCapability() .ExecuteAsync(State.DocumentId, State.CurrentPage, cancellationToken); } } } }这段代码揭示了几个关键实操要点State 是第一公民DocumentReviewerState不是一个 DTO而是一个承载着 Agent “灵魂”的对象。它的每一个属性都代表了 Agent 在执行任务过程中的一个关键状态。State的变更会自动触发序列化为后续的断点续传、状态快照、审计追踪打下基础。实操心得我强烈建议为State类添加[JsonObject]特性并为其所有属性提供默认值。这能极大避免在反序列化时因字段缺失导致的NullReferenceException这是我在早期测试中踩过的一个深坑。Capability 是原子能力单元ReviewDocumentCapability是一个独立的、可测试的类它只做一件事调用文档服务获取某一页的内容并进行分析。它不关心上下文、不维护状态这完美符合单一职责原则。实操心得不要把业务逻辑塞进 Capability 里。Capability 应该是纯粹的“胶水代码”负责与外部系统API、DB、文件系统交互。真正的业务规则判断应该放在OnPlanAsync中。这样你的 Capability 可以被多个不同的 Agent 复用大大提升了代码的复用率。Plan/Act 分离是心智模型的转变OnPlanAsync负责“想”OnActAsync负责“做”。这种分离强制开发者思考“决策”与“执行”的边界。它让代码逻辑更清晰也更容易进行单元测试——你可以 MockChatClient来测试OnPlanAsync的决策逻辑而无需真正调用 LLM。注意事项OnPlanAsync的返回值Plan对象其Steps属性是一个ListPlanStep。每个PlanStep都有一个Name和Parameters。OnActAsync的职责就是遍历这个列表并根据Name去调用对应的能力。这是一个非常明确的契约杜绝了 SK 时代那种“一个 InvokeAsync 调用里混杂了所有逻辑”的混乱。3.2 Orchestrator 的实战让多个 Agent 协同工作的指挥官定义好单个 Agent 后真正的挑战在于如何让它们协同。MAF 的Orchestrator就是这个指挥官。下面是一个典型的CustomerSupportOrchestrator的实现它协调IntentClassifierAgent、KnowledgeBaseAgent和ResponseGeneratorAgent三个 Agentpublic class CustomerSupportOrchestrator : Orchestrator { private readonly IntentClassifierAgent _intentAgent; private readonly KnowledgeBaseAgent _kbAgent; private readonly ResponseGeneratorAgent _responseAgent; public CustomerSupportOrchestrator( IntentClassifierAgent intentAgent, KnowledgeBaseAgent kbAgent, ResponseGeneratorAgent responseAgent) { _intentAgent intentAgent; _kbAgent kbAgent; _responseAgent responseAgent; } // 重写 ExecuteAsync定义协同流程 public override async TaskOrchestrationResult ExecuteAsync( string input, CancellationToken cancellationToken default) { // Step 1: 让 IntentClassifierAgent 分析用户意图 var intentResult await _intentAgent.InvokeAsync(input, cancellationToken); var intent intentResult.Output.ToString(); // e.g., BillingIssue, FeatureRequest // Step 2: 根据意图动态选择后续 Agent string knowledgeQuery; switch (intent) { case BillingIssue: knowledgeQuery $How to dispute a charge on invoice {ExtractInvoiceNumber(input)}?; break; case FeatureRequest: knowledgeQuery $What is the roadmap for {ExtractFeatureName(input)}?; break; default: knowledgeQuery input; break; } // Step 3: 并行查询知识库和生成初步回复 var kbTask _kbAgent.InvokeAsync(knowledgeQuery, cancellationToken); var responseTask _responseAgent.InvokeAsync(input, cancellationToken); await Task.WhenAll(kbTask, responseTask); // Step 4: 聚合结果生成最终响应 var finalResponse await _responseAgent.InvokeAsync( $User query: {input}. Knowledge context: {kbTask.Result.Output}. $Preliminary response: {responseTask.Result.Output}. $Generate a final, professional, and empathetic response., cancellationToken); return new OrchestrationResult(finalResponse.Output.ToString()); } }这个例子展示了 MAF Orchestrator 的强大之处显式流程控制整个客服流程被清晰地拆解为Analyze - Route - Query Generate - Synthesize四个步骤。每一步的输入、输出、依赖关系都一目了然。这比在 SK 中用一个巨大的 Prompt 来描述整个流程要可靠、可维护得多。动态路由能力switch (intent)语句实现了基于业务逻辑的动态路由。这不再是 Prompt 工程的“黑盒猜测”而是确定性的、可测试的代码逻辑。实操心得我建议将ExtractInvoiceNumber和ExtractFeatureName这样的解析逻辑封装成独立的UtilityService并在 Orchestrator 的构造函数中注入。这样Orchestrator 本身保持纯净只关注流程编排而具体的业务规则则交由专门的服务去处理。并行与聚合Task.WhenAll的使用体现了 MAF 对现代异步编程的原生支持。它允许你充分利用硬件资源让知识库查询和初步回复生成这两个 IO 密集型操作同时进行显著缩短了端到端的响应延迟。注意事项在await Task.WhenAll之后务必检查每个 Task 的IsFaulted属性。如果kbTask失败了你不应该直接将kbTask.Result传递给ResponseGeneratorAgent而应该捕获异常并生成一个友好的降级响应比如“抱歉我暂时无法访问知识库请稍后再试”。MAF 的OrchestrationResult类型本身就包含了IsSuccess和Error属性你应该善用它们。3.3 Runtime 的配置让 Agent 在生产环境中稳健运行MAF 的 Runtime 层是其“超越”SK 的关键。要让它在生产环境中发挥威力正确的配置至关重要。以下是一个典型的Program.cs配置片段var builder WebApplication.CreateBuilder(args); // 1. 注册 MAF 的核心服务 builder.Services .AddAgents() // 注册所有 Agent 和 Orchestrator .AddChatClient(openai, configure configure .WithModelId(gpt-4-turbo) .WithApiKey(builder.Configuration[OpenAI:ApiKey])) .AddMemoryStore(redis, configure configure .WithConnectionString(builder.Configuration[Redis:ConnectionString])) .AddObservability(); // 启用 OpenTelemetry // 2. 配置 Agent 的全局行为 builder.Services.ConfigureAgentOptions(options { options.DefaultTimeout TimeSpan.FromSeconds(30); // 全局超时 options.MaxRetries 3; // 全局最大重试次数 options.RetryDelay TimeSpan.FromMilliseconds(500); // 重试间隔 }); // 3. 配置可观测性 builder.Services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder { tracerProviderBuilder .AddSource(Microsoft.Extensions.AI) // MAF 的 trace source .AddAzureMonitorTraceExporter(); // 发送到 Azure Monitor }); var app builder.Build(); // 4. 添加 MAF 的中间件可选用于 HTTP 端点 app.UseAgents(); // 这会自动为所有注册的 Agent 创建 /agents/{name} 的 REST API app.MapGet(/health, () Results.Ok()); app.Run();这段配置代码蕴含了丰富的实操经验AddAgents()是魔法入口这个扩展方法会自动扫描程序集中所有继承自AgentTState和Orchestrator的类型并将它们注册为 Scoped 服务。这意味着每次 HTTP 请求进来你都能获得一个全新的、隔离的 Agent 实例其State也是全新的。这天然解决了并发安全问题是 MAF 设计的精妙之处。实操心得我习惯在AddAgents()之后紧接着调用builder.Services.AddHostedServiceAgentWarmupService()。这个AgentWarmupService是一个后台服务它会在应用启动时主动调用每个 Agent 的InitializeAsync方法预热模型、加载缓存、建立连接。这能有效避免第一个用户请求时遭遇漫长的冷启动延迟。内存存储Memory Store的选择MAF 支持多种内存后端包括 In-Memory仅用于开发、Redis推荐用于生产、Cosmos DB用于需要强一致性和全球分布的场景。选择 Redis 不仅因为它速度快更因为它支持 Pub/Sub 模式。你可以利用这一点实现 Agent 之间的事件驱动通信。例如当DocumentReviewerAgent完成审批后它可以发布一个DocumentApprovedEvent由另一个NotificationAgent订阅并发送邮件。注意事项在生产环境中切勿使用AddInMemoryMemoryStore()。它会导致每个 Agent 实例都拥有自己独立的内存无法实现跨实例的状态共享这对于需要水平扩展的微服务架构是致命的。可观测性是生产环境的生命线AddObservability()和AddOpenTelemetry()的组合是 MAF 的“眼睛和耳朵”。它会自动为每一个 Agent 的OnPlanAsync、OnActAsync、OnThinkAsync方法生成 Trace Span。你可以在 Azure Monitor 的 Application Map 中直观地看到一个用户请求是如何被分解为多个 Agent 的调用以及每个调用的耗时、成功率、错误堆栈。实操心得我强烈建议在OrchestrationResult的构造函数中手动添加一个Attributes字典将关键的业务指标如document_id,customer_tier,sla_met注入到 Trace 中。这样你就可以在 Azure Monitor 中直接按这些业务维度进行筛选和聚合分析而不是只能看到一堆技术指标。4. 实操过程与核心环节实现一个端到端的“智能会议纪要”项目4.1 项目背景与需求拆解我们来落地一个真实的、有代表性的项目智能会议纪要生成系统。传统方案是会议结束后由助理手动整理录音、提炼要点、撰写纪要。我们的目标是在会议进行中实时生成一份结构化的、可编辑的会议纪要草稿并在会后自动分发给与会者。核心需求拆解如下实时性需要处理流式音频转录ASR的文本流而非一次性处理完整文本。结构化纪要需包含“会议基本信息”、“讨论要点”、“待办事项Action Items”、“决策项Decisions”四个核心模块。可编辑性生成的纪要应是 Markdown 格式方便与会者在会后直接修改。分发自动化会后系统需自动将最终版纪要通过邮件发送给所有与会者。4.2 系统架构与 Agent 角色划分基于 MAF我们设计了一个四层架构Input Layer输入层一个 ASP.NET Core Minimal API接收来自前端的 WebSocket 连接持续接收 ASR 引擎推送的文本片段{text: Hello, everyone...}。Orchestration Layer编排层MeetingSummaryOrchestrator作为总指挥负责接收文本流、分发给下游 Agent、聚合结果、管理会话状态。Agent Layer智能体层TranscriptAggregatorAgent: 负责将零散的文本片段聚合成连贯的会议发言记录并识别发言人。StructureExtractorAgent: 负责从聚合后的发言记录中抽取出“要点”、“待办事项”、“决策项”。MarkdownFormatterAgent: 负责将抽取的结构化数据渲染成美观的 Markdown 纪要。Output Layer输出层一个EmailService在Orchestrator完成最终纪要生成后被调用以发送邮件。4.3 核心代码实现从流式输入到结构化输出步骤 1定义会议状态Statepublic class MeetingSummaryState { public string MeetingId { get; set; } Guid.NewGuid().ToString(); public string Title { get; set; } Untitled Meeting; public DateTime StartTime { get; set; } DateTime.UtcNow; public Liststring Attendees { get; set; } new(); // 流式聚合的原始发言记录 public ListTranscriptSegment TranscriptSegments { get; set; } new(); // 结构化抽取的结果 public Liststring KeyPoints { get; set; } new(); public ListActionItem ActionItems { get; set; } new(); public Liststring Decisions { get; set; } new(); // 最终生成的 Markdown public string FinalMarkdown { get; set; } string.Empty; } public class TranscriptSegment { public string Speaker { get; set; } Unknown; public string Text { get; set; } string.Empty; public DateTime Timestamp { get; set; } DateTime.UtcNow; } public class ActionItem { public string Description { get; set; } string.Empty; public string Owner { get; set; } string.Empty; public DateTime DueDate { get; set; } DateTime.UtcNow.AddDays(7); }步骤 2实现TranscriptAggregatorAgentpublic class TranscriptAggregatorAgent : AgentMeetingSummaryState { private readonly ISpeakerDiarizationService _diarizationService; public TranscriptAggregatorAgent( IChatClient chatClient, ISpeakerDiarizationService diarizationService, ILoggerTranscriptAggregatorAgent logger) : base(chatClient, logger) { _diarizationService diarizationService; } protected override async TaskPlan OnPlanAsync( Plan plan, CancellationToken cancellationToken default) { // 如果是第一次调用初始化会议基本信息 if (!State.TranscriptSegments.Any()) { State.Title New Meeting; State.Attendees new Liststring { You, John, Sarah }; } // 基于最新的文本片段决定是否需要进行发言者识别 var latestText plan.Input; if (latestText.Length 50 !State.TranscriptSegments.Any(s s.Text.Contains(latestText))) { // 调用外部服务进行说话人分离 var speakers await _diarizationService.IdentifySpeakersAsync( latestText, cancellationToken); State.TranscriptSegments.Add(new TranscriptSegment { Speaker speakers.FirstOrDefault() ?? Unknown, Text latestText, Timestamp DateTime.UtcNow }); } return plan; } }步骤 3实现MeetingSummaryOrchestratorpublic class MeetingSummaryOrchestrator : Orchestrator { private readonly TranscriptAggregatorAgent _aggregator; private readonly StructureExtractorAgent _extractor; private readonly MarkdownFormatterAgent _formatter; private readonly EmailService _emailService; public MeetingSummaryOrchestrator( TranscriptAggregatorAgent aggregator, StructureExtractorAgent extractor, MarkdownFormatterAgent formatter, EmailService emailService) { _aggregator aggregator; _extractor extractor; _formatter formatter; _emailService emailService; } public override async TaskOrchestrationResult ExecuteAsync( string input, CancellationToken cancellationToken default) { // Step 1: 聚合文本流 await _aggregator.InvokeAsync(input, cancellationToken); // Step 2: 当累积到一定长度例如 200 字触发结构化抽取 if (_aggregator.State.TranscriptSegments.Count 5) { var fullTranscript string.Join(\n, _aggregator.State.TranscriptSegments.Select(s ${s.Speaker}: {s.Text})); var extractionResult await _extractor.InvokeAsync(fullTranscript, cancellationToken); // 更新 State _aggregator.State.KeyPoints extractionResult.KeyPoints; _aggregator.State.ActionItems extractionResult.ActionItems; _aggregator.State.Decisions extractionResult.Decisions; } // Step 3: 生成 Markdown var markdownResult await _formatter.InvokeAsync( JsonSerializer.Serialize(_aggregator.State), cancellationToken); _aggregator.State.FinalMarkdown markdownResult.Output.ToString(); // Step 4: 如果会议结束由外部信号触发发送邮件 if (input.Contains([END_OF_MEETING])) { await _emailService.SendSummaryEmailAsync( _aggregator.State.MeetingId, _aggregator.State.Attendees, _aggregator.State.FinalMarkdown, cancellationToken); } return new OrchestrationResult(_aggregator.State.FinalMarkdown); } }步骤 4WebSocket API 的实现app.Map(/ws/meeting, async (HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) { context.Response.StatusCode StatusCodes.Status400BadRequest; return; } using var webSocket await context.WebSockets.AcceptWebSocketAsync(); var orchestrator context.RequestServices.GetRequiredServiceMeetingSummaryOrchestrator(); try { var buffer new byte[1024]; while (webSocket.State WebSocketState.Open) { var result await webSocket.ReceiveAsync(new ArraySegmentbyte(buffer), CancellationToken.None); if (result.MessageType WebSocketMessageType.Text) { var text Encoding.UTF8.GetString(buffer, 0, result.Count); // 将接收到的文本交给 Orchestrator 处理 var orchestrationResult await orchestrator.ExecuteAsync(text); // 将生成的 Markdown 实时推送给前端 var responseBytes Encoding.UTF8.GetBytes(orchestrationResult.Output.ToString()); await webSocket.SendAsync( new ArraySegmentbyte(responseBytes), WebSocketMessageType.Text, true, CancellationToken.None); } } } catch (Exception ex) { // 记录错误 context.RequestServices.GetRequiredServiceILoggerProgram() .LogError(ex, WebSocket error); } });这个端到端的实现完美展现了 MAF 的核心优势流式处理、状态持久、模块解耦、可观测性强。每一个 Agent 都职责单一可以独立开发、测试和部署。Orchestrator则像一个精密的瑞士钟表将所有齿轮Agent严丝合缝地咬合在一起共同驱动整个系统运转。5. 常见问题与排查技巧实录来自生产环境的第一手经验5.1 问题速查表高频故障与解决方案问题现象可能原因排查与解决技巧Agent 执行超时抛出OperationCanceledException1. LLM 响应慢2.AgentOptions.DefaultTimeout设置过短3.Orchestrator中的Task.WhenAll某个子任务卡死。排查首先查看 Azure Monitor 的 Trace定位是哪个 Agent 的OnPlanAsync或OnActAsync耗时最长。解决1. 为关键 Agent 单独设置更长的超时agent.WithOptions(new AgentOptions { DefaultTimeout TimeSpan.FromMinutes(2) })2. 在OnActAsync中为每个Capability的调用都加上CancellationToken并确保外部服务如 HTTP Client也尊重该 Token3. 使用Task.WhenAny替代Task.WhenAll对慢任务进行降级处理。State 状态丢失每次调用 Agent 都是全新状态1. 错误地使用了AddSingleton注册 Agent2.Orchestrator没有正确地将State传递给下游 Agent3.State类未标记[JsonObject]反序列化失败。排查在OnPlanAsync开头打日志Logger.LogInformation(Current State: {State}, State)观察State的MeetingId是否每次都变化。解决1.绝对禁止AddSingletonYourAgent必须使用AddScoped2. 确保Orchestrator在调用agent.InvokeAsync(...)时传入的是同一个State的引用3. 为State类添加[JsonObject(MemberSerialization.OptIn)]并为所有需要序列化的属性添加[JsonProperty]。Orchestrator 的 Trace 在 Azure Monitor 中看不到Plan和Act的详细 Span1.AddObservability()未被调用2.IChatClient的实现未正确集成 OpenTelemetry3

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑