长程后台 Agent:持久执行


文档摘要

长程后台 Agent:持久执行 本节摘要:生产级长程 Agent 不在 里跑。每一次 LLM 调用都变成一个带检查点、重试、重放(replay)的活动(activity)。Temporal 与 OpenAI Agents SDK 的集成在 2026 年 3 月 GA;Claude Code Routines(Anthropic)无需持久本地进程就能跑定时 Claude Code 调用。会话可在人工输入上暂停、跨部署存活、按 从最近检查点恢复。新工效背后是一个老模式——工作流编排——只有一个新输入:LLM 调用作为非确定性活动,恢复时必须确定性重放。

长程后台 Agent:持久执行

本节摘要:生产级长程 Agent 不在 while True 里跑。每一次 LLM 调用都变成一个带检查点、重试、重放(replay)的活动(activity)。Temporal 与 OpenAI Agents SDK 的集成在 2026 年 3 月 GA;Claude Code Routines(Anthropic)无需持久本地进程就能跑定时 Claude Code 调用。会话可在人工输入上暂停、跨部署存活、按 thread_id 从最近检查点恢复。新工效背后是一个老模式——工作流编排——只有一个新输入:LLM 调用作为非确定性活动,恢复时必须确定性重放。本节讲透活动/工作流/事件日志/重放四件套、LLM 调用为何天然契合活动画像、按 thread_id 键的检查点与后端选型(PostgreSQL/SQLite/Redis/Durable Objects)、把人工输入当作一等状态,以及一个反复出现的主题:METR 观察到的「35 分钟退化」——持久执行不修复可靠性衰减,而是让你跑得比可靠性画像支持的更久,设计对了是新式的安全失败,设计错了是不安全失败。

对应原课程:Phase 15 · Lesson 12 · durable-execution(原英文 phases/15-autonomous-systems/12-durable-execution/docs/en.md)。

学习目标

阅读完本节,你应当能够:

  1. 区分工作流(确定性编排代码)、活动(非确定性工作单元)、事件日志(持久后端)、重放(恢复时只跑未完成活动) 四者,并说出为什么编排器必须确定性。
  2. 解释为什么 LLM 调用天然契合活动画像(非确定、贵、可能失败、带副作用),以及把它包成活动带来什么(指数退避重试、跨重启检查点、可重放调试轨迹)。
  3. 描述按 thread_id 键的检查点 API 形态,以及 PostgreSQL / SQLite / Redis / Cloudflare Durable Objects 各自的取舍。
  4. 人工输入当作一等状态:Propose-Then-Commit 需要持久的「等人」状态,隔夜批准早上自动续跑。
  5. 说明 35 分钟退化 的含义,以及为什么持久执行必须配「重进入需新 HITL」与「预算 kill switch」才安全。

一、问题与直觉

设想一个跑四小时的 Agent:调三个工具、提示用户两次、做四十次 LLM 调用。跑到一半,宿主机重启。会发生什么?

  • 朴素 while True 循环:一切丢失。运行从头开始。三个工具调用(带真实副作用)再执行一次。用户被再问一遍已经批准的事。四十次 LLM 调用重新计费。
  • 持久执行:运行从最近检查点恢复。已完成的活动重新执行,结果从持久日志重放。用户不再批准已批准的事。已做的 LLM 调用不再计费。

这是工作流引擎(Temporal、Cadence、Uber Cherami)出货十年的同一模式。新东西是:LLM 调用现在是一类活动——非确定、昂贵、带副作用——它干净地套进这个模式。

💡 本节反复主题:METR 观察到「35 分钟退化」——成功率随跨度大致平方下降。持久执行让你跑得比可靠性画像支持的更长;设计对了是新的安全失败方式,设计错了是不安全失败方式。

二、从零实现:最小持久执行引擎

原课程 code/main.py 用标准库实现一个最小持久执行引擎:@activity 装饰器把输入输出记到 JSON 事件日志;一个工作流函数串起活动;run_or_replay(workflow, event_log) 重放已完成活动而不重新执行。驱动器模拟一个三活动工作流,在中途崩溃,展示 (a) 朴素重试重新执行一切 vs (b) 重放只跑缺失活动。

活动与重放骨架

def activity(fn): def wrapped(ctx, *args): key = (fn.__name__, serialize(args)) if key in ctx.log: # 已完成 -> 重放,不重执行 return ctx.log[key].output ctx.log.record_start(key, args) out = fn(*args) # 真执行(非确定: LLM/工具/网络) ctx.log.record_complete(key, out) return out return wrapped def run_or_replay(workflow, event_log, thread_id): ctx = Context(log=event_log, thread_id=thread_id) return workflow(ctx) # 工作流代码必须确定性

活动、工作流、重放

  • 工作流:确定性编排代码。定义活动序列、分支、等待。必须确定性,以便从事件日志重放时不出意外分歧。
  • 活动:非确定性、可能失败的工作单元。LLM 调用、工具调用、文件写、HTTP 请求。每个活动记其输入与(完成后)输出。
  • 事件日志:持久后端。每个活动开始/完成/失败/重试、每个工作流决策都被记录。
  • 重放:恢复时工作流代码从头重跑;每个已完成活动返回日志结果而不重执行;只有未完成的活动才真跑。

