Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

pi-atelier:让 AI 编程助手变得专业

从零教会你使用 pi-atelier 扩展生态,让 AI 编程助手从「会写代码」进化到「会管理项目」

这本书是给谁看的?

如果你在做以下任何一件事,这本书适合你:

  • 用 AI 编程助手(如 pi、Cursor、Copilot)写代码
  • 觉得 AI 助手“差一点“就能做得更好
  • 想让 AI 从“问答工具“进化成“项目搭档“

什么是 pi-atelier?

pi-atelier 是一组 pi 扩展,让 AI 编程助手具备项目管理能力。

普通 AI 助手能写代码,但:

  • 每次开会话都忘光之前的事
  • 做大任务容易跑偏
  • 没有规矩,容易犯低级错误
  • 会话一长就变笨

pi-atelier 的扩展补上了这些能力缺口:

🧠 记忆

让 AI 记住跨会话的知识

📋 规划

管理 Epic → Story → Task 三层路线图

🛡️ 守卫

给 AI 立规矩,防止犯错

🔍 诊断

控制上下文质量 + token 消耗分析

📊 分析

搜索和回溯历史会话

🗜️ 压缩

长会话中保持 AI 的聪明

详细对比见下表:

能力扩展一句话描述
记忆pi-memory让 AI 记住跨会话的知识
规划pi-roadmap让 AI 管理 Epic → Story → Task
守卫pi-shepherd给 AI 立规矩,防止犯错
上下文与诊断pi-context-manager控制 AI 看到的信息质量 + token 消耗诊断
日志pi-journal日志报告生成(git 活动 + 会话事件 + 记忆变更)
分析pi-session-analyzer搜索和回溯历史会话
压缩pi-smart-compact长会话中保持 AI 的聪明
定时pi-scheduler定时提醒和周期任务
工作流pi-workflow子代理编排,并行执行
工具库pi-shared-utils扩展开发的公共工具函数

阅读路线

快速上手路线(1 小时)

  1. 第一章:一个 AI 的记忆 → 5 分钟装上 pi-memory
  2. 第二章:从记忆到规划 → 学会用路线图管理任务
  3. 第七章:自己动手做扩展 → 了解扩展机制

全面了解路线(3 小时)

按顺序读完所有章节。每章包含:

  • 痛点:你一定会遇到的真实问题
  • 原理:扩展是怎么工作的
  • 案例:真实的使用场景
  • 最佳实践:怎么用得更好

按需查阅路线

遇到具体问题时,直接翻到对应章节。每章独立完整。

快速安装

所有扩展包已发布到 npm,在 pi 的 settings.json 中添加你需要的扩展:

推荐组合(日常开发)

{
  "packages": [
    "npm:pi-shepherd",
    "npm:pi-ate-memory",
    "npm:pi-roadmap",
    "npm:pi-ate-smart-compact"
  ]
}

全量安装

{
  "packages": [
    "npm:pi-shepherd",
    "npm:pi-ate-memory",
    "npm:pi-roadmap",
    "npm:pi-context-manager",
    "npm:pi-session-analyzer",
    "npm:pi-ate-smart-compact",
    "npm:pi-ate-scheduler",
    "npm:pi-ate-workflow",
    "npm:@pi-atelier/shared-utils",
    "npm:pi-usage-stats",
    "npm:pi-journal"
  ]
}

大部分扩展都是 开箱即用——安装后无需额外配置(但你可以按需定制)。

💡 提示:pi-ate-workflow 和 @pi-atelier/shared-utils 是供其他扩展调用的开发库,一般用户不需要直接安装。

💡 安装方式npm: 前缀表示从 npm registry 安装。也可以用 git:github.com/catlain/pi-<name> 从源码安装(适合开发者)。

包名速查

功能npm 包名源码仓库
守卫pi-shepherdcatlain/pi-shepherd
记忆pi-ate-memorycatlain/pi-memory
规划pi-roadmapcatlain/pi-roadmap
上下文与诊断pi-context-managercatlain/pi-context-manager
会话分析pi-session-analyzercatlain/pi-session-analyzer
智能压缩pi-ate-smart-compactcatlain/pi-smart-compact
定时任务pi-ate-schedulercatlain/pi-scheduler
工作流pi-ate-workflowcatlain/pi-workflow
日志报告pi-journalcatlain/pi-journal
使用统计pi-usage-statscatlain/pi-usage-stats
工具库@pi-atelier/shared-utilscatlain/pi-shared-utils

重要文件路径

在开始之前,你需要知道 pi 的关键文件在哪里:

