# 第一章：pi-ai 包概述 -- 为什么需要统一 LLM API

## 面临的问题

假设你要做一个 AI 编码助手。你的用户可能使用不同的 LLM 服务商：

- 有人用 **OpenAI** 的 GPT 系列
- 有人用 **Anthropic** 的 Claude 系列
- 有人用 **Google** 的 Gemini 系列
- 有人用 **xAI**、**Groq**、**Mistral** 等等

问题是，每个服务商的 API 都不一样：

- **请求格式不同**：OpenAI 用 `messages` 数组里放 `role: "user"` 和 `role: "assistant"`；Anthropic 的消息结构不同；Google 用完全不同的 protobuf 格式
- **流式响应格式不同**：OpenAI 返回 SSE（Server-Sent Events）格式的 JSON 块；Anthropic 有自己的 event 类型
- **功能差异**：有的支持"思考/推理"功能（thinking），有的不支持；有的支持图片输入，有的不支持
- **认证方式不同**：有的用 API Key，有的用 OAuth（类似微信扫码登录），有的用 AWS 的复杂签名

如果你的应用直接对接每个服务商，代码会变成一团乱麻。每增加一个新服务商，你就要在无数地方加 `if-else`。

## pi-ai 的解决方案：适配器模式（Adapter Pattern）

pi-ai 包的核心思路很简单：

> **定义一套统一的类型和接口，然后为每个服务商写一个"适配器"（adapter），把各家不同的 API 翻译成统一格式。**

这就像一个万能充电头：无论你的插座是美标、欧标还是英标，插上适配器后，你的设备用同一根线就能充电。

```
你的应用代码
    ↓  （只用统一类型）
┌─────────────────────────┐
│       pi-ai 统一层       │
│  Model, Context, stream()│
└─────────┬───────────────┘
          │ （适配器翻译）
    ┌─────┼─────┬─────┬──────┐
    ▼     ▼     ▼     ▼      ▼
 OpenAI  Claude Google xAI  Mistral ...
```

## 这个包解决了哪些具体问题

| 问题 | pi-ai 的解决方案 |
|------|------------------|
| 不同 API 请求格式 | 统一的 `Context` 类型（systemPrompt + messages + tools） |
| 不同的响应格式 | 统一的 `AssistantMessage` 类型 |
| 流式响应差异 | 统一的 `AssistantMessageEvent` 事件流协议 |
| 模型元数据不统一 | 统一的 `Model` 类型，自动生成的模型注册表 |
| 工具调用差异 | 统一的 `Tool` 定义 + `ToolCall` / `ToolResultMessage` |
| 费用计算 | 每个模型携带定价信息，自动计算 |
| 跨模型切换 | 同一段上下文可以在不同 provider 之间无缝传递 |

## 包的目录结构

```
packages/ai/src/
├── types.ts                     # 核心类型定义（本章重点）
├── stream.ts                    # 四个顶层调用入口
├── api-registry.ts              # API 注册表（路由到具体 provider）
├── models.ts                    # 模型查询函数
├── models.generated.ts          # 自动生成的模型数据库（384KB）
├── env-api-keys.ts              # 环境变量中的 API Key 检测
├── index.ts                     # 包的公开导出
├── providers/                   # 各服务商的适配器实现
│   ├── register-builtins.ts     # 注册所有内置适配器
│   ├── transform-messages.ts    # 通用消息转换
│   ├── simple-options.ts        # 简化选项映射
│   ├── anthropic.ts             # Anthropic 适配器
│   ├── openai-completions.ts    # OpenAI Chat Completions 适配器
│   ├── openai-responses.ts      # OpenAI Responses 适配器
│   ├── google.ts                # Google Gemini 适配器
│   ├── mistral.ts               # Mistral 适配器
│   ├── amazon-bedrock.ts        # AWS Bedrock 适配器
│   ├── faux.ts                  # 测试用假 provider
│   └── ...
└── utils/                       # 工具函数
    ├── event-stream.ts          # EventStream 异步队列
    ├── validation.ts            # 工具参数验证
    ├── json-parse.ts            # 增量 JSON 解析
    └── oauth/                   # OAuth 认证工具
```

## 核心设计原则

1. **调用者无需关心 provider 差异**：你写 `stream(model, context)` 就够了，具体走哪个 API 由 `model.api` 字段自动决定

2. **错误不抛异常，而是编码在事件流中**：这一点很重要。传统做法是出错就 `throw Error`，但在流式场景下，你可能已经收到了一部分响应，直接抛异常会丢失这些数据。pi-ai 的做法是通过 `error` 事件通知你出错了，同时保留已收到的部分内容

3. **懒加载 provider**：并非启动时就加载所有 20+ 个 provider 的代码。而是当你实际使用某个 provider 时才加载它的实现模块，节省启动时间和内存

4. **模型数据自动生成**：通过脚本从各服务商的 API 拉取最新模型列表，生成 `models.generated.ts` 文件。每次发版时更新，保持模型信息最新

## 下一步

下一章我们会详细拆解核心类型系统 -- 这是理解整个项目的基础。
