项目初始化与最小化Agent构建

本期视频由博主小木头主讲,旨在展示基于 Pi Durable 框架快速迭代出一个基本款 Agent 的开发体验。视频从一个空白目录开始,首先构建一个使用 DeepSeek 模型、以 SQLite 为存储的最小化应用,目标是让用户能够与其进行直接对话。在开始编码前,需要确认 Node.js 和 npm 的版本已就绪,并确保配置了 DEEPSEEK_API_KEY 环境变量。项目初始化包括安装核心依赖库:chord、pi-ai 和 pi-durable,同时补充 TypeScript 支持并将项目设置为 ES module。在 agent.ts文件中,首先通过 createModels() 创建模型集合,并注册 deepseekProvider()。该提供者默认从环境变量读取 API Key,若需更换服务商仅需修改此部分配置。

“这里呢并不打印这个key 只确认它的存在”

存储层方面,代码使用 openNodeSqliteStorage() 打开当前目录下的 .mini-agent.sqlite SQLite文件,并将其传递给 Harness.open().root()。首次调用时,系统会创建一个主会话(根会话)并记录当前使用的模型;后续再次打开同一份存储时,将恢复该会话。命令行逻辑通过循环读取用户输入,调用 submit() 提交请求,并使用 wait()等待本轮对话结束。当用户输入简单问题时,模型正常回答,且目录中生成了 SQLite 文件。这证明会话数据并非仅存在于进程内存中,而是已持久化写入数据库。通过 sqlite3 命令检查发现,数据库中新增了 conversations、entries、submissions 和 tasks 等表及记录。查看 submission 的 request ID 和状态,确认了对话状态的持久化存储机制。

迭代一:集成工具能力与执行环境

在最小化 Agent 基础上进行第二次迭代,核心目标是赋予其 工具(Tools) 能力。Pi Durable 内置了 read、write、edit 和 bash 四个常用工具,统一封装在 CodingTools 扩展中。开发者只需将这些工具安装进 registry 即可启用。关键在于理解工具与执行环境的分离:工具本身仅定义能力,而 NodeExecutionEnv 才是它们实际工作的环境。在本演示中,执行环境被指向当前项目目录(Demo workspace),这意味着 Agent 能够直接读写该工作区内的文件,而非在隐藏的沙箱中操作。

为了验证工具是否被正确调用,代码中订阅了会话事件并仅打印 tool_execution_start。当用户要求 Agent 检查项目文件时,终端日志首先显示了 read 或 bash 的执行事件,随后才是最终的回答。这一变化标志着 Agent 从“仅给建议”转变为“能在指定目录中执行具体操作”。然而,通用工具虽然解决了 Agent “能做什么”的问题,但面对特定项目或任务时,Agent 仍不清楚用户希望它按照何种流程执行以及输出何种格式。因此,需要引入更高层级的抽象——Skills(技能)来规范工作流。

迭代二:引入 Skills 与工作流编排

第三次迭代的重点是加入 Skills 支持。Pi Durable 框架本身不直接负责加载 Skills,官方示例借用了 loadSkills() 和 formatSkillsForPrompt() 两个函数/接口。本演示遵循这一模式,读取用户目录和项目目录中的 Skills。这里有一个重要的技术细节:formatSkillsForPrompt() 函数不会将所有 SKILL.md 的完整内容塞入系统提示词,而是仅放入名字、描述和文件路径。当模型判断当前任务需要某个 Skill 时,才会通过 read 工具读取完整的说明文档。这种按需加载机制有效控制了上下文窗口的大小。

“模型判断当前任务 需要某个skill的时候 再用read工具读取完整的说明”

项目中放置了一份 todo-report Skill,其要求扫描 src 目录下所有的 TODO 和 FIXME,并将结果写入 TODO-REPORT.md。报告需包含文件行号、类型注释和优先级,并统计总数。当用户询问“有哪些待办事项”并要求生成报告时,事件流首先触发 read 读取 todo-report/SKILL.md,接着执行 bash运行 grep命令进行扫描,最后通过 write 输出报告。结果显示报告中包含三项内容(两个 TODO、一个 FIXME)。至此,Agent 通过 Tools 提供基础能力,并通过 Skills 将这些能力组织成符合项目需求的工作方式。

