关于 pi-durable-dev-rules
pi-durable-dev-rules 是面向使用 @earendil-works/pi-durable 及相关包构建应用的开发者的参考技能。它涵盖持久化执行、API、运行时行为和实现范例;该 API 仍处于实验阶段。
主要功能
- 01Harness 设置与存储选择
- 02对话和任务生命周期指南
- 03工具架构与执行模式
- 04崩溃恢复与重放安全
- 05API、运行时及范例参考
适用场景
- 01搭建 Agent 应用 Harness
- 02审查持久化任务和工具代码
- 03诊断恢复或任务阻塞问题
- 04实现前核对包的 API
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ agents365-ai/ 365-skills --skill pi-durable-dev-rules安装后在 Claude Code 中输入/pi-durable-dev-rules确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.skillspector-baseline.yamlassets/workflow-en.drawioworkflow-zh.drawioreferences/codemode-sandbox.mddurable-api.mddurable-guide.mddurable-recipes.mddurable-runtime.mddurable-spec.mdpi-ai-models.mdscripts/build-references.pyREADME.mdrefresh.sh详细介绍
Pi 持久化开发规则
@earendil-works/pi-durable 是一个持久化 Agent 框架:对话、模型轮次、工具调用、任务以及你自己的文档,都会先提交到存储中,再显示出来,因此即使进程在某轮中途退出,也能从中断处继续。它与 @earendil-works/pi-ai(模型访问、工具、消息)和 @earendil-works/chord(受追踪的 JSON 状态)一同发布在 pi monorepo 中。
有两点会决定大多数设计选择:
- API 处于实验阶段。 1.2.0 版与 monorepo 同步;每次发布都可能引入破坏性变更。请依据你安装的版本引用并编写代码,并将签名差异视为版本差异,而非错误。
- 这是一个用于构建 Agent 应用的库,而非编码 Agent 命令行工具。 如需使用 Pi CLI、它的扩展、技能、MCP 和 SDK,请使用同级技能
pi-dev-rules。
何时使用此技能
- 编写或审查导入
@earendil-works/pi-durable或@earendil-works/pi-ai的代码。 - 组装框架:选择存储后端、配置
HarnessOptions、设置、注册表和执行环境。 - 对话:提交 / 等待、忙碌收件箱(引导、跟进、写入)、重置和交接、分叉、按对话更改 Agent。
- 工具:TypeBox 模式、三种输出通道、使用
executeTool()进行嵌套调用、callers、deferred工具和工具搜索、重放安全、输出限制、图像处理。 - 持久化任务:阶段和检查点、所有权、等待和
failFast、中止、恢复、迁移。 - 子 Agent 和子任务、前台与后台、汇报答案。
- 文档:作用域、历史记录和分叉语义、迁移、检查点、观察。
- 代码模式(在对话的工具上运行的 QuickJS 脚本)、钩子和守卫、压缩、用量。
- 运行时问题:崩溃后哪些内容会保留、为什么输入仍在排队、任务为何被阻塞或成为孤儿、中止如何级联、部分答案为何可见或缺失。
- 扩展框架:通过一致性测试套件验证的存储后端和执行环境、自定义任务和提示词章节。
参考索引:按需加载文件
| 文件 | 内容 |
|---|---|
references/durable-guide.md | 软件包自带的指南,原样打包:快速入门、概念、持久化与恢复、扩展、工具(包括嵌套调用和 callers)、代码模式、系统提示词、按对话配置 Agent、设置、环境、重新加载、监视、繁忙对话、重置与移交、压缩、Agent 事件、钩子、更多对话与分支、中止与子 Agent、子任务、任务图、自有状态、用量与成本、存储、错误和可运行示例。从这里开始。 |
references/durable-api.md | 精确的公开接口:入口映射、Context、Harness/Session/Conversation/ConversationHandle、提交、Tx 事务接口、文档、条目、扩展与分区、钩子、工具与 ToolExecutionApi、任务与 TaskRuntime、环境、存储、测试、观察、错误类,以及各子路径的导出清单 |
references/durable-runtime.md | 工作的实际执行方式:提交线及其规则、一次输入的完整流程、任务检查点、所有权树、按情况恢复、压缩、收件箱边界、实时状态与进度节奏、故障模型、改变行为的参数,以及刻意不包含的目标 |
references/durable-recipes.md | 项目设置、全部 32 个可运行示例的目录,以及包含代码的 19 个示例方案:最简聊天、持久化与输入恰好处理一次、实时 UI、Agent 事件、结构化输出工具、工具调用工具、前台与后台子 Agent、具有持久等待的子任务、编码 Agent、计划模式、按对话配置沙箱、工具覆盖与包装器、扩展状态与迁移、钩子、压缩、代码模式、工具搜索、一致性测试,以及陷阱检查清单 |
references/durable-spec.md | 原样打包的规范性 Pico5 规范(5340 行):记录与生命周期、文档与变更所有权、事务与存储所有权、任务与效果夹层、结构化并发、提交与收件箱、扩展/钩子/工具、内置任务、文档观察、存储契约与故障模型、后端、API 易错点和非目标。发布前请阅读第 12 节 |
references/pi-ai-models.md | 原样打包的模型层:createModels()/builtinModels()、提供方工厂与身份验证、查询模型目录、环境变量、使用 TypeBox 定义工具、约束采样、事件参考、图像输入、用量与成本、重试,以及最终写入条目的消息类型 |
references/codemode-sandbox.md | 原样打包的 @earendil-works/pi-codemode:脚本可以调用的内容、注入的全局变量、沙箱模块接口、确定性与内存限制,以及本地与远程沙箱 |
速查表
npm install @earendil-works/pi-durable @earendil-works/pi-ai @earendil-works/chord
node --conditions=source --experimental-strip-types app.ts # run TS directly against the source
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
import { createRegistry, Harness, AssistantEntry } from "@earendil-works/pi-durable";
import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node";
const context = BACKGROUND_CONTEXT;
const harness = await Harness.open(await openNodeSqliteStorage("./session.sqlite"), {
models: builtinModels(), registry: createRegistry(), env: (target) => new NodeExecutionEnv({ cwd: target.cwd ?? process.cwd() }),
}, context);
const root = await harness.root(context, { agent: { model: { provider: "openai", modelId: "gpt-6-sol" } } });
harness.resume(); // continue unfinished work from a previous process
const submission = await root.submit({ type: "input", content: "Hello", requestId: "greeting-1" }, context);
const settled = await submission.wait(context); // "done" | "unanswered"
if (settled.status === "done" && settled.type === "input") {
const answer = await root.commit((tx) => tx.entry(AssistantEntry, settled.answer), context);
}
await harness.close(context);
存储:MemoryStorage(不持久化任何内容)、openNodeSqliteStorage(path)(长期运行时的默认选择;使用 WAL,提交在进程崩溃后仍能保留)、openNodeJsonlStorage(directory, context)(仅追加,整个历史记录保留在内存中)、openDurableObjectSqliteStorage(ctx.storage)。
每次只能由一个进程拥有某个存储。
硬性规则
- 写入都必须经过同一条提交路径,且提交存储前不会显示任何内容。 在提交回调中,只能通过
tx读取;在首次写入表之前,先读取所需的每一行表数据(ReadAfterWrite),且绝不要在其中等待耗时操作:模型、工具、进程、网络、人类、嵌套提交或会话等待者。 - 每个异步调用都要传入一个
Context。 取消它只会取消该调用或等待,不会取消持久化工作。无需取消时使用BACKGROUND_CONTEXT,在宿主边界使用withCancel()。withoutAbortSignal()用于close()等必须完成的清理操作。 - 只有提交后才能报告已完成的操作。 任务步骤的模式是:提交意图、执行操作、提交结果。如果操作完成后、提交前发生崩溃,该步骤会重新运行,因此要让步骤具备幂等性,使用
runtime.memo()实现首个写入者胜出,并且只有在重新运行确实安全时,才为工具设置replay: "safe"。不安全的工具若被中断,会向模型返回interrupted错误结果,其中包含截至目前已提交的输出。 - 工具有三个输出通道。
output是模型读取的内容,structuredOutput是调用该工具的程序读取的内容(会根据structuredOutputSchema验证;除非结果是错误,否则必须提供),details用于宿主 UI。只有output和details会进入对话记录。 - 每次工具调用都是由其调用方拥有的独立持久化任务。 使用
api.executeTool()发起的嵌套调用会将结果返回给调用方,而不会写入对话记录;重新运行时会复用同一个键。取消上下文只会停止等待。嵌套结果的usage已计入其自身工具,再次转发会导致重复计数。 - 所有权决定生命周期。 每项任务创建时都要指定所有者;每段对话要么没有所有者,要么归某项任务所有。中止会向下传播;只有普通的所属工作全部完成后,任务才会结束(在此之前会存储为
completing);background: true的任务是一个边界,普通中止和空闲等待都会跳过它。绝不要在中止处理程序中创建所属工作(此时任务已标记为中止):应在原处补偿,或使用由对话拥有的后台任务,并调用runtime.waitForTask()。 - 运行会话拥有自己的写入。 不要向有活动运行会话的对话追加原始条目:应使用写入提交,因为原始
tx.appendEntry()无法替代它,也不会执行过期检查。 - 以
pi.为前缀的名称属于内置项,适用于任务名称、文档类型和条目类型(pi.user、pi.assistant、pi.system、pi.tool-result、pi.reset、pi.compaction、pi.agent、pi.provider、pi.live、pi.inbox、pi.usage、pi.generation、pi.tool)。没有任何机制强制遵守此规则,重用这些名称会与框架行为冲突。 - 存储错误会终止框架。 出错的调用会被拒绝;之后的每个调用和待处理的等待都会以
SessionFailed被拒绝;监视会以session_failed结束;onReport会收到一次错误通知。不会重试;要恢复,请重新打开并使用相同的requestId重新提交。无效请求只会导致该次调用失败(StorageRequestError),你自己的代码出错则只会导致其自身单元失败。 - 文档按类型寻址,而非按对象寻址。 类型和可见名称是持久化协议的一部分:值迁移不能重命名类型(应复制并停用旧类型);存储的版本高于代码版本,或低于代码版本但没有
migrate,都会导致访问失败。只有带类型的tx.doc()才会创建不存在的文档。 snapshot()、文档状态和监视返回的值都是受跟踪的容器。 绝不要修改它们;先复制。监视最多保留 100 个未交付帧,之后会合并为一个完整值替换帧,因此必须看到每次状态转换的消费者需要自行记录日志。- 扩展是整体选择的。 Hook 和包装器仅在选中相应扩展时才会生效,因此,如果某个对话将工具指定为显式数组且未包含某个防护扩展,该对话运行时就不会启用它。包装器必须是纯函数;卸载扩展会停止后续使用,但不会释放正在运行的调用所占用的资源。
- 提示词章节必须保持稳定。 如果章节文本在内容没有实际变化时仍会变化(例如受时钟或随机值影响),每次请求都会追加一条
pi.system条目,并破坏提供方提示词缓存。设置可以是 getter:每次使用时都会读取,且绝不会存储。 - 该 API 仍处于实验阶段,Pico5 规范是一份设计文档。 有关行为,请阅读
references/durable-guide.md;规则的重要性不明确时,请阅读references/durable-spec.md;发布前请阅读规范的第 12 节(API 易错点)。
评论
支持 Markdown