让 Agent 下班后继续跑:有界自主运行怎么设计?

无人值守 Agent 如何避免卡住、反复重试和消耗 Token?从任务边界、进展证据、预算、停止原因与恢复检查,设计可接手的有界自主运行。

发布 ·更新 ·约 9 分钟 ·hugfeature

Agent Harness 系列 #16
上一篇:Agent Harness 的终点:从执行引擎走向 Quality Gate

让 Agent 在人离开电脑后继续工作,一个很实际的目标是:在已经确定的范围内,能完成多少就完成多少,回来后能接着处理。

最担心的通常有两件事:它停在某个地方,再也没有进展;或者一直运行,反复尝试同一个问题,持续消耗 Token。

这两个风险指向同一个要求:系统需要分清“正在运行”和“正在推进”,并且能在推进不了时留下可接手的状态。

本文把这种方式称为“有界自主运行”。这是一个工程设计用语,不是某个框架的标准名称。下面给出的是设计建议和验证方法,不代表这些控制点已经在某个项目中全部实现,也没有据此宣称效率提升。

一、先把“能跑多少是多少”变成有限任务

“持续优化这个项目”很难收敛。每改一处,Agent 都可能发现新的可优化项。

适合无人值守的任务需要四个要素:允许处理的工作清单、每项的完成条件、允许使用的工具和资源,以及本轮预算。发现新问题时,可以记录到候选清单,但不能自动把它变成本轮必须完成的目标。

例如,给一个文档处理 Agent 的任务可以是:解析已确认范围内的需求,为规则明确的部分生成用例草稿,记录无法确定的预期结果。文档里的业务空白需要留待确认,不能靠补写规则来完成任务。

还要确认依赖关系。一个子任务被阻塞后,只有不依赖它、且仍在授权范围内的其他任务可以继续;不能跳过必要前置条件,假装整条链路仍然有效。

二、活动、进展、完成是三个信号

持续输出文字能证明模型还在响应,但不足以证明任务正在接近完成。

信号 可以说明什么 不能说明什么
心跳、日志、工具调用 进程或调用仍有活动 工作取得了有效进展
新产物及其检查结果 某个任务状态发生了可解释的变化 整个任务已完成
验收证据 指定范围满足了完成条件 范围之外也已验证

“进展”也不能简单等于文件变了:反复改措辞、刷新时间戳、生成相同报告,都能制造变化。

更合适的进展事件是:一个需求条目获得了来源定位;一个失败被缩小到可复现输入;一个原本失败的检查在相同条件下通过。诊断阶段也可能产生进展,例如排除了一条假设,但需要相应证据,不能只写“分析更深入了”。

因此,监督逻辑可以分别记录 last_activity_at 和 last_progress_at。前者用于判断是否失去响应,后者用于识别长时间没有有效推进。长耗时工具还需要自己的截止时间,避免被简单的“无新文件”规则误判。

三、预算必须由能停止执行的一层控制

在 Prompt 里写“最多尝试两次”有帮助,但它依赖 Agent 自己遵守。真正的运行上限,需要放在宿主、执行器或能够终止任务的监督进程中。

下面只是一个配置示意,数值用于解释结构,不能直接视为适合所有任务的默认值:

run_budget:
  wall_time_seconds: 3600
  max_tool_calls: 120
  max_retries_per_failure: 2
  max_no_progress_cycles: 3
  max_resume_attempts: 1

这里的 retry 指首次失败后的额外尝试。达到任何硬上限后,都不再启动新工作;已有调用要按工具能力取消或等待到它自己的截止时间,并记录是否仍有操作在进行。

如果宿主可以获取可靠的 Token 或费用统计,可以再加入对应上限;获取不到时,就明确记录未知,并用时间和调用次数限制运行。不能把无法读取的费用写成零。

恢复任务也不能刷新预算。监督进程需要持久化累计消耗,并让新会话读取剩余额度。否则“到上限就重启”只是另一种无限循环。

四、重试要看失败有没有变化

单纯比较工具名称,会把正常的批量读取误判为重复;只比较错误文字,又可能漏掉换一种说法的同一问题。

可以结合任务项、输入版本、工具、失败类型和相关状态,生成一个失败标识。这个标识用于聚合类似失败,并不是证明根因相同的绝对依据。

现场情况 建议处理
只读查询遇到短暂网络错误 在截止时间和重试预算内退避重试
输入或依赖已经改变 记录变化依据,再决定是否尝试
同样输入、同样失败、没有新证据 结束本项,保留阻塞原因
写入超时,结果未知 先查询目标状态,不能直接重复写入
权限或审批阻塞 等待或停止,不通过换工具绕过

