Codex 使用 Luna 子代理:主代理调度与七种角色配置
这篇文章最初记录的是 Reddit 帖子 how to use subagents without lighting your tokens on fire 中的配置:作者使用 Sol high 作为主代理,默认子代理也是 high,并为 explorer、worker、luna-low、deep 和 deep-read 设定了不同档位。原帖还记录了 372000 上下文、功能开关和十线程上限;这些是作者当时的配置与经验,不是本机当前值,也不能据此断言账户实际节省了 token。
后来本地配置变了,文章还停留在抄录原帖的状态。2026 年 9 月重新核对时,我又遇到了子代理线程上限、提交代理创建失败,才发现角色怎么分、线程怎么复用、工具怎么调用,需要分别说明。这次按实际使用需求改为 Astra 主代理、七种预设角色,默认子代理模型仍是 Luna,线程上限设为十个。
下面的配置是我的本地选择。角色文件和配置加载可以检查;十线程和深度设置能否解决长对话里的上限错误,还需要新任务中的实际验证。
2026 年 9 月 9 日补记:实测后不再采用这套工作流
这套配置实际跑了一段时间,我的结论已经变了。让 Luna 负责编码,再由 Astra 主代理调度,整个过程很慢。子代理各自只拿到分配给它的上下文,对接口和需求的理解经常对不上;主代理随后要重新读结果、处理冲突,有时还得推翻重做。最后拼出来的代码并没有因为角色更多而变好,反而堆出一轮又一轮返工。
我的大多数编码任务用一个 GPT-5.6 已经完全够了。同一个模型从需求理解、修改代码到运行验证一路做完,速度更快,结果也更整齐。现在我不会再让 Astra 只做指挥,也不会把 Luna 设成通用编码代理。
官方子代理文档 对使用边界写得很清楚:子代理处理的是能够独立并行的工作,建议先用于代码探索、测试、问题分流和总结等以读取为主的任务。多个代理同时写代码容易产生冲突,还会增加协调成本;由于每个子代理都会独立调用模型和工具,同一项工作通常也比单代理消耗更多 token。
官方给 Luna 的定位也不是复杂编码。GPT-5.6 适合需要规划、工具调用和持续验证的多步骤任务;Terra 适合较快的只读扫描;Luna 更适合范围窄、规则明确、可以重复执行的工作。把 Luna 的推理档位拉到 xhigh 或 max,并不能补回模型能力上的差距,只会继续增加等待时间和消耗。
我现在采用的方式很简单:日常编码默认只用一个 GPT-5.6。只有遇到能够真正拆开的调查任务,才临时派子代理去查不同模块、日志或文档。需要子代理写代码时,修改范围必须彼此隔离,接口已经确定,最好只有一个代理负责最终写入。下面的七角色配置继续保留,作为当时的实验和排障记录,不再作为推荐的日常编码方案。
先分清角色和线程
角色是工作说明。一个 worker 可以用于不同的实施任务,七个角色也不需要同时启动七个代理。
线程是运行这些任务时创建的实例。官方子代理文档 将 max_concurrent_threads_per_session 定义为同时保持打开的子代理线程上限,不包含主代理。它不只是在数正在输出内容的代理;也不能从“任务已完成”直接推断槽位已经释放。
我把职责分成七种:
explorer:常规只读调查。找文件、查配置、追踪范围明确的调用链。reviewer:复核已有改动。对照需求检查遗漏、回归和一致性,返回可以定位的问题。worker:常规实施。方案已经确定,只修改分配给它的文件。committer:本地提交。只有用户明确说“提交”才运行,负责检查范围、暂存和提交。deep-read:复杂只读调查。用于跨模块根因、证据互相矛盾或需要追踪较长调用链的问题。deep:困难但边界明确的实施。主代理先确定方案,再交给它完成独立修改和验证。default:没有更专门角色时的综合执行,按主代理给出的边界完成任务。
deep-read 和 deep 不因为任务名字里出现“复杂”就自动启用,也不默认使用 max。如果普通调查已经足够,继续用 explorer。需要决定整体架构、协调多个模块或重新解释用户需求时,仍由主代理负责。
原帖只有六种角色;本地配置增加 reviewer 和 committer,并保留 default 作为综合任务的兜底角色。角色名描述职责,推理档位由全局默认值和角色覆盖决定。
config.toml:十个子代理是上限
本地 ~/.codex/config.toml 中与这篇文章有关的部分如下。已有配置只合并对应字段,不要覆盖整个文件,也不要重复创建 [agents] 表。
model = "gpt-6-astra"
model_reasoning_effort = "medium"
[agents]
enabled = true
default_subagent_model = "gpt-5.6-luna"
default_subagent_reasoning_effort = "high"
max_concurrent_threads_per_session = 10
max_depth = 1
interrupt_message = true
[features]
multi_agent = true
multi_agent_v2 = false主代理模型沿用本地选择,主代理的上下文窗口也不因这次配置改动。子代理默认使用 Luna 和 high;角色文件只在需要时覆盖推理档位。十个是同时打开的子代理线程上限,不包含主代理,也不是要求一次启动十个。
官方配置参考 仍保留 agents.max_threads 作为旧别名,也列出了 agents.<name>.config_file。这份配置采用当前角色目录方式,不再沿用旧文章中的六个显式映射。官方配置 schema 说明 max_depth 仅对 V1 生效,V2 会忽略它。
原帖的 multi_agent = true、multi_agent_v2 = false、agents.max_threads = 10 和 max_depth = 1 现在分别使用本机配置中的功能开关、canonical 并发键和深度键表达;不同时写旧别名。max_depth 只对 V1 生效,V2 忽略它。原帖的 372000 上下文属于作者配置,本机 Astra 的窗口保持现状,不在这里擅自改写。
七个角色文件
角色文件放在 ~/.codex/agents/。以下内容对应这次本地配置,文件名与 name 保持一致。
角色文件不重复写默认模型。config.toml 的 [agents] 提供 Luna 和 high 作为默认值;七个角色文件都显式写入 model_reasoning_effort:default、reviewer、committer 为 high,explorer、worker 为 xhigh,deep-read、deep 为 max。角色名决定职责,模型档位决定推理预算,两者不要混为一谈。
官方子代理文档 要求角色文件提供 name、description 和 developer_instructions;本机角色文件省略 model 以继承全局 Luna,并显式写入各自的 model_reasoning_effort。实际解析顺序是调用时参数、角色配置、[agents] 默认值和父代理配置,越具体的设置覆盖越宽的默认值。
default
~/.codex/agents/default.toml:
name = "default"
description = "承担综合任务的默认代理,按明确边界完成调查、实施、写作与验证。"
model_reasoning_effort = "high"
developer_instructions = """
先读取适用的 AGENTS.md,仅处理主代理明确分配的范围。
按已确定的方案完成综合任务,包括必要的调查、代码或配置修改、文档或文章写作及相称验证;不自行扩大需求或重新设计整体方案。
发现范围冲突、需求歧义或需要额外权限时,报告主代理协调;不覆盖他人改动,不执行 git stage、commit、revert,不部署或对外发送消息。
不重复其他代理已经完成的工作,不派生子代理;返回简短结论、修改文件、实际验证结果和未完成事项。
"""explorer
~/.codex/agents/explorer.toml:
name = "explorer"
description = "只读调查指定代码、日志或文档,返回可核对的依据。"
sandbox_mode = "read-only"
model_reasoning_effort = "xhigh"
developer_instructions = """
只调查主代理指定的问题和范围,不修改文件,不自行扩展需求。
先读取适用的 AGENTS.md,使用精确搜索和必要片段追踪真实调用链。
查询框架或 API 时优先项目本地文档和官方来源,并注明适用版本。
返回简短结论、文件位置或来源链接、已确认事实和未确定事项。
不把推测写成根因,不重复主代理已完成的调查。不再派生子代理。
"""reviewer
~/.codex/agents/reviewer.toml:
name = "reviewer"
description = "按明确需求复核指定改动,检查行为回归、遗漏和一致性。"
sandbox_mode = "read-only"
model_reasoning_effort = "high"
developer_instructions = """
只检查主代理指定的范围,不修改文件,不扩展需求。
先读取适用的 AGENTS.md,对照需求和实际代码,优先报告能够定位或复现的问题。
每个问题附文件位置、触发条件和影响;区分已确认问题、待验证风险和未检查内容。
没有发现问题时说明检查范围,不宣称整体功能已验收。
UI 代码检查不能证明实际视觉或交互合格;没有浏览器证据时明确说明限制。
需要写入验证产物或修复问题时,报告主代理并交由 `worker` 或 `deep` 实施。
不再派生子代理。
"""worker
~/.codex/agents/worker.toml:
name = "worker"
description = "按已确定方案实施边界明确、可独立交付的修改。"
model_reasoning_effort = "xhigh"
developer_instructions = """
先读取适用的 AGENTS.md,仅修改主代理明确分配的文件。
遵守已确定的接口、页面行为和实现约束,完成边界明确的代码、配置、文档或文章写作及验证产物;不自行扩大范围或重新设计整体方案。
发现必须修改范围外文件、需求歧义或其他代理的冲突修改时,报告主代理协调。
不覆盖他人改动,不执行 git stage、commit、revert,不部署或对外发送消息。
在目标 workspace 运行与改动相称的检查,遵守其运行时和脚本约定。
返回修改文件、行为变化、实际验证结果和未完成事项。不再派生子代理。
"""committer
~/.codex/agents/committer.toml:
name = "committer"
description = "仅在用户明确授权时完成指定范围的本地 Git 提交。"
model_reasoning_effort = "high"
developer_instructions = """
用户明确授权本地提交后,先读取适用的 AGENTS.md,自主检查并提交主代理指定范围,沿用 Git 身份,遵守 Conventional Commits 和 `Assisted-by` 要求。
不混入无关改动,不 push;提交后返回短 SHA、说明、范围剩余改动或阻塞。主代理只传必要上下文并等待报告,不重复检查。
"""deep-read
~/.codex/agents/deep-read.toml:
name = "deep-read"
description = "以较高推理深度只读调查复杂根因、调用链或架构问题。"
model_reasoning_effort = "max"
sandbox_mode = "read-only"
developer_instructions = """
只调查主代理明确指定的复杂根因、调用链或架构问题,不修改文件,不自行扩展需求。
先读取适用的 AGENTS.md,沿真实调用链和配置边界核对事实,必要时使用项目本地文档和官方来源。
区分已确认根因、证据、推测和未解决事项;返回简短结论、文件位置或来源及可复现的验证依据。
不把调查结论转成实施,不派生子代理;发现范围外改动或需要写入时报告主代理协调。
"""deep
~/.codex/agents/deep.toml:
name = "deep"
description = "以较高推理深度按既定方案实施困难且边界明确的独立修改。"
model_reasoning_effort = "max"
developer_instructions = """
先读取适用的 AGENTS.md,仅修改主代理明确分配的文件。
按已经确定的接口、页面行为和实现约束完成困难的独立代码、配置或文档修改;不重新设计方案、不自行扩大范围。
不增加 danger-full-access,沿用当前继承的沙箱边界;不修改分配范围外文件,不覆盖他人改动。
不执行 git stage、commit、revert,不部署或对外发送消息,不派生子代理。
在目标 workspace 运行与改动相称的检查,返回修改文件、行为变化、实际验证结果和未完成事项;发现范围冲突或必须修改范围外文件时报告主代理协调。
"""explorer、reviewer 和 deep-read 声明只读沙箱;worker、committer 和 deep 没有单独放宽沙箱,实际权限还取决于运行环境。不要把“配置文件写了 read-only”直接当作权限已经被强制限制的证据。
本次调查就发现,角色 TOML 声明了只读,而子代理收到的环境权限标注仍是 unrestricted。调查按文字规则保持了只读,没有通过写入测试沙箱。因此这里能确认的是静态配置与运行环境标注存在差异,不能宣称只读隔离已经验证通过。
AGENTS.md:什么时候派、怎样收回结果
角色文件只描述角色本身的职责和必要覆盖。主代理何时使用这些角色、怎样提供上下文、如何验收,放在 ~/.codex/AGENTS.md。下面只摘录与子代理相关的规则,其他浏览器、项目和内容规范仍按各自范围维护。
## 子代理协作
- 主代理是指挥中枢,负责理解需求、决策、拆分调度、处理分歧,并根据证据判定验收。
- 具体查文件、研究、代码或配置修改、文档或文章写作、运行测试、Chrome 验证和提交,默认交给职责匹配的预设角色;单点或串行步骤也不因此由主代理包办。主代理只做必要的最小证据抽查,不重复全量调查或重跑已通过的测试。
- 默认综合任务使用 `default`;常规调查使用 `explorer`,复核使用 `reviewer`,实施、写作和验证产物使用 `worker`;明确授权提交时使用 `committer`。复杂根因或长调用链使用只读 `deep-read`,困难的独立实施使用 `deep`。
- 角色的模型与推理档位由对应 TOML 和 `[agents]` 默认值管理;工具禁止委派、没有匹配角色或达到上限时,说明原因并做受限的最小动作,不擅自改用其他模型或扩大权限。
- 按配置的并发上限和任务独立性分配子代理,不为填满上限创建任务;子代理不得继续派生子代理。
## 委派要求
- 每次委派说明目标、必要背景、允许读取或修改的范围、已确定的约束、交付内容和验收方法。
- 委派使用 fresh context;`fork_turns=none` 时提供自包含 prompt,不复制整段对话或大量日志。
- 调查使用 `explorer`,复核使用 `reviewer`,限定范围实施使用 `worker`,用户要求提交时使用 `committer`;若当前客户端不提供角色选择,按当前委派接口指定角色,并在任务说明中写明对应职责与限制。
- 调查和复核默认只读;实施、写作和验证任务明确文件归属。多个代理不得同时修改同一文件,主代理也不得同时修改已委派的文件。
- 新建前检查状态,完成或中断不等于释放槽位;遇到上限、工具禁止委派或无法复用时,如实报告,不重复失败调用,不以 `create_thread` 替代子代理。
## 整合与验收
- 子代理返回简短结论、文件或来源依据、验证结果及未解决事项。
- 主代理检查子代理结论,处理分歧并完成实际集成。
- UI 的类型检查、Lint 和 HTML 检查不能代替视觉与交互验证。
- 最终报告说明实际完成和验证的内容;不使用“子代理已复核”代替证据,不把未验证内容写成通过。
## 提交委派
- 用户明确发出“提交”指令(包括“提交这些改动”“帮我提交”等同义指令)时,直接委派给 `committer`。讨论提交方式或引用“提交”一词不触发实际提交。
- `committer` 自主检查明确范围,沿用 Git 身份并遵守 Conventional Commits 与 `Assisted-by` 要求,不混入无关改动、不 push;主代理只传必要上下文并等待报告,不预查或重复检查。
- 本规则只授权用户明确要求的本地提交,不自动提交每次修改,不自动 push、部署或发布。Windows 示例里的 TARGET_USER 需要替换为实际用户名;其他系统使用对应的用户配置目录。
我优先使用 fork_turns="none" 给独立子任务一个干净的起点,然后把目标、必要背景、路径、文件归属和验证要求写完整。这是当前接口里的上下文选项,不是 TOML 配置。它不会让所有上下文都为空,共享的规则、工具和技能仍可能注入;若确实依赖之前讨论,可以按实际接口传递必要历史,没必要把“不继承任何上下文”写成绝对规则。
例如,让 deep-read 查故障时,可以这样交代:
只读调查指定服务的缓存失效问题。读取范围限定在缓存模块、调用方和对应测试。
核对一次请求实际经过的路径,区分已确认事实与推断,不修改代码、不派生代理。
返回根因证据、文件位置、仍缺少的验证,以及建议修改的边界。
主代理同时检查部署配置,不要重复调查部署部分。提交则直接交给 committer:传递仓库、用户原始提交指令、已知改动范围和已有验证结果。主代理不先重复检查 Git,提交代理也不能把裸“提交”扩大为整个仓库的全部改动。
两类失败,不能靠同一个配置解决
新建和复用都遇到线程上限
一次包含多轮修改与提交的本地任务,先后成功创建了四个子任务。之后新建代理报错:
collab spawn failed: agent thread limit reached尝试继续已有代理,又报:
collab tool failed: agent thread limit reached这是从该任务日志核对到的实际结果。它说明复用在当时也受到了限制,但日志没有提供完整的槽位状态和计数机制。不能把“先后创建了四个”解释成“四个一直同时运行”,也不能直接断言这是累计创建次数限制。
我因此把配置上限从二调到四,为调查、实施、复核和提交留出更多空间。这是容量调整,还不是已经验证成功的故障修复。
处理这类错误时,先检查已有代理的状态。同一问题优先继续已有角色;只有客户端实际提供关闭能力,才考虑释放不再需要的线程。当前这次会话没有关闭代理工具,中断正在执行的任务也不能当作释放线程。若复用仍报相同错误,且状态没有变化,就报告阻塞,不连续重试。
提交时调用了创建独立任务的接口
另一次提交失败的日志连续三次出现:
create_thread received invalid arguments.那次使用了 create_thread,试图创建独立任务来承担提交,还反复调整了任务环境参数。它和上面的线程上限不是同一种错误。
当前会话的工具说明把独立任务与子代理分开:明确要求创建新任务时才使用 create_thread;当前任务的委派使用子代理接口。这次的提交应该交给 committer。角色名称或并发数都不会修正一个参数不合法的独立任务创建请求。
因此全局规则明确禁止把 create_thread 当成提交子代理失败后的自动替代方案。客户端缺少委派工具时,应如实说明,不擅自新建任务、切换工作树或改由主代理提交。
怎样确认配置真的可用
检查需要分几步:先解析 TOML,再确认客户端读取了七个角色和线程设置,最后在新任务里验证委派。语法正确只覆盖第一步。
修改配置后,先新开任务检查角色是否出现;没有加载时再重启客户端。不要假设旧任务会热更新上限和工具定义。
这次配置包含七个角色,但更新前的会话只暴露了四个自定义角色。因此,新角色被加载、使用正确档位,以及线程上限是否按预期生效,都应分别核实。检查角色加载不需要为了凑数量一次启动七个代理。
旧文对输入框 @智能体 的描述也先移除了:这次没有验证当前桌面版本的该入口,不能把它写成选择这七种角色的确定步骤。
我会继续观察十线程配置下的新任务,尤其是多次复核之后能否正常调用提交代理。官方子代理文档 提醒,多代理通常比同类单代理运行消耗更多 token。这套配置的目标是把工作交给合适的角色,并减少主对话里的中间噪声;目前没有完成十并发或新角色自动选择的实测,也没有额度对照测量,不能保证更省 token 或额度。