第 9 章 · 01 fleet/coordinator 子代理编排


文档摘要

第 9 章 · 01 fleet/coordinator 子代理编排 本节摘要:本节精读 Reasonix 的多 agent 协作机制—— 和 。单个 agent 干所有事有上限(上下文窗口、注意力、并行度),Reasonix 的答案是子代理编排:主代理把任务拆分给多个子代理并行处理。 工具一次派发 2-64 个子代理任务,要求写任务预声明不重叠的 ,预检失败一个都不启动; 定义子代理配置(角色 prompt/工具白名单/模型/权限/profile)。 实现双模型协作——planner(规划者)只研究不执行,executor(执行者)只执行不规划,两者通过 Runner 接口对 CLI 透明。子代理会话隔离(各自独立 Session,缓存稳定),结果只把最终答案回收到父代理。

第 9 章 · 01 fleet/coordinator 子代理编排

本节摘要:本节精读 Reasonix 的多 agent 协作机制——internal/agent/fleet.gocoordinator.go。单个 agent 干所有事有上限(上下文窗口、注意力、并行度),Reasonix 的答案是子代理编排:主代理把任务拆分给多个子代理并行处理。fleet 工具一次派发 2-64 个子代理任务,要求写任务预声明不重叠的 write_paths,预检失败一个都不启动;SUBAGENT_PROFILES.md 定义子代理配置(角色 prompt/工具白名单/模型/权限/profile)。coordinator.go 实现双模型协作——planner(规划者)只研究不执行,executor(执行者)只执行不规划,两者通过 Runner 接口对 CLI 透明。子代理会话隔离(各自独立 Session,缓存稳定),结果只把最终答案回收到父代理。本节还归纳四种多 agent 协作编排模式(并行/串行/条件委派/后台)。

内容来源:原项目源码 internal/agent/fleet.gointernal/agent/coordinator.godocs/SUBAGENT_PROFILES.md,对照第 5 章 Session 与第 6 章 prefix-cache。

⚠️ 注意:子代理编排是 Reasonix 的高级特性,默认并发上限 agent.max_subagent_concurrency = 6、并行写者上限 agent.max_parallel_writers = 3(都可配 1-32)。本节聚焦编排契约与隔离机制,不逐函数精读调度器实现。

学习目标

阅读完本节,你应当能够:

  1. 解释为什么需要子代理编排——单 agent 的上下文/注意力/并行度都有上限。
  2. 说清 fleet 工具的契约——2-64 任务并行、write_paths 不重叠预检、独立失败默认。
  3. 描述 SUBAGENT_PROFILES 的配置维度(角色 prompt/工具/模型/effort/read-only/profile)。
  4. 讲清 coordinator 的双模型分工——planner 只研究不执行、executor 只执行不规划、Runner 接口对 CLI 透明。
  5. 区分四种多 agent 协作模式(并行 fleet / 串行 task / 条件委派 / 后台 job)。

一、为什么需要子代理编排

第 5 章讲的 Session + harness loop 是"一个 agent 干所有事"——一个会话、一个上下文、一个模型、串行地推理-工具-推理。这个模型在大多数任务上够用,但有三个上限:

  1. 上下文上限:复杂任务(重构大模块、跨多文件调查)需要的上下文可能撑爆窗口,即使第 6 章的 compact/snip 也救不回来。
  2. 注意力上限:一个 agent 同时考虑太多子任务,容易顾此失彼——比如"同时改 10 个文档"远不如"派 10 个子代理各改一个"质量高。
  3. 并行度上限:串行 agent 一次只能做一件事,而很多子任务(各文档改写、各模块审查)是相互独立的,可以并行。

Reasonix 的答案是子代理编排:主代理(父代理)把任务拆分,派给多个子代理并行或串行处理,每个子代理有独立的 Session(独立上下文、独立缓存),最后只把最终答案回收到父代理。父代理的上下文不会被子代理的中间步骤污染——这正是第 6 章 prefix-cache 友好哲学的延伸(把会话隔离做得更彻底)。

