大家好,我是小木头。平时我们用 coding agent 帮自己开发项目,那能不能再往前走一步?用这个 Agent 发现它的界面或者功能缺失了一些什么,就直接让它修改自己的软件呢?这一期我们还是继续聊 Pi Durable,来看看 Daniel 做的 Ultimate 怎样把这件事变成了一个可以实际操作的过程。
我在本机用 ChatGPT 订阅把它跑了起来,让它先给自己的界面增加一个清单面板,再追加一条进度条。修改之后,正在打开的页面就直接出现了新的功能,之前勾选的状态也保留着。接着我让它重启核心进程,重启完成后,它继续了同一条请求。这里有几部分在配合:扩展接口、隔离开发、代码加载,以及 Pi Durable 提供的持久化运行。
Ultimate 是一个带 Web 界面的本地 coding agent,由 Daniel 开发。按他的说法呢,这更像是一个自己的、一个小小的玩具项目。你可以和它讨论代码,让它读取文件、执行命令、修改项目;也可以按照项目的需要,定制它自己的工具和界面。这些扩展、角色和配置可以跟着项目保存在 .ultimate/ 目录里。
Daniel 就为自己的博客项目定制了一个博客视图,把文章列表、文章的配音、站点预览和写作 Agent 放在了一起。在这套结构里,Ultimate 组织界面、扩展和协作流程;Pi Durable 负责底层的运行与状态持久化。这个博客案例里,文章、文章的配音和站点预览围绕同一项工作组织起来;那写作 Agent 就在这些内容旁边,处理的是当前项目里的文章和流程。
项目需要什么能力,就可以继续补充对应的界面。Daniel 也用 Ultimate 开发 Ultimate 自己,这让本期的问题更加具象了:开发者正在使用的工具也可以成为当前正在开发的项目。先把项目在本地跑起来,环境需要 Node.js 26(我这里呢使用的是 macOS)。克隆仓库、安装依赖,再启动 Ultimate。浏览器访问终端打印的地址就可以了。
进入界面后选择 OpenAI 的比如 ChatGPT Plus 或 Pro 的登录入口。在浏览器完成授权,就可以选择订阅模型开始对话。Ultimate 和 Pi 共享本地的登录与模型配置。这次演示中呢,我用的是 GPT-6.1 Sol 项目里的协调者角色。设计角色还有摘要功能,都可以单独设置模型;使用订阅时,把这些角色也设成对应的订阅模型。
接着启用项目配置,加载里面的角色和扩展。启动入口背后有一个 supervisor(也就是管理核心进程的程序)。完成登录后,先做一次普通对话,再让它读取项目文件。这样接下来提出界面修改需求时,Agent 已经能够在本地项目里使用文件与命令工具了。
第一个需求蛮简单的:在右侧增加一个演示进度面板,里面有本地启动连接、订阅和自我修改三个条目。点击条目就切换完成状态,底部显示完成了几项。Ultimate 创建了协调会话和独立的工作区,在项目的扩展目录里生成了面板代码以及一份说明文件。然后呢在独立预览里打开这个版本。
咱们现在逐项的点击,完成数量就从 0 变成 1、2、3。刷新页面后,勾选状态呢依然保留。这个需求已经变成了应用里可以使用的功能,是不是很方便呢?很神奇呢?我们把需求交给 Ultimate 之后呢,可以沿着它的实现咱们一步一步的看下去。
先来创建了工作区,再增加扩展文件,然后启动这个版本的预览。这里生成的核心文件是 web.tsx,里面包含了面板的注册和具体的组件。第一次打开预览时,先看三个条目是否齐全;再来检查点击一个条目后,底部数量是否跟着变化。然后继续勾选和取消,看看数量是否始终对应着当前状态。
那最后刷新一次,确认已有选择还能保留。接下来我继续提出需求:在完成数量下面增加一条细的进度条,长度随着勾选数量变化。它修改了同一个扩展,原来打开的页面里,进度条已经出现了;之前的一项完成状态还在,进度条也对应 1/3。不用刷新页面,新增的进度条就出现了;之前勾选状态依然保留着。
在追加修改之前,我保留了一项已经完成的状态,这样呢发生变更后,既能看到新增加的进度条,也能看到旧的交互状态有没有继续保留。咱们在这段的演示中可以看到:新代码进入了原来页面,页面里已有的状态继续参与到了新组件的计算。应用的功能发生了变化,咱们在未来使用中就可以用到这些新开发的功能。
前端更新之后,再来看看核心进程。我通过 supervisor 启动带有这个面板的版本,让它管理核心的重启。我先告诉 Agent 一段文字,让它调用重启工具;然后接着回答,并把这段文字说出来。结果是会话编号仍然是 45,界面的 generation 从 1 变成 2,说明核心进程已经更换过了。
Agent 接着回答并说出重启前给它的那段文字,面板也保留着 1/3 的完成状态。前端更新主要发生在浏览器里;核心进程会负责会话、工具调用、存储和 API,所以它的替换还涉及到正在执行的工作。这段演示呢把两个结果都放在了一起:核心进程已经更换,原来请求还能继续完成。
整个过程是这样的:Agent 调用重启工具;管理程序完成核心进程的切换;随后呢继续处理原来的请求。这种热加载效果看起来是不是像 Agent 在自我进化呢?这两个结果又是怎样实现的呢?先看新增面板,它通过 defineWebExtension 注册,提供面板的标识、标题、图标和组件。
Ultimate 界面已经为扩展准备好了接入的位置,Agent 生成相应的组件就能把需求放进应用当中。勾选状态使用的是 useExtensionUiState 这个接口,把完成条目保存在当前实例的 UI 状态中。所以刷新页面、切换对话之后仍然可以继续使用。
第二轮的进度条这个功能呢,直接复用完成数量计算出对应的宽度。这样一个已有的小组件就能继续随着需求来做变化。注册面板时,模型需要填写的是扩展接口已经定义好的结构:面板标题决定显示名称;组件决定里面的内容;图标和标识让应用能够把它作为一个独立面板来管理。
对于我们这个例子呢,组件中的数据也很简单:哪些条目已经完成以及当前完成了多少项。点击条目会更新已完成的列表,数量和进度条都是从这份状态计算出来的。扩展还可以组合更完整的 UI 或者 Surface,让 Agent 停靠在项目专属应用的旁边。
前面看到的博客视图就属于这种用法:小面板与完整的项目界面之间有一条可以继续进行扩展的路径。这些修改是在独立的工作区里完成的。Ultimate 它的 coordinator(也就是协调者角色)默认使用了 Git worktree,把开发中的版本与正在使用的版本分开。
那它就可以在自己的目录里实现功能、执行检查、启动预览。我们先使用预览,再决定是否集成常规的合并和配置检查由代码管线来进行处理;那需要诊断和修复时,lander 这个角色就可以参与。这样功能实现与版本集成呢就有清晰的分工:worktree 可以理解为同一个 Git 项目的另一份工作目录。
它拥有自己的开发分支,Agent 可以在里面修改文件、运行检查、启动预览。因此这个分工呢很适合持续定制应用:当前使用的版本继续运行,新的需求则在另一份工作目录里实现。预览提供了一个先使用新功能的机会;代码修改完成以后,它是怎样进入运行中的程序呢?
我们要看改的是哪一层。前端组件和扩展界面,它通过 Vite 实现热更新;服务端扩展保存后重新安装。正在执行的工具调用用旧代码完成,后续调用呢使用新代码。如果改的是核心就通过 supervisor 切换进程;管理这些进程的 supervisor 自身也可以修改,不过它的更新呢需要手动的重启。
所以咱们可以从局部扩展开始,也可以继续修改核心;每一层都有各自的加载方式。服务端扩展提供的是另一类能力,例如工具和后端逻辑。扩展更新后,正在执行的工具调用会继续用旧代码完成;之后的新调用再使用更新后的代码。
修改核心代码后需要重启核心进程才能生效。supervisor 会先启动新进程,等它准备好再让它接替旧进程继续处理原来的工作。这些加载方式决定了修改之后的下一步动作:给应用增加一个面板与更换它的核心实现,都属于修改自身的软件;但它们对应着不同的运行过程。
切换核心进程之前 Ultimate 会先对修改后的代码做类型检查,再启动新进程。旧进程这时还在运行;新进程先完成接手工作前的准备工作。如果代码检查没有通过或者新进程还没准备好,就继续使用原有的旧进程;等新进程准备好了才开始交接。
交接时,旧进程会先把重启工具的调用结果写入数据库,再释放数据库的控制权。随后呢新进程接管数据库继续处理原来的请求。那么新进程怎么知道接下来该做什么呢?Pi Durable 会保存会话内容、执行到哪一步以及已经完成的工具调用结果。
新进程就能根据这些记录继续工作了。这些记录保存在 SQLite 数据库里,新进程启动后通过 Pi Durable 的 Harness 打开数据库读取之前保存的状态。刚才重启之后 Agent 还能接着回答,靠的就是这套机制;面板里的勾选状态则由 Ultimate 的 UI 状态接口另外保存。
所以刚才我们看到了两个结果:Agent 接着完成原来的请求,面板也保留了之前的选择。至于每一轮改了哪些代码可以通过 Git 查看;代码版本、请求的执行进度、界面里的选择分别有对应的保存方式。对于会操作外部系统的工具,还要考虑恢复执行时怎样避免重复操作。
把整个过程连起来看一下:用户提出需求,Agent 在独立的工作区利用 Git worktree 的机制来实现代码的修改;同时呢运行检查,再打开预览试用。准备好之后通过热更新或者核心的重启让改动生效;Pi Durable 保存执行状态,让核心进程更换后原来的工作还能继续。
这个小面板只是一个起点;Daniel 的博客界面展示了更完整的用法:围绕一个项目把需要的工具、内容和 Agent 放在一起,再根据实际使用中的需求继续调整。如果你也想尝试可以先让它增加一个小面板或者一个具体的工具,先用起来再看看还缺什么;继续提出下一轮修改需求。
希望这期的分享能帮助大家打开脑洞,大胆想象一下 AI 还可以有哪些存在方式又能用在哪些场景里。如果你已经有类似的创意或者做出了更有趣的应用,也欢迎在评论区分享。我是小木头,我们下期见。