Appearance
长任务 Agent:持续进度、恢复与 Harness 演进
前置:控制平面、执行平面与 Agent 协作模式
核心问题:当任务跨进程、跨 Context 甚至跨天继续时,系统靠什么证明“做到哪了”,而不是靠模型记住整段聊天?
长任务不是“让 Context 更长”
核心直觉:任务能跨进程、跨 Context 继续,靠的是外部可恢复事实,不是靠模型一直记住过去。
1. 为什么更大的 Context 仍然不够
一个代码迁移任务可能持续两天。期间会发生:
- Model Context 被压缩或重置;
- Worker 进程重启;
- 用户等待数小时后再批准;
- Workspace、Git HEAD、依赖版本发生变化。
如果系统只保存 transcript,新进程仍然很难可靠回答:
- 已经完成了哪些步骤?
- 哪些文件真的改过?
- 哪些测试真的运行并通过?
- 哪个外部动作已经产生过副作用?
2. 三类外部事实
- Progress State:任务做到哪一步、下一步是什么。
- Artifact:代码 diff、测试报告、搜索结果、计划等可复用工作成果。
- Checkpoint:一个以后可以安全恢复的状态边界。
Checkpoint consistency、idempotency 与 crash window 的完整机制放在 Advanced。
3. Context 是工作集,不是唯一状态源
可以把 Model Context 理解成当前回合的“工作桌面”。桌面上的东西可以被裁剪、压缩、重新组装;真正需要跨进程成立的事实应该放到 Runtime state、DB、artifact store、workspace 等外部载体。
text
Durable Progress / Artifact / State
↓
Harness 重新组装
↓
New Context
↓
Model因此 Resume 不是“把旧 Prompt 再发一遍”,而是读取仍然有效的事实,重新建立当前 Context。
4. 恢复时世界可能已经变了
旧任务留下“准备修改 config.yaml”并不代表今天还应该直接修改。恢复时至少要意识到:代码版本、权限、依赖和用户意图可能已经变化。后续会把这个问题正式展开成 revalidation / environment reconciliation。
Advanced / Canonical 深入内容
1. 为什么“让 Agent 跑更久”不是长任务能力
一个 Agent 连续运行 100 个 Turn,并不意味着它具备 Long-running 能力。
真正的长任务会跨越至少四种边界:
text
Context boundary
Process boundary
Human/external wait boundary
Environment change boundary例如一个持续两天的代码迁移任务:
- 第一天分析仓库并修改 20 个文件;
- 中途 Context 接近上限,需要 compaction/reset;
- 晚上服务升级,原进程结束;
- 第二天等待人工审批;
- 审批通过后继续测试;
- 期间仓库可能出现新的 commit。
如果系统只有一个不断增长的 messages 数组,它无法可靠回答:
text
哪些工作真的完成?
哪些 side effect 已经提交?
恢复后从哪里继续?
哪些旧结论已经被环境变化推翻?因此 Long-running Agent 的核心不是“更长的聊天”,而是:
跨不连续执行环境维持可验证进度,并在恢复时避免丢状态、重复副作用和错误继承。
Harness 还常包含对“当前模型能力”的补偿。模型升级以后,这些补偿可能由帮助变成负担,因此 Harness 本身也必须能够演进。
2. Mental Model:连续性不是一条 Context,而是多层持久状态
可以把长任务理解成四层连续性:
text
┌───────────────────────────────────┐
│ Goal / Durable Task State │ 最终目标、约束、已完成工作
├───────────────────────────────────┤
│ Checkpoint / Execution Records │ 从哪里可以安全恢复
├───────────────────────────────────┤
│ Workspace / Artifacts │ 文件、patch、测试结果、产物
├───────────────────────────────────┤
│ Model Context │ 当前这一轮真正看到的工作集
└───────────────────────────────────┘四层生命周期不同。
最短的是 Model Context:可能几十分钟内就被 compaction/reset。
Workspace 可能持续数小时或数天。
Durable Task State 和 Execution Record 则可能需要保存到任务彻底结束甚至审计期之后。
因此:
text
Context continuity
≠
Task continuity长任务恢复的核心,是从 durable facts 和 environment 中重新构建足够的 Context,而不是强行保存无限长的 transcript。
3. 核心机制
3.1 Progress State:记录“已经完成什么”
长任务最容易犯的错误是只保存:
text
conversation history但历史里可能充满探索、错误尝试、过期计划。
真正恢复需要的是结构化 Progress State,例如:
python
@dataclass
class TaskProgress:
task_id: str
objective: str
constraints: list[str]
completed_steps: list[str]
pending_steps: list[str]
blockers: list[str]
important_decisions: list[str]
artifact_refs: list[str]Owner
Progress State 应由 Runtime / TaskStore 持有。
模型可以建议:
“Step 3 已完成。”
但 Runtime 应结合 execution evidence 决定是否写入 completed_steps。
生命周期
text
task created
→ progress initialized
→ after meaningful boundary update
→ checkpoint
→ resume
→ final task complete它可以跨多个 Run / Session / process。
3.2 Checkpoint:定义安全恢复边界
Checkpoint 不是简单“把所有对象 pickle 一下”。
更准确的工程意义是:
在一个已知一致边界保存足够状态,使未来能够从该边界继续执行。
例如:
text
Node A succeeded
Node B succeeded
Node C not startedCheckpoint 应明确:
text
A/B 已提交
C 未执行恢复后不能盲目重跑 A/B。
对于有副作用的 Node,这一点尤其关键。
例如:
text
send_email succeeded
process crashed before next node如果恢复逻辑只知道:
“整个 step 没完成。”
就可能再次发送邮件。
所以 checkpoint 必须与:
- execution record;
- idempotency key;
- pending writes;
- external side-effect reconciliation
一起设计。
3.3 Artifact:把工作成果放到 Context 之外
长任务中的很多状态天然适合外部化:
text
repo files
patch
report.md
progress.md
test-results.json
trace
datasetAnthropic 在 long-running coding agent 实践里强调通过 progress artifacts 和环境状态跨 Context 继续工作。
Artifact 的优势是:
text
durable
addressable
re-readable
not permanently occupying context模型需要时再读取:
text
artifact ref
↓
read relevant slice这比把完整内容永远塞在聊天历史里更可控。
3.4 Context Handoff:新 Context 怎样接上旧工作
新的模型 Context 不应该从“请继续”三个字开始。
恢复包至少应该包含:
text
objective
constraints
current progress
unresolved blockers
important decisions
artifact refs
latest environment state
next candidate actions可以形成:
yaml
task:
objective: migrate auth module
completed:
- locate old token API
- replace parser in 5 files
pending:
- integration tests
- deployment config approval
decisions:
- keep backward-compatible token format
artifacts:
- workspace://diff/42
- artifact://test/last
checkpoint:
id: cp-19这个结构是为了执行,不是为了“总结得好看”。
3.5 Environment Reconciliation:恢复时不能假设世界没变
长任务暂停期间,外部环境可能变化:
text
Git branch updated
API version changed
ticket state changed
file deleted
approval expired
database row modified所以 Resume 不是:
text
load checkpoint
→ blindly continue而应该:
text
load checkpoint
→ revalidate environment assumptions
→ reconcile conflicts
→ possibly replan
→ continue例如代码任务:
text
checkpoint commit = abc123
current HEAD = def456Runtime 应意识到工作基线改变,而不是把旧 patch 强行应用。
3.6 Harness Assumption:为什么模型升级会让旧策略过时
Harness 往往会编码很多“因为模型目前不够强,所以系统替它做”的策略:
text
强制 planner → executor → critic
每两轮强制 summary
固定 Tool 顺序
大量 few-shot
人为限制一次只能调用一个 Tool
频繁 context reset这些策略在某个模型版本上可能确实有净收益。
但模型能力变化后:
- 更强的 planning 可能让固定 planner 链成为额外 latency;
- 更大的有效 Context 可能让过频 compaction 损失信息;
- 更好的 Tool Calling 可能让复杂路由 Prompt 变成噪音。
Anthropic Managed Agents 的经验明确指出:Harness 会携带关于模型能力的假设,而这些假设可能随着模型进步而失效。
因此 Harness 的 workaround 应该是:
text
explicit
versioned
replaceable
measurable而不是永久写死。
4. 如何设计:长任务连续性与 Harness 演进的边界
4.1 什么必须 Durable
如果丢失后会导致:
- 无法恢复;
- 重复副作用;
- 违反用户约束;
- 无法审计;
就应该 durable。
典型:
text
objective
approval state
committed side effects
checkpoint
artifact refs
idempotency records4.2 什么不应该强行 Durable
运行时对象:
text
asyncio.Task
open socket
DB connection
file descriptor
HTTP client不应该尝试跨进程恢复。
恢复时应该重建这些 ephemeral resources。
4.3 什么应该重新验证
属于外部世界的事实:
text
branch HEAD
remote API state
inventory
ticket status
permission恢复时应该重新获取,而不是把旧 Observation 当永恒事实。
4.4 Harness workaround 什么时候删除
不能因为“新模型看起来更强”就删。
正确过程:
text
old policy ON baseline
vs
old policy OFF candidate
↓
same eval set
↓
success / latency / token / error只有证据支持,才删除或调整。
5. 真实执行案例:一个跨两天的代码迁移任务
用户目标:
“把旧认证模块迁移到新 API,确保所有测试通过;部署配置修改必须审批。”
Day 1 / Step 1:初始化任务
Runtime 创建:
text
task_id = T-17
run_id = R-1
workspace = W-9保存:
text
objective
constraints
repo HEADStep 2:Agent 调查并修改代码
模型逐步:
text
search
→ read
→ patch
→ testExecution Plane 产生真实:
text
workspace diff
test results
tool execution recordsStep 3:Context 接近阈值
ContextManager 不保存全部 raw history,而生成结构化 handoff:
text
completed
pending
decisions
failed attempts
artifact refs原 transcript 仍可归档用于 debug。
Step 4:写 Checkpoint
此时:
text
12 files patched
unit tests passed
integration tests pending
deployment config untouchedRuntime 保存:
text
checkpoint cp-4并记录每个 patch/test execution。
Step 5:服务升级,进程退出
丢失的是:
text
Python objects
model connection
event loop tasks没有丢:
text
TaskProgress
Checkpoint
Workspace
Artifacts
Execution RecordsDay 2 / Step 6:恢复
新进程:
text
load cp-4
attach W-9但首先发现:
text
remote branch HEAD changed因此进入 reconciliation:
text
fetch latest
compare touched files发现无冲突后继续。
Step 7:模型请求修改 deployment config
Policy 返回:
text
WAITING_APPROVAL状态持久化,计算停止。
Step 8:用户批准
Runtime 重新校验:
- approval 仍有效;
- target config 仍存在;
- 当前 identity 仍有权限。
然后才执行 patch。
Step 9:全部测试通过
完成证据:
text
integration test exit_code = 0
workspace diff recorded
deployment approval record exists
RunState = SUCCEEDED
TaskProgress = COMPLETE注意:
“模型说迁移完成”不是完成证据。
6. 最小实现:Resume Bundle
python
from dataclasses import dataclass
@dataclass
class ResumeBundle:
task_id: str
checkpoint_id: str
objective: str
constraints: list[str]
completed: list[str]
pending: list[str]
artifact_refs: list[str]
workspace_ref: str
environment_version: str恢复:
python
async def resume(bundle: ResumeBundle):
state = await checkpoint_store.load(
bundle.checkpoint_id
)
workspace = await workspace_manager.attach(
bundle.workspace_ref
)
current_env = await inspect_environment(workspace)
if current_env.version != bundle.environment_version:
await reconcile(state, current_env)
context = await context_manager.rehydrate(
state=state,
artifacts=bundle.artifact_refs,
)
return await runtime.continue_run(
state,
context,
)这里有三个关键思想:
text
attach existing workspace
revalidate environment
rehydrate context而不是“把旧 messages 再发一遍”。
7. Shadow Harness 中如何落地
建议:
text
state/
├── task_store.py
├── checkpoint_store.py
└── execution_record.py
workspace/
├── manager.py
└── snapshot.py
context/
├── compactor.py
└── rehydrator.py
runtime/
├── resume.py
└── reconciliation.py
eval/
└── long_horizon.py关键记录:
text
TaskProgress
Checkpoint
NodeExecutionRecord
ToolExecutionRecord
ApprovalRecord
ArtifactReference
WorkspaceSnapshot
RunManifest其中 RunManifest 用来解释:
text
当时使用了哪个 model/prompt/tool schema/runtime version?长任务没有版本信息,恢复和回归分析都容易变成猜测。
8. Production Engineering:长任务真正难在哪里
8.1 Side Effect 必须幂等或可对账
长任务 crash 很正常。
要假设:
text
外部动作已经成功
但本地确认记录可能还没写完所以至少需要:
- idempotency key;
- operation record;
- reconciliation。
8.2 Checkpoint 频率是 Trade-off
太频繁:
text
storage I/O ↑
latency ↑太稀:
text
crash 后重做工作 ↑
副作用边界更难判断应在有意义的一致边界保存,而不是每个 token checkpoint。
8.3 Artifact 需要 provenance
必须知道:
text
谁生成?
基于哪个 input/version?
是否仍然有效?否则第二天读到一个旧测试报告,模型可能把它当成当前事实。
8.4 Resume 要有 deadline/budget 重新计算
恢复不是无限延长原任务。
需要重新检查:
text
用户是否取消?
deadline 是否过期?
预算是否还有?
approval 是否失效?8.5 Harness 策略需要版本化 Eval
Model upgrade 时至少重新跑:
- long-horizon success;
- tool trajectory;
- compaction fidelity;
- recovery;
- cost/latency。
否则“升级模型”可能被旧 Harness 抵消。
9. Failure Modes
9.1 把完整 Transcript 当成唯一任务状态
错误表现。 恢复只依赖历史 messages,没有结构化 progress/checkpoint。
为什么危险。 对话包含过期探索,且无法证明哪些 side effect 已成功;Context 还可能超过窗口。
修复方向。 分离 transcript、TaskProgress、Checkpoint、ExecutionRecord 和 Artifact。
验证方式。 删除模型 Context,仅依靠 durable state 恢复;断言任务仍能定位 pending work。
9.2 Resume 时重跑已经成功的副作用
错误表现。 crash 后从整个 step 开始,重复发邮件、重复写 DB 或重复提交 patch。
为什么危险。 产生不可逆业务错误。
修复方向。 为 side effect 保存 execution/idempotency record,在恢复前查询已提交结果。
验证方式。 在“外部成功、本地即将 checkpoint”位置故障注入,恢复后断言 side effect 总次数仍为 1。
9.3 Compaction 丢失关键约束
错误表现。 新 Context 记得“继续迁移”,却忘了“部署配置必须审批”。
为什么危险。 有损摘要把安全/业务约束永久丢失。
修复方向。 protected state 与自由 summary 分离,constraints 作为结构化 durable facts 注入新 Context。
验证方式。 多次 compaction 后检查 protected constraints 逐字/结构保持。
9.4 Resume 时相信过期环境
错误表现。 两天前的 repo/API 状态被当成当前事实继续执行。
为什么危险。 patch 冲突、错误决策、权限变化都可能被忽略。
修复方向。 为外部环境保存 version/provenance,resume 时执行 reconciliation。
验证方式。 修改远端 branch 或资源状态后恢复,断言 Runtime 进入 revalidation/replan 而不是直接执行旧 Action。
9.5 Harness workaround 永久化
错误表现。 新模型升级后仍保留大量旧 planner/critic/summary 链,从不做 ablation。
为什么危险。 增加 token/latency,甚至压制新模型能力。
修复方向。 把 workaround 做成 versioned policy,并为每项记录其假设和 Eval 证据。
验证方式。 Model upgrade 后运行 policy ON/OFF 对照,只有有统计/工程收益的策略继续保留。
10. 如何验证:Testing 与 Evaluation
10.1 Crash Injection Test:外部成功后立即崩溃
流程:
text
execute side effect
→ external returns success
→ inject crash before next checkpoint
→ resume断言:
text
side effect executed exactly once这是 deterministic reliability test。
10.2 Context Reset Test:完全丢弃当前模型 Context
保留:
text
checkpoint
task progress
workspace
artifacts创建全新 model session。
断言它能够恢复:
- objective;
- constraints;
- completed/pending;
- next valid action。
10.3 Environment Drift Test
Checkpoint 后修改:
text
repo HEADResume 时断言:
text
reconciliation triggered而不是继续使用旧基线。
10.4 Compaction Fidelity Test
构造 20 个必须保存的 facts:
text
5 constraints
5 decisions
5 completed steps
5 failed attempts连续做多次 compaction/reset。
检查:
text
protected facts retained10.5 Evaluation:Harness Policy Ablation
对真实长任务 Eval Set 比较:
text
old harness policy
new simplified policy指标:
- success rate;
- turns;
- input tokens;
- latency;
- repeated work;
- recovery correctness。
语义任务质量属于 Eval;crash/recovery/idempotency contract 属于 Testing。
11. 常见问题(Q&A)
Q1:Long-running Agent 最大的问题是不是 Context Window 不够大?
不是。
Context Window 只是其中一个限制。真正难的是跨 Context、进程、人工等待和外部环境变化保持可验证连续性。
即使模型有超大 Context,如果进程 crash 后会重复付款/重复发邮件,这个系统仍然没有 Long-running reliability。
Q2:Checkpoint 和 Compaction 有什么区别?
Compaction 解决:
下一次模型推理应该怎样用更短表示继续理解任务。
Checkpoint 解决:
程序未来从哪个一致执行边界安全恢复。
一个偏 Context representation,一个偏 durable execution。它们可能在同一时机触发,但语义不同。
Q3:为什么不能直接序列化整个 Runtime 对象?
因为很多对象没有稳定的跨进程语义,例如 socket、DB connection、event-loop Task、open file descriptor。
Durable State 应保存领域事实和 reference;恢复时重新建立 ephemeral resource。
Q4:Progress State 和 Memory 是一回事吗?
不是。
Progress State 是某个长任务必须依赖的执行事实,例如“Node A 已完成”“正在等审批”。
Memory 通常是跨任务/会话可选择检索的经验或用户信息。Progress State 的一致性要求更高,也不应依赖语义检索“碰巧找到”。
Q5:为什么 Resume 时还要重新检查环境?
因为外部世界在暂停期间可能变化。
Checkpoint 只能证明“当时是什么状态”,不能保证当前 Git、API、权限、资源仍然一样。
所以 Resume 应包含 environment revalidation。
Q6:Artifact 为什么比把所有内容塞进 Context 更适合长任务?
Artifact 可以持久化、按引用读取、保留 provenance,而且不永久占用 Token。
Context 只在当前 inference 中需要工作集;Artifact 是外部工作成果。二者生命周期不同。
Q7:Harness 为什么会随着模型能力变强而需要删除代码?
因为 Harness 经常承担模型弱点的补偿。
当模型能力变化后,旧的分步、critic、summary 或 tool workaround 可能不再产生收益,却继续消耗 Token 和 latency。
所以 Harness 需要“可删除的脚手架”,而不是只会不断加层。
Q8:怎么判断某个旧 Harness 策略是否还能保留?
不要凭感觉,也不要因为策略“以前有用”就默认永久保留。
应该在同一版本化 Eval Set 上做 controlled ablation:
text
policy ON
vs
policy OFF除了最终任务成功率,还要同时观察 token、latency、Tool trajectory、recovery correctness 和新出现的 failure mode。如果关闭策略后质量不下降而成本更低,就应该考虑删除;如果某类任务明显退化,则保留策略并记录它依赖的模型/任务假设。这样 Harness 才能随着模型能力演进,而不是只会不断堆补丁。
12. Long-running Harness 的稳定边界
Long-running Agent 的稳定结构可以表示为:
text
不是:
一个永远不断的聊天
而是:
durable task facts
+ recoverable execution boundary
+ persistent artifacts/workspace
+ replaceable model context
+ side-effect consistency
+ environment reconciliationHarness 也需要随模型能力变化持续校准:
text
Model capability changes
↓
old assumptions become questionable
↓
versioned Eval / ablation
↓
keep / simplify / remove policy所以长任务工程的两条主线是:
让任务状态比单次 Context 活得更久;让 Harness 假设不要比它的证据活得更久。
13. Sources
Anthropic — Effective harnesses for long-running agents
https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agentsAnthropic — Scaling Managed Agents: Decoupling the brain from the hands
https://www.anthropic.com/engineering/managed-agentsOpenAI Agents SDK — Running agents
https://openai.github.io/openai-agents-python/running_agents/LangGraph — Persistence
https://docs.langchain.com/oss/python/langgraph/persistence
自测
- 一个两天的代码迁移任务在 Worker 重启、Context reset 后继续。哪些事实必须存在 Context 之外,才能判断“已经做过什么”?
- 为什么保存完整 transcript 仍不足以安全 Resume?举一个环境已经变化、旧对话却仍然成立不了的例子。
- Progress、Artifact、Checkpoint 各自回答什么问题?哪一种最接近“从哪里可以安全继续执行”的边界?