文件路径说明
全局配置~/.pi/agent/settings.json安装扩展、配置 provider
项目配置.pi/settings.json(项目根目录)项目级自定义配置(覆盖全局)
项目指令AGENTS.md(项目根目录或 .pi/agent/注入给 AI 的项目规则
扩展安装目录~/.pi/agent/npm/node_modules/npm 包安装位置
记忆目录.pi/memory/(项目级)项目级持久记忆
全局记忆~/.pi/agent/memory/跨项目通用记忆

💡 新手提示~ 指你的用户主目录。在 macOS/Linux 上是 /home/你的用户名/,Windows 上是 C:\Users\你的用户名\

约定

本书中的示例使用以下约定:

  • 代码块:命令、文件路径、代码片段
  • 粗体:重要概念
  • 💡 提示:实用技巧和注意事项

  • 表格:快速对比和速查

准备好开始了吗?翻开第一章,让我们从“记忆“开始。

一个 AI 的记忆

你可能遇到过这种情况

你正在用 AI 编程助手开发一个项目。第一天的会话里,你花了半小时向 AI 解释:

  • 项目用的是 Rust + Axum 技术栈
  • 数据库用的 DuckDB 而不是 PostgreSQL
  • 认证模块用的 JWT,不是 Session
  • 部署目标是 ARM 架构的嵌入式设备

AI 听懂了,帮你写了完美的代码。

第二天,你开了一个新会话,AI 全忘了。它又开始建议你用 Express.js、连 PostgreSQL、用 Session 认证……

💡 这就是 AI 的“金鱼记忆“问题:每次新会话都是一张白纸。

记忆:让 AI 拥有跨会话的知识

pi-memory 就是来解决这个问题的。它给 AI 装上了一个“笔记本“:

  • 自动记录:AI 在会话中做出的架构决策、踩过的坑、达成的共识
  • 自动加载:每次新会话开始时,AI 自动读取之前的关键知识
  • 按项目隔离:不同项目的记忆互不干扰

工作原理

┌─────────────────────────────────────────┐
│              AI 会话                      │
│                                          │
│  ┌──────────┐     ┌──────────────────┐  │
│  │ 用户对话  │ ──→ │ AI 自动提取知识点  │  │
│  └──────────┘     └────────┬─────────┘  │
│                            │             │
│                            ▼             │
│                   ┌─────────────────┐    │
│                   │  memory_update   │    │
│                   │  写入记忆文件     │    │
│                   └─────────────────┘    │
│                                          │
│  ┌──────────┐     ┌──────────────────┐  │
│  │ 新会话    │ ──→ │ before_agent_start│  │
│  │ 开始      │     │ 自动加载记忆索引   │  │
│  └──────────┘     └──────────────────┘  │
└─────────────────────────────────────────┘

自动注入:每轮对话自动加载记忆索引

pi-memory 在每个会话开始时(before_agent_start 事件)会自动执行以下操作:

  1. 读取 memory-prompt.md — 记忆系统的使用说明(告诉 AI 有记忆功能、文件在哪)
  2. 读取 MEMORY.md 索引 — 全局 ~/.pi/agent/memory/MEMORY.md + 项目 .pi/memory/MEMORY.md
  3. 注入到 system prompt — AI 在每轮对话开头就能看到所有记忆的标题和关键词

这意味着 AI 不需要主动去“查“记忆——记忆索引已经在它的上下文里了。AI 看到 JS_replace_$陷阱 这样的标题,就知道有这条记忆存在,需要时用 read 工具读取完整内容。

⚠️ 注入的是索引,不是全部内容。MEMORY.md 里只有标题和关键词,不是完整的记忆内容。AI 需要 read 具体文件才能获取细节。

记忆的核心结构:

组件作用
MEMORY.md索引文件,列出所有记忆的标题和关键词(自动注入到每轮对话)
记忆文件(.md每个文件一个主题,包含具体知识点(按需读取)
memory_update 工具AI 用来写入/更新记忆文件 + 自动更新 MEMORY.md 索引
memory_index 工具AI 用来查看已有记忆(手动查询)
before_agent_start 钩子会话开始时自动注入记忆索引到 system prompt

记忆文件的格式

每个记忆文件遵循统一格式:

# 标题

关键词:`kw1` `kw2` `kw3` ...

## 具体内容

- 知识点 1
- 知识点 2
- 决策记录:为什么选择了方案 A 而不是方案 B

文件命名规则:主题--关键词1,关键词2,关键词3.md

例如:数据库选型--DuckDB,嵌入式,ARM,列存,分析查询.md

实际案例:新项目的第一周

让我们看一个真实的场景。假设你在开发一个数据分析工具:

第 1 天:项目初始化

你和 AI 讨论了技术选型,决定用 Python + FastAPI + DuckDB。会话结束时,AI 自动写入记忆:

# 技术栈决策

关键词:`Python` `FastAPI` `DuckDB` `技术选型`

## 选择理由

- FastAPI:异步支持好,自动生成 API 文档
- DuckDB:嵌入式分析数据库,无需单独部署,适合单机分析场景
  - 不选 PostgreSQL:项目不需要并发写入,嵌入式更简单
- Python 3.12+:用了新的类型语法

第 3 天:踩了一个坑

你在 DuckDB 的日期处理上遇到了问题——默认时区是 UTC,但你的用户在中国。AI 帮你解决后,写了一条记忆:

# DuckDB时区问题

关键词:`DuckDB` `时区` `日期` `UTC` `Asia/Shanghai`

## 问题和解决方案

DuckDB 默认用 UTC 时区。查询 `SELECT NOW()` 返回 UTC 时间。

解决:在连接时设置 `SET timezone = 'Asia/Shanghai'`

注意:不要在 SQL 层面做时区转换,在 Python 层用 datetime 处理更可靠。

第 7 天:新会话,无需重复解释

你开了一个新会话说“给分析接口加个导出 CSV 的功能“。AI 已经知道:

  • 项目用 FastAPI + DuckDB
  • 时区用 Asia/Shanghai
  • 数据库查询在 Python 层处理

不需要你再说一遍。

这就是记忆的价值:省去了每次重复解释的 30 分钟。

最佳实践

✅ 应该记住的内容

  • 技术决策:为什么选了 A 而不是 B
  • 踩过的坑:遇到的问题和解决方案
  • 项目约定:命名规范、目录结构、部署方式
  • 架构知识:模块关系、数据流、关键接口

❌ 不应该记住的内容

  • 临时调试信息(“这个变量值是 42”)
  • 已经过时的结论(记得定期清理)
  • 通用编程知识(AI 本来就知道怎么写 for 循环)

记忆文件管理

记忆不是越多越好。系统设有多层保护机制:

阈值行为
20 个提示注意控制数量
25 个警告接近上限,建议清理合并
40 个硬拒绝写入,必须先清理

清理方法:

  1. 合并:同主题的多文件合并为一个
  2. 归档:过时的结论被新结论取代,删掉旧的
  3. 拆分:单文件超过 200 行时,按子主题拆分

此外,记忆系统还有冲突检测——如果新文件与已有记忆的 topic 相同或关键词重叠 3 个以上,会直接拒绝写入,强制先处理冲突(合并或覆盖)。

配置

pi-memory 通过 pi 的 settings.json 安装:

{
  "packages": [
    "pi-memory"
  ]
}

无需额外配置,安装即用。记忆文件存储在项目的 .pi/memory/ 目录下。

记忆的作用域

作用域路径用途
项目级.pi/memory/项目特有的知识(架构、决策、坑)
全局~/.pi/agent/memory/跨项目通用的知识(工具链、编码纪律)

进阶场景:记忆清理与知识演化

场景:记忆碎片化了

用了一个月,记忆文件越来越多:

.pi/memory/
├── 数据库选型--DuckDB,嵌入式,ARM.md
├── DB选型--数据库,DuckDB,性能.md       ← 跟上一个重复!
├── 部署问题--Docker,ARM,内存.md
├── 部署问题2--Docker,内存,OOM.md      ← 跟上一个也重复!
├── auth-bug--JWT,过期,refresh.md
├── fastapi-cors--CORS,FastAPI,跨域.md
├── test-tricks--vitest,mock,测试.md
└── ... (还有 20 个文件)

AI 在每次写入前会自动检查(memory_index),如果发现同主题的已有记忆,会先合并再写入。但如果碎片化已经发生,需要手动清理。

清理步骤

  1. 让 AI 执行 memory_index,查看当前所有记忆
  2. 标记同主题的文件(关键词 3+ 重叠)
  3. 让 AI 读取标记的文件,合并为一个
  4. memory_update 覆盖写入到已有文件的文件名(而不是新建文件名,否则会触发冲突检测被拒绝)
  5. 删除旧的碎片文件

场景:旧结论被推翻了

上个月写的记忆说“用 Express.js 做后端“,但这个月项目决定迁移到 FastAPI。每次 AI 读到旧记忆都会用 Express 的思路,产生错误的建议。

处理方式:用 memory_update 覆盖旧文件,写入新结论:

# 后端框架选型

关键词:`FastAPI` `Python` `迁移`

## 当前决策

2026-05: 从 Express.js 迁移到 FastAPI。

## 迁移原因

- 需要更好的异步支持
- Python 生态的数据分析库更丰富
- Express.js 版本已归档,不再更新

> ⚠️ 旧结论(已废弃):使用 Express.js + TypeScript

关键:不要只删旧文件——把新结论和旧结论的关系写清楚,这样 AI 不会在别的上下文里又“发明“出旧的方案。

场景:跨项目的知识复用

你在项目 A 里踩了一个坑,想确保项目 B 不会重蹈覆辙。

方案:把通用知识写到全局记忆:

~/.pi/agent/memory/
└── npm-file引用陷阱--npm,file引用,node_modules,缓存.md

全局记忆对所有项目可见。这样不管在哪个项目里,AI 都会知道“npm file: 引用有缓存陷阱“。

原则

  • 项目特有的知识(这个项目的架构、约定)→ 项目级 .pi/memory/
  • 通用经验(工具链踩坑、编码纪律)→ 全局 ~/.pi/agent/memory/

下一步

现在 AI 有了记忆,能记住过去的知识。但面对一个大项目,它知道第一步该做什么、第二步该做什么吗?

下一章,我们来看如何让 AI 学会规划

从记忆到规划

你可能遇到过这种情况

你让 AI 做一个“把项目从 JavaScript 迁移到 TypeScript“的大任务。

前 30 分钟一切顺利——AI 按计划迁移了配置文件、类型定义、核心模块。但到第 5 个文件的时候,AI 开始“跑偏“:

  • 它忘了之前的约定,开始用不同的命名风格
  • 它跳过了测试文件的迁移
  • 它开始做一个你没要求的“顺便重构“
  • 当你提醒它回到正轨,它已经不记得原计划的前 3 步是什么了

💡 金鱼记忆解决了,但还有“注意力涣散“问题:AI 记得住知识,但管不住任务。

路线图:让 AI 学会管理复杂任务

pi-roadmap 给 AI 装上了“项目管理大脑“:

  • 结构化拆解:把大目标拆成 Epic → Story → Task 三层
  • 进度追踪:每个任务有明确的状态(todo / doing / done / blocked)
  • 持久化:路线图保存在文件中,新会话也能继续推进
  • 优先级排序:自动推荐下一步该做什么

为什么是三层结构?

Epic(大方向)
 └── Story(可交付的工作块)
      └── Task(最小执行单元)

这个结构来自敏捷开发的实践,但做了一点适配:

概念传统敏捷pi-roadmap理由
Epic2-8 周一个完整项目方向AI 会话不会持续几周
Story1-3 天1-3 个会话可完成适配 AI 的工作节奏
Task0.5-1 天30 分钟 - 2 小时AI 单次能专注的粒度

工作原理

用户描述目标
     │
     ▼
┌──────────────────────────────────┐
│         roadmap_plan             │
│  AI 分析目标 → 拆解为三层结构      │
│  对比已有路线图 → 增量更新          │
└──────────────┬───────────────────┘
               │
               ▼
┌──────────────────────────────────┐
│  ~/.pi/roadmap/<id>.roadmap.json │
│  全局存储,跨会话跨项目可用         │
│  + 项目级 .pi/roadmap/roadmap.json│
│    (自动同步派生)                │
└──────────────┬───────────────────┘
               │
     ┌─────────┼─────────┐
     ▼         ▼         ▼
 roadmap_list  roadmap_show  roadmap_next
  查看所有     查看详情     推荐下一步

实际案例:迁移一个多包项目

让我们看一个真实的场景——把 12 个 npm 包同时做文档升级:

步骤 1:创建路线图

你说:“帮我规划一下给所有包写文档的工作。”

AI 调用 roadmap_plan,自动拆解:

{
  "roadmapId": "package-docs",
  "title": "包文档升级",
  "epics": [
    {
      "id": "E0",
      "title": "制定模板并验证",
      "stories": [
        {
          "id": "E0.S0",
          "title": "分析最佳实践,提炼模板",
          "tasks": [
            { "id": "E0.S0.T0", "title": "调研 GitHub 文档规范" },
            { "id": "E0.S0.T1", "title": "提炼 README 模板" },
            { "id": "E0.S0.T2", "title": "用第一个包验证模板" }
          ]
        }
      ]
    },
    {
      "id": "E1",
      "title": "批量升级所有包",
      "stories": [
        { "id": "E1.S0", "title": "核心扩展(4个包)" },
        { "id": "E1.S1", "title": "工具扩展(4个包)" },
        { "id": "E1.S2", "title": "辅助扩展(4个包)" }
      ]
    }
  ]
}

步骤 2:按计划推进

每次新会话,你说“继续“。AI 调用 roadmap_next

📊 推荐下一步任务:

E0.S0.T1 — 提炼 README 模板(high priority)
  所属:E0 制定模板并验证 > S0 分析最佳实践

是否开始?

步骤 3:完成标记

AI 完成一个任务后,调用 roadmap_done

✅ E0.S0.T1 已完成
   产出:templates/README-template.md

步骤 4:遇到阻碍

如果某个包的源码缺失,AI 可以标记任务为 blocked:

⚠️ E1.S1.T3 已阻塞
   原因:pi-journal 的 API 文档不完整,需要先补充源码注释

路线图 vs 记忆:什么关系?

维度记忆(pi-memory)路线图(pi-roadmap)
存什么知识、决策、踩坑任务、进度、计划
粒度自由文本结构化 JSON
查询方式关键词状态/优先级
生命周期长期保留项目结束可归档

简单来说:

  • 记忆是“我知道什么“
  • 路线图是“我要做什么、做到哪了“

两者互补:记忆帮 AI 不忘知识,路线图帮 AI 不忘任务。

最佳实践

✅ 好的 Epic 拆解

Epic: 发布 npm 包
  Story: 准备发布环境
    Task: 配置 package.json 的 exports 字段
    Task: 添加 bundledDependencies 配置
    Task: 配置 tsconfig 的 declaration 输出
  Story: 编写文档
    Task: 完成 README.md
    Task: 添加 CHANGELOG.md

❌ 不好的拆解

Epic: 把所有事情做完        ← 太模糊,没有方向
  Story: 做第一步            ← 没说做什么
    Task: 开始干活            ← 无法执行

拆解的黄金法则

  1. Epic 的标题应该是一个动词短语:“发布 npm 包“而不是“npm”
  2. Story 应该有明确的交付物:“完成 README“而不是“写文档”
  3. Task 应该 30 分钟内可执行:“配置 package.json 的 name 字段“而不是“配置构建”
  4. 同层级的项应该是同粒度:不要一个 Story 有 2 个 Task,另一个有 20 个

进阶场景:计划调整与进度追踪

场景:需要改变方向

计划赶不上变化。昨天规划好的路线图,今天发现需求变了。你不需要重新建一个——用 roadmap_plan 更新即可:

你:昨天规划的重构方案太大了,我想先只做认证模块。

AI 调用 roadmap_plan(action="update"):
  → 对比当前路线图和你的新需求
  → 保留已完成的任务不动
  → 标记不需要的任务为 dropped
  → 添加新的任务

关键原则roadmap_plan 是增量更新,不是覆盖。已经 done 的任务永远不会被改回去。

场景:追踪谁做了什么

在多会话协作中,你经常想知道“这个 task 是哪个会话完成的?“Roadmap 自动追踪:

roadmap_show(roadmapId="package-docs")

结果:
  E0.S0.T0 调研 GitHub 文档规范 ✅ by: 8740-8fce3e7af232
  E0.S0.T1 提炼 README 模板 ✅ by: b8b5-85516ead6253
  E0.S0.T2 用第一个包验证模板 ✅ by: b8b5-85516ead6253
  E1.S0.T0 核心扩展 - pi-shepherd 🔄 doing by: aa55-a4860e851afb

每个完成的 task 后面的 by: xxxx-xxxxxxxxxxxx 是会话 ID 的短格式(UUID 最后两段)。你可以用这个 ID 搜索到具体的会话:

session_search(action="grep", query="8740-8fce3e7af232")

→ 找到该会话,然后用 session_analyze(action="summary") 查看详情

场景:归档已完成的 Epic

项目做完了,不想让已完成的 Epic 占据视线:

roadmap_archive(roadmapId="package-docs")

→ 自动归档所有已完成的 Epic
→ 默认隐藏,需要时用 show_archived=true 查看

场景:不知道下一步做什么

打开 pi 时不知道该继续什么:

roadmap_next()

结果:
  📊 推荐下一步任务(按优先级排序):
  
  1. E1.S0.T3 — 配置 package.json 的 files 白名单 (high, todo)
  2. E1.S1.T0 — 工具扩展 - pi-roadmap (medium, todo)
  3. E2.S0.T0 — 调研 mdBook 主题定制 (low, todo)

roadmap_next 自动按 doing → todo、high → medium → low 排序,告诉你最该做什么。

进阶功能

当路线图越来越多、结构越来越复杂时,你可能想快速找到某个关键词相关的任务。roadmap_search 支持跨路线图全文搜索,匹配 title + description + note:

你:之前有没有规划过 CI 相关的任务?

AI 调用 roadmap_search(query="CI")

→ 在 3 个路线图中找到 7 个匹配项,显示完整层级上下文

任务依赖关系:dependsOn

复杂项目中,任务之间往往有先后顺序——B 必须等 A 做完才能开始。dependsOn 让你在 Story 和 Task 上声明依赖:

{
  "id": "E1.S2.T1",
  "title": "配置 CI 流水线",
  "status": "todo",
  "dependsOn": ["E1.S1.T3"]
}

roadmap_show 会自动展示依赖关系:

E1.S2 发布流程
  E1.S2.T1 配置 CI 流水线 📋 depends: [E1.S1.T3]
  E1.S2.T2 发布到 npm registry 📋 depends: [E1.S2.T1]

📖 依赖的添加和更新方式,见 pi-roadmap README

保护机制

  • 禁止向已归档/已完成的 Epic/Story 添加子项——避免在已完成的工作下追加任务
  • 重复 ID 检查——roadmap_update 会检查 dependsOn 中的 ID 是否存在

下一步

AI 有了记忆(记住知识)和路线图(管理任务)。但有时候,AI 还是会“犯错“——改不该改的文件、用不该用的方式。

下一章,我们来看如何给 AI 立规矩

给 AI 立规矩

你可能遇到过这种情况

你让 AI 帮你“修一下登录页面的样式“。30 秒后你看了一眼代码–

AI 不但改了样式,还:

  • “顺手“重构了整个登录组件的目录结构
  • 把 CSS 模块改成了 Tailwind(你项目没用 Tailwind)
  • 删除了 3 个它认为“没用“的测试文件
  • 把 package.json 里的依赖升级到了最新版

等你发现的时候,代码已经提交了。

💡 AI 越能干,越需要规矩。能力强但没有边界,造成的破坏反而更大。

两道防线:Shepherd 和 Context

pi-atelier 提供了两层防护机制:

第一道:pi-shepherd - 行为守卫

Shepherd(牧羊犬)是一个规则驱动的事件钩子引擎,在 AI 的关键动作前后做检查,相当于“门卫“。

AI 准备执行动作(工具调用)
     │
     ▼
┌──────────────────────────────┐
│     Shepherd tool_call 钩子   │
│   检查:该不该做?该怎么做?    │
└──────┬───────────────────────┘
       │
   ┌───┴────┐
   │        │
  放行     改写/阻止 + 提示原因

... 工具执行完毕 ...

┌──────────────────────────────┐
│    Shepherd tool_result 钩子  │
│   检查:需要后续动作吗?       │
└──────┬───────────────────────┘
       │
   注入提醒 / 追加动作

支持的钩子时机:

钩子触发时机典型用途
tool_callAI 调用工具改写命令、阻止危险操作
tool_result工具执行自动提醒跑测试、lint 检查
message_endAI 回复完成匹配回复文本,拦截错误猜测
agent_endAI 完成对话提醒提交代码、更新记忆
session_shutdown会话关闭清理临时数据

Shepherd 的四种动作:

动作效果典型用途
block阻止工具执行禁止危险操作
notify向 AI 上下文注入提醒“编辑了 TS 文件,记得跑测试”
steer静默注入引导(不显示给用户)引导 AI 查阅规范
rewrite改写工具调用参数自动给命令加前缀

第二道:pi-context-manager - 信息质量与诊断

Context Manager(上下文管家)控制 AI 能看到什么信息,还能帮你诊断 token 消耗问题。

核心能力:

  • Distill(蒸馏):自动压缩工具返回的大段内容,保留关键信息
  • Tool Result Processor(后处理器):对特定工具输出做格式化精简
  • Aging(老化淘汰):自动淘汰长期未被引用的旧工具输出
  • Payload 分析:用数据诊断 token 都花在哪了
工具返回大量内容(可能 50KB)
     │
     ▼
┌────────────────────────┐
│   Context Manager       │
│   Distill + Processor   │
│   压缩到 ~5KB 关键信息   │
└────────────────────────┘
     │
     ▼
AI 看到精炼后的信息,做出更好的判断

详细原理见 3.3 Context Manager 原理

实际案例:防止 AI 犯错

场景 1:编辑后自动提醒跑测试

{
	"comment": "[TypeScript] 编辑后必须跑测试",
	"hook": "tool_result",
	"tool": "edit",
	"action": "notify",
	"conditions": [
		{ "field": "path", "pattern": "\\.ts$", "flags": "" }
	],
	"reason": "编辑了 TypeScript 文件,必须跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
	"enabled": true
}

当 AI 编辑了 .ts 文件后,Shepherd 自动提醒 AI 跑测试。

场景 2:会话结束提醒提交代码

{
	"comment": "[收尾] 编辑后提醒 commit + 记忆更新 + 总结",
	"hook": "agent_end",
	"action": "notify",
	"conditions": [{ "builtin": "has_edits" }],
	"reason": "检测到文件编辑,执行收尾工作:\n1️⃣ Git commit...\n2️⃣ 更新记忆...\n3️⃣ 会话总结",
	"stopReason": ["stop"],
	"enabled": true
}

conditions: [{ builtin: "has_edits" }] 表示只有本轮会话确实编辑了文件才触发。stopReason: ["stop"] 表示只在 AI 正常结束(而非被中断)时触发。

场景 3:自动改写命令

{
	"comment": "[rtk] 自动代理高频 bash 命令",
	"tool": "bash",
	"action": "rewrite",
	"pattern": "^(git\\s+(status|log|diff)|cargo\\s+(test|build|clippy)|pytest)\\b",
	"flags": "",
	"reason": "rtk command rewrite:自动加 rtk 前缀压缩输出",
	"enabled": true
}

当 AI 尝试执行 git status 等命令时,Shepherd 自动改写为 rtk git status(rtk 是一个输出压缩工具)。

场景 4:代码风格检查

{
	"comment": "[TS] 禁止空格缩进 - TS 文件必须用 Tab",
	"hook": "tool_call",
	"tool": "edit",
	"action": "notify",
	"conditions": [
		{ "field": "path", "pattern": "\\.ts$", "flags": "" },
		{ "field": "text", "pattern": "\\n  [\\S ]", "flags": "" }
	],
	"reason": "❌ TS 文件要求 Tab 缩进,不是空格。请用 Tab 缩进重写代码。",
	"enabled": true
}

两个条件同时满足才触发:文件是 .ts 且代码内容包含空格缩进。

场景 5:连续出错时提醒翻记忆

{
	"comment": "[debug] 工具反复出错时提醒翻记忆",
	"hook": "tool_result",
	"action": "steer",
	"state": { "countKind": "errors", "gte": 5 },
	"reason": "🔍 **工具反复出错**:连续失败多次,翻看 .pi/memory/ 目录下的记忆文件,看是否已有踩坑记录。",
	"enabled": true,
	"subagent": false
}

state 实现了状态追踪–Shepherd 会记住工具出错的次数,累计到阈值才触发。subagent: false 表示子代理中不触发此规则。

Shepherd 规则管理

规则可以通过两种方式管理:

  • shepherd_rules 工具:让 AI 帮你安全地增删改规则,内置写入校验和回滚机制
  • 直接编辑 JSON 文件:全局规则在 ~/.pi/agent/extensions/shepherd/rules.json,项目规则在 .pi/extensions/shepherd-rules.json

规则文件修改后即时生效,无需重启。

📖 完整的字段说明和参数文档,见 pi-shepherd README

典型用法

# 让 AI 帮你添加一条规则
你:帮我加一条规则,编辑 .rs 文件后提醒跑 cargo clippy

AI 调用 shepherd_rules(action="add", rule={...})

# 查看项目级规则
AI 调用 shepherd_rules(action="list", scope="project")

Context 的配置方式

pi-context-manager 提供以下命令:

命令用途
/record [on|off]开关 payload 录制
/contextTUI 面板:可视化上下文使用情况
/distill-config [N]查看/设置 distill token 阈值
/distill-config --cap [N]查看/设置首次全文上限(firstSeenCap,0 = 不设上限)
/processor-config [N|off]查看/设置 tool-result-processor 阈值
/aging-config [N|off]查看/设置 aging 淘汰轮数
/context-clean [sessionId]清理持久化数据

💡 所有命令无参调用时显示当前配置和用法说明,例如直接输入 /distill-config 即可查看当前阈值和用法。

详细原理见 3.3 Context Manager 原理

最佳实践

✅ 好的规则设计

  • 精确的条件:用 conditions 限定触发范围,不要一刀切
  • 清晰的提示:告诉 AI “为什么不行“和“应该怎么做”
  • 分层防护:重要的事情用 block(强制阻止),次要的用 notify(提醒),内部引导用 steer(静默)
  • 善用状态追踪:连续出错 3 次再提醒,比每次都提醒更有效

❌ 不好的规则设计

  • 过于频繁:每个工具调用都 notify,AI 会被提醒淹没
  • 过于严厉:deny 所有 bash 命令,AI 连 ls 都不能执行
  • 模糊的提示:"reason": "注意" – 注意什么?
  • 忽略子代理:某些规则用 "subagent": false 排除子代理场景,避免干扰独立任务

规则的优先级

当多条规则同时匹配时:

  1. block > notify > steer(阻止 > 提醒 > 静默引导)
  2. 同优先级下,按规则文件中的定义顺序依次执行
  3. agent_end 钩子中,check 条件不满足的规则直接跳过

下一步

有了记忆、规划和规矩,AI 已经是一个靠谱的助手了。但一个会话做了很多事之后–你怎么知道它具体做了什么?哪些文件改了?哪些决策做了?

下一章,我们来看如何让 AI 学会复盘

3.2 pi-shepherd 原理:规则驱动的钩子系统

Shepherd 是 pi-atelier 的“神经系统“–它不提供工具或命令,而是通过事件钩子连接所有其他扩展。

架构概览

pi 事件总线
     │
     ├─ before_provider_request  ← Shepherd 在此注入临时提示
     │
     ├─ tool_call                ← Shepherd 拦截/改写工具调用
     │      │
     │      ▼
     │   工具执行
     │      │
     │      ▼
     ├─ tool_result              ← Shepherd 检查结果、触发后续动作
     │
     ├─ agent_end                ← Shepherd 触发收尾工作
     │
     └─ session_shutdown         ← Shepherd 清理临时状态

核心概念

规则(Rule)

每条规则是一个 JSON 对象,定义了“什么时机什么条件做什么动作“:

规则 = 钩子时机(hook) + 匹配条件(conditions/pattern) + 动作(action) + 提示(reason)

动作类型详解

动作注入方式用户可见典型用途
notify注入到 AI 上下文✅ 是提醒 AI 跑测试、lint
steer静默注入❌ 否引导 AI 查阅规范
rewrite修改工具参数✅ 是自动给命令加前缀
block阻止执行✅ 是禁止危险操作

状态追踪(State)

Shepherd 内部维护了工具调用的状态计数器:

"state": { "countKind": "errors", "gte": 5 }

这表示“累计错误 ≥ 5 次时触发”。countKind 支持:

  • "errors":工具返回错误时计数
  • "calls":工具被调用时计数

message_end 钩子

message_end 是一个特殊的钩子时机:它在 AI 回复完成之后触发,匹配的是 AI 输出的文本内容,而不是工具参数。这让 Shepherd 可以“监听” AI 说了什么,并在发现问题时注入纠正。

AI 回复完成
     │
     ▼
┌──────────────────────────────┐
│  Shepherd message_end 钩子    │
│  正则匹配 AI 回复文本         │
└──────┬───────────────────────┘
       │
   匹配到了?
   ├── 是 → steer 静默注入纠正(下一轮生效)
   └── 否 → 无操作

tool_call/tool_result 的区别:

维度tool_call / tool_resultmessage_end
匹配对象工具参数(命令、文件路径等)AI 的回复文本
conditions[].fieldpathtext(工具参数)text(AI 回复)
适用动作block/notify/rewrite/steer通常只用 steer
典型用途拦截危险命令、编辑后提醒拦截错误猜测、引导修正

典型用法见 场景 7:拦截 AI 的错误归因猜测

跨扩展通信

Shepherd 通过 pi.events 事件总线接收其他扩展的“提示“(hint):

其他扩展发出 hint → pi.events.emit("ephemeral:hint") → Shepherd 收集
                                                                 │
before_provider_request 时 → Shepherd 把收集的 hints 注入 AI 上下文

这种机制让扩展之间可以协作而不需要直接依赖。

规则加载流程

1. 加载扩展包内的 rules.json(全局默认规则)
     │
     ▼
2. 加载全局自定义规则 ~/?pi/agent/shepherd-rules.json(通过 shepherd_rules 工具管理)
     │
     ▼
3. 扫描项目目录下的 .pi/shepherd-rules-*.json(项目规则)
     │
     ▼
4. 规则叠加生效(项目规则覆盖同名全局规则)

规则文件变更后即时生效,无需任何操作。

shepherd_rules 工具的 scope 参数

shepherd_rules 工具的 scope 参数控制操作目标:

  • global(默认):操作 ~/.pi/agent/shepherd-rules.json,所有项目生效
  • project:操作 .pi/shepherd-rules.json,仅当前项目生效

list 操作不传 scope 时,返回全局 + 项目合并列表(标注每条规则的来源)。

配置

Shepherd 的配置支持三层合并(默认值 → 全局 settings → 项目 settings),可以在 .pi/settings.json 中按需覆盖。

📖 完整配置参数,见 pi-shepherd README

下一步

了解 Shepherd 如何守卫 AI 行为后,下一节我们看 Context Manager 如何控制信息质量

3.3 pi-context-manager 原理:信息质量控制与 Token 诊断

pi-context-manager 由原 pi-context 和 pi-payload-analyzer 合并而来,统一管理上下文质量和 token 诊断。

三个核心能力

1. Distill:压缩工具返回的海量信息

当工具返回大量内容(比如读取一个 1000 行的文件),pi-context-manager 会自动压缩,只保留关键信息:

原始工具输出(50KB)
     │
     ▼
┌────────────────────────┐
│   Distill 处理器        │
│   提取关键行 + 摘要      │
└────────────────────────┘
     │
     ▼
压缩后输出(~5KB)
     │
     ▼
AI 看到精炼后的信息

Distill 默认自动开启。有两个关键参数:

配置项命令说明
distillThreshold/distill-config超过此 token 数的工具输出会被压缩
firstSeenCap/distill-config --cap首次遇到的工具输出上限(0 = 不设上限)

💡 firstSeenCap 的作用:有些工具第一次返回结果非常大(比如 ls 列出一个大目录),但又不需要保留全部内容。firstSeenCap 限制首次输出的最大 token 数,后续请求中如果该结果被 distill 处理会进一步压缩。

2. Tool Result Processor:智能格式化精简

Tool Result Processor 对特定工具的输出做结构化精简,比 distill 更精准:

  • Code Graph 输出精简:自动压缩 AST 搜索结果,保留关键签名和位置
  • MCP JSON 输出精简:压缩 MCP 工具返回的冗长 JSON
  • 错误输出精简:截断过长的错误堆栈
  • Web 搜索输出精简:只保留搜索结果的关键信息

通过 /processor-config 命令查看或设置处理阈值。

3. Aging:老化淘汰旧内容

在长会话中,早期的工具输出可能已经不再相关。Aging 机制自动淘汰“过时“的内容:

轮次 1: 工具输出 A(新鲜 🟢)
轮次 5: 工具输出 A(有点旧 🟡)
轮次 10: 工具输出 A(太旧了 🔴 → 自动删除)

Aging 的智能豁免:某些内容类型不会被老化淘汰,包括:

  • 技能文件(SKILL.md)的内容
  • 用户手动标记保留的内容
  • 最近一次被 AI 引用的内容

通过 /aging-config 命令设置淘汰轮数,/aging-config off 禁用。

4. Payload 分析:用数据诊断上下文问题

会话变长后 AI 变笨?用 payload_analyze 找出原因。

⚠️ 重要payload_analyze 是一个 AI 工具,不是终端命令。你需要在 pi 的聊天对话中让 AI 来执行它。例如:

帮我用 payload_analyze 看一下当前的 token 使用情况

或者更精确地指定:

运行 payload_analyze action="budget"
分析模式怎么跟 AI 说看什么
budget“分析一下 token 预算分布”system/tools/history 各部分的 token 占比
growth“看看 token 增长趋势”随会话进行,token 是怎么膨胀的
expensive“找最耗 token 的工具调用”最贵的工具调用 Top N
overview“详细分析 payload”逐消息的详细 token 分析
messages“查看第 5 条消息”按索引/范围/关键词精确定位消息
chain“追踪这个工具调用的命运”跨 payload 追踪同一个工具调用
diff“对比两个 payload 的差异”找出两次请求之间的变化
stats“看 distill/processor 的命中率”聚合统计压缩效率

💡 先用 budget,再深入:遇到上下文问题时,先用 budget 看整体分布,然后用 expensive 定位大户,最后用 messages 精确查看某条消息。

/context TUI 面板

pi-context-manager 还提供了一个 TUI(终端界面)面板,让你可视化浏览上下文内容:

/context 命令
     │
     ▼
┌─────────────────────────────────────┐
│  📊 Context Panel                    │
│                                      │
│  [分类] [工具详情] [标记删除]         │
│                                      │
│  ├─ System Prompt    4.2K tokens     │
│  ├─ Tool Definitions 8.1K tokens     │
│  ├─ Memory           2.3K tokens     │
│  ├─ History          52K tokens      │
│  │   ├─ 轮次 1-10  (已标记删除)      │
│  │   ├─ 轮次 11-20                   │
│  │   └─ 轮次 21-30                   │
│  └─ Tool Results     64K tokens      │
│      ├─ read(schema.ts)  8.2K 🔴     │
│      └─ grep("TODO")    4.1K 🟡     │
└─────────────────────────────────────┘

在面板中可以:

  • 分类浏览:按类型查看上下文内容
  • 工具详情:查看每个工具返回的完整内容
  • 标记删除:手动标记不需要的内容,下次请求时自动排除

💡 无参调用即查看:所有 /distill-config/processor-config/aging-config 命令不带参数时会显示当前配置和用法说明。

📖 完整命令参考和参数说明,见 pi-context-manager README

最佳实践

你遇到的问题先做什么再做什么解决方案
会话 30 轮后 AI 变笨payload_analyze(action="growth")看 token 在哪个阶段暴涨调低 distill 阈值 / 装智能压缩
AI 忽略某些文件内容检查 distill 配置可能是 distill 过度压缩调整 distillThreshold
每次工具调用特别慢payload_analyze(action="expensive")找出最贵的 Top N 调用限制大文件读取或拆分文件
旧工具输出占空间/aging-config 查看设置合适的淘汰轮数Aging 自动淘汰 + 手动 /context 面板清理
首次工具输出太大/distill-config --cap设置首次全文上限firstSeenCap 限制首次输出大小

下一步

下一章,我们来看如何让 AI 学会复盘——自动记录会话事件,随时回溯历史。

3.5 Shepherd 实战场景

这一节通过真实场景展示如何用 Shepherd 规则解决 AI 编码中的常见问题。

场景 1:编辑代码后自动提醒跑测试

问题

AI 改了 TypeScript 代码但忘了跑测试。你每次都得手动说“跑一下测试“。

规则

{
	"comment": "[TypeScript] 编辑后必须跑测试",
	"hook": "tool_result",
	"tool": "edit",
	"action": "notify",
	"conditions": [
		{ "field": "path", "pattern": "\\.ts$", "flags": "" }
	],
	"reason": "编辑了 TypeScript 文件,必须跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
	"enabled": true
}

效果

AI:我修改了 src/auth/login.ts 的空值检查逻辑。
🛡️ Shepherd 提醒:编辑了 TypeScript 文件,必须跑覆盖该代码的单元测试。
AI:好的,我来跑测试... ✅ 3 个测试全部通过。

场景 2:防止 AI 乱改别人的代码

问题

你在 team 项目里工作,工作区有同事的未提交改动。AI 一看“这里不对“,顺手就 git checkout 恢复了别人的文件。

规则

{
	"comment": "[安全] 禁止 git checkout 恢复文件",
	"hook": "tool_call",
	"tool": "bash",
	"action": "block",
	"conditions": [
		{ "field": "text", "pattern": "git\\s+checkout\\s+--", "flags": "" }
	],
	"reason": "❌ 禁止 git checkout 恢复文件!工作区里有别人的未提交改动,你没有权力决定哪些改动是"无关"的。",
	"enabled": true
}

效果

AI 准备执行:git checkout -- src/config.ts
🛡️ Shepherd 阻止:禁止 git checkout 恢复文件!
AI:抱歉,我不会恢复别人的文件。让我看看其他方案...

场景 3:会话结束自动提交代码

问题

AI 改了一堆文件,会话结束了,但代码没提交。第二天发现工作区一团糟。

规则

{
	"comment": "[收尾] 编辑后提醒 commit + 记忆更新 + 总结",
	"hook": "agent_end",
	"action": "notify",
	"conditions": [{ "builtin": "has_edits" }],
	"reason": "检测到文件编辑,执行收尾工作:\n1️⃣ Git commit...\n2️⃣ 更新记忆...\n3️⃣ 会话总结",
	"stopReason": ["stop"],
	"enabled": true
}

check: "has_edits" 确保只有真的改了文件才提醒,不会在纯聊天会话中干扰。stopReason: ["stop"] 确保只在 AI 正常结束时触发,被中断时不触发。

场景 4:编辑 .gd 文件后自动提醒跑架构检查

问题

你在做 Godot 游戏项目,AI 编辑了 .gd 文件后应该跑架构检查 + 格式化检查,但每次都得手动提醒。

规则(同一个文件可以有多条规则,按顺序执行)

{
	"comment": "[arch] 编辑 .gd 文件后提醒跑编译验证",
	"hook": "tool_result",
	"tool": "edit",
	"action": "notify",
	"conditions": [
		{ "field": "path", "pattern": "\\.gd$", "flags": "" }
	],
	"reason": "编辑了 .gd 文件,请跑 check_arch 验证架构合规。",
	"enabled": true
},
{
	"comment": "[format] 编辑 .gd 文件后提醒跑格式化检查",
	"hook": "tool_result",
	"tool": "edit",
	"action": "notify",
	"conditions": [
		{ "field": "path", "pattern": "\\.gd$", "flags": "" }
	],
	"reason": "编辑了 .gd 文件,请跑 gdformat 格式化检查。",
	"enabled": true
}

两条规则都会触发,AI 会依次跑架构检查和格式化检查。

场景 5:工具反复出错时自动提醒翻记忆

问题

AI 在连续出错——edit 匹配失败、bash 命令找不到、测试反复不过。它在同一个死胡同里绕圈。

规则

{
	"comment": "[debug] 工具反复出错时提醒翻记忆",
	"hook": "tool_result",
	"action": "steer",
	"state": { "countKind": "errors", "gte": 5 },
	"reason": "🔍 **工具反复出错**:连续失败多次,翻看 .pi/memory/ 目录下的记忆文件,看是否已有踩坑记录。",
	"enabled": true,
	"subagent": false
}

关键点:

  • state: { "countKind": "errors", "gte": 5 } — 累计出错 5 次才触发,不会每次都提醒
  • action: "steer" — 静默注入引导,不在用户界面显示
  • subagent: false — 子代理中不触发,避免干扰独立任务

效果

AI 尝试 edit,失败...
AI 尝试 edit,失败...
AI 尝试 bash sed,失败...
AI 尝试 edit,失败...
AI 尝试 edit,失败...
🛡️ Shepherd 静默引导:翻看记忆文件。
AI:让我看看记忆... 找到了!记忆文件说"edit 匹配失败时先检查 CRLF"。
AI:运行 audit_format.py 检查格式... 果然是 CRLF 问题。

场景 6:自动改写高频命令

问题

AI 经常执行 git statusgit lognpm test 等命令,这些命令的输出可能很长,浪费 token。

规则

{
	"comment": "[rtk] 自动代理高频 bash 命令",
	"tool": "bash",
	"action": "rewrite",
	"pattern": "^(git\\s+(status|log|diff)|cargo\\s+(test|build|clippy)|pytest)\\b",
	"flags": "",
	"reason": "rtk command rewrite:自动加 rtk 前缀压缩输出",
	"enabled": true
}

当 AI 执行 git status 时,Shepherd 自动改写为 rtk git status(rtk 是一个输出压缩工具),减少 token 消耗。AI 不需要知道这个改写——对它来说,执行结果就是更简洁了。

场景 7:拦截 AI 的错误归因猜测

问题

测试失败时,AI 经常说“可能是 jiti 缓存问题““应该是 vitest proxy 没生效”——它不去查自己的代码逻辑,而是假设工具链有 bug。这种猜测不仅浪费时间,还可能误导方向。

规则

{
	"comment": "归因猜测拦截:AI 回复含'可能'时提醒验证",
	"hook": "message_end",
	"action": "steer",
	"conditions": [
		{
			"field": "text",
			"pattern": "(可能|应该|估计).{0,30}(缓存|jiti|vitest|proxy|模块解析|工具链)",
			"flags": ""
		}
	],
	"reason": "⚠️ 不要假设工具链有问题。先查自己的代码——逻辑错误、条件顺序、类型不匹配、路径拼接。绝大多数时候都是代码有 bug。",
	"enabled": true,
	"subagent": false
}

关键点

  • hook: "message_end" — 在 AI 回复完成后触发,匹配的是 AI 的回复文本
  • action: "steer" — 静默注入引导,不干扰用户界面,只在下一轮对话中影响 AI
  • field: "text" — 匹配 AI 回复的文本内容(tool_call/tool_result 匹配的是工具参数,message_end 匹配的是 AI 输出)
  • subagent: false — 只在主代理生效,子代理中的探索性猜测不应被拦截

效果

AI:测试失败了,可能是 jiti 缓存问题...
🛡️ Shepherd 静默注入:不要假设工具链问题,先查代码。
AI:让我重新看看代码... 发现是条件判断顺序写反了。

为什么不用 blocknotify

  • block 只能阻止工具调用,不能阻止 AI 说一段文字
  • notify 会弹通知打断用户
  • steer 静默注入,AI 下一轮会自动调整,用户几乎无感

规则设计模式总结

模式动作适用场景
编辑后提醒notify + conditions改代码后跑测试、lint、格式化
危险操作阻止block + conditions禁止 git checkout、禁止删文件
收尾自动化agent_end + conditions会话结束自动 commit + 记忆更新
连续出错引导steer + state反复失败时引导翻记忆
命令改写rewrite + pattern自动给命令加前缀压缩输出
回复内容拦截message_end + steer拦截 AI 的错误归因猜测

📖 回到 3.1 给 AI 立规矩 看完整的规则字段说明。

让 AI 学会复盘

你可能遇到过这种情况

你开了一个 3 小时的会话,AI 帮你做了很多事:

  • 修了两个 bug
  • 重构了一个模块
  • 配置了 CI 流水线
  • 写了一堆测试

第二天你想回顾一下:“昨天那个登录 bug 具体是怎么修的?“但你只有模糊的记忆——是改了 auth.ts 还是 middleware.ts?是加了空值检查还是改了类型断言?

你翻遍了 git log,commit message 写的是“fix: update auth“——等于没写。

💡 AI 做了很多事,但没有人记录“为什么这样做“。git 只记了改了什么,没记思考过程。

核心工具:pi-session-analyzer

pi-atelier 提供了 Session Analyzer(会话分析器)来搜索和分析历史会话:

功能说明
跨会话搜索关键词搜索所有历史会话的内容
按文件搜索找到所有修改过某个文件的会话
时间线视图按时间顺序查看一个会话的完整过程
摘要生成自动总结一个会话做了什么
分支分析分析 /tree 产生的平行分支
接手报告5 维度上下文,帮 AI 快速恢复工作
审计检查检查会话中是否有违规操作

💡 关于 pi-journal:pi-journal 可以通过 /journal 命令或 journal 工具生成日报/周报,采集 git 活动、记忆变更、会话活动三路数据。详见 4.2 pi-journal 原理

实际案例:找到上周的 bug 修复

场景:跨会话搜索

你说:“帮我找一下上次修 DuckDB 时区问题的会话。”

AI 调用 session_search

🔍 搜索 "DuckDB 时区" — 找到 2 个匹配会话:

1. 05-22 19:36 — DuckDB 时区配置修复
   改了 src/db/connection.ts
   
2. 05-20 14:30 — 数据库初始化讨论
   讨论了时区策略但没改代码

场景:查看会话详情

你说:“看看第一个会话的详细过程。”

AI 调用 session_analyze,生成时间线:

📅 会话时间线(05-22 19:36)

[19:36] 用户:DuckDB 查询返回 UTC 时间,需要改成中国时区
[19:37] AI:读取 db/connection.ts
[19:38] AI:发现连接时没有设置时区参数
[19:39] AI:修改 connection.ts,添加 SET timezone = 'Asia/Shanghai'
[19:40] AI:运行测试,2 个测试失败
[19:41] AI:发现测试用的 mock 没有时区设置
[19:42] AI:修改测试 mock
[19:43] AI:所有测试通过 ✅
[19:44] AI:提交 git commit "fix: set DuckDB timezone to Asia/Shanghai"

场景:按文件追踪

你说:“connection.ts 最近被谁改过?”

AI 调用 session_searchfile 模式:

📄 src/db/connection.ts 的修改历史:

1. 05-22 19:36 — DuckDB 时区修复(你)
2. 05-20 14:30 — 数据库连接池配置(同事)
3. 05-18 09:15 — 初始创建(你)

Session Analyzer 的 compact 模式

entries 动作支持 compact 参数,精简输出适合快速浏览大会话:

# 标准模式
session_analyze(action="entries")
→ 完整的每条记录(含时间戳、类型、完整内容)

# Compact 模式
session_analyze(action="entries", compact=true)
→ 去掉 type 列、时间只保留 HH:MM、预览 60 字符
→ 适合 100+ 条记录的大会话

Session Analyzer 的分析维度

模式用途命令示例
summary会话概览“这个会话做了什么”
entries逐条事件“列出所有文件修改”
timeline时间线“AI 的操作顺序是什么”
chain子代理追踪“子代理做了什么”
audit审计检查“有没有违规操作”
digest对话序列“我和 AI 聊了什么”
takeover接手报告“帮我接手上次的工作”

最有用的模式:takeover

takeover 生成一份“接手报告“,包含 5 个维度:

📋 会话接手报告

1. 用户意图:修复 DuckDB 时区问题
2. 修改文件:connection.ts, connection.test.ts
3. 最近步骤:修改了测试 mock,测试通过
4. 下一步:考虑是否需要在文档中说明时区行为
5. 关键决策:选择在连接层而非 SQL 层处理时区

当你想“接着上次的工作继续“时,这个报告能帮你(或者另一个 AI)快速恢复上下文。

最佳实践

✅ Session Analyzer 的高效用法

  • grep 模式:跨所有会话搜索关键词(比翻 git log 快得多)
  • file 模式:找到所有动过某个文件的会话(代码审查利器)
  • takeover 模式:接手别人的工作时,先生成接手报告
  • compact 模式:大会话快速浏览,精简输出
  • audit 模式:定期检查 AI 是否有违规操作

❌ 常见误区

  • 不要用 session_search 代替记忆——搜索是回溯(过去做了什么),记忆是知识(学到了什么)
  • 不要期望找到代码全文——会话记录是摘要,不是完整备份

进阶场景

场景:审查 AI 是否遵守了规范

你给 AI 定了很多规矩(不要直接写 settings.json、不要覆盖大文件),但不确定它是否真的遵守了。用 audit 模式检查:

你:审查一下上次会话有没有违规操作

AI:
  🛠 session_analyze(action="audit", sessionId="...")
  
  ⚠️ 发现 2 个问题:
  1. [违规] 直接 writeFileSync(settings.json) — 应该用 patchSettingsSectionWithBackup
  2. [警告] 大文件 write 覆盖(312 行)— 应拆分或用 edit

audit 模式会检查:是否用了被禁止的操作、是否遵循了文件修改规范、是否有不安全的操作。

场景:了解 AI 的探索过程

AI 用 /tree 开了探索分支——试了方案 A 不行,又试了方案 B 成功了。你想知道方案 A 为什么不行:

你:上次 AI 试了两种方案,帮我看看每个分支的结果

AI:
  🛠 session_analyze(action="branches", sessionId="...")
  
  🌿 分支分析
  
  [主分支] 讨论重构方案
  [B1] 方案 A — 修改函数签名 + 兼容层
    结果:测试失败,兼容层引入了循环依赖
  [B2] 方案 B — 新建接口 + 渐进迁移
    结果:所有测试通过 ✅,已合回主分支

这比翻 git log 更有用——git 只记录最终结果,branches 能看到 AI 的试错过程。

场景:追踪子代理做了什么

会话中用了 subagent 启动了多个子代理。你想知道每个子代理的具体输出:

你:上次会话的子代理都干了什么?

AI:
  🛠 session_analyze(action="chain", sessionId="...")
  
  🔗 子代理链
  
  主代理
    ├──→ pv-explorer
    │     任务:分析 src/auth/ 目录的架构
    │     结果:5 个模块,2 个设计模式,依赖方向正确
    │
    └──→ pv-reviewer
          任务:审查 JWT → session 迁移方案
          结果:发现 1 个数据模型违反,2 个测试缺口

chain 模式追踪主代理和子代理之间的调用关系,清晰展示每个子代理收到了什么任务、返回了什么结果。

下一步

有了复盘能力,AI 和你都能回顾过去的工作。但还有一个问题:会话越长,AI 越容易“变笨“——开始重复自己、忘记前面的约定。

下一章,我们来看如何让 AI 在长会话中保持聪明。

4.2 pi-journal:自动日志报告

为什么需要日志

你开了一天 pi,做了很多事:

  • 上午修了两个 bug,顺带重构了一个模块
  • 下午配置了 CI 流水线,写了一堆测试
  • 晚上研究了一个新方案,更新了记忆文件

到了晚上你想回顾:“今天到底干了什么?“你翻 git log,全是碎片化的 commit;翻记忆文件,只记了关键结论;翻会话记录,十几个会话不知道从哪看起。

💡 你需要一个“自动日报“——把散落在 git、记忆、会话中的活动汇总成一份可读的报告。

pi-journal 做什么

pi-journal 采集三种数据源,自动生成 Markdown 格式的日报/周报:

数据源采集内容
Git 活动扫描 ~/.pi/agent/git/ 下所有仓库,统计提交数、文件变更、增加/删除行数
记忆变更扫描全局和项目记忆目录,找出时间范围内的文件变更
会话活动扫描 pi 会话记录,统计会话数、工具调用、编辑操作、活跃时长

使用方式

命令:/journal

# 今天的日报(默认)
/journal

# 指定时间范围
/journal yesterday
/journal this_week
/journal 3d          # 最近 3 天
/journal 2025-05-27  # 指定日期

工具:journal

AI 也可以主动调用 journal 工具来生成报告。当你说“写个日记“、“今天做了什么”、“写周报“时,AI 会自动触发。

生成的报告格式

# 📓 日报 — 2025-05-27

## Git 活动
- **pi-shepherd** (2 commits, +45/-12)
  - 新增 tool_result 规则支持
  - 修复优先级排序 bug
- **pi-context-manager** (1 commit, +30/-5)
  - 添加 aging 自动淘汰功能

## 记忆变更
- 新增: debug_anti_pattern.md
- 更新: coding_standards.md

## 会话活动
- 019e6494 (45m) — Shepherd 规则引擎重构
  - 工具调用: 23, 编辑: 8
- 019e6203 (30m) — Context aging 功能实现
  - 工具调用: 15, 编辑: 5

## 摘要
- 总提交: 3
- 总会话: 12
- 总编辑: 43

最佳实践

✅ 推荐用法

  • 每日收工前/journal 生成日报,回顾今天做了什么
  • 周五总结/journal this_week 生成周报
  • 让 AI 写:直接说“写个今天的日记“,AI 会调用工具生成

⚠️ 注意事项

  • 报告中的“AI 总结“部分需要 AI 在生成后补充
  • Git 活动只扫描 ~/.pi/agent/git/ 下的仓库,不包含系统其他位置的 git 仓库
  • 会话活动依赖 pi 的会话记录存储

工作原理

用户输入时间范围
      ↓
  parseTimeRange()  →  解析为 since/until 时间戳
      ↓
  ┌─────────────┬──────────────┬──────────────────┐
  │ Git 活动     │ 记忆变更      │ 会话活动          │
  │ 自动发现仓库  │ 扫描 memory/ │ 从 session-analyzer│
  │ git log 统计 │ 文件时间戳    │ 获取会话列表      │
  └──────┬──────┴──────┬───────┴────────┬─────────┘
         ↓             ↓                ↓
      renderReport()  →  汇总渲染为 Markdown
         ↓
      输出报告

下一步

pi-journal 解决了“回顾过去“的需求。但有时候你需要的不只是回顾——你需要找到某个具体的会话,看 AI 当时是怎么想的。下一节我们来看 pi-session-analyzer 的详细用法。

长会话生存指南

你可能遇到过这种情况

你开了一个长会话,AI 帮你做了很多事。到第 50 轮对话的时候,你发现:

  • AI 开始问你之前已经回答过的问题
  • 它重新提了一个已经被否决的方案
  • 它的代码质量明显下降——少了错误处理、类型定义
  • 有时候它甚至开始“幻觉“——编造不存在的函数和文件

最极端的情况:AI 直接报错了——“上下文窗口超出限制”(context window exceeded),整个会话崩溃。

💡 这就是“上下文膨胀“问题:AI 的“工作记忆“有容量上限,装太多东西就会溢出。

问题的根源

AI 的上下文窗口是一个固定大小的“工作台“:

上下文窗口(例如 128K tokens)
┌──────────────────────────────────────┐
│ 系统提示(System Prompt)    ≈ 5K     │
│ 工具定义(Tools)            ≈ 8K     │
│ 记忆注入(Memory)           ≈ 2K     │
│ ─────────────────────────────        │
│ 对话历史(前 50 轮)         ≈ 80K    │ ← 膨胀的主要来源
│ 工具返回结果                 ≈ 30K    │ ← 工具返回的内容可能很大
│ ─────────────────────────────        │
│ 剩余可用空间                 ≈ 3K     │ ← 快满了!
└──────────────────────────────────────┘

问题在于:

  1. 对话历史只增不减:每轮对话都往上下文里加内容,从不删除
  2. 工具返回结果可能巨大read 一个 1000 行的文件可能就占 5K tokens
  3. 重复信息累积:AI 多次读取同一个文件,每次都占空间

两件工具:Smart Compact 和 Context Manager

你可能会问:这两个包都要装吗?答案是:推荐都装,它们解决不同层面的问题:

维度pi-smart-compactpi-context-manager
做什么压缩历史对话诊断 token 消耗 + 压缩工具返回
主动/被动全自动触发诊断需你叫 AI 跑,distill 自动
解决什么“历史对话太长”“工具返回太多” + “为什么这么慢”
能互相替代吗❌ 不能❌ 不能

💡 一句话总结:context-manager 帮你发现问题(token 都花哪了),smart-compact 帮你自动解决问题(压缩历史)。两个配合使用效果最佳。

pi-smart-compact — 智能压缩

Smart Compact 在上下文快满的时候自动“压缩“历史对话:

压缩前(80K tokens 的对话历史):
┌─────────────────────────────────┐
│ 用户:帮我看看 auth.ts          │
│ AI:我读取了 auth.ts...(500字) │
│ 用户:加个空值检查               │
│ AI:好的,我修改了...(300字)    │
│ 用户:测试一下                   │
│ AI:测试结果...(200字)         │
│ ... 重复 50 轮 ...               │
└─────────────────────────────────┘

压缩后(15K tokens 的摘要):
┌─────────────────────────────────┐
│ 摘要:                          │
│ - 在 auth.ts 中添加了空值检查    │
│ - 修改了对应的测试文件           │
│ - 所有测试通过                   │
│ - 使用了 JWT 认证方案            │
│ ... 关键信息保留 ...              │
└─────────────────────────────────┘

两阶段压缩策略:

阶段方法说明
Phase 1提取关键信息(如决策、文件修改、结论)遍历对话,生成结构化意图摘要
Phase 2丢弃低价值信息(如重复的文件读取、中间调试输出)让 LLM 逐批判断工具结果去留

pi-context-manager — 诊断工具

pi-context-manager 提供了 payload_analyze 工具,帮你看清 token 到底花在哪了:

📊 Token 预算分析

System Prompt:    4,200 tokens ( 3.2%)
Tool Definitions: 8,100 tokens ( 6.2%)
Memory Injection: 2,300 tokens ( 1.8%)
Conversation:    52,400 tokens (40.0%)
Tool Results:    64,800 tokens (49.5%)  ← 大头在这!
──────────────────────────────────────
Total:          131,800 / 128,000      ← 超了!

Top 3 最贵的工具调用:
1. read(src/database/schema.ts)  — 8,200 tokens
2. code_graph_module_overview    — 6,400 tokens
3. grep("TODO|FIXME")           — 4,100 tokens

实际案例:诊断上下文崩溃

真实场景回顾

有一次,一个会话在上下文使用率只有 34.8% 的时候就崩溃了。看起来不应该——才用了三分之一啊?

用 pi-context-manager 的 budget 模式分析后发现:

问题根因:
工具返回结果中有 34.8% 是 error 输出
→ 大量重复的 "Command not found" 错误信息
→ 每次错误都消耗了 token 但没有有价值的信息
→ 累积导致上下文提前耗尽

解决方案:在 shepherd 规则中加入 after_bash 钩子,失败命令的错误输出自动截断,避免无意义的 token 消耗。

用 growth 模式看趋势

📈 上下文增长趋势

请求 #1:  15K  ████
请求 #5:  28K  ███████
请求 #10: 45K  ████████████
请求 #15: 72K  ████████████████████
请求 #20: 98K  ██████████████████████████  ← 接近上限
请求 #23: 💥 崩溃!

关键发现:第 10-15 轮之间增长最快,因为那个阶段做了大量的文件搜索。

优化:用 compact 模式的 code_graph_semantic_code_search(只返回签名和位置)替代 grep(返回完整匹配行),token 消耗减少 70%。

配置 Smart Compact

通过 settings.json 安装:

{
  "packages": ["pi-smart-compact"]
}

安装后默认为 手动模式enabled: false),只在用户执行 /smart-compact 时触发。

/smart-compact-config auto 可开启自动接管模式。

可选的高级配置

.pi/smart-compact.json 中:

{
  "enabled": true
}
  • enabled: true:自动触发(pi 触发 compact 时接管)
  • enabled: false:手动模式(默认,只响应 /smart-compact 命令)

手动触发:在对话中输入 /smart-compact。 配置查看:输入 /smart-compact-config

Payload Analyzer 的常用命令

命令用途何时用
budgetToken 预算分析(system/tools/history 构成)“token 都花哪了”
growth上下文增长趋势(token 随请求变化曲线)“为什么越来越慢”
expensive最贵的工具调用(Top N 排序)“哪个工具最吃 token”
overview逐消息详细分析(含 distill 事件)“精确诊断某个时间点”
messages按索引/范围/关键词定位消息“看看第 10 条消息说了什么”
chain跨 payload 追踪同一个工具调用“这个调用后来怎么样了”
chain-tcid跨 payload 追踪同一个 toolCallId“验证 distill 是否压缩了这条调用”
chain-tcid跨 payload 追踪同一个 toolCallId“验证 distill 行为”
diff对比两个 payload 的差异“这两次请求有什么不同”
stats聚合统计 distill/processor 命中率“压缩效率如何”
single分析单个 payload 文件“深入看一个录制文件”
list列出所有录制文件“有哪些可以分析的”

💡 诊断流程:先用 list 看有哪些录制文件 → budget 看整体分布 → expensive 找大户 → messages 精确定位。

Context Manager 的 Aging 和 Processor

除了 Distill,pi-context-manager 还提供了两个辅助机制:

Aging(老化淘汰)

自动淘汰长期未被引用的旧工具输出。通过 /aging-config 设置淘汰轮数。

特殊豁免:技能文件(SKILL.md)内容不会被 aging 淘汰,确保 AI 始终能看到当前加载的技能。

Tool Result Processor(后处理器)

对特定类型的工具输出做格式化精简(如 code-graph 的 AST 搜索结果、MCP JSON 输出)。通过 /processor-config 设置阈值。

/context TUI 面板

输入 /context 可以打开可视化面板,分类浏览上下文内容,手动标记不需要的内容进行删除。

最佳实践

✅ 保持长会话健康的习惯

  1. 用 compact 模式搜索code_graph_semantic_code_search(compact: true)grep 省 70% token
  2. 及时压缩:不要等到崩溃才处理,上下文接近上限时就应该触发压缩
  3. 避免重复读取:用记忆记住文件内容,不要反复 read 同一个文件
  4. 大文件分块读:用 offset/limit 只读需要的部分,而不是整个文件
  5. 合理配置 aging:设置 8-12 轮淘汰,自动清理过时内容
  6. 定期用 payload_analyze 体检:长会话中途跑一次 budget,提前发现问题

✅ 诊断的优先级

上下文出问题时:
  1. payload_analyze budget → 看总量分布
  2. payload_analyze expensive → 找最贵的调用
  3. payload_analyze growth → 看增长趋势
  4. 针对性优化(换工具、加过滤、调整策略)

❌ 常见误区

  • “上下文还有 50% 空间,不用担心” → 错,工具返回结果可能突然膨胀
  • “压缩会丢失重要信息” → Smart Compact 优先保留决策和结论
  • “重启会话就好了” → 治标不治本,下次还会遇到同样的问题

下一步

现在 AI 有了记忆、规划、规矩、复盘和压缩——它已经是一个相当能干的助手了。但它还是“被动“的——你问它才做。

能不能让 AI 主动工作?比如每天自动检查代码质量,或者自动跑一轮研究分析?

下一章,我们来看如何让 AI 自动化工作

5.2 pi-smart-compact 原理:两阶段增强压缩

Smart Compact 是 pi 内置 Compaction 机制的增强版——它不是简单地截断历史,而是“聪明地“决定保留什么、丢弃什么。

为什么需要增强压缩?

pi 内置的 Compaction 会在上下文接近上限时自动压缩旧对话,但它不够“聪明“:

内置 Compaction:
  压缩前 100 轮对话 → 压缩后生成一段通用摘要
  问题:摘要太粗糙,关键细节丢失,工具调用结果被无差别截断

Smart Compact 的改进——接管 pi 的压缩事件,执行两阶段增强压缩:

阶段做什么怎么做
Phase 1:意图总结提取用户意图、关键决策、当前状态遍历对话,提取 AI 回复中的非工具文本,生成结构化意图摘要
Phase 2:工具去留判断哪些工具调用结果可以安全丢弃将所有工具调用配对(调用+结果),让 LLM 逐批判断保留/丢弃
pi 触发 compact 事件
  → Smart Compact 接管(如果 auto 模式开启)
    → Phase 1:提取意图摘要(保留决策、约定、结论)
    → Phase 2:逐批判断工具结果去留
  → 输出精简后的对话历史,替代 pi 默认的粗糙摘要

两个阶段是一次性顺序执行的——Smart Compact 接管压缩事件后,先做意图总结,再做工具筛选,最后输出精简结果。不是按上下文使用率分阶段触发。

配置

安装

{
  "packages": ["pi-smart-compact"]
}

命令

命令用途
/smart-compact手动触发两阶段压缩
/smart-compact-config [auto|manual]查看或切换自动/手动模式

自动/手动模式

  • auto/smart-compact-config auto 开启):pi 触发 compact 事件时自动接管,执行增强压缩
  • manual(默认):只在用户执行 /smart-compact 时触发

