跳转到内容

Pi 1.3.0:介绍与整体架构

本文件回答三个问题:Pi 是什么?内部怎么分层?关键设计取舍是什么? 基于学习区版本 v1.3.0(71023aec,2026-10-15 发布)。路径均相对 $PI_SRC。 用法:配合学习路线开头的「七块」总表阅读;每个学习阶段深入其中一块。

README 的原话:“Pi is a minimal, extensible agent harness that you can make your own.”

逐词拆解这句话:

  • agent harness(代理挽具):Pi 不是模型,也不是编辑器。它负责把「模型、工具、上下文、会话」编排成一个能自动干活的系统——模型是发动机,Pi 是传动系统和底盘。
  • minimal(极简核心):核心保持小而稳。连 MCP、工具搜索、codemode 这类别家当核心功能做的东西,Pi 都尽量以扩展形态内置(见 packages/coding-agent/src/extensions/ 下的 codemode、mcp、tool-search、llama)。
  • extensible(可扩展):扩展是进程内 TypeScript 模块;技能、提示模板、主题按需加载;可打包为 Pi packages 经 npm / git 分发。
  • make your own:官方明确跳过 sub-agents、plan mode 这类功能——不是做不了,而是这类能力更适合由你或社区按自己的方式长出来(“Adapt Pi to your workflows, not the other way around.”)。

五种使用形态(同一个引擎、同一套会话机制,详见阶段 7):

形态 入口 适合
交互模式 终端直接运行 pi 人机结对:流式输出、工具块、斜杠命令
print pi -p "任务" 一次性执行拿结果、脚本化
JSON pi --mode json stdout 输出完整事件流,供程序解析
RPC pi --mode rpc stdin/stdout JSONL 协议,外部进程双向控制
SDK TypeScript 库(@earendil-works/pi-coding-agent) 进程内嵌:建会话、发任务、收事件

二、部件全景:任何 agent 都是七块

跳转到“二、部件全景:任何 agent 都是七块”

学习清单的总表在这里补上「在 Pi 里的位置」一列:

部件 要回答的问题 Pi 中的对应 阶段
循环 一轮怎么推进?何时调模型、执行工具、停? packages/agent + coding-agent 会话编排 2
上下文 每次发给模型什么?怎么存、压、找回来? 会话树 JSONL、压缩、系统提示词 3
工具 模型能做哪些动作?怎么执行、校验、展示? packages/coding-agent/src/core/tools 4
模型接入 怎么把多家厂商统一成一套接口?凭据怎么管? packages/ai + 模型目录 5
扩展 能力怎么长出来?核心与生态怎么分工? 扩展 / 技能 / MCP / codemode 6
界面 同一个引擎怎么同时服务人、脚本、程序? TUI / print / JSON / RPC / SDK 7
工程 怎么让改动可靠、可持续演进? 测试(faux provider)/ check 流水线 / 发布流程 8

三、分层架构:每一层到底在做什么

跳转到“三、分层架构:每一层到底在做什么”

先记住一个总原则:Pi 把「和人交互、安排任务、循环调用模型与工具、连接模型厂商」拆成了不同的层。 换界面、换模型或增加工具时,只需改动对应的一层,不必推倒重来。

3.1 先看简化图:任务向下,事件向上

跳转到“3.1 先看简化图:任务向下,事件向上”
┌──────────────────────────────────────────────────────────────┐
│ 入口层:交互 TUI / print / JSON / RPC / SDK │
│ 负责接收输入、显示结果;不负责决定模型该做什么 │
└───────────────────────────┬──────────────────────────────────┘
│ ① 输入:文字、图片、命令
┌───────────────────────────▼──────────────────────────────────┐
│ 应用层:coding-agent(产品本体) │
│ │
│ AgentSession 一次任务的「总调度」 │
│ 整理输入、检查认证、排队、压缩、启动与收尾 │
│ │
│ SessionManager 会话「账本」 │
│ 读写 JSONL 会话树,按活动分支重建上下文 │
│ │
│ 工具 / 扩展 / 技能与提示模板 / 模型选择与凭据 │
└───────────────────────────┬──────────────────────────────────┘
│ ② 历史消息 + 系统提示词 + 工具声明
┌───────────────────────────▼──────────────────────────────────┐
│ 引擎层 │
│ agent:循环执行「请求模型 → 执行工具 → 再请求模型」 │
│ ai:把不同厂商的请求格式和流式回复统一成一套内部格式 │
└───────────────────────────┬──────────────────────────────────┘
│ ③ 统一格式的请求
┌───────────────────────────▼──────────────────────────────────┐
│ 模型供应商:Anthropic / OpenAI / Google / … │
│ 真正生成回答的地方 │
└──────────────────────────────────────────────────────────────┘
事件沿反方向返回:
模型回复片段、工具开始/结束、消息落盘……
最终由入口层决定怎么显示,或交给外部程序处理。

