Durable Session中断恢复

Durable Session实战:Agent中断后如何继续执行

用会话历史、环境检查和业务对账构建可靠恢复流程,避免重复创建、重复上传和越权继续执行。

Agent运行了半小时,浏览器断开了;程序正在创建WordPress草稿,服务重启了;Coding任务写出了文件,但执行机器已经被替换。遇到这些情况,输入一句“继续”可能有用,也可能制造第二篇文章、第二笔订单,或者让Agent从已经丢失的文件继续推理。

Durable Session的价值,是把会话从某一次HTTP连接和某一个调用进程中分离出来。但可靠恢复不仅需要会话历史,还需要执行环境、业务检查点、外部操作回执和权限状态。

本文延续OpenAI Agents API教程,以官方Session、Events和Items接口为基础,设计一套“先核对状态,再决定继续”的控制面。适合研究、Coding、文档生成、n8n自动化和WordPress草稿任务,不把无人值守运行等同于无人审批。

一句话结论:持久会话解决“记住做过什么”,检查点解决“从哪里继续”,幂等与对账解决“不要重复做”;三者必须组合。

本文将官方接口事实、作者参考架构和概念代码分别标注。代码未连接读者的账户,也没有执行真实发布或生产恢复;部署前需要在测试项目进行故障注入和集成验收。

一、Durable究竟持久化了哪一层?

在本教程中,Durable Session指持久会话设计,不是承诺某个叫resume()的万能接口。OpenAI官方Session保存Agent配置、会话和已保存工作,可以复用同一Session发送后续输入;正在工作的Session接收消息会转向当前Turn,空闲Session则开始新Turn。OpenAI Run and continue sessions

必须区分三个状态域:

状态域保存内容恢复所需证据
会话消息、工具调用、已保存结果session_id、Items、Turn结果
环境文件、依赖、进程、工作目录文件摘要、快照、环境健康
业务草稿、订单、上传、审批、进度外部ID、操作账本、检查点

保存聊天历史不等于保存进程内存;保存文件不等于恢复正在运行的Shell命令;保存模型回答也不等于外部写入成功。

以WordPress为例,Agent可能已经成功创建草稿,但回执还没传回调用程序。程序看到“超时”后重试创建,就会产生重复文章。问题不是Agent失忆,而是外部副作用没有被可靠核对。

二、六类中断,要用六种判断

中断类型不应立即做什么先检查什么
浏览器或SSE连接断开再次提交原任务Session、当前Turn、保存结果
调用应用重启重新创建所有Session持久任务映射与待处理动作
等待工具结果发消息催Agent继续required_actions和操作回执
执行器离线假设命令自动重启连接动作、机器健康、文件
外部写入后响应丢失立即重试写入外部资源是否已创建
用户取消或权限撤销自动恢复执行当前授权和取消原因

恢复策略应由程序分类,不让模型决定是否绕过权限。401/403、人工拒绝、访问策略改变属于停止条件;临时网络错误可以有限退避,但“外部写入结果未知”必须进入对账。

最危险的状态不是失败,而是UNKNOWN:你不能证明没做,也不能证明做完。在这种状态下,暂停副作用操作比盲目重跑更重要。

会话环境和业务状态
三个玻璃状态舱分别象征会话文档、文件环境和业务回执;文字会话·环境·业务状态

三、参考架构:Session外面还需要任务控制面

flowchart TD
  U[用户任务] --> C[任务控制面]
  C --> S[Agents API Session]
  C --> D[任务数据库]
  S --> E[执行环境与工具]
  E --> B[外部业务系统]
  B --> L[副作用账本]
  L --> C
  S --> R[状态重建与对账]
  D --> R
  R --> C

控制面承担任务身份、租约、审批、预算和恢复决策。上述数据库、账本和状态重建模块是实施建议,不是宣称Agents API默认提供完整的业务事务引擎。

推荐一个任务对应一个稳定job_id;session_id对应会话容器;turn_id对应工作轮次;call_id对应某次函数调用;operation_key对应业务副作用。不要把这些ID当成同一个东西。

