第二章:项目全景 -- Monorepo 结构与构建系统
"在你理解一个系统的局部之前,先理解它的全貌。"
2.1 什么是 Monorepo?
如果你用过 Maven 的多模块项目,那你就已经理解了 Monorepo 的核心思想:多个相关的包/模块放在同一个仓库中,共享构建工具和依赖管理。
Pi 的 Monorepo 使用 npm workspaces(等价于 Maven 的 <modules>),根 package.json 声明所有子包:
{
"workspaces": [
"packages/*",
"packages/web-ui/example",
"packages/coding-agent/examples/extensions/*"
]
}⚡ Java 对照 ─────────────────────────────────────
| npm/Maven | Java 等价 |
|---|---|
package.json | pom.xml |
workspaces | <modules> |
npm install | mvn install |
npm run build | mvn compile |
npm run test | mvn test |
dependencies | <dependencies> |
devDependencies | <scope>test</scope> 的依赖 |
─────────────────────────────────────────────────
2.2 七大包:洋葱架构
Pi 的 7 个包按依赖关系形成了清晰的分层:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ packages/tui 终端 UI 框架(独立,不依赖其他 pi 包) │
│ packages/ai 统一 LLM API(独立基础层) │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ packages/agent Agent 核心运行时(依赖 ai) │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ packages/coding-agent 主产品(依赖 agent + ai + tui) │
│ packages/web-ui Web 界面(依赖 ai + tui) │
│ packages/mom Slack Bot(依赖 coding-agent) │
│ packages/pods GPU Pod 管理(依赖 agent) │
│ │
└─────────────────────────────────────────────────────────────────────┘★ Insight ─────────────────────────────────────依赖是单向的,永远不循环。 agent 依赖 ai,coding-agent 依赖 agent。你不会看到 ai 反过来依赖 agent。这和 Java 的分层架构原则完全一致:DAO → Service → Controller。每一层只知道自己下面的那一层。 ─────────────────────────────────────────────────
2.3 包详解
@mariozechner/pi-ai(packages/ai)-- 基石
职责:提供统一的 LLM 调用接口,屏蔽 25+ 个 AI 提供商的差异。
Java 类比:这是一个 DAO 层,类似于你用 JPA 屏蔽不同数据库的差异。
packages/ai/
├── src/
│ ├── types.ts # 核心类型:Model, Message, Context, Tool
│ ├── api-registry.ts # 全局 Provider 注册表(ServiceLoader 模式)
│ ├── stream.ts # streamSimple() 入口函数
│ ├── providers/ # 18 个 Provider 实现
│ │ ├── anthropic.ts # Anthropic Claude
│ │ ├── openai.ts # OpenAI GPT
│ │ ├── google.ts # Google Gemini
│ │ └── ...
│ └── models.generated.ts # 自动生成的模型目录(~400KB)关键接口:
// 你只需要知道这一个函数
function streamSimple(
model: Model<any>,
context: Context, // { systemPrompt, messages, tools }
options?: SimpleStreamOptions
): AssistantMessageEventStream;★ Insight ─────────────────────────────────────整个 AI 层的设计哲学是"一个函数搞定一切"。 你不需要知道底层是 Anthropic、OpenAI 还是 Google——你只需要调用 streamSimple(model, context, options)。这和 Java 中用 Repository.findById(id) 而不用关心底层是 MySQL 还是 PostgreSQL 是同一个道理。 ─────────────────────────────────────────────────
@mariozechner/pi-agent-core(packages/agent)-- 心脏
职责:Agent 运行时——状态管理、事件循环、工具执行。
Java 类比:这是一个 Service 层,类似于 Spring 的核心容器。
packages/agent/
├── src/
│ ├── agent.ts # Agent 类(543 行)-- 状态持有者
│ ├── agent-loop.ts # Agent Loop(684 行)-- 核心循环
│ ├── types.ts # 类型定义(366 行)-- 接口契约
│ ├── proxy.ts # 代理流(用于远程 LLM 调用)
│ └── index.ts # 导出只有 5 个文件,总共约 1600 行代码。这是整个项目最精炼的部分。
@mariozechner/pi-coding-agent(packages/coding-agent)-- 主产品
职责:完整的 Coding Agent CLI,包含工具、会话管理、扩展系统、TUI。
Java 类比:这是 Controller 层 + 所有业务逻辑。
packages/coding-agent/
├── src/
│ ├── cli.ts # CLI 入口(main 方法)
│ ├── main.ts # 主编排逻辑
│ ├── config.ts # 配置管理
│ ├── core/
│ │ ├── agent-session.ts # 核心会话类(103KB!)
│ │ ├── session-manager.ts # 会话持久化
│ │ ├── settings-manager.ts # 设置管理
│ │ ├── model-registry.ts # 模型注册
│ │ ├── system-prompt.ts # 系统提示词构建
│ │ ├── skills.ts # 技能系统
│ │ ├── tools/ # 7 个内置工具
│ │ ├── extensions/ # 扩展系统
│ │ └── compaction/ # 上下文压缩
│ └── modes/
│ ├── interactive/ # TUI 交互模式
│ ├── rpc/ # JSON-RPC 模式
│ └── print-mode.ts # 非交互打印模式2.4 构建系统
Pi 使用 tsgo(TypeScript Go 编译器)构建,比标准 tsc 快 10 倍以上。
📌 源码定位 ───────────────────────────────────── 根目录 package.json 中的构建脚本:
{
"scripts": {
"build": "npm run build --workspaces",
"check": "npm run check --workspaces",
"test": "npm run test --workspaces"
}
}─────────────────────────────────────────────────
每个子包有自己的构建配置(tsconfig.json),但共享根目录的 lint/format 规则(biome)。
⚡ Java 对照 ─────────────────────────────────────
| Pi 工具 | Java 等价 | 用途 |
|---|---|---|
tsgo | javac | TypeScript 编译器 |
biome | checkstyle + spotless | 代码格式化和 lint |
vitest | JUnit 5 | 测试框架 |
husky | pre-commit | Git 钩子 |
npm workspaces | Maven <modules> | 多模块管理 |
─────────────────────────────────────────────────
2.5 代码风格约定
Pi 项目有严格的代码规范,写在 AGENTS.md 中:
| 规则 | 说明 |
|---|---|
不用 any | 类型必须明确(等价于 Java 的 -Xlint:all) |
| 不用内联 import | import 必须在文件顶部 |
| Tab 缩进 | 不是空格 |
| 120 字符行宽 | 比 Java 的 120 一致 |
const 优先 | 默认 const,需要时才用 let,永远不用 var |
★ Insight ─────────────────────────────────────const vs let vs var 的区别:const 是不可变引用(类似 Java 的 final),let 是可变引用(类似普通变量),var 是旧语法(函数作用域,容易出 bug)。Pi 项目全部使用 const,只在确实需要重新赋值时用 let。这和 Java 的 final 优先原则一致。 ─────────────────────────────────────────────────
2.6 测试策略
Pi 使用 vitest 作为测试框架,测试文件和源码放在一起:
src/
├── core/
│ ├── agent-session.ts
│ └── agent-session.test.ts # 测试文件⚡ Java 对照 ─────────────────────────────────────
| vitest | JUnit 5 |
|---|---|
describe("Agent", () => { | @Nested class AgentTest { |
it("should emit events", () => { | @Test void shouldEmitEvents() { |
expect(result).toBe(expected) | assertEquals(expected, result) |
beforeEach(() => { | @BeforeEach void setUp() { |
vi.fn() | mock() / @Mock |
─────────────────────────────────────────────────
2.7 本章小结
pi-mono/
├── packages/ai/ ← Layer 1: LLM 统一接口(DAO 层)
├── packages/agent/ ← Layer 2: Agent 运行时(Service 层)
├── packages/coding-agent/ ← Layer 3: 完整产品(Controller 层)
│ ├── core/tools/ ← 7 个内置工具
│ ├── core/extensions/ ← 扩展系统
│ ├── core/compaction/ ← 上下文压缩
│ └── modes/ ← 4 种运行模式
├── packages/tui/ ← 终端 UI 框架
├── packages/web-ui/ ← Web 界面
├── packages/mom/ ← Slack Bot
└── packages/pods/ ← GPU Pod 管理你现在对项目有了全景视角。接下来我们从最底层开始,逐层深入。