1. 187 小时断流后,先别重启:把 TaoToken 请求现场固定下来
当那个 5.7K+ Star 的国产开源 AI Agent 跑到第 187 小时突然断流时,别急着重启;先用 TaoToken 固定请求现场,并从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_intro 获取 Key,Base URL 填 https://taotoken.net/api。长任务最怕的不是单次失败,而是失败之后你不知道上一轮 checkpoint 到哪里、最后成功的 request_id 是什么、当时用的是哪个模型和哪些参数。很多“200 小时不掉线”的演示看起来很稳,真正到了连续跑一周、日志几十 GB 的场景,恢复现场靠的就是日志里那几个字段,而不是重新开头再跑一遍。
本文按“长任务 Token 消耗与稳定性排查”的视角,把接入、配置、启动、掉线日志对照和请求回放串起来。你会看到 Claude Code 的settings.json与ANTHROPIC_*变量、Codex 的config.toml、CC Switch 三件套分别怎么填;也会拿到一套本地可执行的日志提取与重放示例。重点不是把某个工具吹成永不断线,而是让断线发生时,你能从日志回到可复现的请求,继续执行而不是重复执行。
200 小时级别的 Agent 通常有三个压力源:第一,上下文持续增长,input token 越滚越大,缓存命中率一掉,每轮耗时和成本都上来;第二,工具调用和流式输出让单请求持续时间变长,网络抖动、限流、超时都会被放大;第三,重试如果没有幂等设计,会把已经跑过的文件操作、命令、写入动作再做一遍。把模型入口统一到 TaoToken 之后,Base URL 固定为https://taotoken.net/api,Key 用YOUR_API_KEY占位,日志里至少可以用同一套字段去记录 session、request、turn、model、token 用量和错误码。下面先建立最小日志规范。
1.1 请求回放必须固定三类 ID
长任务日志不要只打“开始”“结束”“失败”。最少要落三类 ID:
session_id:一次 200 小时长任务的全局 ID,用来串联所有 checkpoint。request_id:单次模型请求 ID,用来回放某一次调用,定位是模型侧、网络侧还是工具侧。turn_id:任务轮次或步骤 ID,用来决定从哪一步继续。
推荐每轮日志带这些字段:
ts=2025-01-01T12:00:00Z level=info session_id=agent-200h-001 turn=1842 request_id=req_xxx model=xxx input_tokens=182340 output_tokens=920 cache_read=120000 tool_calls=3 checkpoint=ckpt-1842 latency_ms=28410如果掉线时只看到:
level=error msg=stream_disconnected before completion retry=0那还不够。你还需要在同一行附近找到request_id、session_id、turn。没有这些字段,回放只能靠猜。
1.2 日志最小字段模板
可以先用一个统一的日志格式,把你现有 Agent 的输出补齐:
session_id=<全局会话> turn=<轮次> request_id=<模型请求ID> model=<模型名> base_url=https://taotoken.net/api input_tokens=<输入token> output_tokens=<输出token> cache_read=<缓存命中token> tool_calls=<工具调用数> checkpoint=<检查点文件或目录> retry=<第几次重试> status=<ok|error|stream_disconnected|rate_limited> error=<错误摘要>这些字段不需要一次全打全,但至少要保证断线瞬间能查到session_id和request_id。恢复时先加载checkpoint,再用request_id回放最后一次失败请求,确认是继续执行还是需要回退一轮。
2. 写入 Key 前:先到 TaoToken 官网拿 Key,Base URL 不要带 UTM
在把YOUR_API_KEY写进任何配置文件之前,先到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_key 创建并复制 Key。这个顺序很重要:不要先抄别人的旧 Key,也不要把 Key 写进仓库。写入本地环境变量或本地配置后,Base URL 统一填:
https://taotoken.net/api注意,Base URL 是工具配置参数,不需要追加 UTM 参数。UTM 只用于官网入口和文档入口的跳转统计。你可以先把本地环境变量准备好:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export TAOTOKEN_BASE_URL="https://taotoken.net/api"如果你用 Claude Code,则要使用ANTHROPIC_*体系;如果你用 Codex,则要使用config.toml供应商体系。两者不要混。不要把ANTHROPIC_BASE_URL写进 Codex 的config.toml,也不要把 Codex 的model_providers段塞进 Claude Code 的settings.json。
对于 200 小时长任务,建议再补两个本地变量:
export AGENT_SESSION="agent-200h-$(date +%Y%m%d%H%M)" export AGENT_MODEL="你的模型名"这样启动命令、日志文件名和 checkpoint 目录都能带上同一个session_id,后面回放时不会把多组长任务日志混在一起。
3. Claude Code 配置:settings.json、ANTHROPIC_* 与 CC Switch 三件套
Claude Code 的配置入口通常在用户目录或项目目录下的settings.json。如果你希望所有项目都走 TaoToken,可以优先改用户级配置;如果只是某个长任务项目使用,可以在项目内放.claude/settings.json。示例配置如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "YOUR_API_KEY" } }如果你的 Claude Code 版本或接入方式使用 auth token 模式,可以把 Key 字段换成:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY" } }关键点是:ANTHROPIC_BASE_URL的值固定为https://taotoken.net/api,不要写成带utm_source的官网地址。官网地址用于获取 Key、查看文档和进入控制台;API 调用只认 Base URL。
如果你不想把 Key 写进文件,也可以在启动终端里临时导出:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="YOUR_API_KEY" claude --verbose或者使用 auth token:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="YOUR_API_KEY" claude --verbose启动后建议先用一个短任务验证:
claude -p "只回复当前配置是否可用,不要执行任何工具"如果返回正常,再进入长任务。不要一上来就把 200 小时的任务挂上去,先用 2 到 3 轮短对话确认模型、Base URL、Key 都能通。
3.1 CC Switch 三件套怎么填
如果你用 CC Switch 管理多套配置,新增供应商时可以把三件套填成:
| 字段 | 建议值 |
|---|---|
| 供应商名称 | TaoToken |
| Base URL | https://taotoken.net/api |
| API Key | YOUR_API_KEY |
这里的“三件套”是名称、Base URL、Key。切换到 Claude Code 时,CC Switch 帮你映射到ANTHROPIC_BASE_URL、ANTHROPIC_API_KEY或ANTHROPIC_AUTH_TOKEN;切换到 Codex 时,它应写入config.toml的model_providers段。不要把 Claude Code 的ANTHROPIC_*变量复制到 Codex 配置里,这是长任务排查中最常见的“配置串味”问题。
3.2 Claude Code 长任务启动命令
假设你的 Agent 项目在~/agent-200h,可以使用:
cd ~/agent-200h export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="YOUR_API_KEY" export AGENT_SESSION="agent-200h-$(date +%Y%m%d%H%M)" mkdir -p logs "checkpoints/$AGENT_SESSION" nohup claude --verbose \ > "logs/$AGENT_SESSION.claude.stdout" 2>&1 & echo $! > "logs/$AGENT_SESSION.claude.pid"如果你的 Claude Code 工作流由外层脚本驱动,就把AGENT_SESSION传给外层脚本,确保模型请求日志、工具日志、checkpoint 日志使用同一个会话 ID。
4. Codex 配置:config.toml、启动命令与模型供应商切换
Codex 使用config.toml,不要用ANTHROPIC_*。典型配置路径是~/.codex/config.toml。示例:
model = "你的模型名" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"然后在终端导出 Key:
export TAOTOKEN_API_KEY="YOUR_API_KEY" codex --config ~/.codex/config.toml如果你的 Codex 版本要求其他wire_api取值,以本地codex --help和实际报错为准。关键是三点:model_provider指向taotoken;base_url填https://taotoken.net/api;env_key指向你实际导出的环境变量名。不要写成ANTHROPIC_BASE_URL。
启动长任务时,建议把 Codex 输出也重定向到同一个会话日志目录:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export AGENT_SESSION="agent-200h-$(date +%Y%m%d%H%M)" mkdir -p logs "checkpoints/$AGENT_SESSION" nohup codex --config ~/.codex/config.toml \ > "logs/$AGENT_SESSION.codex.stdout" 2>&1 & echo $! > "logs/$AGENT_SESSION.codex.pid"如果你通过外层 Agent 调 Codex,那么外层脚本要负责把AGENT_SESSION、turn、request_id写进日志。Codex 自身日志和外层任务日志可以通过session_id关联起来。
5. 长任务 Token 消耗与稳定性排查:从心跳、退避到 checkpoint
200 小时长任务不是“一次请求跑 200 小时”,而是大量请求、工具调用和状态保存的组合。稳定性排查要围绕四个动作:记录、限流、退避、恢复。
第一,记录。每轮至少记录 input token、output token、缓存读取、工具调用数、耗时、状态。Token 消耗突然上升通常不是模型变贵,而是上下文膨胀或工具输出没裁剪。比如某轮工具返回了 2 万行日志,下一轮 input token 直接翻倍,随后触发限流或超时。
第二,限流。长任务不要所有子任务并发拉满。给模型请求设置并发上限,给工具调用设置队列,给流式读取设置首 token 超时和总超时。并发越高,遇到 429 时越容易形成重试风暴。
第三,退避。重试要有指数退避和随机抖动。示例策略:
第 1 次失败:等待 2s + jitter 第 2 次失败:等待 5s + jitter 第 3 次失败:等待 15s + jitter 第 4 次失败:等待 60s + jitter 超过 4 次:保存 checkpoint,标记人工介入不要无限重试。长任务最怕卡在一个失败请求上反复打,Token 消耗完,日志里全是重试记录。
第四,恢复。每完成一个可验证步骤就写 checkpoint。checkpoint 至少包含:
{ "session_id": "agent-200h-001", "turn": 1842, "last_request_id": "req_xxx", "model": "你的模型名", "messages": [], "tool_results": [], "created_at": "2025-01-01T12:00:00Z" }如果你使用 TaoToken 作为统一入口,可以在排查时回到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_replay 查看 Key、模型与文档入口,再对照本地日志确认请求参数是否一致。注意,这个链接是官网入口,不是 API Base URL;API 请求仍然使用https://taotoken.net/api。
5.1 长任务启动命令模板
下面是一份本地可改的启动模板。把your_agent_entry.py替换成你的 Agent 入口:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export AGENT_MODEL="你的模型名" export AGENT_SESSION="agent-200h-$(date +%Y%m%d%H%M)" mkdir -p logs "checkpoints/$AGENT_SESSION" nohup python your_agent_entry.py \ --session-id "$AGENT_SESSION" \ --model "$AGENT_MODEL" \ --base-url "$TAOTOKEN_BASE_URL" \ --checkpoint-dir "checkpoints/$AGENT_SESSION" \ --log-file "logs/$AGENT_SESSION.log" \ --max-turns 50000 \ --retry-max 4 \ > "logs/$AGENT_SESSION.stdout" 2>&1 & echo $! > "logs/$AGENT_SESSION.pid"查看是否还活着:
ps -p "$(cat logs/$AGENT_SESSION.pid)" -o pid,etime,cmd tail -f "logs/$AGENT_SESSION.log"如果进程不在了,先看 stdout 和主日志最后 200 行:
tail -n 200 "logs/$AGENT_SESSION.stdout" tail -n 200 "logs/$AGENT_SESSION.log"不要第一时间删日志重启。先保留现场。
6. 掉线前后日志对照表:从 stream_disconnected 到 checkpoint_loaded
下面这张表用于快速判断断线发生在哪一层。日志关键字可以根据你的实际项目调整,但字段含义不要缺。
| 阶段 | 典型日志字段 | 你要确认什么 | 处理动作 |
|---|---|---|---|
| 断流前 10 分钟 | turn=1842 input_tokens=182340 cache_read=120000 tool_calls=3 latency_ms=28410 | input token 是否持续上涨,缓存是否失效 | 裁剪工具输出,压缩历史,检查缓存键 |
| 断流前 1 分钟 | status=streaming request_id=req_xxx first_token_ms=4200 | 首 token 是否变慢 | 降低并发,检查限流 |
| 断流瞬间 | msg=stream_disconnected request_id=req_xxx retry=0 | request_id 是否已落盘 | 保留 request_id,不要只重启 |
| 首次重试 | status=rate_limited retry=1 wait=2s | 是否 429 或限流 | 指数退避 + jitter,降低并发 |
| 第二次重试 | status=timeout retry=2 wait=5s | 是连接超时还是读超时 | 调整流式读取超时,检查网络出口 |
| 恢复加载 | checkpoint_loaded session_id=agent-200h-001 turn=1842 | checkpoint 是否完整 | 从 turn+1 继续 |
| 回放请求 | replay request_id=req_xxx model=xxx base_url=https://taotoken.net/api | 模型、参数、Base URL 是否一致 | 用相同参数复现失败请求 |
| 恢复成功 | turn=1843 status=ok request_id=req_yyy | 是否重复执行工具 | 对比工具调用 ID,启用幂等 |
这张表的核心是:断线不是结束,而是进入“定位 + 恢复”流程。日志里如果只有stream_disconnected,没有request_id,那就要在代码里补日志。日志里如果有request_id,但没有 checkpoint,那就要补 checkpoint。日志里如果两者都有,但重试后重复执行了工具,那就要补幂等。
6.1 一次典型掉线现场
假设日志如下:
ts=2025-01-01T12:00:00Z level=info session_id=agent-200h-001 turn=1842 request_id=req_abc model=xxx input_tokens=182340 output_tokens=920 cache_read=120000 tool_calls=3 checkpoint=ckpt-1842 ts=2025-01-01T12:00:28Z level=info session_id=agent-200h-001 turn=1842 request_id=req_abc status=streaming first_token_ms=4200 ts=2025-01-01T12:00:41Z level=error session_id=agent-200h-001 turn=1842 request_id=req_abc msg=stream_disconnected retry=0 ts=2025-01-01T12:00:43Z level=warn session_id=agent-200h-001 turn=1842 request_id=req_abc status=rate_limited retry=1 wait=2s ts=2025-01-01T12:00:50Z level=warn session_id=agent-200h-001 turn=1842 request_id=req_abc status=timeout retry=2 wait=5s ts=2025-01-01T12:00:58Z level=info session_id=agent-200h-001 turn=1842 request_id=req_abc checkpoint_loaded=ckpt-1842 ts=2025-01-01T12:01:05Z level=info session_id=agent-200h-001 turn=1843 request_id=req_def status=ok从这段日志能得到的结论:
- 断在
turn=1842,request_id=req_abc。 - 断流前有
first_token_ms=4200,说明不是完全无响应。 - 重试遇到
rate_limited和timeout,需要退避。 - 已加载
ckpt-1842,所以可以从 1843 继续。 - 回放时应使用
req_abc对应的模型和参数,而不是直接换模型。
如果第 5 步直接换模型,可能会得到不同工具调用结果,导致恢复后的任务分支不一致。对于长任务,模型和参数的一致性比“快速恢复”更重要。
7. 请求回放:用 request_id 复原一次 TaoToken 调用
恢复现场的目标不是重跑整个任务,而是从 checkpoint 之后继续。下面给出一个本地日志提取脚本,先找到最近失败的request_id和turn。所有命令都在你本地终端执行。
import json import re from pathlib import Path log_path = Path("logs/agent-200h-001.log") text = log_path.read_text(encoding="utf-8", errors="ignore") pattern = re.compile( r"turn=(?P<turn>\d+).*?" r"session_id=(?P<session>\S+).*?" r"request_id=(?P<request>\S+)" ) rows = [m.groupdict() for m in pattern.finditer(text)] for row in rows[-10:]: print(json.dumps(row, ensure_ascii=False))如果日志顺序是session_id在前、request_id在后,可以调整正则。重点是把turn、session_id、request_id提取出来。然后从 checkpoint 目录找到对应轮次:
find checkpoints/agent-200h-001 -maxdepth 1 -type f | sort | tail -n 20假设最后成功 checkpoint 是ckpt-1842,失败请求是req_abc。接下来可以写一个最小重放脚本:
import os import requests api_key = os.environ["TAOTOKEN_API_KEY"] base_url = "https://taotoken.net/api" model = "你的模型名" request_id = "req_abc" payload = { "model": model, "messages": [ { "role": "system", "content": "你正在恢复一个长任务。请从 checkpoint 之后继续,不要重复已经完成的工具调用。" }, { "role": "user", "content": "读取 ckpt-1842 后的状态,继续执行下一轮。" } ], "stream": True, } headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "X-Replay-Request-Id": request_id, } with requests.post( f"{base_url}/v1/chat/completions", headers=headers, json=payload, stream=True, timeout=600, ) as resp: resp.raise_for_status() for line in resp.iter_lines(): if line: print(line.decode("utf-8"))这段脚本只做请求回放,不负责执行工具。工具执行必须由你的 Agent 在确认幂等后继续。特别是文件写入、命令执行、外部通知这类动作,回放前要先检查tool_call_id是否已经完成。
如果你只想验证“配置是否还能通”,可以先用非流式短请求:
import os import requests api_key = os.environ["TAOTOKEN_API_KEY"] base_url = "https://taotoken.net/api" resp = requests.post( f"{base_url}/v1/chat/completions", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={ "model": "你的模型名", "messages": [{"role": "user", "content": "只回复 OK"}], "stream": False, }, timeout=60, ) print(resp.status_code) print(resp.text[:500])短请求能通,再启动长任务。短请求不通,先查 Key、Base URL、模型名,不要直接扩大重试次数。
8. 200 小时 Checklist:把掉线变成可恢复事件
最后给一份长任务上线前检查清单。每一项都和你断线后能不能回放有关。
- Key 来源:在写入
YOUR_API_KEY前,从 TaoToken 官网获取,不用来路不明的共享 Key。 - Base URL:所有工具统一填
https://taotoken.net/api,不要带 UTM。 - Claude Code:使用
settings.json或ANTHROPIC_*环境变量,字段不要和 Codex 混用。 - Codex:使用
config.toml和model_providers,不要写ANTHROPIC_*。 - CC Switch:三件套填名称、Base URL、Key,切换后确认实际配置文件已更新。
- 日志:至少记录
session_id、turn、request_id、model、input_tokens、output_tokens、status、error。 - Checkpoint:每完成一个可验证步骤就落盘,保存 messages、tool_results、last_request_id。
- 重试:指数退避 + jitter,超过上限保存 checkpoint,不要无限打。
- 幂等:文件操作、命令、外部写入必须带幂等键,回放前先查已经完成的工具调用。
- 回放:从最后成功 checkpoint 开始,使用原模型、原参数、原 Base URL,不要随意换模型。
- 监控:观察 input token 趋势、缓存命中、429 比例、平均首 token 延迟。
- 恢复演练:在长任务上线前,人为断一次流,确认能从日志和 checkpoint 恢复。
如果只能记住一句话:200 小时长任务的稳定性,不是模型永远不报错,而是每次报错之后都能从日志回到现场。TaoToken 在这里承担的是统一模型入口:Base URL 固定,Key 单独管理,日志字段一致,回放时才能把请求、checkpoint 和工具状态对齐。
需要继续配置或验证时,可以按下面路径走:
- 模型对话:https://taotoken.net/models/detail/chat?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_chat
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_plan
- 创建 Key:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_keys
- Claude Code 文档:https://taotoken.net/doc/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=agent200h_doc
配置完成后,先用短请求验证https://taotoken.net/api、YOUR_API_KEY和模型名,再启动你的 200 小时长任务。掉线时不要删日志,先找request_id,再找checkpoint,最后按原参数回放。这样,断线才是一次可恢复事件,而不是从头再来。