任务有多次Turn时仍可使用相同job_id,但修改需求或目标资源后应升级任务版本。不能因为还在同一聊天会话,就沿用上一版审批。

最小任务表

以下SQL是自建应用数据模型,可在SQLite中演示,不是OpenAI响应Schema:

CREATE TABLE agent_jobs (
  job_id TEXT PRIMARY KEY,
  session_id TEXT UNIQUE,
  task_version INTEGER NOT NULL,
  phase TEXT NOT NULL,
  resume_attempts INTEGER NOT NULL DEFAULT 0,
  checkpoint_json TEXT NOT NULL DEFAULT '{}',
  policy_hash TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE side_effects (
  operation_key TEXT PRIMARY KEY,
  job_id TEXT NOT NULL,
  request_hash TEXT NOT NULL,
  state TEXT NOT NULL,
  remote_id TEXT,
  receipt_json TEXT,
  updated_at TEXT NOT NULL
);

生产系统另需租户范围、外键、索引、事件去重表、权限记录和保留策略。不可把秘密放在checkpoint_json中;保存凭据引用和作用域即可。

四、从官方接口开始:读取状态,再发送后续输入

OpenAI官方管理指南要求在应用数据库保存Session ID。通过retrieve可以查看Session、环境和required_actions;重启或断流后应重新读取待处理动作。OpenAI Manage sessions

准备更新后的OpenAI SDK,并把API Key配置在调用应用的安全环境中。不要把它放进Agent可读的文件或Shell环境。

python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade openai

下面示例只读取当前状态和保存历史,不恢复执行,也不展示完整敏感数据:

from openai import OpenAI

def inspect_saved_work(client: OpenAI, session_id: str):
    session = client.beta.agents.sessions.retrieve(session_id)
    items = []
    page = client.beta.agents.sessions.items.list(
        session_id, order="asc", limit=100
    )
    while True:
        items.extend(page.data)
        if not page.has_next_page():
            break
        page = page.get_next_page()
    return session, items

历史应分页读取,不能只取100条就宣布已恢复全部结果。生产系统应按任务范围增量持久化必要证据,设置大小与访问限制,避免每次恢复都把整个会话加载到内存。

确认需要后续Turn后,才发消息

以下是基于官方事件输入形状的示例,不是“无条件重试按钮”。调用前必须确认原提交不在等待、没有未核对的副作用,且用户授权仍有效:

def send_follow_up(client: OpenAI, session_id: str, text: str):
    return client.beta.agents.sessions.events.create(
        session_id,
        events=[{
            "type": "agent.session.input.message",
            "input": [{
                "role": "user",
                "content": [{"type": "input_text", "text": text}],
            }],
        }],
    )

建议后续输入是已核验的结构化摘要,例如:“job版本2,已保存正文,测试站草稿ID已对账;只核验SEO字段,不创建第二篇,不发布。”摘要只是上下文,真正的限制仍由工具权限网关执行。

五、断流恢复:重建视图,不等于重新执行

官方Events指南给出的恢复顺序是:打开新流并缓冲事件,读取Session与已保存Items,以Item ID恢复本地状态,合并缓冲更新,再继续处理实时事件。已达到最终状态的历史Item不应被旧的缓冲更新覆盖。事件流不重放遗漏的中间事件。OpenAI Events and items

这意味着“最后收到哪个事件”不一定等于“业务执行到哪一步”。UI光标可用于显示,任务恢复必须核对保存结果与业务账本。

状态重建伪代码

获取恢复租约
→ 开新事件流,持续收集到有界缓冲
→ retrieve(session_id)
→ 分页读取相关Items与Turn结果
→ 按Item ID重建保存视图
→ 合并缓冲更新,保留终态Item
→ 核对副作用账本与外部回执
→ 决定观察、处理动作、继续或人工介入

这里需要并发读流和历史抓取,而不是打开流后一直不消费。缓冲区满时应记录视图失效并重建,不静默丢弃事件后仍宣称状态准确。

Session空闲不是业务成功

Turn完成也不代表所有工具都成功。控制面应检查根Agent的Turn结局、工具结果和业务断言,不让子Agent完成事件提前终止整个任务观察。OpenAI Session结果处理

例如文件生成失败后,Agent可能仍正常返回失败说明。它的Turn可以结束,但“文档已交付”这个业务条件没有成立。

六、required_actions:需要补结果,不是补一句“继续”

如果Session等待函数结果,应识别对应的name、arguments、turn_id和call_id,经过参数与权限验证执行,保存回执,再按同一调用标识返回结果。等待环境连接则应处理environment_connection,不能把它当业务函数执行。OpenAI required_actions说明

对函数工具,可以用“会话+Turn+call_id”登记一次调用,但业务操作仍需要operation_key。因为同一业务动作可能在新的Turn再次产生一个不同call_id。

结果回传失败后,不应再次执行函数;先确认业务结果,再复用已保存回执。本文不捏造函数输出事件Schema,具体返回形状应跟随对应工具官方参考与当前SDK。

调用结果为什么要先入账?

假设函数创建测试站草稿,远端已返回ID,但应用在回传Agent前崩溃。恢复后仍看到待处理call_id。如果没有账本,应用可能再次创建;有账本,就能返回同一已确认结果。

然而“远端写入成功”和“本地账本保存成功”通常不在同一事务中。只加一张表不能消除这个缝隙,需要远端幂等支持或可可靠查询的业务标识。

七、环境恢复:ID相同,不代表文件和进程相同

OpenAI官方Sandbox生命周期文档明确指出,复用Environment ID不会恢复替换计算资源上的文件;中途断连不会自动重启被杀死的命令。进程崩溃后的待处理输入也没有通用自动恢复保证。OpenAI Sandbox lifecycle

Coding任务至少应保存代码快照、Git提交或Diff、依赖锁文件、测试报告、产物摘要和运行命令。重建机器后先校验文件,不把旧测试报告用于新文件。

资源推荐恢复策略
源码从审查过的提交或快照恢复
临时下载按来源与摘要重新获取
安装依赖按锁文件重建并记录版本
正在运行的命令核对结果后决定重跑
浏览器登录重新授权,不复制无关Cookie
短期凭据根据当前任务权限重新签发

文件存在还不够,应检查摘要和来源。如果恢复目录混入了别的任务数据,Agent可能继续工作但操作错误对象。

执行器重连有等待窗口,不能把迟到的连接当作超时输入会自动重放。原请求仍在等待时不要重复提交;先确认请求或Session结局,再安排下一步。OpenAI环境重连边界

八、副作用幂等:不能靠提示词保证只执行一次

创建草稿、上传文件、发送通知、修改数据库都是副作用。正确策略是“稳定业务键+请求摘要+操作状态+远端回执+未知状态对账”。

operation_key可以由租户、job_id、任务版本和动作类型构成;同一键对应不同请求摘要时应拒绝,避免把修改后的内容误认为已完成的旧操作。

import hashlib
import json

def request_digest(payload):
    canonical = json.dumps(
        payload, ensure_ascii=False,
        sort_keys=True, separators=(",", ":")
    )
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

def decide_effect(state):
    if state == "CONFIRMED":
        return "reuse_receipt"
    if state in {"SENT", "UNKNOWN"}:
        return "reconcile_before_retry"
    if state == "PREPARED":
        return "check_lease_and_dispatch"
    return "human_review"

这个函数只演示状态决策,不提供远端幂等保证。发送前状态必须持久化为SENT;之后发生异常时保守标记UNKNOWN。若远端不支持幂等键,也没有唯一可查询标识,不能安全声称自动重试永不重复。

WordPress标题不是可靠唯一键。建议在受控发布服务中登记job_id与文章ID,并在服务端实现去重或唯一业务标识。只在客户端搜索同标题文章,无法排除并发和同名情况。

先核对再继续
回执检查、暂停执行轨道、审批钥匙与防重复门;文字先核对,再继续

九、检查点:保存可验证进度,不保存一句自我总结

检查点应是阶段提交证据。推荐分为资料采集、正文生成、结构质检、图片准备、草稿创建和待发布审批,每阶段明确输入版本和产物摘要。

{
  "job_id": "article-demo-001",
  "task_version": 2,
  "phase": "DRAFT_CONFIRMED",
  "artifacts": [{
    "role": "article",
    "uri": "controlled-artifact-reference",
    "sha256": "REPLACE_WITH_ACTUAL_DIGEST"
  }],
  "remote": {"wordpress_post_id": 101},
  "next_allowed_actions": ["verify_seo"],
  "approval_status": "not_granted"
}

这是应用检查点格式,不是API参数。Agent不能自行把approval_status改成已批准;审批记录由独立身份写入,并绑定任务版本、内容摘要、目标资源和有效期。

检查点记录“正文完成”,但正文文件找不到或摘要不匹配时,应回到重建与复核阶段,不跳过验收。检查点记录“草稿创建”,却没有可核对的文章ID时,应对账而不是继续发布。

十、n8n+WordPress案例:断在创建草稿之后怎么办?

以AI Stack Nav资料到文章流程为例:采集来源→生成正文与SEO→生成图片→上传媒体→创建WordPress草稿→人工发布。

故障发生在草稿已创建、n8n尚未归档ID的时候。此时恢复步骤不是重跑整个n8n流程:

  1. 读取job_id对应的Session和操作账本。
  2. 查询受控发布服务的operation_key结果。
  3. 若确认草稿存在,登记文章ID并读回状态。
  4. 核验正文摘要、Excerpt、图片ID和SEO字段。
  5. 只补缺失字段,不再次创建文章。
  6. 保持draft,将生产发布留给有效审批。

如果只能找到疑似同标题文章,没有稳定业务标识,应停止自动写入并交编辑核对。

n8n负责流程,恢复账本需要可靠数据层

不应把全部状态只保存在Code节点内存中。应用数据库或受控服务保存任务身份与外部回执,n8n传递job_id并读写状态。开发和生产凭据分离,测试流程不持有生产发布权限。

Excerpt、正文图片和Rank Math字段需要逐项读回验收。HTTP返回成功不代表所有插件元数据已经生效;恢复时同样如此。媒体上传是独立副作用,应分别登记媒体ID,避免恢复后重复上传三张图。

恢复后的资料包推荐插入也应绑定文章ID和操作键,不让任务重复给正文尾部追加同一推广内容。

十一、Webhook、轮询与并发恢复

Webhook适合通知控制面有状态变化,但处理器收到消息后仍应读取当前状态,不直接按照延迟事件启动或停止机器。官方提供Session Webhook配置与事件说明,实施时应遵循签名验证和事件Schema。OpenAI Session webhooks

参考处理流程:验证原始请求签名→校验租户与事件范围→持久化事件→成功入队后返回→Worker读取当前Session→获取恢复租约→对账→执行允许的动作。

不能先返回成功再尝试入队,否则队列故障会导致通知被“确认但未处理”。重复通知通过唯一事件记录去重;Worker崩溃后任务可重派,但副作用必须由账本控制。

轮询作为补偿通道,扫描长时间无更新任务并重新读取状态。扫描间隔应结合任务时长、账户限流和成本,不把每秒查询所有Session作为默认方案。

租约失效不等于旧Worker真的停止

租约需要到期时间、所有者和递增fencing token。旧Worker仍可能执行写入,因此受控网关也要检查token;若下游不能验证,应串行化写入入口并重新对账。仅在数据库里加锁,不能撤销已经发出去的HTTP请求。

十二、安全、预算与清理

恢复前重验当前权限:原用户是否仍有效,凭据是否撤销,工具白名单是否变化,审批是否到期,业务目标是否被修改。持久化的是任务证据,不是永久授权。

长任务容易累积过期上下文。恢复包应包含已核验目标、检查点、产物和允许动作,并明确外部资料与旧日志都不是系统指令。模型提出扩大权限时应生成申请,不自动执行。

设定单任务时间预算、推理成本预算、工具调用预算和恢复次数。恢复三次是可选示例,不是厂商推荐;UNKNOWN副作用和授权问题不应通过更多重试解决。

清理也要分层:会话删除、执行资源停止、临时文件清理和业务数据保留不是一件事。官方指出删除Session不会自动停止自托管计算资源;会话与资源需要协调清理。OpenAI Sandbox清理说明

取消任务后先禁止新输入,确认正在执行的动作结局,再收回短期凭据和释放资源。取消推理不会自动撤销已创建草稿或已经发送的通知。

十三、故障注入与验收清单

测试必须观察到的结果
关闭客户端连接不重复提交原任务,恢复已保存结果
调用应用重启从持久映射找回Session
保存100条以上Items正确分页,不漏关键回执
写入后丢失响应进入UNKNOWN并先对账
双Worker同时恢复只有授权写入者能产生副作用
执行机器替换校验并恢复文件,不信旧环境ID
撤销凭据或审批停止执行,不自动绕过
用户取消不自动重新开启任务

验收不要只检查“任务最后完成”。还应检查重复草稿数、重复媒体数、无授权调用数、错误检查点推进数和未知操作遗留数。

系统可定义恢复时间和最大可接受进度损失,但不要把业务数据库的指标直接当成厂商会话SLA。最终报告区分已完成、已确认失败、结果未知和待人工决定。

十四、FAQ

1. Durable Session是不是永久记忆?

不是。它不能替代账户保留策略、权限管理和应用数据备份;不要假设无限期保存。

2. 断网后Agent一定停止吗?

不能仅根据客户端断网判断。应读取服务端Session与Turn状态。

3. 可以直接发送“继续”吗?

先核对原提交、待处理动作和副作用。工作中的消息可能改变当前Turn,而不是恢复旧步骤。

4. 事件流能重放断开期间的所有内容吗?

不能。应依靠保存Items重建结果,遗漏的中间事件未必可恢复。

5. Environment ID相同就能找到原文件吗?

不能这样假定。替换机器需要存储、快照或重新构建,并校验摘要。

6. 函数结果回传失败,要重新执行吗?

先查回执。已确认完成的操作复用结果;未知结果先对账。

7. 幂等表能保证Exactly-once吗?

不能单独保证。还需要远端幂等或可可靠核对的唯一业务标识。

8. 恢复任务会继承之前的审批吗?

必须检查审批范围、版本和有效期,不能自动继承过期或被撤销授权。

9. Turn完成就是业务完成吗?

不是。工具失败和业务断言失败都可能存在,需要独立验收。

10. 可以完全无人值守恢复吗?

可以自动处理明确、受限且可核对的情况;权限变化、未知副作用和高风险动作保留人工介入。

十五、GEO结论、相关阅读与来源

可引用结论:Agent中断恢复需要同时处理会话历史、执行环境和业务副作用;Durable Session不是自动重放全部操作的保证。安全恢复遵循先读取、再对账、后继续,并保持审批与幂等边界。

证据口径:接口、事件流和环境边界来自OpenAI官方文档;任务表、检查点、租约、账本和WordPress恢复策略是作者参考设计。核验日期为2026年9月15日,生产部署需重新确认SDK与服务行为。

相关阅读:AI Stack Nav教程入口、站内Agents API文章检索。使用真实首页和搜索入口,不推测未经核验的文章Slug。

安装部署教程

环境配置与 Docker 工作流

适合阅读安装部署、本地配置、服务器搭建和自动化流程类文章后继续转化。

环境配置资料包 包含 Windows / Mac / Linux 常见环境配置、依赖安装和报错排查清单。 查看资料包 Docker 工作流包 整理 Docker 部署模板、compose 示例和常用服务编排流程。 查看资料包

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

本站累计访问量: 367,877 次
AI Stack Nav 客服会员 / 支付 / 下载 / 工具库
你好,我是 AI Stack Nav 客服助手。你可以问我会员开通、微信支付、资料下载、订单入口、AI 工具库等问题。