尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

openrig:用YAML+tmux编排Claude Code与Codex的AI编程工作台

openrig:用YAML+tmux编排Claude Code与Codex的AI编程工作台 1. 从 openrig 这个名字说起它到底想解决什么问题第一次看到openrig这个词我脑子里蹦出来的不是某个具体工具而是一种把散装零件拼成一台能跑的机器的画面。rig 在英文里本来就有装配、搭建的意思比如把一台电脑的各个部件装起来叫 rig把一套实验设备搭起来也叫 rig。前面加个 open意思就很明确了这是一套开放的、可组装的配置骨架用来把 Claude Code、Codex 这类命令行 AI 编程助手和 YAML 配置、tmux 会话管理这些东西串成一条顺手的流水线。我之所以对这个方向特别有感触是因为过去大半年我几乎每天都在和 Claude Code、Codex 这两个工具打交道。刚开始用的时候我的工作流是这样的打开一个终端敲claude聊几句关掉再开一个终端敲codex聊几句关掉。项目一多配置文件散落在各个角落~/.claude、~/.codex、项目根目录下的各种 yaml改一个参数要翻半天。更别提有时候想让两个工具同时跑、互相参考对方的输出结果终端窗口开了一堆自己都分不清哪个是哪个。openrig要解决的恰恰就是这个散的问题。它不是某个具体的软件包而更像一套约定俗成的组织方式用 YAML 描述配置用 tmux 管理会话把 Claude Code 和 Codex 的启动参数、模型接入、上下文策略统一收拢到一份可版本控制的文件里。你换一台机器把这份 rig 拉下来几条命令就能还原出几乎一模一样的工作环境。这篇文章适合谁看如果你已经在用 Claude Code 或 Codex但每次配置都靠记忆和手敲那这篇能帮你把流程固化下来如果你还没入门只是想搞清楚这两个工具和 YAML、tmux 之间到底怎么配合那这篇也能给你一条清晰的路径。我会尽量把每一步的为什么讲透而不是甩一堆命令让你照抄。提示本文提到的所有配置思路都基于公开的通用实践具体参数请以你本地实际安装的版本为准。工具迭代很快遇到对不上的地方优先看官方文档。2. 为什么是 YAML tmux 这套组合而不是别的2.1 YAML 承担的是声明不是执行很多人第一次接触 YAML 是在写 CI 流水线或者 Docker Compose 的时候潜意识里觉得它就是个配置文件格式。这个理解没错但不够准确。YAML 真正的价值在于它把我想要什么状态和怎么达到这个状态分开了。你写model: claude-sonnet你声明的是意图至于这个意图怎么被解析、怎么被传给底层那是工具的事。放到 openrig 这个场景里YAML 要声明的东西大概有这么几类。第一类是模型接入信息比如你用的是官方端点还是本地模型端点地址、模型名称、超时时间这些。第二类是会话行为比如是否开启自动上下文压缩、历史保留多少轮、是否允许工具调用。第三类是项目级覆盖不同项目可能有不同的系统提示词、不同的工作目录、不同的忽略规则。我见过不少人图省事把这些东西直接写死在 shell 的 alias 里比如alias ccclaude --model xxx --append-system-prompt ...。短时间看没问题但一旦你要维护三五个项目alias 就会变成一坨谁也不敢动的意大利面。YAML 的好处是它是数据不是代码你可以用脚本去生成它、校验它、diff 它甚至让 AI 帮你改它。2.2 tmux 解决的是会话持久化和多路复用tmux 这个东西老运维和服务器玩家都很熟但很多做 AI 编程的朋友反而没怎么用过。它的核心能力有两个一是会话可以脱离终端窗口独立存在你关掉 SSH、关掉终端里面的进程照样跑二是可以在一个窗口里切分出多个面板同时看多个东西。这两个能力放到 AI 编程场景里简直是量身定做。你想想Claude Code 跑一个长任务可能要读几十个文件、改十几处代码中间你去泡杯咖啡回来发现终端被误关了上下文全没了那种崩溃感谁经历谁知道。用 tmux 起一个会话tmux new -s work然后在里面跑 Claude Code你随时可以tmux detach离开回来tmux attach -t work接着看进程一直在。多路复用就更实用了。我常用的布局是左边一个大面板跑 Claude Code 做主力开发右边上下分两个小面板上面跑 Codex 做代码审查或者写测试下面留一个 shell 用来跑构建和测试命令。三个东西在同一个 tmux 会话里切换用快捷键比开三个终端窗口清爽太多。2.3 为什么不是别的方案有人会问用 VS Code 的集成终端不行吗用 screen 不行吗用 systemd 或者 nohup 不行吗VS Code 集成终端的问题在于它和编辑器窗口绑定你关掉 VS Code 它就没了而且多面板管理不如 tmux 灵活。screen 是老前辈功能上够用但配置语法和快捷键都不如 tmux 现代社区活跃度也差一截。nohup 只能解决后台跑解决不了多路复用和随时切回去看。至于 systemd那是给守护进程用的交互式会话用它是杀鸡用牛刀。所以 openrig 选 YAML tmux 这套组合本质上是选了声明式配置加持久化多路复用这两个最贴合 AI 编程工作流的特性。这不是拍脑袋是踩过坑之后的最优解。3. 把 openrig 的目录结构搭起来3.1 一份可版本控制的骨架长什么样我自己的 openrig 目录大概是这样组织的你可以根据自己的习惯调整但核心思路是分层全局默认、工具专属、项目覆盖三层各管各的。openrig/ ├── README.md ├── global/ │ ├── claude.yaml │ ├── codex.yaml │ └── tmux.conf ├── projects/ │ ├── project-a/ │ │ ├── claude.yaml │ │ └── codex.yaml │ └── project-b/ │ └── claude.yaml └── scripts/ ├── rig-up.sh └── rig-down.shglobal/放的是所有项目通用的默认值比如你常用的模型、通用的系统提示词、tmux 的快捷键绑定。projects/下面每个子目录对应一个具体项目只放这个项目需要覆盖的字段。scripts/里放两个入口脚本一个负责把配置组装起来并启动 tmux 会话一个负责清理。这种分层的好处是改全局默认只动一个文件改项目特例只动对应目录不会互相污染。而且整个目录可以直接丢进 git换机器 clone 下来就能用。3.2 全局配置里该放什么不该放什么全局claude.yaml我一般会放这些字段model: claude-sonnet timeout: 120 max_tokens: 8192 system_prompt_file: ./prompts/default.md auto_compact: true compact_threshold: 0.8 tools: allow: - read_file - write_file - run_command deny: - delete_file这里有几个点值得展开说。auto_compact和compact_threshold是控制上下文压缩的阈值设成 0.8 意思是当上下文用到 80% 的时候自动触发压缩。这个值我试过 0.6 到 0.9 之间太低会导致频繁压缩、丢失细节太高又容易在关键时刻爆掉0.8 是我用下来比较平衡的点。tools里的 allow 和 deny 是权限控制。我强烈建议在全局层面就把delete_file这类危险操作禁掉需要的时候在项目级单独放开。这不是不信任 AI而是给自己留一道保险。我有个朋友就是没设这个AI 在重构的时候顺手删了一个他还没提交的目录虽然最后从 git 找回来了但那半小时的心跳是真的。全局codex.yaml结构类似但 Codex 的配置项和 Claude Code 不完全一样比如它更强调approval_mode和sandbox这类安全相关的设置。我一般会把approval_mode设成on-request也就是关键操作要人工确认避免它在没看清楚的情况下乱改。3.3 项目级覆盖的写法项目级的 yaml 不需要写全只写要覆盖的字段就行。比如 project-a 需要用一个更长的超时和不同的系统提示词timeout: 300 system_prompt_file: ./prompts/project-a.md tools: allow: - delete_file这里tools.allow是追加还是替换取决于你的合并策略。我自己的脚本用的是深度合并、数组追加的策略也就是项目级允许的会加到全局允许的上面而不是覆盖掉。这个策略要在脚本里明确实现不然容易出现我以为放开了结果没放开的困惑。注意YAML 对缩进极其敏感用空格不要用 Tab。我见过太多因为一个 Tab 导致整个配置解析失败、排查半天的案例。建议在编辑器里把 Tab 自动转成两个空格。4. 用 tmux 把 Claude Code 和 Codex 编排到一张工作台上4.1 会话布局的设计思路tmux 的布局不是随便切的要按信息流来设计。我的核心原则是主力工具占最大面积辅助工具放在视线容易扫到的位置shell 放在手最容易够到的地方。具体来说我常用的布局是左侧 60% 宽度上下分两个面板上面跑 Claude Code下面跑 Codex右侧 40% 宽度上下分两个面板上面跑一个长期运行的测试监听下面留一个自由 shell为什么把 Claude Code 和 Codex 上下放而不是左右放因为这两个工具的输出都是长文本上下排列能让每个面板获得更宽的显示区域代码行不容易折行读起来舒服。左右排列的话每个面板宽度不够稍微长一点的代码就换行了看着累。4.2 用脚本一键拉起整个工作台手动切面板、起进程太麻烦我写了个rig-up.sh来干这件事。核心逻辑是先读配置把环境变量准备好然后创建 tmux 会话按预设布局切分面板最后在每个面板里发送对应的启动命令。#!/usr/bin/env bash set -euo pipefail SESSIONopenrig PROJECT${1:-default} # 读取全局和项目配置合并后导出为环境变量 source ./scripts/load-config.sh $PROJECT # 如果会话已存在直接 attach if tmux has-session -t $SESSION 2/dev/null; then tmux attach -t $SESSION exit 0 fi # 创建会话第一个窗口跑 Claude Code tmux new-session -d -s $SESSION -n main tmux send-keys -t $SESSION:main claude --config $CLAUDE_CONFIG C-m # 垂直切分下半部分跑 Codex tmux split-window -v -t $SESSION:main tmux send-keys -t $SESSION:main.1 codex --config $CODEX_CONFIG C-m # 右侧再切一列 tmux split-window -h -t $SESSION:main tmux send-keys -t $SESSION:main.2 npm run test:watch C-m tmux split-window -v -t $SESSION:main.2 tmux send-keys -t $SESSION:main.3 clear C-m # 调整面板大小 tmux resize-pane -t $SESSION:main.0 -x 60% tmux select-pane -t $SESSION:main.0 tmux attach -t $SESSION这个脚本里有几个细节值得说。set -euo pipefail是必须的任何一步出错就停下来避免半拉子状态。has-session的判断是为了幂等重复执行不会创建一堆重复会话。send-keys后面的C-m相当于回车少了它命令只是被输入但不会执行这个坑我踩过。load-config.sh负责把 YAML 转成环境变量。我用的方案是用yq解析 YAML然后按需导出。如果你不想引入额外依赖也可以用 Python 的pyyaml写个小脚本效果一样。4.3 tmux.conf 里几个真正有用的设置默认的 tmux 配置对新手不太友好前缀键是Ctrl-b和很多编辑器的快捷键冲突。我一般会改成Ctrl-a然后把一些常用操作绑到更顺手的键上。# 前缀键改成 Ctrl-a set -g prefix C-a unbind C-b bind C-a send-prefix # 开启鼠标支持方便点选面板和滚动 set -g mouse on # 窗口和面板编号从 1 开始符合直觉 set -g base-index 1 setw -g pane-base-index 1 # 用 | 和 - 切分面板比默认的 % 和 好记 bind | split-window -h -c #{pane_current_path} bind - split-window -v -c #{pane_current_path} # 用 h/j/k/l 在面板间移动和 vim 一致 bind h select-pane -L bind j select-pane -D bind k select-pane -U bind l select-pane -R # 增大历史滚动缓冲 set -g history-limit 50000history-limit这个设置特别重要。AI 编程工具的输出动辄几百行默认的 2000 行缓冲很快就不够用了往上翻看不到之前的输出会非常抓狂。设成 50000 之后基本够用一整天。mouse on有人喜欢有人讨厌但我觉得在 AI 编程场景下开着更方便因为经常需要点选面板、滚动查看长输出。如果你习惯纯键盘操作关掉也行。5. 配置合并与加载那些容易翻车的地方5.1 深度合并不是简单的字典覆盖前面提到项目级配置要覆盖全局配置这个覆盖具体怎么实现坑很多。最简单的做法是dict.update()但这是浅合并遇到嵌套字典会直接把整个子字典替换掉。比如全局配置里tools.allow有三个元素项目级只想加一个浅合并的结果是项目级那一个把全局三个全顶掉了这显然不是你要的。正确的做法是深度合并遇到字典就递归进去合并遇到数组就追加或者按你的策略去重后追加遇到标量才覆盖。下面是一个 Python 实现的参考def deep_merge(base, override): result base.copy() for key, value in override.items(): if key in result and isinstance(result[key], dict) and isinstance(value, dict): result[key] deep_merge(result[key], value) elif key in result and isinstance(result[key], list) and isinstance(value, list): # 数组追加并去重保持顺序 merged result[key] [x for x in value if x not in result[key]] result[key] merged else: result[key] value return result这个函数我用了很久基本能覆盖大部分场景。唯一要注意的是数组去重用的是in判断如果数组元素是字典in比的是引用不是内容会失效。如果你的配置里有字典数组需要自己写更精细的比较逻辑。5.2 环境变量和配置文件的优先级有时候你不想改文件只想临时换个模型试试这时候环境变量就派上用场了。我的策略是环境变量优先级最高其次是项目级配置最后是全局配置。加载脚本里按这个顺序依次覆盖。# 先加载全局 export CLAUDE_MODEL$(yq .model global/claude.yaml) # 再加载项目级 if [ -f projects/$PROJECT/claude.yaml ]; then export CLAUDE_MODEL$(yq .model // \$CLAUDE_MODEL\ projects/$PROJECT/claude.yaml) fi # 最后环境变量兜底 export CLAUDE_MODEL${CLAUDE_MODEL_OVERRIDE:-$CLAUDE_MODEL}yq的//操作符是如果左边是 null 就用右边这个在处理可选字段时很好用。不过要注意yq有多个版本语法不完全一样用之前先yq --version确认一下。5.3 配置校验别等到运行时才发现写错了YAML 写错了最怕的是工具启动到一半才报错那时候你可能已经等了几十秒。我的做法是在加载脚本里加一道校验用 JSON Schema 或者简单的必填字段检查把错误提前暴露出来。validate_config() { local file$1 # 检查文件能否被解析 if ! yq . $file /dev/null 21; then echo 配置解析失败: $file return 1 fi # 检查必填字段 local model$(yq .model $file) if [ $model null ] || [ -z $model ]; then echo 缺少必填字段 model: $file return 1 fi return 0 }这个校验很简陋但能拦住 80% 的低级错误。如果你追求更严格可以引入check-jsonschema这类工具把配置的 schema 定义清楚校验会更全面。提示把校验放在rig-up.sh的最前面任何配置有问题就直接退出不要带着错误配置去启动工具。我吃过这个亏工具跑了一半才发现模型名写错了白等了好几分钟。6. 实测中遇到的几个典型问题和处理方式6.1 会话里的进程被意外终止tmux 会话虽然持久但里面的进程不是无敌的。我遇到过几次 Claude Code 跑到一半突然退出排查下来原因各不相同。有一次是内存不够被系统 OOM 杀了有一次是网络波动导致 API 调用连续失败触发了工具的自我保护还有一次纯粹是我自己手滑在面板里按了Ctrl-C。针对 OOM我的做法是给 tmux 会话里的进程加一个内存监控超过阈值就告警。针对网络问题Claude Code 和 Codex 一般都有重试机制但重试次数和间隔可以在配置里调我一般会把重试次数设成 3 到 5 次间隔用指数退避。针对手滑这个没辙只能养成习惯在主力面板里操作时手放轻点。6.2 上下文丢失与恢复AI 编程最怕的就是上下文丢失。你聊了半小时它已经理解了整个项目的结构结果会话断了重新开始它又变成一张白纸。tmux 能保证进程不因为终端关闭而终止但保证不了进程本身不崩溃。我的应对策略是双保险。第一Claude Code 和 Codex 都支持把对话历史落盘配置里开启persist_history之类的选项会话恢复时能读回来。第二我会定期让 AI 把当前的理解和待办事项写到一个CONTEXT.md文件里万一会话彻底丢了新会话第一件事就是读这个文件能快速恢复大部分上下文。这个CONTEXT.md的写法也有讲究不要写成一坨流水账要结构化当前任务是什么、已经完成了哪些、下一步计划是什么、有哪些已知的坑。我一般让 AI 每完成一个阶段性任务就更新一次这样即使中途换工具比如从 Claude Code 换到 Codex新工具也能快速接手。6.3 两个工具同时跑时的资源竞争Claude Code 和 Codex 同时跑最容易出的问题是文件锁冲突。比如两个工具都想改同一个文件一个写了一半另一个也来写结果就是内容错乱。我遇到过最离谱的一次是两个工具互相把对方的修改覆盖了最后文件内容变成了四不像。解决办法有两个层面。配置层面我会在项目级 yaml 里给两个工具划分不同的工作范围比如 Claude Code 负责src/下的代码Codex 负责tests/下的测试井水不犯河水。流程层面如果确实需要两个工具协作我会用 tmux 的synchronize-panes功能临时同步输入让它们看到同样的指令但输出还是各自独立避免直接冲突。# 临时同步所有面板的输入 tmux set-window-option synchronize-panes on # 用完记得关掉 tmux set-window-option synchronize-panes off这个功能要慎用开着的时候你在任何一个面板敲的字都会同步到所有面板很容易误操作。我一般只在需要给两个工具发同样指令的时候开一下发完立刻关。6.4 配置漂移为什么昨天还好好的今天就不行了配置漂移是长期使用 openrig 最隐蔽的问题。你可能改了一个全局配置当时没觉得有什么过了几天发现某个项目的行为不对劲排查半天才想起来是那次改动的影响。我的做法是给 openrig 目录上 git每次改配置都提交commit message 写清楚改了什么、为什么改。这样出问题的时候git log一翻就知道最近动了什么。更进一步我写了个rig-diff.sh对比当前生效的配置和 git 里的版本有差异就提示。#!/usr/bin/env bash # 对比当前配置和 git 版本 for f in global/*.yaml projects/*/*.yaml; do if ! git diff --quiet $f 2/dev/null; then echo 配置有未提交的改动: $f git diff $f fi done这个脚本我放在rig-up.sh的开头每次启动前跑一下有未提交的改动就提醒我。不是强制阻止只是让我心里有数。7. 把这套东西用顺之后的几点个人体会用 openrig 这套思路管理 Claude Code 和 Codex 有一段时间了最大的感受是确定性变强了。以前每次开新项目都要重新配一遍现在 clone 下来改几个字段就能跑省下来的时间累积起来很可观。而且因为配置是文件、是数据我可以让 AI 帮我改配置比如把超时从 120 改成 300它直接改 yaml 就行不用我手敲命令。另一个体会是 tmux 的价值被严重低估了。很多人觉得它就是个终端复用工具但在 AI 编程这个场景里它其实是工作台的骨架。你把面板布局设计好把每个面板的职责定清楚整个人的工作节奏都会变得不一样。我现在打开电脑第一件事就是rig-up.sh几秒钟之后一个完整的工作台就位直接进入状态不用再花时间搭环境。最后分享一个小技巧给 tmux 会话起名字的时候用项目名而不是固定的openrig。这样你可以同时开多个项目的会话tmux ls一眼就能看出哪个是哪个切换用tmux attach -t 项目名比在一堆无名会话里翻找高效得多。我现在的习惯是每个活跃项目一个会话不活跃的定期清理保持列表清爽。这套东西没有什么高深的技术核心就是把重复的事情固化下来。YAML 负责声明tmux 负责承载脚本负责串联三者各司其职。你不需要一次做到完美先跑起来用着用着自然就知道哪里该优化了。
返回列表