1. 项目概述:DeerFlowd 的设计哲学
当我第一次打开 DeerFlowd 的 GitHub 仓库时,README 里简短的描述让我误以为这只是一个基于 LangGraph 的 Agent UI 框架。但随着代码阅读的深入,我逐渐意识到这个项目的野心远不止于此——它实际上构建了一套完整的 Super Agent Harness 系统。
与市面上大多数 Agent 项目不同,DeerFlowd 的核心价值不在于实现某个炫酷的 AI 功能,而是解决 Agent 在真实生产环境中面临的一系列工程挑战:
- 上下文管理:如何处理不断膨胀的对话历史?如何在不丢失关键信息的前提下进行摘要压缩?
- 记忆系统:被摘要掉的旧消息如何转化为长期记忆?短期记忆和长期记忆如何协同工作?
- 子代理协作:如何实现子代理的并发执行?如何传递中间状态?如何优雅地处理取消操作?
- 容错机制:当工具调用失败时,如何保证系统继续运行而非直接崩溃?
- 安全沙箱:如何设计文件系统和执行环境,在赋予 Agent 行动能力的同时确保系统安全?
这些问题的解决方案共同构成了 DeerFlowd 的核心竞争力。它不是又一个"玩具级"的 Agent 实现,而是一套已经具备生产级质量的 Agent 工程框架。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 架构解析:分层设计与模块划分
2.1 整体目录结构
让我们先看项目的整体结构:
code复制deer-flow-main/
├─ frontend/ # Next.js 16 + React 19 前端
├─ backend/
│ ├─ app/ # FastAPI Gateway 与 IM 渠道接入
│ └─ packages/harness/ # 核心 Agent 运行时
├─ skills/ # 公共技能与自定义技能
├─ docs/ # 文档
├─ docker/ # 容器化配置
└─ scripts/ # 辅助脚本
这个结构最值得注意的特点是后端的明确分层。backend/app/ 作为应用层,负责暴露 API、管理文件上传、线程清理等业务逻辑;而 backend/packages/harness/ 则是框架层,专注于 Agent、Sandbox、Subagent 等核心运行时组件的实现。
2.2 部署视角的架构
从部署角度看,系统各组件的关系如下:
code复制Browser/Workspace UI
↓
Nginx (:2026)
↓
Next.js (:3000)
↓
FastAPI Gateway (:8001)
↓
LangGraph Runtime (:2024)
↓
config.yaml / extensions_config.json
↓
deerflow-harness
├─ Model Factory
├─ Skills System
├─ Memory Queue/Storage
├─ Sandbox Provider
└─ Subagent Executor
这种分层设计与现代分布式系统的常见模式高度一致:
- 前端专注于工作区交互体验
- Gateway 统一管理各类 API
- LangGraph Runtime 处理线程和运行状态
- Harness 实现真正的 Agent 执行逻辑
3. 前端设计:超越聊天框的工作区
3.1 技术栈选择
DeerFlowd 前端基于:
- Next.js 16
- React 19
- App Router
- TanStack Query
- @langchain/langgraph-sdk
特别值得注意的是,前端没有尝试自行实现 Agent 状态机,而是围绕 LangGraph SDK 构建线程流、状态恢复和事件消费机制。这种设计清晰地划分了职责边界:前端负责工作区体验,运行时负责执行语义。
3.2 useThreadStream:前后端的关键纽带
useThreadStream 的实现(位于 frontend/src/core/threads/hooks.ts)完美诠释了这一设计哲学:
typescript复制const thread = useStream<AgentThreadState>({
client: getAPIClient(isMock),
assistantId: "lead_agent",
threadId: onStreamThreadId,
onCreated(meta) {
handleStreamStart(meta.thread_id);
setOnStreamThreadId(meta.thread_id);
},
onLangChainEvent(event) {
if (event.event === "on_tool_end") {
listeners.current.onToolEnd?.({
name: event.name,
data: event.data,
});
}
},
onCustomEvent(event: unknown) {
if (event.type === "task_running") {
updateSubtask({
id: event.task_id,
latestMessage: event.message
});
}
}
});
这段代码展示了前端如何:
- 接收后端生成的 thread id
- 将工具事件和子任务事件转换为 UI 状态
- 订阅而非发明运行时状态
3.3 消息发送机制
消息发送的实现同样体现了这一理念:
typescript复制const sendMessage = useCallback(async (threadId, message) =>