“换个模型再试”仍然消耗同一任务的预算。它可以是预先允许的策略,但不能让失败计数归零,也不能改变授权边界。

五、停止时留下的状态,比结束语更有用

无人值守任务的合理结果,不只有成功和失败。

可以将执行状态和结果验收分开:执行可能因完成、预算耗尽、无进展、等待输入、运行错误而结束;验收则说明产物已验证、未验证或未通过。这样,部分产物存在就不会被误写成整项成功。

例如,下列记录表示任务受阻,但已经留下两项可检查的工作:

{
  "run_id": "example-run-017",
  "stop_reason": "blocked_on_input",
  "verification": "unverified",
  "completed_items": ["REQ-01", "REQ-02"],
  "blocked_items": ["REQ-03"],
  "artifacts": ["cases-draft.json", "validation.json"],
  "next_action": "确认 REQ-03 的重复提交规则后再生成预期结果"
}

这是合成示例,不是真实运行数据。实际保存时,还应关联输入版本、产物版本、累计预算和外部操作状态。

写检查点时,应先保存产物和验证结果,再更新引用它们的状态;不能先宣布检查点完成,随后才写文件。停止时如果无法落盘,监督层要报告保存失败,并指出最后一个可用检查点。

六、恢复前重新确认事实

检查点只是上次运行留下的记录。它不能证明当前环境、权限和输入仍然相同。

恢复时至少重新检查这些条件:

  • 输入一致性: 文档、任务范围或代码版本是否变化?变化影响哪些产物?
  • 产物完整性: 引用的文件是否存在,版本或哈希是否匹配,验证是否针对该版本?
  • 执行现场: 上次工具是否还在运行,有没有结果未知的写入?
  • 权限与预算: 原授权是否仍适用,累计消耗后还剩多少额度?

如果旧进程状态不明,不能立即启动另一个进程做同一项写入。需要先确定旧任务已停止或被有效隔离,避免两个执行者同时修改目标。

Anthropic 在长任务 Harness 的工程文章中讨论了增量推进、进展记录与跨会话接续。这支持“把状态留在会话之外”的方向;本文进一步列出的预算、停止分类和恢复检查,是围绕无人值守风险提出的设计建议,并非该文定义的统一协议。

七、先验证三个故障,再延长运行时间

运行时长本身不能证明自主运行可靠。可以先做三个小规模故障演练:

注入的故障 需要观察到的结果
工具持续返回同一可重试错误 重试和累计预算生效,停止后留下错误证据
进程有心跳,但检查结果没有有效变化 无进展策略触发,且没有被空文件或重复报告重置
保存检查点后中断,再修改输入 恢复识别输入变化,对受影响产物重新验证

这些验证不是为了追求“永远不停”。它们要确认:能推进时继续,无法推进时有明确结论,中断后有事实可查。

如果宿主只支持聊天和工具调用,还没有外部监督能力,应如实把当前方案称为“带运行约束的任务指令”。等到停止、预算和恢复确实由执行层落实,再把它作为有界自主运行能力交付。

参考与继续阅读

继续阅读这个系列

Agent Harness:从入门到 Reliability →
  1. Agent Harness 是什么?为什么它正在成为 Agent 工程的关键一层
  2. Agent、Framework、Workflow、Runtime、Harness 到底有什么区别?
  3. 2026 Agent Harness / Runtime 工具盘点:主流方案怎么选
  4. OpenAI Agents SDK 教程:安装、Tool、Session 与 Trace
  5. smolagents 教程:用 CodeAgent 跑通一个 Agent Loop
  6. 手写 Agent Harness:用 Python 实现 Loop、Tool、Policy 与 Trace
  7. 一个 Agent Loop 到底是怎么跑起来的?
  8. Agent 为什么需要 Harness:模型负责决策,系统负责约束
  9. Harness 到底应该管什么?从 Agent Loop 到 Reliability Boundary
  10. Trace 不是日志:Harness 应该记录哪些执行证据?
  11. Agent 调错工具怎么办:Timeout、Retry、Budget 与 Side Effect
  12. 结果正确就够了吗?给 Agent 设计 Evidence Contract
  13. 一次 Agent 失败,怎么变成一条 Regression Case?
  14. Harness 和 Eval 平台到底是什么关系?
  15. Agent Harness 的终点:从执行引擎走向 Quality Gate
  16. 让 Agent 下班后继续跑:有界自主运行怎么设计? · 当前篇

沿着问题继续探索

从执行机制走向验证与交付。

相关文章

在 GitHub 反馈 ↗