这与 React 对虚拟 DOM 重渲染、或 Git 从 commit 重建工作树同形。编排器的确定性是让持久性便宜的关键。

LLM 调用为何契合

LLM 调用是:非确定(temperature>0;即便 temperature=0 也跨模型版本漂移)、昂贵(钱与延迟)、可能失败(限速、超时)、带副作用(若调工具)。这正是活动画像。把每个 LLM 调用包成活动,你就得到了指数退避重试、跨重启检查点、可重放调试轨迹。

thread_id 键的检查点

LangGraph、Microsoft Agent Framework、Cloudflare Durable Objects、Claude Code Routines 都收敛到同一 API 形态:一个 thread_id(或等价物)标识会话;每个状态转换持久到后端(PostgreSQL 默认,SQLite 用于开发,Redis 用于缓存);恢复读最新检查点。

后端选择要紧:

  • PostgreSQL:持久、可查询、跨部署存活。LangGraph 默认。
  • SQLite:仅本地开发;跨主机丢数据。
  • Redis:快但瞬态,除非配 AOF/快照。
  • Cloudflare Durable Objects:透明分布式;按唯一键划定范围;存活数小时到数周。

人工输入作为一等状态

Propose-Then-Commit(第 15 节)要求一个持久的「等人」状态。工作流暂停,外部队列持挂起请求,批准从恰好那一点恢复。没有持久性这是 best-effort;有了它,隔夜批准到达,工作流早上续上。

35 分钟退化

METR 观察到,测过的每个 Agent 类别在连续运行超过约 35 分钟后都出现可靠性衰减:任务时长翻倍,失败率大致翻四倍。持久执行不修复这个;它让你跑得比可靠性画像支持的更长。安全模式是把持久性与「重进入需新 HITL」的检查点、以及「不论墙钟时间封顶总算力」的预算 kill switch(第 13 节)结合。

持久执行是错答案的情况

  • 短于几分钟、无人工输入的运行:开销 > 收益。
  • 严格只读的信息检索。
  • 正确性要求在一个上下文窗口内端到端完成的任务(某些推理任务、某些一次性生成)。

三、框架对比:后端选型与「跑得更长 vs 更可靠」

后端 持久 适合 风险
PostgreSQL 生产默认(LangGraph) 需运维
SQLite 本地开发 跨主机丢数据
Redis(无 AOF) 缓存 瞬态
Cloudflare Durable Objects 分布式长程 供应商锁定
Temporal 企业工作流 重基础设施

⚠️ 关键认知:持久执行让运行更长,但不让运行更可靠。它必须配「重进入需新 HITL」与「预算 kill switch」,否则它放大的是失败窗口,不是成功概率。

四、可复用产物

outputs/skill-durable-execution-review.md 审查一个拟部署的长程 Agent 是否有正确的持久执行形态:活动是否包住所有非确定步骤、编排器是否确定性、检查点后端是否合适、有没有持久的「等人」状态、恢复时的 HITL 策略。

code/main.py 把崩溃点做成可调,适合演示「崩溃点变,重放的活动数跟着变」——给团队建立「重放只跑未完成」的直觉。

五、练习

  1. code/main.py:观察朴素重试与重放的活动执行数差异。改崩溃点,展示重放数相应变化。

  2. 把玩具引擎显式用 thread_id:模拟两个并发会话共享引擎,确认它们的事件日志不冲突。

  3. 在玩具引擎的一个活动里引入非确定性(工作流决策里的墙钟时间戳),演示重放时的分歧。解释真实引擎如何处理(副作用注册、Workflow.now() API)。

  4. 读 LangChain「Runtime behind production deep agents」,列出运行时持久化的每个状态,说出每个覆盖哪种失败模式。

  5. 为一个 6 小时自主编码任务设计检查点策略:在哪检查?崩溃恢复长什么样?什么需要新 HITL?

本节要点回顾

  1. 生产长程 Agent 不在 while True 里跑:每次 LLM 调用都是带检查点/重试/重放的活动。
  2. 四件套:工作流(确定性编排)、活动(非确定单元)、事件日志(持久后端)、重放(只跑未完成)。
  3. 编排器必须确定性:才能从事件日志重放而不出意外分歧(同形于 React 虚拟 DOM、Git commit)。
  4. LLM 调用天然是活动:非确定、贵、可能失败、带副作用——包成活动即得退避重试、跨重启检查点、可重放轨迹。
  5. thread_id 键检查点是行业收敛形态:后端 PostgreSQL(默认)/SQLite(开发)/Redis(缓存)/Durable Objects(分布式)。
  6. 人工输入是一等状态:Propose-Then-Commit 需要持久「等人」状态,隔夜批准早上续跑。
  7. 35 分钟退化:任务时长翻倍、失败率大致翻四倍;持久执行让运行更长但不让它更可靠。
  8. 必须配 HITL-on-resume 与预算 kill switch:否则放大的是失败窗口。

下一节,我们处理长程运行的成本维度——动作预算、迭代上限与成本治理,看如何在墙钟时间之外封顶总算力开销。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U