持久化测试:正常退出与上下文恢复

为了验证数据的持久性,进行了一次温和的测试。输入 exit 让第一个进程正常结束,随后重新运行程序。虽然新进程的代码可能已更新,但它打开的是同一个 .mini-agent.sqlite 文件。当用户询问“刚才聊了什么”以及“报告写到哪个文件了”时,Agent 能够准确复述上一轮的任务细节。它知道自己读取过哪个 Skill、执行了哪些命令(如 grep),并明确指出报告保存在 TODO-REPORT.md 文件中。

“在这里我们并没有额外 做一套所谓的长期记忆”

这一结果证明,对话记录、工具调用及其结果原本就存储在指定的数据库中。通过 root() 重新获取原来的主会话后,Agent 自然能够沿用这些上下文。这种机制无需额外开发复杂的长期记忆模块,即可实现会话的连续性。然而,正常退出再进入仅证明了“状态是连续的”,更严峻的挑战在于:当任务正在执行、工具调用中途进程突然崩溃时,系统能否恢复?

压力测试:任务中断与断点续传

最后一次迭代旨在验证 Pi Durable 的任务恢复能力。代码中新增了两个用于模拟长任务中断的工具:fetch_report(模拟读取报告,分为 12 个步骤)和 send_email(模拟产生外部副作用的操作)。这两个工具均通过 defineTool() 定义并安装进 registry。其中,fetch_report 声明了 replay: "safe",表示进程中断后可以安全地重新执行;而 send_email 未声明安全重放,因为重复发送邮件可能产生副作用。

测试过程如下:运行 node crash.ts start agent.sqlite 提交长任务。当两个工具并行执行到第 7 步附近时,在另一个终端使用 kill -9 强制结束进程。随后运行 node crash.ts resume agent.sqlite,新进程打开同一份数据库并调用 harness.resume()。日志显示,系统没有创建新的请求 ID,而是找回了原来的 request ID 和 submission。由于 fetch_report 允许安全重放,系统从第一步重新执行至第 12 步并成功完成。最关键的不是重启后重跑,而是系统知道之前发生过什么:持久化的会话、任务状态和工具结果均被保留,恢复逻辑据此决定从哪里继续。

“durable并不等于所有 操作都能够无脑地重试”

对于 send_email,系统将其状态记录为 “interrupted”,并附带提示说明该工具可能已部分执行。Agent 最后的回答中没有自动重发邮件,而是明确告知用户邮件发送被中断。对于涉及扣款、发布内容等敏感操作,这种区分至关重要。Pi Durable 提供的不是无脑重试,而是足够完整的状态信息,让应用和 Agent 在恢复时能做出更可靠的判断。

结论

回顾这四次迭代,App 的主体代码并未发生根本性变化。第一次迭代添加了模型(DeepSeek)和存储(SQLite)支持;第二次迭代通过 registry 添加工具能力,并明确执行环境为当前目录;第三次迭代引入 Skills,实现了基于项目需求的能力编排;最后一次迭代在同一份存储基础上,验证了中断任务的恢复执行能力。

“这就是 Pi Durable 给 开发者的Harness编排体验”

Pi Durable 通过明确的编程接口,解决了模型集成、工具安装、执行环境配置及状态保存等核心问题。演示中每一步仅增加一种能力以验证新行为,但其核心价值在于:开发者可以用很少的代码先让基本 Agent 跑起来,随后基于同一框架和简单接口,轻松添加智能体产品所需的各类能力。这种开发过程不仅降低了门槛,也通过内置的任务恢复机制提升了应用的鲁棒性。博主强调,Durable 并非意味着所有操作都能无脑重试,而是提供完整状态以支持可靠恢复。