看这张图,只需抓住三件事:

  1. 向下:一次任务被逐层整理成模型能处理的请求。
  2. 向上:执行过程中的状态变化以「事件」返回,界面和程序都消费它。
  3. 旁边:会话账本持续记录发生了什么,并在下一次请求前提供历史。
层 你可以把它想成 负责 不负责
入口层 前台 收输入、显示流式输出和工具进度 决定如何循环、如何压缩历史
应用层 项目经理 把一次任务安排妥当:会话、工具、扩展、资源、认证 具体调用哪家模型、模型内部怎么运行
引擎层 执行队 + 翻译 推进循环、执行工具、统一模型接口 终端长什么样、用户按了哪个快捷键
供应商 外聘专家 根据请求生成文本或工具调用 记住上一次请求;保存 Pi 的会话历史

表格里的「不负责」很重要。分层清不清楚,主要看边界,而不是看名词多不多。

3.3 应用层:两个最容易混淆的对象

跳转到“3.3 应用层:两个最容易混淆的对象”

同属应用层,AgentSession 和 SessionManager 经常被混为一谈。可以这样区分:

对象 像什么 具体管什么
AgentSession 一次任务的调度员 判断扩展命令、处理输入、展开技能/模板、流式时排队、检查认证、压缩检查、触发扩展事件、启动和收尾一次运行(run)
SessionManager 档案馆管理员 把消息和状态追加到 JSONL;维护 id/parentId 树;确定活动分支;生成投影;读写标签、模型切换等记录

一句话:AgentSession 管「这次任务怎么跑」,SessionManager 管「发生过的事情怎么存、怎么取」。

真正的「问模型—跑工具—再问模型」循环不在这两个对象里,而在引擎包的 agent 中。

3.4 引擎层:循环与翻译

跳转到“3.4 引擎层:循环与翻译”
  • agent:循环引擎。 它只关心消息、回合(turn)、工具调用、事件,不知道外面是终端、脚本还是另一个程序。也正因如此,同一个引擎才能同时服务五种使用形态。
  • ai:模型适配层。 Anthropic、OpenAI、Google 的鉴权方式、请求字段、流式事件格式各不相同;ai 把这些差异收在自己的 provider 适配器里,向上只交出统一格式的文本增量、思考增量、工具调用增量、结束和错误事件。
  • 模型目录也由 ai 提供:上下文窗口、价格、能力标记等。它让「有哪些模型、各自能做什么」不必硬编码进循环。

如果不熟悉 provider:在这里可以把它理解成「某一家模型厂商的接入插件」。Pi 的业务代码只面对统一接口,不直接写满 if Anthropic ... else if OpenAI ...。

3.5 入口层:五张门,同一个内核

跳转到“3.5 入口层:五张门,同一个内核”

交互 TUI、print、JSON、RPC、SDK 看起来差别很大,但做的事都只有两件:

  1. 把输入交给同一个会话系统;
  2. 消费同一套事件流,并决定怎样显示或转发。

因此,你在终端里看到的行为,和脚本通过 JSON/RPC/SDK 得到的行为,来自同一套引擎与会话机制。print 只保留最终答案,JSON 输出完整事件,RPC 允许外部进程双向控制,SDK 则把会话嵌入 TypeScript 程序。这部分到阶段 7 再展开。

部件 作用 需要时再记
tui 终端差分渲染 交互模式使用
mcp 连接外部 MCP 工具服务器 外部工具生态
codemode 在沙箱里运行 JS,通过注入的工具完成复杂操作 工具编排
tool-search 在工具较多时寻找合适的工具 扩展形态内置
chord → durable → env 插件宿主、持久化运行时、SSH 远程执行 平台化运行时,独立演进
telemetry / evals 遥测数据格式、评测框架 工程与测试

这些部件不是理解主链路的入口。初读时只要知道它们「按需接入」,不要被包名打断主线。

3.7 两条贯穿全系统的规则

