# pi-starter 使用指南

面向**第一次拿到这个仓库、想知道怎么用**的人。目标是一页看完：跑起来、说上第一句话、加进你自己的业务、出问题知道去哪查。

三件套里的另外两份分工是这样：

- **本文**：怎么用（操作步骤、判断、排错）。
- [`参考手册.md`](参考手册.md)：它对外到底暴露什么（WS 协议 / REST 路由 / 工具 / 环境变量）。由源码生成，不手改。
- [`能力与边界.md`](能力与边界.md)：它做到哪、刻意不做什么、为什么。

要往上加东西的细节在 [`README.zh-CN.md`](../README.zh-CN.md) 的「快速开始」与「二次开发」两节，本文不重复，只给判断依据。

---

## 1. 它是什么，三个词说完

**一个垂直 Agent 的起步模板。** 三层意思：

- **脚手架**：不是成品应用，是「长得像应用」的骨架——模型、工具、知识库、会话、HTTP 接口、前端都接好了，业务留口子给你注入。
- **三种形态同一套内核**：命令行对话、HTTP + SSE 服务、被当库 `import` 进别人的服务，都走同一个 `buildAgent()`。
- **默认安全**：只绑本机回环、编码工具默认关、HTTP 无鉴权（所以别直接暴露公网）。

不是编码助手。仓库里没有 LSP、没有 diff 视图、没有终端面板——那些是给「改代码的驾驶舱」用的，不是每个垂直 Agent 都需要。

---

## 2. 五分钟跑起来

### 2.1 前置条件

- **Node.js ≥ 22.19**。硬要求，因为用了内置 `node:sqlite`；低版本直接启动失败。
- 一把 **OpenAI 兼容供应商的 Key**（默认示例是 ModelScope，换别的也行）。

### 2.2 装依赖、写配置

```bash
npm install
cp .env.example .env          # Windows: copy .env.example .env
# 编辑 .env，至少填 PI_API_KEY
npm run setup
```

`npm run setup` 做的事：把 provider 与密钥 **merge** 写进 Pi 原生配置——`~/.pi/agent/models.json` 与 `~/.pi/agent/auth.json`（后者 mode `0600`）。已有密钥默认保留，要覆盖加 `--force`。

`.env` 里模型有两种写法，选一种：

```bash
# 推荐：一个变量说清 provider 与模型
PI_MODEL=modelscope/deepseek-ai/DeepSeek-V4-Flash-0731

# 或拆开写。模型 id 自带斜杠时（如 Qwen/Qwen3-...）斜杠原样保留，不会被当成 provider
PI_PROVIDER=modelscope
PI_MODEL=deepseek-ai/DeepSeek-V4-Flash-0731
```

**没有模型会直接抛错，不会静默落到某个内置默认**——这是有意的：一个悄悄选错的模型比启动失败更难查。

### 2.3 三种形态，跑哪个

| 你想干什么 | 命令 | 说明 |
|---|---|---|
| 在终端里对话 | `npm run dev` | 交互式 CLI；临时换模型 `npm run dev -- --model zhipu/glm-4.5-air` |
| 给别人调 HTTP 接口 | `npm run web` | 默认 `http://127.0.0.1:3000`；换端口 `--port 4000` |
| 在浏览器里用产品前端 | `npm run ui:dev` | Vite 跑在 `:5173`，`/ws` 代理到后端 `:3000`（后端要同时开着） |
| 跨语言 / 子进程集成 | `npm run dev -- --mode rpc` | 官方 stdio JSONL 协议 |

单进程部署（前端产物挂到后端同一个端口）：

```bash
npm run ui:build   # 产出 web/dist
npm run web        # 之后后端直接把前端挂在 /
```

### 2.4 说上第一句话

CLI 起来后直接打字回车。会话里可以用这些不带斜杠的本地命令（不发模型）：

```
/models                # 列出可用模型
/model modelscope/Qwen/Qwen2.5-72B-Instruct   # 切当前会话的模型，不重建会话
/cycle                 # 沿 scopedModels 轮换下一个
```

HTTP 侧发一句话（SSE 流式）：

```bash
curl -N http://127.0.0.1:3000/chat \
  -H "content-type: application/json" \
  -d '{"message":"你好，介绍一下你自己"}'
```

返回是一串 `event: <type>` 帧，`text` 是回答文本、`done` 是结束、`error` 是出错。事件词表见 [`参考手册.md`](参考手册.md) 的 REST 一节。

**两个会绊人的点，先说在前面**：

1. **一个进程同一时刻只能跑一轮**。正在流式输出时再发 `/chat` 会拿到 `429`。这是有意的闸门（避免状态交错），不是 bug。要真并发多用户，每个用户一个 `buildAgent()` 自己管会话。
2. **`npm run web` 起的服务没有任何鉴权**，只绑 `127.0.0.1`。改 `PI_HOST` 绑非回环地址会启动告警——**别在没有反向代理和鉴权的情况下暴露公网**。

---

## 3. 不配模型也能验的东西

判断「装好了没」不该依赖模型可用。这些接口返回的是仓库里的静态资源与本地状态：

