三层包架构与依赖边界
学习目标
- 理解 Langflow 三层包架构的设计动机和每层的职责
- 掌握单向依赖规则:
frontend → langflow → langflow-base → lfx - 学会使用”代码去哪”决策树判断新文件应放在哪一层
- 了解当前 ~14 个已知依赖违规及其成因
前置知识
- Python 包与模块导入机制
- 依赖注入基础概念
项目实践
1. 三层包架构
Langflow 不是单体 Python 包,而是三个独立包 + 一个前端:
每层职责:
| 包 | 职责 | 路径 |
|---|---|---|
| lfx | 组件基类、Graph 执行引擎、CLI(lfx run)、基础原语 | src/lfx/src/lfx/ |
| langflow-base | FastAPI 路由、认证、数据库模型、Alembic 迁移、服务工厂 | src/backend/base/langflow/ |
| langflow | 集成发行包、vendor 组件、CLI(langflow run) | src/backend/langflow/ |
2. 单向依赖规则
langflow → langflow-base → lfx → langchain-core, pydantic, third-party关键约束:
lfx不能导入langflow.*或langflow-base.*langflow-base可以导入lfx.*frontend只能通过 HTTP/WebSocket 与后端通信,不共享文件系统状态
为什么 lfx 不能有向上依赖:lfx 被设计为可独立使用的执行引擎。用户可以在不安装 langflow(平台层)的情况下,仅用 lfx 运行 Flow。如果 lfx 导入了 langflow-base 的数据库服务,就破坏了这种独立性。
3. “代码去哪”决策树
Langflow 的 ARCHITECTURE.md 定义了严格的决策树:
4. 依赖注入模式
当 lfx 需要访问 langflow-base 的功能(如数据库会话)时,使用接口 + 注入模式:
# 在 lfx 中定义接口class SessionProvider(Protocol): def get_session(self) -> Session: ...
# lfx 的组件接受接口class GraphExecutor: def __init__(self, session_provider: SessionProvider): self.session_provider = session_provider
# 在 langflow 启动时注入具体实现from langflow.services.database import session_scopegraph = GraphExecutor(session_provider=session_scope)当前已知违规(~14 个):
src/lfx/src/lfx/components/中有 ~14 处向上导入from langflow.*(auth、db、helpers)- 这些是历史遗留代码,在迁移过程中先放在了 lfx 中但依赖了平台层功能
- 策略:修复时不扩展,逐步向下迁移
5. lfx/base/ vs langflow/base/
两个 base/ 目录并存:
| 目录 | 状态 | 新代码 |
|---|---|---|
src/lfx/src/lfx/base/ | 活跃,新共享原语 | 放这里 |
src/backend/base/langflow/base/ | 遗留,不扩展 | 不添加 |
两者都有 agents/、data/、models/、prompts/ 子目录。新共享原语必须放在 lfx/base/。
问题与规避
| 陷阱 | 现象 | 对策 |
|---|---|---|
| 向上依赖 | lfx 中 from langflow.services.db import ... | 定义接口在 lfx,实现在 langflow 注入 |
放错 langflow/base/ | 新共享原语放在遗留层 | 新代码放 lfx/base/ |
| 服务 vs 工具函数混淆 | 纯函数包装成 Service | 无共享状态和生命周期的函数放 helpers/ 或 utils/ |
| 组件导入平台功能 | 组件中 from langflow.services.* | 组件属于 lfx,不应导入 langflow-base |
设计取舍
三层包 vs 单层单体:
| 维度 | 三层包 | 单层单体 |
|---|---|---|
| 独立性 | lfx 可独立使用 | 全部耦合 |
| 维护成本 | 需要维护包边界 | 简单 |
| 测试隔离 | 可单独测试执行引擎 | 测试依赖全栈 |
| 发布灵活性 | 可独立发布 lfx SDK | 必须全量发布 |
Langflow 选择三层包的核心原因是其十元原则之四:「lfx 是运行时,langflow-base 是平台,langflow 是发行版」。这使得 lfx 可以被其他平台集成,而不需要依赖 Langflow 的全栈。
参考来源
- 架构文档:
docs/agents/ARCHITECTURE.md - 哲学文档:
docs/agents/PHILOSOPHY.md