internal/agent 里有两个相关的编排原语:

  • fleet.go:fleet 工具,一次派发 2-64 个子代理任务并行执行。
  • coordinator.go:双模型协作(planner + executor),让"规划"和"执行"分开。

下面分别精读。

💡 契约要点:子代理编排是"会话隔离"的强化版。第 5 章 Session 隔离的是"用户和 agent",第 9 章隔离的是"父代理和子代理"。每次隔离都让 prefix cache 更稳定(各子代理各自的系统提示前缀不被对方污染),这是 Reasonix 把缓存友好贯彻到多 agent 层的设计。

二、fleet 工具:2-64 子代理并行

internal/agent/fleet.go 实现的 fleet 工具是多 agent 并行的核心。它的 Description 把契约说得非常清楚:

func (*FleetTool) Description() string { return "Dispatch 2–64 sub-agent tasks in parallel and return bounded previews plus stable Subagent references for full-result retrieval from completed persisted children with read_subagent_result. Each item may select a profile, model, effort, tools, write_paths, or read_only. Multiple writers must declare non-overlapping write_paths; omitted write_paths claims the whole workspace, so two or more writers without paths fail preflight before any task starts. Independent failure is the default: one failure does not cancel others. Background mode returns a fleet job id collectable with wait." }

这段 Description 浓缩了 fleet 的五条核心契约,逐条拆解。

2.1 契约一:2-64 并行

const ( fleetMinTasks = 2 fleetMaxTasks = 64 )

fleet 至少派发 2 个任务(单任务用 task 工具,不必动用 fleet),最多 64 个。64 是 session scheduler 的硬上限,实际并发受 agent.max_subagent_concurrency(默认 6)约束——64 个任务排队进 6 个并发槽。这个上限是为了防止单个回合派生出失控的子代理风暴。

2.2 契约二:write_paths 不重叠预检

这是 fleet 最精妙的安全设计。多个子代理并行同一份工作区,如果不约束,会出现竞态——两个代理同时改同一个文件,后写的覆盖先写的。fleet 的解法是预声明写路径:

每个写任务必须声明 write_paths(它能写哪些路径); 多个写任务的 write_paths 不能重叠; 省略 write_paths 等于声明"整个工作区都是我的",于是两个省略者必然冲突; 任何路径重叠 → 预检失败 → 一个任务都不启动(fail-closed)。

看 Schema 里的相关字段:

"write_paths": { "type": "array", "items": {"type": "string"}, "description": "Write targets for this item. Parallel writers must declare non-overlapping paths. Omitting write_paths claims the whole workspace; multiple whole-workspace claims (or any path overlap) fail preflight and start nothing." }

举例——并行改写两份文档:

fleet(tasks=[ {profile="doc-rewriter", prompt="rewrite docs/01.md", write_paths=["docs/01.md"]}, {profile="doc-rewriter", prompt="rewrite docs/02.md", write_paths=["docs/02.md"]} ])

两个任务的 write_paths 不重叠(docs/01.mddocs/02.md),预检通过,两个子代理并行跑。如果第三个任务也声明 write_paths=["docs/01.md"],和第一个冲突,整个 fleet 预检失败,一个都不启动——这是 fail-closed,不是"跳过冲突的跑剩下的",因为跳过会让模型以为任务成功了但实际少做了一个。

2.3 契约三:独立失败默认

Independent failure is the default: one failure does not cancel others.

fleet 里一个子代理失败(比如某个文档改写出错),不会取消其他子代理。这是工程上的合理选择——子任务相互独立,一个失败不该拖累其他。最终 fleet 汇总时,父代理看到每个子任务的独立状态(completed/failed/cancelled/skipped),可以决定怎么处理失败的那些。

状态枚举印证了这一点:

const ( fleetItemPending fleetItemStatus = "pending" fleetItemCompleted fleetItemStatus = "completed" fleetItemFailed fleetItemStatus = "failed" fleetItemCancelled fleetItemStatus = "cancelled" fleetItemSkipped fleetItemStatus = "skipped" )

2.4 契约四:profile/profile-aware

每个 fleet item 可以选一个 profile(子代理配置,见第三节),profile 决定子代理的角色 prompt、工具白名单、模型、effort 等。这让 fleet 不仅能并行,还能异构并行——比如同时派 reviewer(审查)、doc-rewriter(改文档)、test-writer(写测试)三种不同角色的子代理。

2.5 契约五:后台模式 + wait 收集

Background mode returns a fleet job id collectable with wait.

fleet 可以前台同步(等所有子代理完成)或后台异步(立刻返回 job id,父代理之后用 wait 工具收集结果)。后台模式适合长任务——派 10 个子代理各跑 5 分钟,父代理不必阻塞,可以继续干别的,需要结果时再 wait。

💡 契约要点:fleet 的五条契约——2-64 并行、write_paths 不重叠 fail-closed 预检、独立失败默认、profile-aware 异构并行、后台 job 模式——共同把"多代理并行写工作区"这个本来危险的操作变成了可预测的工程行为。其中 write_paths 预检是灵魂,它用声明式约束代替了运行时锁,让并行写在源头就不冲突。

三、SUBAGENT_PROFILES:子代理配置

docs/SUBAGENT_PROFILES.md 定义子代理配置。每个 profile 是一个可复用的、显式调用的 agent,专注于某一类工作(代码审查、调查、文档改写)。

3.1 profile 的本质:一个 Skill

Subagent profiles are reusable, explicitly invoked agents for focused work such as code review, investigation, or documentation. Each profile is a manual Skill with `runAs: subagent`: Reasonix starts an isolated child agent, gives it the profile prompt and task, and returns only its final answer to the parent.

关键信息——profile 复用 Skill 文件格式(SKILL.md),带 runAs: subagent 标记。这是第 7 章两层扩展体系(Plugin Manifest v1 / Skill)的延伸——子代理配置不发明新格式,直接复用 Skill。profile 存储在 .reasonix/skills/<name>/SKILL.md(项目级)或 Reasonix home 的 Skill 目录(全局级)。

3.2 profile 的配置维度

一个 profile 的 YAML 头:

--- name: reviewer description: Review changes for correctness and regressions color: orange invocation: manual runAs: subagent model: deepseek-pro effort: high read-only: true allowed-tools: [read_file, grep, bash]

配置维度归纳:

字段 作用
name profile 名,字母数字下划线短横点
description 描述
color 在 UI 里的颜色标识
invocation: manual 显式调用(不被自动触发)
runAs: subagent 关键标记——以子代理方式运行
model 模型覆盖(可用比父代理更强/弱的模型)
effort 推理强度(low/medium/high)
read-only 强制只读注册表(即使 profile 不是只读)
allowed-tools 工具白名单(和 runner 可用工具取交集)

注意三个安全相关字段——read-onlyallowed-toolswrite_paths(运行时传入)。它们共同构成子代理的最小权限——子代理默认只能干 profile 声明的事,父代理的完整权限不下放。这是零信任工具执行(第 9 章第 2 节)在子代理层的体现。

3.3 profile 的调用方式

profile 有三种调用入口:

  1. slash 命令(交互式):/reviewer review the current diff,在 CLI 或桌面聊天里。
  2. task/fleet 工具(模型调用):父代理在推理时调 task(profile="reviewer", ...) 或 fleet 的 item 里带 profile
  3. CLI 命令(脚本):reasonix subagent run reviewer "review and fix"reasonix subagent try reviewer(try 是只读预览)。

第三种特别有用——CI 脚本里 git diff | reasonix subagent run reviewer --max-steps 20 就能让 reviewer 子代理审查 PR diff,不需要交互。

3.4 关键约束:profile prompt 是子代理的完整系统提示

