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-sqlite 和 checkpoint-postgres 是具体实现。这种拆分允许:
- 独立演进:checkpoint 接口稳定后很少变化,但后端实现可能频繁更新(如新增 Redis 后端)
- 减少依赖:只使用内存后端的项目不需要安装 SQLite 或 PostgreSQL 驱动
- 第三方扩展:任何人都可以遵循接口规范创建自定义后端,无需 fork 核心框架
一致性测试框架
checkpoint-conformance 是一个特殊的包:它不发布到 PyPI,而是定义了一组测试用例,验证各 checkpoint 后端是否符合接口规范。
checkpoint-conformance/└── tests/ └── conftest.py # 定义 save_test、get_test、list_test 等每个后端包(checkpoint-sqlite、checkpoint-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:运行所有包的 lintermake test:运行所有包的测试
每个包也可以独立运行:
cd libs/langgraph && make testcd 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/— 一致性测试