多 Agent 协作中的状态快照与持久化:基于 Redis 与 SQLite 的 State 序列化实战
2026/9/4 23:00:41 网站建设 项目流程

多 Agent 协作中的状态快照与持久化:基于 Redis 与 SQLite 的 State 序列化实战

在多 Agent 协同执行复杂长流程任务(例如全自动分析财务报表、多步骤代码重构、跨系统工单流转)时,一个任务往往需要耗费数十秒甚至数分钟,中间经历十几次的状态流转(State Transitions)与工具调用。

在很多简陋的 Demo 中,所有 Agent 的状态、对话历史和执行变量都仅仅保存在应用的内存对象中。
一旦遇到:

  • K8s 集群触发滚动发布或节点驱逐(Pod Eviction);
  • 宿主机发生 OOM 或网络短暂闪断;
  • 某个第三方 Tool 调用超时导致当前工作协程挂起;

整个长任务的上下文就会在瞬间灰飞烟灭,用户端只剩下一个无限处于 Loading 的死连接,之前的几十次大模型推理成本全部白白浪费。

为了让多 Agent 系统具备真正的生产级容灾能力,系统必须具备**状态快照(State Snapshot)与断点恢复(Checkpoint Resume)**机制。

今天我们拆解如何利用 Redis(用于高频热状态存储)与嵌入式 SQLite(用于低成本全量归档),实现一套轻量、强类型、零数据丢失的 Agent 状态持久化方案。


一、状态机的数据模型定义:强类型与可序列化

Agent 的状态机不能随便塞入不可序列化的原生对象(如未关闭的 Socket 连接、Thread 实体或复杂闭包)。必须将状态严格抽象为纯数据对象(Data Transfer Object, DTO)。

import time from typing import Dict, Any, List, Optional from pydantic import BaseModel, Field class ToolExecutionRecord(BaseModel): tool_name: str arguments: Dict[str, Any] result: Any latency_ms: int timestamp: float class MultiAgentWorkflowState(BaseModel): workflow_id: str = Field(..., description="工作流唯一全局实例 ID") session_id: str = Field(..., description="用户会话 ID") current_node: str = Field(default="START", description="当前停留的状态机节点") step_index: int = Field(default=0, description="当前执行步数序号") shared_memory: Dict[str, Any] = Field(default_factory=dict, description="共享业务上下文数据") history_records: List[ToolExecutionRecord] = Field(default_factory=list, description="历史工具调用与执行轨迹") is_finished: bool = Field(default=False) updated_at: float = Field(default_factory=time.time) def serialize_json(self) -> str: """序列化为标准紧凑 JSON""" return self.model_dump_json() @classmethod def deserialize_json(cls, json_str: str) -> "MultiAgentWorkflowState": """反序列化并自动进行强类型校验""" return cls.model_validate_json(json_str)
flowchart LR State[Agent 状态转移 State Transition] --> Checkpoint[生成快照 Checkpoint] Checkpoint --> Redis[(Redis 内存热存储 - 秒级读写 & TTL)] Checkpoint --> SQLite[(本地 SQLite 数据库 - 永久归档 & 审计)] Crash[发生服务重启 / Pod 重建] --> Loader[断点加载器 CheckpointLoader] Redis -.-> Loader SQLite -.-> Loader Loader --> Resume[无缝恢复现场从 step_index 续跑]

二、双层存储架构:Redis 热缓存 + SQLite 冷持久化

在生产设计中,我们采用轻量双层架构:

  1. Redis 负责运行时热读写:每次 Agent 节点执行完毕,在毫秒级将 State 写入 Redis,Key 设为agent:state:{workflow_id},并设置 24 小时过期时间(TTL)。
  2. SQLite 负责本地永久留痕与离线分析:单文件轻量嵌入,每个 Pod 或宿主机维护一个workflows.db,异步写入每个 Step 的版本历史,便于事后排障与 Bad Case 回放。
