Workflow 系列(03):状态管理——持久化、幂等性与版本绑定

为什么状态管理是核心问题

一个 Agent Workflow 运行到第 5 个 Phase 时崩溃了。重启之后,它从头开始还是从第 6 个 Phase 继续?

没有状态持久化,只能从头开始:之前的 LLM 调用、工具执行、人工确认全部作废。

状态管理的作用:工作流在任意时刻中断,从最近的检查点精确续接。这也是人工确认门的基础,门被触发意味着工作流暂停在某个确定状态,人工响应后从该状态继续。


Durable Execution 模式

将执行过程序列化为可恢复的检查点,任何时候中断,从最近检查点重新执行,结果与不中断一致。Temporal.io 是这个模式的代码层典型实现,但用 JSON 文件同样可以实现相同的语义。

状态文件结构

json 复制代码
{
  "workflow_id": "wf-bug-e2e-AE-33995-20260601",
  "workflow_version": "1.3.0",
  "jira_key": "AE-33995",
  "started_at": "2026-06-01T10:00:00+08:00",
  "phase": "phase_4",
  "phases": {
    "phase_1": {
      "status": "done",
      "completed_at": "2026-06-01T10:02:30+08:00",
      "output_file": "bug_info.json"
    },
    "phase_2": {
      "status": "done",
      "completed_at": "2026-06-01T10:05:00+08:00",
      "output_file": "log_analysis.json"
    },
    "phase_3": {
      "status": "done",
      "completed_at": "2026-06-01T10:18:00+08:00",
      "output_file": "analysis_final.json"
    },
    "phase_4": {
      "status": "in_progress",
      "step": "step_4_1",
      "steps": {
        "step_4_1": {"status": "done", "output_file": "candidate_a.json"},
        "step_4_2": {"status": "in_progress"},
        "step_4_3": {"status": "pending"}
      }
    }
  }
}

续接协议

python 复制代码
def resume_workflow(state_file: Path) -> None:
    state = json.loads(state_file.read_text())

    for phase_id, phase_data in state["phases"].items():
        if phase_data["status"] == "done":
            continue  # 跳过已完成的 Phase

        if phase_data["status"] == "in_progress":
            # in_progress 视同 pending 重新执行(幂等性保证安全)
            execute_phase(phase_id, state)
            return

        if phase_data["status"] == "pending":
            execute_phase(phase_id, state)
            return

关键设计:续接时只信状态文件,不信记忆 。主 Agent 不记得自己做过什么,只读取状态文件里的 status 字段。in_progress 的 Phase 会被重新执行,这要求每个 Phase 的操作是幂等的。

两端写入

状态文件要在 Phase 开始 和完成都写入,不能只写完成时:

python 复制代码
def execute_phase(phase_id: str, state: dict) -> None:
    # 开始前:标记 in_progress(即使崩溃,续接时也能找到这个 Phase)
    state["phases"][phase_id]["status"] = "in_progress"
    write_state(state)

    try:
        result = run_phase_logic(phase_id, state)
        
        # 完成后:标记 done,记录输出文件路径
        state["phases"][phase_id]["status"] = "done"
        state["phases"][phase_id]["output_file"] = result.output_file
        write_state(state)
        
    except Exception as e:
        state["phases"][phase_id]["status"] = "failed"
        state["phases"][phase_id]["error"] = str(e)
        write_state(state)
        raise

幂等性设计

续接协议里说"in_progress 视同 pending 重新执行",意味着一个 Phase 可能被执行两次。如果 Phase 里的操作不幂等,就会产生重复副作用:两条 Jira 评论、两个 git commit、两封通知邮件。

各类操作的幂等性分析

文件写入(天然幂等)

python 复制代码
# 覆盖写是幂等的------多次执行结果相同
output_file.write_text(json.dumps(result))  # ✅

Jira 评论(不幂等,需要检测)

python 复制代码
# ❌ 错误:直接写评论,重复执行会写两条
jira.add_comment(issue_key, comment_text)

# ✅ 正确:写前先检查是否已有本次处理的评论
def add_comment_idempotent(issue_key: str, comment_text: str, run_id: str) -> None:
    existing = jira.get_comments(issue_key)
    marker = f"[run_id:{run_id}]"  # 每次工作流运行用唯一 ID 标记
    
    if any(marker in c.body for c in existing):
        return  # 已写过,跳过
    
    jira.add_comment(issue_key, f"{marker}\n{comment_text}")

Git commit(不幂等,需要检测)

python 复制代码
# ❌ 错误:直接提交,重复执行会创建两个 commit
git.commit(message)

# ✅ 正确:提交前检查 commit result 文件是否已存在且 passed=true
def commit_idempotent(message: str, output_file: Path) -> dict:
    if output_file.exists():
        result = json.loads(output_file.read_text())
        if result.get("passed"):
            return result  # 已提交成功,直接返回之前的结果
    
    # 执行提交
    commit_sha = git.commit(message)
    result = {"passed": True, "sha": commit_sha}
    output_file.write_text(json.dumps(result))
    return result

