长时程执行与状态
第 32 课:长时程执行与状态
第 31 课把"怎么在巨大空间里搜、什么时候砍方向"讲完了;这一课只追一个问题:没人看着跑 23 小时,Agent 凭什么不跑偏、状态放哪、什么时候该停。我们从一条真实的 23 小时自主实验曲线讲起,把 best-so-far 曲线、项目状态外置(File-as-Bus)、失败诊断、环境隔离、token 成本账与停机判据,一路算到工程细节。
② 说清项目状态为什么要落在文件里(File-as-Bus),以及去掉它会掉多少分
③ 用"并发 × 单任务显存"估资源、用"证据条件"决定继续、改向还是终止
第 31 课我们把"怎么在巨大空间里搜、怎么砍方向"讲完了;这一课换个视角:让它自己长跑,凭什么不跑偏?
因为长时程最怕的不是单步慢,而是"跑了半天越跑越差还没人发现"。判断力来自一条可审计的曲线。每轮把结果写进 context(模型上下文,即模型这一次能看到的全部 token)之外的持久状态。并且只在 held-out(预留测试集,即不参与搜索的那部分数据)上变好,才计入 best-so-far(至今最好成绩)。这样曲线才会单调向上。就像长跑要看配速表,不是看某一步迈得多大;配速乱了,越跑越远也没用。所以先把这条曲线读出来。
| 读数 | 数值 | 来源 |
|---|---|---|
| 连续自主运行 | 23 小时 | arXiv:2604.13018 图 1 |
| 无干预实验轮次 | 74 轮 | 同上 |
| best-so-far 更新次数 | 18 次 | 同上 |
| 验证 AUC | 0.903 → 0.982 | 同上 |
| 算式 | 结果 |
|---|---|
| 更新密度 = 18 ÷ 74 | ≈ 0.243 → 24.3% 的轮次产生新最优 |
| AUC 绝对增幅 = 0.982 − 0.903 | 0.079 |
| AUC 相对增幅 = 0.982 ÷ 0.903 | ≈ 8.7% |
| 项 | 算式 | 结果 |
|---|---|---|
| 总分钟数 | 23 × 60 | 1,380 分钟 |
| 轮均耗时 | 1,380 ÷ 74 | ≈ 18.6 分钟/轮 |
| 含义 | 每 18.6 分钟才回一次信号 | |
判断长时程跑得好不好,先看三件事:曲线是否单调向上(靠 best-so-far)、新最优是否只在 held-out 上准入、每轮回信号要等多久。23 小时 / 74 轮 / 18 次更新 / AUC 0.903→0.982 给出的就是一条"越跑越好"的证据链。
上一张卡片看到它在 74 轮里越跑越好;可 context 是易失的——一次调用结束就清空。状态放哪,才是长时程能不能接上的关键。
因为74 轮的历史、诊断、产物不可能全塞进窗口,硬塞只会挤掉真正要用的证据;正确做法是把决策相关的状态外置到文件,每轮只把"该看的那部分"读回 context,并在关键轮写一个检查点(checkpoint,即把当前状态快照落盘,便于中断后恢复)。就像项目交接不靠某个人的记忆,靠的是共享盘里的文档;人换了,文档还在。所以要把状态从"对话"搬进"文件总线"。
| 模式 | 存什么 | 为什么 |
|---|---|---|
| append-only 日志 | 实现日志、实验诊断 | 证据不能覆盖 |
| versioned 工件 | 计划、分析、报告 | 要当前版 + 可恢复历史 |
| mutable 状态 | 短命控制信息 | 不必留历史、保持轻 |
| 基准 | 去掉 File-as-Bus | 差额 |
|---|---|---|
| PaperBench 分数 | 下降 | −6.41 分 |
| MLE-Bench Lite Any Medal | 下降 | −31.82 个点 |
| MLE-Bench Lite 剩余 | 81.82 − 31.82 = 50.00 个点 | |
| 项 | 算式 | 结果 |
|---|---|---|
| 去掉 File-as-Bus 掉的 PaperBench 分 | 6.41 | — |
| 对照:相对最强基线的提升(单 backbone Gemini-3-Flash) | 9.92 | — |
| 被吃掉的比例 | 6.41 ÷ 9.92 | ≈ 64.6% |
上下文是易失的,项目状态必须落在上下文之外(File-as-Bus)。消融给出量化代价:PaperBench −6.41、MLE-Bench Lite Any Medal −31.82。判据是:换一个角色、换一次调用,它还能不能从文件里读回上一轮的证据。
上一张卡片把状态外置到了文件;状态一持久,失败就不再"白费"。那它到底怎么从失败里爬起来?
因为长时程的失败往往是沉默的——进程没崩,但指标不对,成因可能横跨论文解读、数据、模型、训练配置、基础设施好几层;这就要求每轮失败都被写成"诊断记录"(错在哪一环 + 依据),这就要求把它写回文件而不是留在 context 里,下一轮才谈得上修。就像医生看检验单:不先定位是哪个器官,开再多药也是碰运气。所以回路的关键不是"重试",是"先诊断再修"。
| 层 | 典型症状 | 干预 |
|---|---|---|
| 论文解读 | 方法与目标对不上 | 退回分析、改假设 |
| 数据处理 | 指标偏低但能跑 | 修预处理 |
| 模型设计 | 结构不匹配 | 改实现 |
| 训练配置 | 不收敛 / 波动 | 调超参 |
| 基础设施 | OOM / 超时 | 改资源 |
| 交互 | 多个原因叠加 | 分而治之 |
| 项 | 算式 | 结果 |
|---|---|---|
| 总轮次 | 74 | — |
| 假设首跑失败率(示意) | 40% | — |
| 触发诊断的轮次 | 74 × 0.4 | 29.6 轮 |
| 项 | 算式 | 结果 |
|---|---|---|
| 轮均耗时 | 1,380 ÷ 74 | ≈ 18.6 分钟/轮 |
| 一次诊断 + 重跑(按 2 轮计) | 2 × 18.6 | ≈ 37.2 分钟 |
| 对照"闷头重试" | 诊断省下的是后面 N 轮的无效盲跑 | |
失败要能被诊断成"错在哪一环",才能被复用。回路 = 产出证据 → 定位层级 → 选干预 → 写回状态;触发条件是"指标缺口 / 偏差",而不是"进程崩溃"。判定:它有没有把失败写成下一轮的输入。
上一张卡片讲失败怎么被诊断;但要是多个假设同时跑、还共用同一份代码,诊断就没了意义——A 的改动会污染 B 的证据。为什么要给每个假设一套隔离环境?
因为长时程常常并行搜索,多个执行器会同时改文件、抢显存;没有隔离,一个假设的失败会被误判成另一个的,反过来也一样。就像多人同时编辑同一份文档,谁改坏了都说不清;一人一份副本 + 合并审查才可控。所以一个假设一个工作树,再加资源预算兜底。
| 边界 | 隔离对象 | 目的 |
|---|---|---|
| worktree | 代码工作目录 | 改动互不覆盖 |
| 工件目录 | 输出 / 日志 / 模型 | 证据归属清晰 |
| 资源配额 | GPU 显存 / tokens | 防止一个吃光全部 |
| 项 | 算式 | 结果 |
|---|---|---|
| 并发任务数(示意) | 8 | — |
| 单任务显存(示意) | 24 GB | — |
| 总显存需求 | 8 × 24 | 192 GB |
| 按 48 GB/卡折算 | 192 ÷ 48 | 4 张卡 |
| 维度 | 共享一份 | 一假设一 worktree |
|---|---|---|
| 证据可信 | 互相污染 | 可归因 |
| 失败定位 | 难 | 清楚 |
| 合并 | 隐式覆盖 | 显式准入 |
| 代价 | 低 | 多占磁盘 |
并发搜索的前提是失败互不污染,所以一个假设一套 worktree + 独立工件目录 + 资源配额。资源账 = 并发数 × 单任务显存 → 需要多少卡。判定:任意两条支线的证据能不能分别归因。
上一张卡片把并发折成了卡数;卡数背后是钱。这笔账怎么算才算得清?
因为长时程要跑 74 轮、每轮涉及多次模型往返,成本随轮数线性放大;只看"单次调用多少钱"会严重低估。所以要把账拆成 时间 × 并发 × 单价,并对照第 26 课的 token 口径。就像出差报销:不是看单张发票,是把交通、住宿、补贴乘天数一起算。所以先立一个可复算的成本公式。
| 项 | 算式 | 结果 |
|---|---|---|
| 轮数 | 74 | — |
| 单轮普通对话量(示意,承第 26 课 20,000 锚点) | 20,000 token | — |
| 总 token(1× 口径) | 74 × 20,000 | 1,480,000 token |
| 口径 | 单轮 token | 74 轮合计 |
|---|---|---|
| 普通对话 1× | 20,000 | 1,480,000 |
| 单 Agent 4× | 80,000 | 5,920,000 |
| 多智能体 15× | 300,000 | 22,200,000 |
| 项 | 算式 | 结果 |
|---|---|---|
| 总秒数 | 23 × 3,600 | 82,800 秒 |
| 轮均 | 82,800 ÷ 74 | ≈ 1,119 秒/轮 |
| 含义 | 时间账按小时,token 账按轮数——两者都要算 | |
长时程的成本公式 = 时间 × 并发 × 单价,其中 token 是暗账。对照口径(第 26 课):单 Agent ≈4×、多智能体 ≈15× 普通对话。判定:报一条 run 时,能不能同时给出小时数与总 token 数。
上一张卡片把账算平了;账再平,也总得有个停的时候。什么信号说明该停?
因为长时程里"再多跑一轮"永远有诱惑(沉没成本 + 可能翻盘),但预算有限;把停与不停交给证据条件,比交给"感觉差不多了"可靠得多。就像考试到点就得交卷:不是不会写了,是时间到了;该交还是该再想,要有明确规则。所以把 continue / revise / terminate 写成可执行判据。
| 动作 | 触发条件 | 依据 |
|---|---|---|
| continue | 近期仍有 best-so-far 更新 | 证据在累积 |
| revise | 连续多轮无更新且方向单一 | 换支线 / 换假设 |
| terminate | 预算触顶或 held-out 长期不升 | 证据不足 + 成本已满 |
| 项 | 算式 | 结果 |
|---|---|---|
| 平均更新间隔 | 74 ÷ 18 | ≈ 4.11 轮/次 |
| 容忍阈值(2× 平均间隔,示意) | 2 × 4.11 | ≈ 8.22 轮 |
| 含义 | 连续约 8 轮无 best-so-far → 触发 revise | |
| 信号 | 含义 | 动作 |
|---|---|---|
| held-out 不升且预算触顶 | 证据不足 + 成本满 | terminate |
| 总 token 达上限 | 钱花完 | terminate |
| 仍有支线待试且预算未满 | 有证据空间 | continue |
停机不是"感觉",是证据条件——continue / revise / terminate 三选一。用 best-so-far 的更新密度定"该换方向",用预算 + held-out 定"该终止"。判定:每一次收手都能说清触发的是哪条判据。