```bash
curl http://127.0.0.1:3000/health          # 进程活着（不碰依赖）
curl http://127.0.0.1:3000/health/ready    # 模型 + 数据库 + 知识库都就绪才是 200
curl http://127.0.0.1:3000/info            # 当前模型、技能 / 知识库目录、供应商鉴权状态
curl http://127.0.0.1:3000/skills          # 技能目录
curl http://127.0.0.1:3000/knowledge/search?q=切换模型
curl http://127.0.0.1:3000/db
```

`/health` 与 `/health/ready` 刻意分开：模型供应商故障时 `/health` 仍为绿（进程没死，别让编排器重启一个健康进程），`/health/ready` 才转 `503`。

不启动服务也能自检整套装配：

```bash
npm test        # 后端单元 + 集成（不调模型、不联网）
npm run smoke   # 23 项真实 WebSocket 端到端检查
npm run verify  # 九项门禁一次跑完：typecheck / lint:unused / test / test:web / docs:check / verify:audit / smoke / build / verify:embed
npm run verify:all   # 再加真进程 e2e（重启 + 会话恢复，不需要 API Key）
npm run test:coverage  # 覆盖率棘轮（只在远端跑，阈值见 scripts/coverage-thresholds.mjs）
```

---

## 4. 把它用在你自己的业务上

两种接法，先选路线：

| 你的情况 | 走哪条 |
|---|---|
| 只想改这个仓库，做成自己的 Agent | 直接改 `src/`：人设改 `src/prompts/`、工具登记进 `src/tools/index.ts`、技能丢 `src/skills/`、知识库丢 `src/knowledge/` |
| 把 Agent 当能力，接进已有的 Express 服务，不改它一行 | 当库用：`npm run build` 后 `import { buildAgent, createApp } from "pi-starter"` |

当库用的最小样子：

```ts
import { buildAgent, createApp } from "pi-starter";

const agent = await buildAgent({
  systemPrompt: "你是客服助手。",
  extraTools: [myTool],
  extraSkillPaths: ["./skills"],
  extraKnowledgeDirs: ["./docs"],
  inMemory: true,
});
const { app, dispose } = createApp({ agent, staticDir: false });
```

**鉴权只能挂在父应用上。** 在 `createApp()` 之后再 `app.use(auth)`、或写进 `configure` 里，都**无效**——内核路由已经先注册，中间件排在后面永远命中不到（有实测：无 token 也能 200）。正确写法是把内核 app 当子应用挂：

```ts
const { app: agentApp } = createApp({ agent, staticDir: false });
const server = express();
server.use("/agent", requireAuth, agentApp);   // 这样才拦得住
server.listen(3000);
```

加工具、加技能、加知识库、加扩展、接数据库、自定义 WS 命令与 HTTP 路由的完整代码片段，见 [`README.zh-CN.md`](../README.zh-CN.md) 的「二次开发」一节，以及 [`嵌入指南.md`](嵌入指南.md)（含实测自检清单）。

---

## 5. 出问题怎么查

**先看进程日志。** 默认 `info` 级、结构化 JSONL；开发想全开：`PI_LOG_LEVEL=debug`。日志同时落盘到 `./logs`（`PI_LOG_TO_FILE=false` 可关，`PI_LOG_DIR` 改目录）。

| 症状 | 大概率原因 | 去哪 |
|---|---|---|
| 启动就抛「未指定模型」 | `.env` 里没有 `PI_MODEL`（或 `PI_PROVIDER` + `PI_MODEL`） | 本文 §2.2；这是刻意不留兜底 |
| `/chat` 返回 `429` | 上一轮还在流式输出，进程内同时只允许一轮 | 本文 §2.4 |
| 工具没被调用 | 工具不在 `src/tools/index.ts` 或 `buildAgent({ extraTools })` 里——只在扩展里 `pi.registerTool` 的名字不会被 `readonly` / `coding` 档放行 | [`FAQ.md`](../FAQ.md) |
| 自定义 provider 鉴权不生效 | provider 没写进 `~/.pi/agent/models.json`，或 key 没进 `auth.json` | 重跑 `npm run setup` |
| 前端 dev 模式连不上 WS | Vite 代理把转发 `Host` 改掉了，被同源校验 403 | README「二次开发」里的代理说明：**不要**给代理加 `changeOrigin` |
| 想在容器里隔离着跑 | 用仓库自带 `Dockerfile`，发布端口务必只绑回环 | [`SECURITY.md`](../SECURITY.md) |
| `guard` 挡住了某条命令 | 命中危险命令正则或路径越出 cwd | 规则在 `src/extensions/guard.ts`；它不是沙箱，见 [`能力与边界.md`](能力与边界.md) |

还找不到答案就看 [`FAQ.md`](../FAQ.md)——按「安装配置 / 运行时 / 模型 / 部署 / 开发」分好组了。

---

## 6. 下一步去哪

- 想改行为：`src/prompts/persona.md`（人设）+ `rules.md`（规则），改文件即改性格。
- 想加能力：`src/tools/`（工具）、`src/skills/<name>/SKILL.md`（技能）、`src/knowledge/*.md`（知识库）、`src/extensions/`（钩子）。
- 想接外部系统：MCP 桥（stdio）、自定义 HTTP 路由、自定义 WS 命令——都在 README「进阶」一节。
- 想确认某个接口的准确形状：查 [`参考手册.md`](参考手册.md)，别猜，它是从源码生成的。
