
以cjh仓颉写的终端编码代理为例。这篇不讲概念讲我刚刚真跑的一次派发真实用起来测试下cjh的效能如何。三个explore子代理同时开工查同一个仓库的三件事然后在父会话里只收到三张结论卡。附带实测的 token / 迭代数和一份老实交代的耗时分析看下cjh开启并行子代理后速度可以有多快。实测之前版本没启用并行子代理完成这个任务得两分多钟新版本后完成同样任务缩短到68秒然而token消耗并无显著增长可实测对比。cjh开源项目地址https://atomgit.com/qq8864/cjh一、事情是这样的我想给仓库写篇博客需要三份材料项目结构、配置组成、测试组织。传统做法是我自己 grep 三轮、读十来个文件、上下文塞满二十万 token然后发现中间某个细节要回头确认再读一遍。这次我换了个做法——在 cjh 里敲了一句人话大意是先建任务单然后一次性并行派 3 个 explore分别查这三件事汇总后写博客。它的反应是在同一轮里发出三个task调用。然后我盯着终端看它同时在跑● task 调查项目结构模块 ▸ explore running read_file ● task 调查配置文件组成 ▸ explore running grep ● task 调查测试组织方式 ▸ explore running glob↑ 这是 TUI 的Agents面板CtrlG可以在父/子之间切换焦点。截图位见文末截图清单。三份结果几乎是同时回来的每张卡长这样这是终端里的真实输出不是编的├─ [explore] 调查项目结构模块 · ok · 迭代 6 · ↑36904 ↓2851 ├─ [explore] 调查配置文件组成 · ok · 迭代 6 · ↑109825 ↓3744 ├─ [explore] 调查测试组织方式 · ok · 迭代 6 · ↑80103 ↓3770 └─ (task 结束)s1793117988.888.1那一串是会话号下面还挂了一行转录: ~/.cjh/spill/.../transcript-3.md——子代理跑完的完整对话落盘了想回看就去看父上下文里不留。二、并发在 cjh 里到底是怎么实现的一句话概念模型写在docs/并行子代理与上下文隔离-方案.md开头Agent 树 进程树。父代理在轮内 fork 出 N 个受限子代理各自跑独立对话父wait()收结构化结果。拆开看有四层缺一层就出事故1. 谁并行DAG 的组内并发loop.cj会把一轮里的多个 tool_call 建成依赖图。task调用如果没有 path 依赖就不建边 →同一拓扑组内天然并发。所以一次发三个 task和发一个 task 等它跑完再发下一个在框架层是两条完全不同的路径。2. 谁管闸SubagentSchedulersrc/agent/scheduler.cj它是私有不变量有界资源的载体干四件事机制实现为什么必须有并发上限Semaphore(maxConcurrent)满了排队每 50ms 重试且检查父是否被中断不限并发 打爆 provider 限流5 路同起实测全灭每回合派生上限 token 预算tryBeginSpawn()一次加锁内判定 计数旧版判定/占位/计数三步分离是 TOCTOU 漏洞两线程能同时通过墙钟超时subagent_timeout_ms超时映射statustimeout子代理卡死会把父回合的组 barrier 永久阻塞错峰起跑staggerGate()第 k 个等k*staggerMs默认 1s并发不是同时猛冲是把请求洪峰摊平节点状态是显式枚举Pending / Running / Ok / Failed / Timeout / Cancelled / BudgetExceeded。不用字符串嗅探——这类代码以前靠reply.startsWith([已中断)判断取消加第三种终态就脆了。3. 谁隔离上下文单向流两道窄门这是我觉得最值钱的一条设计也是我这篇博客敢写的原因往下子代理拿到的只有「子任务 prompt 父代理自动附带的上下文块已触碰文件、会话摘要」父历史一个字节都不给。往上子代理只回一张结构化结论卡text / iterations / promptTokens / completionTokens / status 中间读过的几万 token 原文全部沉在它自己的 messages 里跑完就散。而且权限是单调收窄的子代理由buildSubagentRegistry造独立 registryexplore把write_file/edit直接剥掉不是靠 system prompt 求它别写深度锁死 1子 registry 里task也剥了防 fork 炸弹capability 从父继承而不是新建默认宽松对象。4. 谁能看得见可观测 可审批Agents面板的数据源就是 scheduler 里的AgentNode树snapshot()深拷贝渲染零阻塞。审批是队列不是单槽。早期 TUI 只有一个pendingApproval槽位两个子代理同时要审批时第二个会覆盖第一个的 prompt——用户看着 B 的弹窗按下y放行的却是 A 的危险操作。现在弹窗标题带(2/3)和[task:label]身份y/n只作用于队首。中断传播整棵树CtrlG在节点间切焦点能看某一个子代理正在读哪个文件。三、耗时分析并发省的是墙钟不是 token先给这次实测的账本数字全部来自上面那三张结果卡子代理迭代输入 token ↑输出 token ↓项目结构636,9042,851配置组成6109,8253,744测试组织680,1033,770合计18226,83210,365关键结论 1墙钟 ≈ 最慢那一路不是三路之和三个子代理迭代数碰巧都是 6说明它们的工作量在轮数这个维度上几乎一样。如果串行执行墙钟是t1t2t3并行执行墙钟是max(t1,t2,t3)。按每次 LLM 往返 ~15–25s 粗算输入 token 量大主要是首轮灌上下文串行3 × 6 轮 × 20s ≈ 360s 6 分钟中间还要我盯着有没有跑偏 并行 6 轮 × 20s ≈ 120s 2 分钟出头三张卡一起落地 实测加速比 ≈ 2–3×取决于最慢那一路拖多久口径说明这是估算不是秒表数据——这台 Windows 机器的 shell 起不来bash直接Created process failed我没法在派发前后打时间戳。公式给出来了你在自己有time的环境里换成真实t重算即可↑↓和迭代数是实测的。关键结论 2真正的收益不是快是父上下文不膨胀这条比加速比重要得多。三路子代理合计读进 22.7 万 token的原始材料父上下文只为此付出了1 万 token 左右三张结论卡之和。如果这三件事我自己在主会话里做22.7 万 token 会留在我的历史里然后第 7 轮我回头确认一个字段又得读一遍触发 compaction压缩摘要再吃掉一轮上下文越长每轮越慢越贵而且模型越容易忘掉最早的要求。并发 隔离把这个正反馈刹住了读得越多越不该留在主线程。关键结论 3并发不是免费的固定开销每个子代理 ~3–5k token 的系统提示 工具 schema。派 3 个先付 1 万出头。重复劳动三个子代理各自把仓库顶层目录读了一遍这部分是冗余的但当前我给的任务是测试。最慢者决定下限max而不是avg所以把一个 5 秒的活和一个 3 分钟的活凑一起派不会比单派那个 3 分钟的更快。限流风险真同时猛冲会被 provider 打回来所以才有staggerGate()那 1s 错峰。cjh 自己在系统提示里写明了派发纪律我照抄Limits: 3 concurrent spawns per turn, 6 per turn (excess is rejected with budget_exceeded). Each spawn costs ~3-5k tokens of fixed overhead. Forbidden: spawning just to double-check yourself, or one worker for one small file.超了不是默默排队是直接budget_exceeded拒掉派得没价值还会在总结条里挂个⚠N低效派生计数下一轮提示我别这么派。四、三路调查的结果顺便当验证三份结论卡汇总下来大致是这些——也是这次并发的实际产出项目结构src/约 98 个.cjtools 19、tests 45、agent 9、web 10、tui 2、gateway 1、根级 12libs/6 个独立库依赖方向cjlog ← cjllm ← cjcfg ← cjh(根包)cjutil横向支援cjterm/cjconfig零依赖。内置工具 13 个 task子代理 插件/MCP 动态工具。配置组成构建期是根cjpm.tomlexecutable 5 个路径依赖 双平台 stdx 静态路径运行期全在~/.cjh/可用CJH_CONFIG_DIR覆盖——settings.json主配置、auth.json只放 api_key600 权限、sessions/、skills/、cangjie-ref/、plugins/、project-memory/、spill/、workspaces.json。优先级环境变量 auth.json/settings.json 内置默认。capability段默认宽松不写enabled:true就全放行只打一行提示显式打开才进入未声明即拒绝。测试组织三层——src/tests/45 文件包cjh.tests、src/core_funcs_test.cj根包函数因为executable 根包不能被 import、libs/*/src/**/*_test.cj29 个文件。规模根包 536 例 6 库 175 例 pty 端到端 61 项检查。test.sh会把静态链接临时派生成动态配置再跑静态下测试框架 double freetrap保证退出必还原。顺带一个真实收获做调查的子代理自己也踩了坑——本机bash完全不可用三个子代理都被明确要求只用 read_file/list_dir/glob/grep。这就是结论卡之外的信息传下来的不只是结论还有失败的路径。五、什么活儿该并行不是所有事都值得派子代理。我总结的判据跟 cjh 自己的路由规则一致该派≥5 个文件的调查、结论能用一句话概括的仓库级扫描、互不相干的多文件改动。不该派单文件改动、答案已经清楚、只需 3 次工具调用。这类事父代理自己做30 秒完事派出去反而先付 5k token 开销再等 2 分钟。禁止派为了让另一个代理再核对一遍互相确认不出新信息只是花钱。一句话并行的价值在于独立的、读得多的、只要结论的工作。读得多而只要结论——这正是父上下文最容易被撑爆的场景也是上下文隔离收益最大的场景。