前几周,我在这台 Linux 机器上专门养了一个 OpenClaw agent:coder。
它的定位很明确——不是陪聊,也不是通用助理,而是一个偏工程执行的开发代理。我最早给它做了几组 skills,目标也很务实:把日常开发任务一键 dispatch 给 Claude Code,尤其是放在 Telegram 里以 slash command 方式触发。
一开始这套东西是能跑的,而且不只是“勉强跑通”,甚至已经有一点像真正的工作流:
/dispatch <project> <task-name> <prompt...>:跑 headless 的异步开发任务/dispatchi <project> <task-name> <prompt...>:跑需要交互式 slash-only 插件的任务/cancel <run-id>:中止一个正在执行的交互任务
再配合本地约定:
- workdir 固定映射到
/home/miniade/repos/<project> - 结果目录按 project/task 分层,避免覆盖
- 默认把完成通知打到 CodeHook 群
- 交互型任务通过 callback 自动汇报收尾
这套设计并不“学术”,但很实用。问题是,它越实用,就越暴露出一个事实:我其实是在自己造一层 agent 调度壳,而 OpenClaw 后来原生支持了 ACP。
一旦 ACP 可用,很多之前必须靠自定义 skill、脚本、tmux、callback、env 约定去补的东西,突然变成了平台原生能力。
于是问题就从“怎么把 dispatch 技能做得更强”变成了:
既然 OpenClaw 已经支持 ACP,我是不是应该直接用 ACP 重建整个开发流程?
我现在的答案是:是,而且越早越好。
这篇文章就是一次完整复盘:不是抽象讨论,而是结合 coder 的 workspace、长期记忆和 session 里过去几周的真实轨迹,整理出一条从“dispatch 技能驱动”迁移到“OpenClaw ACP 原生驱动”的思路。
最早那套 dispatch 流程,解决了什么问题
先别急着否定旧方案。
coder 早期那套 dispatch skills 并不是“错误路线”,它其实非常符合当时的问题形状:
- 我想从聊天界面快速把开发任务扔给外部 coding agent
- 我不想每次手动拼命令、切目录、建日志、盯结果
- 我需要异步执行,而不是把主会话阻塞住
- 有些任务需要交互式 loop,例如 slash-only 的插件场景
所以 dispatch / dispatchi / cancel 的本质,不是“做 skill 为了炫技”,而是把一个重复出现的开发工作流包装起来。
coder 的长期记忆里后来把这些约定写得很清楚:
/dispatch用来做 headless 派发/dispatchi用来做交互式派发,默认max-iterations=20、completion-promise=COMPLETE/cancel用来收尾交互任务- 结果目录、日志路径、CodeHook 群号、本地 env 文件路径都有固定约定
这很像一个人在把“口头默契”变成“可执行流程”。
而且它确实解决了一个早期最重要的问题:把“我想让 Claude Code 去干活”从一堆散乱 shell 操作,收敛成一个稳定入口。
真正的问题,不是能不能跑,而是这套系统本身太重了
过去几周里,coder 的 session 有一个非常典型的演化:
一开始讨论的是功能;后面越来越多讨论的是边角稳定性。
例如这些问题,都是在真实使用里慢慢浮出来的:
1. callback 和通知链路很脆
dispatchi 如果想在任务结束后自动通知 CodeHook 群,必须保证本地配置里正确打开 callback,像:
ENABLE_CALLBACK=1TELEGRAM_GROUP=-1002547895616- 或对应的
CODEHOOK_GROUP_DEFAULT
一旦 skill 更新、重装,或者 env 文件被覆盖,这条通知链路就会静默失效。
换句话说,任务可能已经跑完了,但你以为它还没结束;或者它出结果了,但没有回传。
这不是模型能力问题,而是自建工作流最常见的“胶水层脆弱性”。
2. 交互式任务要自己维护 tmux / socket / auto-exit
dispatchi 为了支持交互式 loop,本质上做了很多平台外工作:
- 创建 tmux session
- 管理 socket 名称
- 记录 run-id 和 session 关联
- 监听 completion promise
- 适时
/exit - 出问题时再
/cancel
在 session 里还能看到一次非常典型的 bug:tmux socket 文件名过长导致启动失败。后来为了修复这个问题,又不得不把 session/socket 名裁短,引入哈希键,继续补逻辑。
这就是典型的工程信号:
你本来想解决的是“如何调度外部 agent”,结果越来越多时间花在“如何维护调度系统本身”。
3. skill 为了可发布、可移植、可过审,不断增加额外负担
当这套 dispatch 技能准备发到 ClawHub 时,又暴露出另一类问题:不是功能不对,而是包装方式和安全模型不够平台友好。
例如当时需要处理的点包括:
- skill 不能只是“表面自包含”,实际却依赖外部 repo 里的脚本
- 不能简单
source一个本地 env 文件,否则审计视角下会放大风险 - callback 行为必须清晰、可控,不能默认带隐式外发
- 默认权限策略要更保守,不能看起来像“默认高权限 agent 启动器”
于是又做了一轮“低 VT 风险”改造:
- 将运行时脚本 vendoring 进 skill 包本身
- 把 env 读取改成 allowlist 的
key=value解析 - callback 默认关闭,显式开启才启用
- 权限参数默认更保守
- 修复 dispatchi 的假阳性完成检测
这些改动都合理,也都值得做。但它们有一个共同点:
你投入的工程力越来越多地用在“让自定义派发层不出问题”,而不是用在“让开发任务本身更顺滑”。
我后来意识到:这套 dispatch 技能,其实是在模拟平台原生能力
当我回头看 coder 的这段实践,最强烈的感受不是“这套技能写得挺不错”,而是:
它在很大程度上,是在手工模拟一个本应由平台承担的 agent orchestration 层。
看一下 dispatch 技能到底在手工解决什么:
- 异步启动外部 coding agent
- 给任务分配上下文和工作目录
- 跟踪任务生命周期
- 回传启动信息与状态
- 在会话结束时通知用户
- 支持取消、恢复、查看进展
- 尽量把外部 agent 的运行结果绑定回聊天链路
而这些,恰恰就是 ACP 想解决的问题。
一旦 OpenClaw 原生提供 ACP runtime,并且可以通过 sessions_spawn 之类的能力直接启动外部 coding agent,那么之前那一大层自定义 glue code,就开始显得越来越像“过渡方案”。
不是说它完全没价值,而是:它不再是最优抽象层。
为什么 ACP 是更好的重建方向
我现在更倾向于把 ACP 理解成:
OpenClaw 与外部 coding agents 之间的统一调度协议层。
这个“统一”很重要,因为它带来了几个旧 dispatch 流程很难天然具备的优势。
1. agent 启动不再是“脚本技巧”,而是平台能力
旧流程里,启动 Claude Code 其实是一套脚本工程:
- 拼命令
- 建目录
- 建日志
- 处理权限模式
- 决定 headless 还是交互
- 写 meta 文件
- 回传 run-id
ACP 之后,启动外部 coding agent 这件事本身就是 runtime 能力。
这会带来一个关键变化:
你不再需要围绕“如何把 agent 拉起来”设计 skill,而是围绕“要不要把这个任务交给某个 agent”设计流程。
这两个抽象层次完全不一样。
2. 状态跟踪更自然
coder 的长期记忆里后来明确写了一条非常重要的规则:
没拿到
accepted + childSessionKey + streamLogPath三件套,就不能对用户说“已经开始”。
这条规则之所以会被写进长期记忆,就是因为过去确实踩过坑:如果只是“看见某个日志文件在变”,你很容易误把上一个任务的痕迹当成这一个任务已经启动。
ACP 模式下,状态确认来自平台级 session 语义,而不是“某个目录里有个新文件”“某个 tmux session 像是活着”。
这件事听起来很小,但实际体验差别非常大:
- 用户会更少收到假启动反馈
- agent 自己更不容易搞混任务归属
- 进度查看和后续干预有天然锚点
3. 少掉大量非业务性的脆弱点
ACP 并不会让 bug 消失,但它能把很多原本由你自己维护的脆弱点收回到平台层:
- 少掉一层 wrapper script
- 少掉一层 tmux 约定
- 少掉 callback 手搓协议
- 少掉结果目录规范的自建绑定
- 少掉“这个 skill 本体到底是不是自包含”的发布焦虑
也就是说,故障面会更小,而且更集中。
旧方案的复杂性分散在 skill、env、repo、tmux、Telegram callback、ClawHub packaging、workspace 同步等很多层;ACP 则把“调用外部 agent”尽量收口到一个平台能力上。
4. 不再被单一外部 agent 工作流绑死
dispatch 技能最初几乎就是为 Claude Code 工作流量身做的。
这在早期是优点,因为它针对性强;但从长期看,它也意味着一件事:
你的调度层天然偏向某一个外部 agent 的交互模型。
而 ACP 的价值之一就在于,它把“外部 agent 的差异”下沉到了协议/运行时层,让上层可以更自然地面向任务,而不是面向某个具体 CLI 的怪癖。
这会让后续扩展到 Codex、Pi、Claude Code 甚至更多 ACP harness 时更顺手。
那是不是 dispatch skills 就没有价值了?
也不是。
我更愿意把它们看成一个典型的“过渡时代产物”:
- 在 ACP 不成熟或不可用时,它们非常有价值
- 在特定平台(例如 Telegram 直接 slash command 分发)里,它们仍然能作为轻量入口
- 它们沉淀出的约定,本身仍然有参考意义
比如 coder 在这些技能实践里沉淀下来的很多经验,迁移到 ACP 时代照样成立:
经验 1:开发流程要有固定的发布回归闭环
coder 后来被明确要求长期遵循这条流程:
本地开发 → 本地测试 → 发布 → 从 ClawHub 安装并回归测试
这条经验并不会因为从 dispatch 转向 ACP 就失效。
相反,在 ACP 时代我会把它稍微泛化成:
本地实现 → 本地验证 → 平台入口验证 → 真实聊天面回归
因为问题往往不出在“代码能不能运行”,而是出在“平台接入之后的真实行为是不是和你想的一样”。
经验 2:把长期约定写进 memory,而不是指望 agent 临场记住
coder 这段实践里最对的一件事,就是不断把经验写回 MEMORY.md 和 daily memory:
- callback 要怎么开
- 结果目录怎么分层
- 哪些技能是 explicit-only
- 发布链路应该怎么走
- 什么条件下才能宣称 ACP 任务“已启动”
这也是我越来越认同的一条原则:
agent 的稳定性,很大一部分来自可写回的工作记忆,而不是模型当下的临场发挥。
经验 3:真正该抽象的不是命令,而是工作流边界
早期 dispatch skills 已经说明了一个事实:
dispatch、dispatchi、cancel之所以拆开,是因为它们的任务边界不同
ACP 时代也一样。
真正应该抽象清楚的,依然不是某条 shell 命令,而是:
- 什么是一次性异步 coding task
- 什么是需要持续线程上下文的 coding session
- 什么是中途干预/取消
- 什么是发布后的回归验证
抽象对了,底层是 shell 也好、ACP 也好,都只是实现细节。
如果现在用 OpenClaw ACP 重建开发流程,我会怎么做
如果让我今天基于这些实践,重新设计 coder 的开发协作流程,我会尽量收敛到下面这套结构。
一、把“请求外部 coding agent”默认切到 ACP
也就是说,今后用户说:
- “让 Claude Code 做这个”
- “用 Codex 帮我改一下”
- “在一个持续线程里跟进这个开发任务”
默认都应该先走 ACP 路由,而不是优先想到 dispatch skills。
dispatch 只保留为一种显式兼容入口,或者只在特别适合 slash command 的旧场景里使用。
换句话说:
- ACP 是主路径
- dispatch 是遗留兼容层 / 特定场景快捷方式
二、把流程分成“任务型”和“会话型”两类
这是我从旧 dispatch 与新 ACP 对比里最想强调的一点。
以前很多设计复杂,根本原因是“任务型调用”和“持续会话型调用”被混在了一起。
更合理的拆法应该是:
1)任务型
适合:
- 明确目标
- 可异步完成
- 完成后回报一次结果即可
- 不需要长期上下文线程
例如:
- 修一个 bug
- 写一个小工具
- 改一个脚本
- 跑一次 review
这类就适合直接 ACP spawn 一次任务,平台管理生命周期。
2)会话型
适合:
- 需要多轮交互
- 任务会反复追加约束
- 用户想在同一个 thread/session 里持续推进
- 期间可能中途打断、纠偏、重新设计
这种就该走 thread-bound 的 ACP session,而不是用一层层 callback 和 tmux session 去假装“有持续会话”。
这会极大简化 mental model:
- task 是 task
- session 是 session
- 不再让某个 slash skill 同时扮演二者
三、保留一个极薄的“开发工作流 skill”,但不再负责 agent 调度
我不觉得 ACP 时代就完全不需要 skill 了。
我认为仍然应该保留一个很薄的本地 skill,例如 development-workflow 这种角色:
它不负责启动外部 agent,而负责:
- 判定任务规模(tiny / medium / large)
- 决定是否先做设计
- 约束输出格式
- 明确验证步骤
- 在真正进入 ACP 前先把任务整理清楚
这类 skill 的价值仍然很高,因为它解决的是“如何更稳定地组织开发任务”,而不是“如何把 agent 启动起来”。
也就是说:
- skill 负责方法论和流程约束
- ACP 负责外部 agent 调度与运行时
这是一种更干净的分层。
四、把“启动确认”和“进度跟踪”严格建立在 session 语义上
这也是从过去踩坑里直接得出的经验。
以后只要是 ACP 任务,就应该坚持:
- 没有平台确认,不说 started
- 查进度只认该任务绑定的 session / child session
- 不再拿“最新日志文件”或“最近创建的目录”当近似替代
这听上去像个小流程问题,但它会显著降低代理在多任务并行时的混乱度。
五、把旧 dispatch 时代最有价值的经验迁移到 ACP 时代
这一步很关键,因为迁移不是“推倒重来”,而是“保留有效经验,换掉脆弱底盘”。
我认为至少有这些经验应该保留:
- 所有重要约定写入 memory
- 复杂开发任务先给极简执行框架再开工
- 发布相关工作必须有回归测试闭环
- 用户可见状态必须来自可靠锚点
- 工作目录、结果归属、验证方式都要显式
真正该淘汰的,不是这些工程纪律,而是围绕旧 dispatch 技能堆出来的那层重脚本调度壳。
一个更现实的判断:ACP 不是“更高级”,而是“更省系统维护成本”
很多人聊新能力时,容易把重点放在“更先进”“更智能”。
但如果从这几周 coder 的真实轨迹看,我觉得 ACP 最大的价值甚至不在这里。
它更重要的价值是:
把原本要你自己维护的大量调度基础设施,交还给平台。
而这对一个长期使用的个人开发系统来说,非常关键。
因为真正会拖垮体验的,往往不是大故障,而是大量小故障:
- 某次 callback 配置丢了
- 某次重装后 env 不一致了
- 某次会话名太长了
- 某次 packaging 与 workspace 版本漂移了
- 某次你以为任务开始了,其实并没有
这些事情单看都不严重,但累计起来会不断消耗信心。
ACP 的意义,就是尽量把这类“系统维护税”压低。
结语
回头看 coder 这几周的实践,我并不觉得那几组 dispatch skills 白做了。
恰恰相反,我觉得它们非常有价值,因为正是这些真实使用里的成功与踩坑,才让我看清楚一件事:
我真正需要的不是“把派发 Claude Code 的脚本做得更完美”,而是让 OpenClaw 原生承担外部 agent 编排这件事。
所以如果要给这段经历下一个结论,我会写成这样:
dispatch skills 解决了“先把开发任务扔出去”的问题; ACP 才更适合解决“长期、稳定、可追踪地与外部 coding agents 协作”的问题。
接下来,如果我要继续演进 coder,方向不会是继续堆更复杂的 dispatch skill,而是:
- 用 ACP 作为主调度层
- 用极薄的 workflow skill 约束任务组织方式
- 用 memory 保存长期有效的工程约定
- 用真实聊天链路做最终回归测试
这样重建出来的开发流程,不一定最花哨,但大概率会比旧方案更稳,也更配得上“长期使用”这四个字。