type
Post
status
Published
date
Mar 20, 2026
slug
multi-agent-routing-sessions-spawn-retrospective
summary
多-agent 路由、sessions_spawn 派工链路、控制面分层与授权配置的真实修复复盘。
tags
category
AI
icon
password
摘要
这不是一篇“某个字段填错了所以改一下就好了”的故障记录,而是一次典型的多层控制面误判案例:表面上,系统里的 agent 已经注册、路由配置也看起来正确、前端还能正常拉起新会话;实际上,真正承担派工动作的 sessions_spawn 链路依然持续失败。更具迷惑性的是,日志里出现了 Gateway: agents.defaults.tools failed,让排障方向一度偏向 tools 配置;与此同时,又因为旧 session 与新 session 在行为上呈现出不一致,团队短时间内误以为这是“缓存未刷新”或“会话绑定没切干净”的问题。
最终确认,问题根因并不在 tools.agentToAgent.allow,它并不是这条派工链路的关键控制面;真正决定某个 agent 能否继续向下分派子 agent 的,是 agents.list.<agent>.subagents.allowAgents。换言之,路由是否能到达目标 agent,和目标 agent 是否被授权继续派工,是两个不同层面的控制问题。前者通了,不代表后者正确;后者错误时,前端表面看起来仍可能“像是通的”。
本文完整复盘这次修复过程,包括:问题现象、错误假设、日志误导、配置层级陷阱、后台验证与前台验证如何配合、为什么旧/新 session 会制造“绑定错觉”,以及升级与回归测试应如何设计。对工程师与架构师而言,这次案例的价值不只在于改对了一个字段,更在于识别多-agent 系统里“控制面分层失配”这类故障的排障方法论。
---
引言
多-agent 系统最容易让人产生一种错觉:只要能看到 agent 列表、能发起路由、能生成新 session,系统就已经“差不多是通的”。这在单层调用模型里有时成立,但在带有分工派发、会话代理、权限继承和子代理授权的体系里,这种判断经常是错的。
本次故障恰好落在这个误区中心。系统中存在一个 main,它负责接收上游请求,并通过内部路由把任务分配给下游 agent。下游 agent 中,有的只是执行端点;有的则还要继续进行 sessions_spawn,把任务拆给更细分的子 agent。问题正出在这里:
- 从 main 看,目标 agent 能被路由到;
- 从配置文件看,agent-to-agent 工具似乎已经打开;
- 从前台表现看,新会话有时还能创建出来;
- 但真正的派工链路,仍然失败。
如果只看表面,这类问题很容易被归结为“配置还没生效”“旧 session 没清掉”“Gateway 读了旧版本”“前端展示和后台状态不同步”。这些猜测并非完全不合理,但都不是这次问题的核心。
真正值得关注的是:在多-agent 架构中,一条业务链路常常跨越多个独立控制平面。你在 A 平面上看到的“允许”,并不等于 B 平面上的“允许”;而错误日志里提到的模块名,也未必就是导致失败的实际控制点。
因此,这次修复的价值并不只是“找到一个正确字段”,而是确认了一种更稳健的排障范式:
- 先拆清链路;
- 再区分控制面;
- 然后分别做后台验证与前台验证;
- 最后通过新旧 session 对比,识别哪些现象只是历史绑定残留,哪些才是真配置生效。
---
架构背景
1. 系统角色分层
为了匿名化说明,本文统一使用以下角色名称:
- main:入口 agent,负责接收请求、判断意图、路由派发
- agent1 ~ agent7:不同职责的下游 agent,其中部分 agent 还会继续调用 sessions_spawn 派发子任务
本次问题涉及的不是简单的“main 调 agentX”,而是更深一层的链路:
- 上游请求进入 main
- main 将任务路由给某个目标 agent
- 该目标 agent 在自己的上下文中再次执行派工
- 派工通过 sessions_spawn 创建或绑定新的子 session
- 子 session 对应的 agent 继续执行任务
因此,系统至少存在以下几层控制逻辑:
- 路由控制:某个 agent 能不能被选中、能不能被调用
- 工具控制:当前 agent 能不能使用某类工具
- 子代理控制:当前 agent 被允许派给哪些下游 agent
- 会话控制:spawn 出来的 session 绑定给谁、是否复用旧上下文、是否使用新的配置快照
而故障恰恰来自这些控制面的边界不清。
2. 为什么 sessions_spawn 容易制造错觉
sessions_spawn 与普通工具调用不同,它往往同时涉及三件事:
- 新建或绑定会话对象
- 选择目标 agent
- 把当前上下文、权限和执行环境一起传递下去
这意味着,一个“spawn 成功”的表象,并不能证明整条链路真正正确。可能出现的情况包括:
- session 创建成功,但目标 agent 不具备继续下派的权限
- session 绑定的是旧配置上下文,而不是当前修改后的 agent 定义
- 前台看起来切到了新 agent,实际后台仍在沿用旧的会话身份
- 路由命中了某 agent,但该 agent 的 subagents.allowAgents 未授权,导致二次派工失败
也正因为此,排障时如果只看“有没有新 session”“UI 上显示的 agent 名称对不对”,很容易得出错误结论。
3. 配置层级的潜在误导
本次故障涉及两类容易混淆的配置:
#### 3.1 工具允许项
例如:
- tools.agentToAgent.allow
这类字段从语义上看非常像“允许 agent 之间互相调度”,因此极具误导性。团队最早把它当成关键控制面,认为只要这里允许了,agent 就应该可以继续派工。
#### 3.2 agent 的子代理白名单
真正关键的,是:
- agents.list.<agent>.subagents.allowAgents
这个配置不是“工具是否存在”,而是“某个具体 agent 被授权把任务派给哪些 agent”。它的控制粒度更高,也更贴近 sessions_spawn 的实际语义。
如果没有这个字段,或字段内容不包含目标 agent,那么即使:
- 路由层面可达,
- agent-to-agent 工具表面已启用,
- 前端界面看起来能切换 session,
真正的派工仍然可能失败。
4. 为什么日志会带偏方向
故障期间出现了一个高频提示:
Gateway: agents.defaults.tools failed
从字面看,它像是在说“默认 tools 配置失败了”。这会自然引导工程师去怀疑:
- tools 段落格式不对;
- agentToAgent 的工具默认项没加载成功;
- Gateway 对 tools 做了 schema 校验但没通过。
但后续验证发现,这条信息本质上只是路径层级错误,即 Gateway 在读取 agents.defaults.tools 时发现该路径不符合预期或不存在。它并不直接等同于“agent-to-agent 派工失败的根因”。
换言之:
- 这条日志是真的;
- 但它不是这次派工失败的主因;
- 它属于“配置结构异常的伴随噪音”,而不是业务控制面的最终答案。
这类日志最危险的地方,不在于它错,而在于它“只说了部分真相”。
---
排障过程
第一阶段:从“表面配置正确”出发的错误自信
问题最早暴露时,现象非常典型:
- main 能看到多个 agent;
- 路由时目标 agent 可选;
- 某些前台操作会显示 session 已切换或新建;
- 但真正执行到多-agent 派工时,链路中断。
第一反应通常是检查几类显性配置:
- agent 是否注册成功;
- 路由配置是否包含目标 agent;
- tools 是否启用了 agent-to-agent 相关能力;
- Gateway 是否重载了最新配置。
这些检查的结果都“看起来没问题”。也正因为如此,团队最开始做了一个危险假设:既然表面配置是对的,那失败大概率是生效时机、缓存、旧 session 复用、或者前后端状态不一致。
这个假设听起来合理,但它把“配置看起来对”与“控制面真正对”混为一谈了。
第二阶段:误把 tools.agentToAgent.allow 当关键控制面
随着日志和配置文件被逐段核对,注意力逐渐集中到工具开关上。因为从命名直觉看,tools.agentToAgent.allow 很像是“允许 agent 相互调用”的总开关。
于是排障路径一度演变成:
- 检查这个字段是否存在;
- 检查它是否为允许状态;
- 怀疑 Gateway 是否只读取默认值而没有读取 agent 级别覆写;
- 进一步对照 agents.defaults.tools 与单 agent tools 定义的层级。
这个阶段的排障并非没有价值,它至少帮助确认了几个事实:
- tools 相关配置确实存在历史残留和层级混用问题;
- Gateway: agents.defaults.tools failed 确实说明某些路径写法不合法;
- 部分配置看似“被读到了”,但并不能解释为什么某些 agent 可以路由、却不能继续 spawn 子 agent。
真正的问题在于:团队把“工具是否打开”当成了“派工是否被授权”的同义词。事实上,这两者不是一回事。
可以把它理解为:
- tools.agentToAgent.allow 更像“系统里有没有这把刀”;
- agents.list.<agent>.subagents.allowAgents 才是“这个人被不被允许拿刀去做指定操作”。
前者存在,不等于后者授权。
第三阶段:被 Gateway: agents.defaults.tools failed 带偏
日志中的 Gateway: agents.defaults.tools failed 一度成为最强线索。因为它与“tools 配置”高度关联,团队自然将其视为阻断点。
但在进一步拆解后发现,这条日志的性质更接近:
- 某个配置路径层级写错;
- 或默认配置段写在了错误位置;
- Gateway 在解析默认 tools 配置时校验失败。
也就是说,它说明“有配置结构问题”,但不能推出“sessions_spawn 失败一定由它引起”。
这个阶段真正的突破,不是修掉了那条日志,而是意识到:
如果一个 agent 已经能够被路由命中,那说明至少在部分控制面上系统是通的。
如果它仍无法继续派工,那根因更可能位于“该 agent 的下游授权”而不是“全局 tools 总开关”。
这是排障方向第一次从“全局工具层”转向“agent 局部授权层”。
第四阶段:旧 session 与新 session 制造了“绑定错觉”
在修配置的过程中,另一个非常干扰判断的现象出现了:同样一组配置修改后,某些操作在旧 session 里仍然失败,而在某些新开的 session 里似乎又能走得更远;还有一些情况下,前台显示已经切换到了目标 agent,但后台的行为像是仍然沿用了旧身份。
这引发了几种常见猜测:
- 是不是 Gateway 没有完全重载;
- 是不是前端缓存了旧会话元数据;
- 是不是 session 复用时,agent 绑定信息没有更新;
- 是不是不同入口创建的 session 使用了不同配置快照。
这些猜测部分成立,但它们不是根因,而是观察层被历史会话污染后的典型噪声。
在多-agent 场景中,旧 session 与新 session 的最大区别不是“有没有 ID 变化”,而是:
- 是否在创建时读取了新配置;
- 是否继承了旧的 agent 身份或上下文;
- 是否沿用了旧的 spawn 授权状态;
- 前台展示名称是否真的等于后台执行身份。
如果没有把这些变量隔离开来,排障结论就会被“绑定错觉”污染:工程师会误以为某配置“有时生效,有时不生效”,但实际上比较的根本不是同一种运行对象。
第五阶段:从“路由是否可达”转向“该 agent 是否允许继续派工”
真正的转折点,是把问题拆成两个判断:
判断一:路由是否通
即 main 能否找到并把任务送到目标 agent。
这个层面如果是通的,说明:
- agent 注册没问题;
- 基础路由信息基本没问题;
- 至少不是“目标 agent 根本不存在”。
判断二:目标 agent 是否被授权继续派工
即目标 agent 在收到任务之后,能否通过 sessions_spawn 再次把任务分派给指定子 agent。
这一层才真正对应:
- agents.list.<agent>.subagents.allowAgents
当排障按这两个层面拆开后,很多之前彼此冲突的现象突然统一了:
- 为什么前台看起来能选中目标 agent?因为路由层是通的;
- 为什么实际派工还失败?因为子代理授权层没通;
- 为什么改 tools 有时似乎有点变化却不彻底?因为碰到的是伴随配置问题,不是根授权;
- 为什么新 session/旧 session 现象不同?因为它们读取或继承的上下文不一致。
到这里,根因已经基本浮出水面。
---
根因分析
1. 根因不是“agent-to-agent 工具没开”,而是“目标 agent 未被授权派给指定下游”
最终确认,真正阻断 sessions_spawn 派工链路的,不是 tools.agentToAgent.allow,而是:
agents.list.<agent>.subagents.allowAgents
更具体地说:
- 某个承担中间调度职责的 agent 已经能被 main 路由到;
- 但该 agent 的 subagents.allowAgents 中没有正确声明它可以派给哪些下游 agent;
- 因此,当它尝试执行 sessions_spawn 时,系统从授权面拒绝了进一步派工。
这解释了整个故障中最令人困惑的一点:为什么“看上去都通了”,链路还是断。
答案是: 因为“通”只通到了被路由命中的那一层,并没有通到该 agent 再往下派工的授权层。
2. 为什么 tools.agentToAgent.allow 会被误判为关键控制面
这个字段之所以有极强迷惑性,主要有三点原因。
第一,命名极像总开关
看到 agentToAgent,自然会想到“agent 调 agent 的能力是否启用”。而 allow 又强化了这种理解,让人直觉上把它看成最终许可位。
第二,它确实可能影响某些能力暴露
在某些实现中,tools 配置会决定某类调用接口是否对 agent 可见、可用、可枚举。因此修改它后,局部行为可能发生变化,这会进一步强化“找对方向了”的错觉。
第三,它处于更显眼的配置层
工程师通常会先看:
- 全局 defaults
- tools 配置
- 路由配置
而不是先从某个具体 agent 的 subagents 白名单入手。结果就是:越容易看到的字段,越容易被高估为“主控制面”。
但从架构上看,这两类配置是不同职责:
- tools.agentToAgent.allow:偏工具可用性或接口暴露层
- agents.list.<agent>.subagents.allowAgents:偏主体授权与下游可派发目标层
前者更像“能力存在”,后者才是“具体授权”。
3. Gateway: agents.defaults.tools failed 为什么只是路径层级错误
该日志在本次故障中确实出现过,但最终确认它只说明一件事:
- agents.defaults.tools 这个路径的配置结构存在层级问题或 schema 不匹配。
它带来的影响是:
- 配置校验报错或默认 tools 不生效;
- 可能造成部分能力默认项异常;
- 容易引发“问题出在 tools”的认知偏差。
但它不是这次 sessions_spawn 派工失败的关键阻断。因为即便修正了该路径层级问题,只要具体 agent 的 subagents.allowAgents 没有正确声明,下游派工仍会失败。
这是一个非常典型的多因子场景:
- 一个是真正阻断业务的根因;
- 一个是同时存在但不决定业务结果的配置错误;
- 二者时间上重叠,导致排障优先级被扰乱。
在工程实践中,这种情况极其常见。真正成熟的排障,不是“把所有错误都修掉再看运气”,而是识别哪个错误属于主控制面,哪个只是伴随噪音。
4. 旧/新 session 绑定错觉的本质
为什么旧 session 和新 session 的表现会让人误判配置生效状态?因为 session 不是纯展示对象,它往往隐含以下绑定信息:
- 创建时刻的 agent 配置快照
- 当前执行身份
- 历史上下文
- 可用工具集
- 子代理授权状态
因此,同样修改一份配置后:
- 旧 session 可能继续沿用旧上下文;
- 新 session 可能读取新配置;
- 前台显示的 agent 名称,并不必然等于后台真正执行的 agent 身份;
- 某些 spawn 结果看似成功,只是 session 被创建了,但后续授权仍不成立。
这就是“绑定错觉”的来源: 会话对象让系统看起来像已经切过去了,但控制面并没有同步完成同等程度的切换。
5. “路由通”与“口径正确”是两层问题
这次故障最重要的架构启示之一,是必须把以下两个问题分开看:
路由通不通
意思是,请求能不能被送到某个 agent。 这是寻址问题、可达性问题、注册问题。
口径对不对
意思是,被送到的这个 agent,是否拥有与其职责一致的权限、工具、子代理授权和执行边界。 这是授权问题、职责问题、控制面一致性问题。
在本次案例中:
- 路由是通的;
- 但口径是不对的。
系统把任务送到了一个“看起来该能继续派工”的 agent,但该 agent 并没有被正确授权去派给目标下游。于是从业务链路上看,它像是“半通不通”。
这类问题尤其危险,因为单一指标往往会报喜:
- “agent 找到了”
- “session 创建了”
- “前端切过去了”
- “tools 也开了”
但业务仍然失败。 原因就在于:这些指标只证明局部层面成立,不证明整条控制链一致成立。
---
配置修复
1. 修复目标
修复不是简单地“让错误消失”,而是让控制面与业务链路重新对齐。具体目标包括:
- 清理错误的配置层级,消除误导性日志;
- 明确区分 tools 相关配置与 subagents 授权配置;
- 在具体承担派工职责的 agent 上,补齐 subagents.allowAgents;
- 确保新 session 读取到修正后的配置;
- 用后台验证和前台验证双重确认链路闭环。
2. 纠正错误层级:Gateway: agents.defaults.tools failed
这一步的意义主要是“去噪”和“避免二次误判”。 需要做的是:
- 把写错层级的 agents.defaults.tools 调整到 Gateway 实际支持的正确结构;
- 确保 defaults 段与具体 agent 覆写段之间没有混杂;
- 避免把默认工具项写到 schema 不接受的位置。
这一修复本身不等同于派工根因修复,但它有两个重要价值:
- 让日志恢复可信度;
- 防止后续排障继续被错误路径带偏。
3. 修复关键授权面:agents.list.<agent>.subagents.allowAgents
真正决定派工能否成功的修复,在于为承担中间调度职责的 agent 明确声明其允许派发的下游 agent 列表。
核心思想不是“全放开”,而是按职责最小授权。也就是说:
- 哪个 agent 负责继续拆任务,就给它相应的下游白名单;
- 不承担派工职责的 agent,不应默认拥有广泛的 allowAgents;
- 白名单要与业务拓扑一致,而不是为了“先跑通”随意扩大。
这种修复方式有两个好处:
- 真正解决当前链路失败;
- 把多-agent 系统的权限边界显式化,降低后续漂移风险。
4. 为什么不能只修 defaults,而必须修到具体 agent
在很多系统里,工程师会习惯先修 defaults,希望“一处改动,全局生效”。但本次案例说明,这种思路在子代理授权上并不总是成立。
原因在于:
- 默认值只能表达“普遍约定”;
- 具体 agent 的派工权限属于职责级授权;
- 职责级授权必须落到具体 agent,而不能完全依赖泛化默认项。
如果把 subagents.allowAgents 的策略完全寄希望于 defaults,最终常见的问题是:
- 某些 agent 获得了不该有的派工权;
- 某些关键 agent 仍未拿到明确授权;
- 排障时不容易看出实际生效的是哪一层。
因此,本次修复强调的是: 能在具体 agent 上显式声明的派工权限,不要只靠模糊 defaults 推断。
5. 让配置修复与会话生命周期对齐
仅仅改配置并不够。如果系统中存在旧 session 复用、历史上下文继承、前台缓存展示等机制,那么修复后的验证必须建立在明确的会话策略上:
- 对比旧 session 与新 session 的行为;
- 优先以全新 session 验证新配置;
- 不把旧 session 的残留行为当成配置是否生效的唯一依据;
- 必要时清晰区分“配置修复未生效”与“旧会话未重新绑定”。
这不是额外复杂化,而是多-agent 系统的基本排障纪律。 否则很容易出现一种错误结论:明明配置已经修对了,却因为旧 session 还在沿用旧状态而被误判为“修复失败”。
---
验证设计
修复完成后,如果只看某一类验证结果,仍然可能误判。因此本次采用了“后台验证 + 前台验证”的双层设计。
1. 后台验证:验证控制面是否真的被修正
后台验证关注的是事实层,而不是展示层。要回答的问题包括:
1.1 目标 agent 是否具备正确的 subagents.allowAgents
这是最关键的一步。验证点不是“配置文件里写了什么”,而是“运行中的系统实际读取并应用了什么”。
关注以下问题:
- 目标 agent 的授权白名单中是否包含预期下游 agent;
- 是否存在 defaults 覆盖或 agent 局部覆写冲突;
- Gateway 读取后的配置树是否与预期一致。
1.2 sessions_spawn 是否从授权面被放行
需要验证在实际调用路径上:
- spawn 请求是否到达授权判定;
- 判定时引用的是当前 agent 的 subagents.allowAgents;
- 放行后的目标 agent 是否与预期一致。
1.3 错误日志是否回落到合理水平
修复后应确认:
- Gateway: agents.defaults.tools failed 这类由路径层级错误引发的日志已消失或被消除;
- 若仍有错误日志,它们是否与当前业务失败直接相关;
- 日志是否从“结构错误噪音”回到“业务控制面真实反馈”。
后台验证的意义在于,先确认系统控制面已经正确,不要把所有希望寄托在 UI 表现上。
2. 前台验证:验证业务链路是否闭环
前台验证关注的是最终用户感知与真实业务结果。核心问题包括:
2.1 从 main 发起路由,能否稳定命中目标 agent
这里验证的是“路由通”。 如果这一层不通,说明问题还停留在更上游。
2.2 目标 agent 收到任务后,能否继续完成 sessions_spawn
这里验证的是“授权与派工链路通”。 这是本次修复的核心指标。
2.3 spawn 出来的 session 是否绑定到正确下游 agent
这里要特别防止“UI 上看起来切过去了,但后台并不是那个 agent”的情况。 验证方式应尽量基于:
- 明确的 session 元数据;
- 后续执行行为;
- 下游 agent 的可识别响应特征。
2.4 同一流程在新 session 中是否稳定复现为成功
必须以新 session 为主进行验证,避免旧 session 残留掺杂判断。 如果新 session 稳定成功,而旧 session 偶发异常,应优先把问题归因到历史绑定,而不是立即否定配置修复。
3. 为什么必须“后台验证 + 前台验证”同时成立
只做后台验证,容易出现的问题是:
- 配置看似正确,但业务入口仍未走到预期链路;
- session 绑定或前端触发方式仍有问题;
- 工程师误以为“配置正确 = 修复完成”。
只做前台验证,容易出现的问题是:
- 某次成功只是偶然命中了旧缓存或特殊路径;
- UI 展示掩盖了真实控制面状态;
- 日志与配置结构问题继续潜伏,未来仍可能回归。
因此,只有当以下两件事同时成立,才能认为修复真正完成:
- 后台控制面已按预期变更;
- 前台业务链路已稳定闭环。
4. 新旧 session 对比验证的正确姿势
本次案例中,新旧 session 是重要变量,但不能被当成“神秘因素”。正确做法是:
- 明确标记哪些验证发生在旧 session;
- 明确标记哪些验证发生在新 session;
- 不用“旧的失败、新的成功”直接推出“系统仍然不稳定”;
- 先判断是否存在会话绑定差异,再决定是否继续追查。
换句话说, 新旧 session 对比是定位手段,不是结论本身。
---
经验总结
1. 表面配置正确,不等于控制面正确
这次故障的第一课,是不要轻信“看起来都对了”。
- agent 能看到,不代表它具备完整职责;
- route 能命中,不代表下游可继续派工;
- session 能创建,不代表绑定和授权都正确;
- tools 开了,不代表主体权限已经到位。
在多-agent 架构里,“表面正确”尤其危险,因为系统总能给出一些局部成功信号,诱导人过早下结论。
2. 命名相似的配置,可能属于完全不同的控制面
tools.agentToAgent.allow 与 agents.list.<agent>.subagents.allowAgents 就是典型例子。
前者看起来像“总许可”,后者看起来像“局部细节”;但真正决定本次业务链路成败的,是后者。
这说明一个重要原则:
不要根据字段名的直觉语义,推断它在架构中的真实控制层级。
必须沿着实际调用链路确认:谁在什么时候读取这个配置,用它来判定什么。
否则就会掉进“名字像、语义像、所以一定是它”的陷阱。
3. 日志里的真相,常常只有一半
Gateway: agents.defaults.tools failed 并不是假信息,它确实指出了一个配置结构问题;但它不是这次业务失败的主因。
这类日志最容易让团队误判,因为它满足三个条件:
- 看起来专业且具体;
- 与当前怀疑方向高度契合;
- 确实对应一个真实错误。
但“真实错误”不等于“主要根因”。 成熟的排障需要持续追问:这条错误,是主阻断,还是伴随噪音?
4. 路由与授权必须分开验证
“能到达”与“被允许继续执行”是两回事。
本次故障若只验证“main 能否命中目标 agent”,会得到一个乐观但错误的结论;若只验证“tools 是否启用”,也会得到一个似是而非的结论。真正有效的方法,是把链路拆成:
- 可达性验证
- 授权验证
- 会话绑定验证
- 业务闭环验证
这种拆法不只适用于本次案例,也适用于所有多级代理系统。
5. 旧/新 session 的差异,是架构信号,不是玄学问题
很多团队一看到“旧 session 不一样,新 session 又不一样”,就会觉得是缓存玄学或系统不稳定。其实它通常反映的是:
- 会话对象携带配置快照;
- 会话生命周期与配置生效周期并不完全一致;
- 前台展示状态与后台执行状态存在解耦。
只要把 session 看成一等公民来设计验证流程,这类“错觉”并不神秘。
6. 升级与回归不能只测“能不能启动”,必须测“派工权限是否正确”
这次故障也暴露出一个常见测试缺口:升级后只验证了 agent 是否注册成功、界面是否可见、基本路由是否可用,却没有覆盖“中间 agent 继续派工”的链路。
对于多-agent 系统,这类回归测试至少应覆盖:
- agent 注册与枚举
- 基础路由
- 中间 agent 的 sessions_spawn
- 子代理白名单生效
- 新 session 与旧 session 的行为差异
- 错误日志是否出现结构性回归
否则,系统会在“看起来能用”的状态下潜伏真实缺陷。
---
升级与回归启示
1. 升级最容易破坏的,不是显性功能,而是层级关系
版本升级、配置重构或 schema 调整时,最容易出问题的往往不是“agent 完全消失”,而是这种更隐蔽的情况:
- 路由还在;
- agent 还能看到;
- UI 还能展示;
- 但某个更深层的授权或默认继承关系已经悄悄失效。
这意味着升级验证不能停留在“系统能起来、入口能点开、基础链路跑通”。 必须主动覆盖那些依赖配置层级和继承关系的链路。
2. 回归用例必须覆盖“中间调度 agent”
单层调用最容易测,真正容易漏的是“既是被调方、又是派工方”的中间 agent。因为它承担双重身份:
- 对上游,它是目标 agent;
- 对下游,它又是派工主体。
这类 agent 正是 subagents.allowAgents 最关键的作用点。回归测试若绕开它,就会错过最有代表性的失败模式。
3. 对日志的回归要关注“消除误导性错误”
回归不只是看有没有报错,也要看有没有会误导排障的噪音错误。 像 Gateway: agents.defaults.tools failed 这类结构层级报错,即使短期内不直接阻断业务,也应该被纳入回归门槛。因为它会显著抬高未来故障的定位成本。
4. 配置评审应以“控制面地图”代替“字段堆砌”
当系统越来越复杂时,单纯罗列字段意义已经不够。更好的办法是建立控制面地图,至少回答:
- 哪些字段控制路由;
- 哪些字段控制工具暴露;
- 哪些字段控制主体授权;
- 哪些字段影响 session 绑定与继承;
- 哪些默认值会被具体 agent 覆写。
有了这张地图,类似本次“误把 tools 当关键控制面”的问题会少很多。
---
附录 / 检查清单
A. 多-agent 路由与 sessions_spawn 排障检查清单
1. 先拆链路,不要一上来就改字段
- 是否已经明确:
- 请求从 main 到哪个 agent; - 该 agent 是否还要继续派工; - sessions_spawn 发生在哪一层; - 失败发生在路由、授权、还是会话绑定阶段。
2. 分开验证“路由通”与“口径正确”
- main 是否能命中目标 agent;
- 目标 agent 是否具备继续派工职责;
- 目标 agent 是否被允许派给指定下游;
- 不要把“可达”误判为“已授权”。
3. 不要误把 tools.agentToAgent.allow 当最终控制面
- 检查它是否只是工具暴露层;
- 确认真正控制 sessions_spawn 派工授权的是不是:
- agents.list.<agent>.subagents.allowAgents
4. 检查具体 agent 的子代理白名单
- 哪个 agent 在执行中间派工;
- 它的 subagents.allowAgents 是否存在;
- 是否包含预期的下游 agent;
- 是否被 defaults 或局部覆写冲掉。
5. 识别 Gateway: agents.defaults.tools failed 的性质
- 是否只是路径层级错误;
- 是否属于 schema/结构问题而非业务主阻断;
- 修掉它,但不要因此停止追查真正的授权面。
6. 后台验证必须做
- 运行时实际读取的配置是否正确;
- 目标 agent 的 subagents.allowAgents 是否生效;
- sessions_spawn 是否在授权判定处被放行;
- 错误日志是否回归正常。
7. 前台验证也必须做
- 从 main 发起的真实业务流程能否闭环;
- spawn 出来的 session 是否真的绑定到正确 agent;
- 后续执行行为是否符合该 agent 的职责;
- 是否能稳定复现成功,而非偶发通过。
8. 新旧 session 要分开看
- 旧 session 是否可能继承旧配置;
- 新 session 是否明确读取了新配置;
- 前台显示切换不等于后台身份已切换;
- 不要把历史会话噪音当成配置根因。
9. 回归测试要覆盖中间 agent
- 不只测 main -> agentX
- 还要测:
- main -> 中间 agent -> 下游 agent - 特别是 sessions_spawn 路径 - 以及 subagents.allowAgents 白名单效果
10. 升级后重点看控制面是否漂移
- defaults 与局部 agent 配置的层级是否变动;
- tools 段位置是否仍符合 schema;
- 子代理授权是否仍然按预期继承或覆写;
- 日志是否出现新的结构性报错。
---
B. 本次案例的最终结论
可以用一句话概括本次修复:
表面上像是 tools 配置问题,实际上是中间 agent 的子代理授权问题;
路由是通的,但派工口径不对;
Gateway: agents.defaults.tools failed 只是路径层级错误,不是业务主根因;
真正修复点在 agents.list.<agent>.subagents.allowAgents,而验证必须同时覆盖后台控制面与前台业务链路,并警惕旧/新 session 带来的绑定错觉。
这也是多-agent 系统排障最值得记住的一点: 不要被“看起来已经通了”骗过。真正需要验证的,从来不是某个局部功能是否出现,而是整条控制链是否一致成立。
- 作者:Leisurelywolf
- 链接:https://blog.869669.xyz//AI/multi-agent-routing-sessions-spawn-retrospective
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。


