基于 pi-coding-agent 构建个人定制 Agent

已整理为可直接阅读的网页版本,附 20 轮 review 摘要。

基于 pi-coding-agent 构建个人定制 Agent 的中文开发实战文档

文档目标:让开发者可以直接基于 badlogic/pi-mono 中的 @mariozechner/pi-coding-agent 做出一个可运行、可扩展、可长期维护的个人 Agent,而不是泛泛讨论“AI Agent 应该怎么做”。

文档依据:badlogic/pi-mono 主仓库、packages/coding-agent/README.mddocs/sdk.mddocs/extensions.mddocs/skills.mddocs/packages.mddocs/settings.mddocs/session.mddocs/rpc.mddocs/development.md,以及官方 examples/sdk/*examples/extensions/* 示例。本文内容以这些官方源码与文档为主,并结合工程化实践整理。


0. 先说结论:pi-coding-agent 适不适合拿来做你自己的 Agent?

适合,前提是你想做的是下面这类东西:

它不太像那种“全家桶式代理平台”,而更像一个极简但钩子非常多的 coding harness / agent runtime。官方 README 里说得很直白:

换句话说:pi-coding-agent 的优势不是“功能很多”,而是“你能比较干净地把它改造成你自己的 Agent”。


1. 你在构建的到底是什么

先统一概念。基于 pi-coding-agent 做定制 Agent,本质上有四条路线:

路线 A:直接重度定制 pi 自己

适合: - 你就想要一个换皮/换能力版的 pi - 用户主要通过终端交互 - 希望保留 /model/resume/tree/compact 等现成功能

你会主要使用: - .pi/settings.json - .pi/extensions/ - .pi/skills/ - .pi/prompts/ - pi install 安装包

路线 B:基于 SDK 嵌入自己的应用

适合: - 你想做 Web UI、桌面客户端、移动端壳、IDE 插件、业务系统内嵌助手 - 你需要自己控制消息流、事件、持久化和 UI 展示

你会主要使用: - createAgentSession() - AgentSession.subscribe() 事件流 - DefaultResourceLoader - SessionManager / SettingsManager / AuthStorage

路线 C:通过 RPC 把 pi 当成无头 Agent 引擎

适合: - 主程序不是 Node/TS - 你更愿意用子进程 + JSONL 协议集成 - 想让宿主程序与 Agent 运行时隔离

你会主要使用: - pi --mode rpc - stdin/stdout JSONL 协议 - prompt / steer / follow_up / get_state 等 RPC 命令

路线 D:做一个“你的品牌 Agent 分发包”

适合: - 你要把一整套扩展、技能、主题、提示模板打包给团队使用 - 希望通过 npm / git 分发

你会主要使用: - pi package 约定(package.json 里的 pi 字段) - extensions/skills/prompts/themes/ - pi install npm:... / pi install git:...

推荐判断: - 想最快落地:A - 想做“真正属于你产品”的 Agent:B - 想跨语言或和别的宿主集成:C - 想做团队可复用能力包:D

很多实际项目会是 B + D:先用 SDK 做你的壳,再把扩展和技能打成包。


2. 官方架构总览:pi-mono 里哪些包跟你最相关

pi-mono 是 monorepo。根 README 列出的关键包如下:

如果你基于 pi-coding-agent 定制,你主要会站在这个包之上,但心里要有一层分工图:

你的自定义 Agent 应用
  ├─ 自己的 UI / 业务逻辑 / 路由 / 服务层
  └─ @mariozechner/pi-coding-agent
       ├─ AgentSession / SDK / RPC / SessionManager
       ├─ DefaultResourceLoader
       ├─ 内建工具:read/bash/edit/write/grep/find/ls
       ├─ Extensions(运行时钩子 + 自定义工具 + 命令 + UI)
       ├─ Skills(按需加载说明书)
       └─ Prompt Templates / Themes / Settings
            └─ @mariozechner/pi-agent-core
                 └─ @mariozechner/pi-ai

一个重要理解: - skills 更像“给模型看的作战手册” - extensions 更像“给运行时加能力的插件” - prompts 更像“快捷命令模板” - sessions 是“长期上下文与分支管理”

如果你把四者混在一起,项目会很快失控。


3. 开发前的最低准备

3.1 环境要求

根据 packages/coding-agent/package.json

3.2 安装方式

方式 1:作为终端工具安装

npm install -g @mariozechner/pi-coding-agent

方式 2:作为应用依赖安装

npm install @mariozechner/pi-coding-agent

如果你要做 SDK 集成,通常选方式 2。

3.3 模型认证

官方支持两类接入:

最简单是:

export ANTHROPIC_API_KEY=sk-ant-...
pi

SDK 里则通常通过: - AuthStorage.create() - ModelRegistry - 或 authStorage.setRuntimeApiKey(provider, key)

3.4 先跑通最小例子

如果你要基于 SDK 开发,先验证下面最小代码。

import { createAgentSession } from "@mariozechner/pi-coding-agent";

const { session } = await createAgentSession();

session.subscribe((event) => {
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("列出当前目录文件");

如果这一步跑不通,后面的定制都不要开始。


4. 先理解 pi-coding-agent 的核心对象

4.1 createAgentSession() 是总入口

官方 SDK 文档给出的主入口就是 createAgentSession()

它可以配置:

这意味着:你完全可以把“默认 pi”替换成你自己的 Agent 壳。

4.2 AgentSession 是运行中的会话实例

你最常打交道的是:

关键区别: - prompt():发一条普通用户消息 - steer():Agent 正在执行时,插入“转向消息” - followUp():让 Agent 做完当前任务后再处理下一条

这个设计对做复杂 Agent UI 很重要,因为它天然支持排队与中途纠偏

4.3 DefaultResourceLoader 是资源发现器

它会负责加载:

默认情况下,它会从标准路径自动发现资源。你也可以通过 override 精准控制。

4.4 SessionManager / SettingsManager / AuthStorage

三者职责非常清晰:

实践建议:不要把这三个揉到自己业务数据库里,至少第一版不要。 先沿用官方对象,把业务状态与 Agent 状态分层。


5. pi 的默认能力边界:你不是从零开始

官方 README 里,默认模型工具为:

此外 SDK 还提供:

你可以: - 直接用默认工具 - 组合一部分工具 - 自己创建定制工具 - 用扩展在运行时拦截、限制、替换工具行为

这点非常关键:自定义 Agent 不必一上来就发明一套工具协议。 对于很多内部 Agent,真正需要做的是: 1. 收紧默认工具权限 2. 增加你的业务工具 3. 做安全与审计

而不是“重写 read/write/bash”。


6. 推荐的项目结构

如果你要做一个长期维护的个人 Agent,我建议你不要把所有逻辑塞进一个 index.ts。下面是更靠谱的结构:

my-agent/
├─ package.json
├─ tsconfig.json
├─ src/
│  ├─ app/
│  │  ├─ main.ts                 # 你的应用入口
│  │  ├─ create-session.ts       # createAgentSession 包装
│  │  └─ stream.ts               # 事件流到 UI/日志 的桥接
│  ├─ config/
│  │  ├─ env.ts                  # 环境变量与密钥读取
│  │  ├─ models.ts               # 模型选择策略
│  │  └─ settings.ts             # SettingsManager 封装
│  ├─ agent/
│  │  ├─ extensions/
│  │  │  ├─ safety.ts
│  │  │  ├─ product-tools.ts
│  │  │  ├─ session-policy.ts
│  │  │  └─ observability.ts
│  │  ├─ skills/
│  │  │  ├─ repo-review/
│  │  │  │  └─ SKILL.md
│  │  │  └─ deploy-checklist/
│  │  │     └─ SKILL.md
│  │  ├─ prompts/
│  │  │  ├─ review.md
│  │  │  └─ implement.md
│  │  └─ system/
│  │     └─ base-system-prompt.ts
│  ├─ infra/
│  │  ├─ auth.ts                 # AuthStorage / provider key 注入
│  │  ├─ session-store.ts        # SessionManager 工厂
│  │  └─ resource-loader.ts      # DefaultResourceLoader 工厂
│  └─ ui/
│     ├─ cli.ts
│     ├─ rpc.ts
│     └─ web-adapter.ts
├─ .pi/
│  ├─ settings.json
│  ├─ extensions/
│  ├─ prompts/
│  └─ skills/
└─ AGENTS.md

结构原则


7. 最小可落地方案:做一个“个性化编码助手”

下面给出一个很实用的起步方案:做一个只在特定目录下工作的、带安全限制和项目技能的 Agent。

7.1 安装依赖

mkdir my-agent && cd my-agent
npm init -y
npm install @mariozechner/pi-coding-agent @mariozechner/pi-ai @sinclair/typebox

7.2 建立启动文件 src/app/main.ts

import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager, createCodingTools } from "@mariozechner/pi-coding-agent";
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";

const cwd = process.cwd();
const agentDir = process.env.MY_AGENT_DIR || `${process.env.HOME}/.pi/agent`;

const authStorage = AuthStorage.create();
const modelRegistry = new ModelRegistry(authStorage);
const settingsManager = SettingsManager.create(cwd, agentDir);

const resourceLoader = new DefaultResourceLoader({
  cwd,
  agentDir,
  settingsManager,
  additionalExtensionPaths: ["./src/agent/extensions/safety.ts"],
  additionalSkillPaths: ["./src/agent/skills"],
  additionalPromptTemplatePaths: ["./src/agent/prompts"],
  systemPromptOverride: (base) => `${base ?? ""}\n\n你是我的个性化开发代理,优先遵守项目约束、保持改动最小、解释决策。`,
});

await resourceLoader.reload();

const { session } = await createAgentSession({
  cwd,
  agentDir,
  authStorage,
  modelRegistry,
  settingsManager,
  sessionManager: SessionManager.create(cwd),
  tools: createCodingTools(cwd),
  resourceLoader,
});

session.subscribe((event) => {
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
  if (event.type === "tool_execution_start") {
    console.log(`\n[tool] ${event.toolName}`);
  }
  if (event.type === "tool_execution_end") {
    console.log(`\n[/tool] ${event.toolName} ${event.isError ? "error" : "ok"}`);
  }
});

await session.prompt("先阅读项目结构,再告诉我你能帮我做什么。", {
  streamingBehavior: undefined,
});

7.3 加一个安全扩展 src/agent/extensions/safety.ts

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";

export default function safety(pi: ExtensionAPI) {
  const blocked = [".env", "node_modules/", ".git/"];

  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName !== "write" && event.toolName !== "edit" && event.toolName !== "bash") {
      return;
    }

    if ((event.toolName === "write" || event.toolName === "edit") && typeof event.input.path === "string") {
      const path = event.input.path;
      if (blocked.some((item) => path.includes(item))) {
        ctx.ui.notify?.(`阻止修改敏感路径: ${path}`, "warning");
        return { block: true, reason: `protected path: ${path}` };
      }
    }

    if (event.toolName === "bash" && typeof event.input.command === "string") {
      const cmd = event.input.command;
      if (/rm\s+-rf|sudo\s+/.test(cmd)) {
        return { block: true, reason: `dangerous command blocked: ${cmd}` };
      }
    }
  });
}

7.4 加一个技能 src/agent/skills/repo-review/SKILL.md

---
name: repo-review
description: 审查当前代码仓库的结构、关键模块、构建方式与潜在风险。适用于首次进入项目、接手旧项目、准备改造前的快速摸底。
---

# Repo Review

## 目标
在开始改代码前,先理解:
- 项目目录结构
- 入口文件
- 依赖与构建工具
- 主要业务模块
- 测试与部署方式

## 工作步骤
1. 先读根目录 README、package.json、锁文件、构建配置。
2. 列出 src、app、packages、services、scripts 等核心目录。
3. 找入口与运行命令。
4. 输出“结构摘要 + 风险点 + 建议下一步”。
5. 未确认前,不要大改代码。

7.5 加一个模板 src/agent/prompts/implement.md

---
description: 让 Agent 实现一个功能并先做影响分析
---
请先分析要改动的文件、风险、测试点,然后再实现这个需求:$@

这样你已经有了: - 一个 SDK 启动器 - 一个安全扩展 - 一个仓库摸底 skill - 一个常用 prompt template

这已经是“个人定制 Agent”的雏形,而不是一个裸 SDK demo。


8. 如何做“真正个性化”:定制的四个层次

很多人说“我要个性化 Agent”,结果只是在 system prompt 里加一句“你要更懂我”。这没什么用。

真正个性化,至少有四层:

8.1 行为层:system prompt 与 AGENTS.md

适合放: - 语言风格 - 输出格式偏好 - 改码原则 - 与用户协作方式

例如: - 优先最小改动 - 先解释方案再改代码 - 改动后必须说明影响面 - 对不确定项明确标记

但不要把所有细节都塞系统提示里。它不是万能垃圾桶。

8.2 能力层:extensions

适合放: - 自定义工具 - 风险拦截 - 状态持久化 - UI 与命令扩展 - 事件观测

这是最重要的一层。因为这层真的改变了 Agent 能做什么、不能做什么。

8.3 场景层:skills

适合放: - 某个工作流的步骤说明 - 某种任务的最佳实践 - 需要按需加载的长文档 - helper script 的使用说明

例如: - 数据库排障 skill - 发版检查 skill - 代码审查 skill - 接手遗留项目 skill

8.4 交互层:prompt templates / UI commands

适合放: - 高频快捷命令 - 固定模板化任务 - 用户发起工作流的入口

例如: - /review - /implement 登录表单加验证码 - /ship 预发环境

建议:先做能力层,再做场景层,最后补行为层和交互层。


9. Extensions:定制 Agent 的主战场

根据官方 docs/extensions.md,extension 可以做的事非常多:

9.1 什么时候该用 extension,而不是 skill?

用 extension: - 你要执行逻辑 - 你要改运行时行为 - 你要注册工具 - 你要做安全拦截 - 你要持久化某种状态

用 skill: - 你要告诉模型如何做一类任务 - 你要按需加载大段说明文本 - 你要提供某个脚本/参考文档的使用方式

9.2 extension 典型模式

模式 1:安全闸门

官方示例 confirm-destructive.tsprotected-paths.ts 就属于这个模式。

建议至少拦: - rm -rf - sudo - 改 .env - 改 .git/ - 改 node_modules/ - 改锁文件(视团队策略) - 大范围批量替换

模式 2:业务工具

比如注册: - jira_search - deploy_preview - query_logs - read_design_doc - create_ticket

模式 3:会话态能力

官方 todo.tstools.ts 非常值得学。 重点不是 UI,而是它们展示了:

这对长期 Agent 很关键。

模式 4:定制 compaction

官方 custom-compaction.ts 展示了如何替换默认压缩逻辑,用另一个模型来做低成本总结。

如果你的 Agent 会跑长任务,这是高价值功能。

9.3 一个实用业务工具例子

下面注册一个“读取项目元信息”的工具:

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { promises as fs } from "node:fs";
import path from "node:path";

export default function projectTools(pi: ExtensionAPI) {
  pi.registerTool({
    name: "project_meta",
    label: "Project Meta",
    description: "读取项目 package.json、README 和构建信息,输出结构化摘要",
    parameters: Type.Object({
      includeReadme: Type.Optional(Type.Boolean({ default: true })),
    }),
    async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
      const cwd = ctx.cwd;
      const pkgPath = path.join(cwd, "package.json");
      const readmePath = path.join(cwd, "README.md");

      let pkg: any = null;
      let readme = "";

      try {
        pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
      } catch {}

      if (params.includeReadme) {
        try {
          readme = await fs.readFile(readmePath, "utf8");
        } catch {}
      }

      return {
        content: [{
          type: "text",
          text: JSON.stringify({
            name: pkg?.name,
            scripts: pkg?.scripts ?? {},
            dependencies: Object.keys(pkg?.dependencies ?? {}),
            hasReadme: !!readme,
            readmePreview: readme.slice(0, 1200),
          }, null, 2),
        }],
        details: {
          packageName: pkg?.name,
          scriptCount: Object.keys(pkg?.scripts ?? {}).length,
        },
      };
    },
  });
}

这类工具非常适合把“外部系统/项目上下文”结构化暴露给模型。


10. Skills:不要把 skill 当“第二个 system prompt”

官方 docs/skills.md 里强调:skill 是按需加载的能力包,Pi 遵循 Agent Skills 标准。

10.1 官方发现路径

Pi 会从这些位置发现 skill:

10.2 skill 应该写什么

写: - 使用场景 - 工作步骤 - 输出要求 - 需要读哪些参考文档 - 需要调用哪些脚本

不要写: - 一堆通用废话 - 和别的 skill 高度重复的原则 - 本应在 extension 里做的运行时约束

10.3 好 skill 的标准

一个好 skill 至少满足: - 触发条件明确:description 具体 - 工作步骤明确:模型读完能执行 - 引用材料清楚:相对路径指向 reference / scripts - 边界清楚:能做什么,不能做什么

10.4 推荐的 skill 目录模板

skills/
└─ deploy-check/
   ├─ SKILL.md
   ├─ references/
   │  ├─ release-process.md
   │  └─ env-matrix.md
   └─ scripts/
      └─ verify.sh

10.5 一个适合个人 Agent 的 skill 清单

如果你在做开发型个人 Agent,我建议最少先做这些 skill:

  1. repo-review:首次进入仓库摸底
  2. feature-implement:功能实现流程
  3. bug-triage:缺陷定位流程
  4. release-check:发版前检查
  5. code-review:审查流程
  6. handoff:上下文交接流程

11. Prompt Templates:让常用任务变成命令

官方 docs/prompt-templates.md 说明,prompt template 是 Markdown 片段,会映射成 /name 命令。

发现路径: - ~/.pi/agent/prompts/*.md - .pi/prompts/*.md - package 的 prompts/ - settings 的 prompts - CLI --prompt-template

11.1 为什么它对个人 Agent 有价值

因为很多“个性化体验”不是靠模型变聪明,而是靠你把高频任务变成稳定入口

11.2 推荐模板

review.md

---
description: 审查当前变更
---
请审查当前变更。重点关注:
- 逻辑错误
- 边界条件
- 安全问题
- 测试缺失
- 与现有架构的一致性

fix.md

---
description: 修复问题并先分析原因
---
请先复现并定位这个问题,再给出最小修复方案:$@

ship.md

---
description: 发版前检查
---
请按发版检查清单执行:$@

11.3 不要把 template 做成“复杂工作流引擎”

复杂逻辑应该去 extension 或 skill。template 最适合做“入口 prompt”,不要过度承担流程控制。


12. ResourceLoader:定制自己的资源发现与装配

如果你只会把 extension 和 skill 扔到默认目录,那只能算会“使用 pi”,不算会“基于 pi 构建 Agent”。

DefaultResourceLoader 才是做装配层的关键。

官方支持的 override 包括:

12.1 常见用法:给系统提示追加组织规范

const loader = new DefaultResourceLoader({
  systemPromptOverride: (base) => `${base ?? ""}\n\n附加要求:回答尽量先给结论,再给执行步骤。`,
});

12.2 常见用法:过滤 skill

const loader = new DefaultResourceLoader({
  skillsOverride: (current) => ({
    skills: current.skills.filter((s) => !s.name.includes("experimental")),
    diagnostics: current.diagnostics,
  }),
});

12.3 常见用法:注入虚拟 AGENTS.md

const loader = new DefaultResourceLoader({
  agentsFilesOverride: (current) => ({
    agentsFiles: [
      ...current.agentsFiles,
      {
        path: "/virtual/ORG-AGENTS.md",
        content: `# 组织级开发约束\n- 优先最小改动\n- 修改后给出回滚点`,
      },
    ],
  }),
});

12.4 设计建议

DefaultResourceLoader 包装成你自己的工厂函数:

export async function createMyResourceLoader(opts: { cwd: string; agentDir: string; settingsManager: SettingsManager }) {
  const loader = new DefaultResourceLoader({
    cwd: opts.cwd,
    agentDir: opts.agentDir,
    settingsManager: opts.settingsManager,
    additionalExtensionPaths: ["./src/agent/extensions"],
    additionalSkillPaths: ["./src/agent/skills"],
    additionalPromptTemplatePaths: ["./src/agent/prompts"],
    systemPromptOverride: (base) => `${base ?? ""}\n\n你是团队内部定制开发代理。`,
  });
  await loader.reload();
  return loader;
}

这样后续切换默认资源来源、加包、做隔离都不会影响主入口。


13. 上下文与记忆:AGENTS.md、skills、session 各管什么

很多 Agent 项目后期混乱,就是因为“记忆/上下文/设定”没有分层。

13.1 AGENTS.md / CLAUDE.md

DefaultResourceLoader 会向上查找上下文文件并注入系统提示上下文。

适合放: - 项目级开发规范 - 代码风格 - 目录说明 - 团队协作约束

不适合放: - 动态状态 - 大量历史记录 - 高频变化的偏好

13.2 skills

适合放: - 特定任务流程 - 参考资料说明 - 如何使用脚本/工具

13.3 session

适合放: - 会话过程 - 分支历史 - tool result - 历史总结 - 扩展状态条目

13.4 扩展自定义状态

如果你需要“持久但跟会话分支绑定”的状态,优先用 extension 的: - appendEntry() - tool result details - 在 session_start / session_tree / session_fork 时重建

官方 todo.tstools.ts 演示的就是这种模式。

13.5 一条非常重要的工程原则

不要把长期记忆一股脑塞进 system prompt。 应该分三层:


14. Session 系统:pi 最大的强项之一

pi 的 session 不是简单聊天记录,而是JSONL + 树结构分支会话

根据 docs/session.md: - 会话文件存放在 ~/.pi/agent/sessions/ 下(按 cwd 组织) - entry 有 id / parentId - 支持树状分支与原地跳转 - 当前版本为 v3

14.1 你能得到什么能力

14.2 这对自定义 Agent 有什么价值

如果你做的是产品级 Agent,这个系统能直接支撑: - 多轮复杂任务 - 中途纠偏 - 方案分叉 - 回溯与审计 - 工作状态续跑

这比很多“只保留 N 条消息”的聊天机器人强得多。

14.3 自己集成时要注意什么

如果你在 SDK 中自行管理 session: - 开发态可先 SessionManager.inMemory() - 生产态建议 SessionManager.create(cwd) 或自定义目录 - 如果宿主应用允许“一个任务多个分支”,优先用 pi 原生 tree/fork 语义,不要自创一套冲突模型

14.4 实战建议


15. Compaction:长上下文 Agent 的必要配置

官方说明: - 自动压缩默认开启 - 触发场景:接近上下文上限或溢出恢复 - 配置项在 settings.json 里: - compaction.enabled - compaction.reserveTokens - compaction.keepRecentTokens

15.1 默认 compaction 已经够不够?

小项目和 CLI 交互够用。

但如果你做的是: - 长流程开发助手 - 大型仓库分析器 - 业务系统内嵌 Agent - 高成本模型的长期任务

你最好做自己的 compaction 策略。

15.2 推荐自定义策略

策略 A:便宜模型做压缩

官方 custom-compaction.ts 用了 Gemini Flash 做摘要,这是非常实用的思路。

策略 B:保留“状态摘要”而不是“聊天摘要”

摘要重点应包含: - 当前目标 - 已做改动 - 关键文件 - 已决策事项 - 未决问题 - 下一步

策略 C:分层压缩

对长会话可采用: - 最近消息保留原文 - 中期消息保留结构摘要 - 早期消息只保留高层总结

15.3 风险提醒

compaction 是有损的。 全量历史仍在 JSONL 文件里,但模型上下文不再包含全部原文。 所以: - 法务/合规场景不要只依赖压缩后的上下文 - 关键事实要进入 custom entry 或文件 - 对重要计划可用 /handoff 风格生成交接 prompt


16. 模型与提供商集成

README 提到官方内置多个 provider 与模型目录,支持:

订阅: - Anthropic Claude Pro/Max - OpenAI ChatGPT Plus/Pro (Codex) - GitHub Copilot - Google Gemini CLI - Google Antigravity

API keys: - Anthropic - OpenAI - Azure OpenAI - Google Gemini / Vertex - Amazon Bedrock - Mistral - Groq - Cerebras - xAI - OpenRouter - Vercel AI Gateway - ZAI - OpenCode Zen / Go - Hugging Face - Kimi For Coding - MiniMax

16.1 定制 Agent 的模型策略建议

不要把“默认模型”写死在代码里然后结束。你至少要有:

16.2 SDK 层面的基本做法

import { getModel } from "@mariozechner/pi-ai";

const model = getModel("anthropic", "claude-sonnet-4-20250514");

也可以通过 SettingsManager 管理默认 provider / model。

16.3 生产建议

16.4 thinking level

Settings 支持: - off - minimal - low - medium - high - xhigh

但实际会受模型能力限制。

建议: - 默认 lowmedium - 代码迁移/复杂设计时提升 - 批量小任务时关掉或降低


17. 工具系统设计:默认工具 + 自定义工具 + 动态工具

17.1 内建工具怎么选

SDK 示例 05-tools.ts 已说明: - 默认可用 codingTools - 只读可用 readOnlyTools - custom cwd 时一定要用 createReadTool(cwd) 这类工厂函数,而不是直接用全局常量工具

这是个容易踩坑的点。

17.2 为什么 custom cwd 必须用 factory

官方示例明确提醒: - 如果你传了自定义 cwd - 又直接用了 readToolbashTool - 工具路径解析可能还是基于 process.cwd()

所以正确姿势:

import { createCodingTools } from "@mariozechner/pi-coding-agent";

const tools = createCodingTools(customCwd);

17.3 自定义工具设计原则

工具参数要: - 小而明确 - schema 完整 - 输出结构化 - details 可恢复状态

工具描述要: - 清楚说明何时使用 - 不要过泛 - 必要时提供 promptSnippet / promptGuidelines

17.4 动态工具

官方 dynamic-tools.ts 展示了运行时注册工具。适合: - 根据项目类型加载能力 - 根据用户选择启用工具 - 根据权限动态注入工具

17.5 推荐的工具分层

L1 基础工具

L2 业务桥接工具

L3 高阶工作流工具

尽量不要让模型直接用 bash 去模拟一切业务 API。能做结构化工具就做结构化工具。


18. RPC 模式:如果你的宿主不是 Node,这很有用

根据 docs/rpc.md,pi --mode rpc 提供 JSONL 协议,通过 stdin/stdout 通信。

18.1 适合场景

18.2 关键协议能力

18.3 集成注意事项

文档特别提醒: - RPC 使用严格 JSONL,以 \n 为记录分隔 - 不要用会错误识别 U+2028/U+2029 的泛型行读取器 - Node 的 readline 不符合该协议要求

18.4 什么时候选 SDK,什么时候选 RPC

选 SDK: - 你就是 Node/TS - 想直接复用对象与类型 - 想做更细粒度控制

选 RPC: - 跨语言 - 进程隔离 - 宿主只想看 JSON 事件流

如果你本来就是 Node 项目,优先 SDK。 官方也明确建议 Node/TS 用户优先直接用 AgentSession


19. 安全设计:别把“能改代码”直接暴露成“能干一切”

pi-coding-agent 的强大之处,也是它最大的风险来源:

官方多处文档都提醒:第三方包和技能要审查来源。

19.1 你至少该做的安全控制

控制 1:敏感路径保护

拦: - .env - .git/ - node_modules/ - 系统目录 - SSH / 凭据目录

控制 2:危险命令保护

拦: - rm -rf - sudo - curl ... | sh - 高风险网络下载执行 - 改 shell profile

控制 3:外部副作用审批

对于: - 部署 - 发工单 - 发消息 - 改线上配置 - 删资源

最好做“确认 + 审计日志”。

控制 4:第三方包准入

pi install 的 npm/git 包做来源审核,至少: - 自己维护 allowlist - 生产环境禁用随意安装 - 团队共享只用固定版本

19.2 安全实现位置建议

19.3 安全不是只靠 prompt

靠 prompt 写一句“不要做危险操作”几乎没什么意义。 真正有效的是: - extension block - tool surface 收敛 - 宿主层权限隔离 - 最小密钥暴露


20. 可观测性:别等出事了才看 session 文件

很多 Agent 项目都忽略 observability,最后只能翻 JSONL。

AgentSession.subscribe() 已经给了你足够的事件流。

20.1 你应该至少记录这些事件

20.2 推荐日志字段

20.3 简单日志桥接示例

session.subscribe((event) => {
  const now = new Date().toISOString();
  console.log(JSON.stringify({ now, eventType: event.type, event }));
});

实际生产建议写到结构化日志系统,而不是 console.log

20.4 隐藏 /debug

官方开发文档提到有隐藏命令 /debug,会把渲染行和发送给 LLM 的消息写入 ~/.pi/agent/pi-debug.log。这对调试很有帮助,但也意味着: - 里面可能有敏感上下文 - 生产环境要明确处理策略


21. 测试策略:怎么测试一个基于 pi 的定制 Agent

官方仓库自身有大量测试,覆盖 session、extensions、skills、rpc、compaction 等。

你自己的 Agent 项目建议至少做四层测试:

21.1 纯函数测试

测: - prompt 生成器 - resource loader 封装 - 工具输入输出转换 - 日志格式化

21.2 extension 单元测试

重点测: - tool_call 是否正确 block - appendEntry 状态恢复逻辑 - session tree / fork 后状态是否正确

21.3 SDK 集成测试

SessionManager.inMemory() + SettingsManager.inMemory() 进行: - prompt → tool call → response 的闭环验证 - custom tool 注册验证 - event 发射顺序验证

21.4 端到端测试

如果你做 CLI/RPC/Web 宿主,需要测: - 消息流 - 中途 steer / followUp - 会话恢复 - 模型切换 - 错误恢复

21.5 实用建议


22. 部署与分发

如果你的 Agent 只给自己用,SDK 直接跑即可。 如果要给团队或客户用,至少有三种分发方式。

22.1 方式 A:直接发一个 Node 应用

适合: - 内部系统 - Web 服务 - 本地桌面壳

22.2 方式 B:做 pi package

根据 docs/packages.md,你可以把: - extensions - skills - prompts - themes

打进 npm 或 git 包,再通过:

pi install npm:@foo/bar@1.0.0
pi install git:github.com/user/repo@v1

分发。

22.3 方式 C:fork / rebrand CLI

开发文档提到 package.json 里的 piConfig

{
  "piConfig": {
    "name": "pi",
    "configDir": ".pi"
  }
}

如果你真的要做品牌化 CLI: - 改 piConfig.name - 改 configDir - 改 bin

这样会影响: - CLI 名称 - 配置目录 - 环境变量名 - banner

22.4 实战建议


23. 凭据与密钥管理

23.1 不要把 API key 写进 skill 或 prompt

这是底线。

23.2 推荐方式

23.3 运行时注入示例

官方 12-full-control.ts 示例:

const customAuth = AuthStorage.create("/tmp/my-agent/auth.json");
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);

这个思路很好: - key 可运行时注入 - 不必持久化 - 宿主可控

23.4 生产建议


24. 开发者体验(DX):怎么让你的 Agent 团队真的愿意维护

一个 Agent 能不能长期活下去,DX 比“多聪明”更重要。

24.1 推荐做法

24.2 建议补充的项目文档

至少写: - README.md:你的 Agent 是什么 - ARCHITECTURE.md:system prompt / skill / extension / session 分工 - SECURITY.md:哪些行为被阻止,哪些需要审批 - OPERATIONS.md:如何调试、如何看日志、如何恢复 session

24.3 对新成员最友好的方式

别让新人去猜“为什么这件事写在 skill 里,不写在 extension 里”。 把分层原则写清楚,能少踩很多坑。


25. 常见坑位清单

坑 1:custom cwd 却直接用 readTool 等全局工具

后果:路径解析错位。

正确:createReadTool(cwd) / createCodingTools(cwd)

坑 2:把所有规则都堆进 system prompt

后果:提示膨胀,难维护,且很多约束实际无法强制执行。

正确: system prompt 管原则,extension 管执行,skill 管任务流程。

坑 3:把 skill 写成废话大全

后果:模型不触发或触发后也没法执行。

正确: description 精准,步骤明确,可操作。

坑 4:状态只保存在内存

后果:session 切换、fork、tree 导航后状态丢失。

正确: 用 tool result details 或 appendEntry() 做 session 绑定状态。

坑 5:所有任务都用一个最贵模型

后果:成本炸,响应慢。

正确: 按任务类型路由模型。

坑 6:不做 observability

后果:问题定位时只能读长 JSONL。

正确: 事件流日志化、成本统计、工具耗时统计。

坑 7:在生产环境随便 pi install 第三方包

后果:供应链与权限风险。

正确: 审核、固定版本、白名单。

坑 8:把外部副作用都交给 bash

后果:不可控、不可审计、难回滚。

正确: 关键业务操作优先做结构化 custom tool。

坑 9:忽视 compaction

后果:长任务越来越不稳定,最后上下文爆炸。

正确: 设计摘要策略与 handoff 机制。

坑 10:把 session 当聊天记录,而不是工作状态树

后果:不会利用 /tree/fork、branch summary 的价值。

正确: 把 session 当任务工作树来设计。


26. 推荐的实施路线图(4 个阶段)

阶段 1:跑通最小 Agent

目标:让 SDK session 能工作

交付: - createAgentSession() 启动脚本 - 默认工具 - 模型认证 - 最小事件打印

验收: - 能 prompt - 能看流式输出 - 能执行 read/bash

阶段 2:加个性化骨架

目标:让 Agent 不再是裸 pi

交付: - systemPromptOverride - 1~2 个 skills - 2~3 个 prompt templates - 1 个安全 extension

验收: - 进入项目后能按你的规则工作 - 对高风险操作能拦截 - 高频任务有快捷入口

阶段 3:加业务能力

目标:让 Agent 真能帮你干活

交付: - 3~5 个 custom tools - 事件日志 - session naming / labels - compaction 策略

验收: - 能访问业务系统或项目元数据 - 长任务可恢复 - 工具调用有审计

阶段 4:产品化

目标:可给团队或客户使用

交付: - package 分发或宿主应用 - 文档与测试 - 模型路由策略 - 安全策略 - 监控与成本统计

验收: - 新人可安装使用 - 关键能力可测试 - 问题可定位 - 成本可控制


27. 一个更完整的参考实现骨架

下面给出一个更贴近真实项目的装配代码。

import { getModel } from "@mariozechner/pi-ai";
import {
  AuthStorage,
  createAgentSession,
  DefaultResourceLoader,
  ModelRegistry,
  SessionManager,
  SettingsManager,
  createCodingTools,
} from "@mariozechner/pi-coding-agent";

export async function bootMyAgent(opts: {
  cwd: string;
  agentDir: string;
  mode?: "in-memory" | "persistent";
}) {
  const authStorage = AuthStorage.create(`${opts.agentDir}/auth.json`);

  if (process.env.ANTHROPIC_API_KEY) {
    authStorage.setRuntimeApiKey("anthropic", process.env.ANTHROPIC_API_KEY);
  }

  const modelRegistry = new ModelRegistry(authStorage, `${opts.agentDir}/models.json`);
  const settingsManager = SettingsManager.create(opts.cwd, opts.agentDir);

  settingsManager.applyOverrides({
    compaction: {
      enabled: true,
      reserveTokens: 16384,
      keepRecentTokens: 20000,
    },
    retry: {
      enabled: true,
      maxRetries: 3,
      baseDelayMs: 1500,
      maxDelayMs: 60000,
    },
  });

  const resourceLoader = new DefaultResourceLoader({
    cwd: opts.cwd,
    agentDir: opts.agentDir,
    settingsManager,
    additionalExtensionPaths: [
      "./src/agent/extensions/safety.ts",
      "./src/agent/extensions/project-tools.ts",
      "./src/agent/extensions/observability.ts",
    ],
    additionalSkillPaths: ["./src/agent/skills"],
    additionalPromptTemplatePaths: ["./src/agent/prompts"],
    systemPromptOverride: (base) => {
      return `${base ?? ""}

你是一个面向真实工程交付的个性化开发 Agent。
要求:
1. 优先最小可验证改动;
2. 动手前先快速阅读上下文;
3. 不确定就明确标注;
4. 重要风险和影响面要先说;
5. 优先使用结构化工具,不要滥用 bash。`;
    },
  });
  await resourceLoader.reload();

  const preferredModel =
    getModel("anthropic", "claude-sonnet-4-20250514") ||
    getModel("openai", "gpt-5") ||
    getModel("google", "gemini-2.5-pro");

  const sessionManager =
    opts.mode === "in-memory"
      ? SessionManager.inMemory()
      : SessionManager.create(opts.cwd);

  const { session, modelFallbackMessage } = await createAgentSession({
    cwd: opts.cwd,
    agentDir: opts.agentDir,
    authStorage,
    modelRegistry,
    settingsManager,
    sessionManager,
    resourceLoader,
    model: preferredModel,
    thinkingLevel: "low",
    tools: createCodingTools(opts.cwd),
  });

  session.subscribe((event) => {
    if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
      process.stdout.write(event.assistantMessageEvent.delta);
    }
  });

  return { session, modelFallbackMessage };
}

这个骨架已经具备: - 自定义资源装配 - 模型与凭据管理 - 持久化会话 - 默认安全与可观测性入口 - 可扩展为 CLI / Web / RPC 宿主


28. 我建议你优先借鉴的官方示例

如果你打算基于源码继续深挖,建议优先读这些官方示例:

SDK 示例

Extension 示例

如果你时间有限,先把这些读透,已经足够做出像样的个人 Agent。


29. 最终建议:构建你自己的 pi-based Agent 时,哪些事最重要

如果只保留最核心的建议,我会给这 10 条:

  1. 优先用 SDK,不要一开始就 fork 内核。
  2. 先做安全 extension,再做业务工具。
  3. system prompt 管原则,skill 管流程,extension 管执行。
  4. 把 session 当任务树,不是聊天记录。
  5. 对 custom cwd 一定用工具 factory。
  6. 让结构化工具替代 bash 黑魔法。
  7. 做 compaction 与 handoff,不然长任务迟早失控。
  8. 把观测、成本、错误恢复前置,而不是后补。
  9. 把你的能力包成 package,方便复用和分发。
  10. 尽量组合官方机制,不要重造一套和 pi 冲突的运行时。

30. 一份可直接照着做的落地清单

第一天

第二天

第三天

第四天

完成这四天,你的 Agent 已经不是 demo,而是一个够用的个人定制开发 Agent。


31. 20 轮审校后的最终结论

经过围绕以下 20 个维度的审校与修订:

  1. 准确性
  2. 架构清晰度
  3. 环境搭建步骤
  4. 代码组织
  5. 可扩展性
  6. 工具体系
  7. 记忆/上下文
  8. 模型集成
  9. prompt/skill 设计
  10. session 管理
  11. 安全性
  12. 可观测性
  13. 测试
  14. 部署
  15. 认证与密钥
  16. 个性化策略
  17. 性能与成本
  18. 开发体验
  19. 易踩坑
  20. 最终润色

最终建议是:

如果你的目标是“在一个可靠的 coding harness 之上,做出自己的个人开发 Agent”,pi-coding-agent 是非常合适的底座。

它最适合的做法不是“魔改内核”,而是:

这条路线既贴合官方设计,也最容易维护。


附:本文直接参考的官方材料范围

如需继续扩写,下一步最值得补的是: - 一个完整的 Web 宿主集成示例 - 一个真正的团队级业务工具包示例 - 一个从本地 CLI 到团队 package 发布的完整样板仓库

20 轮 Review 日志

下方为该报告的 20 轮审校摘要。

pi-coding-agent 自定义 Agent 文档 20 轮审校日志

目标文件:/home/horace/.openclaw/workspace/reports/pi-coding-agent-custom-agent-report.md

说明:以下为 20 轮针对不同质量维度的复核/修订摘要。每轮都基于官方源码与文档内容做校正、补强或重组,最终结果已合并进报告文件。


Pass 01 — 准确性(Accuracy)

Pass 02 — 架构清晰度(Architecture Clarity)

Pass 03 — 环境搭建步骤(Setup Steps)

Pass 04 — 代码组织(Code Organization)

Pass 05 — 可扩展性(Extensibility)

Pass 06 — 工具体系(Tool System)

Pass 07 — 记忆 / 上下文(Memory / Context)

Pass 08 — 模型集成(Model Integration)

Pass 09 — Prompt / Skills(Prompts / Skills)

Pass 10 — Session 管理(Session Management)

Pass 11 — 安全性(Safety)

Pass 12 — 可观测性(Observability)

Pass 13 — 测试(Testing)

Pass 14 — 部署(Deployment)

Pass 15 — 认证与密钥(Auth / Secrets)

Pass 16 — 个性化策略(Personalization)

Pass 17 — 性能与成本(Performance / Cost)

Pass 18 — 开发体验(DX)

Pass 19 — 易踩坑(Pitfalls)

Pass 20 — 最终润色(Final Polish)


最终结果