oh-my-openagent Team Mode 实战指南:基于共享邮箱与任务清单的并行多 Agent 协作
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
Team Mode 是 oh-my-openagent(OmO)提供的并行多 Agent 协调机制,参照 Claude Code 的 Agent Teams 设计,通过共享邮箱、共享任务清单与可选的 tmux 可视化布局,让一个 Lead 会话同时调度多个专用成员会话。本文以 docs/guide/team-mode.md 为主体,结合 packages/team-core 与 packages/omo-opencode 的源码实现,完整覆盖从配置启用、团队定义、成员资格、12 个
team_*工具、生命周期到磁盘存储布局的全部细节。读完本文,你将能够独立搭建并调优一个受限并行、有界协调、可优雅关停的 Agent 团队。
什么是 Team Mode:默认关闭的并行协调层
Team Mode 是 OmO 中"受限并行(bounded parallelism)+ 有界协调"的实现:多个成员会话围绕**共享邮箱(mailbox)和共享任务清单(tasklist)**协作,全部由当前会话(Lead)驱动。它默认关闭(OFF),需要通过 JSONC 配置显式开启。
从源码结构看,这套能力被拆成两层:
- 领域原语层packages/team-core:无 harness 依赖的注册表、邮箱、任务清单、状态存储、worktree 与 tmux 布局原语,共 82 个 TypeScript 文件;
- OpenCode 适配层packages/omo-opencode/src/features/team-mode:负责会话派生、hooks、工具注册与配置集成,被
team_mode.enabled门控。
适用场景
- 并行探索 + 受限协调:多个侦察成员同时扫读代码库,通过任务清单汇总发现,避免无边界发散;
- 跨专用 Agent 拆分的长周期多步重构:不同成员各管一个模块,Lead 统一派活与收口;
- 需要共享任务清单的"研究 + 实现"流水线:研究成员的产出以任务形式交接给实现成员。
如果只需要一次性委托单个子任务,且不需要团队级协调,应当走task(delegate-task)工具而不是 Team Mode——两者并不等价(详见下文"Team Mode 不做什么")。
启用 Team Mode:11 个配置字段全解
在[opencode]块中写入配置:用户级写入~/.omo/omo.jsonc,项目级写入<project>/.omo/omo.jsonc:
{ "team_mode": { "enabled": true, "max_parallel_members": 4, "max_members": 8, "tmux_visualization": false } }启用后必须重启 opencode,12 个team_*工具才会出现在工具注册表中——工具注册通过 packages/omo-opencode/src/plugin/tool-registry.ts 的teamModeToolsRecord完成,且仅在启用时注册。
Schema 与默认值(11 个字段)
该 schema 的权威定义在 packages/team-core/src/config.ts#L3-L15,由 Zod 校验,字段与默认值如下:
| 字段 | 类型 / 取值范围 | 默认值 | 含义 |
|---|---|---|---|
enabled | boolean | false | 总开关 |
tmux_visualization | boolean | false | 是否为每个成员派生 tmux pane 布局 |
max_parallel_members | int1..8 | 4 | 同时在飞的成员上限 |
max_members | int1..8 | 8 | 硬性成员上限(同时约束团队规格) |
max_messages_per_run | int>=1 | 10000 | 单次运行消息总量上限 |
max_wall_clock_minutes | int>=1 | 120 | 单次运行墙钟时间上限 |
max_member_turns | int>=1 | 500 | 单个成员轮转次数上限 |
base_dir | 可选 string | ~/.omo | 覆盖团队规格 / 运行时 / worktree 的基础目录 |
message_payload_max_bytes | int>=1024 | 32768 | 单条消息体字节上限 |
recipient_unread_max_bytes | int>=1024 | 262144 | 单收件人未读邮箱字节上限 |
mailbox_poll_interval_ms | int>=500 | 3000 | 收件轮询间隔(毫秒) |
几点值得注意的实现细节:
base_dir未设置时,resolveBaseDir会展开为~/.omo(packages/team-core/src/team-registry/paths.ts#L68-L70),并支持~/~/形式的主目录展开;基础目录及其子目录(teams、runtime、worktrees)会在首次使用时以0o700权限创建并校验(ensureBaseDirs,同上文件#L209-L228)。message_payload_max_bytes与消息 schema 中body的max(32 * 1024)校验(packages/team-core/src/types.ts#L91-L103)一致,超限消息会在写入阶段被拒绝。- 所有运行期上限(
bounds)会被固化到运行时状态中:maxMembers、maxParallelMembers、maxMessagesPerRun、maxWallClockMinutes、maxMemberTurns(packages/team-core/src/types.ts#L151-L157)。
启用后的验证与排障
启动日志会输出解析后的team_mode状态以及团队工具数量([tool-registry] Built tool registry条目)。若重启后工具仍未出现,检查oh-my-opencode.log中的已加载配置路径与[tool-registry] Built tool registry条目。仓库还配有覆盖"最小配置 + 全新安装"场景的回归测试,专门防住这类配置未生效的回归。
定义团队:TeamSpec 与两种作用域
团队规格文件(TeamSpec)存放在:
- 用户级:
~/.omo/teams/{name}/config.json - 项目级:
<project>/.omo/teams/{name}/config.json
同一团队名在两处同时定义时,项目级优先。这一优先级逻辑在discoverTeamSpecs中实现:先收集项目级规格,再跳过与项目级同名的用户级规格(packages/team-core/src/team-registry/paths.ts#L146-L181),并在冲突时输出team-spec-collision日志。
一个完整的团队规格示例:
{ "name": "ccapi-explorers", "description": "Explore the ccapi project structure.", "members": [ { "kind": "category", "name": "scout-1", "category": "deep", "prompt": "Scout the source directory for auth patterns." }, { "kind": "category", "name": "scout-2", "category": "quick", "prompt": "Scout tests for auth coverage." }, { "kind": "subagent_type", "name": "auditor", "subagent_type": "my-security-auditor", "prompt": "Audit the auth findings the scouts report." } ] }字段约定与校验(来自 packages/team-core/src/types.ts#L58-L89 的TeamSpecSchema):
version与createdAt为可选项,loader 会自动填充(version固定为 1,createdAt默认当前时间戳);name与成员name必须匹配正则^[a-z0-9-]+$;members至少 1 个、至多 8 个(与max_members上限一致);- Lead 无需声明:当前会话始终是 Lead,团队中没有 lead member 条目;
team_create也接受同样的内联形状:{ name, members: [{ name, category|subagent_type, prompt? }] }。
loader 还会对常见错误给出专门的可读报错(packages/team-core/src/team-registry/loader.ts):成员数超过 8 报TEAM_MEMBER_LIMIT_EXCEEDED;同时指定category与subagent_type报AMBIGUOUS_MEMBER_KIND;缺失kind判别字段报MISSING_MEMBER_KIND;category成员缺少必填的prompt报MISSING_CATEGORY_PROMPT。
成员种类与资格边界
两种成员种类
kind: "category":路由到分类 worker——一个由该分类的模型与技能配置的全新 worker 会话。prompt必填。若分类无法解析,直接以UNRESOLVABLE_CATEGORY失败,错误信息会列出可用分类。kind: "subagent_type"(别名"agent"):直接调用omo.json中用户自定义的agents。prompt可选。kind 可根据你设置的字段自动推断,因此可省略kind本身。
两种成员的 schema 差异也在 packages/team-core/src/types.ts#L37-L49 中体现:category成员强制要求category+prompt;subagent_type成员要求subagent_type,prompt可选。
谁能成为成员
- 合格:任何可解析的 category,以及任何用户自定义 agent。
- 解析期拒绝:curated 只读 agent(
explore、librarian、plan-consultant、plan-reviewer)与 ulw-loop 审查三件套(omo-senpi-code-reviewer、omo-senpi-qa-executor、omo-senpi-gate-reviewer)。
拒绝原因在 packages/senpi-task/src/team/member-validator.ts#L25-L69 中给出:curated agent 是只读且进程内的,无法写入邮箱状态;reviewer 三件套被拒绝是因为进程模式成员会丢失审查指令与工具白名单。这两类都应通过task工具路由。
需要说明的是,成员词汇表是宿主相关的:senpi-task 宿主使用上述的CURATED_READONLY_AGENT_NAMES/ULW_REVIEWER_AGENT_NAMES校验;而在 team-core 的 OpenCode 适配层,AGENT_ELIGIBILITY_REGISTRY(packages/team-core/src/types.ts#L190-L237)给出了三级裁决:
| 裁决 | Agent | 说明 |
|---|---|---|
eligible | sisyphus、atlas、sisyphus-junior | 直接可用 |
conditional | hephaestus | 默认缺少teammate: "allow"权限;要么打 D-36 补丁(在tool-config-handler.ts中加teammate: "allow"),要么改用subagent_type: "sisyphus" |
hard-reject | oracle、librarian、explore、multimodal-looker、metis、momus、prometheus | 只读或仅 plan 模式,无法写邮箱;请改用 delegate-task |
hard-reject 会在 TeamSpec 解析期直接抛错("Agent 'X' is read-only…"),错误信息会指引使用者改用 delegate-task——即解析期拒绝、运行时永不接触的原则。
生命周期:从 team_create 到 team_delete
team_create—— 派生团队与各成员会话;- Lead 通过
team_send_message、team_task_create派活; - 成员通过
team_task_update(status: "claimed")认领任务,完成后用team_send_message回报; team_shutdown_request→ 成员或 Lead 通过team_approve_shutdown/team_reject_shutdown应答;team_delete—— 清理运行时状态、worktrees 与(可选的)tmux 布局。
底层有几条关键不变量(packages/omo-opencode/src/features/team-mode/AGENTS.md):
- 派生竞态安全:每次派生都在 sessionID 已知时同步调用
registerTeamSession;所有 hooks 在loadRuntimeState之前先lookupTeamSession,避免 spawn-race 窗口; - 延迟确认:消息 fire-and-forget,收件方通过独立调用确认(ack);
- 任务加锁:任务认领使用原子文件锁,并发认领可安全解决;
- 原子写入:状态变更走"临时文件 + rename",见 packages/team-core/src/team-state-store/locks.ts;
- 仅合格成员:解析期拒绝,运行时不再复查;
- 禁止嵌套团队:成员不可调用
team_create。
运行时状态 schema(RuntimeStateSchema,packages/team-core/src/types.ts#L176-L188)记录version: 1、UUIDteamRunId、teamName、specSource(project/user)、createdAt、status、leadSessionId、可选tmuxLayout、members、shutdownRequests与bounds。运行状态机为creating → active → shutdown_requested / deleting → deleted / failed / orphaned;成员状态机为pending → running → idle / errored / completed / shutdown_approved。
12 个 team_* 工具
工具在启用后注册(teamModeToolsRecord),完整清单与职责如下:
| 工具 | 用途 |
|---|---|
team_create | 派生团队(按命名或内联 TeamSpec) |
team_delete | 拆除(仅 Lead 可用;存在活跃成员时拒绝,除非force: true) |
team_shutdown_request | Lead 请求某成员收尾 |
team_approve_shutdown/team_reject_shutdown | 成员或 Lead 应答关停请求 |
team_send_message | 点对点邮箱;Lead 可*广播 |
team_task_create/_list/_update/_get | 共享任务清单的增、查、改、取 |
team_status | 聚合运行时视图(成员、任务、邮箱) |
team_list | 已声明 + 活跃团队 |
实现分布在 packages/omo-opencode/src/features/team-mode/tools 下:生命周期类(lifecycle.ts、lifecycle-create-tool.ts、lifecycle-participant.ts、lifecycle-shutdown-tools.ts)、消息类(messaging.ts及其 live-delivery 子模块)、任务类(tasks.ts)、查询类(query.ts)。
消息kind有五种:message、shutdown_request、shutdown_approved、shutdown_rejected、announcement(packages/team-core/src/types.ts#L4-L10);任务状态五种:pending、claimed、in_progress、completed、deleted(同上#L14)。任务 schema 还支持blocks/blockedBy依赖关系与metadata,可构建任务间的先后依赖(同上#L105-L119)。
运行边界(Bounds 默认值)
- 最多 8 个成员、4 个同时在飞;
- 每条消息体最大 32 KB,每个收件人未读上限 256 KB;
- 单次运行最多 10000 条消息、120 分钟墙钟、每成员 500 轮。
这些默认值都可被上文 11 字段配置覆盖,并固化进运行时状态供审计。
Worktrees:可选的成员级 git 工作树
在成员条目中加"worktreePath": "../wt-scout"即可为该成员分配独立工作树。路径支持相对文件系统路径或绝对路径;裸分支名被拒绝(避免把成员会话丢进无法工作的状态)。需要环境存在git。工作树创建、校验与孤儿清理由 packages/team-core/src/team-worktree 负责,目录约定为~/.omo/worktrees/{teamRunId}/{member}/。
tmux 可视化:可选的面板布局
设置tmux_visualization: true后,每个成员会获得一个专属 tmux pane,通过opencode attach挂到该成员的会话上——pane 内运行成员完整的交互式 opencode TUI,可以实时观察流式输出。要求:
- 运行在 tmux 会话内,且 PATH 上有 tmux;
- 失败被隔离:缺少 tmux 永远不会阻塞团队创建——依赖探测在 packages/omo-opencode/src/features/team-mode/deps.ts#L16-L30 完成,
tmux_visualization=true但 tmux 不可用时只打警告,运行时跳过布局; - pane 默认从
process.cwd()启动;配置了 worktree 的成员从各自 worktree 启动; team_delete关闭全部 pane 并拆除团队布局;单成员关停只关闭其 pane 并重新平衡剩余布局。
布局原语(网格 pane 排布、调用方 tmux 会话解析、布局重平衡、陈旧会话清扫)位于 packages/team-core/src/team-layout-tmux。
Team Mode 不做什么
- 无嵌套团队:成员不能调用
team_create(由team-tool-gatinghook 强制); - 无同步回复等待:
team_send_message是 fire-and-forget; - 成员被引导不要再生子:成员指导 prompt(member-guidance)会要求成员不再派生子会话;
task工具未被预算门控到 0,但嵌套team_create被拒绝; team_delete拒绝活跃成员,除非force: true。
诊断:doctor 的 team-mode 检查
bunx oh-my-openagent doctor包含team-mode检查项,输出 tmux / git 可用性、已声明团队数量与活跃运行时目录数。其实现(packages/omo-opencode/src/cli/doctor/checks/team-mode.ts#L10-L33):
enabled为 false 时直接skip(team_mode: disabled);- 启用时探测
tmux/git(缺失则warn),报告基础目录状态、teams/下声明数与runtime/下运行时目录数。
存储布局:邮箱、任务清单与投递预留
默认(base_dir未覆盖)磁盘结构如下:
~/.omo/ ├── teams/{name}/config.json # declared specs └── runtime/{teamRunId}/ ├── state.json # durable runtime state ├── inboxes/{member}/{uuid}.json # mailbox (atomic per-message files) ├── inboxes/{member}/.delivering-{uuid}.json # transient live-delivery reservation ├── inboxes/{member}/processed/ # acked messages ├── tasks/{id}.json # shared task list ├── tasks/claims/ # task claim records └── tasks/.highwatermark # tasklist id allocator几点底层机制值得展开:
- 投递预留(delivery reservation):
.delivering-{uuid}.json仅在消息正通过promptAsync实时投递时存在。投递成功则提交到processed/;失败则释放回{uuid}.json;若因崩溃遗留,团队恢复时会按10 分钟 TTL回收。listUnreadMessages会忽略点文件条目,因此兜底轮询绝不会重复注入已被预留的消息——这是"实时投递 + 轮询兜底"双通道不丢不重的前提(相关实现见 packages/team-core/src/team-mailbox)。 - 任务 ID 分配:任务 ID 为十进制自增(
.highwatermark分配器),与 senpi 的st_*任务 ID 体系区分;任务认领使用tasks/claims/{id}.lock文件锁实现原子认领(packages/team-core/src/team-registry/paths.ts#L116-L123)。 - 路径安全:所有运行时路径都经过
resolveContainedPath校验,拒绝..、空段、含/或\的段与 NUL 字符,越界直接抛TeamPathTraversalError(packages/team-core/src/team-registry/paths.ts#L43-L66),任务 ID 还额外要求纯数字。 - senpi-task 项目的存储差异:对 senpi-task 项目,状态目录默认解析到
<project>/.omo/senpi-task,团队运行时路径随之变为<stateDir>/teams/runtime/<teamRunId>/...(详见 packages/team-core/AGENTS.md 与 packages/senpi-task/src/team/storage.ts)。
源码地图:继续深入的方向
- 用户文档:docs/guide/team-mode.md(本文主体);
- 配置 schema:packages/team-core/src/config.ts、packages/omo-opencode/src/config/schema/team-mode.ts;
- 领域类型与资格注册表:packages/team-core/src/types.ts;
- 规格加载与校验:packages/team-core/src/team-registry(
loader.ts、validator.ts、paths.ts、team-spec-input-normalizer.ts); - OpenCode 适配层:packages/omo-opencode/src/features/team-mode/AGENTS.md(12 工具、模块布局、集成点与反模式)及其
tools/目录; - 领域原语总览:packages/team-core/AGENTS.md(注册表、邮箱、任务清单、状态存储、worktree、tmux 布局六大原语);
- 依赖探测与诊断:packages/omo-opencode/src/features/team-mode/deps.ts、packages/omo-opencode/src/cli/doctor/checks/team-mode.ts;
- senpi 成员校验:packages/senpi-task/src/team/member-validator.ts。
自检提示:启用后若team_*工具未出现,按顺序检查配置是否落在正确作用域(用户或项目)、是否已重启 opencode、oh-my-opencode.log中是否出现[tool-registry] Built tool registry;若团队成员创建失败,优先查看错误码(UNRESOLVABLE_CATEGORY、TEAM_MEMBER_LIMIT_EXCEEDED、AMBIGUOUS_MEMBER_KIND、MISSING_MEMBER_KIND、MISSING_CATEGORY_PROMPT、hard-reject 的只读 agent 提示),它们已覆盖绝大多数配置失误。
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考