TL;DR

长任务 Agent 的续跑依赖可验证的交接协议:保存任务版本、验收证据和副作用记录,恢复时重新核对真实环境。上下文压缩只减少对话长度,不能替代持久检查点。完成状态应由验收产生;不确定操作先核实再重试,并用中断实验检验恢复是否改变结果。

第二个会话为什么接不上

第一个会话写下“登录已完成”,第二个会话却可能拿到改过的代码、失效的测试数据和尚未确认的数据库写入。直接按摘要继续,就会沿用已经失效的判断。

Agent Harness 负责工具、状态、预算和验收。跨会话执行时,它还要保存当前目标、已验证结果和未完成操作。会话结束、进程崩溃和机器重启需要不同的恢复措施。

Anthropic 的 2025 年长任务实践采用初始化 Agent、功能清单、进度文件和编码 Agent:先拆出可验收功能,后续会话从环境检查开始,再用浏览器验证真实行为。

以订单查询应用为例,登录、订单过滤和错误提示分别验收。登录能演示,只能证明这一项完成;接口超时后,先查订单是否创建成功,再决定是否重试。

压缩、重置与状态外置分别负责什么

压缩减少上下文,重置建立新会话,外置状态保存任务进度与证据。

机制 保存或改变什么 主要风险 恢复时仍须检查
上下文压缩 以摘要替换部分历史 丢失约束、失败原因或证据出处 摘要中的事实是否有效
会话重置 从交接材料构造新上下文 重复探索、遗漏隐含约束 任务契约和当前环境
持久检查点 执行状态、游标、消息或节点结果 检查点与外部写入不同步 外部副作用是否提交
外置任务账本 目标、候选版本、验收及操作状态 多写者竞争、陈旧读 版本一致性和操作结果

上下文压缩不能补回未记录的事务信息,检查点也不保证下游操作只执行一次。LangGraph 持久化文档区分线程级检查点和跨线程 Store;内存检查点在进程重启后丢失。换成持久后端之后,仍需处理下游写入与检查点不同步的问题。

Anthropic 的 2026 年后续实验中,Sonnet 4.5 需要重置上下文;换成 Opus 4.5 后,该场景采用压缩,不再主动重置。选择策略要测目标任务的质量与恢复成本,这组结果不足以推断普遍优劣。

任务账本保存事实与出处,每个会话只读取当前步骤需要的内容。审计历史可以持续增长,上下文无需同步增长。

用任务账本约束交接

交接至少包含任务版本、待验收版本、验收证据和结果不确定的操作。摘要解释意图,结构化字段决定能否继续。

json
{
  "schemaVersion": 1,
  "taskId": "order-app-42",
  "revision": 7,
  "contractHash": "sha256:...",
  "artifactHash": "sha256:...",
  "checks": [
    {"id": "login-browser", "status": "passed", "evidenceId": "ev-91"}
  ],
  "effects": [
    {"operationId": "seed-order-8", "status": "unknown"}
  ],
  "nextAction": "reconcile seed-order-8 before retry"
}

revision 用于控制并发更新,artifactHash 标识代码或产物,contractHash 标识任务要求。测试结果还要关联检查器版本、环境快照和输出证据,防止新代码沿用旧结果。

更新账本时使用比较并交换(CAS):读取版本 7 的执行者提交时,账本必须仍是版本 7。发现版本冲突就重读并合并。较大的证据单独存储,账本只保存不可变引用。

passed 由验收器写入,生成 Agent 只能提交版本和申请验收。账本也要记录失败原因:权限不足时,继续修改查询 SQL 没有用。

先核对状态,再继续执行

恢复依次验证版本、核实外部操作、选择下一步。完成这些检查后,才开放写权限。

flowchart TD A[读取交接账本] --> B{任务和候选版本匹配} B -->|否| C[重新检查并更新证据] B -->|是| D{有不确定副作用} D -->|有| E[按操作标识查询下游] E --> F{可以确定结果} F -->|否| G[暂停并升级处理] F -->|是| H[补记结果或幂等重试] H --> I[选择下一项未通过验收] D -->|无| I I --> J[执行并提交候选] J --> K[独立验收并写入新版本]

下游已创建订单,客户端却在收到响应前崩溃,重放可能再次创建订单。用稳定操作标识、下游幂等键或业务主键查询结果;既查不到又无法安全重试时,暂停处理。

恢复时还要核对代码提交和服务身份。代码或租户改变,原验收就可能失效。可按变更依赖重跑受影响检查,文件修改时间不足以证明结果仍然有效。

