1. 夜间批量任务为什么总在凌晨断掉
如果你把 Claude Code 或 Codex 挂在终端里跑批量重构,第二天早上大概率会看到两种结果:一种是任务确实跑完了,但中间某几次请求返回了 502;另一种更常见——进程还在,但日志停在凌晨两点,后面全是重试超时。很多人第一反应是模型不行、上下文太长、任务太复杂,但把日志翻出来逐行看,真正卡住的位置往往跟模型推理无关,而是请求在链路上被掐断了。
我试过把同一个重构任务分别跑在直连和固定 endpoint 两种配置下,直连那晚在 3 小时 17 分左右开始出现ECONNRESET,之后每 5 分钟重试一次,直到早上都没恢复;换成固定 endpoint 后,同样的任务连续跑了 6 小时 40 分,中间只有两次正常的 429 限流退避,没有一次因为通道问题中断。差别不在模型,在于请求出口是否稳定。
夜间批量任务的特殊性在于:它没有人盯着,没有交互式重试,一旦某次请求失败且客户端没有正确退避,整个 Agent 循环就会卡死在一个状态上。Claude Code 的 agentic loop 和 Codex 的 exec 模式都会在请求失败后进入等待,如果失败原因是网络层而不是模型层,等待时间会非常长。所以让它们稳定跑一整晚,核心动作只有一个:把 endpoint 固定到一个可控的入口,让请求失败的原因收敛到「限流」和「业务错误」这两类可处理的情况,而不是「通道抖动」这种没法处理的情况。
这篇文章拆的就是这个工作流:怎么改 endpoint、auth.json 里哪些字段必须对齐、连续跑 6 小时要看哪些日志、以及最常见的几个报错怎么排。适合已经在用 Claude Code 或 Codex 做自动化、但被夜间中断折磨过的人。
2. 把 endpoint 固定到 TaoToken 的前置准备
在改配置之前,先把三件套对齐:Base URL、API Key、Model ID。这三者任何一个不匹配,夜间任务都会以 401 或 404 的形式断掉,而且断得毫无规律,因为有些客户端只在第一次请求时校验,后续请求复用连接就不报错了,等你发现时已经跑了几个小时。
TaoToken 的 API 入口是https://taotoken.net/api,这个地址不带任何查询参数,直接作为 Base URL 使用。注意不要把它和官网地址混用,官网是https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=,那是给人看的页面,不是给客户端请求的。客户端只认/api这个路径。
API Key 在控制台的 API Keys 页面生成,生成后只显示一次,复制下来存到环境变量里,不要硬编码进配置文件。Model ID 要跟你实际调用的模型对齐,Claude Code 走 Anthropic 协议时用 Claude 系列模型 ID,Codex 走 OpenAI 兼容协议时用对应的模型 ID,两者不要混。
前置准备清单:
- 一个可用的 API Key,存到
TAOTOKEN_API_KEY环境变量 - 确认 Base URL 为
https://taotoken.net/api - 确认要用的 Model ID,写进配置
- 本地或远程环境能访问该地址(用 curl 测一次)
这里有个容易踩的坑:很多人把 Base URL 写成https://taotoken.net/api/带尾斜杠,某些客户端拼接路径时会变成//v1/messages,服务端返回 404,但错误信息里不会明确说是路径问题,只会说 model not found,误导排查方向。所以 Base URL 结尾不要带斜杠。
另外,夜间任务建议在远程 Linux 主机上跑,本地笔记本合盖休眠会直接杀掉进程,这跟 endpoint 无关,但会伪装成「通道中断」。用 tmux 挂住会话,SSH 断了任务还在。这一步做完,再进入配置环节。
3. 可复制的 endpoint 配置与 auth.json 字段
这一节给三套配置:Claude Code 的 settings、Codex 的 auth.json、以及通用的环境变量方式。三套都基于同一个 Base URL 和同一个 Key,只是客户端读取位置不同。
3.1 Claude Code 的 settings.json
Claude Code 读取~/.claude/settings.json,把 endpoint 和 key 写进去:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" }, "permissions": { "allow": [ "Bash(git *)", "Bash(npm *)", "Bash(pytest *)" ] } }注意ANTHROPIC_BASE_URL不要带/v1,Claude Code 会自己拼/v1/messages。如果你写成https://taotoken.net/api/v1,最终请求会变成/api/v1/v1/messages,直接 404。这是夜间任务最常见的配置错误之一。
permissions.allow里把夜间任务需要的命令放开,避免 Agent 在凌晨卡在权限确认上。但只放开任务必需的,不要用Bash(*)全放开。
3.2 Codex 的 auth.json
Codex 读取~/.codex/auth.json,字段名跟 Claude Code 不同:
{ "OPENAI_BASE_URL": "https://taotoken.net/api", "OPENAI_API_KEY": "sk-你的key", "OPENAI_MODEL": "gpt-5-codex", "provider": "openai" }Codex 的OPENAI_BASE_URL同样不带/v1,它会拼/v1/chat/completions或/v1/responses,取决于模型。如果你用的是 Codex 的 exec 模式跑批量任务,还要在~/.codex/config.toml里确认model_provider指向正确的 provider:
model_provider = "openai" model = "gpt-5-codex" [model_providers.openai] base_url = "https://taotoken.net/api" env_key = "OPENAI_API_KEY"TOML 和 JSON 两处都要对齐,只改一处会出现「auth.json 读到了但 config.toml 还在用默认地址」的情况,表现为部分请求走旧地址、部分走新地址,日志里看起来像随机中断。
3.3 环境变量方式(通用)
如果你不想改配置文件,或者要在 CI 里跑,用环境变量:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的key" export OPENAI_BASE_URL="https://taotoken.net/api" export OPENAI_API_KEY="sk-你的key"环境变量的优先级通常高于配置文件,但不同客户端行为不一致,建议只保留一种方式,避免排查时搞不清哪个生效了。
3.4 三件套对照表
| 客户端 | Base URL 字段 | Key 字段 | Model 字段 | 配置文件路径 |
|---|---|---|---|---|
| Claude Code | ANTHROPIC_BASE_URL | ANTHROPIC_API_KEY | ANTHROPIC_MODEL | ~/.claude/settings.json |
| Codex | OPENAI_BASE_URL | OPENAI_API_KEY | OPENAI_MODEL | ~/.codex/auth.json |
| Codex TOML | base_url | env_key | model | ~/.codex/config.toml |
三件套必须同时对齐,缺一个都会在夜间任务里以不同形式爆出来。改完配置后,先别急着跑长任务,用下一节的验证请求确认一次。
4. 验证请求与连续 6 小时日志观察
配置改完,先用一条最小请求确认链路通。Claude Code 用:
curl -s https://taotoken.net/api/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role": "user", "content": "reply with ok"}] }'返回里如果有content字段且stop_reason是end_turn,说明 Base URL、Key、Model 三件套都对。如果返回 401,是 Key 问题;返回 404,大概率是 Base URL 带了多余路径;返回 400 且提示 model 不存在,是 Model ID 写错。
Codex 用:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "content-type: application/json" \ -d '{ "model": "gpt-5-codex", "messages": [{"role": "user", "content": "reply with ok"}], "max_tokens": 64 }'两条都通了,再挂夜间任务。挂的时候把日志重定向到文件,方便第二天分析:
tmux new -s night-agent claude --dangerously-skip-permissions -p "按 TASK.md 执行重构任务" 2>&1 | tee -a ~/logs/night-$(date +%F).log--dangerously-skip-permissions只在隔离环境用,配合前面 settings.json 里的 allow 列表,避免凌晨卡权限确认。
连续跑 6 小时后,看日志里这几个指标:
第一,请求成功率。用 grep 统计:
grep -c "HTTP 200" ~/logs/night-2025-01-01.log grep -c "ECONNRESET\|ETIMEDOUT\|502\|503" ~/logs/night-2025-01-01.log如果 502/503 出现次数为 0,说明通道稳定;如果出现但后面跟着成功的重试,说明退避逻辑生效了,可以接受;如果出现后没有恢复,说明客户端退避策略有问题,需要调max_retries。
第二,请求间隔。正常 agentic loop 的请求间隔在几秒到几十秒,如果日志里出现超过 5 分钟的空白,说明卡住了。用 awk 算时间差:
awk '/timestamp/ {print $1}' ~/logs/night-2025-01-01.log | \ awk 'NR>1 {print $1-prev} {prev=$1}' | sort -rn | head第三,token 消耗曲线。如果某段时间 token 消耗突然归零,说明请求没发出去。这个要结合客户端自己的 usage 日志看。
实测下来,配置正确的情况下,6 小时连续运行只会看到两类日志:正常的请求成功记录,和偶发的 429 限流退避。任何ECONNRESET、ETIMEDOUT、502都不应该出现。如果出现了,按下一节排查。
5. 夜间任务常见报错排查
5.1 401 Unauthorized
日志里出现401且提示invalid api key,先确认环境变量有没有被覆盖。Claude Code 会优先读ANTHROPIC_API_KEY,如果你在 shell 里 export 了一个旧 key,settings.json 里的新 key 就不生效。用env | grep ANTHROPIC确认当前生效的值。
另一个原因是 key 复制时带了空格或换行。把 key 用echo -n "$ANTHROPIC_API_KEY" | wc -c看长度,跟控制台显示的长度对比。
5.2 local proxy failed / connection refused
这个报错说明客户端在尝试连本地代理,而不是直连 Base URL。常见于之前配过代理工具、环境变量里残留了HTTP_PROXY或HTTPS_PROXY。夜间任务里这个报错特别隐蔽,因为白天手动跑可能走的是另一套 shell 配置。
排查:
env | grep -i proxy如果有输出,在启动夜间任务前 unset:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY然后在同一个 tmux 会话里启动任务,确保环境干净。
5.3 reading choices 相关报错
日志里出现error reading choices或unexpected response format,通常是响应体不是预期的 JSON 结构。原因可能是 Base URL 指向了一个返回 HTML 的地址(比如误填了官网地址),或者 Model ID 跟协议不匹配(用 Anthropic 协议调了 OpenAI 模型)。
确认 Base URL 是https://taotoken.net/api,不是官网地址。确认 Claude Code 用 Anthropic 协议、Codex 用 OpenAI 协议,不要交叉。
5.4 OAuth 相关报错
如果日志里出现OAuth token expired或refresh token failed,说明客户端在走 OAuth 流程而不是 API Key。Claude Code 和 Codex 都支持两种认证方式,夜间任务必须用 API Key,OAuth 的 token 有效期短,凌晨过期后无法自动刷新。
检查~/.claude/settings.json里有没有oauth相关字段,有就删掉,只保留ANTHROPIC_API_KEY。Codex 同理,auth.json里只保留OPENAI_API_KEY。
5.5 报错对照表
| 报错关键词 | 最可能原因 | 排查动作 |
|---|---|---|
| 401 invalid api key | key 被环境变量覆盖或带空格 | env grep + 长度检查 |
| local proxy failed | 残留代理环境变量 | unset HTTP_PROXY |
| reading choices | Base URL 或协议不匹配 | 确认 /api 路径 + 协议 |
| OAuth token expired | 用了 OAuth 而非 API Key | 删掉 oauth 字段 |
| 404 model not found | Base URL 带 /v1 或 Model ID 错 | 去掉尾斜杠 + 核对 Model ID |
排障时优先看第一条报错,后面的报错往往是第一条的连锁反应。比如 401 之后客户端会重试,重试时可能触发 OAuth 流程,日志里就同时出现 401 和 OAuth 报错,但根因只有一个。
6. 把夜间任务跑成可复用的工作流
配置和排障都过了之后,剩下的就是把整套动作固化成脚本,下次直接跑。我自己的做法是写一个night-run.sh,里面做四件事:检查环境变量、启动 tmux 会话、跑任务、把日志按日期归档。
#!/bin/bash set -e export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="$TAOTOKEN_API_KEY" unset HTTP_PROXY HTTPS_PROXY ALL_PROXY LOG=~/logs/night-$(date +%F).log mkdir -p ~/logs tmux new-session -d -s night "claude --dangerously-skip-permissions -p \"$(cat TASK.md)\" 2>&1 | tee -a $LOG" echo "started, log at $LOG"第二天早上先看日志尾部的stop_reason,如果是end_turn,说明任务正常结束;如果是max_turns或空,说明中途卡住了,用第 4 节的 awk 命令找时间空白点。
长期跑的话,建议把 API Key 和 Base URL 的管理集中到一处,比如用.env文件配合 direnv,避免每个项目都要重新配。TaoToken 的控制台可以生成多个 Key,给不同项目分配不同的 Key,这样某个 Key 出问题时不会影响其他夜间任务。
最后一点经验:夜间任务不要一次跑太复杂的重构,拆成多个小任务串行跑,每个任务单独提交 Git,这样即使某个任务失败,前面的成果还在。Agent 的稳定性不只取决于 endpoint,也取决于任务粒度。粒度越细,单次失败的影响越小,第二天恢复的成本越低。