Skip to content

耐久化 Agent 工作流:LangGraph、Temporal 与长任务恢复

工作流一旦跨越分钟、小时甚至人工审批周期,问题就从“模型回答得怎样”变成“进程重启、网络超时、重复消息、审批延迟后还能不能正确继续”。这类系统的核心不是记忆,而是耐久执行(durable execution)。

一、什么叫耐久执行

耐久执行不是把完整对话存到数据库。它至少意味着:

  1. 每个运行有稳定身份,例如 thread_id / workflow_id / run_id
  2. 关键状态与执行进度在进程外持久化,服务重启后可定位继续点。
  3. 运行历史能够说明已完成哪些节点、用了什么输入、为什么路由。
  4. 重放时能识别已发生的副作用,避免重复执行。
  5. 等待人工、定时器、回调或长工具任务时,计算资源不必一直占着。
  6. 版本升级后旧运行仍能以兼容策略完成或被安全迁移。

耐久并不等于完全不重复。很多运行时会从某个节点边界重新执行;因此节点设计必须考虑可重入与幂等。最危险的误解是:“有 checkpoint,就不会重复发邮件。”Checkpoint 记录的是工作流世界的状态,外部邮件、扣款、写库属于业务世界,仍需由业务键和回执保护。

二、LangGraph:显式图状态与 Interrupt

LangGraph 将长运行 Agent 表达为 State、Node、Edge 与 checkpoint。官方文档强调 durable execution、流式输出、人机协作和持久化;interrupt() 可在节点中暂停,保存状态并等待用相同 thread_id 恢复。这很适合“模型参与决策、流程仍需工程约束”的场景。

State 应该存什么

ts
type InvestigationState = {
  runId: string
  actor: { id: string; tenant: string; roles: string[] }
  workflowVersion: string
  currentCase: { id: string; version: number }
  evidence: EvidenceRef[]
  toolCalls: ToolCallSummary[]
  proposal?: { action: string; params: unknown; idempotencyKey: string }
  approval?: { status: 'pending' | 'approved' | 'rejected'; decisionId?: string }
  budget: { stepsLeft: number; tokensLeft: number; deadlineAt: string }
  errors: NodeError[]
}

存“事实、引用和控制状态”,少存格式化后的长文本;存工具结果的引用或摘要,避免状态无限膨胀;同时记录模型、Prompt、工具 Schema 和策略版本,才能做回放与问题定位。

Node 的三条铁律

  1. 单一职责:一个节点要么检索,要么调用一个外部服务,要么生成提案,要么审批;不要把三种外部副作用塞进一个节点。
  2. 可重入:节点可能在恢复时从头运行。调用外部系统前使用幂等键,或先读取结果;写操作放在审批/确认之后。
  3. 结构化结果:节点返回状态增量和错误类型;不要以一段自然语言决定边。

Interrupt 的工程含义

LangGraph 的 interrupt 在暂停时保存状态,恢复后节点会从头开始执行。官方文档特别提醒:interrupt 之前的副作用应幂等,interrupt 不能被宽泛的 try/catch 吞掉,多个 interrupt 的顺序不能随条件随机改变。这些规则不是框架细节,而是分布式恢复的一般规律。

一个安全的审批节点结构是:先构造纯数据的提案 -> interrupt 等待批准 -> 恢复后校验批准令牌与对象版本 -> 在独立写节点执行幂等动作。不要在 interrupt 前创建正式订单,然后期待恢复时“不再创建”。

三、Temporal:耐久业务编排的另一类答案

Temporal 更像面向分布式业务流程的耐久工作流平台。它的核心价值是让 Workflow Execution 具备持久执行历史,使用 Worker 执行 Activity,并围绕重试、定时、信号、查询和版本演进组织长任务。它并不要求工作流中必须有 LLM,恰好适合承载“AI 之外的可靠协调”。

LangGraph 与 Temporal 不应简单互相替代

问题LangGraph 更自然Temporal 更自然
Agent 的状态、工具循环、子图、人工 steer需自行建模 Agent loop
LLM 节点的上下文、路由和流式体验通常由 Activity/服务实现
长事务、定时器、跨服务补偿、严格业务工作流可做但需要更多基础设施
业务 SLA、稳定 Worker、版本演进可结合周边系统实现
低代码业务自动化不以此为主不以此为主

常见组合:Temporal 管理订单/案件/审批等外层业务生命周期;某个 Activity 调用 LangGraph 运行受预算限制的 Agent;Agent 返回结构化提案而不是直接提交业务动作。也可以反过来让 LangGraph 主导交互图,把跨日等待、重试与补偿委托给外部耐久服务。关键是只定义一个最终业务动作的权威协调者,避免两个运行时各自重试。

四、恢复、重试、补偿的分层

1. 模型级恢复

结构化输出不合法、上下文过长、模型限流、内容审核拦截。策略可包括修复 Prompt、换更强/更小模型、缩短上下文、重试有限次数、输出部分答案或转人工。模型错误不应被包装成“业务失败”。

2. 工具级恢复

网络抖动、429、5xx 可以指数退避并加抖动;4xx 参数/权限错误通常不重试;超时后必须查询下游是否已处理。工具结果应带 correlation_id 或业务 id,以便对账。

3. 工作流级恢复

