跳转到内容

Monorepo 架构与多包依赖管理

Monorepo 架构与多包依赖管理

学习目标

本章要解决什么问题:

  • LangGraph 为何选择 monorepo 结构
  • 各包之间的依赖关系与边界划分
  • checkpoint-conformance 包如何实现一致性测试
  • 独立版本管理带来的发布策略

项目实践

libs/ 目录结构

LangGraph 的 libs/ 目录下包含多个独立的 Python 和 JavaScript 包,每个包有自己的 pyproject.toml、源码和测试:

libs/
├── checkpoint/ # 基础接口:BaseCheckpointSaver
├── checkpoint-sqlite/ # SQLite 实现
├── checkpoint-postgres/ # PostgreSQL 实现
├── checkpoint-conformance/ # 一致性测试框架
├── langgraph/ # 核心框架:StateGraph、Pregel、Channel
├── prebuilt/ # 高层封装:create_react_agent、ToolNode
├── cli/ # 命令行工具与本地服务器
├── sdk-py/ # LangGraph Server REST API 客户端
└── sdk-js/ # JS/TS 客户端

依赖关系图

为什么拆分 checkpoint 为独立包?

隔离接口与实现checkpoint 包定义了 BaseCheckpointSaver 接口,而 checkpoint-sqlitecheckpoint-postgres 是具体实现。这种拆分允许:

  1. 独立演进:checkpoint 接口稳定后很少变化,但后端实现可能频繁更新(如新增 Redis 后端)
  2. 减少依赖:只使用内存后端的项目不需要安装 SQLite 或 PostgreSQL 驱动
  3. 第三方扩展:任何人都可以遵循接口规范创建自定义后端,无需 fork 核心框架

一致性测试框架

checkpoint-conformance 是一个特殊的包:它不发布到 PyPI,而是定义了一组测试用例,验证各 checkpoint 后端是否符合接口规范。

checkpoint-conformance/
└── tests/
└── conftest.py # 定义 save_test、get_test、list_test 等

每个后端包(checkpoint-sqlitecheckpoint-postgres)继承这些测试:

# checkpoint-sqlite 的测试文件
from checkpoint_conformance import save_test, get_test
def test_sqlite_save(save_test):
save_test.run(SqliteSaver(conn))

这种设计确保所有后端实现的行为一致。

Makefile 构建流程

根目录的 Makefile 定义了跨包操作:

  • make format:运行所有包的代码格式化
  • make lint:运行所有包的 linter
  • make test:运行所有包的测试

每个包也可以独立运行:

Terminal window
cd libs/langgraph && make test
cd libs/checkpoint && make format

设计取舍

优势

  • 关注点分离:每个包职责单一,接口明确
  • 独立发布:各包可以独立版本号,按需发布
  • 减少依赖:用户只安装需要的包

代价

  • 版本管理复杂:各包版本需要协调,不匹配会导致运行时错误
  • CI/CD 复杂:需要确保所有包的测试和构建都通过
  • 开发者体验:跨包修改需要同时处理多个 pyproject.toml

替代方案

  • 单包结构:所有代码在一个包中,简单但缺乏隔离
  • Workspace 工具:使用 uv workspace 或 pdm 管理多包,自动处理依赖

陷阱与对策

跨包版本不匹配

问题langgraph 依赖 checkpoint>=0.2.0,但用户安装了 checkpoint==0.1.0

规避:在 pyproject.toml 中明确声明最低版本依赖。

参考来源

  • 源码验证: libs/*/pyproject.toml — 各包依赖定义
  • 源码验证: 根目录 Makefile — 构建流程
  • 源码验证: libs/checkpoint-conformance/ — 一致性测试