ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

卷疯了!Codex 再出新功能,关机也可以继续跑任务:Handoff 迁移到远程主机实操

卷疯了!Codex 再出新功能,关机也可以继续跑任务:Handoff 迁移到远程主机实操 1. Codex Handoff 到底解决了什么痛点关机后任务中断的完整场景复盘Codex 是 OpenAI 推出的命令行编程智能体能在终端里读写文件、跑测试、执行重构。Handoff 是它近期加入的接力能力把本地正在跑的会话、未提交改动、上下文状态整体迁移到一台远程 Linux 主机上继续执行。适合谁适合下班要合盖走人、但任务还没跑完的开发者也适合本地机器性能吃紧、想把重活丢给云主机的团队。我平时的节奏是这样的晚上十点本地开一个 Codex 会话让它重构一个模块并跑全量单测。跑到一半要回家以前只能 CtrlC 中断第二天重新描述上下文AI 还得重新读一遍代码库中间调试状态全丢。Handoff 出现后这个流程变成在对话框里说一句“把当前任务迁到远程主机继续跑”本地打包未提交改动和会话历史通过 SSH 推到远程远程接管执行。合上笔记本任务在服务器上继续。这里的关键词是“会话历史”和“未提交改动”。传统换机协作要先 git commit、push再到另一台机器 pull然后重新向 AI 描述“我刚才在做什么”。Handoff 把这三步压缩成一次迁移动作会话 ID、变量状态、中间调试信息一起走。对长任务尤其重要——比如一个要跑四十分钟的测试套件或者一个多轮迭代的重构中断一次成本极高。但 Handoff 不是魔法它依赖三个前置条件远程主机装好 Codex 运行环境、本地到远程的 SSH 免密通道畅通、双端模型服务配置一致。任何一环出问题迁移都会卡住。下面按“环境准备 → 通道配置 → 模型接入 → 触发迁移 → 验证结果 → 排错”的顺序拆开讲每一步都给可复制的命令和配置。需要先说明一点Handoff 迁移的是任务执行位置不是把本地编辑器搬走。你本地该写的代码还是本地写它解决的是“执行阶段”的连续性。理解这一点后面的配置逻辑就顺了。2. TaoToken 前置准备统一 Key 与 API 通道让迁移后模型调用不断档Handoff 迁移过去之后远程主机上的 Codex 要能正常调用模型否则任务迁过去也跑不动。这里最容易踩的坑是本地用一套 Key远程用另一套迁移后环境变量没同步直接 401。我的做法是用 TaoToken 做统一的 API 通道本地和远程共用同一个 Base URL 和 Key迁移前后模型调用完全一致。TaoToken 是一个多模型聚合服务提供标准的 OpenAI Compatible API 格式。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。它的作用是让你用一套 Key 调度不同模型本地和远程配置同一份省去双端对账的麻烦。前置准备分三步。第一步拿到 API Key。进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在令牌管理里创建一个新令牌。创建时绑定你要用的模型分组不确定就先留空。复制生成的 Key形如sk-xxxxxxxx。第二步确认模型 ID。去模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 或接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 查当前可用的模型名称。Codex 场景下常用的编码模型按文档实时列表为准不要凭记忆写死。第三步规划配置存放位置。Codex 读取模型配置的方式因版本而异常见的是环境变量或配置文件。我建议把 Base URL、Key、Model ID 三项写进一个统一的地方本地和远程都引用它。这样 Handoff 迁移时远程主机只要环境变量一致模型调用就不会断。这里有个细节远程主机如果是全新的环境变量不会自动带过去。所以要么在远程的 shell 配置文件如~/.bashrc或~/.zshrc里写死要么用 Codex 自己的配置文件。我倾向后者因为迁移时配置文件跟着项目走更可控。注意不要把 Key 硬编码进会提交到 Git 的文件里。用.env加.gitignore或者用 Codex 的本地配置目录。配置完成后先在本地验证一次模型调用能通再去做 Handoff。顺序反了的话迁移失败你分不清是 SSH 问题还是 Key 问题。验证方法在第四节给。3. 可复制配置SSH 通道 Codex 模型接入的完整片段这一节给可直接复制的配置。分两块SSH 免密通道和 Codex 的模型服务配置。先配 SSH。编辑本地~/.ssh/config加入远程主机条目。假设远程 IP 是203.0.113.10用户名ubuntu密钥~/.ssh/id_ed25519别名tokyoHost tokyo HostName 203.0.113.10 User ubuntu IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60 ServerAliveCountMax 3 TCPKeepAlive yesServerAliveInterval 60是关键它让本地每 60 秒发一次心跳防止迁移过程中 SSH 超时断开。ServerAliveCountMax 3表示连续 3 次心跳无响应才断开给网络波动留缓冲。配完测试连通性ssh tokyo hostname uname -a能返回远程主机名和内核信息说明通道通了。如果提示Permission denied (publickey)说明公钥没传到远程执行ssh-copy-id -i ~/.ssh/id_ed25519.pub ubuntu203.0.113.10再测一次。接着配远程主机的 Codex 环境。SSH 登录远程ssh tokyo安装 Codex 和依赖npm install -g openai/codex codex --version检查 bubblewrap 和 gitif ! command -v bwrap /dev/null 21 || ! command -v git /dev/null 21; then sudo apt-get update sudo apt-get install -y bubblewrap git else echo bubblewrap and git are already installed. fibubblewrap 是 Codex 构建沙箱隔离环境的核心组件git 用于同步代码差异两个都不能少。然后是模型服务配置。Codex 支持通过配置文件或环境变量接入自定义 API。以配置文件方式为例在项目根目录或 Codex 配置目录创建config.tomlmodel 按文档填入模型ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat对应的环境变量在本地和远程都要设置export TAOTOKEN_API_KEYsk-你的Key写进~/.bashrc或~/.zshrc让它持久化。远程主机同样操作一遍。这样本地和远程引用的是同一个 Base URL、同一个 Key、同一个 Model IDHandoff 迁移后模型调用无缝衔接。如果你用的是 Codex 的 auth.json 方式部分版本支持配置结构类似{ auth_mode: apikey, providers: { taotoken: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: 按文档填入模型ID } } }三件套记牢Base URL 是https://taotoken.net/apiKey 从控制台拿Model ID 从文档查。任何一处不一致迁移后都会报错。4. 验证请求与成功结果迁移前后各跑一次确认配置写完不能直接上 Handoff先做两次验证本地一次远程一次。两次都通才说明迁移后模型调用不会断。本地验证。在本地终端跑一个最小请求确认 Key 和 Base URL 有效curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: 按文档填入模型ID, messages: [{role: user, content: reply with ok}] }返回 JSON 里choices[0].message.content有内容说明本地通道正常。如果返回 401检查 Key 是否复制完整、有没有多余空格。如果返回 404检查 Base URL 是不是写成了https://taotoken.net/api而不是带/v1的完整路径——具体以文档为准。远程验证。SSH 到远程跑同样的 curlssh tokyo curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {\model\:\按文档填入模型ID\,\messages\:[{\role\:\user\,\content\:\reply with ok\}]}远程也能返回内容说明双端模型通道一致。这一步很多人跳过结果 Handoff 迁移过去任务卡在模型调用上回头排查浪费半小时。两次验证都通过后做一次 Handoff 触发。在本地 Codex 会话里确认当前任务正在运行然后输入迁移指令把当前正在执行的任务迁移到 tokyo 主机上继续运行Codex 会做几件事打包未提交改动相当于 stash、序列化会话历史、通过 SSH 传输到远程、在远程恢复会话。过程中终端会显示迁移进度。完成后给出远程会话的标题和会话 ID。成功结果长这样本地终端提示迁移完成附带远程会话标识远程主机上codex进程开始接管执行你本地可以安全合盖。验证远程任务确实在跑ssh tokyo ps aux | grep codex能看到 codex 进程说明任务已在远程执行。再等几分钟看远程日志有没有新的模型调用记录确认任务在持续推进而不是卡住。如果迁移后远程任务立刻退出多半是远程工作目录和本地不一致。Handoff 依赖双端存在同一个 Git 仓库路径。本地项目在/Users/you/projects/my-app远程也要有对应仓库或者在 Codex 项目设置里显式关联工作区。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 逐条对照配置和迁移过程中几个报错反复出现。逐条给现象、原因、解决。401 Unauthorized。现象curl 或 Codex 调用模型返回 401。原因Key 无效、过期、或环境变量没生效。排查echo $TAOTOKEN_API_KEY看有没有值注意前后空格。远程主机如果没设这个变量echo会是空。解决在远程~/.bashrc里补上export TAOTOKEN_API_KEYsk-...然后source ~/.bashrc。另外确认 Key 绑定的模型分组包含你要用的模型。local proxy failed。现象Codex 启动或迁移时报本地代理失败。原因环境里残留了HTTP_PROXY/HTTPS_PROXY之类的变量指向一个不存在的本地端口。排查env | grep -i proxy。解决unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy或者把代理配置指向正确地址。远程主机同样检查一遍很多人本地清了远程忘了清。reading choices 报错。现象模型返回的 JSON 解析失败提示读取choices字段出错。原因Base URL 路径不对请求打到了非兼容端点返回了 HTML 或错误结构。排查确认 Base URL 是https://taotoken.net/api且请求路径拼成了/v1/chat/completions。解决对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 核对完整端点。另外确认wire_api设成了chat而不是其他模式。OAuth 相关报错。现象Codex 提示 OAuth 登录失败或 token 过期。原因Codex 默认走账号 OAuth但你配了自定义 API Key两种认证方式冲突。排查看配置文件里auth_mode是不是还留着 OAuth。解决把auth_mode改成apikey并确保env_key指向正确的环境变量名。如果用的是 auth.json检查providers结构有没有写错层级。SSH 迁移中途断开。现象迁移进度条卡死提示Connection closed by remote host。原因网络波动导致 SSH 超时。解决确认~/.ssh/config里有ServerAliveInterval 60。如果还断把值降到 30并加TCPKeepAlive yes。bwrap 权限错误。现象远程任务运行时报bwrap: No permissions。原因部分云服务器内核默认禁用用户命名空间。解决sudo sysctl -w kernel.unprivileged_userns_clone1永久生效写入/etc/sysctl.conf。改完重启 Codex 会话。代码冲突。现象接力后远程代码和本地不一致。原因本地有未追踪文件没被 Git 管理Handoff 打包时漏掉。解决迁移前git status检查把新文件git add进去或者让 Codex 自动处理临时暂存。这些报错里401 和 reading choices 占了我遇到问题的大半根因都是配置三件套没对齐。把 Base URL、Key、Model ID 三项在本地和远程各核对一遍能省掉大量排查时间。6. 长期编码与 Agent 场景用 Coding Plan 把接力变成常态Handoff 单次用起来已经能解决“关机不断任务”但如果你经常跑长任务、经常换机器建议把配置固化成常态。TaoToken 的 Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 提供了面向长期编码场景的接入方案适合把本地和远程的模型通道统一管理。我的做法是把 SSH 别名、Codex 配置、环境变量三样东西写成一个初始化脚本新机器一条命令配好。远程主机扩容或换机时跑一遍脚本Handoff 立刻可用。脚本大致长这样#!/bin/bash # setup-codex-handoff.sh set -e # 1. 检查依赖 command -v bwrap /dev/null || sudo apt-get install -y bubblewrap command -v git /dev/null || sudo apt-get install -y git # 2. 安装 Codex npm install -g openai/codex # 3. 写入模型配置 mkdir -p ~/.codex cat ~/.codex/config.toml EOF model 按文档填入模型ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat EOF # 4. 提示设置 Key echo export TAOTOKEN_API_KEYsk-你的Key ~/.bashrc source ~/.bashrc echo setup done. run: codex --version本地侧同样维护一份 SSH 配置模板新机器复制~/.ssh/config即可。这样无论本地换机还是远程扩容Handoff 的迁移链路都能快速重建。对于 Agent 类长任务比如让 Codex 持续跑一个多轮重构Handoff 的价值更明显。你可以白天在本地盯着跑晚上迁到远程让它自己迭代第二天早上迁回来继续。整个过程模型调用走同一个 TaoToken 通道会话历史不断上下文不丢。最后给一个实用技巧迁移前先git stash list确认没有遗留的 stash避免 Handoff 打包时和已有 stash 冲突。迁移完成后在远程跑一次git status确认工作区状态符合预期。这两个动作花不了十秒能避免大部分“迁移后代码不对”的问题。配置一次后面就是一句话的事。把 SSH 通道、模型三件套、初始化脚本准备好关机继续跑任务这件事就真正落地了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进