节点失败后按错误类型路由:回到上一步修复、切换替代工具、暂停人工、跳过非关键分支、进入补偿或终止。要记录这次选择的原因,防止自动重试造成难以解释的循环。

4. 业务级补偿

跨系统操作没有天然分布式事务时用 Saga。例:先冻结额度 -> 创建订单草稿 -> 风控审批 -> 扣减额度 -> 通知。中间失败时释放额度、取消草稿或标记人工处理。补偿动作本身也需要幂等,并要考虑“补偿失败怎么办”的人工处置路径。

五、长任务的状态设计与数据生命周期

状态分层

内容存储原则
控制状态节点、边、重试、预算、审批强一致、可恢复
业务事实订单/案件版本、外部回执领域系统是权威源
运行证据Prompt、模型、工具摘要、引用可检索、脱敏、可审计
大对象文档、图片、完整 trace对象存储/日志平台,状态只存引用
会话记忆偏好、上下文摘要受权限/保留期约束,不能替代事实

这样做能避免把几十 MB 文档或无限聊天历史塞进 checkpoint,也能让删除请求、数据保留和审计导出有清晰边界。

超时与取消

长任务必须有截止时间、心跳、取消令牌和生命周期状态:queued -> running -> waiting_human -> retrying -> succeeded | failed | cancelled | expired。取消不是简单杀进程:若外部动作正在进行,要查询回执、标记不再发起新动作、必要时进入补偿或人工确认。前端显示“已取消”之前要区分“停止计划”与“外部副作用已撤销”。

六、版本演进:最容易被忽略的生产题

假设一个审批任务已等待三天,此时团队发布了新图、新 Prompt 和新 Tool Schema。如何保证它恢复后不会走到错误分支?

  1. 每个 run 固定 workflow_versionprompt_versiontool_schema_version 和策略版本。
  2. 新版本默认只接新 run;旧 run 仍由旧兼容处理器完成,或显式迁移状态。
  3. 迁移是带审计的函数:输入旧状态、输出新状态、校验不变量、支持回滚。
  4. Tool API 应做向后兼容:新增字段有默认值,删除字段走双读/双写或适配层。
  5. 人工审批页面展示“批准的是哪个版本的提案”;若对象/政策变了,旧批准失效并要求重审。

面试里若能主动讲到“等待中的旧 run”,通常说明你理解的不是 demo,而是长期运行系统。

七、系统设计题:跨日投研/尽调 Agent

需求

用户发起一项企业尽调,系统需要检索公开资料、读取内部已授权文档、调用结构化数据、生成风险报告;任务可能持续数小时,部分来源失败需要重试,报告发布前由分析师审核,不能把未授权内部资料带到外部模型。

推荐编排

text
Case Workflow (Temporal/耐久协调)
  -> 创建案件与授权快照
  -> LangGraph Research 子图
       -> 规划 -> 受控检索 -> 证据归档 -> 事实冲突检测 -> 草稿
  -> 规则与数据出境 Gate
  -> 人工审核 Interrupt/Signal
  -> 发布报告 / 归档 / 反馈入评测

必答点

  • 外层案件工作流管理超时、重试、审批、通知、版本与补偿;研究子图管理开放式工具循环。
  • 每份证据带来源、抓取时间、授权域、哈希与引用位置;模型只能访问经过数据出境策略批准的内容。
  • 每个外部抓取、数据库查询和报告发布都用可追踪的 request ID;发布动作依赖审批令牌和不可变报告版本。
  • 任务恢复先读取案件/报告外部状态,若已发布就停止,不重复发送。
  • 指标不仅是报告“好不好”,还包括证据覆盖、引用准确率、无授权数据外发、恢复成功率、人工修改率和单案件预算。

八、项目讲法模板

我们把长任务拆成两个运行层:外层耐久工作流负责案件生命周期、超时、通知、审批和版本;内层 LangGraph 负责研究任务的动态检索与工具选择。State 只保存案件事实、证据引用、预算和审批状态,完整文档放对象存储。每个写操作带幂等键并以外部回执为准;人工审批通过 interrupt/signal 恢复时会重新校验案件版本和授权。这样服务重启、模型限流或审批跨天都不会让任务丢失,也不会因为重放而重复发布报告。

九、面试高频问答

Q:Checkpoint 能解决哪些问题,不能解决哪些问题? 它能保存图状态、定位恢复点、支持人工暂停和回放;不能自动保证外部写操作恰好一次,也不能自动完成分布式事务。外部动作仍需要幂等、回执、对账和补偿。

Q:为什么要把副作用拆成独立节点? 恢复可能重新执行一个节点。把纯计算、审批、写入拆开,能让副作用拥有单独的幂等键、授权和监控,也能减少重复执行范围。

Q:LangGraph 的 interrupt 前能否做写操作? 最好不要;如果必须做,写操作必须幂等且恢复时能识别已完成。因为恢复会从节点开头重新运行。更安全的设计是把写入放在审批恢复后的独立节点。

Q:什么时候选 Temporal? 当主要困难是跨服务长事务、可靠定时/重试、审批等待、版本演进和业务 SLA 时。若主要困难是 Agent 的上下文、工具循环与状态图,LangGraph 更自然;两者可以组合但要避免双重重试和双重权威状态。

十、官方资料与延伸阅读

基于 MIT 许可发布