高级配置

在项目级 .pi/smart-compact.json 中可配置:

{
  "enabled": false,
  "intentModel": "",
  "filterModel": "",
  "thinkingTruncateChars": 500,
  "toolCallTruncateChars": 1000,
  "toolResultTruncateChars": 2000,
  "filterBatchSize": 20
}
参数默认值说明
enabledfalse是否自动接管 pi 的 compaction
intentModel空(用会话默认模型)Phase 1 意图总结使用的模型
filterModel空(用会话默认模型)Phase 2 工具去留判断使用的模型
thinkingTruncateChars500thinking 块截断字符数
toolCallTruncateChars1000toolCall arguments 截断字符数
toolResultTruncateChars2000toolResult 内容截断字符数
filterBatchSize20Phase 2 每批判断的工具数量

压缩保留什么?

Smart Compact 的 Phase 2 会根据以下优先级判断工具结果:

优先级内容类型为什么保留
🔴 最高用户明确的要求和约束这是任务目标
🟠 高关键决策和选择理由避免 AI 重复讨论已否决的方案
🟡 中文件修改记录(edit/write)让 AI 知道哪些文件已改过
🟢 低文件读取和搜索结果可以重新执行
⚪ 最低失败的尝试和调试过程已经学到了教训

最佳实践

  • 长会话时开启 auto 模式:Smart Compact 会在 pi 准备压缩时自动接管,比默认压缩保留更多关键信息
  • 手动触发适合关键操作前:在开始一个重要的重构之前,手动 /smart-compact 清理上下文
  • 配合 context-manager:Smart Compact 压缩对话历史,Context Manager 的 distill 压缩工具输出,两者互补
  • 可用便宜模型做压缩:如果不想浪费主模型的 token,可以在配置中指定 filterModel 为更便宜的模型

