oh-my-openagent 的 `max_background_agents` 后台代理并发限制:一份从分支、原子提交到三门禁验证的完整执行计划
本文以仓库中一份真实的功能执行计划文档为骨架,逐阶段拆解 oh-my-openagent 为后台代理(background agent)新增 max_background_agents 配置项的完整交付流程:Worktree 环境准备、两个原子提交的实现设计、PR 创建、CI / review-work / Cubic 三重门禁验证循环,以及合并与清理。读完后你将掌握该特性"为什么这样设计、改哪些文件、如何验证、如何收尾"的全链路细节,并能对照仓库源码理解每处设计决策的落点。
这份执行计划位于 execution-plan.md,是一份"可直接照做"的 PR 工作手册;同目录下的 code-changes.md 则给出了每个文件的具体改动代码,二者可配合阅读。
功能背景:给后台代理加一道"全局上限"
oh-my-openagent 允许主会话派生多个后台代理并行执行子任务。现有的并发控制是按模型维度的:每个模型(或 provider)各自有并发上限,超出的任务进入该 key 的排队队列。执行计划要解决的问题是:当用户同时使用多个模型时,各模型各自跑满,全局同时运行的后台代理总数不受控。为此计划新增一个跨模型/跨 provider 的全局配置项 max_background_agents(配置路径 background_task.maxBackgroundAgents),默认值 5,最小值 1。
仓库当前源码印证了这一前提:
- 配置 Schema BackgroundTaskConfigSchema 目前包含
defaultConcurrency、providerConcurrency、modelConcurrency、maxDepth、maxLiveDescendantsPerRoot、各类超时与熔断字段,尚无maxBackgroundAgents字段——说明该特性在本文快照中仍是待实施状态; - ConcurrencyManager 只维护
Map<key, count>的按模型计数与等待队列,getConcurrencyLimit()在未配置任何并发项时返回默认值 5(concurrency.ts 第 39 行)——计划中全局上限默认 5 与这一既有默认值刻意保持一致,避免行为突变; - BackgroundManager.launch() 目前先经
reserveSubagentSpawn通过派生守卫,再创建status: "pending"的任务并按并发 key 入队。计划中的全局检查正是插在这一链路的前置位置。
Phase 0:环境准备——分支 + Worktree
执行计划的第一步是隔离开发环境,保证主工作树(main worktree)始终干净:
-
从
dev分支拉取最新代码并创建特性分支:git checkout dev && git pull origin dev git checkout -b feat/max-background-agents -
在兄弟目录创建 worktree:
mkdir -p ../omo-wt git worktree add ../omo-wt/feat-max-background-agents feat/max-background-agents -
计划明确规定:后续所有工作都发生在
../omo-wt/feat-max-background-agents/,绝不在主工作树中操作。这一约束在 Phase 4 的清理步骤中与之呼应。
Phase 1:实现——两个原子提交
Commit 1:在配置 Schema 中新增 max_background_agents
涉及文件(计划路径):
src/config/schema/background-task.ts— 为BackgroundTaskConfigSchema增加maxBackgroundAgents字段(对应当前仓库的 packages/omo-opencode/src/config/schema/background-task.ts)src/config/schema/background-task.test.ts— 新增该字段的校验测试(对应 packages/omo-opencode/src/config/schema/background-task.test.ts)
实现要点:
- 新增字段定义为
maxBackgroundAgents: z.number().int().min(1).optional(); - 默认值放在运行时处理(默认 5),而不是写进 Schema——这是遵循项目约定:Schema 中所有字段均为 optional,默认值由消费方(
ConcurrencyManager)在运行时兜底; - 测试采用项目既有的 given/when/then 风格,覆盖四个场景:合法值、低于最小值(0)、未提供(应为
undefined)、非数字(应为ZodError)。
Commit 2:在 BackgroundManager + ConcurrencyManager 中强制执行全局上限
涉及文件(计划路径):
src/features/background-agent/concurrency.ts— 增加全局计数与getGlobalRunningCount()、canSpawnGlobally()等方法(对应当前仓库 concurrency.ts)src/features/background-agent/concurrency.test.ts— 全局上限的测试用例src/features/background-agent/manager.ts— 在launch()与trackTask()中检查全局上限(对应 manager.ts)
实现要点:
-
ConcurrencyManager已管理按模型并发,计划新增一个独立的全局计数器,而不改动现有队列机制:private globalRunningCount: number = 0private maxBackgroundAgents: number(来自配置,默认 5)acquireGlobal()/releaseGlobal()配对方法getGlobalRunningCount()用于可观测性
-
BackgroundManager.launch()在创建任务前调用concurrencyManager.canSpawnGlobally()检查;trackTask()(外部任务注册入口)同样要过这道检查; -
任务完成、取消或出错时调用
releaseGlobal()归还名额(配套的 code-changes.md 进一步指明释放点分布在tryCompleteTask()、cancelTask()、session.error事件处理与 prompt 错误处理器中,且pending状态的任务从不占用名额); -
触顶时抛出带指引的描述性错误:
Background agent spawn blocked: ${current} agents running, max is ${max}. Wait for existing tasks to complete or increase background_task.maxBackgroundAgents.错误信息直接告诉用户"等现有任务完成"或"调大
background_task.maxBackgroundAgents",把排查路径写进了异常本身。
本地验证
计划要求两个提交完成后依次执行:
bun run typecheck
bun test src/config/schema/background-task.test.ts
bun test src/features/background-agent/concurrency.test.ts
bun run build
Phase 2:创建 PR
推送分支并创建以 dev 为基线的 PR:
git push -u origin feat/max-background-agents
gh pr create \
--base dev \
--title "feat: add max_background_agents config to limit concurrent background agents" \
--body-file /tmp/pull-request-max-background-agents-$(date +%s).md
PR 正文以文件形式准备后用 --body-file 注入,时间戳命名避免本地文件冲突。
Phase 3:验证循环——三道门禁
执行计划的核心特色是把 PR 验证拆成三道独立门禁(Gate),全部通过才允许合并。
Gate A:CI
- 等待
ci.yml工作流完成; - 用
gh pr checks <PR_NUMBER> --watch实时盯结果; - 失败时:读日志、修复、推送、重新等待。
Gate B:review-work(5 个并行后台子代理)
运行 /review-work skill,它并行启动 5 个后台子代理做交叉审查:
- Oracle — 目标/约束核验(改动是否忠于 PR 目标);
- Oracle — 代码质量;
- Oracle — 安全性;
- Hephaestus — 动手执行 QA;
- Hephaestus — 从 GitHub/git 历史中挖掘上下文。
5 个必须全部通过;任何一个失败都要修复并重新推送。
Gate C:Cubic 机器人(cubic-dev-ai[bot])
- 等待 Cubic bot 在 PR 上给出评审;
- 必须输出 "No issues found";
- 若发现问题:逐条处理反馈、推送、重新检查。
循环结构
三个门禁构成一个显式循环,直到全绿:
while (!allGatesPass) {
if (CI fails) → fix → push → continue
if (review-work fails) → fix → push → continue
if (Cubic has issues) → fix → push → continue
}
值得注意的是,Gate B 本身就在"用这个仓库的后台代理机制来审查这个改动后台代理限制的 PR"——特性与验证手段形成了自我印证。
Phase 4:合并 + 清理
-
Squash 合并并删除远端分支:
gh pr merge <PR_NUMBER> --squash --delete-branch -
移除 Phase 0 创建的 worktree,与开头形成闭环:
git worktree remove ../omo-wt/feat-max-background-agents
文件影响面总览
计划给出的 File Impact Summary(计划路径 → 当前仓库对应路径):
| 文件(计划路径) | 改动类型 |
|---|---|
src/config/schema/background-task.ts |
Modified — 新增 schema 字段 |
src/config/schema/background-task.test.ts |
Modified — 新增校验测试 |
src/features/background-agent/concurrency.ts |
Modified — 新增全局上限计数 |
src/features/background-agent/concurrency.test.ts |
Modified — 新增全局上限测试 |
src/features/background-agent/manager.ts |
Modified — 在 launch/trackTask 中强制执行全局上限 |
共 5 个文件、2 个原子提交,不新建任何文件(刻意沿用既有组织模式)。其中 src/ 前缀相对 omo-opencode 包根目录,在当前仓库快照中对应 packages/omo-opencode/src/ 前缀。
源码纵深:计划落点在当前仓库中的证据
把执行计划映射回当前仓库源码,可以看到每个设计决策都有明确的"接口缝":
- 按模型队列与全局计数分而治之。concurrency.ts 中
QueueEntry采用 settled-flag 模式防止release()与cancelWaiters()双重 resolve,release()会优先把空闲槽位直接"移交"给队首等待者。这套机制只作用于单一 key,跨 key 语义(全局总数)无法由它表达,因此计划选择叠加一个简单计数器(globalRunningCount+acquireGlobal/releaseGlobal),而不重构既有队列——这也是 File Impact Summary 中concurrency.ts仅标注"Modified"、约 25 行的原因。 - 默认值 5 的一致性。
getConcurrencyLimit()的兜底默认是 5(concurrency.ts#L39),全局上限沿用同一默认值,意味着未配置用户在新旧语义下的体感并发规模一致。 - 检查点与释放点配对。
launch()(manager.ts#L579)创建任务时状态为pending并立即入队,计划让全局名额在任务注册时acquireGlobal()、在tryCompleteTask()/cancelTask()/session.error/ prompt 错误处理中releaseGlobal(),并明确pending从未 acquire 的任务在取消时不 release,保证计数不漂移。 trackTask()是第二道入口。除launch()外,外部创建的任务经trackTask()(manager.ts#L1216)注册,计划特意让它也过canSpawnGlobally()检查,堵住"绕过 launch 的旁路不受全局上限约束"的漏洞。- 配置文档联动。
background_task各配置项的使用方式在 docs/reference/configuration.md 与 docs/reference/omo-json.md 中有对应章节,新字段合入后这些文档即为max_background_agents的用户可见说明落点;测试侧则已有 background-task.test.ts 的 given/when/then 用例族可直接追加。
需要说明的适用前提:本快照中 BackgroundTaskConfigSchema 尚未包含 maxBackgroundAgents,concurrency.ts 也不存在全局计数字段,因此本文描述的"实现"是执行计划所规划的目标状态,而非当前树中已合并的行为;文中所有 src/... 计划路径均按上述 packages/omo-opencode/src/... 映射理解。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00