已整理为可直接阅读的网页版本,附 20 轮 review 摘要。
文档目标:让开发者可以直接基于
badlogic/pi-mono中的@mariozechner/pi-coding-agent做出一个可运行、可扩展、可长期维护的个人 Agent,而不是泛泛讨论“AI Agent 应该怎么做”。文档依据:
badlogic/pi-mono主仓库、packages/coding-agent/README.md、docs/sdk.md、docs/extensions.md、docs/skills.md、docs/packages.md、docs/settings.md、docs/session.md、docs/rpc.md、docs/development.md,以及官方examples/sdk/*与examples/extensions/*示例。本文内容以这些官方源码与文档为主,并结合工程化实践整理。
适合,前提是你想做的是下面这类东西:
它不太像那种“全家桶式代理平台”,而更像一个极简但钩子非常多的 coding harness / agent runtime。官方 README 里说得很直白:
read、write、edit、bash换句话说:pi-coding-agent 的优势不是“功能很多”,而是“你能比较干净地把它改造成你自己的 Agent”。
先统一概念。基于 pi-coding-agent 做定制 Agent,本质上有四条路线:
pi 自己适合:
- 你就想要一个换皮/换能力版的 pi
- 用户主要通过终端交互
- 希望保留 /model、/resume、/tree、/compact 等现成功能
你会主要使用:
- .pi/settings.json
- .pi/extensions/
- .pi/skills/
- .pi/prompts/
- pi install 安装包
适合: - 你想做 Web UI、桌面客户端、移动端壳、IDE 插件、业务系统内嵌助手 - 你需要自己控制消息流、事件、持久化和 UI 展示
你会主要使用:
- createAgentSession()
- AgentSession.subscribe() 事件流
- DefaultResourceLoader
- SessionManager / SettingsManager / AuthStorage
适合: - 主程序不是 Node/TS - 你更愿意用子进程 + JSONL 协议集成 - 想让宿主程序与 Agent 运行时隔离
你会主要使用:
- pi --mode rpc
- stdin/stdout JSONL 协议
- prompt / steer / follow_up / get_state 等 RPC 命令
适合: - 你要把一整套扩展、技能、主题、提示模板打包给团队使用 - 希望通过 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 做你的壳,再把扩展和技能打成包。
pi-mono 是 monorepo。根 README 列出的关键包如下:
@mariozechner/pi-ai:统一多模型/多提供商 LLM API@mariozechner/pi-agent-core:Agent 运行时、工具调用、状态管理@mariozechner/pi-coding-agent:CLI / SDK / 会话 / 扩展 / 资源加载 / 默认工具@mariozechner/pi-tui:终端 UI@mariozechner/pi-web-ui:Web 组件如果你基于 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 是“长期上下文与分支管理”
如果你把四者混在一起,项目会很快失控。
根据 packages/coding-agent/package.json:
>=20.6.0pi-mononpm install -g @mariozechner/pi-coding-agent
npm install @mariozechner/pi-coding-agent
如果你要做 SDK 集成,通常选方式 2。
官方支持两类接入:
/login最简单是:
export ANTHROPIC_API_KEY=sk-ant-...
pi
SDK 里则通常通过:
- AuthStorage.create()
- ModelRegistry
- 或 authStorage.setRuntimeApiKey(provider, key)
如果你要基于 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("列出当前目录文件");
如果这一步跑不通,后面的定制都不要开始。
createAgentSession() 是总入口官方 SDK 文档给出的主入口就是 createAgentSession()。
它可以配置:
cwd:工作目录agentDir:全局配置目录,默认 ~/.pi/agentauthStoragemodelRegistrymodelthinkingLeveltoolscustomToolsresourceLoadersessionManagersettingsManager这意味着:你完全可以把“默认 pi”替换成你自己的 Agent 壳。
AgentSession 是运行中的会话实例你最常打交道的是:
prompt(text, options?)steer(text)followUp(text)subscribe(listener)setModel(model)compact()newSession()switchSession()fork(entryId)navigateTree(targetId)abort()关键区别:
- prompt():发一条普通用户消息
- steer():Agent 正在执行时,插入“转向消息”
- followUp():让 Agent 做完当前任务后再处理下一条
这个设计对做复杂 Agent UI 很重要,因为它天然支持排队与中途纠偏。
DefaultResourceLoader 是资源发现器它会负责加载:
默认情况下,它会从标准路径自动发现资源。你也可以通过 override 精准控制。
SessionManager / SettingsManager / AuthStorage三者职责非常清晰:
SessionManager:会话文件与分支树SettingsManager:设置加载、覆盖、持久化AuthStorage:凭据实践建议:不要把这三个揉到自己业务数据库里,至少第一版不要。 先沿用官方对象,把业务状态与 Agent 状态分层。
官方 README 里,默认模型工具为:
readwriteeditbash此外 SDK 还提供:
grepfindlsreadOnlyToolscodingTools你可以: - 直接用默认工具 - 组合一部分工具 - 自己创建定制工具 - 用扩展在运行时拦截、限制、替换工具行为
这点非常关键:自定义 Agent 不必一上来就发明一套工具协议。 对于很多内部 Agent,真正需要做的是: 1. 收紧默认工具权限 2. 增加你的业务工具 3. 做安全与审计
而不是“重写 read/write/bash”。
如果你要做一个长期维护的个人 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
extensions/skills/prompts/system prompt + extension + settings 共同承担下面给出一个很实用的起步方案:做一个只在特定目录下工作的、带安全限制和项目技能的 Agent。
mkdir my-agent && cd my-agent
npm init -y
npm install @mariozechner/pi-coding-agent @mariozechner/pi-ai @sinclair/typebox
src/app/main.tsimport { 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,
});
src/agent/extensions/safety.tsimport 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}` };
}
}
});
}
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. 未确认前,不要大改代码。
src/agent/prompts/implement.md---
description: 让 Agent 实现一个功能并先做影响分析
---
请先分析要改动的文件、风险、测试点,然后再实现这个需求:$@
这样你已经有了: - 一个 SDK 启动器 - 一个安全扩展 - 一个仓库摸底 skill - 一个常用 prompt template
这已经是“个人定制 Agent”的雏形,而不是一个裸 SDK demo。
很多人说“我要个性化 Agent”,结果只是在 system prompt 里加一句“你要更懂我”。这没什么用。
真正个性化,至少有四层:
适合放: - 语言风格 - 输出格式偏好 - 改码原则 - 与用户协作方式
例如: - 优先最小改动 - 先解释方案再改代码 - 改动后必须说明影响面 - 对不确定项明确标记
但不要把所有细节都塞系统提示里。它不是万能垃圾桶。
适合放: - 自定义工具 - 风险拦截 - 状态持久化 - UI 与命令扩展 - 事件观测
这是最重要的一层。因为这层真的改变了 Agent 能做什么、不能做什么。
适合放: - 某个工作流的步骤说明 - 某种任务的最佳实践 - 需要按需加载的长文档 - helper script 的使用说明
例如: - 数据库排障 skill - 发版检查 skill - 代码审查 skill - 接手遗留项目 skill
适合放: - 高频快捷命令 - 固定模板化任务 - 用户发起工作流的入口
例如:
- /review
- /implement 登录表单加验证码
- /ship 预发环境
建议:先做能力层,再做场景层,最后补行为层和交互层。
根据官方 docs/extensions.md,extension 可以做的事非常多:
用 extension: - 你要执行逻辑 - 你要改运行时行为 - 你要注册工具 - 你要做安全拦截 - 你要持久化某种状态
用 skill: - 你要告诉模型如何做一类任务 - 你要按需加载大段说明文本 - 你要提供某个脚本/参考文档的使用方式
官方示例 confirm-destructive.ts、protected-paths.ts 就属于这个模式。
建议至少拦:
- rm -rf
- sudo
- 改 .env
- 改 .git/
- 改 node_modules/
- 改锁文件(视团队策略)
- 大范围批量替换
比如注册:
- jira_search
- deploy_preview
- query_logs
- read_design_doc
- create_ticket
官方 todo.ts、tools.ts 非常值得学。
重点不是 UI,而是它们展示了:
session_start / session_tree / session_fork 后重建状态这对长期 Agent 很关键。
官方 custom-compaction.ts 展示了如何替换默认压缩逻辑,用另一个模型来做低成本总结。
如果你的 Agent 会跑长任务,这是高价值功能。
下面注册一个“读取项目元信息”的工具:
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,
},
};
},
});
}
这类工具非常适合把“外部系统/项目上下文”结构化暴露给模型。
官方 docs/skills.md 里强调:skill 是按需加载的能力包,Pi 遵循 Agent Skills 标准。
Pi 会从这些位置发现 skill:
~/.pi/agent/skills/~/.agents/skills/.pi/skills/.agents/skills/(会向上查祖先目录,直到 git 根或文件系统根)skills--skill <path>写: - 使用场景 - 工作步骤 - 输出要求 - 需要读哪些参考文档 - 需要调用哪些脚本
不要写: - 一堆通用废话 - 和别的 skill 高度重复的原则 - 本应在 extension 里做的运行时约束
一个好 skill 至少满足: - 触发条件明确:description 具体 - 工作步骤明确:模型读完能执行 - 引用材料清楚:相对路径指向 reference / scripts - 边界清楚:能做什么,不能做什么
skills/
└─ deploy-check/
├─ SKILL.md
├─ references/
│ ├─ release-process.md
│ └─ env-matrix.md
└─ scripts/
└─ verify.sh
如果你在做开发型个人 Agent,我建议最少先做这些 skill:
repo-review:首次进入仓库摸底feature-implement:功能实现流程bug-triage:缺陷定位流程release-check:发版前检查code-review:审查流程handoff:上下文交接流程官方 docs/prompt-templates.md 说明,prompt template 是 Markdown 片段,会映射成 /name 命令。
发现路径:
- ~/.pi/agent/prompts/*.md
- .pi/prompts/*.md
- package 的 prompts/
- settings 的 prompts
- CLI --prompt-template
因为很多“个性化体验”不是靠模型变聪明,而是靠你把高频任务变成稳定入口。
review.md---
description: 审查当前变更
---
请审查当前变更。重点关注:
- 逻辑错误
- 边界条件
- 安全问题
- 测试缺失
- 与现有架构的一致性
fix.md---
description: 修复问题并先分析原因
---
请先复现并定位这个问题,再给出最小修复方案:$@
ship.md---
description: 发版前检查
---
请按发版检查清单执行:$@
复杂逻辑应该去 extension 或 skill。template 最适合做“入口 prompt”,不要过度承担流程控制。
如果你只会把 extension 和 skill 扔到默认目录,那只能算会“使用 pi”,不算会“基于 pi 构建 Agent”。
DefaultResourceLoader 才是做装配层的关键。
官方支持的 override 包括:
extensionsOverrideskillsOverridepromptsOverridethemesOverrideagentsFilesOverridesystemPromptOverrideappendSystemPromptOverrideconst loader = new DefaultResourceLoader({
systemPromptOverride: (base) => `${base ?? ""}\n\n附加要求:回答尽量先给结论,再给执行步骤。`,
});
const loader = new DefaultResourceLoader({
skillsOverride: (current) => ({
skills: current.skills.filter((s) => !s.name.includes("experimental")),
diagnostics: current.diagnostics,
}),
});
const loader = new DefaultResourceLoader({
agentsFilesOverride: (current) => ({
agentsFiles: [
...current.agentsFiles,
{
path: "/virtual/ORG-AGENTS.md",
content: `# 组织级开发约束\n- 优先最小改动\n- 修改后给出回滚点`,
},
],
}),
});
把 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;
}
这样后续切换默认资源来源、加包、做隔离都不会影响主入口。
很多 Agent 项目后期混乱,就是因为“记忆/上下文/设定”没有分层。
AGENTS.md / CLAUDE.mdDefaultResourceLoader 会向上查找上下文文件并注入系统提示上下文。
适合放: - 项目级开发规范 - 代码风格 - 目录说明 - 团队协作约束
不适合放: - 动态状态 - 大量历史记录 - 高频变化的偏好
适合放: - 特定任务流程 - 参考资料说明 - 如何使用脚本/工具
适合放: - 会话过程 - 分支历史 - tool result - 历史总结 - 扩展状态条目
如果你需要“持久但跟会话分支绑定”的状态,优先用 extension 的:
- appendEntry()
- tool result details
- 在 session_start / session_tree / session_fork 时重建
官方 todo.ts、tools.ts 演示的就是这种模式。
不要把长期记忆一股脑塞进 system prompt。 应该分三层:
pi 的 session 不是简单聊天记录,而是JSONL + 树结构分支会话。
根据 docs/session.md:
- 会话文件存放在 ~/.pi/agent/sessions/ 下(按 cwd 组织)
- entry 有 id / parentId
- 支持树状分支与原地跳转
- 当前版本为 v3
--session <path>:指定会话--fork <path|id>:从历史会话派生/tree:在同一会话文件里切分支/fork:单独创建新文件/resume:恢复历史/compact:压缩历史如果你做的是产品级 Agent,这个系统能直接支撑: - 多轮复杂任务 - 中途纠偏 - 方案分叉 - 回溯与审计 - 工作状态续跑
这比很多“只保留 N 条消息”的聊天机器人强得多。
如果你在 SDK 中自行管理 session:
- 开发态可先 SessionManager.inMemory()
- 生产态建议 SessionManager.create(cwd) 或自定义目录
- 如果宿主应用允许“一个任务多个分支”,优先用 pi 原生 tree/fork 语义,不要自创一套冲突模型
官方说明:
- 自动压缩默认开启
- 触发场景:接近上下文上限或溢出恢复
- 配置项在 settings.json 里:
- compaction.enabled
- compaction.reserveTokens
- compaction.keepRecentTokens
小项目和 CLI 交互够用。
但如果你做的是: - 长流程开发助手 - 大型仓库分析器 - 业务系统内嵌 Agent - 高成本模型的长期任务
你最好做自己的 compaction 策略。
官方 custom-compaction.ts 用了 Gemini Flash 做摘要,这是非常实用的思路。
摘要重点应包含: - 当前目标 - 已做改动 - 关键文件 - 已决策事项 - 未决问题 - 下一步
对长会话可采用: - 最近消息保留原文 - 中期消息保留结构摘要 - 早期消息只保留高层总结
compaction 是有损的。
全量历史仍在 JSONL 文件里,但模型上下文不再包含全部原文。
所以:
- 法务/合规场景不要只依赖压缩后的上下文
- 关键事实要进入 custom entry 或文件
- 对重要计划可用 /handoff 风格生成交接 prompt
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
不要把“默认模型”写死在代码里然后结束。你至少要有:
import { getModel } from "@mariozechner/pi-ai";
const model = getModel("anthropic", "claude-sonnet-4-20250514");
也可以通过 SettingsManager 管理默认 provider / model。
Settings 支持:
- off
- minimal
- low
- medium
- high
- xhigh
但实际会受模型能力限制。
建议:
- 默认 low 或 medium
- 代码迁移/复杂设计时提升
- 批量小任务时关掉或降低
SDK 示例 05-tools.ts 已说明:
- 默认可用 codingTools
- 只读可用 readOnlyTools
- custom cwd 时一定要用 createReadTool(cwd) 这类工厂函数,而不是直接用全局常量工具
这是个容易踩坑的点。
官方示例明确提醒:
- 如果你传了自定义 cwd
- 又直接用了 readTool、bashTool
- 工具路径解析可能还是基于 process.cwd()
所以正确姿势:
import { createCodingTools } from "@mariozechner/pi-coding-agent";
const tools = createCodingTools(customCwd);
工具参数要: - 小而明确 - schema 完整 - 输出结构化 - details 可恢复状态
工具描述要:
- 清楚说明何时使用
- 不要过泛
- 必要时提供 promptSnippet / promptGuidelines
官方 dynamic-tools.ts 展示了运行时注册工具。适合:
- 根据项目类型加载能力
- 根据用户选择启用工具
- 根据权限动态注入工具
尽量不要让模型直接用 bash 去模拟一切业务 API。能做结构化工具就做结构化工具。
根据 docs/rpc.md,pi --mode rpc 提供 JSONL 协议,通过 stdin/stdout 通信。
promptsteerfollow_upabortnew_sessionget_stateget_messagesset_model文档特别提醒:
- RPC 使用严格 JSONL,以 \n 为记录分隔
- 不要用会错误识别 U+2028/U+2029 的泛型行读取器
- Node 的 readline 不符合该协议要求
选 SDK: - 你就是 Node/TS - 想直接复用对象与类型 - 想做更细粒度控制
选 RPC: - 跨语言 - 进程隔离 - 宿主只想看 JSON 事件流
如果你本来就是 Node 项目,优先 SDK。 官方也明确建议 Node/TS 用户优先直接用 AgentSession。
pi-coding-agent 的强大之处,也是它最大的风险来源:
官方多处文档都提醒:第三方包和技能要审查来源。
拦:
- .env
- .git/
- node_modules/
- 系统目录
- SSH / 凭据目录
拦:
- rm -rf
- sudo
- curl ... | sh
- 高风险网络下载执行
- 改 shell profile
对于: - 部署 - 发工单 - 发消息 - 改线上配置 - 删资源
最好做“确认 + 审计日志”。
对 pi install 的 npm/git 包做来源审核,至少:
- 自己维护 allowlist
- 生产环境禁用随意安装
- 团队共享只用固定版本
tool_callctx.ui.confirm()靠 prompt 写一句“不要做危险操作”几乎没什么意义。 真正有效的是: - extension block - tool surface 收敛 - 宿主层权限隔离 - 最小密钥暴露
很多 Agent 项目都忽略 observability,最后只能翻 JSONL。
AgentSession.subscribe() 已经给了你足够的事件流。
agent_start / agent_endturn_start / turn_endmessage_start / message_endtool_execution_start / tool_execution_endauto_compaction_start / auto_compaction_endauto_retry_start / auto_retry_endsession.subscribe((event) => {
const now = new Date().toISOString();
console.log(JSON.stringify({ now, eventType: event.type, event }));
});
实际生产建议写到结构化日志系统,而不是 console.log。
/debug官方开发文档提到有隐藏命令 /debug,会把渲染行和发送给 LLM 的消息写入 ~/.pi/agent/pi-debug.log。这对调试很有帮助,但也意味着:
- 里面可能有敏感上下文
- 生产环境要明确处理策略
官方仓库自身有大量测试,覆盖 session、extensions、skills、rpc、compaction 等。
你自己的 Agent 项目建议至少做四层测试:
测: - prompt 生成器 - resource loader 封装 - 工具输入输出转换 - 日志格式化
重点测:
- tool_call 是否正确 block
- appendEntry 状态恢复逻辑
- session tree / fork 后状态是否正确
用 SessionManager.inMemory() + SettingsManager.inMemory() 进行:
- prompt → tool call → response 的闭环验证
- custom tool 注册验证
- event 发射顺序验证
如果你做 CLI/RPC/Web 宿主,需要测: - 消息流 - 中途 steer / followUp - 会话恢复 - 模型切换 - 错误恢复
如果你的 Agent 只给自己用,SDK 直接跑即可。 如果要给团队或客户用,至少有三种分发方式。
适合: - 内部系统 - Web 服务 - 本地桌面壳
根据 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
分发。
开发文档提到 package.json 里的 piConfig:
{
"piConfig": {
"name": "pi",
"configDir": ".pi"
}
}
如果你真的要做品牌化 CLI:
- 改 piConfig.name
- 改 configDir
- 改 bin
这样会影响: - CLI 名称 - 配置目录 - 环境变量名 - banner
这是底线。
AuthStorage官方 12-full-control.ts 示例:
const customAuth = AuthStorage.create("/tmp/my-agent/auth.json");
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
这个思路很好: - key 可运行时注入 - 不必持久化 - 宿主可控
一个 Agent 能不能长期活下去,DX 比“多聪明”更重要。
examples/ 目录演示典型流程至少写:
- README.md:你的 Agent 是什么
- ARCHITECTURE.md:system prompt / skill / extension / session 分工
- SECURITY.md:哪些行为被阻止,哪些需要审批
- OPERATIONS.md:如何调试、如何看日志、如何恢复 session
别让新人去猜“为什么这件事写在 skill 里,不写在 extension 里”。 把分层原则写清楚,能少踩很多坑。
readTool 等全局工具后果:路径解析错位。
正确: 用 createReadTool(cwd) / createCodingTools(cwd)。
后果:提示膨胀,难维护,且很多约束实际无法强制执行。
正确: system prompt 管原则,extension 管执行,skill 管任务流程。
后果:模型不触发或触发后也没法执行。
正确: description 精准,步骤明确,可操作。
后果:session 切换、fork、tree 导航后状态丢失。
正确: 用 tool result details 或 appendEntry() 做 session 绑定状态。
后果:成本炸,响应慢。
正确: 按任务类型路由模型。
后果:问题定位时只能读长 JSONL。
正确: 事件流日志化、成本统计、工具耗时统计。
pi install 第三方包后果:供应链与权限风险。
正确: 审核、固定版本、白名单。
bash后果:不可控、不可审计、难回滚。
正确: 关键业务操作优先做结构化 custom tool。
后果:长任务越来越不稳定,最后上下文爆炸。
正确: 设计摘要策略与 handoff 机制。
后果:不会利用 /tree、/fork、branch summary 的价值。
正确: 把 session 当任务工作树来设计。
目标:让 SDK session 能工作
交付:
- createAgentSession() 启动脚本
- 默认工具
- 模型认证
- 最小事件打印
验收: - 能 prompt - 能看流式输出 - 能执行 read/bash
目标:让 Agent 不再是裸 pi
交付:
- systemPromptOverride
- 1~2 个 skills
- 2~3 个 prompt templates
- 1 个安全 extension
验收: - 进入项目后能按你的规则工作 - 对高风险操作能拦截 - 高频任务有快捷入口
目标:让 Agent 真能帮你干活
交付: - 3~5 个 custom tools - 事件日志 - session naming / labels - compaction 策略
验收: - 能访问业务系统或项目元数据 - 长任务可恢复 - 工具调用有审计
目标:可给团队或客户使用
交付: - package 分发或宿主应用 - 文档与测试 - 模型路由策略 - 安全策略 - 监控与成本统计
验收: - 新人可安装使用 - 关键能力可测试 - 问题可定位 - 成本可控制
下面给出一个更贴近真实项目的装配代码。
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 宿主
如果你打算基于源码继续深挖,建议优先读这些官方示例:
examples/sdk/01-minimal.ts:最小入口04-skills.ts:skill 装配05-tools.ts:工具选择与 custom cwd 注意事项06-extensions.ts:扩展注册07-context-files.ts:AGENTS.md 注入10-settings.ts:settings override 与 flush11-sessions.ts:session 继续、列举、打开12-full-control.ts:完全自管装配protected-paths.ts:保护敏感路径confirm-destructive.ts:危险操作确认todo.ts:状态持久化与分支恢复tools.ts:工具启停与 custom entrydynamic-tools.ts:动态注册工具custom-compaction.ts:自定义压缩qna.ts:定制交互 UIhandoff.ts:上下文交接到新 sessionsession-name.ts:会话命名如果你时间有限,先把这些读透,已经足够做出像样的个人 Agent。
如果只保留最核心的建议,我会给这 10 条:
@mariozechner/pi-coding-agentcreateAgentSession()DefaultResourceLoader 工厂systemPromptOverrideSessionManager.create() 做持久化/tree / fork / resume 思路完成这四天,你的 Agent 已经不是 demo,而是一个够用的个人定制开发 Agent。
经过围绕以下 20 个维度的审校与修订:
最终建议是:
如果你的目标是“在一个可靠的 coding harness 之上,做出自己的个人开发 Agent”,pi-coding-agent 是非常合适的底座。
它最适合的做法不是“魔改内核”,而是:
createAgentSession() 做会话核心DefaultResourceLoader 做资源装配extensions 做能力与约束skills 做任务说明书prompts 做高频入口sessions 做长期工作树packages 做分发这条路线既贴合官方设计,也最容易维护。
badlogic/pi-mono 根 READMEpackages/coding-agent/README.mdpackages/coding-agent/docs/sdk.mdpackages/coding-agent/docs/extensions.mdpackages/coding-agent/docs/skills.mdpackages/coding-agent/docs/prompt-templates.mdpackages/coding-agent/docs/packages.mdpackages/coding-agent/docs/settings.mdpackages/coding-agent/docs/session.mdpackages/coding-agent/docs/rpc.mdpackages/coding-agent/docs/development.mdpackages/coding-agent/examples/sdk/*packages/coding-agent/examples/extensions/*如需继续扩写,下一步最值得补的是: - 一个完整的 Web 宿主集成示例 - 一个真正的团队级业务工具包示例 - 一个从本地 CLI 到团队 package 发布的完整样板仓库
下方为该报告的 20 轮审校摘要。
目标文件:/home/horace/.openclaw/workspace/reports/pi-coding-agent-custom-agent-report.md
说明:以下为 20 轮针对不同质量维度的复核/修订摘要。每轮都基于官方源码与文档内容做校正、补强或重组,最终结果已合并进报告文件。
pi-coding-agent 在 pi-mono 中的实际定位:CLI + SDK + RPC + 扩展/技能系统,而非通用“全栈 agent 平台”。read、write、edit、bash。createAgentSession() 的主要参数、SessionManager、SettingsManager、AuthStorage、ModelRegistry 的角色。pi-ai / pi-agent-core / pi-coding-agent / pi-tui 的职责分层图。>=20.6.0。/login 两类认证入口。app/config/agent/infra/ui 目录。DefaultResourceLoader 是装配中枢,而不是只会把资源扔进默认目录。extensionsOverride、skillsOverride、agentsFilesOverride、systemPromptOverride 等可扩展点。codingTools、readOnlyTools、grep、find、ls 等。createReadTool(cwd) / createCodingTools(cwd) 工厂函数” 的关键踩坑提示。AGENTS.md、skills、session、自定义 entry 的职责边界。/tree、/fork、/resume、/compact 的实际价值。tool_call + UI confirm + 审计日志三层实现路径替代“只靠 prompt 约束”。session.subscribe() 的事件观测建议。/debug 的调试价值与敏感信息风险。piConfig 对 CLI 名称/配置目录/bin 的影响。AuthStorage 的用途与 runtime API key 注入方式。README.md、ARCHITECTURE.md、SECURITY.md、OPERATIONS.md。/home/horace/.openclaw/workspace/reports/pi-coding-agent-custom-agent-report.md/home/horace/.openclaw/workspace/reports/pi-coding-agent-custom-agent-review-log.md