The profile body becomes the full child system prompt — no implicit concise default is stacked on top.

这条约束很关键——profile 的 prompt 体就是子代理的完整系统提示,Reasonix 不会在它前面再叠一个默认 prompt。这保证了子代理行为的可预测性:profile 作者写什么,子代理就遵循什么。同时也是缓存友好的——子代理的系统提示前缀由 profile 完全决定,profile 不变则前缀字节稳定,prefix cache 命中。

⚠️ 注意:profile 名可含字母/数字/_/-/.,但不能和已有的项目/全局/自定义/内置 Skill 重名。reasonix subagent create 会拒绝重名,防止 profile 名空间污染。

四、coordinator:双模型协作

internal/agent/coordinator.go 实现另一种编排——双模型协作(planner + executor),和 fleet 的"多子代理并行"是正交的特性。

4.1 Runner 接口:对 CLI 透明

coordinator 的设计起点是这个接口:

// Runner carries out one task turn. Both Agent (single model) and Coordinator // (two-model) satisfy it, so the CLI stays agnostic to which is in use. type Runner interface { Run(ctx context.Context, input string) error }

Agent(单模型,第 5 章)和 Coordinator(双模型,本节)都实现 Runner。CLI/Controller 只调 Runner.Run,不关心底层是单模型还是双模型——这是接口优先注册表哲学(第 1 章/第 3 章)在编排层的再一次落地。

4.2 planner 与 executor 的分工

DefaultPlannerPrompt 是 planner 的系统提示,把分工讲得非常明确:

You are the planner in a two-model coding agent. Given a task, produce a concise, ordered plan for the executor model to carry out. Use the read-only tools available to you when the task needs context ... Do not write full implementations or attempt side effects. ... Crucial: You only have research tools plus the stable use_capability proxy for authorized MCP. You do NOT have bash, execute, file writers, or other side-effect tools — those belong to the executor. Never question or dwell on the lack of execution tools; it is by design. Just plan what the executor should do with its tools.

分工是硬约束:

  • planner(规划者):只有只读工具(读文件、grep、use_capability),负责研究 + 输出计划。不能写文件、不能跑 bash、不能有任何副作用。
  • executor(执行者):有完整工具(写文件、bash 等),按 planner 给的计划执行。

这种分工让"思考"和"行动"在模型层分离——planner 专心想清楚做什么,executor 专心把它做出来。两个模型可以不同(planner 可以用推理更强的模型,executor 用执行更稳的模型),而且各自缓存稳定(planner 的会话和 executor 的会话隔离,各算各的 prefix cache)。

4.3 计划的三种结局

planner 输出的计划有三种可能的结局,通过 marker 标记:

  • [planner_requires_approval]:计划需要用户批准(对接第 9 章第 2 节 planmode),planner 要求停下来等人审。
  • <planner-ask>...</planner-ask>:计划需要用户做个决策(multiple choice),结构化提问。
  • [no_changes]:planner 研究后发现不需要执行(已经做完了,或是个纯问答),直接把答复交给用户,不启动 executor。

这三种 marker 让 planner 不仅是"输出计划",还能控制执行流——要不要审批、要不要问用户、要不要执行。这是双模型协作的完整契约。

4.4 planner 失败的优雅降级

const plannerFallbackNotice = "Planner failed; continuing this turn with the executor only."

planner 不是强依赖——如果 planner 出错(模型挂了、超预算),coordinator 不会让整个回合失败,而是降级成 executor-only(单模型模式,退回第 5 章的 Agent 行为)。这是工程鲁棒性的体现——双模型是增强,不是单点故障源。

五、四种多 agent 协作编排模式

把 fleet、task、coordinator、background 组合起来,Reasonix 支持四种典型的多 agent 协作模式。

5.1 模式一:并行(fleet)

多个独立子任务同时跑,fleet 是典型。适合"改 10 份文档"、"审查 5 个模块"这类相互独立的批量任务。

5.2 模式二:串行(task 链)

