问题:agent 都在电脑上,我在外面
Herdr 是一个给 coding agent 用的终端工作区管理器。它把 Claude Code、Codex、pi 这些交互式 agent 各放在一个 pane 里常驻,能识别它们是 idle、working 还是 blocked,并且暴露一套 herdr CLI 来操作它们。我的本机 Herdr 里长期挂着四个:一个 Claude Code、两个 Codex、一个跑便宜模型的 pi;Mac mini 上还有两个。
在电脑前,这套东西很顺手。出了门就只剩手机,问题来了:
- Claude Code 自带的 Remote Control 只覆盖 Claude 会话,Codex 和 pi 管不到。
- SSH 客户端加 Tailscale 能连上,但在手机上盯终端、切 pane、打命令,体验很差。
- 我真正想要的是:在手机上说一句「让 Codex 把测试跑一遍」,然后等结果,而不是远程操作一个终端。
飞书是我手机上常驻的入口,Larkway 已经把本机 agent 接进了飞书。于是问题变成:能不能让 Larkway 里的 agent 去操作 Herdr 里的 agent?
思路:不加后端,让 agent 学会用工具
第一反应是给 Larkway 加一个 herdr backend,把飞书话题和 Herdr pane 一一绑定。这条路能做,但把用法写死了:一个 bot 只能对应一个 pane,Herdr 里 agent 增减都要改配置。
最后选的是另一条路:Larkway 里的 agent 本来就能跑 Bash,那就让它学会 herdr CLI。它自己去发现现在有哪些 agent、谁有空、在哪个目录,自己决定派给谁,自己等、自己取结果。Larkway 一行代码不改,Herdr 里的成员可以随时变。
为此我把一个 Larkway bot 重新定义成「管家」:理解需求和目标,拆解任务,简单的自己做,其余通过 herdr CLI 派给 Herdr 里的 agent,跟进后汇总回报。它的角色,其实和你在电脑前开着的那个 Claude Code 主对话一样,只是入口换成了飞书。
落地:三处配置,零代码
Larkway 的 bot 子进程和 Herdr server 在同一台机器上,走本地 socket 就能通。真正要处理的是三件小事:
装 herdr skill。Herdr 自带一份给 agent 读的 skill,用
herdr --skill就能打出来。放到~/.agents/skills/herdr/SKILL.md,Claude Code 和 Codex 两种后端都能读到。绕过 skill 的守卫。官方 skill 第一步会检查
HERDR_ENV=1,不满足就要求 agent「说明自己不在 Herdr 里并停止」。Larkway 的 bot 不在 pane 里,但 CLI 从外面调完全正常。在~/.larkway/.env里加一行HERDR_ENV=1,bridge 会透传给子进程。放行命令。Larkway 给 Claude 后端的 bot 用的是 acceptEdits 权限模式,Bash 命令靠白名单。在
~/.larkway/config.json的permissions.allowExtra里加Bash(herdr *),要操作另一台机器再加一条Bash(ssh <host> *)。
# 1. skill $ mkdir -p ~/.agents/skills/herdr && herdr --skill > ~/.agents/skills/herdr/SKILL.md # 2. 守卫变量(bridge 透传给 bot 子进程) $ echo 'HERDR_ENV=1' >> ~/.larkway/.env # 3. 放行(~/.larkway/config.json) "permissions": { "allowExtra": ["Bash(herdr *)", "Bash(ssh mac-mini *)"] } $ larkway stop && larkway start
然后在管家 bot 的职能 memory 里写清角色和原则:你在 Herdr 外面当调度员;每次先 herdr agent list 发现现在有谁;挑人看状态、目录、类型;派出去的任务要自包含;working 的不打断,blocked 的把问题带回来。
实录一:先证明链路是通的
第一条测试故意很小:让 bot 去问 Herdr 里的 pi「1+1 等于几」。
1. herdr agent list → work-pi 状态 idle
2. herdr agent prompt work-pi "1+1 等于几,用一句话回答" --wait
3. herdr agent read work-pi → work-pi 的回答:「1+1 等于 2。」
与此同时,电脑上 Herdr 那个 pi 的 pane 里,能看到这一问一答真实发生了。链路通了。
实录二:管家自己挑人
第二条不指定 agent,考它「发现、挑人、派活、回报」整套动作:看看 Herdr 里有谁可用,挑一个最轻量的问「今天星期几」,说明挑人的理由。
当前 Herdr 里的 agent(4 个,全部 idle)
| 名字 | 类型 | 状态 |
|---|---|---|
| work-claude | claude | idle |
| work-codex | codex | idle |
| work-codex-2 | codex | idle |
| work-pi | pi | idle |
挑人理由:选 work-pi。这是一句话问答,零代码、零项目上下文,按选人原则该给 pi,把 Claude / Codex 两类重型编码 agent 留着跑真活。
一个小插曲:第一次派活我写了「不要执行任何命令」,pi 回「我无法凭空判断今天星期几」。第二次允许它跑 date 后正确答出星期六。结论:问 agent 时间类问题,必须允许它查系统时间。
最后那段「小插曲」是它自己发现并主动写进回报的,之后还追加到了自己的知识库里。这正是我想要的:skill 给基础用法,agent 在使用中把坑记下来,下次自己避开。
实录三:派给 Codex,读文件取证
第三条是真正的派活:让一个空闲的 Codex 在临时目录写一个 hello.py(打印时间和 Python 版本)并运行,把输出原文回报,并说明结果从哪取证。
派给了 Herdr 里的 work-codex(Codex agent),它写好并运行了 /tmp/herdr-out/hello.py,输出为 2026-09-06 00:41:16 的时间戳和 Python 3.11.4 版本信息,exit 0。
证据来自它按约定写的结论文件 /tmp/herdr-out/work-codex-20260906-004050.md,我在本机独立重跑一次结果一致。
Codex 那边留下的结论文件长这样:源码、运行命令、输出原文、exit code,一样不少。管家读的是这个文件,不是抓屏。
# hello.py ## 运行输出原文 2026-09-06 00:41:16.087130 3.11.4 (main, Jul 25 2023, 17:07:07) [Clang 14.0.3 ...] ## exit code 0
整轮从 @ 到卡片回来不到一分钟。
踩到的坑
1. 等待状态:done 不是 idle
第一版 memory 让它用 --wait --until idle。结果 pi 跑完后 Herdr 把状态标成 done(后台完成、UI 没看过),wait 一直等不到 idle,直到 Bash 工具 2 分钟超时。正确用法是 --wait 不加 --until:默认就等 idle、done、blocked 三个状态中的任一个。另外要把 Bash 工具的超时参数调到 10 分钟,不然超时的是 bot 自己。
2. bot 自己重启 bridge,把自己杀了
中途我让管家升级本机 Larkway。它装完新版,问我要不要重启 bridge,我选了「后台延迟重启」。它执行 larkway stop 的瞬间,bridge 连同它自己的进程一起被杀,后面的 larkway start 永远跑不到,四个 bot 离线了几分钟,最后是我手动起的。
所有会影响 bridge、Herdr server、网络的操作,都不该经过这条链路。写进硬规则:bot 不重启自己的 bridge,要重启就把命令给人自己跑。
3. 二手信息会「合理化」
bridge 恢复后,管家自己挂的闹钟触发了,它跑去确认版本,回报里写道「重启脚本撞上了 launchd 竞争,launchd 自己把新进程拉起来了」。结果是对的,原因是编的。它没有证据,但给出了一个听起来合理的解释。这条链路里 agent 转述 agent,每一层都可能加一点自己的推断,所以后来把「取证来源」写成了硬规则。
4. 抓屏当返回值靠不住
herdr agent read 读到的是渲染后的终端文本:状态栏、进度条、输入框、上一轮残留都混在一起。短回答还行,长任务的输出基本没法用。agent 跑在 alternate screen 上时,旧行连 scrollback 都进不去,加大 --lines 也读不回来。
沉淀:herdr-operator skill
把以上全部收进一份 skill,视角是「我在 Herdr 外面调度一群 agent」,放在 ~/.agents/skills/herdr-operator/。管家和备用操作员的 memory 都只指向它,不再各写一遍命令。要点:
- 发现:每次先
herdr agent list,关心 name、agent 类型、agent_status、cwd 四个字段。另一台机器就ssh <host> herdr agent list,命令一模一样。 - 挑人:只挑 idle / done;任务涉及项目就优先 cwd 已在那个项目里的;改代码给 Claude 或 Codex,轻问答给 pi;任务独立就并行派。
- 派活:
herdr agent prompt <name> "<自包含任务>" --wait --timeout 590000。任务文本里必须带目标、约束、目录、期望产出,以及交付约定。 - 交付约定:让目标 agent 把最终结论写到
/tmp/herdr-out/<name>-<时间戳>.md,最后一行只输出路径。 - 取证优先级:它写的文件 → 会话记录原文(Claude Code 的 jsonl 可以从 Herdr 给的 session id 直接定位;Codex 只能按 cwd 和时间就近匹配;pi 没有本地记录)→ 抓屏。汇报时标明来源。
- blocked:先
agent read看清它在问什么。老板已授权的确认用send-keys回答;开放问题贴回话题让人决定。 - 硬规则:不
herdr server stop、不 kill、不关别人的 pane、不重启自己的 bridge、working 的不打断、结论只来自取证。
这条路适合什么,不适合什么
跑了一晚上,我的判断是:它是一个异步派单系统,不是远程终端。
| 场景 | 推荐 | 为什么 |
|---|---|---|
| 人在外面,给一个清楚的目标,等结果 | 飞书 → Larkway → Herdr | 覆盖所有 agent 类型,不用装 SSH 客户端,共享电脑上的会话,回来能接着看。 |
| 只用 Claude,要原生流式体验和权限提示 | Claude Code Remote Control | 结构化、实时,但只覆盖 Claude。 |
| 要盯着屏幕边聊边改 | SSH + Tailscale | agent 弹选项、要确认的交互场景,一个来回经过飞书要一两分钟,体验会断。 |
三条不冲突。日常派活走飞书,Claude 深聊用 Remote Control,真要盯屏就开 SSH。
为什么坚持「让 agent 学工具」
做完回头看,这次最关键的决定是没有给 Larkway 写 herdr 后端。管道式的方案会更「稳」,但每次 Herdr 里多一个 agent、换一个项目目录、要并行派两个,都得改配置或改代码。让 agent 学会工具之后,这些都是它自己判断的事:它会看谁有空、谁在哪个目录、哪个便宜,也会在遇到坑之后把经验记下来。
Larkway 一直的定位就是薄通道:bridge 只做 agent 做不了的事(飞书长连、卡片、进程管理),流程和判断交给 agent。这次只是把「工具」从 git 和 MR 换成了另一群 agent。
想自己搭一套
- 装 Herdr,把你常用的 agent 起在 pane 里并命名(
herdr agent rename)。 - 装 Larkway,建一个 bot,跑
larkway start。 - 按上面三处配置:skill、
HERDR_ENV=1、allowExtra。 - 给 bot 的 memory 写清角色(调度员,不是执行者),命令用法交给 skill。
- 在群里 @ 它,从一句「问问 pi 1+1 等于几」开始。
有问题欢迎来 交流群 聊。