Monorepo 架构:pnpm Workspaces 的包组织
学习目标
理解 LobeHub 如何使用 pnpm workspaces 管理 70+ 包的 monorepo 架构,包括包分类、workspace 协议、apps 与 packages 的职责分离。
前置知识
本章无通用知识前置,直接聚焦 LobeHub 的工程实践。
项目实践
包分类与职责分离
LobeHub 的 monorepo 按功能域分为 5 类:
| 分类 | 路径 | 职责 | 包数 |
|---|---|---|---|
| Agent 运行时 | packages/agent-runtime | Agent 决策循环、图驱动编排 | 1 |
| 模型与上下文 | packages/model-runtime、context-engine | 多模型路由、上下文处理 | 3+ |
| 工具与技能 | packages/builtin-tool-*、builtin-skills、builtin-tools | 27+ 内置工具和 4 内置技能 | 30+ |
| IM 网关 | packages/chat-adapter-* | 微信、飞书、Line、QQ、钉钉 | 5 |
| 基础设施 | packages/database、utils、types、config | 数据库、类型定义、工具函数 | 15+ |
| 异构集成 | packages/heterogeneous-agents | Claude Code/Codex 等外部 Agent 封装 | 1 |
| 记忆系统 | packages/memory-user-memory | 5 层分层记忆提取 | 1 |
Workspace 协议
包间依赖使用 workspace:* 协议:
// 伪代码:包依赖{ "dependencies": { "@lobechat/agent-runtime": "workspace:*", "@lobechat/model-runtime": "workspace:*", "@lobechat/database": "workspace:*", // ... }}workspace:* 的优势:
- 始终使用本地源码版本,不依赖 npm 发布
- pnpm 自动解析 workspace 内的符号链接
- 避免版本不一致导致的类型错误
Apps 目录
| App | 路径 | 说明 |
|---|---|---|
| Desktop | apps/desktop/ | Electron 桌面应用 |
| CLI | apps/cli/ | 命令行工具 |
| Device Gateway | apps/device-gateway/ | 设备网关服务 |
包命名约定
LobeHub 使用两个 npm scope:
@lobechat/*:内部包,不发布到 npm@lobehub/*:公共包,发布到 npm(如@lobehub/ui、@lobehub/icons)
循环依赖检测
LobeHub 使用 dpdm 工具检测循环依赖:
npm run lint:circular:main # 检测 src/ 下的循环依赖npm run lint:circular:packages # 检测 packages/ 下的循环依赖CI 中配置 --exit-code circular:1,循环依赖会导致 CI 失败。
问题与规避
| 问题 | 场景 | 规避策略 |
|---|---|---|
| 依赖安装慢 | 70+ 包的依赖安装需要数分钟 | 使用 pnpm 而非 npm/yarn,共享 node_modules |
| 类型不同步 | 包间共享类型定义修改后未同步 | CI 中 type-check 步骤检测 |
| 循环依赖 | 包 A 引用包 B,包 B 也引用包 A | lint:circular CI 检查,重构依赖方向 |
| 本地开发不一致 | workspace 依赖未重新安装 | 修改 workspace 包后运行 pnpm install |
设计取舍
为什么用 pnpm 而非 Turborepo/Nx?
LobeHub 选择 pnpm workspaces 而非 Turborepo/Nx 等更复杂的 monorepo 工具。
原因:
- pnpm 的硬链接和符号链接天然节省磁盘空间
- 配置简单,不需要额外的配置文件
- 与 Next.js、Vite 等工具兼容性好
代价:
- 无内置缓存和远程执行(Turborepo 的优势)
- 大规模并行构建不如 Turborepo 优化
为什么使用双 scope 命名?
@lobechat/* 用于内部包,@lobehub/* 用于公共发布包。
原因:内部包名称区分于公共包,避免意外发布内部代码。公共包(如 @lobehub/ui)需要独立版本管理和发布流程。
参考来源
- LobeHub package.json —
package.jsonworkspaces 和依赖配置 - pnpm Workspaces 文档