Go 实践:拒绝陈旧证据和含糊完成

下面的 Go 1.23 示例实现交接入口检查。它使用标准库,演示候选哈希失配、不确定副作用和缺少验收证据三种情况;没有实现持久存储、签名认证或下游幂等。

go
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

type Check struct {
	ID           string
	Passed       bool
	ArtifactHash string
}

type Handoff struct {
	SchemaVersion int
	TaskID        string
	Revision      int
	ArtifactHash  string
	Checks        []Check
	UnknownOps    []string
}

func digest(data []byte) string {
	sum := sha256.Sum256(data)
	return hex.EncodeToString(sum[:])
}

func resume(h Handoff, taskID string, artifact []byte, required []string) (string, error) {
	if h.SchemaVersion != 1 || h.TaskID != taskID || h.Revision < 1 {
		return "", fmt.Errorf("invalid handoff identity or version")
	}
	currentHash := digest(artifact)
	if currentHash != h.ArtifactHash {
		return "revalidate", nil
	}
	if len(h.UnknownOps) > 0 {
		return "reconcile:" + h.UnknownOps[0], nil
	}
	verified := make(map[string]bool)
	for _, check := range h.Checks {
		if check.Passed && check.ArtifactHash == currentHash {
			verified[check.ID] = true
		}
	}
	for _, id := range required {
		if !verified[id] {
			return "verify:" + id, nil
		}
	}
	return "completed", nil
}

func main() {
	artifact := []byte("order-app revision 7")
	hash := digest(artifact)
	h := Handoff{
		SchemaVersion: 1, TaskID: "order-app-42", Revision: 7,
		ArtifactHash: hash,
		Checks: []Check{{ID: "login", Passed: true, ArtifactHash: hash}},
		UnknownOps: []string{"seed-order-8"},
	}
	required := []string{"login", "orders"}
	for _, input := range [][]byte{[]byte("changed artifact"), artifact} {
		action, err := resume(h, "order-app-42", input, required)
		if err != nil {
			panic(err)
		}
		fmt.Println(action)
	}
	h.UnknownOps = nil
	action, err := resume(h, "order-app-42", artifact, required)
	if err != nil {
		panic(err)
	}
	fmt.Println(action)
}

将代码保存为 main.go,运行 go run main.go:

text
revalidate
reconcile:seed-order-8
verify:orders

最后一行仍要求验收订单功能。required 必须来自受信任的任务要求,Agent 无权删除验收项。

哈希只检测内容变化;证据写入需要认证,数据库任务还要关联快照或业务版本。返回 revalidate 后,恢复器仍须对账,不能直接写入。

如何评测续跑是否可靠

对同一任务分别执行无中断和故障注入,比较最终验收结果,并单独统计重复写入。

中断位置 需要注入的故障 独立验收
写候选后、保存账本前 进程退出 找回候选,未误报完成
下游提交后、响应到达前 响应丢失 不重复创建订单
验收结束后、标记完成前 账本写入失败 重读证据后补记状态
两个执行者同时续跑 版本冲突 只有合法版本更新生效
压缩后恢复 删除关键失败上下文 正确读取外置阻塞原因

每个任务应多次恢复,并保留相同环境快照与不同采样种子。报告恢复成功率、重复副作用率、错误完成率、恢复延迟和额外成本;同时按任务长度、外部写入数量和交接次数切片。重复可靠性的计算可继续阅读重复 Trial 评测。

由评测器选择中断点,覆盖事务提交前后;让 Agent 自选停顿位置会漏掉危险窗口。报告中分开统计环境恢复失败与决策错误。

常见问题

一份高质量摘要为什么还不够?

摘要没有事务能力,也可能遗漏关键否定条件。“已准备测试订单”无法判断订单是否真正落库。保留摘要解释工作意图,同时用操作账本记录可查询的业务事实。

每次恢复都要运行全部测试吗?

不必。可以按依赖关系重跑受影响的检查,但需要保留候选、检查器和环境版本。对没有可靠变更分析的任务,全量验收通常比继承陈旧通过标记更可信。

验收失败后如何继续?

将失败转换为有证据的修复请求,包括观察结果、预期结果和复现条件。采用Evaluator 反馈闭环控制修订预算,避免恢复后无限返工。

应该把全部历史写进长期记忆吗?

不应无差别写入。任务事实、临时推测和用户长期偏好具有不同生命周期。交接账本服务于当前任务的可恢复性;长期记忆需要独立的更新、撤销和访问策略。

参考资料