长任务要有交接物和终态
Goal 是可执行契约,必须写明 outcome、constraints、verification 并定义明确终态,否则本质上是无限循环。
长任务最常见的失败方式不是做错,是跑不完。上下文窗口迟早不够用,Agent 会忘记早期决策、偏离计划、重复已经被否定的路线。撑住它的不是更大的窗口,而是两样东西:写在文件里的交接物,和事先定义好的终态。缺少停止证明和资源上限的 Goal,本质上仍然是一个无限循环。
Goal 是一份可执行契约
合格的 Goal 写三件事:outcome(最终交付什么)、constraints(不能碰什么、预算多少)、verification(什么算完成)。写成"一直做下去直到完成"不是目标,是许愿。
三要素里最常被省略的是 constraints 和 verification。没有约束,Agent 会用一个你不会接受的手段达成目标——删掉测试让套件变绿,也是一种"完成"。没有验证方法,你和 Agent 对"完成"的理解会在中途分叉,等到交付时才发现。
完成只能由外部证据确认
完成判定要落在测试、可测指标、Diff、截图、外部状态或人工验收上,不能以模型自述"已完成"为依据。理由是结构性的:LLM 对 LLM 的生成物天然宽容。Anthropic 让 agent 自评工作质量时,它会"自信地夸自己",即使人看来明显平庸;主观任务(设计)尤甚,连有可验证结果的任务,agent 的判断力也常常碍事 anthropic-long-running。
把做事的 agent 和评判的 agent 分开。分离本身不消除宽容,但调教一个独立的 evaluator 让它保持怀疑,比让 generator 批评自己可控得多。
四类终态,别让循环自己停
持续运行的系统必须有明确终态:blocked、needs-input、cancelled、budget-exhausted。触发它们的是 verifier 不可达、预算耗尽、权限不足、外部依赖永久失败。
区分 blocked 和失败重试很关键:blocked 意味着需要外部变化才能解除,所以它必须带着证据停下来,而不是在循环里空转烧预算。一个跑了两小时才发现缺少权限的任务,和一个跑了两分钟就报 blocked 的任务,差别只在有没有定义终态。
三件套撑住 25 小时
OpenAI 用 GPT-5.3-Codex 不间断跑了 25 小时、13M token、产出 3 万行代码,靠的是文件系统上的 durable project memory 三件套:plan.md 写里程碑、每个里程碑的验收命令和 stop-and-fix 规则(验证失败先修复再前进,禁止带病推进);implement.md 是执行 runbook,约束 diff 规模并持续更新文档;documentation.md 是状态与决策的实时审计日志,让人离开几小时回来还能看懂发生了什么 openai-long-horizon。
Anthropic 用 16 个 agent 写 C 编译器时是同一个道理:靠 README 和 progress 文件帮 agent 定位自己在哪里。他们还发现 agent 有 time blindness——不知道一件事该花多久,会傻跑几个小时的测试。于是 harness 少打印进度(进度输出会诱导 agent 继续等),并提供 --fast 采样让它用小代价先探 anthropic-c-compiler。长任务的敌人不只是遗忘,还有不知道什么时候该停。
用一次空会话验交接物
交接物写得对不对,写它的人和帮它写的人都判断不了——两边都记得那几天的争论,每句话背后的来龙去脉在记忆里,不在文件里。判断方法反过来:开一个全新的会话,只给它那份文件,看它能不能接住。
做法是派三个互不相见的审阅者并行跑。一个复述这份设计想做什么、现在系统怎么运转;一个专挑漏洞,找漏掉的前提、没处理的边界;一个以「明天就要动手」的身份读,把它会问的问题全部列出来。第三个最有用——它问得出的每个问题,都是文档里的一个洞。
三个必须并行生成。串行编排会忍不住把第一个的发现粘进第二个的提示词里「给点上下文」,三个独立读者就变成一个读者加两个回声,而独立上下文是这个测试唯一的机制,污染了它就什么也测不出来。审阅者也不必客气:按方法提出者的经验,约三成的建议真正有价值(个案经验值,不是普适比例),这个比例已经够值回成本 aase-egm。
跑完改文档,再跑一轮,直到只剩措辞级意见,然后才轮到真人审。这个测试真正跑起来是有代价的——密歇根大学那门课自己的课程设计过了六轮、十八份报告,其中一轮抓出的矛盾是:设计里打算扣下两节课作为门禁测试,但十六节课全是必修且后课依赖前课,扣一节就连环断裂。坐在设计会话里的人没看出来,因为在场的人都知道原话是什么意思 aase-egm。
让 Agent 每轮都看见自己在哪
三件套解决跨会话恢复,单轮之内还得有一条状态栏。书里把五种做法做成可独立开关的实验项,其中两项带实测数字 ai-agent-book:
- 详细错误:四层内容——错误类型与描述、完整参数的 JSON、调用栈、针对性的修复建议(FileNotFoundError 就提示验证路径、检查工作目录、改用绝对路径)。错误场景下"找到替代方案"的成功率从 60% 升到 95%,行为从盲目重试变成先分析再动手。
- TODO 列表:每项带唯一 ID、状态和时间戳,充当外部记忆。启用后平均 15 次迭代完成任务,禁用要 21 次,且经常漏掉子任务。
另外三项各管一处。工具调用计数器把 "Tool call #3 for read_file" 写进上下文,让模型自己走完"第一次查路径、第二次列目录、第三次换方案"的节奏,顺带提供了隐式的成本感知。时间戳前缀加在用户消息和工具响应上,而不是系统提示词里——放错位置就把缓存砸了,分层加载与交接包 里有这笔账。系统状态感知里最要紧的是工作目录:Agent 执行 cd 之后必须自动更新,否则下一条命令落在错误的地方,而它完全不知道。
五条的共同点只有一个:把"我试了几次、现在在哪、还差什么"从模型的记忆换成上下文里看得见的事实。模型不擅长数自己的步数,很擅长读一个计数器。
分步推进与结构化交接
每次只推进一个或少量明确任务;会话结束前留下结构化交接,而不是一句"我做到哪了";保持工作区可构建、可理解、可继续。
复杂变更走 proposal 到 design 到 tasks 三层渐进细化,每层独立审查并版本化:proposal 回答为什么做、做什么、验收标准,由产品或业务方审;design 回答怎么做、架构与接口设计,由开发负责人审;tasks 回答拆解、优先级和依赖,交给执行 Agent。上游变更时同步更新下游并记录影响范围,而不是直接改代码。Anthropic 的 AI-Native SDLC 把这条链叫 intent 到 spec 到 plan:每层产物版本化、人机均可读,作为阶段交接契约而不是一次性 prompt,全部进版本控制形成审计 trail。当瓶颈在人速步骤(计划、评审、交接)而不是代码生成时,工件链是压缩交接等待的主要手段 anthropic-sdlc。
逐轮摘要要像 git log,不像 git squash:每轮留一条独立的结构化记录,而不是把整段历史合并成一句。合并会丢掉"哪一轮放弃了什么",而那恰好是下一步唯一用得上的信息。生产系统还会给全量压缩配一个连续失败熔断器——数据表明大量会话卡在反复压缩失败的循环里,熔断器拦住的不是错误,是在这些会话上持续烧钱 ai-agent-book。
漂移要浮出来,不能被吸收
实现到一半,发现要改一个文档没列出的文件。自然的、帮忙的做法是直接改。改十次之后,文档就不再描述这个系统,"唯一真相"变成虚构——而下一个接手的人读的正是那份虚构。
规则是停下来、记一条、给建议、等人决定,然后再动。触发条件有四个:要动文档没列出的文件、按文档写的方式做不成、需要新增文件、文档里有个假设是错的。不管意外大小,响应都是同一个。
处置的力度跟着意外的大小走:一句澄清就够的,就地改文档;动到一节的,按设计流程重写那一节;动到整体的,回去重新走一遍评审。验证失败也算漂移——脚本没过,循环从头开始。
这条和假修复是同一族失败,只是一个糊弄测试,一个糊弄文档。判据也一样:说不清"这次改动对应文档里哪一条",就不该落笔。
耐久执行的四条硬规则
恢复靠重放,所以规则是围着"可重放"定的:
- 非确定性操作和副作用封装成独立 Task。
- Task 的输入输出必须可序列化。
- 可重试的 Task 必须幂等。
- 假设中断前的代码可能再次执行。
最后一条最反直觉。你不是在保存一个断点,而是在重放一段历史,所以必须假设中断点之前的代码还会跑一遍——中断点之前不能执行无保护的外部副作用。
取舍与检查:三件套和工件链都是有成本的,一次会话内能验证完的任务不该付。判据是跨不跨会话。三个问题自检:你的 Goal 写了 verification 吗?有没有明确的终态和预算上限?把进程 kill 掉再拉起来,它知道自己在哪吗?