📖 回到 5.1 长会话生存指南 看完整的诊断和优化案例。

5.3 用 pi-context-manager 诊断 Token 消耗

pi-payload-analyzer 的功能已合并到 pi-context-manager 中。本节介绍如何使用统一的 payload_analyze 工具诊断上下文问题。

Token 都花在哪了?

长会话中 AI 变笨,往往是因为上下文被“垃圾“填满了。但具体是哪些内容占了 token?靠猜是不行的。

pi-context-manager 提供了 payload_analyze 工具,用数据告诉你 token 的去向。

需要先开启录制

payload_analyze 需要先录制 payload 才能分析。在对话中输入:

/record on

录制会保存在 ~/.pi/agent/distill/recordings/ 目录下。录制期间会有轻微的性能开销,用完记得 /record off 关闭。

分析模式速查

全局概览类

模式用途输出
list列出所有录制文件文件列表 + 大小
budgetToken 预算分析system/tools/history 各部分占比
growth增长趋势token 随请求变化的曲线
stats聚合统计distill/processor 命中率、压缩效率

深入诊断类

模式用途输出
expensive最贵的工具调用按 token 排序的 Top N
overview逐消息详细分析每条消息的 token 构成 + distill 事件
messages精确定位消息按索引/范围/关键词过滤

