跳到正文

运行时、沙箱与状态存储 ​

一句话:运行时解决“在哪持续执行”,状态存储解决“凭什么能接着执行”。两者必须分开设计:沙箱可以被替换,任务事实不能随沙箱一起消失。

对应原书:第 7 章《Agent 运行时与沙箱》、第 8 章《Agent 状态存储与语义资产》。

为什么需要 ​

把 Agent 放进容器里跑,只解决了第一层问题。真实系统里失败通常长这样:

  • 进程还活着,但会话已经断了——用户重连后看到的是一个空窗口;
  • 实例被替换,任务“从头开始”,重复发送了上周的邮件;
  • 快照恢复成功,但外部数据库里的事务已经提交,系统状态与任务状态对不上。

三件事的根因是同一个:把“进程活着”当成了“任务还在”。运行时要管理的是任务生命周期,状态存储要保存的是可恢复的事实,二者都不能用进程内存来承担。

核心机制 ​

一、沙箱是可编程的受控工作空间 ​

沙箱提供用户空间的计算、文件系统、语言与依赖、终端/浏览器/桌面入口,可选网络、数据挂载、身份凭证、观测与状态保留。它按任务配置、有隔离边界、有明确生命周期,任务结束回收。

但不是所有动作都要建沙箱:参数固定的业务动作直接调 API 更稳、更便宜。沙箱的价值在于“需要探索、需要真实环境、需要影响范围可控”的场景。

二、三类负载必须分开 ​

负载部署关系首要指标纪律
工具执行编排在沙箱外,按步骤调用代码/浏览器/桌面首次可用时延、任务间隔离按产物验收,退出码成功不等于任务正确
长时任务Agent 与工具链共居一个持续工作区会话连续性、恢复效果、等待成本只维持进程存活不足以承担连续性
训练与评测批量独立环境,收集轨迹环境吞吐、重置与分叉、可复现区分“任务没完成”与“环境故障”

混在一起会误判瓶颈:工具执行的瓶颈在启动时延,长任务的瓶颈在恢复与等待,训练评测的瓶颈在吞吐与重置。用同一个池子跑,任何一类的指标都会被另外两类污染。

环境类型至少分五类:代码、浏览器、桌面、连续任务(下载→计算→生成→预览共用工作区)、MCP/Skill 适配环境。按产物验收,而不是按命令退出码验收。

三、会话亲和、休眠与围栏 ​

逻辑会话到执行环境的映射由路由层查找或恢复;任务身份到数据目录的映射按租户、用户、项目校验归属。两类映射不必一一对应,但只把用户编号拼进目录名不能代替服务端检查。

休眠分三层:持续运行 → 浅暂停(保内存)→ 深休眠(持久化后释放资源)→ 仅保留文件重建。唤醒入口必须在休眠沙箱之外,否则沙箱睡了就没人叫醒它。能否休眠也不能只看“没有新请求”,要看有没有未完成动作、未确认消息与恢复窗口。

围栏(Fencing) 是防重复副作用的关键:旧实例失联不等于已经停止,用执行代次加受保护存储拒绝旧代次写入;外部操作结果按原标识核对后再续。

四、并行的四件套 ​

Template(一致起点)→ Snapshot(保存准备成果)→ Fork(同一快照并行分支)→ Reset(防上一轮污染)。一次准备可以产出多条独立轨迹,这是评测与训练效率的来源。

但要记住快照的边界:它只覆盖沙箱内状态,回退不了外部数据库、已发消息、已完成的业务操作;沙箱快照也不等于模型训练检查点。

五、状态分层承载 ​

顺序是先定义正确性边界,再选引擎。反过来做,通常会被某个引擎的能力带着走,最后用“最终一致”掩盖业务上不能接受的不一致。

四类对象的恢复要求不同:运行时状态要求低 RPO、事件可重放、提交幂等;工作区与产物要求版本可追溯、正式制品不可变;记忆与知识要求权威源与可重建派生物分离;本体要求 Schema 受控变更、结论可解释。

六、Event Log、Checkpoint 与执行租约 ​

  • Event Log 只追加、按任务或会话顺序一致,不必全局串行;事件与当前状态同事务提交,或以 Outbox 方式投递;用事件标识去重。只有包含完整状态变更与必要输入的事件才能重放,诊断日志不能冒充。
  • Checkpoint 的频率要匹配任务成本:外部调用前后、长计算之后、人工确认前后、工作区变更之后、交接时。增量快照等于完整快照加变化集。
  • 执行租约是接力棒:只有持最新租约的实例可以提交;围栏标识防止旧实例复活写入。

恢复的目标不是“恰好一次”,而是“可解释地继续”。外部副作用靠幂等标识、结果确认与补偿来处理,光靠重放做不到。

七、工作区分层与写时复制 ​

基础层(只读可复用)+ 共享层 + 可写层(当前任务独占)。共享不变的部分,隔离变化的部分,这同时是成本优化和治理边界。

