1. 长时域任务里,Agent 为什么总在“最后一公里”前停下
如果你最近在跑 Agent 评测,大概率遇到过这种场景:任务本身不难,模型也“会做”,但跑到一半就自己收尾了,输出一句“已完成”或者干脆不再调用工具。AutoLab 那篇长时域评测论文里有个数字很扎眼——43.3% 的提前终止率,也就是说接近一半的失败不是“不会”,而是“不跑了”。这个现象在短任务里几乎看不出来,因为短任务一轮就结束,没有“坚持”这个维度。
长时域任务(long-horizon)的本质是:从一个正确但次优的基线出发,在严格的墙钟预算内反复“跑基准 → 分析 → 编辑 → 再跑”,直到收敛或超时。它考验的不是单次推理质量,而是持续迭代的持久性。claude-opus-4.6 在这类任务里提前终止率只有 22%,平均迭代 8.3 轮,而不少模型 4~5 轮就停了。差距不在“聪明”,在“愿不愿意继续改”。
这篇要交付的是一套可复制的本地评测骨架:用 TaoToken 统一 Key 接入 claude-opus-4.6 等模型,把长时域闭环跑起来,然后定位你的 Agent 到底在第几轮、因为什么触发点提前放弃。适合正在做 Agent 评测、闭环优化、或者想复现 AutoLab 类基准的工程师。
2. 前置:用 TaoToken 统一 Key 管住多模型评测
长时域评测的第一个工程坑不是算法,是 Key 管理。你要对比 claude-opus-4.6、claude-sonnet-4、gpt-4o 在同一个任务上的迭代行为,如果每家一个 SDK、一套鉴权、一份重试逻辑,评测脚本会先把自己跑崩。TaoToken 的价值在这里很直接:一个 API 端点、一个 Key,兼容 Anthropic 和 OpenAI 两种调用风格,模型名切换即可对比。
先拿 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 列表在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。API 基址统一用 https://taotoken.net/api (这个不加 UTM,直接写进配置)。
注意:Key 只存在本地环境变量或配置文件里,不要硬编码进评测脚本提交到仓库。长时域任务会跑很多轮,Key 泄露的暴露面比短任务大得多。
模型侧建议先确认可用模型名,用模型对话页快速验证: https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。如果你要长期跑编码类 Agent,Coding Plan 更划算: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。
3. 可复制配置:settings.json 与 config.toml 双骨架
下面这套配置我按“评测脚本能直接读”的标准写。核心思路:把模型、预算、收敛判断、检查点全部外置成配置,Agent 逻辑里不写死任何轮次。
3.1 settings.json(Anthropic 风格调用)
{ "api_base": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "default_model": "claude-opus-4.6", "fallback_models": ["claude-sonnet-4", "gpt-4o"], "long_horizon": { "wall_clock_budget_sec": 1800, "max_iterations": 100, "checkpoint_interval": 10, "convergence": { "patience_rounds": 3, "min_improvement": 0.001 }, "early_stop_guard": { "enabled": true, "min_iterations_before_stop": 5, "require_benchmark_after_edit": true } } }这里有两个关键字段值得展开。convergence用的是“连续 3 轮改进小于 0.1% 才停”,而不是“跑满 10 轮就停”——这正是论文里强调的收敛判断而非固定轮次。early_stop_guard是我自己加的护栏:模型在第 5 轮之前不允许主动终止,且每次编辑后必须重新跑一次基准,防止它“改完不验证就宣布完成”。
3.2 config.toml(OpenAI 风格 / 多模型对比)
[provider] base_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" timeout_sec = 120 max_retries = 3 [models.claude-opus-4.6] display = "claude-opus-4.6" context_window = 200000 [models.claude-sonnet-4] display = "claude-sonnet-4" [models.gpt-4o] display = "gpt-4o" [evaluation] task_dir = "./tasks/autolab_like" result_dir = "./runs" log_every_iteration = true save_trajectory = truesave_trajectory = true是定位提前放弃的关键。没有完整轨迹,你只能看到“失败了”,看不到它在第几轮、看到什么反馈之后决定停的。
3.3 环境变量与启动
export TAOTOKEN_API_KEY="sk-你的key" python run_eval.py --config config.toml --model claude-opus-4.64. 验证请求:先确认单轮能通,再跑长时域
别一上来就跑 30 分钟的长任务。先用一个最小请求确认链路通,再进闭环。
import os, json, urllib.request API_BASE = "https://taotoken.net/api" KEY = os.environ["TAOTOKEN_API_KEY"] payload = { "model": "claude-opus-4.6", "max_tokens": 256, "messages": [ {"role": "user", "content": "回复两个字:就绪"} ] } req = urllib.request.Request( f"{API_BASE}/v1/messages", data=json.dumps(payload).encode(), headers={ "Content-Type": "application/json", "x-api-key": KEY, "anthropic-version": "2023-06-01" }, method="POST" ) with urllib.request.urlopen(req, timeout=60) as resp: print(resp.status, resp.read().decode()[:200])返回 200 且内容里出现“就绪”,说明 Key、基址、模型名三者对齐。如果返回 401,检查 Key 是否带空格;返回 404,检查模型名拼写;返回 429,说明并发或额度问题,长时域任务要控制并发数。
链路通了之后,跑一个 3 轮的迷你闭环,观察轨迹文件里每轮的improvement和stop_reason:
python run_eval.py --config config.toml --model claude-opus-4.6 --max-iterations 3 --task ./tasks/demo cat ./runs/demo/trajectory.jsonl | python -c " import sys, json for line in sys.stdin: r = json.loads(line) print(r['iter'], r.get('improvement'), r.get('stop_reason')) "正常的长时域轨迹应该是improvement逐步收敛、stop_reason为converged或budget_exhausted。如果stop_reason频繁出现model_declared_done而improvement还在明显上升,那就是提前放弃的典型信号。
5. 本篇常见错排查:提前放弃的四个触发点
5.1 模型在第 2~3 轮就宣布完成
最常见。原因是提示词里没有明确“未达收敛阈值不得终止”,模型把“改了一版”当成“改好了”。修法是在 system prompt 里加硬约束:每轮必须输出当前基准分数,且只有连续 N 轮改进低于阈值才允许调用终止。配合early_stop_guard.min_iterations_before_stop双保险。
5.2 编辑后不重新跑基准,直接进入下一轮
这会让 Agent 在“盲改”,几轮后自己也不知道有没有变好,于是放弃。require_benchmark_after_edit = true强制每次编辑后必须有一次基准调用,轨迹里能看到benchmark_after_edit: true才算合规。
5.3 反馈太长导致上下文被截断
长时域任务跑到第 8 轮以后,历史轨迹可能撑爆上下文,模型“忘了”前面试过什么,重复劳动后失去耐心。解法是检查点机制:每 10 轮把状态摘要写入checkpoint.json,下一轮只加载摘要而非全量历史。这也是论文里建议的 checkpoint 设计。
5.4 墙钟预算设得太紧
wall_clock_budget_sec如果小于模型完成 5 轮所需时间,模型会在预算耗尽前“主动收尾”以避免超时惩罚。先用--max-iterations 3测单轮耗时,再按单轮耗时 × 预期轮次 × 1.5设预算。
提示:排查时优先看
trajectory.jsonl里的stop_reason字段,它比最终成功率更能说明问题。model_declared_done是主动放弃,budget_exhausted是预算问题,converged才是正常收敛。
6. 把闭环跑成习惯:从单次评测到持续对比
跑通一次不代表什么,长时域评测的价值在于横向对比。把config.toml里的模型名换掉,同一批任务、同一套收敛参数,跑 claude-opus-4.6 和 gpt-4o,对比两者的平均迭代轮次和提前终止率。你会发现短任务上打平的两个模型,在长时域上差距可能拉到 2 倍以上——这正是论文里“短跑冠军不等于马拉松选手”的工程复现。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各语言 SDK 的完整参数说明。如果你要跑的是编码类长时域 Agent,比如 ClaudeCode 风格的持续重构任务,Coding Plan 的额度模型更适合长时间挂机: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。先把early_stop_guard打开,再跑一轮 10 任务的对比,你会清楚看到自己的 Agent 到底卡在第几轮。