# SUBAGENT-GATING-DESIGN — 为什么是"一个下拉", 而不是更繁或更简 > 2026-10-28 · v27(4.3.0)。回答三个问题: > ① 主会话点 /moa 与 subagent 门控是什么关系? ② 为什么不简化成单纯一个开关? > ③ 为什么不做"用语言指定哪个 subagent 开 MoA"? ——附取舍依据与已否决方案。 ## 一、先厘清最易混淆的点: 斜杠 vs 门控, 两套正交机制 ``` 主会话: /moaR /moaF → 只覆盖当轮"参考模式"(R裁剪/F镜像) —— 门控对主会话永远不生效 subagent: 路由到 moa-native → 先过门控(身份闸) —— 斜杠照常可用, 但同样先过门控 ``` - **斜杠 = 视野覆盖**(这一轮 refs 看什么), 作用于"怎么跑"; **斜杠不覆盖 cadence/阵容/agg**——这三样随会话常驻条目, 与门控正交; - **门控 = 会话身份闸**(这个会话配不配跑完整 MoA), 作用于"跑不跑"。 - 主会话点 `/moa` **不会**影响任何 subagent 的状态——两者是完全独立的会话, 各自的路由各自过闸。不存在"主会话跳两次、subagent 跟着跳"的联动; subagent 何时扇出只由它自己的 `subagent_policy` 与计数决定。 ## 二、为什么是三态下拉, 而不是单纯一个开关 **单纯开关(开/关)其实已经是本设计的主体**: 默认 `passthrough` 就是"关"(子会话直转 agg), `full` 就是"开"(同权全扇)。第三个态 `every_n` 回答的是开关回答不了的一问: > "子会话大多数时候不值得扇 5 路, 但偶尔的关键步(比如深层规划/最终校对)想要一次完整 MoA, 怎么办?" - 纯开关下只有"永远直转"或"每步都扇"两个极端——后者对一个跑 30 步的 subagent 意味着 150 路参考调用, 成本不可接受(这正是门控要解决的问题); - `every_n:5` = 每 5 次被拦截请求放一次完整扇出(第 1 次必扇), 其余直转——**用 1/5 的成本买到周期性的多视角注入**。 三态在 UI 上仍是**一个下拉**(不是三组配置), 心智负担与一个开关几乎相同: "默认关 / 每 N 次一次 / 全开"。若未来实测显示 every_n 无人使用, 可在下一版降级为双态——它是一个纯增量态, 删除不影响另外两态。 ## 三、为什么不做"用语言(自然语言)指定哪个 subagent 开 MoA" 设想形态: 主会话里说"给做审计的那个 subagent 开 MoA", 或派单时写"此 subagent 用 MoA"——即**按单次委派逐个指定**。分析后否决, 理由: 1. **指定面 vs 判定面错位**: 语言指定的粒度是"这一次委派", 但门控要回答的是"这个会话**每一步**要不要扇"。一个 30 步的 subagent 任务, 开了就是 30×N 路; 语言指定一次后仍要回答"每步都扇吗"——问题没有被消解, 只是被推迟; 2. **成本失控面扩大**: 语言指定把开关交给了**每一条派单的措辞**, 而 prompt 会被模板/技能/子代理层层复制。实践中会出现"模板里带了一句'请用 MoA', 于是每个继承模板的 subagent 都扇 5 路"的静默膨胀——比全局策略更难审计(策略在一个 config 里可查, 语言散落在全部会话历史里); 3. **不可观测**: 全局下拉的面板状态=单一事实来源(面板/config/trace 三处一致); 语言指定需要"解析每条派单意图"才能重建策略, 排障时无法回答"这个 subagent 为什么扇了 5 路"; 4. **已有等效且更稳的出口**: 真正想给某个 subagent 持久开 MoA 的场景, 正确做法是给它一个**常驻配方**(它的 agentPreset 选 moa-native 条目)——那是 DSH 原生的逐会话路由机制, 不需要本插件再造一套语言层。every_n 已覆盖"偶尔想要"的中间地带。 5. **机制性约束**: 派单语言到达时 subagent 会话尚未创建(会话头 origin/delegationDepth 在创建时才定), 按"派单语言"预置门控需要在 pre-step 之前解析文本意图——为一处可用配置覆盖的语义引入一个提示词解析层, 复杂度不成比例。 **结论**: 语言通道是"更麻烦"的方向——它把一个**策略问题**伪装成**表达问题**, 而策略问题的正解是可查的单一配置源 + 原生的逐会话路由出口。 ## 四、已考虑并否决的其他形态(备查) | 方案 | 否决理由 | |---|---| | 按 subagent 类型(角色/preset)分别配置 | 配置面膨胀(每角色一行); 与"全局一个事实源"冲突; agentPreset 常驻配方已是按角色的正解 | | depth 阈值(≥2 层才门控) | depth 与"是否值得扇"无实证相关; 引入第二个判据徒增解释成本 | | 每会话内存里逐个 tag(usage-ledger 式 ALS 打标) | 适用于记账(旁听), 不适用于门控(门控在会话首步就要判定, 无打标时机) | | 默认 full(保留旧行为, 想省的人自己关) | 默认值即产品立场: 大多数 subagent 任务(单点提取/机械中转)的 MoA 增益为负(延迟+成本), 默认省是对的 | ## 五、当前实现速查(v27) - 识别: 会话头 `origin:'subagent'` 或 `delegationDepth>0`(只读一次缓存; 判不出=按主会话, 门控只收紧子会话不误伤); - 计数: 每会话累计"被拦截请求数", `(n-1) % N === 0` 放行(第 1 次必扇); purpose 辅助调用(compaction/标题)不消耗计数; - 配置: `subagent_policy`: `"passthrough"`(缺省, 可省略) / `{"every_n": N}`(1-50 钳制) / `"full"`; 面板「subagent 门控」一个下拉, 全局字段不随配方; - 观测: debug 开启时每次门控判定落日志(`subagent 门控 every_n 第 n 次…`), 直转与放行均可见。 *设计裁决记录于 versions.md v2026.10.28 节; 实现锚点 host.mjs sessionOriginOf/gateBypass; 单测 scripts/verify-v27.mjs。*