多 Agent 对证循环协作架构:Hermes + Claude Code + Codex 三角色工作流实战 一套让 AI Agent 自己写代码、自己审查、自己归档的自动化协作协议一、背景为什么要做这个单个 AI Agent 编写代码时缺少审查环节写完直接提交逻辑漏洞、安全隐患和边界情况处理完全依赖其自身难免疏漏。很自然的想法是引入两个 Agent 协作一个负责编码一个负责审查。但这里存在一个核心矛盾如果让同一个 Agent 既写代码又自审由于认知偏差它无法有效审查自己的工作如果人工在两个窗口之间拷贝代码又会丧失自动化的意义。这套架构给出的答案三次提交、两个 Agent、零人工介入。角色做什么工具Hermes拆需求 → 创建工作间 → 归档Hermes AgentCC (Claude Code)写代码、commitClaude Code CLICodex (Codex CLI)审查代码、commitOpenAI Codex CLI二、架构总览整个系统分为两层text┌─────────────────────────────────────────────────┐ │ 内容层skills/ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Hermes │ │ CC │ │ Codex │ │ │ │ skills │ │ skills │ │ skills │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ 谁做什么、不做什么、怎么提交 │ ├─────────────────────────────────────────────────┤ │ 稳定层scripts/ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ git hook │→│ route- │→│ inject │ │ │ │ │ │ agent │ │ target │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ 路由硬编码一次写好不动 │ └─────────────────────────────────────────────────┘核心原则稳定层与内容层分离为什么要这样分层Agent 推理并不可靠——会遗漏步骤、跳转逻辑甚至产生幻觉。如果把流转逻辑写在技能文件里让 Agent 自行推理每次执行都可能出现不同的行为。因此我们将路由从技能中剥离写死在稳定层hook → route-agent → inject。Agent 只需回答“我是谁、我要干什么、我不能干什么”不参与流程决策。纯技能方案分层方案流转逻辑写在技能里依赖 Agent 推理路由硬编码在 route-agentAgent 可能遗忘或跳过步骤hook 代理机制不会遗漏一个文件混入身份、流程和路由skill 只描述身份和能力边界三、角色定义Hermes — 规划者 归档者Hermes 是交互式 CLI也就是你正在使用的这个 Agent负责三件事Plan拆需求接收大任务分析需求拆分成独立的工作间workspaceCreate-workspace搭建创建目录、编写 STATUS.md、建立 Git 分支Archive归档在 CC↔Codex 循环结束后将最终成果写入存档Hermes 不介入 CC↔Codex 的编码与审查循环。CCClaude Code— 写代码的人读取 STATUS.md 了解任务与约束编写代码执行git commit提交信息格式[CC-WSxxx] 做了什么更新 STATUS.md 状态为waiting-review如果接到 REVIEW-FAIL修复后重新提交CC不负责审查代码、归档结果也不调度其他 Agent。CodexCodex CLI— 审查代码的人读取 STATUS.md git diff了解变更内容审查功能完整性、逻辑正确性、安全性、约束满足情况给出结论REVIEW-PASS → 状态改为completedREVIEW-FAIL → 状态改为waiting-fix并写明具体问题Codex不编写业务代码可进行少量修补不执行归档也不创建工作间。四、工作间生命周期完整流程textHermes: 大任务 → ws001 / ws002 / ws003 │ ├── create-workspace │ ├── 建目录 ~/.project-archive/projects/项目/workspaces/wsxxx/ │ ├── 写 STATUS.md任务 约束 状态 created │ └── git checkout -b ws/ws001-xxx │ ├── CC 开始写代码 │ ├── 状态 in-progress │ ├── 写代码 → git commit -m [CC-WS001] 实现JWT │ └── 状态 waiting-review │ ├── [git hook 触发 → route-agent → 注入 Codex 窗口] │ ├── Codex 审查 │ ├── 状态 in-progress │ ├── git diff HEAD~1 HEAD │ ├── REVIEW-FAIL token过期校验缺失 │ │ ├── git commit -m [Codex-WS001] REVIEW-FAIL: ... │ │ ├── 状态 waiting-fix │ │ └── [hook 触发 → 注入 CC 窗口 → 回到修复循环] │ │ │ └── REVIEW-PASS 审查通过 │ ├── git commit -m [Codex-WS001] REVIEW-PASS │ └── 状态 completed │ └── [hook 触发 → 通知 Hermes 归档] └── Archive ├── 读 STATUS.md 迭代记录 ├── git log --oneline / git diff --stat └── 写归档文件到 archive/YYYY-MM-DD--feat--xxx.md五、STATUS.md — 工作间通信中枢CC 和 Codex不直接对话只读取 STATUS.md 中的「状态」字段来决定行动路由工作由 route-agent 负责。text# ws001 — JWT 认证 ## 任务 实现 JWT 登录签发 ## 约束 使用 pyjwttoken 有效期 24h ## 状态 in-progress / waiting-review / waiting-fix / completed ## 迭代记录 | 轮次 | 提交者 | 结果 | 详情 | |------|--------|--------------|--------------------------| | 1 | CC | 完成 | 实现 JWT 登录签发 | | 2 | Codex | REVIEW-FAIL | token 过期校验缺失 | | 3 | CC | 完成 | 修复: 增加过期校验 | | 4 | Codex | REVIEW-PASS | 审查通过 |状态流转图textcreated → in-progress → waiting-review → waiting-fix → in-progress → waiting-review → completed ↑ ↑ CC 开始写 ↑ CC commit ↑ Codex FAIL ↑ CC 修复 ↑ CC commit ↑ Codex PASS | Hermes 初始话筒规则很简单状态字段是唯一的指示器谁看到状态就知道该谁干活。六、路由机制——事件驱动不依赖推理最精巧的设计在于整个 CC↔Codex 循环完全不依赖任何 Agent 的推理能力。textCC commit -m [CC-WS001]... │ ▼ post-commit hook.git/hooks/post-commit │ ▼ curl POST /event → route-agentHTTP 后台服务 │ ▼ route-agent 硬编码路由决策 [CC-WSxxx] → 找 Codex 窗口 → inject-target.ps1 → Codex 终端 REVIEW-FAIL → 找 CC 窗口 → inject-target.ps1 → CC 终端 REVIEW-PASS → 找 Hermes 窗口 → 注入归档提示hook 不做任何逻辑处理只负责发送 curl100ms不影响 Git 体验所有路由逻辑集中由 route-agent.py 处理。七、关键技术细节7.1 终端注入方案把消息“打”进另一个终端窗口比看上去复杂得多。经历了三次方案迭代方案结果原因SendInput❌ 失败prompt_toolkit 不走 Console APIWriteConsoleInput❌ 失败Windows Terminal 不是传统控制台AttachThreadInput keybd_event✅ 成功绕过前台限制模拟键盘硬件事件最终方案AttachThreadInput将当前线程附加到目标窗口线程绕过前台限制设置剪贴板文本keybd_event发送 CtrlV粘贴keybd_event发送 Enter提交分离线程并发注入通过命名互斥量Global\HermesInjectMutex排队超时 60s 保护。7.2 HWND 管理Claude Code 启动后会将窗口标题自行更改为✳ Claude Code覆盖wt.exe --title参数因此无法依靠标题查找窗口。应对策略启动时捕获后续直接读取textspawn-agent.py 创建窗口 → find-window.ps1UIAutomation捕获 HWND → 保存至 ~/.hermes/hwnd-{name}-{workspace}.txt → POST /register 到 route-agent 后续注入route-agent 收到事件后 → 从 ~/.hermes/hwnd-{target}-{workspace}.txt 读取 HWND → inject-target.ps1 -HWND hwnd -Message prompt → 背景注入不切换焦点7.3 Windows Terminal 启动命令的坑不要用powershell -Command exe——PowerShell 的语法在 Windows Terminal 的命令解析中会被当作可执行路径名导致0x80070002错误。正确写法textwt.exe new-tab --title claude - ws001 -- cmd /k cd /d project claude --permission-mode bypassPermissions八、文件清单脚本层所有脚本位于project-archive/scripts/文件作用route-agent.pyHTTP 代理后台服务仪表盘localhost:8765spawn-agent.py创建 Agent 终端窗口并注册 HWNDinject-target.ps1通用注入脚本HWND 注入 剪贴板 互斥量find-window.ps1UIAutomation 窗口查找post-commit.pygit hook 脚本curl POST /eventinstall-hook.sh将 hook 安装到项目start-task.sh启动入口hermes-archive-prompt.txt归档提示文本技能文件位于project-archive/skills/文件作用hermes/hermes-plan.md拆需求规范hermes/hermes-create-workspace.md创建工作间规范hermes/hermes-archive.md归档规范hermes/hermes-recall.md历史检索规范claude-code/SKILL.mdCC 开发规范codex/SKILL.mdCodex 审查规范workspace.mdSTATUS.md 格式规范九、实测验证已验证的链路spawn → 捕获 HWND → 注入 hello✅spawn-agent.py 创建 CC 窗口find-window.ps1 捕获 HWND如5507670保存至~/.hermes/hwnd-CC-ws001.txtinject-target.ps1 通过 HWND 直接注入成功后台注入✅AttachThreadInput 背景注入不激活窗口窗口收到消息并正常响应并发排队✅3 个注入同时启动命名互斥量排队顺序执行待验证CC → Codex 完整对证循环commit → hook → route-agent → inject → Codex 窗口Codex REVIEW-FAIL → CC 修复循环Codex REVIEW-PASS → Hermes 归档十、Pitfalls 与教训Windows 平台特殊问题PS1 文件必须使用 CRLF 行尾——PowerShell 5.1 对纯 LF 解析会报MissingCatchOrFinallyAdd-Type 的定界符必须在列 0——不能有缩进PowerShell 函数不能在 if/else 内定义——必须写在脚本顶层MSYS bash 向 PowerShell 传参——注意/路径解析使用临时 JSON 文件避免 UTF-8 编码问题架构层面STATUS.md 是唯一的通信渠道——CC 和 Codex 不应通过其他方式沟通hook 只做 curl不处理逻辑——所有路由由 route-agent 统一处理route-agent 需要后台常驻——仪表盘位于localhost:8765便于调试Agent 启动后必须 POST /register——否则 route-agent 不知道注入目标不要使用 --resume——每次 spawn 都创建新的交互式会话进入项目目录后再启动十一、总结这套架构的核心设计决策可归纳为三句话路由硬编码不走 Agent 推理——hook → route-agent → inject 构成的稳定层固定不变Agent 技能不参与流程决策STATUS.md 即为话筒——状态字段是唯一指示器谁看到状态就知道该谁行动Hermes 负责头尾不介入中间循环——Plan → Create-workspace → Archive中间的 CC↔Codex 循环自主运转剩下的部分全部是机械执行Claude Code 写代码Codex 审查git hook 触发route-agent 路由注入脚本传递消息——没有人为判断没有 Agent 推理每一步都确定无疑。