追踪对比类

模式用途输出
chain追踪工具调用命运同一个 argsSig 跨 payload 的变化
chain-tcid追踪 toolCallId同一个 toolCallId 跨 payload 的变化(验证 distill 行为)
chain-tcid追踪 toolCallId验证 distill 行为
diff对比两个 payload找出两次请求的差异
single分析单个文件单个录制文件的完整分析

messages 模式的精确定位

messages 是最灵活的诊断工具,支持多种过滤方式:

# 看第 5 条消息(0-based)
payload_analyze(action="messages", msgIndex=5)

# 看第 5-10 条消息
payload_analyze(action="messages", msgRange="5-10")

# 看最后 5 条消息
payload_analyze(action="messages", msgRange="last:5")

# 按关键词过滤
payload_analyze(action="messages", grep="error|fail")

# 按工具名过滤
payload_analyze(action="messages", toolName="read")

# 按文件路径过滤
payload_analyze(action="messages", file="*.ts")

实战案例

案例 1:找出上下文膨胀的根因

第一步:budget 模式看总量
你:"帮我用 payload_analyze 分析 token 预算"
结果:Tool Results 占 49.5%

第二步:expensive 模式找大户
你:"找出最耗 token 的工具调用 Top 10"
结果:read(schema.ts) 占 8.2K tokens