跳转到“3.7 两条贯穿全系统的规则”
  • 会话是账本。 文件在 ~/.pi/agent/sessions/--<工作目录>--/*.jsonl;里面是一棵可追加的树。当前所在的末端叫 leaf,从 leaf 回溯到根就是「活动分支」,下一次请求的历史从这条分支推导。
  • 权限沿操作系统走。 Pi 不实现一个自制的细粒度沙箱;工具和扩展默认拥有 Pi 进程本身的系统权限。打开项目前先做信任决定;需要强隔离时,把整个 Pi 放进容器,而不是只给某个工具加一层假权限。

这两条规则会在第四、五章反复出现,先各记一句即可。

四、一条请求的完整旅程

跳转到“四、一条请求的完整旅程”

这一章把第三章的图真正走一遍。代码名可以先跳过,只要每一步都能说出「谁把什么交给了谁」。

术语 一句话解释 最容易误解的地方
Prompt 你交给 Pi 的任务文本(可能还带图片、文件引用) 它只是新输入,不是模型这次看到的全部内容
Run 从提交这次输入,到 Pi 确认「不会再有自动工作」为止的一整段执行 一个 run 里通常有多个 turn
Turn 一次「模型回复 + Pi 执行它要求的工具」的完整回合 模型只说话、没有调用工具时,也算一个 turn
Context 本次请求真正发给模型的全部内容:历史、系统提示、工具说明、当前输入等 它不等于会话文件;文件里有很多不进上下文的东西
Projection 从当前活动分支按规则整理出的「模型可见的对话历史」 它不是改写原文件,而是每次临时算出来的视图;完整上下文还要再加上系统提示词、工具声明和当前输入
Event 执行过程中发出的状态通知,如回复片段、工具开始/结束、消息落盘 不是日志附赠品;界面和外部程序就是靠它工作的

优先记住 run、turn、context、projection 这四个词,后面会反复出现。

4.2 从回车到答案:分阶段看

跳转到“4.2 从回车到答案:分阶段看”

阶段 A:入口层收下输入

跳转到“阶段 A:入口层收下输入”

你在输入框敲回车。交互界面只做一件事:把文本(以及可能的图片)交给 AgentSession.prompt()。它不自己猜模型,也不自己执行工具。

阶段 B:AgentSession 做「发送前检查」

跳转到“阶段 B:AgentSession 做「发送前检查」”

prompt() 像总调度,依次处理:

  1. 扩展命令:输入以 / 开头且命中扩展命令,就直接执行,不调用模型。
  2. 输入处理器:扩展可以拦截、改写甚至取消这条输入。
  3. 技能与提示模板:把 /skill:... 或模板展开成真正的提示词。
  4. 流式排队:如果上一条回复还在流式输出,这次输入不会硬插进去,而是按类型放进 steer 或 follow-up 队列。
  5. 认证检查:确认已经选了模型,并且该 provider 有可用凭据;否则报错并提示登录。

这些检查都过了,才进入 _startRun() / _prepareRun(),准备真正启动一次 run。

这一阶段做三件事:

  • 压缩检查:如果历史已经接近模型上限,先用摘要压缩旧历史。
  • 触发 before_agent_start:扩展可以在这里补消息、调整工具集或系统提示。
  • 组装起始消息:把当前输入、待处理的下一轮消息和扩展注入的消息整理好,然后交给引擎的 Agent。

注意:这里交给 Agent 的是「这次要新增的消息」,不是整段历史。完整历史会在真正发请求前从会话账本重新算出来。

Agent 收到起始消息后启动 runLoop。这个循环只有一条主线:

准备请求 → 请求模型 → 模型要调用工具吗?
├─ 要:执行工具,记录结果,回到「准备请求」
└─ 不要:结束本轮;若没有后续工作,结束本次 run

「工具结果回来后自动再问一次模型」并不是某个工具的魔法,而是循环本身的设计。

阶段 E:重建本次上下文

跳转到“阶段 E:重建本次上下文”

这是整条链路最值得慢下来的一步。真正发给模型之前,Pi 会按顺序做三件事:

  1. 找活动分支:从当前 leaf 沿 parentId 回溯到根,得到这条对话路径。
  2. 生成投影:在分支上应用最新压缩检查点和上下文编辑,得到「这次应该让模型看到的对话历史」。
  3. 转换数据格式:把 Pi 内部的 AgentMessage[] 经 convertToLlm 变成模型接口要求的 Message[],再连同系统提示词和工具声明组成请求。

用一张小图表示:

会话文件(完整账本,只追加)
└─ 活动分支(当前这条路径)
└─ 投影(应用压缩/编辑后,模型可见的对话历史)
└─ convertToLlm(内部格式 → 模型格式)
└─ 系统提示词 + 工具声明 + 当前输入
└─ 发给模型

两个词的区别:

  • 投影决定「哪些内容留下、哪些被摘要代替、哪些被编辑」,是内容层面的整理;
  • convertToLlm 只负责「把数据从 Pi 的格式翻译成模型 API 的格式」,不决定历史内容。

阶段 F:调用模型,边生成边显示

跳转到“阶段 F:调用模型,边生成边显示”

引擎通过 ai 选择 provider,带上鉴权信息和模型参数发出请求。回复不是等全部生成完才回来,而是以事件流返回:

  • 文本开始 / 文本增量 / 文本结束;
  • 思考开始 / 思考增量 / 思考结束;
  • 工具调用参数增量;
  • 完整消息与错误。

界面看到 message_update 就能实时刷新。等一条完整消息结束时,message_end 会触发落盘:消息被追加进会话 JSONL。

阶段 G:有工具调用,就再开一轮

跳转到“阶段 G:有工具调用,就再开一轮”

模型的回复里可能只有文字,也可能同时包含一个或多个工具调用。若有工具调用:

  1. 引擎校验并执行工具;
  2. 工具结果变成 toolResult 消息,追加到上下文并落盘;
  3. 循环回到阶段 E,重新从会话账本计算上下文;
  4. 模型看到工具结果,决定继续调用工具还是给出最终回答。

例如你让它修一个登录 bug,实际可能连续发生:

用户:修复登录页报错
→ 模型要读文件
→ Pi 读文件,返回内容
→ 模型要搜调用点
→ Pi 搜索,返回结果
→ 模型要改代码
→ Pi 修改,返回结果
→ 模型给出最终说明(没有工具调用了)

这就是为什么一次用户输入可能对应很多次模型请求和工具执行。它们都属于同一个 run,但分属多个 turn。

阶段 H:agent_end 不等于「彻底结束」

跳转到“阶段 H:agent_end 不等于「彻底结束」”

普通的 steering / follow-up 会在底层 runLoop 内部先处理完;当没有工具调用、也没有待处理消息时,才发出 agent_end。但这不代表一定空闲,因为会话层还可能:

  • 自动重试一次失败请求;
  • 在上下文超限时压缩并重试;
  • 在 agent_end 处理器里排入新的工作。

只有等到 agent_settled,才表示「Pi 已经没有任何会自动继续的工作,现在真正空闲」。交互界面通常到这时才把控制权完全还给你。

4.3 把全过程压成一张时序图

跳转到“4.3 把全过程压成一张时序图”
你按回车
│
▼
入口层 → AgentSession.prompt()
├─ 扩展命令?──────────────是──► 立即执行,结束
│
├─ 输入处理 / 模板展开 / 排队 / 认证
▼
_prepareRun()
├─ 必要时压缩
├─ before_agent_start 扩展事件
└─ 组装起始消息
│
▼
Agent.runLoop ◄──────────────────────────────┐
│ │
▼ │
从活动分支生成投影 │
│ │
▼ │
convertToLlm → 系统提示 + 工具声明 → 请求模型 │
│ │
▼ │
模型回复 ──有工具调用?──是──► 执行工具 ─────┘
│
否
▼
agent_end
│
├─ 自动重试 / 压缩恢复 / agent_end 新工作?──是──► 继续运行
│
否
▼
agent_settled(真正空闲)

一句话记住:AgentSession 把一次输入安排成 run;runLoop 每一轮都从会话账本重建上下文去问模型;模型要工具就执行后接着问,直到没有自动工作才停在 agent_settled。

容易这样理解 更准确的理解
「会话文件里有 10 万字,模型每次就收到 10 万字。」 不会。模型只收到投影后的对话历史,再加上系统提示词、工具声明等;压缩过的旧内容由摘要代替,原文仍留在文件里。
「压缩就是把旧消息删掉。」 不删。压缩只是追加一个摘要检查点,后续投影用它代替旧消息;原始 entry 还在,之后仍可回溯。
「看到 agent_end 就可以继续输入了。」 这是底层循环结束,不是整个系统空闲;真正的空闲信号是 agent_settled。
「模型会记住上一轮。」 Pi 每次都要把当前上下文重新发给模型;模型本身不保存这段会话,账本和投影由 Pi 负责。

完整细节见阶段 1(请求旅程、事件对照)、阶段 2(循环状态机、三个插口)与阶段 3(会话树、压缩与投影、系统提示词)。

五、六个关键设计取舍

跳转到“五、六个关键设计取舍”

Pi 的很多设计初看有些「拐弯」,其实都在解决一个具体麻烦。下面每个选择都按同一顺序讲:不这么做会怎样 → Pi 怎么做 → 你得到什么、要付出什么。

5.1 小核心,能力放在扩展里

跳转到“5.1 小核心,能力放在扩展里”

麻烦:如果每增加一个功能都往核心塞,核心会越来越难读、难改、难测试。

做法:核心只保留缺了就不能工作的东西:循环、会话、工具接口、模型接入。MCP、工具搜索、codemode 这类能力以扩展或内置模块的形式接入,可以按需启用。

结果:核心小,能力可替换、可组合。代价是扩展机制本身要设计周全;读源码时要分清「核心」和「随产品内置的扩展」。

可以把它想成操作系统内核和应用程序:内核不必内置所有软件,但必须提供稳定的进程、文件、网络接口。

5.2 会话是树,不是一条聊天记录

跳转到“5.2 会话是树,不是一条聊天记录”

麻烦:如果聊天记录只能是一条从头到尾的数组,想回到旧消息重开、保留原文做压缩、查看某条分支都会很别扭;直接覆盖旧记录还可能破坏历史。

做法:每个 entry 都有 id 和 parentId,形成一棵只追加的树。当前所在的 leaf 决定活动分支;从旧位置继续聊天,就是在同一文件里长出新枝。压缩摘要和上下文编辑也各追加一条记录,不覆盖原消息。

结果:可回溯、可分支、可回放;写入时不必改写已有历史。代价是「读历史」不能直接顺着数组读,必须先找到活动分支,再生成投影。

5.3 模型请求无状态,上下文每次重建

跳转到“5.3 模型请求无状态,上下文每次重建”

麻烦:模型不会替 Pi 保存整段会话;而 Pi 又会在历史中压缩、编辑。若把「原始记录」和「这次实际发送的内容」混在一起,就很难判断下一次到底该发什么。

做法:把两者彻底分开。每次请求前,Pi 都从活动分支现算一份投影:应用最新压缩检查点和上下文编辑,再经 convertToLlm 转成模型消息。压缩和编辑只记录「怎么改」,不覆盖原消息。

结果:任何一次请求都能从会话文件重新推导出来,压缩后仍可回到原文;代价是每次请求都要重新计算投影,相关逻辑必须非常可靠。

5.4 用事件流做统一接口

跳转到“5.4 用事件流做统一接口”

麻烦:终端界面、脚本、外部程序都想知道 agent 正在做什么。如果每种界面各写一套调用逻辑,就会出现五套行为、五套 bug。

做法:引擎把所有状态变化发成事件;各种界面只是事件的消费者。TUI 负责绘制,print 只输出最终结果,JSON 把完整事件写成 JSONL,RPC 转发给外部进程,SDK 让 TypeScript 程序直接订阅。

结果:一套核心服务多种形态,调试时直接看原始事件流就能知道执行到哪一步。代价是事件格式必须稳定,消费者要正确处理增量、结束和错误。

5.5 把不同模型厂商翻译成同一套接口

跳转到“5.5 把不同模型厂商翻译成同一套接口”

麻烦:Anthropic、OpenAI、Google 的鉴权方式、请求字段、流式协议和工具调用格式都不一样。如果在循环里到处写厂商分支,新增一家模型就会牵动全身。

做法:ai 包在内部提供 provider 适配器,把请求和回复统一成同一套内部事件。模型目录另外记录上下文窗口、价格、能力等信息;目录数据与代码骨架分开维护,避免频繁变化的数据产生大量无关改动。

结果:新增 provider 只需要改接入层,循环和界面不受影响。代价是适配层要处理各家边界情况;某些厂商独有能力需要通过能力标记或降级策略处理。

5.6 安全边界交给操作系统与容器

跳转到“5.6 安全边界交给操作系统与容器”

麻烦:工具能读文件、跑命令,扩展还能执行进程内代码。自己写一个「权限沙箱」听起来安全,实际很容易留下漏洞,反而给人虚假的安全感。

做法:Pi 不自建一套细粒度权限沙箱。工具和扩展直接使用 Pi 进程的操作系统权限;打开项目时先做项目信任决定;需要强隔离时,把整个 Pi 放进容器(Gondolin 扩展、整进程 Docker、OpenShell 策略沙箱)。

结果:权限模型简单、可预期、可审计。代价是默认权限很大:不要在不信任的仓库里随意运行工具,高风险任务应放进容器。

  • 小核心 + 扩展:系统能长大,但核心不必跟着膨胀。
  • 树形账本:历史能回看、能分支,不必只保留一条线。
  • 每次投影:压缩和编辑不破坏原文,请求内容始终可重新推导。
  • 事件流:同一个引擎可以服务人、脚本和外部程序。
  • provider 适配:换模型不用改循环和界面。
  • 操作系统/容器:安全边界简单明确,不制造「假沙箱」。

这六条不是孤立的设计,而是互相配合的:树形账本负责保存事实,投影负责生成本次视图,事件流负责把过程交给外部,provider 适配负责连接不同模型,小核心和操作系统权限则决定了系统的边界。

11 个包的一句话职责(阶段 0 自检的答案表,这里加依赖备注):

包 一句话职责 内部依赖
agent(pi-agent-core) 通用 Agent 运行时:工具调用循环与状态管理,与 UI/模型解耦 ai
ai 统一多供应商 LLM API:流式事件归一化 + 模型目录 + 认证 telemetry
coding-agent 完整 CLI 应用:会话、工具、模式、界面组装 agent、ai、codemode、mcp、tui
tui 终端 UI 库:差分渲染 无
mcp 独立的 MCP 客户端(外部工具协议) 无
codemode 沙箱 JS 运行时:唯一能力是调用注入的工具 无
chord 插件宿主:热重载、契约、复制状态、JSON-RPC 无
durable 持久化对话/任务/文档运行时 chord、ai、codemode
env 远程执行环境(SSH 部署的守护进程 + 客户端) chord、durable
telemetry 遥测数据的共同格式:统一事件和指标的结构,不绑定某家厂商 无
evals 评测框架(开发期工具) ai、coding-agent

主干:coding-agent → agent → ai → telemetry。完整依赖图见阶段 13.2 节,画图练习见第七章。

文档(packages/coding-agent/docs/) 内容 对应阶段
how-pi-works.md 官方心智模型(六节:loop / context / sessions / interfaces / extensions / trust) 1–3 起步必读
session-format.md 会话树 JSONL 格式:entry 类型、分支、压缩、编辑 3
cli.md / json.md / rpc.md / sdk.md / usage.md 五种界面与用法 7
extensions.md / skills.md / prompt-templates.md / themes.md / packages.md 扩展与资源体系 6
其余(models、containerization、security 等) 按需查阅 5 / 8
examples/sdk/01–14 教程式示例,由浅入深 7、9
仓库根 CONTRIBUTING.md、AGENTS.md、.pi/skills/ 开发规则、发布流程 8

配套章节(可在侧栏继续阅读):

章节 内容 对应阶段
学习路线 路线总纲:七块框架、分阶段路线、通关标准(含完整文档地图) 总纲
阶段 0 环境搭建与两种基础用法 0
阶段 1 一条请求的端到端生命周期 1
阶段 2 agent 循环与事件流 2
阶段 3 会话树、上下文组装与压缩 3
阶段 4 工具管线八道关口 4
阶段 5 模型接入与凭据体系 5
阶段 6 扩展、技能、MCP、codemode 6
阶段 7 五种界面形态与集成 7
阶段 8 工程化、测试与发布 8
阶段 9 完整 mini agent、测试与毕业验收 9
选修区 选修区五块(telemetry / durable / env / chord / evals)与缓存诊断,按需深入 选修

阶段 1–9 按同一种学习节奏组织:先说明问题,再讲概念与流程,然后给出代码位置或小实验,最后用常见误区和自检收束;阶段 0 则按安装步骤展开。做题时以详解为准,官方 docs 用来对照。

  • 源码基线:v1.3.0(71023aec,2026-10-15 发布);请从官方仓库检出对应版本。
  • 开发区:$PI_DEV——主仓库;如果要跟随上游开发线,在这里动手。本课程的主线内容和实验以固定在 v1.3.0 的学习区为准。
  • 参考示例:在线文档 https://pi.dev/docs/latest ;实跑入口是 pi-test.ps1(源码直跑,不过构建)。

配套学习路线编写 · 2026-10-19 · 基于 v1.3.0(71023aec)