写时复制让快照、回滚、分支变便宜;Checkpoint 记录工作区快照引用,避免出现“任务状态恢复了、文件状态变了”的错配。制品库要满足三性:可寻址(稳定引用)、可保留(区分临时与正式)、可交付(带来源、版本、审批状态与权限元数据)。

八、记忆与本体 ​

记忆按“来源 × 稳定性”两个维度组织:来源分用户显式表达、Agent 行为归纳、外部业务事实;稳定性分人设层、画像层、事件偏好层。写入流程是抽取 → 合并去重 → 冲突消解(时效性 + 来源可靠度);召回是向量 + 关键词 + 实体关系多路混合,再重排与时间衰减。长期约束不应因为很少被调用而淡出。

本体的四要素是对象、关系、动作、规则,三层递进:语义层 → 数据流转层 → 智能决策层。责任要分开:本体负责语义建模,动作由运行时按语义执行,授权由治理方作出。模型推测不能直接写成组织规则。

知识检索双轨:RAG 做文档理解、切片、混合检索与增量重建;GraphRAG 做实体关系、图谱构建与多跳查询。两者统一重排(相关性、权威性、时效性),答案要能溯源到文档段落版本,权限过滤覆盖检索到生成全链路。

九、平台责任三平面:执行面 · 数据与资源面 · 控制面 ​

运行时与状态存储不是孤立组件,它们分属三个平台平面,职责不能混:

  • 执行面:真实跑 Loop,可弹性、可失败、可恢复。沙箱、Worker 池、调度器都在这层。
  • 数据与资源面:保存可恢复事实,定义一致性与租户边界。Event Log、Checkpoint、Workspace、Session Store 都在这层。
  • 控制面:版本、身份、策略、配额、审计;集中定义、判定点分布式执行,避免强同步依赖。Policy、Budget Lease、Evidence 归属都在这层。

三平面不互推:执行面的故障不能靠数据面兜底,控制面的策略不能由执行面自行解释。边界保留才能判断故障归属、数据位置和最终责任。

最小上手 ​

yaml
runtime:
  loads: {tool_exec: separate_pool, long_task: co_located, eval: isolated}
  sleep: {shallow: keep_memory, deep: persist_and_release, wake_entry: outside_sandbox}
  fencing: {generation: monotonic, reject_stale_writes: true}
state:
  event_log: append_only
  checkpoint_at: [before_external_call, after_long_compute, before_handoff]
  lease: {holder_only_commit: true}
workspace:
  layers: [base_readonly, shared, writable]
  cow: true

验收看三件事:杀掉实例后任务能接手且不重复副作用;深休眠唤醒后工作区与任务状态一致;旧实例复活无法写入。

生产动作 ​

  • 恢复后复查外部状态。连接、临时凭证、远端登录态、服务端口都可能已经失效;按标识核对已发出的请求,再决定继续、重试还是补偿。
  • 重分配前清理。进程、文件、挂载、网络规则都要清,否则上一任务的痕迹会进入下一任务。
  • 容量按可回收性管理。冷启动是资源分配、镜像准备、进程初始化的总和;镜像缓存与预热池降时延,容量耗尽时有界排队或明确拒绝。CPU 低不能单独作为回收依据。
  • 状态提交与索引更新分离。先可靠提交权威事实,再异步推送向量索引与图谱投影;反过来会出现“检索到了但状态没提交”。

边界 ​

  • 沙箱不等于收益。必须用完整任务链验证:能不能续行、每个有效结果花多少时间与成本。
  • 快照不等于恢复。它只回到沙箱内的文件状态,外部副作用只能补偿。
  • 进程存活不等于会话连续。状态与工作区必须独立于承载保存。
  • 统一数据库不等于单一引擎。运行时状态、工作区、记忆、本体的一致性要求不同,强行统一会牺牲其中一类。
  • 回收不等于删除。归档、降权、删除是三种不同操作,删除要连原始记录、摘要、索引、缓存一起处理。

讲清楚 ​

运行时管任务的生老病死,状态存储管任务凭什么能接着跑。三类负载(工具执行、长时任务、训练评测)要分池,因为瓶颈完全不同;沙箱只是执行场所,会话连续性与状态恢复必须独立于它。

状态分四层承载:运行时状态、工作区与产物、记忆与知识、本体语义。底层用 Event Log 记过程、Snapshot 看当前、Checkpoint 做恢复点,靠执行租约和围栏防止旧实例复活。快照回不了外部副作用,重复提交只能靠幂等与补偿解决。

恢复的正确姿势是:定位最后确认事件 → 加载检查点 → 校验租约与幂等 → 核对已发出的外部操作 → 再决定继续、重试、补偿还是转人工。

适用:Agent 运行时、沙箱与持久化状态设计 | 初稿 2026-10

把 AI 工程讲到既懂也会用