第三步:优化
→ 用 offset/limit 分块读取大文件
→ 或者启用 distill 自动压缩

案例 2:诊断压缩效率

第一步:stats 模式看命中率
你:"看 distill 和 processor 的压缩效率"
结果:distill 命中率 75%,processor 命中率 60%

第二步:chain 模式追踪
你:"追踪 read(schema.ts) 的 distill 行为"
结果:第 3 次请求被 distill,从 8.2K 压缩到 1.5K

案例 3:对比两次请求的差异

你:"对比这两个 payload 有什么不同"
AI 调用 payload_analyze(action="diff", payloadPath="...", payloadPath2="...")
结果:第二次请求多了 3 条工具调用,但 distill 压缩了 2 条

📖 完整长会话诊断案例见 5.1 长会话生存指南

5.4 长会话实战场景

这一节通过真实使用场景,展示如何组合 pi-atelier 的工具解决长会话中的常见问题。

场景 1:AI 变笨了——用 Smart Compact 压缩

症状

你跟 AI 已经聊了 2 个小时,做了很多事。突然发现 AI 开始:

  • 问你之前已经回答过的问题
  • 重新提出已经被否决的方案
  • 代码质量明显下降——少了错误处理

传统压缩 vs Smart Compact

pi 内置的 Compaction 会在上下文接近上限时自动触发,但它只是简单地压缩旧对话为一段通用摘要。Smart Compact 更聪明:

传统 Compaction:
  100 轮对话 → 一段 500 字的通用摘要
  问题:关键细节丢失,AI 不知道之前做了什么决定

Smart Compact(两阶段):
  Phase 1(意图总结):
    → 提取:决策、约定、文件修改、结论
    → 保留所有关键信息,丢弃冗余过程
  
  Phase 2(工具去留):
    → 逐批判断每个工具结果是否需要保留
    → 丢弃:重复读取、失败尝试、调试过程

操作步骤

1. AI 已经帮你做了很多事,你感觉上下文快满了
2. 输入 /smart-compact
3. Smart Compact 分析对话历史,生成增强摘要
4. AI 继续工作,但"记得"所有关键决策

或者什么都不做——如果配置了 auto 模式(默认),Smart Compact 会在合适的时机自动触发。

配合 Context Manager 效果更好

Smart Compact 压缩对话历史,Context Manager 的 Distill 压缩工具返回结果。两个配合使用:

上下文窗口
├── 对话历史 ←── Smart Compact 压缩(保留决策和结论)
├── 工具结果 ←── Distill 压缩(保留关键信息,丢弃冗余)
└── 记忆注入 ←── 固定大小,不变

场景 2:上下文已经炸了——Handoff 接手

症状

更极端的情况:AI 报错了——“上下文窗口超出限制”(context window exceeded)。整个会话已经无法继续。

这时候光压缩已经来不及了——会话直接崩溃。

解决方案:开新会话 + takeover 接手

1. 开一个新会话
2. 告诉 AI:"帮我接手上次的工作"
3. AI 调用 session_analyze(action="takeover")
4. 生成 5 维度接手报告:

📋 会话接手报告

1. 用户意图:重构认证模块,从 JWT 切换到 session-based
2. 修改文件:
   - src/auth/middleware.ts(已改完)
   - src/auth/login.ts(进行中,80% 完成)
   - src/auth/__tests__/login.test.ts(待写)
3. 最近步骤:
   - 修改了 middleware.ts 的类型签名
   - 开始改 login.ts 但还没改完
   - 测试还没写
4. 下一步:
   - 完成 login.ts 的修改
   - 写 login.test.ts 的测试
   - 跑一遍全量测试
5. 关键决策:
   - 选择 session-based 而非 refresh token 方案
   - 原因:项目不需要跨域 SSO

takeover 的 5 个维度

维度包含什么为什么重要
用户意图原始需求和目标新 AI 知道“要做什么“
修改文件已改和待改的文件列表知道“改了什么“
最近步骤最近 3-5 步操作知道“进行到哪了“
下一步还需要做什么知道“接下来该做什么“
关键决策重要的技术选择和理由知道“为什么这样做“

有了这 5 个维度,新会话的 AI 可以在 1-2 轮对话内完全恢复工作上下文。

操作示例

你:帮我接手上次修 Godot MCP Bridge 的工作

AI:
  🛠 session_analyze(action="takeover", sessionId="019e620f-...")
  
  📋 接手报告已生成。上次你在调试 MCP Bridge 的 WebSocket 连接问题,
  已经定位到 nohup 导致 listen() 返回值被吞。下一步是不用 nohup 
  直接启动 TCP 端口。要我继续吗?

场景 3:想了解过去发生了什么——会话分析

症状

你昨天让 AI 做了一大堆事。今天你想回顾:“昨天那个 DuckDB 时区 bug 具体是怎么修的?”

操作步骤

第一步:跨会话搜索
你:搜一下之前修 DuckDB 时区的会话

AI:
  🛠 session_search(action="grep", query="DuckDB 时区")
  
  找到 2 个匹配会话:
  1. 05-22 19:36 — DuckDB 时区配置修复
  2. 05-20 14:30 — 数据库初始化讨论

第二步:看时间线
你:看看第一个的详细过程

AI:
  🛠 session_analyze(action="timeline", sessionId="...")
  
  📅 时间线:
  [19:36] 用户:DuckDB 查询返回 UTC 时间
  [19:37] AI:读取 db/connection.ts
  [19:38] AI:发现没有设置时区参数
  [19:39] AI:添加 SET timezone = 'Asia/Shanghai'
  [19:43] AI:所有测试通过 ✅

第三步:看对话原文(如果需要更多细节)
你:看看 19:39 前后的对话原文

AI:
  🛠 session_analyze(action="entries", range="5-10", sessionId="...")

或者按文件过滤——找到所有动过 connection.ts 的操作:

你:这个会话里哪些操作动过 connection.ts?

AI:
  🛠 session_analyze(action="entries", file="*connection*", sessionId="...")

  📋 匹配 3 条操作:
  [12] read(db/connection.ts)  — 读取文件
  [15] edit(db/connection.ts)  — 添加 SET timezone
  [18] read(db/connection.ts)  — 验证修改结果

常用的分析模式组合

快速了解一个会话:summary → 知道做了什么
追踪操作顺序:   timeline → 知道步骤
查看对话原文:   entries  → 知道细节
接手别人的工作: takeover → 知道上下文
检查是否有违规: audit    → 知道有没有问题

场景 4:工具输出占满了上下文——Aging 自动遗忘

症状

AI 在一个会话中用了大量工具:读了 20 个文件、跑了 10 次搜索、执行了 5 次 bash 命令。每个工具返回的结果都留在上下文里。

问题是:你只需要最近的几个结果。20 分钟前 read 的那个文件,现在早就不需要了。

解决方案:Aging 自动淘汰

Aging(老化淘汰)会在指定轮数后自动淘汰未被再次引用的工具输出:

时间线:
  第 1 轮:read(auth.ts) → 5K tokens
  第 2 轮:read(middleware.ts) → 4K tokens
  第 3 轮:grep("TODO") → 3K tokens
  ...
  第 10 轮:edit(auth.ts)  ← auth.ts 又被引用,"续命"成功
  
  第 1+8=9 轮:grep("TODO") → 8 轮没被引用,自动淘汰 ✅
  第 2+8=10 轮:middleware.ts 没被再次引用,自动淘汰 ✅
  第 1+8=9 轮:auth.ts → 被 edit 引用,保留!

配置 Aging

/aging-config 8    # 8 轮后淘汰(推荐 8-12)
/aging-config off  # 关闭自动淘汰

💡 技能文件豁免:SKILL.md 的内容不会被 aging 淘汰——AI 始终能看到当前加载的技能。

手动干预:/context TUI 面板

如果不想等自动淘汰,可以手动标记删除:

1. 输入 /context 打开 TUI 面板
2. 分类浏览:按工具类型、时间顺序查看所有上下文内容
3. 选中不需要的内容,标记为删除
4. 下次 AI 发送请求时,标记的内容不会带上

这在以下场景特别有用:

  • AI 读了一个巨大的配置文件但你只需要其中一行
  • 搜索返回了 50 条结果但你只用了 3 条
  • 之前调试时的错误信息已经不需要了

场景 5:为什么 AI 变慢了——Token 预算诊断