父代理调一次 task(单个子代理),拿到结果,再调下一次 task,形成串行链。适合"先调查 → 再设计 → 再实现"这类有依赖关系的任务流。父代理在每个子代理结束后拿到上下文,决定下一步派谁。

5.3 模式三:条件委派(planner + 子代理)

coordinator 的 planner 研究后,决定要不要派子代理、派哪个 profile。这是"智能路由"——不是父代理硬编码派谁,而是让 planner 根据任务性质动态选择。use_capability(action="list") 让 planner 看到可用的 MCP 能力,从而做更细的路由决策。

5.4 模式四:后台(job + wait)

fleet 的 run_in_background 模式,派出的子代理在后台跑,父代理继续干别的,需要结果时 wait。适合长任务——派子代理跑测试(可能几分钟),父代理同时写别的代码。

5.5 子代理与主代理的通信

无论哪种模式,子代理和主代理的通信都遵循同一契约——只回最终答案,不回中间过程:

The parent conversation retains the task and the child's final answer, not the child's full working context.

这是缓存友好的关键——子代理的中间步骤(几十次工具调用、几万 token 的推理)不进父代理上下文,父代理只看到一个紧凑的最终答案。父代理的 prefix cache 不被子代理的噪声污染。如果父代理确实需要子代理的中间细节,可以用 read_subagent_result 工具显式读取已持久化的子代理会话——这是"按需展开"而不是"默认全收"。

5.6 benchmark:subagent-delegation

Reasonix 的 benchmark 套件里有 subagent-delegation 任务,专门测多 agent 协作的性能。它跑一组需要委派的标准任务,衡量 fleet/task 的调度效率、隔离正确性、结果回收完整性。这是验证子代理编排正确性的回归保障。

💡 契约要点:四种模式覆盖了多 agent 协作的主要场景。共同的基础是会话隔离(各子代理独立 Session)+ 结果只回收最终答案(父代理上下文不被污染)+ profile-aware 异构(各子代理可有不同角色/工具/模型)。这套设计让 Reasonix 既能干小任务(单 agent),也能撑大任务(多 agent 编排),而且无论哪种规模都保持缓存友好。

本节要点回顾

  1. 为什么子代理编排:单 agent 有上下文/注意力/并行度三个上限;子代理编排把任务拆分,各子代理独立 Session 缓存稳定,只回最终答案——是第 6 章 prefix-cache 友好在多 agent 层的延伸。
  2. fleet 工具五契约:2-64 并行;write_paths 不重叠 fail-closed 预检(省略 write_paths 等于声明整个工作区,两个省略者必冲突);独立失败默认(一个失败不取消其他);profile-aware 异构并行;后台 job + wait 收集。
  3. SUBAGENT_PROFILES:profile 复用 Skill 文件格式带 runAs: subagent;配置维度(name/description/color/invocation/runAs/model/effort/read-only/allowed-tools);read-only + allowed-tools + write_paths 构成最小权限;三种调用(slash 命令 / task-fleet 工具 / CLI 脚本);profile prompt 是子代理完整系统提示不叠默认,保证缓存稳定。
  4. coordinator 双模型:Runner 接口让 Agent(单)和 Coordinator(双)对 CLI 透明;planner 只读只研究,executor 完整工具只执行;计划三结局([planner_requires_approval] / <planner-ask> / [no_changes]);planner 失败优雅降级为 executor-only。
  5. 四种编排模式:并行(fleet)、串行(task 链)、条件委派(planner 路由)、后台(job + wait);通信契约是只回最终答案不回中间过程,需要细节用 read_subagent_result 显式读;benchmark 套件含 subagent-delegation 任务回归。

下一节我们钻进 Reasonix 的安全体系——sandbox 沙箱隔离工具执行、guardian 守护审查 agent 行为、planmode 计划模式先规划后执行、permission 权限控制、hook 钩子拦截。这套多层防护让"让模型跑工具"这个本来危险的事变得可控。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U