第 00 章:准备

从这一章开始,我们用十章时间做出一个自己的 coding agent——mini-pi。路线是:

  • 第 01–07 章:纯 TypeScript + fetch,零运行时依赖,从一次 LLM 调用开始,逐章加入 agent loop、四大工具、流式输出、系统提示词、会话树、上下文压缩,最终得到一个约 500 行的完整 agent。
  • 第 08 章:把裸写的代码换成 pi 官方的 pi-ai + pi-agent-core 积木,几十行重构出架构上与 pi 同构的 mini-pi,并看一眼 coding-agent 的 SDK 能省掉多少事。
  • 第 09 章:进阶路线——自定义工具、事件钩子、skills 思想,以及接下来该读什么。

先裸写再上积木,是为了让你看清每一层抽象背后到底发生了什么。等你亲手解析过 SSE 流、亲手拼过 tool_calls,pi-ai 的每一行 API 都会变得理所当然。

环境要求

  • Node.js ≥ 20.19(我们要用全局 fetch、Web Stream、crypto.randomUUID 等现代 API)。用 node -v 确认。
  • 一个 OpenAI 兼容 API 的 key。以下任选其一:
    • OpenAI(https://api.openai.com/v1)
    • DeepSeek(https://api.deepseek.com/v1)
    • Kimi/Moonshot(https://api.moonshot.cn/v1)
    • OpenRouter(https://openrouter.ai/api/v1,一个 key 用所有模型)
    • 任何其他兼容 chat completions 协议的端点
Tip

教程全程使用 OpenAI 的 chat completions 协议,因为它是事实上的行业标准——pi 的作者也发现,真正需要对接的 API 其实只有四种,而 chat completions 是被最多 provider 讲的那种(参见设计思想:LLM 抽象层)。

创建项目

mkdir mini-pi && cd mini-pi
npm init -y
npm install --save-dev tsx typescript @types/node

tsx 让我们直接运行 TypeScript,不需要构建步骤。除此之外没有任何运行时依赖——LLM 调用就是 fetch,终端交互就是 node:readline

package.json 加上:

package.json(片段)
{
  "type": "module",
  "scripts": {
    "start": "tsx src/main.ts",
    "typecheck": "tsc --noEmit"
  }
}
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": ["ES2022"],
    "types": ["node"],
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

配置模型接入

mini-pi 用三个环境变量接入模型,全部可配:

export MINI_PI_API_KEY=sk-...                      # 必填(也会读 OPENAI_API_KEY)
export MINI_PI_BASE_URL=https://api.deepseek.com/v1  # 可选,默认 https://api.openai.com/v1
export MINI_PI_MODEL=deepseek-chat                  # 可选,默认 gpt-4o-mini

对应代码——这也是项目的第一个文件:

src/main.ts
const CONFIG = {
  baseUrl: (process.env.MINI_PI_BASE_URL ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(
    /\/$/,
    "",
  ),
  apiKey: process.env.MINI_PI_API_KEY ?? process.env.OPENAI_API_KEY ?? "",
  model: process.env.MINI_PI_MODEL ?? "gpt-4o-mini",
  compactTokens: Number(process.env.MINI_PI_COMPACT_TOKENS ?? "16000"),
};

compactTokens 到第 07 章(上下文压缩)才会用到,先放在这里。

为什么不装 LLM SDK

Anthropic、OpenAI、Google 都有官方 SDK,pi-ai 这样的统一库更是一步到位。但教程的第一阶段我们故意不用它们,原因和 pi 作者自己造轮子的理由一样:直接跟 provider 的 HTTP API 打交道,你才能完全控制并完全理解上下文里到底有什么。SDK 是把双刃剑——它帮你拼请求,也把请求藏了起来。等第 08 章我们再用 pi-ai,你会清楚地知道它每一笔抽象替你做了什么。

验收

node -v          # ≥ v20.19
npx tsx --version
echo 'console.log("mini-pi ready")' > src/main.ts && npx tsx src/main.ts

看到 mini-pi ready 就可以进入第 01 章:第一次对话了。