项目类型:Long-horizon SuperAgent Harness / Agent Runtime / 开源 Agent 平台
赏析目标:学习一个真实 Agent 项目如何组织 Harness、Agent、Sub-Agent、RunStore、Memory、Skills、Sandbox、Worker 与 Failure Recovery
核心问题:一个 Agent 真正要"跑几分钟到几小时"时,架构为什么必须围绕"失败"而不是"成功"来设计?
前面三次项目赏析,我们分别看了:
- LangGraph ------ 学到了 Workflow / State / Graph(任务怎么编排)
- OpenHands ------ 学到了 Coding Agent / Runtime / Sandbox / Event(Agent 怎么执行真实动作)
- browser-use ------ 学到了 DOM 序列化 / 界面感知(Agent 怎么"看"真实界面)
但继续看这三个项目有一个共同的问题:
它们都默认"一次执行很快结束"。
LangGraph 的图跑完就停;OpenHands 的 step 循环是交互式的、人在旁边;browser-use 一次任务通常几分钟。
而真实世界里有一类需求:
"调研一下 X 领域的最新进展,读 200 篇论文,写一份带引用的综述。"
"把这个仓库从 Python 3.8 升级到 3.12,跑通所有测试。"
"监控这个系统一整夜,出问题自动分析并给出处理建议。"
这些任务的共同特点是:
耗时:分钟级 → 小时级
步骤:几十 → 几百
工具:跨网络 / 跨沙箱 / 跨子代理
失败:中途必然发生(网络抖、模型抽风、租约过期、进程被 kill)
这时候,一个真正的长任务 Agent 和一个 Agent Demo 的区别才开始显现。
因此,这次我选择 DeerFlow 2.0。
DeerFlow 是字节开源的 long-horizon SuperAgent harness,官方定位:
"An open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skills, subagents and message gateway, it handles tasks that could take minutes to hours."
它 2026 年 2 月 28 日登上过 GitHub Trending 第 1,2.0 是一次 ground-up rewrite(注意:1.x 是 src/ 结构,2.0 是 backend/ + frontend/ 结构,本文只谈 2.0 的 main 分支)。
这也是它最值得学习的地方:
它不是在教你"怎么写一个 Agent",而是在展示"一个 Agent 系统怎么在长跑中不死"。
一、先不要看代码:DeerFlow 到底解决什么问题?
先想象一个最简单的长任务 Agent。
用户:
"调研一下 RAG 的最新进展,写一份综述。"
最简单的实现:
User
↓
LLM
↓
循环:调工具 / 写文件
↓
返回综述
但这根本不是 long-horizon Agent。
因为 Agent 必须真正面对:
进程会死
网络会断
模型会抽风
租约会过期
子代理会卡住
任务会被用户取消
中途崩溃要能恢复
这意味着 Agent 必须拥有:
- LLM
- Tool
- Sub-Agent
- Sandbox
- Memory
- Run / Worker
- RunStore(持久化)
- Checkpoint(检查点)
- Lease(租约)
- Guardrail(护栏)
- Observability(可观测)
DeerFlow 的价值就在这里。
它不是单纯让模型多转几圈,而是把模型放进一个能容忍失败的执行环境中。
官方 2.0 的设计哲学可以压缩成一句话:
长任务的本质不是"跑得快",而是"跑不死"。
二、先记住 DeerFlow 最重要的一张图
第一次看 DeerFlow,最容易迷路。
因为你会看到:
harness
app
runtime
subagents
agents
sandbox
skills
guardrails
persistence
tracing
scheduler
gateway
channels
名字很多。
但实际上可以先压缩成两层:
┌─────────────────────────────┐
│ app(主机 / Host) │
│ gateway / channels / │
│ scheduler / mcp_tasks / │
│ subagent_batches │
└──────────────┬────────────────┘
│ 调用
▼
┌─────────────────────────────┐
│ harness(引擎 / Engine) │
│ Agent / Sub-Agent / Run / │
│ Worker / RunStore / Memory / │
│ Sandbox / Skills / Guardrail │
└─────────────────────────────┘
也就是:
app负责"把 Agent 接到各种入口"(HTTP、IM、定时任务、MCP),harness负责"把 Agent 真正跑起来并跑不死"。
这是 DeerFlow 命名上最值得注意的一点:它管自己叫 harness(挽具 / 引擎框架),而不是 framework 或 platform。
harness 这个词在 AI 工程里特指"包裹并持有 Agent 的那一层 "------它不替你决定业务,只负责让 Agent 在各种执行环境里稳定存活。对应的证据是 backend/langgraph.json:
{
"graphs": {
"lead_agent": "deerflow.agents:make_lead_agent"
},
"checkpointer": {
"path": "./packages/harness/deerflow/runtime/checkpointer/async_provider.py:make_checkpointer"
}
}
图的入口在 packages/harness/deerflow/agents,而 backend/app/ 下是 gateway(HTTP)、channels(IM)、scheduler(定时)、subagent_batches、mcp_tasks------这些全是"入口/主机"层,不碰 Agent 内核。
一句话:harness 是可复用的 Agent 引擎,app 是把它接入不同管道的宿主。这个边界,是你看懂 DeerFlow 的第一把钥匙。
三、第一个闪光点:DeerFlow 建在 LangGraph 上,而不是自己造轮子
这是我认为 DeerFlow 最值得学习的设计决策之一,也正好和 OpenHands 形成对照。
OpenHands 选择自建一套 Runtime + Event + State(它在 V1 里明确把状态、事件、运行时做成自己的原语)。
DeerFlow 的选择相反------它直接站在 LangGraph 肩上:
# agents/lead_agent/agent.py
from langchain.agents import create_agent
...
def _assemble_lead_agent(config, *, app_config):
...
graph = create_agent(
model=...,
tools=final_tools,
middleware=normalize_middleware_state_schemas(middlewares, mode),
state_schema=get_thread_state_schema(mode),
...
)
create_agent 是 LangChain 的新一代 Agent 原语(不是 langgraph.prebuilt 的 create_react_agent),它返回一个标准的 CompiledStateGraph。
这意味着:
DeerFlow 没有重新发明"推理-行动循环",而是把 LangGraph 当作自己的"操作系统",然后在上面叠加 harness 层的能力。
为什么这个设计重要?
假设 OpenHands 要支持一个新模型,它得改自己的 Runtime;DeerFlow 要支持新模型,只要 LangGraph/LangChain 支持,它就自动支持------因为模型解析、checkpoint、streaming、human-in-the-loop 这些"Agent 基础设施"全部由 LangGraph 提供。
LangGraph 负责:推理循环 / 状态图 / 检查点 / 流式 / HITL
DeerFlow 负责:Sub-Agent / Run / Worker / RunStore /
Memory / Skills / Sandbox / Guardrail / 容错
这正是"不要重复造轮子"在 Agent 工程里的具体体现。但也带来代价:你如果想彻底理解 DeerFlow,必须先懂 LangGraph------这就是它学习曲线陡的原因。
四、第二个闪光点:middleware 层,而不是一个大 Agent 类
很多初学者写 Agent 时会把所有逻辑堆进一个类:
class Agent:
def run(self, input):
# 澄清歧义
# 过滤工具
# 注入上下文
# 清洗输入
# 处理 LLM 错误
# 写记忆
...
DeerFlow 则把这些都拆成 middleware(中间件) ,挂在 create_agent 外面:
agents/middlewares/
├── clarification_middleware.py # 模糊时反问
├── deferred_tool_filter_middleware.py # 延迟加载工具
├── durable_context_middleware.py # 持久上下文
├── dynamic_context_middleware.py # 动态上下文
├── input_sanitization_middleware.py # 输入清洗
└── llm_error_handling_middleware.py # LLM 错误处理
每个 middleware 都是一个独立职责,通过 LangGraph 的 middleware 机制在图执行的特定切面插入逻辑。
这其实是一条非常重要的工程边界:
把"Agent 该做什么"和"Agent 执行前后要做什么"分开。
澄清、清洗、错误处理、上下文注入,这些都是"横切关注点",不该污染核心推理逻辑。middleware 模式让它们可独立测试、独立开关、独立组合。
五、第三个闪光点(核心):Run / Worker / RunStore ------ 把"长任务"当成一等公民
这是 DeerFlow 2.0 最值钱的设计,也是它和普通 Agent 框架最大的分野。
普通 Agent 框架里,一次执行就是一个函数调用。DeerFlow 则把一次执行建模成一个 Run:
# runtime/runs/manager.py
class RunRecord:
run_id: str
owner_worker_id: str | None = None # 谁在跑
lease_expires_at: str | None = None # 租约何时过期
ownership_lost: bool = False # 所有权是否丢失
status: RunStatus # pending/running/interrupted/...
注意这三个字段:owner_worker_id、lease_expires_at、ownership_lost。
它们共同构成了一个基于租约的所有权模型------这是分布式系统里处理"进程死亡"的经典手段,被 DeerFlow 用在了单机 Agent 执行上。
为什么需要租约?
想象一个场景:
Worker A 正在跑一个 2 小时的任务。
Worker A 所在的 pod 突然被 k8s 杀掉。
Worker B 启动了。
如果没有租约,Worker B 不知道 A 跑的任务是不是还在跑、跑到哪了、要不要接管。
有了租约:
Worker A 每隔 lease_seconds/3 续一次租约(heartbeat)。
Worker A 死了 → 租约过期 → Worker B 发现这个 run 的 owner 已经不可达
→ 触发孤儿恢复(orphan recovery)→ 重新 hydrate 这个 run → 继续跑。
代码证据(runtime/runs/manager.py):
async def _heartbeat_loop(self) -> None:
"""Periodically renew leases and reclaim orphaned runs from dead peers.
Lease renewal runs every lease_seconds / 3. Reconciliation
(sweeping for expired leases owned by dead workers) runs every
lease_seconds (every 3rd cycle) so orphaned runs are recovered
without waiting for a pod restart.
"""
lease_seconds = self._run_ownership_config.lease_seconds
interval = max(1, lease_seconds // 3)
...
if cycle % 3 == 0:
self._schedule_orphan_reconciliation()
ORPHAN_RECOVERY_STOP_REASON = "orphan_recovered" 和 LEASE_ORPHAN_RECOVERY_ERROR = "Run lease expired --- owning worker is unreachable." 这两个常量,把这个容错故事写在了代码里。
一句话:DeerFlow 把"长任务"当成一个需要被持久化、被租赁、被恢复的一等公民,而不是一个转瞬即逝的函数调用。这是它能跑"几分钟到几小时"的根本原因。
六、第四个闪光点(核心):Failure Recovery 四件套
如果说 RunStore 解决了"任务状态存哪",那下面这四件套解决了"任务中途挂了怎么办"。这是 DeerFlow 2.0 真正的护城河,也正好回应了你最初强调的那些 GitHub Issue:
1. 检查点回滚(Checkpoint Rollback)
用户取消任务时,不是简单中止,而是回滚到运行前的检查点:
# runtime/runs/worker.py
rollback_point: RollbackPoint | None = None
...
async def _finish_cancellation(self, action):
if action == "rollback":
checkpoint_rollback_completed = await _rollback_to_pre_run_checkpoint(
rollback_point=rollback_point,
...
)
else: # "interrupted"
RunStatus.interrupted
_capture_rollback_point 会在运行前抓取"物化状态 + 原始待定 channel_values",让取消操作能 fork 出一条干净的 pre-run 谱系。这意味着:用户在任务跑了一半点取消,系统能恢复到"好像没跑过"的状态,而不是留下一堆半成品文件。
2. 中断状态(Interrupted State)
interrupted 不是 error,它是一个独立状态,专门为"被取消但可恢复"设计:
# runtime/runs/store/memory.py
if run["status"] not in ("pending", "running", "interrupted"):
... # 只有这三个状态允许被 rollback
RunStore 把 interrupted 纳入合法的状态机,让恢复路径和错误路径解耦。
3. 循环检测 / 预算护栏(Loop & Budget Guards)
这是最漂亮的一处设计。DeerFlow 把"Agent 卡死"建模成一组护栏中间件 ,每个护栏在 runtime.context 里写入一个 stop_reason:
# runtime/runs/worker.py
# loop_detection -> "loop_capped"
# token_budget -> "token_capped"
# safety_finish_reason -> "safety_capped"
# subagent_limit -> "subagent_limit_capped"
# model_length_finish_reason -> "model_length_capped"
也就是说,当 Agent 在重复同样动作(loop)、token 超预算、触发安全终止、子代理数超限、模型输出超长时,worker 会拿到一个明确的 stop_reason,而不是无限空转。这对长任务至关重要------一个卡住的 Agent 空转一小时,比直接失败更可怕。
4. 孤儿恢复(Orphan Recovery)
前面第五节的 heartbeat + 租约过期检测,最终落到的就是孤儿恢复:当持有租约的 worker 失联,其他 worker 周期性扫描过期租约,把孤儿 run 接管回来继续跑。
汇总成一张表
| 机制 | 解决什么失败场景 | 实现位置 |
|---|---|---|
| 租约续期(heartbeat) | worker 进程假死但还在 | RunManager._renew_leases |
| 孤儿恢复 | worker 进程真死(pod 被 kill) | RunManager._heartbeat_loop → _schedule_orphan_reconciliation |
| 检查点回滚 | 用户取消 / 运行前失败 | RunWorker._rollback_to_pre_run_checkpoint |
| 中断状态 | 取消但可恢复 vs 真错误 | RunStore 状态机(含 interrupted) |
| 循环/预算护栏 | Agent 卡死 / 超预算空转 | guard middleware 写 stop_reason |
| Run 水合(hydration) | 从持久化存储恢复内存状态 | RunManager.hydrate run from store |
这六件套合起来,就是 DeerFlow 敢说"撑几分钟到几小时"的底气。普通 Agent 框架缺的,正是这一整层。
七、第五个闪光点:Sub-Agent 子系统 ------ 委派、隔离与验收
长任务往往可以拆成子任务并行跑。DeerFlow 有一整套 Sub-Agent 子系统(subagents/):
# subagents/executor.py
class SubagentExecutor:
"""Executor for running subagents."""
def __init__(self, config, tools, ...,
acceptance_criteria=None,
loop_detection_recorder=None,
execution_capacity=None):
几个关键设计:
1. 隔离的事件循环
子代理跑在独立的 asyncio 事件循环上,避免父子共享事件循环导致的阻塞:
def run_on_isolated_subagent_loop[T](coro) -> Future[T]: ...
def _get_isolated_subagent_loop() -> asyncio.AbstractEventLoop: ...
这意味着父 Agent 不会因为某个子代理的阻塞而卡死。
2. 执行容量准入(execution_capacity)
不是无限制派发子代理,而是有准入控制:
max_concurrent_subagents = effective_subagent_concurrency(...)
max_total_subagents = cfg.get("max_total_subagents", _default_max_total_subagents(...))
SubagentExecutionCapacity 在父 Agent 的 task tool 派发时共享,防止子代理爆炸式增长。
3. 验收检查(acceptance_checks)
这是 DeerFlow 特别务实的一点------子代理的结果不是"返回就完事",而是有验收标准:
# subagents/acceptance_checks.py(1396 行)
# 例如 "tests_passed:<command>" 这类验收叶节点,
# 必须锚定到具体的执行记录
子代理的 SubagentResult 里携带 tool_receipts、bash_executions、token_usage------父 Agent 能据此判断子任务是否真的完成,而不是轻信子代理的"我搞定了"。
一句话:Sub-Agent 不是"甩锅给小弟",而是"委派 + 隔离 + 准入 + 验收"的完整协议。
八、第六个闪光点:DeerMem ------ 会"挑食"的持久记忆
DeerFlow 2.0 的记忆系统是可插拔的(memory.manager_class),默认后端是 DeerMem 。它最值得注意的设计是:不是什么都记,而是有严格的"事实门槛"。
# agents/memory/backends/deermem/deermem/core/updater.py
def _fact_scope_gate_reason(fact) -> str | None:
"""Return the deterministic rejection reason for a model-extracted fact."""
if _normalize_gate_label(fact.get("scope")) != "user":
return ... # 只收"关于用户"的事实
if _normalize_gate_label(fact.get("durability")) != "durable":
return ... # 只要"持久"的
if _normalize_gate_label(fact.get("authority")) != "descriptive":
return ... # 只要"描述性"的,不要结论性的
也就是说,模型从对话里抽取事实时,DeerMem 只保留关于用户、持久、描述性的事实,自动丢弃临时信息、关于第三方的信息、或带结论的信息。
这背后的工程思考是:
记忆系统的核心难题不是"怎么存",而是"记什么"。 什么都记 = 噪声爆炸 + 上下文污染。DeerMem 用确定的 gate 规则把"记忆膨胀"挡在门外。
此外还有 debounce(防抖)、容量裁剪(select_facts_for_capacity)、来源置信度校准(_coerce_source_confidence)------这些都是生产级记忆系统才会考虑的细节,而不是 demo 里一行 memory.append()。
九、第七个闪光点:Sandbox 与 Skills 的可插拔
DeerFlow 的 sandbox 抽象支持多 provider:
sandbox/
├── local/ # 本地
├── env_policy.py # 环境策略
├── lease.py # 沙箱租约
├── security.py # 安全
├── tools.py # 沙箱工具(2663 行)
└── ...
community/
├── e2b_sandbox/ # E2B 云沙箱
└── aio_sandbox/ # AIO 沙箱
沙箱同样有租约 (注意 _release_run_scoped_references、SANDBOX_SERVER_OWNED_CONTEXT_KEYS)------这说明 DeerFlow 把"资源生命周期"当成一个统一问题来治:Run 有租约、Sandbox 也有租约,死亡时都要能被回收,避免孤儿资源泄漏。
Skills 系统则以 SKILL.md 为包边界(skills.deferred_discovery 控制延迟发现),支持按需加载,避免把所有技能一股脑塞进上下文。
十、但是这里也暴露出一个很值得讨论的问题
DeerFlow 的容错体系非常强,但是:
强,意味着复杂。
现在 backend 有约 16 万行 Python,runtime/runs/worker.py 单文件 2895 行,manager.py 2337 行,sandbox/tools.py 2663 行。你第一次进入源码,很容易看到:
RunManager
RunWorker
RunStore
LeaseRenewal
StatusFinalization
RunIdempotencyConflict
create_run_atomic
create_thread_operation_atomic
_subagent_release_policy
_sandbox_orphan_reconciliation
...
然后产生:
"我是来看 Agent 的,怎么在看分布式系统?"
这其实就是成熟工程项目的特点:
功能复杂度最终会变成架构复杂度。
所以赏析 DeerFlow 最忌讳:
"我把每个类看懂了。"
真正应该问的是:
为什么 Run 需要租约?
为什么 interrupted 是独立状态?
为什么子代理要隔离事件循环?
为什么记忆要挑食?
为什么 sandbox 也要租约?
这些问题,每一个答案都指向"长任务必然失败"这个根本事实。
十一、DeerFlow 的第一个不足:配置与概念爆炸
如果你只是想做:
问答 Agent
根本不需要:
RunManager
Worker
RunStore
Lease
Middleware
SubAgent
DeerMem
Sandbox Provider
Guardrail
...
DeerFlow 的架构是因为:
它需要成为一个真正的 long-horizon SuperAgent harness。
所以这种复杂度是有原因的。
但对于个人项目或小团队:
很容易学到"架构长什么样",却不知道"为什么需要这么复杂"。
正确的学习方式是:
学习它背后的容错边界,而不是照搬所有组件。
比如你自己的 Repo Doctor 项目(博客第五阶段 36--50 篇),目前是单进程、同步跑、没有 RunStore。当你发现"任务跑到一半进程被 kill 就全没了"时,再引入 DeerFlow 式的租约 + 检查点,才是正解------而不是一开始就上全套。
十二、第二个不足:站在 LangGraph 肩上,也背上了 LangGraph 的包袱
因为 DeerFlow 的图是 create_agent 返回的 LangGraph,它的很多行为(checkpoint 格式、streaming 协议、HITL 接口)都受 LangGraph 版本约束。
CHANGELOG 里大量 breaking changes 都和这个绑定有关:
- gateway: Request trace ids 现在无条件签发
- memory: storage_path 语义从"文件路径"变为"根目录"
- sandbox: E2B 现在强制 sandbox.replicas 为进程级容量上限
- config: checkpoint_delta_snapshot_frequency 迁移到嵌套键
这些变更本质上是在"LangGraph 内核 + DeerFlow harness"两层之间做演进,复杂度叠加。
所以这个不足其实是一体两面的:
用 LangGraph 省下了造轮子的成本,但也让自己永远跟着 LangGraph 的节奏走。
十三、和 LangGraph、OpenHands 放在一起看
这是这次项目赏析最重要的地方。三个项目分别提供了不同视角:
LangGraph
= 怎么编排 Agent(有向图 / State / Node / Edge)
OpenHands
= Agent 怎么执行真实动作(自建 Runtime / Event / Sandbox)
DeerFlow
= 长任务 Agent 怎么"跑不死"(Run / Worker / RunStore / 容错)
三者不是竞争关系,而是三个不同层次:
Workflow / Graph (LangGraph 教你的)
↓
Agent Runtime (OpenHands 教你的)
↓
Long-running Fault Tolerance (DeerFlow 教你的)
这里有一个特别有意思的对照:
| 维度 | LangGraph | OpenHands | DeerFlow |
|---|---|---|---|
| 推理循环 | 自建 StateGraph | 自建 reasoning-action loop | 用 create_agent(LangGraph) |
| 执行环境 | 不关心(交给用户) | 自建 Runtime + Sandbox | 用 Sandbox Provider(E2B/AIO) |
| 状态持久化 | Checkpointer(标准) | Event Stream(自建) | RunStore + Checkpoint(自建+LangGraph) |
| 长任务容错 | 基础(HITL/断点) | 中等(Session 恢复) | 重武器(租约/孤儿/回滚/护栏) |
| 子代理 | 有 | 无(单 Agent 强) | 一等公民(委派/验收/准入) |
| 记忆 | 外接 | 外接 | DeerMem 内置可插拔 |
| 适用场景 | 编排复杂 workflow | 软件工程执行 | 长时研究/创作/编码 |
一句话总结三者差异:
LangGraph 给你图,OpenHands 给你手,DeerFlow 给你"长跑不死"的心脏。
十四、正确的阅读路线
我建议按这个顺序,不要从 frontend 开始:
第一层 create_agent 怎么组装 Agent
(agents/lead_agent/agent.py + agents/factory.py)
↓
第二层 Run / Worker / RunStore 怎么管理长任务
(runtime/runs/)
↓
第三层 容错四件套:heartbeat / 回滚 / 中断 / 护栏
(manager.py 的 _heartbeat_loop + worker.py 的 _finish_cancellation)
↓
第四层 Sub-Agent 怎么委派与验收
(subagents/executor.py + acceptance_checks.py)
↓
第五层 DeerMem 怎么记、怎么挑
(agents/memory/backends/deermem/)
↓
第六层 Sandbox / Skills / Guardrail 怎么插拔
(sandbox/ + skills/ + guardrails/)
↓
第七层 app 怎么把 harness 接到入口
(backend/app/ 的 gateway / channels / scheduler)
不要:
从 frontend 开始
否则很快迷路。
十五、这个项目真正让我学到的三个工程原则
如果整篇文章只留下三句话,我会留下这三个。
第一:长任务的本质是"容错",不是"智能"
普通 Agent = 模型够强就行
长任务 Agent = 模型强 + 跑不死
让 Agent 在进程死亡、网络抖动、用户取消、模型抽风时还能恢复,比让模型多答对一道题重要一百倍。
第二:把"任务"建模成可租赁、可恢复的对象
Run = 一等公民
租约 = 进程死亡的检测器
孤儿恢复 = 死亡后的接管者
检查点 = 取消时的后悔药
owner_worker_id + lease_expires_at + ownership_lost 这仨字段,比任何"Agent 怎么思考"的论文都实在。
第三:记忆的核心是"记什么",不是"怎么存"
什么都记 = 噪声爆炸
只记 user/durable/descriptive = DeerMem 的 gate
DeerMem 的 _fact_scope_gate_reason 用确定规则把记忆膨胀挡在门外,这比 RAG 向量库的"全存"高级得多。
十六、如果让我给 DeerFlow 2.0 做一次工程评价
我不会简单地说:
"DeerFlow 很牛。"
而是:
| 维度 | 评价 | 说明 |
|---|---|---|
| Run / Worker 抽象 | ★★★★★ | 长任务一等公民,业界少见 |
| Failure Recovery | ★★★★★ | 租约/孤儿/回滚/护栏六件套 |
| Sub-Agent 系统 | ★★★★★ | 委派+隔离+准入+验收完整 |
| Memory(DeerMem) | ★★★★★ | 会挑食的可插拔记忆 |
| 站在 LangGraph 上 | ★★★★☆ | 省力但绑定内核版本 |
| Sandbox 插拔 | ★★★★☆ | E2B/AIO/local 多 provider |
| 初学者友好度 | ★★☆☆☆ | 16 万行,概念爆炸 |
| 源码学习成本 | ★★☆☆☆ | 必须先懂 LangGraph |
| 小项目适用性 | ★★☆☆☆ | 杀鸡用牛刀 |
| 工程参考价值 | ★★★★★ | 长任务 Agent 的范本 |
它最大的优点:
把 Agent 从"一个能转圈的 LLM"提升成了"一个能长跑、会自愈的执行系统"。
它最大的缺点:
对于小型项目来说,架构和认知成本都偏高。
十七、不要抄 DeerFlow,要学 DeerFlow
这是这次项目赏析最重要的结论。
你完全没必要马上把自己的项目变成:
RunManager
RunWorker
RunStore
Lease
SubAgent
DeerMem
SandboxProvider
Guardrail
如果你的项目只有:
一个 Agent
三个 Tool
跑一次就结束
你可能只需要:
Agent
↓
Tool Registry
↓
Checkpointer
等问题真的出现:
进程会死
任务会卡
用户会取消
要并行子任务
记忆会膨胀
再逐步增加:
Run / Worker
RunStore
Lease / Orphan Recovery
Sub-Agent
Memory Gating
这才是真正的工程演进。
十八、把 DeerFlow 映射回你自己的学习体系
现在你可以把之前学习的东西重新串起来。
LangGraph
↓
Workflow / State / Graph
OpenHands
↓
Agent / Runtime / Event / Sandbox
DeerFlow
↓
Run / Worker / RunStore / Fault Tolerance / Sub-Agent / Memory
你的 AI Engineering OS
↓
Knowledge / Rules / Agent / Project / Review
这三个项目分别提供了不同视角:
LangGraph = 怎么编排 Agent
OpenHands = Agent 怎么执行真实任务
DeerFlow = 长任务 Agent 怎么跑不死
这时候你学习 Agent 的视角已经不再是:
"这个框架 API 怎么用?"
而变成:
"一个 Agent 系统到底要扛住哪些失败?"
这是非常大的提升。
十九、最终总结
DeerFlow 2.0 最值得赏析的,并不是它用了什么模型,也不是它有多少 Tool。
真正值得学习的是它背后的架构思想:
Harness
负责把 Agent 跑起来并跑不死
Run / Worker
负责把长任务当成可租赁、可恢复的对象
RunStore / Checkpoint
负责状态持久化与回滚
Lease / Orphan Recovery
负责进程死亡后的接管
Loop / Budget Guards
负责 Agent 卡死时的熔断
Sub-Agent
负责把大任务拆成可委派、可验收的小任务
DeerMem
负责"记什么"而非"怎么存"
Sandbox / Skills
负责能力与环境的隔离与插拔
然后形成:
提交任务
↓
Run 被租赁给某个 Worker
↓
Agent 在 Checkpoint 保护下推理-行动
↓
必要时委派 Sub-Agent(隔离 + 验收)
↓
失败?→ 租约过期触发孤儿恢复 / 取消触发检查点回滚 / 卡死触发护栏熔断
↓
成功 → 结果交付 + 记忆更新
这才是一个真正 long-horizon Agent 的基本生命循环。
而它暴露出的不足也同样值得学习:
系统越成熟,抽象越多;抽象越多,学习和维护成本也越高。
所以真正优秀的 Agent 工程不是:
"把 DeerFlow 全部搬过来。"
而是:
只在"长跑失败"的问题真正出现时,引入对应的容错机制。
这次项目赏析,我建议你最终记住这张图
┌──────────────────────────────┐
│ User / 入口 │
│ gateway / channels / scheduler │
└───────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ app(宿主 / Host) │
└───────────────┬──────────────┘
│ 调用
▼
┌──────────────────────────────┐
│ harness(引擎 / Engine) │
│ │
│ Agent(create_agent) │
│ │ middleware 层 │
│ ▼ │
│ Run → Worker(租约持有者) │
│ │ │
│ ├── RunStore(持久化) │
│ ├── Checkpoint(回滚点) │
│ ├── Heartbeat(租约续期) │
│ ├── Orphan Recovery │
│ ├── Loop/Budget Guards │
│ ▼ │
│ Sub-Agent(隔离+验收) │
│ ▼ │
│ Sandbox / Skills / Memory │
│ ▼ │
│ Guardrail(护栏) │
└──────────────────────────────┘
如果你能把这张图真正理解,而不是背下来,这次 DeerFlow 2.0 源码赏析就已经达到了目的。
下一次继续赏析项目时,我建议看 Hermes Agent------它和 DeerFlow 正好形成一组对照:
DeerFlow → 长任务 SuperAgent(Runtime / Orchestration / Fault Tolerance)
Hermes → 持久陪伴 + 自我进化 Agent(Memory / Skills / Learning Loop)
一个偏"任务怎么被执行得稳",一个偏"Agent 怎么随用户成长"。两块拼起来,你对 Agent 形态的理解就完整了。