news 2026/4/24 19:47:55

从单体到分层:DeerFlow 的 Harness/App 架构拆分实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从单体到分层:DeerFlow 的 Harness/App 架构拆分实践

本文基于 DeerFlow 开源项目(bytedance/deer-flow)2026 年 4 月的最新架构重构,分析其后端从backend/src/单体结构拆分为packages/harness/deerflow/(框架层)+app/(应用层)的设计意图、实现方式和工程收益。

背景:一个 AI Agent 后端的成长烦恼

DeerFlow 是字节跳动开源的 LangGraph-based AI Agent 系统,支持多工具调用、沙箱执行、MCP 协议集成、技能系统等能力。在早期版本中,所有后端代码都放在backend/src/下,用from src.*统一引用:

backend/ └── src/ ├── agents/ # Agent 编排、middleware、memory ├── models/ # LLM 模型工厂 ├── tools/ # 工具注册 ├── sandbox/ # 沙箱执行 ├── skills/ # 技能加载 ├── mcp/ # MCP 协议 ├── gateway/ # FastAPI REST API ├── channels/ # IM 渠道集成 └── client.py # 嵌入式 Python SDK

这个结构在项目初期没有问题,但随着功能膨胀,三个痛点逐渐暴露:

痛点一:复用不可能。如果你想写一个 CLI 工具或 Slack Bot 来调用 DeerFlow 的 agent 能力,你必须把整个src/拉进来——包括 FastAPI、uvicorn、lark-oapi(飞书 SDK)、slack-sdk 这些完全不需要的依赖。client.py(DeerFlowClient SDK)的存在就是这个需求的直接体现,但它被困在了一个包含 Web 框架的包里。

痛点二:依赖方向混乱。client.py本应属于框架层,但它反向 import 了src.gateway.routers.skillssrc.gateway.routers.uploads中的函数。框架层依赖了应用层——这是典型的架构腐化信号。

痛点三:LangGraph Server 被迫安装多余依赖。LangGraph Server 只需要跑 agent,但因为src/是一个整体,它必须安装 FastAPI、Slack SDK、飞书 SDK 等完全无关的包。

拆分方案:Harness + App

DeerFlow 团队的解法是把后端拆成两部分:

Harness(线束/框架层)

回答"如何构建和运行 agent"的问题。它是一个可独立发布的 Python 包deerflow-harness),包含:

  • Agent 工厂与生命周期管理
  • Middleware pipeline
  • 工具系统(内置 + MCP + 社区工具)
  • 沙箱执行环境
  • 子 agent 委派
  • 记忆系统(memory、task memory、tool memory)
  • 技能加载与注入
  • 模型工厂
  • 配置系统
  • 嵌入式 Python 客户端(DeerFlowClient)

核心设计原则:对上层应用完全无感知。它不知道也不关心谁在调用它——可以是 Web App、CLI、Slack Bot、或者一个单元测试。

App(应用层)

回答"如何将 agent 呈现给用户"的问题。它不打包、不发布,是项目内部的应用代码:

  • Gateway API(FastAPI REST 接口)
  • IM Channels(飞书、Slack、Telegram、Discord、微信、企业微信)
  • 自定义路由(模型管理、告警分析等)

App 依赖 Harness,但 Harness 绝不依赖 App。

拆分后的目录结构

backend/ ├── packages/ │ └── harness/ │ ├── pyproject.toml # deerflow-harness 包定义 │ └── deerflow/ # import 前缀: deerflow.* │ ├── agents/ # Agent 工厂、middleware、memory │ │ ├── lead_agent/ │ │ ├── middlewares/ # 20+ 中间件 │ │ ├── memory/ # 记忆系统 │ │ └── checkpointer/ │ ├── models/ # LLM 模型工厂 │ ├── tools/ # 工具注册与发现 │ ├── sandbox/ # 沙箱执行环境 │ ├── skills/ # 技能加载 │ ├── mcp/ # MCP 协议 │ ├── community/ # 社区工具(tavily、exa、jina...) │ ├── config/ # 配置系统 │ ├── runtime/ # StreamBridge、RunManager、Store │ ├── guardrails/ # 工具调用授权 │ ├── tracing/ # Langfuse 追踪 │ └── client.py # DeerFlowClient SDK │ ├── app/ # 不打包(import 前缀: app.*) │ ├── gateway/ │ │ ├── app.py # FastAPI 入口 │ │ ├── deps.py # LangGraph runtime 依赖注入 │ │ └── routers/ # REST API 路由 │ └── channels/ # IM 渠道集成 │ ├── pyproject.toml # uv workspace root ├── langgraph.json └── tests/

