1. 从 Codex auth.json 说起多智能体协作的真实痛点如果你正在用 Microsoft Agent Framework 做 Multi-Agent 编排大概率会遇到一个很具体的问题SubAgent 各自跑得好好的一旦串成工作流调用链就断在某个节点上日志里只留下一句401 Unauthorized或者local proxy failed。我最近在把一个 Codex 风格的 CLI 工具接进 MAF 的 SubAgent 体系时就反复卡在auth.json这个环节。Microsoft Agent Framework下称 MAF本身是一套用于编排多个 Agent 或处理组件执行流程的 Workflow 框架。它的核心思路是把每个处理单元抽象成 Executor用 Edge 把它们连起来再用 SuperStep 做批量调度。这套模型非常适合做 SubAgent 分工一个负责意图识别一个负责代码生成一个负责结果校验最后由一个汇总节点输出。但问题在于每个 SubAgent 背后往往要调用外部模型服务而模型服务的鉴权配置如果散落在各个 Agent 里维护起来就是灾难。Codex 的auth.json正好是这类配置的典型载体。它通常放在用户目录下的.codex/文件夹里记录 Base URL、API Key、默认模型 ID 这些信息。很多开发者第一次接触 MAF 的 SubAgent 时会直接把 Key 硬编码进 Executor结果就是本地能跑换台机器就 401或者某个 SubAgent 用了旧 Key整个工作流在第三个 SuperStep 才报错排查成本极高。这篇内容要解决的就是把这个配置收敛到auth.json一处让 MAF 的 SubAgent 和 Multi-Agent 协作链路都能稳定拿到鉴权信息。适合谁看如果你已经写过一两个 Executor想让多个 Agent 按工作流协作并且希望调用链可验证、可排查那接下来的步骤可以直接跟做。核心检索词就三个Microsoft Agent Framework、SubAgent、Multi-Agent全文围绕它们展开。我会先讲清楚 MAF 里 SubAgent 的注册与路由机制再给出可复制的auth.json配置片段然后跑一个最小任务集验证协作是否按预期返回。过程中遇到的 401、local proxy failed、reading choices 这些报错也会在排障章节逐一对照。2. TaoToken 前置把模型接入收敛到一处配置在 MAF 里做 Multi-Agent最怕的就是每个 SubAgent 各自维护一套模型接入参数。我的做法是所有 SubAgent 共享同一份auth.json里面只保留一个 Base URL、一个 Key、一个默认 Model ID。这样无论工作流里有多少个 Executor鉴权入口只有一个排查时也只需要看一个文件。这里用到的模型服务接入地址是 TaoToken。它的 API 入口是https://taotoken.net/api控制台和密钥管理在官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。你需要先在控制台创建一个 API Key然后把它写进auth.json。注意Key 只创建一次后续所有 SubAgent 都复用这一个不要每个 Agent 建一个否则 Multi-Agent 的调用链会变得难以追踪。为什么强调“前置”因为 MAF 的 Workflow 是声明式构建的Executor 在Build()之前就已经绑定了路由和依赖。如果你等到运行时报错才去补 Key工作流已经进入 SuperStep 调度改配置需要重新构建整个 Workflow 实例。所以正确顺序是先准备好auth.json再写 Executor最后Build()。具体来说auth.json放在~/.codex/auth.jsonWindows 是%USERPROFILE%\.codex\auth.json。这个路径是 Codex 风格工具的默认读取位置MAF 的 SubAgent 在初始化时也会去这里找。文件内容是一个 JSON 对象包含base_url、api_key、model三个核心字段。下面这段可以直接复制把sk-开头的部分换成你在控制台生成的 Key{ base_url: https://taotoken.net/api, api_key: sk-你的实际密钥, model: claude-sonnet-4-20250514, provider: taotoken }这里model字段填你实际要用的模型 ID。如果你不确定该填哪个可以先去模型对话页面试一下确认模型能正常返回再写进配置。provider字段是给 MAF 的 SubAgent 做路由判断用的保持taotoken即可。有一点要提醒auth.json里不要写多余字段比如proxy、endpoint之类的MAF 的配置解析器对未知字段比较敏感多余字段可能导致local proxy failed这类看起来和网络有关的报错实际是配置解析失败。我踩过的坑就是加了一个自定义字段结果排查了半天才发现是 JSON 结构问题。配置准备好之后先别急着写完整工作流。用一个最小的 Executor 验证auth.json能被正确读取再往下做 Multi-Agent 编排。这样能把“配置问题”和“编排问题”分开排障效率高很多。3. 可复制配置SubAgent 注册与 auth.json 读取片段这一节给出可以直接粘贴运行的配置和代码。目标是在 MAF 里注册两个 SubAgent一个负责生成一个负责校验它们共享同一份auth.json。先看目录结构建议这样组织project/ config/ auth.json agents/ generator_agent.cs validator_agent.cs workflow/ program.csauth.json就放在config/下内容沿用上一节的三个字段。接下来是读取配置的辅助类它负责把 JSON 反序列化成强类型对象供各个 SubAgent 使用using System.Text.Json; public sealed class AuthConfig { public string BaseUrl { get; init; } ; public string ApiKey { get; init; } ; public string Model { get; init; } ; public string Provider { get; init; } ; public static AuthConfig Load(string path) { var json File.ReadAllText(path); var options new JsonSerializerOptions { PropertyNameCaseInsensitive true }; return JsonSerializer.DeserializeAuthConfig(json, options) ?? throw new InvalidOperationException(auth.json 解析失败); } }注意PropertyNameCaseInsensitive true这样 JSON 里的base_url能映射到BaseUrl。如果你不加这一行反序列化会得到空字符串然后请求时就会 401但报错信息不会直接告诉你字段没映射上。然后是 SubAgent 的注册。MAF 里每个 Executor 需要实现ConfigureRoutes声明自己能处理哪些消息类型。下面这个GeneratorExecutor负责调用模型生成内容using Microsoft.AgentFramework; public sealed class GeneratorExecutor : Executor { private readonly AuthConfig _auth; public GeneratorExecutor(AuthConfig auth) { _auth auth; } protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) { return routeBuilder .AddHandlerGenerationRequest, GenerationResult(HandleAsync); } private async ValueTaskGenerationResult HandleAsync( GenerationRequest request, IWorkflowContext context, CancellationToken ct) { using var client new HttpClient(); client.DefaultRequestHeaders.Add(Authorization, $Bearer {_auth.ApiKey}); var payload new { model _auth.Model, messages new[] { new { role user, content request.Prompt } } }; var response await client.PostAsJsonAsync( ${_auth.BaseUrl}/v1/chat/completions, payload, ct); response.EnsureSuccessStatusCode(); var body await response.Content.ReadFromJsonAsyncJsonElement(ct); var text body.GetProperty(choices)[0] .GetProperty(message) .GetProperty(content) .GetString() ?? ; await context.SendMessageAsync( new GenerationResult { Text text }, validator, ct); return new GenerationResult { Text text }; } }这里有几个关键点。第一Authorization头用的是Bearer加_auth.ApiKey这是标准写法。第二请求路径是{BaseUrl}/v1/chat/completionsBaseUrl来自auth.json不要在这里再拼一次域名。第三生成完成后通过context.SendMessageAsync把结果发给validator节点这就是 Multi-Agent 协作的消息传递。ValidatorExecutor结构类似它接收GenerationResult做一次校验后再输出public sealed class ValidatorExecutor : Executor { protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) { return routeBuilder .AddHandlerGenerationResult, ValidationResult(HandleAsync); } private async ValueTaskValidationResult HandleAsync( GenerationResult input, IWorkflowContext context, CancellationToken ct) { var passed !string.IsNullOrWhiteSpace(input.Text) input.Text.Length 10; var result new ValidationResult { Passed passed, Reason passed ? 内容长度与格式校验通过 : 内容为空或过短 }; await context.YieldOutputAsync(result, ct); return result; } }最后是工作流的组装。用WorkflowBuilder把两个 Executor 连起来指定起点和输出节点var auth AuthConfig.Load(config/auth.json); var generator new GeneratorExecutor(auth); var validator new ValidatorExecutor(); var workflow new WorkflowBuilder(generator) .WithName(SubAgent 生成校验工作流) .AddEdge(generator, validator) .WithOutputFrom(validator) .Build();这段配置里AddEdge(generator, validator)建立了一条 Direct Edge消息从生成节点流向校验节点。WithOutputFrom(validator)指定校验节点的输出作为整个工作流的最终结果。到这里SubAgent 的注册和路由就完成了两个 Agent 共享同一份auth.json调用链是generator - validator - output。4. 验证请求用最小任务集检查 Multi-Agent 是否按预期返回配置写完之后不要直接上复杂任务。先用一个最小任务集验证协作链路是否按预期工作。所谓最小任务集就是输入一个简单 Prompt观察工作流是否依次经过 generator 和 validator并且最终输出符合预期。启动工作流的代码大致如下var run await workflow.RunAsync(new GenerationRequest { Prompt 用一句话说明什么是 SubAgent }); await foreach (var evt in run.OutgoingEvents) { Console.WriteLine($[事件] {evt.GetType().Name}); } var output await run.GetOutputAsyncValidationResult(); Console.WriteLine($校验结果: {output.Passed}, 原因: {output.Reason});运行后你期望看到的事件顺序是WorkflowStartedEvent、ExecutorInvokedEventgenerator、ExecutorCompletedEventgenerator、ExecutorInvokedEventvalidator、ExecutorCompletedEventvalidator、WorkflowOutputEvent。如果事件顺序不对比如 validator 没有被调用说明 Edge 没连上或者消息类型不匹配。验证成功的标志有三个。第一控制台没有 401 或 403说明auth.json里的 Key 和 Base URL 生效了。第二choices字段能被正确解析说明模型返回结构符合预期。第三ValidationResult.Passed为true说明校验节点确实收到了生成节点的输出。如果模型返回的内容为空先检查auth.json里的model字段是否拼写正确。模型 ID 写错时服务端通常返回 404 或 400而不是 401这一点容易混淆。另外choices数组为空也会导致GetProperty(choices)[0]抛异常报错信息里会出现reading choices字样这是模型没有正常返回的典型信号。再给一个检查清单按顺序排查检查项预期结果异常表现auth.json 路径文件存在且可读FileNotFoundExceptionapi_key 字段非空且以 sk- 开头401 Unauthorizedbase_url 字段指向 https://taotoken.net/apilocal proxy failedmodel 字段与控制台可用模型一致404 或 reading choicesEdge 连接generator 到 validator 有边validator 未被调用消息类型GenerationResult 匹配路由无匹配 handler这张表建议在第一次跑通前逐项过一遍。尤其是base_url如果误写成带/v1的地址再拼一次/v1/chat/completions就会变成/v1/v1/chat/completions服务端返回 404但报错信息可能被包装成local proxy failed看起来像网络问题实际是路径重复。验证通过之后你可以把 Prompt 换成更复杂的任务比如让 generator 生成一段代码validator 检查代码里是否包含特定关键字。只要最小任务集跑通后续扩展就是加 Executor 和 Edge 的事。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节把实际遇到的报错和原因对照清楚。MAF 的 SubAgent 在 Multi-Agent 协作时报错往往不会直接指向根因需要结合auth.json和调用链一起看。401 Unauthorized最常见。原因通常是api_key为空、过期或者Authorization头没带上。检查auth.json里的api_key是否以sk-开头以及代码里是否真的把_auth.ApiKey拼进了请求头。如果 Key 正确但仍然 401确认base_url没有多余空格。JSON 里字符串前后的空格不会被自动去除 https://taotoken.net/api 会导致请求发到错误地址。local proxy failed这个报错名字容易误导它不一定是网络代理问题。在 MAF 场景下多数是auth.json解析失败或字段缺失导致的。比如base_url写成了baseUrl而反序列化没有开启大小写不敏感结果BaseUrl为空请求发到空地址。解决办法是确认PropertyNameCaseInsensitive true并且 JSON 字段名和类属性名能对应上。另外auth.json里如果混入了注释标准 JSON 解析器会直接失败也会报这个错。reading choices这个报错出现在解析模型响应时通常是choices数组为空或结构不符。原因可能是model字段填了一个不存在的模型 ID服务端返回了错误结构也可能是请求体里messages格式不对。检查model是否和控制台里可用的模型 ID 完全一致注意大小写和日期后缀。请求体里messages必须是数组每个元素有role和content。OAuth 相关报错如果你在auth.json里保留了oauth或token字段MAF 的某些版本会优先走 OAuth 流程导致和 API Key 冲突。解决办法是删掉auth.json里除base_url、api_key、model、provider之外的字段。OAuth 和 API Key 不要混用选一种即可。还有一个容易忽略的点如果你用了 CC Switch 或 Cline MCP 这类工具它们可能会改写auth.json。确保这些工具没有在后台覆盖你的配置。检查方法是运行前先cat ~/.codex/auth.json确认内容运行后再看一次如果被改了说明有工具在写这个文件。排查顺序建议是先看auth.json内容再看请求头再看请求路径最后看响应结构。大部分问题在前两步就能定位。如果 401 和 local proxy failed 同时出现优先解决 401因为鉴权失败时请求可能根本没发出去后面的报错是连锁反应。6. 语义一致 CTA把 SubAgent 协作链路跑稳之后当你的最小任务集能稳定返回事件顺序符合预期ValidationResult.Passed为true说明 Microsoft Agent Framework 下的 SubAgent 注册、auth.json 读取、Multi-Agent 消息传递这条链路已经通了。接下来可以做的扩展包括增加第三个 Executor 做结果格式化或者用 FanOut Edge 把同一个请求分发给多个 SubAgent 并行处理再用 FanIn 汇聚结果。如果你在配置auth.json或排查 401 时需要对照密钥和接入参数可以去 API Keys 页面管理密钥接入文档里有完整的 Base URL 和请求示例。想先确认模型 ID 和返回结构用模型对话页面发一条测试消息最快。长期做编码类 Agent 或需要多轮协作的场景Coding Plan 更适合它把调用配额和模型路由都收敛好了不用每个 SubAgent 单独配。回到 MAF 本身SubAgent 和 Multi-Agent 的价值在于把复杂任务拆成可验证的小步骤。每个 Executor 只做一件事Edge 决定消息流向SuperStep 负责批量调度。只要auth.json这一层配置稳定上层的工作流编排就可以放心迭代。我自己的习惯是每加一个 SubAgent就先单独跑一次它的 Handler确认能拿到模型返回再连进 Workflow。这样出问题时能立刻判断是 Agent 本身的问题还是 Edge 或消息类型的问题。