☰
让Claude Code与Codex跨电脑互唤醒:AI agent消息互通实战
2026/10/8 3:44:13 网站建设 项目流程

1. 两个终端智能体各自为战的真实痛点

如果你同时用 Claude Code 和 Codex 干活,大概率经历过这种割裂感:Claude Code 在终端里帮你改代码、跑命令,Codex 在另一个窗口里帮你审代码、补测试,两边各干各的,中间靠你人肉搬运。你复制一段报错丢给 Codex,它给个修复建议,你再粘回 Claude Code 让它执行——这一来一回,效率全耗在"传话"上了。

我最初也是这么用的,直到某天深夜改一个并发 bug,Claude Code 卡在一个死循环里反复试错,而 Codex 明明在另一个窗口里"看"到了同样的日志却完全不知情。那一刻我意识到:这两个工具本质上都是 agent,agent 之间不该靠人当信使。于是就有了这个让它们互相叫醒、互发消息、甚至跨电脑协作的小工具。

这篇内容适合两类人:一是已经在日常开发里同时使用 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-code@machine-a", "to": "codex@machine-b", "type": "task", "payload": "修复 src/db.ts 里的连接泄漏,日志见附件", "reply_to": "1718000001", "ts": 1718000001 }

type字段是关键,我定义了三种:task(新任务)、result(任务结果)、ping(心跳)。agent 只处理发给自己的task和result,ping由转发进程消化。

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/ fi

4.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 发任务给 Codex,Codex 处理完发结果回来,Claude Code 收到结果后又自动发了个"确认收到"的任务给 Codex,Codex 又回了个"好的"……两个 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" done

7.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 done

7.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 能互相叫醒这一件事做扎实,剩下的慢慢加,比一上来就追求大而全要靠谱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询