让Claude Code与Codex跨电脑互唤醒:AI agent消息互通实战
发布时间:2026/10/8 3:44:18 锦皓数字建站

1. 两个终端智能体各自为战的真实痛点如果你同时用 Claude Code 和 Codex 干活大概率经历过这种割裂感Claude Code 在终端里帮你改代码、跑命令Codex 在另一个窗口里帮你审代码、补测试两边各干各的中间靠你人肉搬运。你复制一段报错丢给 Codex它给个修复建议你再粘回 Claude Code 让它执行——这一来一回效率全耗在传话上了。我最初也是这么用的直到某天深夜改一个并发 bugClaude Code 卡在一个死循环里反复试错而 Codex 明明在另一个窗口里看到了同样的日志却完全不知情。那一刻我意识到这两个工具本质上都是 agentagent 之间不该靠人当信使。于是就有了这个让它们互相叫醒、互发消息、甚至跨电脑协作的小工具。这篇内容适合两类人一是已经在日常开发里同时使用 Claude Code 和 Codex 的开发者二是对 AI agent 之间如何通信、如何编排感兴趣的技术人。我会把整个设计思路、核心机制、踩过的坑和可复现的配置都摊开讲不藏私。核心关键词就几个Claude Code、Codex、跨电脑、AI agent 消息互通。读完你至少能明白让两个 agent 互相叫醒这件事难点到底在哪以及怎么用最朴素的方式把它跑通。先说结论这套东西不是什么黑科技本质是一个本地消息总线 唤醒钩子 跨机转发的组合。难点不在通信本身而在于怎么让两个设计理念完全不同的 agent 愿意听对方的话以及怎么在不破坏各自工作流的前提下把消息塞进去。2. 为什么 agent 之间通信比想象中难2.1 两个 agent 的性格差异Claude Code 和 Codex 虽然都是终端里的编码 agent但它们的工作模式差别很大。Claude Code 更偏向我直接动手它会主动读写文件、执行命令、观察结果再决定下一步是一个行动驱动的循环。Codex 则更偏向我给建议、你确认它的输出往往是 patch 或者解释需要外部触发才会真正落地是一个响应驱动的模式。这个差异直接决定了通信设计你不能简单地把 A 的输出塞给 B因为 A 的输出是行动流B 期待的是任务描述反过来 B 的输出是建议A 需要的是可执行指令。所以中间必须有一层消息规范化把一方的产物翻译成另一方听得懂的格式。2.2 唤醒机制才是真正的拦路虎通信好办写个文件、开个 socket 都行。难的是唤醒——怎么让一个正在等待用户输入的 agent在收到外部消息时主动醒过来处理。Claude Code 和 Codex 默认都是交互式的它们的主循环在等你的键盘输入。你没法直接往它的 stdin 里灌数据就指望它响应因为它的输入解析、上下文管理、工具调用都是耦合在一起的。我试过最粗暴的方式用expect脚本模拟键盘输入。结果很惨——agent 的 TUI 会重绘、会吞字符、会在错误时机触发确认框稳定性极差。后来我换了个思路不去打断它的主循环而是给它一个外部任务队列。具体做法是让 agent 在每轮任务结束后主动去检查一个约定好的目录或接口如果有新消息就当作新任务处理。这需要 agent 支持某种形式的钩子或者自定义命令而 Claude Code 和 Codex 恰好都提供了可扩展的入口。2.3 跨电脑带来的额外复杂度单机内通信一个本地 socket 或文件监听就够了。但一旦跨电脑问题立刻升级网络发现、消息可靠投递、身份认证、断线重连每一样都是坑。我一开始想用现成的消息队列但太重了为了两个 agent 通信引入一整套中间件不划算。最后我选了一个极简方案基于 HTTP 的轻量转发 心跳保活。每台机器上跑一个小的转发进程agent 的消息先落到本地转发进程由它负责跨机投递。这样 agent 本身完全不需要知道对面在哪台机器上解耦得很干净。3. 消息总线的核心设计文件队列 唤醒钩子3.1 为什么用文件而不是 socket很多人第一反应是用 socket 或者命名管道做 agent 间通信毕竟实时。但我实测下来文件队列反而是最稳的。原因有三第一文件天然持久化。agent 崩溃重启后未处理的消息还在不会丢。socket 一断缓冲区里的东西就没了。第二文件调试极其方便。出问题时cat一下队列目录消息内容、时间戳、来源一目了然不用抓包。第三文件没有连接状态不存在对面没起来导致发送阻塞的问题写进去就完事谁消费谁负责。代价是实时性略差需要轮询。但对 agent 协作这个场景来说秒级延迟完全可以接受——毕竟 agent 自己跑一个任务就要几十秒。3.2 队列目录的约定结构我给消息队列定了一个非常朴素的目录结构每台机器上都有这么一套~/.agent-bus/ ├── inbox/ # 待处理消息 │ ├── 1718000001-claude.json │ └── 1718000002-codex.json ├── processing/ # 正在处理防止重复消费 ├── done/ # 已处理归档 └── peers.json # 已知的对端机器列表消息文件用时间戳 来源命名保证顺序且不冲突。内容是一个 JSON字段设计得很克制{ from: claude-codemachine-a, to: codexmachine-b, type: task, payload: 修复 src/db.ts 里的连接泄漏日志见附件, reply_to: 1718000001, ts: 1718000001 }type字段是关键我定义了三种task新任务、result任务结果、ping心跳。agent 只处理发给自己的task和resultping由转发进程消化。3.3 唤醒钩子怎么接进 agent这是整个方案里最需要因地制宜的部分。Claude Code 支持通过配置注入自定义的启动脚本和命令别名我利用这一点在它的工作目录里放了一个check-bus脚本并在它的任务收尾阶段触发。Codex 那边类似通过它的配置入口挂一个轮询检查。具体做法是agent 每完成一轮交互就调用一次check-bus这个脚本扫描inbox/里有没有发给自己的消息。有的话把消息内容格式化成 agent 能理解的 prompt通过 agent 支持的非交互模式比如一次性执行模式喂进去。注意不要试图在 agent 正在处理任务时强行插入消息会导致上下文错乱。一定要等它当前轮次结束这是稳定性的底线。我踩过的最大的坑就是贪图实时在 agent 输出到一半时灌消息结果它把新消息和旧上下文混在一起生成了一个完全跑偏的 patch。后来老老实实改成轮次边界检查再没出过问题。4. 让 Claude Code 和 Codex 互相叫醒的具体接法4.1 Claude Code 侧的接入细节Claude Code 的接入我走了两条路最后选了第二条。第一条路是改它的启动包装脚本在进入交互模式前先跑一次check-bus把积压的消息作为初始上下文。这条路的缺点是只能处理启动时的消息运行中收到的还是得等下次启动。第二条路更彻底利用 Claude Code 可以执行终端命令的能力在它的工作流里注册一个空闲检查动作。当它完成一个任务、准备等待下一个输入时先执行check-bus。如果队列非空就把消息当作新的用户输入注入。这个注入不是模拟键盘而是通过它支持的程序化输入接口——把消息写到一个约定的输入文件agent 在等待输入时会优先读取这个文件。实测下来这个方式的延迟在 1-2 秒完全够用而且不会干扰 TUI 渲染。4.2 Codex 侧的接入细节Codex 的接入思路类似但它的非交互模式更成熟所以我直接用了它的批处理调用。转发进程发现inbox/里有给 Codex 的消息时直接以非交互方式拉起一个 Codex 实例把消息作为任务描述传进去拿到结果后再写回队列。这样做的好处是 Codex 完全不需要常驻按需唤醒资源占用低。坏处是每次唤醒都有冷启动开销大概 3-5 秒。对于审代码补测试这类任务这个开销可以接受。# 转发进程里唤醒 Codex 的核心逻辑伪代码 if [ -f $msg_file ]; then payload$(jq -r .payload $msg_file) codex exec --prompt $payload --output $result_file mv $msg_file done/ fi4.3 双向唤醒的闭环怎么形成单向往返还不够我要的是闭环Claude Code 干完活把结果发给 Codex 审Codex 审完给出修改意见再发回 Claude Code 执行。这个闭环的关键是reply_to字段——每条消息都记录它回应的是哪条这样即使多个任务并发也不会串线。我设计了一个简单的状态机task发出后进入pending收到对应result后转done。如果超过设定时间没收到结果转发进程会重发一次最多三次之后标记为timeout并通知发起方。这套重试机制在跨电脑场景下特别重要因为网络抖动是常态。5. 跨电脑通信从本机到多机的平滑扩展5.1 转发进程的职责边界跨电脑的核心是每台机器上那个转发进程。它的职责非常明确就三件事监听本地inbox/、把发往远程的消息投递出去、把远程发来的消息落到本地inbox/。它不解析消息内容不做业务逻辑纯粹是个搬运工。这种薄转发设计的好处是agent 侧完全感知不到跨机的存在。对 Claude Code 来说它永远只是往本地队列写、从本地队列读至于对面是同一台机器还是地球另一端它不关心。5.2 对端发现与心跳peers.json里维护着已知的对端列表每条记录包含机器标识、地址、最后心跳时间。转发进程每隔 30 秒向所有对端发一次ping收到pong就更新心跳时间。超过 90 秒没心跳的对端标记为unreachable发往它的消息暂存本地等它恢复后补投。{ peers: [ {id: machine-b, addr: http://192.168.1.20:8787, last_seen: 1718000000}, {id: machine-c, addr: http://192.168.1.30:8787, last_seen: 1717999900} ] }提示地址配置建议走内网或者你信任的私有网络别把转发端口暴露到公网。这套东西没有做复杂的鉴权安全边界靠网络隔离来保证。5.3 消息可靠投递的取舍跨机投递我选了至少一次语义而不是精确一次。原因很简单agent 任务大多是幂等的重复执行一次最多浪费点算力但丢消息会导致任务卡死。所以转发进程在收到对端ack之前会一直保留消息副本超时重投。这个取舍在实际使用中很关键。我一开始追求精确一次搞了一套复杂的去重和确认机制结果代码复杂度飙升还引入了新的 bug。后来想通了——对 agent 协作来说可靠性比精确性重要得多果断降级到至少一次世界清净了。6. 实测中暴露的问题与修复过程6.1 消息风暴两个 agent 互相触发死循环上线第一天就翻车了。Claude Code 发任务给 CodexCodex 处理完发结果回来Claude Code 收到结果后又自动发了个确认收到的任务给 CodexCodex 又回了个好的……两个 agent 你来我往几分钟刷了几百条消息token 烧得飞快。根因是我没区分任务消息和确认消息。修复方案是引入type: ack类型确认消息不触发新的任务循环只更新状态。同时加了一个跳数限制一条消息链最多往返 5 次超过就强制终止并告警。这个限制救了我好几次。6.2 上下文污染agent 把别人的任务当自己的有次 Claude Code 收到了本该给 Codex 的消息原因是我的to字段匹配写成了前缀匹配codex和codex-helper撞了。结果 Claude Code 拿着 Codex 的任务描述一顿操作改错了文件。修复很简单to字段改成精确匹配并且消息里带上目标 agent 的完整标识。这个坑提醒我在多 agent 环境里身份标识必须唯一且精确任何模糊匹配都是定时炸弹。6.3 跨机时钟不同步导致的顺序错乱跨电脑场景下两台机器的系统时钟可能有偏差。我用时间戳命名消息文件结果机器 A 的时间比机器 B 快了几秒导致消息顺序在合并时错乱一个结果消息排在了它对应的任务消息前面。修复方案是不依赖绝对时间排序改用reply_to字段建立因果关系。消息处理时先看依赖有依赖的消息等依赖完成后再处理。时间戳只用来做归档命名不参与逻辑排序。6.4 冷启动超时Codex 唤醒失败Codex 按需唤醒在机器负载高的时候经常超时5 秒的冷启动变成 20 秒转发进程以为失败了就重投结果同时拉起好几个 Codex 实例机器直接卡死。修复是加了一个唤醒锁同一时刻只允许一个 Codex 实例被拉起后来的消息排队等待。同时把超时时间从 5 秒放宽到 30 秒给冷启动留足余量。7. 几个能直接抄的配置与脚本7.1 转发进程的最小实现下面这个脚本是转发进程的核心用 bash curl 就能跑不需要额外依赖#!/bin/bash # agent-bus-forwarder.sh BUS_DIR$HOME/.agent-bus PEERS_FILE$BUS_DIR/peers.json # 1. 把本地 inbox 里发往远程的消息投递出去 for msg in $BUS_DIR/inbox/*.json; do [ -e $msg ] || continue to$(jq -r .to $msg) target$(jq -r --arg t $to .peers[] | select(.id ($t | split()[1])) | .addr $PEERS_FILE) if [ -n $target ]; then curl -s -X POST $target/inbox -d $msg mv $msg $BUS_DIR/done/ fi done # 2. 心跳 for peer in $(jq -r .peers[].addr $PEERS_FILE); do curl -s -m 3 $peer/ping /dev/null || echo peer $peer unreachable done7.2 agent 侧的检查脚本agent 每轮结束调用的检查脚本逻辑就是扫队列、格式化、注入#!/bin/bash # check-bus.sh agent-id AGENT_ID$1 BUS_DIR$HOME/.agent-bus for msg in $BUS_DIR/inbox/*.json; do [ -e $msg ] || continue to$(jq -r .to $msg) if [ $to $AGENT_ID ]; then payload$(jq -r .payload $msg) echo $payload $BUS_DIR/agent-input.txt mv $msg $BUS_DIR/processing/ fi done7.3 关键参数速查表参数建议值说明心跳间隔30s太短浪费资源太长发现故障慢心跳超时90s三次心跳丢失判定不可达消息重试次数3超过则标记 timeout重试间隔10s给对端恢复留时间任务链最大跳数5防止 agent 互相触发死循环Codex 冷启动超时30s高负载下留足余量轮询间隔2sagent 检查队列的频率8. 这套方案还能怎么扩展跑通基础版之后我陆续加了几个实用的扩展。第一个是任务优先级消息里加priority字段高优先级的插队处理避免紧急修复被一堆低优先级任务堵住。第二个是结果归档检索所有done/里的消息按日期归档需要回溯上周那个 bug 是谁修的时直接 grep比翻聊天记录快多了。第三个扩展我觉得最有价值引入第三个 agent 做仲裁。当 Claude Code 和 Codex 对同一个问题给出冲突方案时转发进程可以把两个方案都发给第三个 agent比如另一个 Codex 实例或者别的模型让它判断哪个更合理。这就从两方通信升级成了多方协作虽然复杂度上去了但处理复杂任务时确实更靠谱。最后一个心得别过度设计。我一开始想搞一套完整的 agent 编排框架结果写了三千行代码还没跑通。后来砍到只剩文件队列 转发 钩子两百行就稳定运行了。agent 协作这件事简单可靠比功能齐全重要得多。你先把两个 agent 能互相叫醒这一件事做扎实剩下的慢慢加比一上来就追求大而全要靠谱。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。