从字节跳动 DeerFlow 源码看 Agent 平台设计(四):Agent 生命周期与状态管理

从字节跳动 DeerFlow 源码看 Agent 平台设计(四):Agent 生命周期与状态管理 系列导航第一篇什么是 Agent一个成熟 Agent 平台的 8 个核心组件第二篇工具系统设计 — 从全量绑定到按需加载第三篇五个核心中间件深度解析本篇Agent 生命周期与状态管理摘要本文阐明 Agent 系统中三个容易混淆的层次概念Thread、Run、Loop iteration详细说明中间件 hook 的触发时序分析 Clarification 中断后通过 Checkpoint 恢复执行的完整机制并讨论 Docker-compose 部署场景下 AioSandboxProvider 的 DooD 方案。一、三层概念Thread、Run、Loop Iteration理解 Agent 的生命周期首先需要区分三个层次的概念。1.1 Thread线程/会话Thread 代表一个持续的对话线程由唯一的thread_id标识。一个 Thread 可以跨越多天、包含多次用户交互。Thread 的状态通过 Checkpointer 持久化存储。1.2 Run执行/请求Run 代表一次完整的用户请求处理过程——从接收到用户消息到返回最终响应。一个 Thread 中包含多个 Run每次用户发消息触发一个新 Run。1.3 Loop Iteration循环迭代Loop iteration 代表 Agent 核心 ReAct 循环中的一次模型调用。一个 Run 中可能包含多次循环迭代——模型可能需要调用多个工具后才能给出最终回答。1.4 层次关系Thread会话跨多次交互由 Checkpointer 持久化 │ ├── Run 1用户发送第一条消息 │ ├── Loop 1: 模型调用 → tool_call(web_search) → 工具执行 │ ├── Loop 2: 模型调用 → tool_call(read_file) → 工具执行 │ └── Loop 3: 模型调用 → 最终文本回复无 tool_call │ ├── Run 2用户发送第二条消息 │ ├── Loop 1: 模型调用 → tool_call(bash) → 执行 │ ├── Loop 2: 模型调用 → tool_call(write_file) → 执行 │ ├── Loop 3: 模型调用 → tool_call(bash) → 验证 │ ├── Loop 4: 模型调用 → tool_call(str_replace) → 修复 │ └── Loop 5: 模型调用 → 最终回复 │ └── Run 3用户发送第三条消息 └── Loop 1: 模型调用 → 直接文本回复二、中间件 Hook 的触发时序2.1 Hook 与层次的对应关系Hook对应层次执行频率before_agent/after_agentRun一次/Runbefore_model/after_model/wrap_model_callLoop iteration每轮循环wrap_tool_call工具调用每次工具调用DeerFlow 项目文档中有明确表述before_agent/after_agent只跑一次。before_model/after_model/wrap_model_call每轮循环都跑。2.2 执行顺序规则before_*正序执行列表位置 0 → Nafter_*反序执行列表位置 N → 0这意味着列表最后的中间件其after_model最先执行。2.3 完整时序示例以一个包含两轮工具调用的 Run 为例一次 Run ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ before_agent [正序一次] │ │ [0] ThreadData: 创建线程目录 │ │ [1] Uploads: 扫描上传文件 │ │ [2] Sandbox: 分配沙箱 │ │ [12] LoopDetection: 清理旧 run 的 pending warning │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Loop 1 │ │ │ │ before_model [正序] │ │ │ │ [6] Summarization: 检查 token 是否需要压缩 │ │ │ │ [10] ViewImage: 注入图片 base64 │ │ │ │ wrap_model_call │ │ │ │ [3] DanglingToolCall: 补悬空 ToolMessage │ │ │ │ [12] LoopDetection: 注入当前 run warning │ │ │ │ ─── MODEL 调用 → 返回 tool_call ─── │ │ │ │ after_model [反序] │ │ │ │ [12] LoopDetection: 检测循环 │ │ │ │ [11] SubagentLimit: 截断多余 task │ │ │ │ [8] Title: 生成标题 │ │ │ │ wrap_tool_call │ │ │ │ [5] ToolErrorHandling: 异常转 ToolMessage │ │ │ │ [13] Clarification: 检查是否 ask_clarification │ │ │ │ ─── 工具执行 → 返回结果 ─── │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Loop 2 │ │ │ │ before_model → wrap_model_call → MODEL → after_model │ │ │ │ 模型返回最终文本无 tool_call循环终止 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ after_agent [反序一次] │ │ [12] LoopDetection: 清理当前 run 未消费 warning │ │ [9] Memory: 入队记忆更新 │ │ [2] Sandbox: 释放沙箱 │ │ │ └─────────────────────────────────────────────────────────────────────┘2.4 各中间件选择 Hook 的逻辑需求场景选择的 Hook原因整个 Run 只需做一次的初始化/清理before_agent / after_agent避免每轮重复执行每次模型调用前都需要检查before_model消息列表每轮都在增长需要修改发给模型的 messages/toolswrap_model_call在模型调用的最后一刻修改请求需要修改或拦截模型返回after_model第一时间处理模型输出需要拦截特定工具调用wrap_tool_call选择性中断Run 结束后做异步副作用after_agent不阻塞响应返回三、Checkpoint 与中断恢复3.1 Checkpoint 机制LangGraph 的 Checkpointer 在每个图节点执行完毕后自动将当前完整状态序列化并持久化存储。DeerFlow 使用 SQLite 作为默认 Checkpointer 后端。Checkpoint 中保存的内容包括ThreadState的所有字段messages完整对话历史sandbox沙箱 IDtitle自动生成的标题artifacts生成的文件列表todos任务列表promoted已提升的延迟工具3.2 Clarification 中断的完整流程当模型调用ask_clarification工具时步骤 1中间件拦截并中断# ClarificationMiddleware.wrap_tool_callreturnCommand(update{messages:[ToolMessage(content❓ 你要删除哪个文件,...)]},gotoEND,)Command(gotoEND)使图执行跳转到__end__节点。此时图正常终止Checkpointer 保存最终状态# Checkpoint 中保存的状态{messages:[HumanMessage(帮我删除文件),AIMessage(tool_calls[{name:ask_clarification,args:{...}}]),ToolMessage(❓ 你要删除哪个文件)],sandbox:{sandbox_id:local:thread-123},title:文件操作,...}步骤 2SSE 流结束前端展示澄清消息Gateway 发出end事件前端检测到ask_clarification类型的 ToolMessage将其渲染为交互式提问 UI。步骤 3用户回复发起新 RunPOST /api/langgraph/threads/thread-123/runs { input: {messages: [{role: user, content: 删除 temp.txt}]} }关键请求携带同一个 thread_idthread-123。步骤 4Checkpointer 恢复状态Worker 在执行agent.astream()时LangGraph 自动从 Checkpointer 加载该 thread_id 的最新状态并将新消息追加到messages列表# 恢复后的完整状态{messages:[HumanMessage(帮我删除文件),AIMessage(tool_calls[{name:ask_clarification,...}]),ToolMessage(❓ 你要删除哪个文件),HumanMessage(删除 temp.txt)# ← 新追加],...}步骤 5Agent 继续执行图从入口节点开始执行模型接收到完整的对话历史——包括之前的澄清问题和用户的回答——自然理解上下文并继续完成任务。3.3 常见误解澄清误解中断恢复后 Agent 不是从头执行吗图确实从入口节点重新开始。但从头开始指的是图的执行流程不是对话上下文。状态从 Checkpoint 恢复后模型看到的 messages 列表包含了完整的历史对话因此模型具备所有必要的上下文来继续工作。类比每次向 ChatGPT 发送消息时整个对话历史都会发送给模型。LangGraph 的 Checkpoint 机制本质上就是这个对话历史的持久化存储。误解before_agent 的中间件会重新初始化所有资源吗是的新 Run 的before_agent会重新执行。但中间件设计为幂等——例如 SandboxMiddleware 的acquire()对同一 thread_id 会复用已有沙箱而非重新创建。四、Run 级别的状态管理4.1 Pre-run Snapshot 与 RollbackDeerFlow 在每次 Run 开始前保存一份 Checkpoint 快照pre_run_snapshot。如果 Run 执行过程中发生异常可以回滚到 Run 开始前的状态# worker.pypre_run_checkpoint_idNonepre_run_snapshotNoneifcheckpointerisnotNone:ckpt_tupleawaitcheckpointer.aget_tuple(config_for_check)ifckpt_tupleisnotNone:pre_run_checkpoint_idckpt_config.get(checkpoint_id)pre_run_snapshot{checkpoint_ns:...,checkpoint:copy.deepcopy(ckpt_tuple.checkpoint),metadata:copy.deepcopy(ckpt_tuple.metadata),pending_writes:copy.deepcopy(ckpt_tuple.pending_writes),}回滚确保失败的 Run 不会污染 Thread 的持久化状态——用户下次发消息时看到的是上次成功 Run 后的状态。4.2 Run 状态机每个 Run 有明确的状态流转PENDING → RUNNING → COMPLETED → FAILED → CANCELLED状态由RunManager管理通过StreamBridge将状态变更实时推送给前端。五、Docker-compose 部署下的沙箱方案5.1 问题容器内如何创建容器当 DeerFlow 通过 docker-compose 部署时Gateway 服务本身运行在容器内。此时AioSandboxProvider需要创建新的 Docker 容器作为代码执行沙箱——这涉及容器内创建容器的问题。业界有两种解决方案方案原理优劣DinD (Docker-in-Docker)容器内运行完整 Docker daemon性能差、安全风险高、存储驱动嵌套问题DooD (Docker-outside-of-Docker)容器通过宿主机 Docker socket 调用宿主机 daemon轻量、新容器与调用者平级5.2 DeerFlow 的 DooD 方案DeerFlow 采用 DooD 方案。docker-compose.yaml 中gateway:volumes:# 将宿主机 Docker socket 挂载进 Gateway 容器-${DEER_FLOW_DOCKER_SOCKET}:/var/run/docker.sockGateway 容器内的AioSandboxProvider通过挂载的 socket 与宿主机 Docker daemon通信。新创建的沙箱容器与 Gateway 容器是同级兄弟关系不存在容器嵌套宿主机 Docker Daemon ├── deer-flow-nginx (反向代理) ├── deer-flow-frontend (Next.js) ├── deer-flow-gateway (Agent 运行时) │ └── 通过 /var/run/docker.sock 调用宿主机 Docker API ├── sandbox-thread-abc123 ← 沙箱容器与 gateway 平级 └── sandbox-thread-def456 ← 另一个沙箱容器5.3 Provisioner 模式对于更大规模的部署多节点、K8s 环境DeerFlow 支持 Provisioner 模式sandbox:use:deerflow.community.aio_sandbox:AioSandboxProviderprovisioner_url:http://provisioner:8002此模式下Gateway 不直接操作 Docker/K8s API而是通过 HTTP 接口请求 Provisioner 服务分配和管理沙箱容器。Provisioner 作为独立服务运行负责容器的生命周期管理、资源池化、和调度策略。5.4 孤儿容器回收AioSandboxProvider实现了启动时孤儿回收机制——扫描上次进程崩溃后遗留的沙箱容器将其纳入 warm pool 或标记为待清理def_reconcile_orphans(self):启动时扫描遗留容器防止资源泄漏。runningself._backend.list_running()forinfoinrunning:self._warm_pool[info.sandbox_id]info logger.info(fAdopted container{info.sandbox_id}into warm pool)这确保了即使 Gateway 异常退出下次启动时不会出现幽灵容器持续占用资源。5.5 部署前检查DeerFlow 提供了部署前检查机制。deploy.sh脚本在启动前验证 Docker socket 可用性if[!-S$DEER_FLOW_DOCKER_SOCKET];thenecho⚠ Docker socket not found at$DEER_FLOW_DOCKER_SOCKETecho AioSandboxProvider (DooD) will not work.exit1fiscripts/doctor.py提供更全面的环境诊断检查 Docker/Container CLI 可用性、socket 权限等。六、中间件执行顺序为何重要6.1 SafetyFinishReason 与 LoopDetection 的顺序这两个中间件都在after_modelhook 工作。注册顺序为middlewares[...,LoopDetectionMiddleware,SafetyFinishReasonMiddleware,ClarificationMiddleware]LangChain 的after_model按反序执行。因此 SafetyFinishReason后注册先执行执行顺序: SafetyFinishReason.after_model → 先检测安全截断清除 tool_calls LoopDetection.after_model → 后检测循环看到的是已清理的消息如果顺序颠倒LoopDetection 先执行它会看到被截断的 tool_calls 并可能误判为循环模式发出虚假的循环警告。6.2 ClarificationMiddleware 在列表最后位置最后使其wrap_tool_call最先拦截LangChain 工具调用包装也是反序。这确保ask_clarification调用被第一时间拦截不会被其他中间件如 ToolErrorHandlingMiddleware错误地当作异常处理。6.3 SandboxMiddleware 的对称性before_agent列表位置 2获取沙箱after_agent反序执行倒数第 3 个释放沙箱。这种外层进入/外层退出的模式确保了如果中间执行过程中发生异常after_agent仍能正确释放资源。七、总结本文讨论的几个核心问题在 Agent 平台工程化中具有普遍性问题DeerFlow 的解决方案Agent 执行状态跨请求持久化LangGraph Checkpointer SQLite中断后恢复执行Checkpoint 状态恢复 消息追加容器化部署下的代码隔离DooD 方案 Provisioner 可选扩展中间件执行顺序依赖显式列表排序 反序规则Run 失败后的状态回滚Pre-run snapshot 条件回滚理解这些机制后在使用 LangGraph 或类似框架构建 Agent 系统时能够更准确地预判状态管理中的潜在问题并选择合适的持久化和恢复策略。系列总结本系列四篇文章从不同维度分析了 DeerFlow Agent 平台的架构设计全局认知Agent 的定义与 8 个核心组件的系统性拆解工具系统三层工具架构与延迟加载的创新设计中间件模式五个核心中间件的动机、机制与决策生命周期管理状态持久化、中断恢复与部署实践DeerFlow 作为当前开源 Agent 框架中架构完整度较高的项目之一其源码中蕴含的工程决策——从 tool_search 的按需加载、到 Skill Rescue 的摘要保护、到 Safety 中间件的防截断循环——对 Agent 平台的设计与实现具有较高的参考价值。