import redis import sqlite3 import json class WorkflowStateManager: def __init__(self, redis_client: redis.Redis, sqlite_db_path: str = "agent_states.db"): self.redis = redis_client self.sqlite_path = sqlite_db_path self._init_sqlite() def _init_sqlite(self): with sqlite3.connect(self.sqlite_path) as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS workflow_snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, workflow_id TEXT NOT NULL, step_index INTEGER NOT NULL, node_name TEXT NOT NULL, state_json TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(workflow_id, step_index) ) """) conn.commit() def save_checkpoint(self, state: MultiAgentWorkflowState): """保存状态快照(双写)""" state.updated_at = time.time() json_data = state.serialize_json() # 1. 写入 Redis(热状态,覆写当前最新) redis_key = f"agent:state:{state.workflow_id}" self.redis.set(redis_key, json_data, ex=86400) # 2. 写入 SQLite(追加历史快照) try: with sqlite3.connect(self.sqlite_path) as conn: conn.execute(""" INSERT INTO workflow_snapshots (workflow_id, step_index, node_name, state_json) VALUES (?, ?, ?, ?) ON CONFLICT(workflow_id, step_index) DO UPDATE SET state_json=excluded.state_json """, (state.workflow_id, state.step_index, state.current_node, json_data)) conn.commit() except Exception as e: print(f"[Warn] SQLite 快照持久化异常: {str(e)}") def load_latest_state(self, workflow_id: str) -> Optional[MultiAgentWorkflowState]: """优先从 Redis 加载,若无则从 SQLite 兜底恢复""" redis_key = f"agent:state:{workflow_id}" cached = self.redis.get(redis_key) if cached: return MultiAgentWorkflowState.deserialize_json(cached.decode('utf-8')) # Redis 未命中,降级查 SQLite with sqlite3.connect(self.sqlite_path) as conn: cursor = conn.cursor() cursor.execute(""" SELECT state_json FROM workflow_snapshots WHERE workflow_id = ? ORDER BY step_index DESC LIMIT 1 """, (workflow_id,)) row = cursor.fetchone() if row: return MultiAgentWorkflowState.deserialize_json(row[0]) return None

三、带断点续传的图执行器(Graph Runner)实现

在执行循环中,每执行完一个 Node,就递增step_index并保存一次快照。如果启动时发现历史快照存在,直接跳过已执行的步骤!

class ResumableGraphRunner: def __init__(self, state_manager: WorkflowStateManager, node_handlers: Dict[str, Any]): self.state_mgr = state_manager self.nodes = node_handlers # {"PLANNER": func, "EXECUTOR": func, ...} def run_or_resume(self, workflow_id: str, initial_state: MultiAgentWorkflowState): # 1. 尝试断点恢复 current_state = self.state_mgr.load_latest_state(workflow_id) if current_state: print(f"[*] 发现已有快照,从节点 [{current_state.current_node}] 第 {current_state.step_index} 步继续执行...") else: print(f"[*] 初始化全新工作流实例: {workflow_id}") current_state = initial_state self.state_mgr.save_checkpoint(current_state) # 2. 状态机主循环 while not current_state.is_finished: node_name = current_state.current_node handler = self.nodes.get(node_name) if not handler: raise ValueError(f"未找到节点处理器: {node_name}") print(f"-> 正在执行节点: {node_name} (Step {current_state.step_index})") # 执行节点业务逻辑,返回更新后的增量字段或下一跳目标 next_node, state_updates = handler(current_state) # 更新全局状态 current_state.shared_memory.update(state_updates) current_state.current_node = next_node current_state.step_index += 1 if next_node == "END": current_state.is_finished = True # 核心:执行完毕立即落盘快照! self.state_mgr.save_checkpoint(current_state) print(f"[✓] 工作流 {workflow_id} 执行完毕!") return current_state

四、生产实操避坑原则

  1. 共享上下文保持扁平shared_memory中只放关键的结构化结果(如订单 ID、分析结论摘要),大体积的原始文件或完整网页 HTML 存入对象存储,状态机只存 URL 或文件路径,防止单个 State 膨胀到几十兆。
  2. 幂等节点设计:断点恢复时,若某一步骤在崩溃前刚刚执行了外部扣款但未及存快照,重试时工具层必须有幂等防护(结合之前讲过的 ToolIdempotentGuard)。
  3. 设置合理的 TTL 与归档清理:Redis 中的 State 务必加 TTL,SQLite 数据库定期运行VACUUM并按月分割,防止磁盘被历史快照撑满。

把状态快照和断点续跑做进编排核心,你的多 Agent 系统才能在生产环境的风吹草动中稳如泰山。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询