跳转到内容

Monorepo 架构:pnpm Workspaces 的包组织

学习目标

理解 LobeHub 如何使用 pnpm workspaces 管理 70+ 包的 monorepo 架构,包括包分类、workspace 协议、apps 与 packages 的职责分离。

前置知识

本章无通用知识前置,直接聚焦 LobeHub 的工程实践。

项目实践

包分类与职责分离

LobeHub 的 monorepo 按功能域分为 5 类:

分类路径职责包数
Agent 运行时packages/agent-runtimeAgent 决策循环、图驱动编排1
模型与上下文packages/model-runtimecontext-engine多模型路由、上下文处理3+
工具与技能packages/builtin-tool-*builtin-skillsbuiltin-tools27+ 内置工具和 4 内置技能30+
IM 网关packages/chat-adapter-*微信、飞书、Line、QQ、钉钉5
基础设施packages/databaseutilstypesconfig数据库、类型定义、工具函数15+
异构集成packages/heterogeneous-agentsClaude Code/Codex 等外部 Agent 封装1
记忆系统packages/memory-user-memory5 层分层记忆提取1

Workspace 协议

包间依赖使用 workspace:* 协议:

// 伪代码:包依赖
{
"dependencies": {
"@lobechat/agent-runtime": "workspace:*",
"@lobechat/model-runtime": "workspace:*",
"@lobechat/database": "workspace:*",
// ...
}
}

workspace:* 的优势:

  • 始终使用本地源码版本,不依赖 npm 发布
  • pnpm 自动解析 workspace 内的符号链接
  • 避免版本不一致导致的类型错误

Apps 目录

App路径说明
Desktopapps/desktop/Electron 桌面应用
CLIapps/cli/命令行工具
Device Gatewayapps/device-gateway/设备网关服务

包命名约定

LobeHub 使用两个 npm scope:

  • @lobechat/*:内部包,不发布到 npm
  • @lobehub/*:公共包,发布到 npm(如 @lobehub/ui@lobehub/icons

循环依赖检测

LobeHub 使用 dpdm 工具检测循环依赖:

Terminal window
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 也引用包 Alint: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)需要独立版本管理和发布流程。

参考来源