长任务 P0 三件:断点进库、错误分类+熔断、Run/Step 时间线

断点进库:task_checkpoints 表(PG 新库 + 老库幂等补表 + 迁移清单
对齐);worker 在 goal 任务执行期间每 30 秒把磁盘 host-state 断点
镜像进库,终局再补一次;断点续跑认领前,若 host-state 条目丢失
但库里有断点,先按库断点重建最小条目再跑,库里也没有断点时不
造假、走原有拒绝路径。

错误分类+熔断:sandbox 四处解析加永久错误分支(KeyError、配置
缺失等无结论异常退出判 failed 死信,不再判 interrupted;host 已
明言 interrupted 的不受影响,未知错误不分类);worker 自动恢复
加退避(10 秒起指数退避、封顶 300 秒)与无进展熔断(库断点轮次
连续 3 次不变即标 auto_resume_stalled 等人工)。

时间线:task_steps 表 + host 每轮写 host-steps 步骤文件 + worker
导入;GET /tasks/{id}/timeline 一次返回 steps 与 checkpoints;
认领、暂停、自动恢复、终局都记步骤。旧任务返回空数组,不编造。

单测:cloud/tests/test_p0_three.py 14 项(分类、熔断、镜像重建、
步骤导入去重、timeline 接口)。
This commit is contained in:
host committed 2026-10-29 15:13:01 +08:00
1 parent d56bd3a99e
commit 585242fc8f
7 files changed
+982 -3

No files matched your search

+19
View File
@@ -2060,6 +2060,25 @@ def run_goal_loop(server, state, state_path, task, goal_text, verify,
print(f"round {round_no}: turn={result['status']} "
f"verify_rc={v_rc if v_rc is not None else '-'} "
f"verify_out={last_verify[:300]}")
# P0 三件其三:每轮步骤落盘(与 host-state 同目录的
# host-steps/<任务>.jsonl,worker 镜像/终局时导入
# task_steps,供 timeline 接口读;写失败只记不炸循环)。
try:
_steps_dir = os.path.join(
os.path.dirname(os.path.abspath(state_path)),
"host-steps")
os.makedirs(_steps_dir, exist_ok=True)
with open(os.path.join(
_steps_dir, "%s.jsonl" % task["name"]),
"a", encoding="utf-8") as _fh:
_fh.write(json.dumps(
{"round": round_no,
"turn_status": result["status"],
"verify_out": last_verify[:200],
"ts": time.time()},
ensure_ascii=False) + "\n")
except Exception:
pass
if verify is None:
# 模型自决:只有完整跑完的回合里的标记行才算数。
if (result["status"] == "completed"