首页 文章 精选 留言 我的

精选列表

搜索[agentic],共1488篇文章
优秀的个人博客,低调大师

ThinkRail 拆解:一个 agentic IDE 的三环骨架与 23 条决策

JetBrains 今年 7 月开源的 ThinkRail,形态有点反直觉:一个完整的 IDE,Monaco 编辑器、文件树、终端、diff、评审评论一应俱全,可它的引擎是社区里的编码 agent `pi`,直接跑在自己的进程里;对外发布的形式有两种,一个原生桌面壳,和一个跑起来就开浏览器的 `thinkrail` 命令。 仓库里 105 个 `SPEC.md`、一份 466 行的 `architecture.md`,里面是 23 条编号决策,每条都写着被否掉的方案。这种写法在开源项目里不常见,也让我觉得它比功能清单更值得读。我下载了 v0.2.0 的二进制实际跑了一遍,下面按它的骨架顺序拆。 ## 一、它到底是什么 一句话说清:ThinkRail 是一个很薄的宿主,它把 `pi` 跑在自己的进程里,再把 `pi` 的能力用一套带类型的 WebSocket 协议暴露给一个独立的 Web UI。 `pi` 负责模型、skills、上下文压缩、成本计算、会话状态;应用负责工作区、编辑器、和那条协议线。README 里有一句我觉得是全文最重的承诺:应用**从不在你背后拼 prompt**。 宿主的形状在 `packages/server`:一个 `Bun.serve` 的 HTTP + WS 服务,每个对话对应一个进程内的 `pi` 会话。启动它的是两个可选的外壳,`apps/cli` 或 `apps/desktop`,两者共享同一份启动逻辑。 ## 二、三个环,和一条硬规则 架构文档把它叫三个环: ```txt apps/cli browser host launcher ──▶ packages/server apps/web UI client ──▶ packages/contracts packages/server createServer(): Bun.serve(HTTP+WS) + in-process pi packages/contracts the wire (types plus tiny pure runtime) ``` - **引擎宿主**:`packages/server` 加 `packages/shared`,拥有 `pi`、会话状态、持久化。 - **线**:`packages/contracts`,类型加一个极小的无依赖运行时,是客户端和宿主之间唯一的耦合。 - **UI 客户端**:`apps/web`,React 19 + Zustand + Tailwind,可以脱离宿主单独构建、单独发布,运行时连上哪个宿主由 endpoint 参数决定。 配套的规则只有一句,但被当成硬约束执行:**`apps/web` 只能依赖 `contracts`、`locales`、`ui`、`extension-api/web`、`resource-renderers` 和扩展的 web 半边,永远不许依赖 `server` 或 `shared`**。 这条规则换来的是部署形态的自由。同一个 UI,桌面壳里连本机的 loopback 宿主,手机上连 Tailscale 里的那台机器,浏览器里连局域网的另一台;因为 endpoint 是参数,不是编译期常量。 三个环之外还有一层设计意图:launcher 只负责组合、生命周期、endpoint 选择和原生呈现,不拥有任何引擎逻辑。文档里写得很直白——一个普通产品功能改动,动的是契约、宿主里拥有该能力的模块、共享的 web 客户端和它们的测试,**不动任何 launcher**。 ## 三、隔离单元是 git worktree 多数 agent IDE 的隔离靠"对话上下文",ThinkRail 的隔离靠 git。 打开一个仓库作为 project,从它切出 workspace,每个 workspace 是一个 `git worktree`,有自己的分支和目录,落在 `~/.thinkrail/worktrees`。agent 在自己的 worktree 里干活,碰不到你的主工作区;干得好就合分支,不好就删。多个 agent 并行跑,互不干扰。 这个模型有个明显的人性问题:习惯了一个目录改代码的人,一上来会被"工作区"搞晕。项目对此的处理是给了两个显式的例外,两个都带 `kind` 标记,且都是用户拥有的、工具永不改名或回收: - 每个 project 有一个内置的 **Default workspace**(`kind: "default"`),cwd 就是项目目录本身,也就是 git 的主工作树。它必须从 Welcome 页的「Work in project folder」显式进入,不会被自动带进去。 - 用户自己挂进来的已有 worktree(`kind: "external"`),工具可以忘掉它,但绝不改它。 还有一个更细的规则值得抄:**一个路径的写法跟着它的种类走**。目录身份(`Project.path`、`Workspace.worktreePath`)统一用宿主解析后的绝对路径,和 `pi` 记录的 cwd 一致,比较时就是一次 `===`;进入线上协议或存储的**工作区相对路径**统一用 POSIX `/`,进入时转换。 为什么值得单列一条决策?文档给了事故现场:同一个 Windows 目录的两种写法,让所有原始比较失效(issue #766),Default 和 external 工作区里的对话列不出、打不开、改不了名、删不掉。补救时他们拒绝了两条捷径,一是在每个比较点做规范化("第一个补丁漏了十二处里的九处"),二是用 `realpath` 当规范形式(跨符号链接时和磁盘上已有的记录不一致)。代价写在明处:旧版本共享同一个 `~/.thinkrail` 时,会把修复过的项目报成 `ALREADY_OPEN`,直到它升级。 这种"把踩过的坑写进决策,并附上被否掉的修法"的密度,是我推荐读这份文档的主要原因。 ## 四、规范不止是开发文档,它是产品的一半 仓库里有 105 个 `SPEC.md`,和代码并排放在每个模块旁边;`architecture.md` 的 23 条决策也在其中,用 frontmatter 串成一棵树: ```yaml --- id: module-spec-graph type: module-design status: active title: Spec-Graph pi extension parent: architecture depends-on: [] tags: [spec-graph, pi-extension] --- ``` 这棵树有第二个消费者:`packages/spec-graph` 把同一套规范交给 agent。七个工具 `spec_create`、`spec_update`、`spec_get`、`spec_grep`、`spec_graph`、`spec_validate`、`spec_delete`,加一个 skill 和一条项目级规则,随每个会话打包进去。于是 agent 在改代码前能先查规范、改完能顺手更新规范,意图不再随会话结束而丢失。 UI 里有一个只读的 Specs 工具,把这张图渲染成树。文档里的说法是"让意图熬过会话"。 开发纪律也写在明处:代码改动如果移动了边界、契约或决策,**必须在同一次改动里更新对应的 spec**。`architecture.md` 的 frontmatter 里有个 `covers` 字段,列着这 23 条决策覆盖的模块——决策和模块之间是可查的引用关系,不是靠记忆。 这套做法能成立有个前提:agent 得真的会用这些工具。`spec-graph` 因此被设计成不依赖本仓库任何包的 `pi` 扩展,在原生 `pi` 里也能跑;ThinkRail 只是把它打进每个会话。 ## 五、23 条决策,每条都带着被否掉的方案 `architecture.md` 的 23 条决策,形式统一:结论、规则、理由,末尾写清被拒绝的方案和接受的代价。举三个例子。 **终端为什么是 xterm.js 的 DOM 渲染器。** 这是浏览器终端的首选,但真正的争议在渲染后端:他们坚持用 DOM,明确不上 `addon-webgl`。理由不是怀旧——xterm 维护者自己说 DOM 渲染器是触摸支持的前提,而 WebGL 有他们会继承的缺陷:`WebglAddon.dispose()` 泄漏 WebGL2 上下文,对"每个 worktree 一个终端、频繁开关"的场景是致命的,iOS 上还有上下文数量崩溃。代价是放弃连字和 `rescaleOverlappingGlyphs`。他们还写了重新评估的条件:上游给 `libghostty-vt` 打了带 WASM/npm 分发的 tag,**并且** `ghostty-web` 过了 0.4.0、鼠标上报和 OSC 8 都正常。 **终端为什么不用 tmux 做持久化。** 理由是四条具体的:Windows 上不可假设的依赖、与自己的标签模型冲突、环境变量传递会坏、基于轮询的捕获。而持久化本身他们决定不做——宿主重启后终端不保留,标签会带着历史输出重新起一个 shell。 **扩展怎么拆成三块。** 第 21 条决策规定,每个扩展是一个目录,里面合成一个 `pi` 能力、一个 `./server` 半边(宿主打包进去的命名内联工厂)和一个 `./web` 半边(按工具名注册的渲染器)。两个半边永不互相 import,也不 import 宿主内部,宿主的状态只能通过显式 seam 进去——现在只固定了这一个属性,将来的扩展靠扩展 `define*` 对象接上,不做替换。 组装是静态的:两侧各一个 registry 文件,静态 import 把工厂带进每个 launcher,不生成工厂清单。交付规则更硬——每个扩展分三个可独立发布的 PR 到达:新 `pi` 包与旧包并排、web 半边搬走、server 半边接上并删掉旧包,**三个 PR 都不改变用户能观察到的任何行为**。第一个走完这条路的扩展是 `visualize`,第二个是 `ask-user-question`。 **为什么崩溃隔离是被主动放弃的。** 不变量里写着:agent 在进程内跑,**没有崩溃隔离**,一次致命故障会把整个宿主带走。这是相对"子进程 + RPC 模式"的取舍,选择理由是简单。 第 10 条讲依赖版本,值得单独说,因为它是我唯一能在本地直接验证的纪律:所有 manifest 里的每个依赖都钉**精确版本**,不许出现 `^`、`~`、`>`、`*`、`.x`;跨包的依赖(pi、TypeScript、typebox)只在根 `workspaces.catalog` 里钉一次,其他包用 `catalog:` 引用;规范里不许再写一次版本号。 顺带一个产品侧的决定:MCP 服务器要是仓库里定义的,默认关着,等你批准;服务器没标只读的工具,调用前会在对话里问你;你信任的工具可以按项目设成永远允许。OAuth 的服务器从设置里登录。这套默认值把"仓库能悄悄让你连上某个 MCP"这条路堵死了。 ## 六、我实际验证了什么 **依赖纪律是被脚本强制的。** 仓库里有个 `scripts/check-catalog.ts`,我跑了: ```txt $ bun scripts/check-catalog.ts check-catalog: OK (30 catalog entries enforced) ``` 根 catalog 里 30 个条目,抽查三个都是精确值:`typescript 6.0.3`、`react 19.3.1`、`@earendil-works/pi-coding-agent 1.4.0`。 为了确认它真会拦人,我往某个包的 `devDependencies` 里塞了一个 `left-pad: "^1.3.0"` 再跑一次: ```txt Dependency catalog violations: - packages/shared/package.json: devDependencies.left-pad pins "^1.3.0" — pin an exact version (no ranges) ``` 报错精确到文件和字段。改回去,检查重新通过。这个脚本进 pre-commit 和 CI,所以"精确钉版本"不是文档里的倡议。 **宿主能真的起来。** 我下载了 v0.2.0 的 macOS ARM 二进制(115 MB),用 `--no-open --no-analytics` 和一个临时数据目录启动,喂给它一个临时 git 仓库: ```txt $ ./thinkrail --version 0.2.0 $ curl http://localhost:24299/health ok $ curl -s http://localhost:24299/ | grep -o "[^" ThinkRail ``` Web UI 是一份 1454 字节的 SPA 外壳,任何未命中的路径都回落到它。这印证了架构里那条"UI 可以独立发布"的说法:宿主只负责发静态资源,页面本身不知道宿主是谁。 **线协议的第一帧就是状态。** 连上 `/ws?client=probe`,第一条消息是 `server.welcome`: ```json {"channel":"server.welcome","data":{ "protocolVersion":85, "hostPlatform":"darwin", "projects":[{"id":"4dd55fca-…","name":"proj","path":"/private/tmp/tr-verify/proj", "slug":"proj","lastOpened":1793236265591,"piResourceTrust":"untrusted"}], "recentProjects":[…], "config":{"theme":"dark","analyticsEnabled":false,"notificationsEnabled":true, …} }} ``` 这条消息把架构里"hydrate-then-stream"(先读状态、再订阅增量)落实成了可见行为:连接建立时宿主先把 project 列表、最近项目、完整配置读给你,客户端不需要"见证过"历史事件才能画出界面。换标签页、换设备、宿主重启,都是同一套重建路径。 **协议版本在快速漂移。** 上面那个 `protocolVersion: 85` 来自 v0.2.0 的二进制(2026-10-18 发布)。而仓库 `main` 分支的 `packages/contracts/src/wsProtocol.ts` 里,`PROTOCOL_VERSION` 已经是 95: ```ts export const PROTOCOL_VERSION = 95; export const PROJECT_RENAME_PROTOCOL_VERSION = 85; export const WORKSPACE_READ_STATE_PROTOCOL_VERSION = 86; // … 一直到 95,每个数字对应一次能力上线 ``` 十天,十个协议版本。这不是坏事——它正是"UI 独立发布"的代价,而项目用单调递增的整数加按能力的闸门来处理:宿主收到客户端报的协议版本,**按版本决定给这个连接订阅哪些推送频道**。 ```ts if (ws.data.protocolVersion >= MCP_PROTOCOL_VERSION) ws.subscribe(WS_CHANNELS.mcpStatus); if (ws.data.protocolVersion >= LOCALES_PROTOCOL_VERSION) ws.subscribe(WS_CHANNELS.localesChanged); if (ws.data.protocolVersion >= USER_THEMES_PROTOCOL_VERSION) ws.subscribe(WS_CHANNELS.themesChanged); ``` 旧客户端不会被塞进它解析不了的推送,新宿主也不需要为老 UI 保留一套平行逻辑。比起"给每个字段加可选标记",这个办法省事得多。 ## 七、代价与边界 **没有崩溃隔离。** 这是项目自己写在"不变量"里的:agent 在宿主进程内,一次致命故障就是整个宿主挂掉。想要进程级隔离,得等它换架构,或者你自己用子进程模式包一层。 **协议漂移很快。** 十天十个版本意味着:UI 和宿主版本错配是常态,不是意外。自托管的场景下这没问题(两者一起发),自己魔改 UI 的话要把版本闸门当契约读。 **平台门槛不低。** 桌面版 Linux 要 glibc 2.38 以上加 GTK 3、WebKitGTK 4.1、Ayatana AppIndicator 3、librsvg 2,文档直接写"Ubuntu 24.04 或更新"。Windows 和 macOS 的桌面安装包由 JetBrains 内部签名流程签发,社区本地构建出来的包可能被 Gatekeeper 拦。 **默认会发分析数据。** 启动、建对话、发消息、连 provider 这类事件默认上报,跟一个随机安装 ID 绑定,不含 prompt、代码、文件、凭据。额外的产品用量和来源数据要你打开"Share additional usage data",首次启动会问;命令行可以 `--no-analytics`。这一点在自托管工具里算常规,但值得知道。 **它还是 incubator 项目,且发布节奏是 nightly。** 我下载时最新的三个版本都是 nightly(0.3.0-nightly.7/8/9),稳定版停在 v0.2.0。仓库里 243 个 open issue,说明迭代很活跃,也说明细节还在磨。 **我只验证了宿主和协议线。** `bun install` 加 `bun run dev` 的开发流程、真正的 UI 交互、agent 跑任务、worktree 的实际创建、桌面壳、MCP 授权,我都没有跑——没有 `pi` 的 provider 凭据,也无意在你的机器上装一个完整的 IDE。所以本文没有任何"用它写完一个功能"的体验描述;这些部分只能从文档和代码读。 ## 八、什么值得抄 如果你在做 agent 工具,这个仓库有四处可以直接借: **把 UI 做成宿主的远程客户端。** 一条带类型的 WebSocket、一个版本号、一份"先读后订"的状态重建协议,你就同时得到了桌面壳、浏览器访问和手机访问,不用维护三套界面。 **用 git worktree 当隔离边界。** 它比"对话上下文隔离"重,但换来的是真的并行、真的可回滚、真的能一个分支干到底。前提是你得给不习惯的人留一个"就在项目目录里干"的入口——这是产品问题,代码解决不了。 **把规范变成 agent 能读写的对象。** `SPEC.md` 加七个 `spec_*` 工具,比"让 agent 读 README"高一个数量级:规范有结构、可检索、可校验,agent 改完代码能顺手把意图写回去。 **决策文档要写被否掉的方案。** 23 条决策里最有信息量的不是结论,是"我们试过 X,因为 Y 放弃了"。半年后接手的人靠这些能判断一条规则还能不能动。 --- **验证说明**:架构与机制描述来自仓库 `main` 分支的 `architecture.md`、`README.md`、`pi-compatibility.md` 和 `packages/contracts/src/wsProtocol.ts`;`check-catalog.ts` 的正反两次运行、v0.2.0 二进制在 macOS ARM 上的启动、`/health`、`/` 与 `/ws` 握手均为本地实测。工作区创建、agent 执行、桌面壳、开发流程未验证;文中所有引用的版本号、协议号、行数均以 2026-10-28 当天取到的代码为准。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册