工程实现:uv Workspace

这次拆分的技术基础是uv workspace(类似 npm workspaces / Cargo workspaces 的 Python 版本)。

Workspace Root(backend/pyproject.toml)

[project] name = "deer-flow" version = "0.1.0" dependencies = [ "deerflow-harness", # 依赖 harness 包 "fastapi>=0.115.0", # App 层的依赖 "uvicorn[standard]>=0.34.0", "slack-sdk>=3.33.0", "lark-oapi>=1.4.0", # ... 其他 App 层依赖 ] [tool.uv.workspace] members = ["packages/harness"] # 声明 workspace 成员 [tool.uv.sources] deerflow-harness = { workspace = true } # 本地引用

Harness 包(packages/harness/pyproject.toml)

[project] name = "deerflow-harness" version = "0.1.0" description = "DeerFlow agent harness framework" dependencies = [ "langgraph>=1.0.6,<1.0.10", "langchain>=1.2.3", "langchain-openai>=1.1.7", "tavily-python>=0.7.17", "tiktoken>=0.8.0", # ... 纯 agent 框架依赖,没有 FastAPI/IM SDK ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["deerflow"] # wheel 中只包含 deerflow/ 目录

关键点:

  1. deerflow-harness是一个真正的 Python 包,有独立的pyproject.toml,可以被pip install,未来可以发布到 PyPI。
  2. App 层故意不打包。它没有pyproject.toml,通过PYTHONPATH=.让 Python 找到app.*即可。因为 App 的唯一消费者是 DeerFlow 项目自身,没有独立发布需求。
  3. 依赖分离是真实的。Harness 的依赖列表里没有 FastAPI、uvicorn、Slack SDK;App 的依赖列表里没有 tavily-python、tiktoken。

Import 规则

两层使用不同的 import 前缀,职责边界一目了然:

# Harness 内部互相引用fromdeerflow.agentsimportmake_lead_agentfromdeerflow.modelsimportcreate_chat_modelfromdeerflow.configimportget_app_config# App 内部互相引用fromapp.gateway.appimportappfromapp.channels.serviceimportstart_channel_service# App 调用 Harness(单向依赖)fromdeerflow.agentsimportmake_lead_agentfromdeerflow.skillsimportload_skills# ❌ 禁止方向:Harness 绝不能 import App# from app.gateway.routers.skills import xxx ← 这在拆分前存在,现在被消除了

LangGraph Server 配置

{"graphs":{"lead_agent":"deerflow.agents:make_lead_agent"},"checkpointer":{"path":"./packages/harness/deerflow/agents/checkpointer/async_provider.py:make_checkpointer"}}

LangGraph Server 只需要 harness 包,不需要加载 App 层代码。

拆分前的"手术":解除反向依赖

在物理拆分之前,需要先解决client.py中两处从框架层到应用层的反向依赖:

问题 1client.pyimport 了src.gateway.routers.skills._validate_skill_frontmatter

解决:提取到deerflow/skills/validation.py。这是一个纯逻辑函数(解析 YAML frontmatter、校验字段),与 FastAPI 无关。

问题 2client.pyimport 了src.gateway.routers.uploads.CONVERTIBLE_EXTENSIONSconvert_file_to_markdown

解决:提取到deerflow/uploads/manager.py。仅依赖markitdown+pathlib,是通用工具函数。

这两步"手术"是拆分的前置条件——不解除反向依赖,物理拆分就无法完成。

实际收益

1. DeerFlowClient SDK 成为一等公民

fromdeerflow.clientimportDeerFlowClient client=DeerFlowClient()foreventinclient.stream("帮我分析一下这篇论文"):print(event)

任何 Python 项目只需pip install deerflow-harness,就能嵌入式使用 DeerFlow 的全部 agent 能力——不需要启动 Web 服务器,不需要安装 FastAPI。

2. 部署拓扑简化

Gateway 内置了 LangGraph Runtime(StreamBridge、RunManager、checkpointer、store),不再需要外部的langgraph-cli进程。部署从三进程(nginx + langgraph-cli + gateway)简化为两进程(nginx + gateway)。这在下一篇文章中详细展开。

3. 依赖体积缩减

LangGraph Server 运行时只安装 harness 的依赖,不再被 FastAPI、IM SDK 污染。对于容器化部署,这意味着更小的镜像体积和更快的启动速度。

4. 下游定制更友好

如果你 fork 了 DeerFlow 做定制(比如加多用户隔离、加自定义路由),你的定制代码自然地分布在两层:

  • 框架级定制(如 db 模块、通知系统)放在 harness 层
  • 产品级定制(如管理后台路由、告警 API)放在 app 层

未来上游更新 harness 层时,app 层的路由基本不会被冲突影响。

5. 为插件化铺路

不同的 app(Web、CLI、Bot)可以各自独立,都依赖同一个 harness。如果 harness 继续增长,还可以进一步拆出deerflow-sandboxdeerflow-mcp等子包。

迁移实操要点

如果你也在维护一个类似的 AI Agent 后端,想做类似的拆分,以下是几个关键注意事项:

  1. 先解除反向依赖,再做物理拆分。用 grep 扫描所有从框架层到应用层的 import,逐个提取到框架层。
  2. config.yaml中的use字段也要更新。DeerFlow 的工具、沙箱、模型都通过use: deerflow.sandbox.local:LocalSandboxProvider这样的字符串动态加载,路径从src.*改为deerflow.*
  3. 测试中的sys.path问题。用 editable install(uv sync)确保deerflow可导入,conftest.py中添加app/sys.path
  4. 全局 rename 要精确。正则匹配\bsrc\.而不是简单替换src,避免误伤字符串中的src

总结

DeerFlow 的 Harness/App 拆分不是为了拆而拆,而是解决了一个 AI Agent 项目在成长过程中必然遇到的问题:agent 能力如何从 Web 应用中解耦出来,成为可独立复用的基础设施。

uv workspace 提供了 Python 生态中相对成熟的 monorepo 方案,配合 hatchling 的 wheel 构建,让"框架包 + 应用代码"的分层在工程上可落地。这个模式值得所有正在构建 AI Agent 平台的团队参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 19:46:52

League Akari:基于LCU API的英雄联盟智能助手终极指南

League Akari&#xff1a;基于LCU API的英雄联盟智能助手终极指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在英雄联盟的激烈对局中&…

作者头像 李华
网站建设 2026/4/24 19:46:47

别再手动切片了!用Python+SimpleITK搞定工业CT数据三维重建(附完整代码)

工业CT数据三维重建实战&#xff1a;从DICOM到可视化全流程自动化 工业CT扫描技术正在重塑现代质量检测与材料分析的效率边界。当传统商业软件遇到批量处理需求时&#xff0c;工程师们往往陷入重复点击的泥潭——导入数据、调整参数、等待渲染、导出结果&#xff0c;这套流程在…

作者头像 李华
网站建设 2026/4/24 19:43:30

内存对比工具V2.6版

内存对比工具V2.6版介绍 这款内存对比工具V2.6版的主要作用是&#xff1a;对指定进程的内存进行两次快照对比&#xff0c;精准找出内存数据发生变化的地址和内容&#xff0c;常用于逆向分析、游戏数据查找、程序调试等场景。核心功能&#xff1a;对指定进程的内存进行快照对比&…

作者头像 李华
网站建设 2026/4/24 19:42:59

你的迷你主机也能炼丹!蝰蛇峡谷+Intel Arc显卡TensorFlow图像分类实战记录

迷你主机变身AI工作站&#xff1a;Intel Arc显卡实战图像分类模型训练 当大多数人还在用笨重的台式机或昂贵的服务器进行深度学习训练时&#xff0c;一群极客已经将目光投向了那些被低估的迷你主机。我最近尝试在Intel蝰蛇峡谷NUC上搭建了一个完整的TensorFlow训练环境&#xf…

作者头像 李华