外部 API 触发(条件幂等)

python 复制代码
# Gerrit 添加 Reviewer:重复添加不报错,天然幂等 ✅
gerrit.add_reviewer(change_id, reviewer)

# 创建定时任务:重复创建会出现重复任务 ❌
# → 修复:创建前先 list,检查是否已存在
def create_cron_idempotent(job_config: dict) -> None:
    existing_jobs = cron.list_jobs()
    if any(j["name"] == job_config["name"] for j in existing_jobs):
        return  # 已存在,跳过
    cron.create_job(job_config)

幂等性自检表

每个新增的 Step,在实现前回答这三个问题:

vbnet 复制代码
□ 这个 Step 如果执行两次,会产生副作用吗?
□ 如果会,如何检测"已执行过"并跳过?
□ 检测逻辑本身是否也是幂等的?

最后一个问题容易被忽略。检测逻辑如果依赖内存状态或有副作用,在续接场景下同样会失效。


状态文件版本绑定

工作流运行到一半时,如果修改了工作流定义(比如新增了一个 Step),续接时会出现不一致:旧的状态文件没有新 Step 的记录,主 Agent 不知道该如何处理。

在状态文件里绑定工作流版本,续接时校验版本一致性。

python 复制代码
def start_or_resume(state_file: Path, current_version: str) -> dict:
    if state_file.exists():
        state = json.loads(state_file.read_text())
        saved_version = state.get("workflow_version")
        
        if saved_version != current_version:
            # 版本不一致,告知用户选择
            raise WorkflowVersionMismatch(
                f"State file version: {saved_version}\n"
                f"Current workflow version: {current_version}\n"
                f"Options:\n"
                f"  1. Continue with saved state using old workflow ({saved_version})\n"
                f"  2. Start fresh with new workflow ({current_version})\n"
                f"  3. Manually migrate state file"
            )
        
        return state  # 版本一致,正常续接
    
    # 新建状态文件
    state = {
        "workflow_version": current_version,
        "started_at": datetime.now(timezone.utc).isoformat(),
        "phases": {}
    }
    write_state(state, state_file)
    return state

版本号规则(MAJOR.MINOR.PATCH)

markdown 复制代码
MAJOR:Phase 结构变化(新增/删除 Phase,路由逻辑重大变更)
        → 对正在运行的工作流有断裂风险,必须处理版本不一致
        → 一般不可直接续接,需要人工决策

MINOR:新增 Step、模板优化、新增确认门选项
        → 向后兼容,正在运行的工作流可以继续用旧版本完成
        → 新触发的工作流用新版本

PATCH:措辞优化、配置调整、不影响行为
        → 安全更新,旧状态文件可以无感升级

设计 Checklist

状态持久化

  • 每个 Phase/Step 在开始前写入 in_progress,完成后写入 done
  • 续接协议只读状态文件,不依赖主 Agent 的对话历史
  • 状态文件包含 workflow_version 字段

幂等性

  • 所有外部写操作(Jira 评论、git commit、API 调用)都经过幂等性检查
  • 幂等性检测用唯一标识符(run_id 或 output_file 存在性)
  • 检测逻辑本身不产生副作用

版本绑定

  • 续接时校验状态文件版本与当前工作流版本是否一致
  • MAJOR 版本变更有明确的续接策略
  • 版本不一致时给用户提供可操作的选项,不是直接报错退出

总结

  1. Durable Execution 的关键是两端写入 :Phase 开始前写 in_progress,完成后写 done,崩溃在任何位置都能精确续接
  2. 续接要求幂等性in_progress 会被重新执行意味着每个外部写操作都要能安全执行两次,文件写入天然幂等,Jira 评论和 git commit 需要显式检测
  3. 版本绑定防止静默错误:工作流修改后,旧状态文件和新工作流版本不匹配时应该告知用户,而不是用新逻辑处理旧状态,产生难以排查的错误

欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。

更多实用知识和有趣产品,欢迎访问我的个人主页

相关推荐
冬奇Lab2 小时前
每日一个开源项目(第146篇):openpilot - 开源自动驾驶辅助系统,曾在 Consumer Reports 评测中超过特斯拉 Autopilot
人工智能·开源·自动驾驶
吴佳浩3 小时前
AI 工程师知识地图:模型格式、框架、部署工具一次讲明白
人工智能·aigc·ai编程
IT_陈寒3 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
码农胖大海4 小时前
AI额度不够用的解决方案
人工智能
后端小肥肠4 小时前
小红书虚拟商品怎么做?我先用 Skill 跑通了壁纸品类
人工智能·aigc·agent
feiyu_gao4 小时前
从零搭建个人 AI 工作台:一个管理者的 3 个月实验
人工智能·aigc·团队管理
程序员cxuan5 小时前
一句话,让你用上 GPT-5.6
人工智能·后端·程序员
机器之心5 小时前
AI圈刚开始谈Loop Engineering,两位95后博士已经盯上了人类闭环数据
人工智能·openai