症状

AI 的响应越来越慢,而且每轮对话的等待时间明显变长。你怀疑是上下文太大了,但不知道具体是什么在占空间。

操作步骤

第一步:开启录制
你:/record on

... 多做几轮对话 ...

第二步:看 token 预算
你:帮我分析 token 预算

AI:
  🛠 payload_analyze(action="budget")
  
  📊 Token 预算分析
  System Prompt:    4,200 (3.2%)
  Tool Definitions: 8,100 (6.2%)
  Memory Injection: 2,300 (1.8%)
  Conversation:    52,400 (40.0%)
  Tool Results:    64,800 (49.5%)  ← 大头在这!
  
第三步:找最贵的调用
你:找出最耗 token 的工具调用

AI:
  🛠 payload_analyze(action="expensive", topN=5)
  
  Top 5 最贵的工具调用:
  1. read(src/database/schema.ts)  — 8,200 tokens
  2. code_graph_module_overview    — 6,400 tokens
  3. grep("TODO|FIXME")           — 4,100 tokens
  4. read(src/config/settings.ts) — 3,800 tokens
  5. bash("npm test")             — 3,200 tokens

第四步:针对性优化
→ schema.ts 太大,用 offset/limit 只读需要的部分
→ code_graph 用 compact=true 模式
→ grep 加 --include 限定文件类型

诊断流程速查

1. budget  → 看总量分布(哪部分占比最高?)
2. expensive → 找大户(具体哪个调用最耗 token?)
3. growth  → 看趋势(哪段时间增长最快?)
4. messages → 精确定位(看看那条具体的消息内容)
5. 针对性优化(换工具、加过滤、启用 distill)

场景组合:超长会话的完整生存策略

会话开始
│
├── 第 1-20 轮:正常工作,不用操心
│
├── 第 20-40 轮:上下文使用率 ~40%
│   → 开启 /record on(可选,为后续诊断做准备)
│   → 注意避免重复读取大文件
│
├── 第 40-60 轮:上下文开始紧张
│   → Smart Compact 接管 pi 的压缩事件(如果 auto 模式开启)
│   → 或手动 /smart-compact
│   → Aging 开始淘汰旧工具输出
│
├── 第 60-80 轮:接近上限
│   → Smart Compact 已完成两阶段压缩
│   → 考虑是否该开新会话了
│   → 如果要继续:用 payload_analyze budget 检查
│
├── 💥 崩溃了!
│   → 开新会话
│   → session_analyze(action="takeover") 接手
│   → 继续工作
│
└── 收工前:
    → /record off
    → 让 AI 用 /journal 生成日报
    → agent_end 自动提醒 commit + 记忆更新

📖 回到 5.1 长会话生存指南 看完整的工具介绍。

自动化与工作流

你可能遇到过这种情况

每周五下午,你都要做同样的事:

  1. 检查本周所有会话,看看改了哪些文件
  2. 跑一遍测试,确保没有回归
  3. 检查是否有未提交的代码
  4. 写周报——总结本周进展

每次都要手动提醒 AI 做这些事。有时候忘了,周一回来发现上周五的改动没提交。

💡 AI 能做事,但它不会“主动“做事。它需要你告诉它“现在该做什么“。

两件工具:Scheduler 和 Workflow

pi-scheduler — 定时任务

Scheduler(调度器)让 AI 在指定的时间做指定的事:

定时触发
     │
     ▼
┌──────────────────┐
│  注入预设消息      │
│  "现在是周五下午,  │
│   请执行周度检查"  │
└──────────────────┘
     │
     ▼
  AI 自动执行

支持的定时类型:

类型说明示例
一次性指定时间后触发一次“30 分钟后提醒我开会”
重复按固定间隔重复触发“每 2 小时检查一次测试”

pi-workflow — 子代理编排

Workflow(工作流)让 AI 把复杂任务拆成多个子代理并行执行:

主代理:"研究一下 XXX 的最佳实践"
     │
     ├──→ 子代理 1:搜索网页资料
     ├──→ 子代理 2:搜索 GitHub 源码
     └──→ 子代理 3:搜索历史会话
          │
          ▼
     主代理:综合三个子代理的结果,给出建议

子代理是独立的执行环境:

  • 有自己的上下文窗口(不会污染主会话)
  • 有自己的工具集(可以限制权限)
  • 执行完返回结果给主代理

实际案例:自动周报

配置每周五的自动检查

{
  "action": "create",
  "interval_ms": 604800000,
  "recurring": true,
  "prompt": "现在是周五下午。请执行以下检查:\n1. 用 session_search 查看本周所有会话\n2. 用 session_analyze summary 总结每个会话\n3. 检查 git status 是否有未提交的改动\n4. 汇总生成周报并写入 .pi/journal/weekly-summary.md\n5. 提醒用户提交未提交的代码"
}

每周五,AI 会自动:

  1. 搜索本周所有会话
  2. 逐个生成摘要
  3. 检查 git 状态
  4. 生成周报
  5. 提醒你提交

定时提醒的日常使用

用户:"30 分钟后提醒我看看 CI 的结果"
AI:✅ 已创建定时任务,30 分钟后提醒

... 30 分钟后 ...

AI:⏰ 提醒:该检查 CI 结果了。当前时间 15:30。

实际案例:子代理研究

场景:调研一个新技术的最佳实践

你说:“调研一下 Bun 和 Node.js 的性能差异,给我一个选型建议。”

AI 启动研究工作流:

🔬 研究工作流启动

子代理 1(搜索专家):
  → 搜索 "Bun vs Node.js performance benchmark 2026"
  → 找到 5 篇技术文章
  → 提取关键数据点

子代理 2(源码专家):
  → 搜索 Bun 的 GitHub 仓库
  → 查看 benchmark 目录
  → 查看 issue 中的性能讨论

子代理 3(历史专家):
  → 搜索本项目历史会话
  → 看看之前有没有讨论过类似选型

─────────────────────────

主代理综合报告:

📊 Bun vs Node.js 选型建议

1. 性能对比:
   - HTTP 吞吐:Bun 快 3-4x
   - 启动时间:Bun 快 5x
   - npm 兼容性:Node.js 更好(Bun 95% 兼容)

2. 本项目建议:继续用 Node.js
   - 理由:项目依赖了多个 Node.js 原生模块
   - Bun 的兼容性问题可能导致额外维护成本
   - 性能差异对本项目影响不大(IO 密集型)

子代理的优势

特性单代理(普通对话)多代理(Workflow)
上下文隔离所有信息混在一起每个子代理独立
并行执行串行,一个一个来可并行搜索
错误隔离一个环节出错影响全局子代理出错不影响其他
Token 效率所有信息都在主上下文只有最终结果回到主上下文

Scheduler 的配置

通过 settings.json 安装:

{
  "packages": ["pi-scheduler"]
}

工具提供三个操作:

操作说明参数
create创建定时任务interval_ms, prompt, recurring
list查看所有任务
cancel取消任务id

常用的时间间隔

间隔interval_ms用途
30 分钟1,800,000短期提醒
2 小时7,200,000定期检查
每天86,400,000日报/日检查
每周604,800,000周报/周检查

Workflow 的配置

通过 settings.json 安装:

{
  "packages": ["pi-workflow"]
}

Workflow 提供两个核心概念:

  1. 因子研究(Factor Research):多轮搜索 + 评估 + 综合
  2. 因子优化(Factor Optimization):初筛 + 解剖 + 组合 + 迭代 + 验证

通常你不需要直接操作 Workflow——AI 会根据任务复杂度自动选择是否启用子代理。

最佳实践

✅ 好的定时任务设计

  • 明确的指令:告诉 AI 具体要做什么,不要模糊的“检查一下“
  • 合理的间隔:不要每 5 分钟检查一次(浪费资源)
  • 有意义的触发:提醒应该是“该做 X 了“,而不是“你在吗“

✅ 好的子代理设计

  • 单一职责:每个子代理只做一件事
  • 明确的输出:子代理应该返回结构化的结果,不是自由文本
  • 适度的并行:3-5 个子代理是甜蜜点,太多会增加综合难度

❌ 常见误区

  • “定时任务可以替代所有手动操作” → 不行,复杂决策还是需要人参与
  • “子代理越多越好” → 太多子代理的综合成本可能超过收益
  • “Workflow 可以做任何事” → 它擅长研究和分析,不适合需要人工判断的决策

下一步

到目前为止,我们介绍了 pi-atelier 提供的所有核心工具。但如果这些工具还不够——你想做一个当前没有的功能呢?

下一章,我们来看如何自己动手开发扩展

自己动手做扩展

为什么要自己写扩展?

pi-atelier 提供了 10 个扩展,覆盖了记忆、规划、规矩、复盘、压缩、自动化等核心场景。但每个项目都有自己的特殊需求:

  • 你的团队用飞书而不是 Slack,需要一个飞书通知扩展
  • 你做游戏开发,需要一个自动管理 assets 目录的扩展
  • 你写学术论文,需要一个 LaTeX 编译 + 引用检查的扩展

💡 扩展的本质就是给 AI 加新工具和新知识

扩展的架构

一个扩展由什么组成?

pi-xxx/
├── package.json        # 包元数据 + pi 扩展配置
├── index.ts            # 入口,注册工具和钩子
├── lib/                # 工具实现
│   └── tools-xxx.ts
├── prompts/            # 提示模板(AI 看到的描述)
│   └── xxx-description.md
└── README.md           # 文档

核心概念

概念说明类比
ToolAI 可以调用的函数给 AI 一把新锤子
Hook在特定时机执行的逻辑给 AI 一个闹钟
Prompt工具的描述(AI 看到的说明)告诉 AI 这把锤子怎么用
Config用户可配置的参数锤子的力度调节

扩展的生命周期

1. pi 启动
     │
     ▼
2. 加载 settings.json 中的 packages
     │
     ▼
3. 安装/更新扩展(npm 或 git)
     │
     ▼
4. 执行扩展的入口函数 `export default function(pi)`
     │
     ├── 注册工具(pi.registerTool)
     ├── 注册命令(pi.registerCommand)
     └── 监听事件(pi.on)
     │
     ▼
5. AI 会话中可以调用新工具

实战:从零写一个“代码统计“扩展

让我们一步步写一个简单的扩展——统计项目代码行数。

第 1 步:创建项目

mkdir pi-code-stats
cd pi-code-stats
npm init -y

修改 package.json

{
  "name": "pi-code-stats",
  "version": "0.1.0",
  "main": "index.ts",
  "piExtension": true
}

💡 "piExtension": true 告诉 pi 这是一个扩展包。"main" 指向入口文件(TypeScript 或 JavaScript 均可,pi 用 jiti 加载)。

第 2 步:写工具实现

lib/tools-stats.ts

import { execSync } from 'child_process';

export function countLines(directory: string, extension: string): {
  total: number;
  files: { path: string; lines: number }[];
} {
  const cmd = `find ${directory} -name "*.${extension}" -not -path "*/node_modules/*" -not -path "*/.git/*"`;
  const files = execSync(cmd).toString().trim().split('\n');
  
  const results = files.map(file => ({
    path: file,
    lines: Number(execSync(`wc -l < ${file}`).toString().trim())
  }));
  
  return {
    total: results.reduce((sum, r) => sum + r.lines, 0),
    files: results.sort((a, b) => b.lines - a.lines)
  };
}

第 3 步:写入口文件

index.ts

import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
import { countLines } from './lib/tools-stats';

export default function (pi: ExtensionAPI) {
  pi.registerTool({
    name: 'code_stats',
    label: 'Code Stats',
    description: '统计项目代码行数。用户说"统计代码"、"多少行代码"时使用。',
    promptSnippet: '统计项目代码行数',
    parameters: {
      type: 'object',
      properties: {
        directory: {
          type: 'string',
          description: '要统计的目录路径'
        },
        extension: {
          type: 'string',
          description: '文件扩展名,如 ts, py, rs'
        }
      },
      required: ['directory']
    },
    async execute(_toolCallId: string, params: any): Promise<any> {
      const result = countLines(params.directory, params.extension || 'ts');
      return {
        totalLines: result.total,
        fileCount: result.files.length,
        topFiles: result.files.slice(0, 10)
      };
    }
  });
}

第 4 步:写工具描述

prompts/stats-description.md

统计项目代码行数。

参数:
- directory(必填):要统计的目录路径
- extension(可选):文件扩展名,默认 ts

返回:
- totalLines:总行数
- fileCount:文件数
- topFiles:最大的 10 个文件

示例:
  code_stats(directory="src", extension="ts")
  → { totalLines: 12340, fileCount: 45, topFiles: [...] }

第 5 步:安装测试

// settings.json
{
  "packages": [
    "./path/to/pi-code-stats"
  ]
}

重启 pi,AI 就能用 code_stats 工具了。

pi-shared-utils:你的工具箱

写扩展时,不用什么都从零开始。pi-shared-utils 提供了一组常用工具函数:

模块功能什么时候用
logger统一日志格式需要打印调试信息
storage跨会话持久存储需要保存配置或状态
paths统一路径处理需要找文件位置
json安全的 JSON 读写需要操作 JSON 文件
validator参数校验需要验证工具参数
settings-backupsettings.json 备份与回滚需要安全写入配置
file-lock文件锁(proper-lockfile 封装)需要防竞态写入
config三层配置合并(defaults → 全局 → 项目)扩展需要可配置参数

使用示例

import { logger, storage, paths } from '@pi-atelier/shared-utils';

// 日志
logger.info('扩展已激活');
logger.warn('配置文件缺失,使用默认值');

// 路径
const projectRoot = paths.getProjectRoot();
const memoryDir = paths.getMemoryDir();

配置 API 示例

如果你的扩展需要用户可配置的参数:

import { getEffectiveConfig } from '@pi-atelier/shared-utils';

const defaults = { threshold: 1000, enabled: true };
const config = getEffectiveConfig('my-extension', defaults, cwd);
// config = 三层合并后的最终配置

