1. 从一次真实的多智能体踩坑说起
如果你正在把 AI 代理接入已有的 .NET 或 Python 业务系统,大概率会遇到这样的场景:客服系统里需要一个代理判断用户意图,一个代理查订单,一个代理写回复,但它们各自为战,状态对不上、上下文丢失、错误没法回溯。我试过用纯 Prompt 拼接硬扛,结果一到并发就乱套。Microsoft Agent Framework(MAF)就是为解决这类多智能体编排问题而生的开源框架,它同时提供 .NET 和 Python 两套 API,定位是构建 AI 代理和多代理工作流的统一底座。简单说,MAF 能做什么?它把「代理」和「工作流」拆成两个核心构建块:Agent 负责用 LLM 推理、调工具、维护会话;Workflow 负责把多个 Agent 或工具按图结构串起来,支持顺序、并发、交接、群聊等编排模式。适合谁?适合需要把 AI 能力嵌入现有业务系统、又不想被单一模型厂商锁定的开发者。MAF 1.0 已经稳定并承诺长期支持,建立在 Microsoft.Extensions.AI 的 IChatClient 抽象之上,换模型提供商不用改业务代码。下面我从项目骨架开始,带你跑通一个最小可用的多智能体任务分发与结果回收示例。
2. TaoToken 前置:给 MAF 一个稳定的模型入口
MAF 本身不绑定模型,它通过 IChatClient 连接各种提供商。实际开发中,模型端点的稳定性和密钥管理往往是第一个卡点。我习惯用 TaoToken 作为统一的模型接入层,它兼容 OpenAI 风格的接口,MAF 的 OpenAI 连接器可以直接指向它,省去为每个提供商写适配代码的麻烦。
在动手写代码前,你需要先准备好三样东西:Base URL、API Key、Model ID。这三件套在任何 MAF 配置里都会反复出现,建议先记下来。
Base URL 使用https://taotoken.net/api,注意这个地址不带任何查询参数。API Key 需要到控制台创建,路径是 API Keys 页面。Model ID 则根据你实际要调用的模型填写,比如gpt-4o或claude-3-5-sonnet这类标识。
如果你还没创建密钥,可以按这个顺序操作:先访问控制台,进入 API Keys 管理页,新建一个密钥并复制保存。密钥只在创建时完整显示一次,丢了就得重建。创建完成后,建议先用模型对话页面做一次最简单的连通性验证,确认密钥和模型 ID 能正常工作,再进入 MAF 项目配置。这一步能帮你排除掉大部分「代码没问题但请求失败」的情况。
对于长期做编码和 Agent 开发的场景,Coding Plan 提供了更稳定的配额和调用策略,适合把 MAF 工作流跑在生产或持续集成环境里。而如果你只是想先验证模型是否可用,模型对话页面是最轻量的入口。
需要强调的是,MAF 的 IChatClient 抽象让你可以在不改业务代码的前提下切换提供商。这意味着你完全可以在开发阶段用一套配置,上线后换成另一套,只要 Base URL、Key、Model ID 三件套对应调整即可。这种解耦正是 MAF 相比直接调 SDK 的优势所在。
3. 可复制配置:.NET 与 Python 双栈项目骨架
这一节给出可以直接复制的配置片段。MAF 在 .NET 和 Python 下的项目结构略有不同,但核心概念一致:先建 ChatClient,再包成 Agent,最后用 WorkflowBuilder 串联。
3.1 .NET 项目骨架与 appsettings.json
先建一个控制台项目,添加 MAF 相关包。项目文件里需要引用 Microsoft.Extensions.AI 和 Agent Framework 的包。下面是一个典型的appsettings.json,把模型三件套放在配置里,避免硬编码:
{ "TaoToken": { "BaseUrl": "https://taotoken.net/api", "ApiKey": "sk-你的密钥", "ModelId": "gpt-4o" }, "Agent": { "Name": "RouterAgent", "Instructions": "你是一个任务路由代理,负责判断用户请求类型并分发给对应子代理。" } }注意 BaseUrl 不要带 UTM 参数,保持干净的 API 根路径。ApiKey 建议通过环境变量注入,配置文件里只留占位符,生产环境用dotnet user-secrets或环境变量覆盖。
3.2 Python 项目骨架与 settings.toml
Python 侧我用 TOML 管理配置,结构更清晰:
[taotoken] base_url = "https://taotoken.net/api" api_key = "sk-你的密钥" model_id = "gpt-4o" [agent.router] name = "RouterAgent" instructions = "你是一个任务路由代理,负责判断用户请求类型并分发给对应子代理。" [agent.order] name = "OrderAgent" instructions = "你负责查询订单状态,调用订单查询工具并返回结构化结果。"Python 项目建议用虚拟环境,依赖里包含agent-framework和openai兼容包。配置文件放在项目根目录,通过tomllib读取。
3.3 代理注册与工作流串联
.NET 下创建 Agent 的核心代码是这样:先根据配置构造 ChatClient,再用AsAIAgent()扩展方法包装成代理。工具注册用AIFunctionFactory.Create(),把普通 C# 方法转成 AIFunction,框架会自动生成 JSON Schema。
var chatClient = new ChatClientBuilder() .UseOpenAI(config["TaoToken:BaseUrl"], config["TaoToken:ApiKey"], config["TaoToken:ModelId"]) .Build(); var routerAgent = chatClient.AsAIAgent(new AgentOptions { Name = "RouterAgent", Instructions = "判断请求类型并路由" }); var orderTool = AIFunctionFactory.Create((string orderId) => QueryOrder(orderId)); var orderAgent = chatClient.AsAIAgent(new AgentOptions { Name = "OrderAgent", Instructions = "查询订单", Tools = [orderTool] });Python 侧逻辑对称,用Agent类和@ai_function装饰器注册工具。工作流用WorkflowBuilder构建,把 Executor 节点和 Edge 连起来,形成 DAG。顺序编排用AddEdge依次连接,并发编排用 Fan-Out/Fan-In 模式,一个节点输出连到多个下游,再汇聚到一个聚合节点。
这里的关键是 WorkflowContext 作为共享状态容器,各 Executor 通过它读写中间结果。Checkpoint 机制支持断点续跑,长流程任务中断后能从存档点恢复,不用从头再来。
4. 验证请求:端到端任务分发与结果回收
配置写完后,必须做一次端到端验证,确认任务能从路由代理分发到子代理,再把结果回收聚合。这一步能暴露配置错误、模型不兼容、工具签名不对等问题。
4.1 发起一次分发请求
在 .NET 里调用RunAsync()启动工作流,传入用户请求。工作流首先进入 RouterAgent,它根据 Instructions 判断请求类型。比如用户问「我的订单 12345 到哪了」,RouterAgent 识别为订单查询,通过 Edge 把任务转给 OrderAgent。
var workflow = new WorkflowBuilder() .AddExecutor(routerExecutor) .AddExecutor(orderExecutor) .AddEdge(routerExecutor, orderExecutor) .Build(); var result = await workflow.RunAsync("我的订单 12345 到哪了"); Console.WriteLine(result.Output);Python 侧调用方式类似,用await workflow.run(input)触发。执行过程中,OrderAgent 会调用注册的订单查询工具,拿到结构化数据后生成自然语言回复。
4.2 观察结果回收
成功的结果应该包含两部分:一是最终回复文本,二是工作流各节点的执行轨迹。MAF 集成 OpenTelemetry,可以把追踪数据导出到可观测性平台,看到每个 Executor 的耗时、输入输出、工具调用记录。
如果一切正常,你会看到 RouterAgent 输出路由决策,OrderAgent 输出订单状态,聚合节点把两者合并成最终回复。流式执行用RunStreamingAsync(),适合需要边生成边展示的场景。
验证时建议先用最简单的单代理工作流跑通,再加第二个代理,逐步增加复杂度。这样出问题时能快速定位是配置、工具还是编排逻辑的锅。
5. 本篇常见错排查:401、local proxy failed 与 OAuth
实际跑 MAF 时,报错集中在几个地方。下面按真实错误信息对照排查。
401 Unauthorized:最常见。先检查 API Key 是否正确、是否过期、有没有多余空格。再确认 Base URL 是否写成了带路径的形式,MAF 的 OpenAI 连接器期望的是 API 根地址。如果密钥没问题,检查请求头里的 Authorization 格式是否为Bearer sk-xxx。
local proxy failed / connection refused:这类错误通常出现在本地开发环境,说明请求根本没发出去。检查网络配置、防火墙、以及 Base URL 是否可达。如果你在容器里跑,确认容器网络能访问外部端点。注意不要配置任何非官方的网络转发工具,直接用标准 HTTPS 请求即可。
reading choices 相关报错:这通常意味着响应体结构和预期不符。可能是 Model ID 填错了,或者模型返回了非标准格式。先用模型对话页面单独验证该 Model ID 能否正常返回,再回到 MAF 里排查。如果用的是兼容接口,确认返回的 JSON 里有choices字段。
OAuth / 认证失败:如果你用的是需要 OAuth 的提供商,检查 token 获取流程。MAF 的 IChatClient 抽象层对认证方式有约定,OAuth 场景下需要确保 token 刷新逻辑正确。用 TaoToken 的 API Key 方式可以绕过这类复杂度,直接走 Bearer 认证。
工具调用签名不匹配:AIFunctionFactory 自动生成 JSON Schema,但如果参数类型复杂(比如嵌套对象),LLM 可能理解偏差。建议工具参数保持扁平,用基础类型,必要时加描述。
排查时记住一个原则:先隔离变量。用最小请求验证模型端点,再加 Agent,再加 Workflow。每加一层就验证一次,比一次性搭完再 debug 高效得多。
6. 把 MAF 接入你的业务系统
跑通最小示例后,下一步是把它接入真实业务。MAF 的 AgentSession 提供框架托管的对话状态,支持持久化和上下文恢复,这对多轮客服场景很关键。AIContextProvider 可以注入外部数据,比如把向量检索结果塞进代理上下文,实现 RAG 增强。
多代理编排模式里,Handoff 适合任务需要转交给更专业代理的场景,Group Chat 适合多个代理协作讨论,Magentic-One 适合动态任务协调。选择哪种取决于你的业务复杂度,不必一上来就用最复杂的模式。
跨运行时互操作方面,MAF 支持 A2A 协议做代理间通信,MCP 协议接入工具生态,AG-UI 协议对接前端。这意味着你的 .NET 代理可以和 Python 代理协同工作,也可以把代理暴露成 MCP 工具供其他系统调用。
如果你打算长期做 Agent 开发,建议把 Coding Plan 纳入考虑,它能提供更稳定的调用配额,适合持续集成和自动化测试。而日常调试和模型验证,模型对话页面足够轻量。接入文档里有各语言连接器的详细参数说明,遇到配置问题可以先查文档再排查。
最后给一个实用建议:把 Agent 的 Instructions、工具定义、工作流结构都纳入版本控制。MAF 支持声明式 Agent,用 YAML 定义配置,这样团队协作时变更可追溯,部署一致性也有保障。声明式定义和代码分离,是 MAF 相比手写编排逻辑的另一个优势。