跳转到内容

三层包架构与依赖边界

学习目标

  • 理解 Langflow 三层包架构的设计动机和每层的职责
  • 掌握单向依赖规则:frontend → langflow → langflow-base → lfx
  • 学会使用”代码去哪”决策树判断新文件应放在哪一层
  • 了解当前 ~14 个已知依赖违规及其成因

前置知识

  • Python 包与模块导入机制
  • 依赖注入基础概念

项目实践

1. 三层包架构

Langflow 不是单体 Python 包,而是三个独立包 + 一个前端

每层职责

职责路径
lfx组件基类、Graph 执行引擎、CLI(lfx run)、基础原语src/lfx/src/lfx/
langflow-baseFastAPI 路由、认证、数据库模型、Alembic 迁移、服务工厂src/backend/base/langflow/
langflow集成发行包、vendor 组件、CLI(langflow runsrc/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_scope
graph = 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