Skip to content

第二章:项目全景 -- Monorepo 结构与构建系统

"在你理解一个系统的局部之前,先理解它的全貌。"


2.1 什么是 Monorepo?

如果你用过 Maven 的多模块项目,那你就已经理解了 Monorepo 的核心思想:多个相关的包/模块放在同一个仓库中,共享构建工具和依赖管理

Pi 的 Monorepo 使用 npm workspaces(等价于 Maven 的 <modules>),根 package.json 声明所有子包:

json
{
    "workspaces": [
        "packages/*",
        "packages/web-ui/example",
        "packages/coding-agent/examples/extensions/*"
    ]
}

⚡ Java 对照 ─────────────────────────────────────

npm/MavenJava 等价
package.jsonpom.xml
workspaces<modules>
npm installmvn install
npm run buildmvn compile
npm run testmvn 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 依赖 aicoding-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)

关键接口

typescript
// 你只需要知道这一个函数
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 中的构建脚本:

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 等价用途
tsgojavacTypeScript 编译器
biomecheckstyle + spotless代码格式化和 lint
vitestJUnit 5测试框架
huskypre-commitGit 钩子
npm workspacesMaven <modules>多模块管理

─────────────────────────────────────────────────

2.5 代码风格约定

Pi 项目有严格的代码规范,写在 AGENTS.md 中:

规则说明
不用 any类型必须明确(等价于 Java 的 -Xlint:all
不用内联 importimport 必须在文件顶部
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 对照 ─────────────────────────────────────

vitestJUnit 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 管理

你现在对项目有了全景视角。接下来我们从最底层开始,逐层深入。


第一章:语言桥接 | 第三章:AI 层 -- 统一的 LLM 调用抽象 →

基于 MIT 许可证发布