TL;DR
长任务 Agent 的续跑依赖可验证的交接协议:保存任务版本、验收证据和副作用记录,恢复时重新核对真实环境。上下文压缩只减少对话长度,不能替代持久检查点。完成状态应由验收产生;不确定操作先核实再重试,并用中断实验检验恢复是否改变结果。
第二个会话为什么接不上
第一个会话写下“登录已完成”,第二个会话却可能拿到改过的代码、失效的测试数据和尚未确认的数据库写入。直接按摘要继续,就会沿用已经失效的判断。
Agent Harness 负责工具、状态、预算和验收。跨会话执行时,它还要保存当前目标、已验证结果和未完成操作。会话结束、进程崩溃和机器重启需要不同的恢复措施。
Anthropic 的 2025 年长任务实践采用初始化 Agent、功能清单、进度文件和编码 Agent:先拆出可验收功能,后续会话从环境检查开始,再用浏览器验证真实行为。
以订单查询应用为例,登录、订单过滤和错误提示分别验收。登录能演示,只能证明这一项完成;接口超时后,先查订单是否创建成功,再决定是否重试。
压缩、重置与状态外置分别负责什么
压缩减少上下文,重置建立新会话,外置状态保存任务进度与证据。
| 机制 | 保存或改变什么 | 主要风险 | 恢复时仍须检查 |
|---|---|---|---|
| 上下文压缩 | 以摘要替换部分历史 | 丢失约束、失败原因或证据出处 | 摘要中的事实是否有效 |
| 会话重置 | 从交接材料构造新上下文 | 重复探索、遗漏隐含约束 | 任务契约和当前环境 |
| 持久检查点 | 执行状态、游标、消息或节点结果 | 检查点与外部写入不同步 | 外部副作用是否提交 |
| 外置任务账本 | 目标、候选版本、验收及操作状态 | 多写者竞争、陈旧读 | 版本一致性和操作结果 |
上下文压缩不能补回未记录的事务信息,检查点也不保证下游操作只执行一次。LangGraph 持久化文档区分线程级检查点和跨线程 Store;内存检查点在进程重启后丢失。换成持久后端之后,仍需处理下游写入与检查点不同步的问题。
Anthropic 的 2026 年后续实验中,Sonnet 4.5 需要重置上下文;换成 Opus 4.5 后,该场景采用压缩,不再主动重置。选择策略要测目标任务的质量与恢复成本,这组结果不足以推断普遍优劣。
任务账本保存事实与出处,每个会话只读取当前步骤需要的内容。审计历史可以持续增长,上下文无需同步增长。
用任务账本约束交接
交接至少包含任务版本、待验收版本、验收证据和结果不确定的操作。摘要解释意图,结构化字段决定能否继续。
{
"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 没有用。
先核对状态,再继续执行
恢复依次验证版本、核实外部操作、选择下一步。完成这些检查后,才开放写权限。
下游已创建订单,客户端却在收到响应前崩溃,重放可能再次创建订单。用稳定操作标识、下游幂等键或业务主键查询结果;既查不到又无法安全重试时,暂停处理。
恢复时还要核对代码提交和服务身份。代码或租户改变,原验收就可能失效。可按变更依赖重跑受影响检查,文件修改时间不足以证明结果仍然有效。
Go 实践:拒绝陈旧证据和含糊完成
下面的 Go 1.23 示例实现交接入口检查。它使用标准库,演示候选哈希失配、不确定副作用和缺少验收证据三种情况;没有实现持久存储、签名认证或下游幂等。
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:
revalidate
reconcile:seed-order-8
verify:orders
最后一行仍要求验收订单功能。required 必须来自受信任的任务要求,Agent 无权删除验收项。
哈希只检测内容变化;证据写入需要认证,数据库任务还要关联快照或业务版本。返回 revalidate 后,恢复器仍须对账,不能直接写入。
如何评测续跑是否可靠
对同一任务分别执行无中断和故障注入,比较最终验收结果,并单独统计重复写入。
| 中断位置 | 需要注入的故障 | 独立验收 |
|---|---|---|
| 写候选后、保存账本前 | 进程退出 | 找回候选,未误报完成 |
| 下游提交后、响应到达前 | 响应丢失 | 不重复创建订单 |
| 验收结束后、标记完成前 | 账本写入失败 | 重读证据后补记状态 |
| 两个执行者同时续跑 | 版本冲突 | 只有合法版本更新生效 |
| 压缩后恢复 | 删除关键失败上下文 | 正确读取外置阻塞原因 |
每个任务应多次恢复,并保留相同环境快照与不同采样种子。报告恢复成功率、重复副作用率、错误完成率、恢复延迟和额外成本;同时按任务长度、外部写入数量和交接次数切片。重复可靠性的计算可继续阅读重复 Trial 评测。
由评测器选择中断点,覆盖事务提交前后;让 Agent 自选停顿位置会漏掉危险窗口。报告中分开统计环境恢复失败与决策错误。
常见问题
一份高质量摘要为什么还不够?
摘要没有事务能力,也可能遗漏关键否定条件。“已准备测试订单”无法判断订单是否真正落库。保留摘要解释工作意图,同时用操作账本记录可查询的业务事实。
每次恢复都要运行全部测试吗?
不必。可以按依赖关系重跑受影响的检查,但需要保留候选、检查器和环境版本。对没有可靠变更分析的任务,全量验收通常比继承陈旧通过标记更可信。
验收失败后如何继续?
将失败转换为有证据的修复请求,包括观察结果、预期结果和复现条件。采用Evaluator 反馈闭环控制修订预算,避免恢复后无限返工。
应该把全部历史写进长期记忆吗?
不应无差别写入。任务事实、临时推测和用户长期偏好具有不同生命周期。交接账本服务于当前任务的可恢复性;长期记忆需要独立的更新、撤销和访问策略。
参考资料
- Anthropic:Effective harnesses for long-running agents,2025-11-26。
- Anthropic:Harness design for long-running application development,2026-03-24。
- LangGraph:Persistence,检查点与 Store 的持久化边界。
- Agent 评测 Harness 的基础设计,任务、轨迹和验收的完整关系。