调试你的扩展

扩展开发中最常遇到的问题:工具注册了但 AI 不调用、handler 报错了看不到日志、返回结果不是预期的。

查看日志输出

扩展中 logger.info()console.log() 的输出会出现在 pi 的终端窗口中(不是聊天窗口)。调试步骤:

# 在终端中启动 pi(而不是后台运行),这样能看到所有日志输出
pi

# 然后在聊天窗口中让 AI 调用你的工具
# 终端会显示日志输出

确认工具是否注册成功

在 pi 聊天中直接问 AI:

你现在有哪些工具可以用?能看到 code_stats 吗?

如果 AI 看不到你的工具,检查:

  • package.json 中是否有 "piExtension": true
  • settings.json 中包路径是否正确
  • 入口函数是否正确导出(export default function(pi)

常见问题排查

问题原因解决方案
AI 看不到工具piExtension 字段缺失在 package.json 加 "piExtension": true
工具调用报错handler 内部异常查看终端日志中的错误栈
AI 不调用工具description 太模糊让工具描述更具体,包含参数说明和示例
返回值为空异步操作未 awaithandler 加 async,调用加 await
路径找不到相对路径问题paths.getProjectRoot() 获取绝对路径

💡 技巧:开发扩展时,可以在 handler 里先 console.log(JSON.stringify(args, null, 2)) 打印参数,确认 AI 传了什么进来。

发布你的扩展

发布到 npm

# 1. 确认 package.json 信息完整
npm version patch  # 0.1.0 → 0.1.1

# 2. 发布
npm publish --access public

发布后的安装方式

其他用户在 settings.json 中添加你的包名即可:

{
  "packages": [
    "pi-code-stats"
  ]
}

发布前的检查清单

  • package.json 有完整的 descriptionkeywords
  • README.md 按模板写完(安装、使用、配置、示例)
  • LICENSE 文件
  • CHANGELOG.md
  • 代码有基本的错误处理
  • 工具描述(prompts/*.md)清晰完整

扩展开发的最佳实践

✅ 好的扩展设计

  1. 单一职责:一个扩展做一件事,不要把所有功能塞进一个包
  2. 描述即文档:工具的 description 写得足够清楚,AI 不需要猜
  3. 参数校验:在 handler 里验证参数,给出有意义的错误信息
  4. 幂等操作:同样的输入应该返回同样的结果,避免副作用

✅ 工具描述的写法

# 好的描述
统计指定目录下特定类型文件的代码行数。
参数:
- directory(必填):目录路径
- extension(可选):文件后缀,默认 "ts"
返回:{ totalLines, fileCount, topFiles }
# 不好的描述
统计代码

❌ 常见错误

  • 工具名太泛:analyze → 应该是 code_stats
  • 描述太简短:AI 不知道怎么用,会传错参数
  • 忘记处理错误:文件不存在时直接崩溃
  • 返回值太大:整个文件内容作为返回值 → 应该返回摘要

附录:pi 扩展 API 速查

registerTool

pi.registerTool({
  name: string,           // 工具名(唯一标识,AI 用这个调用)
  label: string,          // 显示名(TUI 中展示用)
  description: string,    // 工具描述(AI 看到的,决定 AI 什么时候调用)
  promptSnippet?: string, // 短描述(注入 AI system prompt,不填则不出现在 Available tools)
  promptGuidelines?: string[], // AI 使用指南
  parameters: TypeBox.Object({...}),  // 参数定义(TypeBox schema)
  renderShell?: "default" | "self",  // 渲染模式(默认 "default")
  executionMode?: "sequential" | "parallel", // 执行模式
  async execute(
    toolCallId: string,    // 工具调用 ID
    params: any,           // AI 传入的参数
    signal: AbortSignal | undefined,  // 取消信号
    onUpdate: Function | undefined,   // 流式更新回调
    ctx: ExtensionContext  // 执行上下文
  ): Promise<{ content: [{ type: "text", text: string }], details: any }>
});

registerCommand

pi.registerCommand(name: string, {
  description: string,    // 命令描述
  getArgumentCompletions?: (prefix: string) => AutocompleteItem[],  // 参数自动补全
  handler: async (args: string, ctx: ExtensionCommandContext) => {
    // args: 用户输入的参数(/command 后面的文本)
    // ctx.ui.notify(message, level): 显示通知
    // ctx.compact(): 触发压缩
    // ctx.switchModel(model): 切换模型
  }
});

registerShortcut

pi.registerShortcut(shortcut: string, {
  description: string,
  handler: async (ctx: ExtensionContext) => { ... }
});

事件监听

// 会话生命周期
pi.on('session_start', (event, ctx) => { ... });
pi.on('session_shutdown', (event, ctx) => { ... });
pi.on('session_before_switch', (event, ctx) => { ... });
pi.on('session_before_fork', (event, ctx) => { ... });
pi.on('session_before_tree', (event, ctx) => { ... });
pi.on('session_tree', (event, ctx) => { ... });

// 压缩
pi.on('session_before_compact', (event, ctx) => { ... });
pi.on('session_compact', (event, ctx) => { ... });

// AI 交互
pi.on('before_provider_request', (event, ctx) => { ... });
pi.on('after_provider_response', (event, ctx) => { ... });
pi.on('context', (event, ctx) => { ... });

// Agent 生命周期
pi.on('before_agent_start', (event, ctx) => { ... });
pi.on('agent_start', (event, ctx) => { ... });
pi.on('agent_end', (event, ctx) => { ... });
pi.on('turn_start', (event, ctx) => { ... });
pi.on('turn_end', (event, ctx) => { ... });

// 消息
pi.on('message_start', (event, ctx) => { ... });
pi.on('message_update', (event, ctx) => { ... });
pi.on('message_end', (event, ctx) => { ... });

// 工具执行
pi.on('tool_call', (event, ctx) => { ... });
pi.on('tool_result', (event, ctx) => { ... });
pi.on('tool_execution_start', (event, ctx) => { ... });
pi.on('tool_execution_update', (event, ctx) => { ... });
pi.on('tool_execution_end', (event, ctx) => { ... });

// 其他
pi.on('model_select', (event, ctx) => { ... });
pi.on('thinking_level_select', (event, ctx) => { ... });
pi.on('input', (event, ctx) => { ... });
pi.on('user_bash', (event, ctx) => { ... });
pi.on('resources_discover', (event, ctx) => { ... });

辅助方法

pi.sendMessage(message, options?);     // 发送自定义消息到会话
pi.appendEntry(role, content);           // 向会话追加消息
pi.registerFlag(name, options);          // 注册 CLI 标志
pi.getFlag(name);                        // 获取 CLI 标志值
pi.registerMessageRenderer(type, renderer); // 注册自定义消息渲染器

恭喜你读完了!

现在你已经了解了 pi-atelier 的全部核心概念:

  1. 记忆(pi-memory)— 让 AI 记住知识
  2. 规划(pi-roadmap)— 让 AI 管理任务
  3. 规矩(pi-shepherd + pi-context-manager)— 让 AI 遵守规则、控制信息质量
  4. 复盘(pi-session-analyzer + pi-journal)— 让 AI 记录和回溯工作
  5. 压缩与诊断(pi-smart-compact + pi-context-manager)— 让 AI 在长会话中保持聪明
  6. 自动化(pi-scheduler + pi-workflow)— 让 AI 主动工作
  7. 扩展(pi-shared-utils + 你自己的扩展)— 让 AI 无所不能

欢迎在 GitHub 上提交 Issue 和 PR,一起让 AI 编程助手变得更好!

附录

A. 扩展速查表

扩展npm 安装源码仓库核心工具/命令一句话用途
pi-shepherdnpm:pi-shepherdGitHubshepherd_rules, 规则驱动的钩子引擎AI 行为守卫
pi-ate-memorynpm:pi-ate-memoryGitHubmemory_update, memory_index跨会话知识持久化
pi-roadmapnpm:pi-roadmapGitHubroadmap_plan, roadmap_next, roadmap_done, roadmap_search, roadmap_update任务拆解、进度追踪、依赖管理
pi-context-managernpm:pi-context-managerGitHubpayload_analyze, /record, /context, /distill-config, /aging-config上下文质量控制 + Token 诊断
pi-session-analyzernpm:pi-session-analyzerGitHubsession_search, session_analyze历史会话搜索、分析、接手报告
pi-ate-smart-compactnpm:pi-ate-smart-compactGitHub/smart-compact, /smart-compact-config长会话智能压缩
pi-ate-schedulernpm:pi-ate-schedulerGitHubschedule, /loop, /remind, /tasks定时任务和提醒
pi-ate-workflownpm:pi-ate-workflowGitHubregisterWorkflowTool(供其他扩展调用)工作流框架库
@pi-atelier/shared-utilsnpm:@pi-atelier/shared-utilsGitHublogger, storage, paths, json, validator, settings-backup, file-lock扩展开发工具库
pi-journalnpm:pi-journalGitHub/journal, journal日志报告生成(git 活动 + 会话事件 + 记忆变更)
pi-usage-statsnpm:pi-usage-statsGitHubusage_stats工具使用次数统计

💡 安装方式:在 settings.jsonpackages 数组中添加 "<npm包名>""npm:<npm包名>"。开发者也可以用 "git:github.com/catlain/pi-<name>" 从源码安装。

B. 推荐扩展组合

个人项目(轻量组合)

{
  "packages": [
    "npm:pi-ate-memory",
    "npm:pi-roadmap",
    "npm:pi-ate-smart-compact"
  ]
}

核心三件套:记住知识 + 管理任务 + 长会话不笨。

团队项目(标准组合)

{
  "packages": [
    "npm:pi-shepherd",
    "npm:pi-ate-memory",
    "npm:pi-roadmap",
    "npm:pi-session-analyzer",
    "npm:pi-ate-smart-compact"
  ]
}

加上规矩和复盘能力。

大型重构(全量组合)

{
  "packages": [
    "npm:pi-shepherd",
    "npm:pi-ate-memory",
    "npm:pi-roadmap",
    "npm:pi-context-manager",
    "npm:pi-session-analyzer",
    "npm:pi-ate-smart-compact",
    "npm:pi-ate-scheduler"
  ]
}

全量安装,充分利用诊断和自动化能力。

C. pi 内部机制速览

Compaction(压缩)

pi 内置了上下文压缩机制。当对话历史接近上下文窗口上限时,pi 会自动压缩旧对话。Smart Compact 扩展增强了这个机制——它会识别关键信息(决策、约定、结论)优先保留。

Distill(蒸馏)

工具返回的结果可能很大(比如读取一个 1000 行的文件)。pi 内置了 distill 机制来压缩工具输出。pi-context-manager 扩展提供了:

  • 自动 distill:超过阈值(/distill-config)的工具输出自动压缩
  • 首次全文上限firstSeenCap/distill-config --cap)限制首次输出大小
  • Tool Result Processor:特定工具类型的格式化精简(/processor-config
  • Aging 淘汰:自动淘汰旧工具输出(/aging-config

Tool Call 生命周期

1. AI 决定调用工具
     │
     ▼
2. Shepherd tool_call 钩子(rewrite / block / notify / steer)
     │
     ▼
3. 执行工具
     │
     ▼
4. Context Manager distill + processor 处理返回值
     │
     ▼
5. Shepherd tool_result 钩子(notify / steer)
     │
     ▼
6. AI 生成回复
     │
     ▼
7. Shepherd message_end 钩子(steer / notify)
     │
     ▼
8. 结果返回给用户
     ...
     ▼
9. Shepherd agent_end 钩子(notify:提醒 commit / 记忆更新)

会话存储

所有会话数据存储在 ~/.pi/ 目录下:

~/.pi/
├── roadmap/              # 全局路线图
└── agent/
    ├── settings.json         # 全局配置(安装扩展、provider)
    ├── mcp.json              # MCP server 配置
    ├── memory/               # 全局记忆文件(L1)
    ├── skills/               # 全局技能
    ├── extensions/           # 内联扩展
    ├── agents/               # 子代理定义
    ├── npm/node_modules/     # npm 安装的扩展包
    ├── git/                  # git 包安装位置
    ├── sessions/             # 会话历史记录(JSONL)
    ├── distill/              # context-manager 的数据
    │   └── recordings/       # payload 录制文件

{project}/.pi/
├── settings.json         # 项目级配置(覆盖全局)
├── memory/               # 项目级记忆(L2)
└── roadmap/              # 项目级路线图

D. 常见问题

Q: 安装扩展后不生效?

检查:

  1. settings.json 格式是否正确(JSON 语法)
  2. 包名是否拼写正确
  3. 重启 pi(扩展需要重启才能加载)

Q: 记忆文件太多怎么办?

pi-memory 会自动检查文件数量。超过 25 个时建议清理,超过 40 个会拒绝写入。清理方法:

  1. 合并同主题的多文件
  2. 删除过时的记忆
  3. 大文件拆分为小文件

Q: Shepherd 规则不生效?

检查:

  1. 全局规则通过 shepherd_rules 工具管理(或编辑 ~/.pi/agent/extensions/shepherd/rules.json
  2. 项目规则放在 .pi/extensions/shepherd-rules.json
  3. 确认规则中 "enabled": true(或省略 enabled 字段,默认生效)。如需禁用规则,设 "enabled": false
  4. 规则文件变更即时生效,无需额外操作
  5. message_end 钩子匹配的是 AI 回复文本,不是工具参数——不要写匹配工具参数的 pattern

Q: Token 用得太快?

  1. payload_analyzebudgetexpensive 模式找出 token 消耗大户
  2. 用 compact 模式搜索(semantic_code_search(compact: true)
  3. 调低 distill 阈值(/distill-config
  4. 配置 aging 自动淘汰旧内容(/aging-config

Q: payload_analyze 报“无录制文件“?

需要先开启录制:/record on。录制期间正常使用,用完后 /record off

English