munder-difflin 的多 agent 协调实现

munder-difflin 的多 agent 协调实现

💡 原文中文,约8900字,阅读约需21分钟。
📝

内容提要

munder-difflin 是一个 Electron 桌面应用,将多个 agent CLI 封装为像素小人,通过双数据平面(终端字节流和事件钩子)协调。其核心是蜂巢系统,用本地 git 仓库和文件系统作为协调层,主进程负责路由和提交,agent 只写自身目录。设计注重安全,如终端输入排队需五条件放行,草稿检测偏向保守,并处理 Windows 多行参数截断问题。文档与代码有偏差,但关键机制注释可信。

🔎

延伸解读

双数据平面:为何不合并

munder-difflin 将数据分为终端字节流和事件钩子两个平面,避免解析终端输出来推断工具状态。这种设计让画布和终端视图各自独立,互不干扰。对于需要同时展示原始输出和结构化状态的桌面应用,这种分离能提高可靠性,但实现复杂度也更高。

文件系统作为协调层

蜂巢系统用本地 git 仓库和文件系统协调多 agent,每个 agent 只写自己的目录,由主进程负责路由和提交。这种设计避免了并发 git 冲突,通过原子 rename 投递消息,比引入消息队列更轻量。适合中小规模的多 agent 协作,但依赖文件系统性能,且需要谨慎处理锁和幂等。

安全与误判的权衡

终端输入排队需五条件放行,草稿检测偏向保守,宁可误判有字也不误判为空,因为误判为空的代价更高。这种设计体现了对用户输入安全的重视,但也可能导致消息延迟。对于需要自动化交互的 CLI 工具,这种权衡值得借鉴。

Windows 兼容性陷阱

Windows 上 .cmd 和 .bat 文件通过 cmd.exe 解析,多行参数会被截断,导致 agent 收不到协议。修复方法是解码 npm 的 .cmd shim,直接使用参数数组 spawn,绕过 cmd.exe。这提醒开发者,跨平台工具必须考虑 shell 解析差异,否则会出现难以察觉的静默失败。

Q&A

munder-difflin 是什么?它如何将多个 agent CLI 组织起来?

munder-difflin 是一个 Electron 桌面应用,它将多个 agent CLI(如 claude、codex、grok 等)封装为像素小人,在办公室场景中协调它们。它通过双数据平面(终端字节流和事件钩子)实现协调:终端流通过 node-pty 启动真实进程,事件流通过钩子(shim)将生命周期事件写入本地 socket。核心是蜂巢(hive)系统,使用本地 git 仓库和文件系统作为协调层,主进程负责路由和提交,每个 agent 只写自己的目录。

munder-difflin 的蜂巢系统如何避免多个 agent 并发写 git 仓库导致的冲突?

蜂巢系统通过四条规则避免冲突:1) 只有主进程提交 git,agent 只写普通文件;2) 每个文件只有一个写者,agent 只写自己的 agents/<id>/ 目录,跨 agent 投递由主进程路由器搬运;3) 消息以单个 JSON 文件形式写入,先写临时文件再 rename,避免共享文件追加;4) log.jsonl 只追加,消费者各自记录游标。此外,board.md 由编排 agent 作为唯一抄写员。

munder-difflin 中终端输入排队需要满足哪些条件才能放行?

终端输入排队放行需要同时满足五个条件:agent 状态是 idle;没有全局暂停(或消息被手动放行);过了 35 秒开机宽限;isTerminalAutomationSafe(ptyId) 通过;距上次投递超过 4.5 秒。此外,两次 PTY 写入(文本和回车)都成功才算送达,失败则留在队列重试。

munder-difflin 如何处理 Windows 上多行参数被截断的问题?

Windows 上 .cmd 和 .bat 不能直接交给 CreateProcess,spawn 走 cmd.exe /d /s /c "<line>",cmd.exe 将 CR/LF 当作语句分隔符,导致多行参数被截断。修复方法是解析 npm 的 .cmd shim,解码出真正的解释器和脚本,然后用参数数组 spawn,使 node-pty 的 argsToCommandLine 直接交给 CreateProcess,避免 shell 解析器。同时,对无法解析的 shim 返回 null 并回退到 cmd.exe 路径,并增加警告。

munder-difflin 中编排 agent(Michael)的职责是什么?

编排 agent(Michael)是一个普通的 claude 进程,标记为 isGod。它负责管理名册和路由(registry.json)、裁决(读每条对外请求,例行解决,关键升级)、作为 board.md 的唯一抄写员、维护任务台账。关键决策(如破坏性操作、花钱、范围变更)写在系统提示词中,而不是代码里。

munder-difflin 如何防止提示词前缀破坏 prompt cache?

提示词前缀必须保持无易变量,只插值那些在 agent 整个生命周期中稳定的值,不添加日期、UUID、计数器、board 或 registry 状态等易变内容,因为每次 spawn 都变化的前缀会打掉 prompt cache,导致每次都要重新预热整个系统提示词。易变的上下文通过 inbox 和 PTY 传递,不烤进前缀。

munder-difflin 中记忆与压缩机制是如何工作的?

记忆底座是每个 agent 的 memory.md 加共享 board.md,纯 markdown。语义层使用 MemPalace CLI,将每个 agent 的 memory.md 挖进自己的 wing,按 mtime 判断是否重挖。压缩由 reflect.ts 实现,将文件重写为有界三段:持久事实、递归摘要、最新 K 段原文,使用 claude-haiku-4-5 总结被驱逐的尾巴,预算 128 KB。压缩前备份、验证、原子替换,失败则原文件不变。

munder-difflin 的断路器(breaker)如何控制成本?

断路器模块 breaker.ts 监控成本,触发条件包括用量采样、钩子事件(重复 PostToolUse 和 api_error 风暴)、文件 mtime(无进展)。它提供 steer、constrain、stop 三级升级阶梯,默认 hardStop 关闭,一个心跳只升一级,健康心跳降一级。token 速率兜底为每分钟 60,000 output tokens,无进展需连续两个心跳。上下文压缩有豁免机制。

munder-difflin 中为什么 cost-ledger.jsonl 不应该被 git 跟踪?

cost-ledger.jsonl 只追加,每次用量采样加一行,而 hive 一直在提交,如果跟踪它,每次提交都会存一份整个文件的新拷贝,导致仓库体积膨胀。例如,四分之一 GB 的账本加几千次提交就是几百 GB 的 blob,使例行 gc 变成多 GB pack-objects。而且 git 对已在索引中的文件会继续记录,即使 .gitignore 忽略,所以需要 rm --cached 并先探测是否被跟踪。

munder-difflin 的文档与代码有哪些偏差?

HIVE.md 将“自治循环等于 Stop 钩子”列为设计决策,但代码中 Stop 钩子已改为返回 {},不再强制续跑,而是通过 inbox 文件由渲染进程在 idle 时投递。SPEC.md 描述的是 tmux 附着方式,但实际实现是 spawn PTY。drainForStop() 函数已无生产调用者。文档与代码偏差较大,但关键机制注释可信。

🏷️

标签

➡️

继续阅读