🦌 DeerFlow 架构深度拆解
本文档从源码层面完整拆解 DeerFlow 的智能体编排技术、Agent 编写逻辑、编排机制和设计哲学。 适用读者:想深入理解 DeerFlow 并做二次开发的开发者。
目录
- 总体架构
- Agent 系统
- Lead Agent 机制
- Sub-Agent 编排
- Tool 系统
- Skill 系统
- Sandbox 沙箱
- Memory 记忆系统
- Model 模型接入
- Runtime 运行时
- Gateway API
- 技能开发指南
1. 总体架构
目录结构
deer-flow/
├── backend/
│ ├── app/gateway/ ← FastAPI 网关(HTTP API + LangGraph Runtime)
│ ├── deerflow/ ← 核心 Harness 包
│ │ ├── agents/ ← Agent 定义
│ │ │ └── lead_agent/ ← 主 Agent(编排入口)
│ │ ├── subagents/ ← 子 Agent 系统
│ │ ├── tools/ ← 工具系统
│ │ ├── skills/ ← 技能系统
│ │ ├── sandbox/ ← 沙箱执行环境
│ │ ├── memory/ ← 记忆系统
│ │ ├── models/ ← 模型接入
│ │ ├── runtime/ ← LangGraph 运行时
│ │ ├── persistence/ ← 持久化层
│ │ ├── community/ ← 社区工具(搜索、图片等)
│ │ ├── config/ ← 配置解析
│ │ ├── mcp/ ← MCP 协议支持
│ │ └── uploads/ ← 文件上传
│ └── packages/harness/ ← 可安装的 Harness 包
├── frontend/
│ ├── src/app/ ← Next.js 路由
│ │ ├── workspace/ ← 工作区页面
│ │ └── ...
│ ├── src/components/ ← UI 组件
│ └── src/core/ ← 前端核心逻辑
├── skills/ ← 内置技能
│ └── public/ ← 公开技能包
└── config.yaml ← 主配置文件数据流
用户输入
│
▼
┌──────────────────────┐
│ Gateway (FastAPI) │ ← 接收 HTTP/SSE 请求
│ AuthMiddleware │ ← JWT 认证
│ LangGraph Runtime │ ← LangGraph 驱动
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ Lead Agent │ ← 主 Agent(LangGraph 图)
│ ├── 理解意图 │
│ ├── 选择工具/Skill │
│ └── 编排 Sub-Agent │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ LLM Model │ ← 调用大模型
│ Tools │ ← 执行工具
│ Sandbox │ ← 沙箱环境
│ Memory │ ← 读写记忆
└──────────────────────┘2. Agent 系统
2.1 核心概念
DeerFlow 的 Agent 系统基于 LangGraph 构建。LangGraph 是 LangChain 推出的有状态图框架,允许将 Agent 定义为有向图(DAG),节点是函数/LLM 调用,边是状态转换。
关键词:
| 概念 | 说明 |
|---|---|
| State | 图的状态对象,在图节点之间传递 |
| Node | 图中的节点,每个节点是一个异步函数 |
| Edge | 节点之间的连接,决定执行顺序 |
| Conditional Edge | 条件边,根据状态决定走哪条路径 |
| Checkpoint | 状态快照,支持暂停/恢复 |
2.2 Agent 数据结构
# deerflow/agents/lead_agent/agent.py
# State 定义 — 在图节点之间传递的数据对象
class AgentState(TypedDict):
messages: list[BaseMessage] # 消息历史
context: dict # 上下文(agent_name, thinking_enabled 等)
is_plan_mode: bool # 是否计划模式
deep_research_exhausted: bool # 深度研究是否已用完
training_data: list | None # Agent 训练数据(memory)
memory_updated: bool # 记忆是否已更新2.3 图定义
Lead Agent 是一个 LangGraph StateGraph,包含多个节点:
# 简化的图结构
graph = StateGraph(AgentState)
# 节点注册
graph.add_node("call_model", call_model_node) # 调用 LLM
graph.add_node("call_tool", call_tool_node) # 执行工具
graph.add_node("memory_agent", memory_agent_node) # 更新记忆
# 边定义
graph.add_conditional_edges(
"call_model",
should_continue, # 条件函数:判断是否还有工具调用
{"continue": "call_tool", "end": "__end__"}
)
graph.add_edge("call_tool", "call_model") # 工具执行完后回到 LLM这个图的核心循环:
call_model (LLM 思考)
│
├── 如果 LLM 返回了 tool_calls → call_tool
│ │
│ └── 执行完工具 → 回到 call_model
│
└── 如果 LLM 返回了最终答案 → __end__2.4 Agent 运行时
Agent 通过 LangGraph 的 compile() 编译为可执行图:
# 编译图
agent = graph.compile(
checkpointer=checkpointer, # 状态检查点(支持中断/恢复)
interrupt_before=[], # 在哪些节点前中断
interrupt_after=[], # 在哪些节点后中断
)
# 执行
result = await agent.ainvoke(
input={"messages": [user_message]},
config={"configurable": {"thread_id": thread_id}},
)3. Lead Agent 机制
3.1 总体流程
Lead Agent 是 DeerFlow 的核心编排器。文件位于 agents/lead_agent/agent.py(447 行)+ agents/lead_agent/prompt.py(906 行)。
处理流程:
用户消息
│
▼
1. 构建 System Prompt
├── 加载 config 中的模型配置
├── 注入工具列表(tools)
├── 注入技能(skills)
├── 注入记忆(memory)
├── 注入上下文(agent_name, user_id 等)
└── 注入 SOUL.md(Agent 人格)
│
▼
2. Call Model(调用 LLM)
├── 创建 ChatModel 实例
├── 绑定工具(bind_tools)
├── 设置参数(temperature, max_tokens 等)
└── 调用 LLM(流式或非流式)
│
▼
3. Should Continue(判断下一步)
├── LLM 返回了 tool_calls → 去 call_tool
├── LLM 返回了最终答案 → 结束
└── LLM 返回了空内容 → 去 memory_agent
│
▼
4. Call Tool(执行工具)
├── 解析 tool_call
├── 检查权限/护栏
├── 执行工具函数
└── 将结果放入 messages
│
▼
5. Memory Agent(更新记忆)
├── 从对话中提取关键信息
└── 更新 memory.json3.2 System Prompt 构建
System Prompt 是 Agent 行为的核心。DeerFlow 动态构建 System Prompt:
# prompt.py 中的核心函数
def get_system_prompt(config, user_context, tools, skills) -> str:
parts = []
# 1. 角色定义
parts.append(get_role_prompt())
# 2. 工具列表
parts.append(get_tools_prompt(tools))
# 3. 技能描述
parts.append(get_skills_prompt(skills))
# 4. 记忆上下文
parts.append(get_memory_prompt(user_context))
# 5. SOUL.md(Agent 人格)
parts.append(get_soul_prompt(user_context))
# 6. 规则约束
parts.append(get_rules_prompt())
return "\n\n".join(parts)3.3 关键代码分析
# agents/lead_agent/agent.py — 核心执行函数
async def call_model_node(state: AgentState, config: RunnableConfig):
"""LLM 调用节点 — Agent 的"大脑" """
# 1. 构建消息列表
messages = state["messages"]
# 2. 获取绑定了工具的 LLM
runtime = config["configurable"]["runtime"]
llm = runtime.get_chat_model()
# 3. 注入 Agent 上下文
llm = llm.bind_tools(tools)
# 4. 调用 LLM
response = await llm.ainvoke(messages)
# 5. 返回新的消息
return {"messages": [response]}4. Sub-Agent 编排
4.1 Sub-Agent 机制
DeerFlow 支持 Lead Agent 创建子 Agent 来并行/串行执行任务。这是其”超级 Agent Harness”的核心能力。
# subagents/executor.py
class SubAgentExecutor:
"""子 Agent 执行器"""
async def execute(self, task: SubTask) -> SubTaskResult:
# 1. 为子 Agent 创建独立的 LangGraph 图
# 2. 配置独立的 LLM 和工具集
# 3. 执行任务并收集结果
# 4. 将结果返回给 Lead Agent4.2 Sub-Agent 执行流程
Lead Agent
│
├── 分析复杂任务
├── 拆分为子任务
├── 创建 Sub-Agent(每个一个独立的 LangGraph 实例)
│
├── 模式 1:串行执行
│ SubAgent1 → SubAgent2 → SubAgent3
│
├── 模式 2:并行执行
│ SubAgent1 ──┐
│ SubAgent2 ──┤── 合并结果
│ SubAgent3 ──┘
│
└── 收集子任务结果 → 汇总输出4.3 Sub-Agent 配置
# config.yaml 中的 subagent 配置
subagents:
enabled: true
max_concurrency: 3 # 最大并行数
default_timeout: 300 # 默认超时(秒)5. Tool 系统
5.1 Tool 注册机制
DeerFlow 的 Tool 通过 tools/tools.py 中的 get_available_tools() 函数动态加载:
# tools/tools.py
def get_available_tools(
groups: list[str] | None = None,
model_name: str | None = None,
app_config: AppConfig | None = None,
) -> list[BaseTool]:
"""从 config 中读取 tool 配置,动态加载"""
# 1. 从 config.yaml 读取 tools 列表
config = app_config or load_config()
tool_configs = config.tools
# 2. 过滤 tool group
if groups:
tool_configs = [t for t in tool_configs if t.group in groups]
# 3. 动态加载每个 tool
tools = []
for tc in tool_configs:
# resolve_variable 通过字符串路径加载 Python 对象
tool_class = resolve_variable(tc.use, BaseTool)
tool_instance = tool_class()
tools.append(tool_instance)
return tools5.2 Tool 配置格式
# config.yaml
tools:
- name: web_search
group: web
use: deerflow.community.ddg_search.tools:web_search_tool
max_results: 5
- name: create_plan
group: plan
use: deerflow.plans.tools:create_planuse 字段的格式是 package.module:function_or_class,通过 resolve_variable() 动态解析:
def resolve_variable(use_path: str, expected_type: type) -> Any:
"""解析 'module.submodule:object' 格式的路径"""
module_path, obj_name = use_path.split(":", 1)
module = importlib.import_module(module_path)
return getattr(module, obj_name)5.3 内置工具类型
| 工具 | 用途 | 源码位置 |
|---|---|---|
web_search | DuckDuckGo 搜索 | community/ddg_search/tools.py |
web_fetch | Jina AI 网页抓取 | community/jina_ai/tools.py |
image_search | 图片搜索 | community/image_search/tools.py |
bash | 执行 Shell 命令 | sandbox/tools.py |
read_file | 读取文件 | sandbox/tools.py |
write_file | 写入文件 | sandbox/tools.py |
create_plan | 创建计划 | plans/tools.py |
present_file | 展示文件 | tools/builtins/present_file_tool.py |
6. Skill 系统
6.1 Skill 是什么
Skill 是 DeerFlow 的”技能包”——一个包含完整指令的 .skill ZIP 文件(本质是一个目录),里面包含:
my-skill.skill/
├── SKILL.md ← 技能定义(必选)
├── scripts/ ← 脚本(可选)
├── templates/ ← 模板(可选)
├── references/ ← 参考文件(可选)
└── evals/ ← 评估数据(可选)6.2 SKILL.md 结构
每个 Skill 的核心是 SKILL.md,使用 YAML front-matter + Markdown:
---
name: my-skill
description: 当用户请求 X 时使用此技能。触发词:Y、Z。
---
# My Skill
## Overview
此技能的作用是...
## Workflow
### Step 1: 理解需求
- 识别用户输入
- 确定需要的数据
### Step 2: 执行
- 写代码/查资料/生成内容
### Step 3: 输出
- 格式化结果
- 保存到 /mnt/user-data/outputs/6.3 Skill 加载机制
# skills/parser.py — 解析 SKILL.md
def parse_skill(skill_dir: Path) -> Skill:
"""解析 skill 目录,提取元数据和指令"""
skill_file = skill_dir / "SKILL.md"
with open(skill_file) as f:
content = f.read()
# 解析 YAML front-matter
_, front_matter, body = content.split("---", 2)
meta = yaml.safe_load(front_matter)
return Skill(
name=meta["name"],
description=meta["description"],
instructions=body.strip(),
scripts_dir=skill_dir / "scripts",
templates_dir=skill_dir / "templates",
)6.4 Skill 注入到 System Prompt
Skill 的 description 被注入到 System Prompt 中,LLM 根据匹配度决定是否调用。这是关键的触发机制:
# 在 system prompt 中注入技能描述
skills_section = "## Available Skills\n\n"
for skill in available_skills:
skills_section += f"- {skill.name}: {skill.description}\n"当用户说”分析这个数据”时,LLM 匹配到 data-analysis 的 description,就会按照 SKILL.md 中的步骤执行。
7. Sandbox 沙箱
7.1 Local Sandbox(默认)
# sandbox/local/provider.py
class LocalSandboxProvider:
"""本地沙箱 — 直接在当前进程执行"""
async def execute(self, command: str) -> SandboxResult:
"""在本地执行命令"""
proc = await asyncio.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate()
return SandboxResult(
stdout=stdout.decode(),
stderr=stderr.decode(),
exit_code=proc.returncode,
)7.2 Container Sandbox(Docker)
# community/aio_sandbox/
class AioSandboxProvider:
"""容器沙箱 — 在 Docker 容器中隔离执行"""
async def execute(self, command: str) -> SandboxResult:
"""在隔离容器中执行命令"""
container = await self._get_or_create_container()
result = await container.exec_run(command)
return SandboxResult(
stdout=result.output.decode(),
stderr="",
exit_code=result.exit_code,
)8. Memory 记忆系统
8.1 记忆存储
DeerFlow 使用简单的 JSON 文件存储记忆:
.deer-flow/
├── memory.json ← 全局记忆
└── users/
└── {user_id}/
├── memory.json ← 用户级记忆
└── agents/
└── {agent_name}/
└── memory.json ← Agent 级记忆8.2 记忆读写
# memory/manager.py
class MemoryManager:
"""记忆管理器 — 在对话结束后自动更新"""
async def update_memory(self, messages: list[BaseMessage]):
"""从对话中提取关键信息并更新记忆"""
# 1. 提取关键事实
facts = self._extract_facts(messages)
# 2. 更新用户偏好
preferences = self._extract_preferences(messages)
# 3. 写入 memory.json
memory = self._load_memory()
memory["facts"].extend(facts)
memory["preferences"].update(preferences)
self._save_memory(memory)
def _load_memory(self) -> dict:
with open(self.memory_path) as f:
return json.load(f)9. Model 模型接入
9.1 模型配置
# config.yaml
models:
- name: deepseek-v4-flash
display_name: DeepSeek V4 Flash
use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
model: deepseek-v4-flash
api_key: $DEEPSEEK_API_KEY
supports_thinking: true
when_thinking_enabled:
extra_body:
thinking:
type: enabled9.2 模型加载机制
# models/factory.py
def create_chat_model(model_config: ModelConfig) -> BaseChatModel:
"""根据配置创建 ChatModel 实例"""
# 1. 解析 use 路径
model_class = resolve_variable(model_config.use)
# 2. 构建参数字典
kwargs = {
"model": model_config.model,
"api_key": model_config.api_key,
"timeout": model_config.timeout,
}
# 3. 添加可选参数
if model_config.base_url:
kwargs["base_url"] = model_config.base_url
if model_config.max_tokens:
kwargs["max_tokens"] = model_config.max_tokens
if model_config.temperature:
kwargs["temperature"] = model_config.temperature
# 4. 实例化
return model_class(**kwargs)9.3 Thinking 模式
DeerFlow 对支持思考链的模型(如 DeepSeek R1、Claude)有特殊处理:
# models/patched_deepseek.py
class PatchedChatDeepSeek(ChatDeepSeek):
"""修复多轮对话中 reasoning_content 丢失的问题"""
def _convert_message_to_dict(self, message):
"""确保 reasoning_content 在消息中正确传递"""
d = super()._convert_message_to_dict(message)
if hasattr(message, "reasoning_content"):
d["reasoning_content"] = message.reasoning_content
return d10. Runtime 运行时
10.1 LangGraph Runtime
# runtime/ 目录结构
runtime/
├── __init__.py
├── checkpointer/ ← 状态检查点
├── events/ ← 事件系统
│ └── store/ ← 事件存储
├── runs/ ← 运行管理
│ └── store/ ← 运行记录存储
├── store/ ← 通用存储
└── stream_bridge/ ← SSE 流式传输10.2 流式传输
# runtime/stream_bridge/stream_bridge.py
class StreamBridge:
"""将 LangGraph 的事件流转换为 SSE"""
async def stream_events(self, thread_id: str, input: dict):
"""流式传输 Agent 执行事件"""
async for event in self.agent.astream_events(input):
# 事件类型:
# on_chat_model_start — LLM 开始思考
# on_chat_model_end — LLM 思考完成
# on_tool_start — 工具开始执行
# on_tool_end — 工具执行完成
sse_event = self._convert_to_sse(event)
yield sse_event11. Gateway API
11.1 API 结构
/api/
├── v1/auth/ ← 认证
├── threads/ ← 对话线程 CRUD
├── threads/{id}/runs ← 运行管理
├── threads/{id}/artifacts ← 文件制品
├── threads/{id}/uploads ← 文件上传
├── models ← 模型列表
├── memory ← 记忆管理
├── skills ← 技能管理
├── agents ← 智能体管理
└── plans ← 计划任务11.2 认证中间件
# auth_middleware.py
class AuthMiddleware(BaseHTTPMiddleware):
"""JWT 认证中间件 — 保护所有 /api/* 路由"""
async def dispatch(self, request, call_next):
# 公开路径免认证
if _is_public(request.url.path):
return await call_next(request)
# 检查 JWT Cookie
token = request.cookies.get("access_token")
if not token:
return JSONResponse(status_code=401, ...)
# 验证 JWT
user = await decode_token(token)
request.state.user = user
return await call_next(request)12. 技能开发指南
12.1 如何编写一个 Skill
Skill 是 DeerFlow 的扩展方式。创建一个技能只需三步:
步骤 1:创建目录结构
skills/custom/my-skill/
├── SKILL.md
└── scripts/
└── run.py步骤 2:编写 SKILL.md
---
name: my-skill
description: >
当用户请求执行 X 操作时使用此技能。
触发词:X、Y、Z。
不适用于 A、B 情况。
---
# My Skill
## Overview
此技能的作用是...
## Workflow
### Step 1: 理解需求
- 识别用户输入中的关键参数
### Step 2: 执行
- 使用 bash_tool 运行脚本
### Step 3: 输出
- 结果保存到 /mnt/user-data/outputs/
- 使用 present_file 展示结果步骤 3:注册到 config.yaml
skills:
directories:
- ./skills/public
- ./skills/custom12.2 编写最佳实践
| 原则 | 说明 |
|---|---|
| 精准描述 | description 决定了 Skill 的触发准确率,要包含触发词 |
| 步骤清晰 | Workflow 要足够详细,让 LLM 能按步骤执行 |
| 结果可预测 | 指定输出格式和位置,让系统能正确展示 |
| 最小权限 | 只给 Skill 必要的工具访问权限 |
| 可测试 | 提供 evals/ 评估数据 |
12.3 常用开发模式
模式 1:数据分析型
用户上传文件 → data-analysis 读取 → pandas 处理 → 输出结果模式 2:研究型
用户提问 → deep-research 搜索 → 多角度分析 → 整理报告模式 3:生成型
用户需求 → ppt-generation 规划 → image-generation 配图 → 合成输出核心设计哲学
- 一切皆工具:所有能力都用 Tool 封装,LLM 通过 Tool Calling 选择使用
- Skill 驱动:复杂能力通过 Skill 注入 System Prompt,而非硬编码
- 无状态 API:状态由 LangGraph 管理,API 层保持无状态
- 插件化加载:所有组件(Model、Tool、Sandbox)都通过
use:路径动态加载
config.yaml → 动态解析 → 组件实例化 → 注入 Agent → 执行这种设计让 DeerFlow 具有极高的可扩展性——添加一个新能力不需要改核心代码,只需要:
- 写一个 Python 函数(Tool)
- 在 config.yaml 中添加一行
- 可选:写一个 SKILL.md 让 LLM 知道怎么用
本文档由 DeerFlow 源码分析生成,对应
feat/file-tree-panel分支的最新代码。 核心源码位置:backend/packages/harness/deerflow/