<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>JetBrains on Trevor's Blog</title><link>http://hitrevor.com/tags/jetbrains/</link><description>Recent content in JetBrains on Trevor's Blog</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Wed, 04 Nov 2026 21:10:00 +0800</lastBuildDate><atom:link href="http://hitrevor.com/tags/jetbrains/index.xml" rel="self" type="application/rss+xml"/><item><title>JetBrains 的新 IDE，编辑器是只读的</title><link>http://hitrevor.com/posts/thinkrail/</link><pubDate>Wed, 04 Nov 2026 21:10:00 +0800</pubDate><guid>http://hitrevor.com/posts/thinkrail/</guid><description>JetBrains 出了个 AI IDE，我把它跑起来才发现关键不在功能表：它源码里写死 readOnly: true，我试着往编辑器里打字，文件哈希一动不动。这是一个不让你改代码的 IDE。</description><content:encoded><![CDATA[<p>JetBrains 在 2026 年 8 月 19 日发了个新东西，叫 ThinkRail，中文圈几乎没人写。</p>
<p>我先看了仓库再装的本机版。看完源码有一个发现，比它所有功能都让我意外：这个「IDE」的编辑器是只读的。</p>
<p><code>monacoSetup.ts</code> 第 110 行写着 <code>readOnly: true</code>，还配了一句提示文案。我打开界面试着往里敲字，一个字都进不去，文件哈希也没变。</p>
<p>这篇文章就讲这件事：一个不让你改代码的 IDE，到底在赌什么。</p>
<h2 id="它不是-intellij-的下一代">它不是 IntelliJ 的下一代</h2>
<p>仓库在 <code>JetBrains/thinkrail</code>，Apache-2.0，README 上挂着「JetBrains incubator project」的徽章。</p>
<p>官方对自己的描述只有一句：在这个轻量 IDE 里用 pi 来做 vibe coding，它会围绕你的工作方式自我定制。</p>
<p>pi 是什么？一个第三方的极简 agent harness，包名 <code>@earendil-works/pi-coding-agent</code>，作者是 Earendil 那帮人。它的哲学是「不给功能，给原语」，子 agent、plan mode、权限弹窗全都不内置，让你自己写扩展。</p>
<p>ThinkRail 干的事，是给这个命令行 agent 套一个完整的界面：工作区、编辑器、diff、终端、spec 树。</p>
<p>所以你看到的东西是：一个两千多人的公司，用 TypeScript 写了一个壳，去包一个别人写的 agent。这跟 IntelliJ 那种自研内核的路子，是两条路。</p>
<p><img loading="lazy" src="/images/thinkrail/fig1.png"></p>
<h2 id="装起来是二百一十八兆的一个文件">装起来是二百一十八兆的一个文件</h2>
<p>它有两种形态：桌面安装包，和自包含的命令行版本。</p>
<p>我下的命令行版，单文件 218 MB，里面塞了 Bun 运行时，不用你先装 node。跑起来是这样：</p>
<p><code>./thinkrail --no-open --port 24242 &lt;你的仓库路径&gt;</code></p>
<p>浏览器打开 <code>localhost:24242</code> 就是完整界面。它没有自己的账号体系，模型走 pi 那套配置。</p>
<p>我本来准备去设置里填 DeepSeek 的 key，结果一打开 Providers 页就看见一行：DeepSeek，环境键 <code>DEEPSEEK_API_KEY</code>，已连接。</p>
<p>它直接读了我 shell 环境里 pi 已经配好的那套连接，零配置。</p>
<h2 id="我给它一个坏掉的仓库">我给它一个坏掉的仓库</h2>
<p>测试题目我特意准备了：一个小 JS 库，把标题转成 URL slug，两条测试里有一条是红的，因为实现没处理标点。</p>
<p>入口页有三个选项：写 specs、直接开工、在项目目录里干。我点了第一个，那是它的招牌流程。</p>
<p>提示词我写得很克制：按现状把 specs 写出来，先不要改代码。</p>
<p>点创建之后，它先做了一件事：在 <code>~/.thinkrail/worktrees/demo-repo/workspace-1</code> 切了一个真的 git worktree，分支叫 <code>write-slug-lib-specs</code>。我回主仓看了一眼，<code>main</code> 还在原来的提交上，一根毛都没动。</p>
<p>这一步不是模拟的，<code>git worktree list</code> 能看到两行。</p>
<h2 id="六分十七秒三个文件">六分十七秒，三个文件</h2>
<p>跑完我看结果：6 分 17 秒，26 次工具调用，15 个步骤，其中 <code>spec_create</code> 三次、读文件三次、改文件三次。一兆的上下文只用了 5.3%，花了 0.138 美元。</p>
<p>它没有直接开写。先 <code>ls</code> 看结构，读 README，读测试，然后真的跑了一遍 <code>node --test</code>，看到那条测试红了，才开始动笔。</p>
<p>产出三个 markdown：</p>
<p><code>goal-and-requirements.md</code> 讲这个库是什么、给谁用、现在只做「小写化 + 空格换横线」。<code>architecture.md</code> 讲单模块拓扑、依赖只有测试指向源码。<code>src/SPEC.md</code> 讲 <code>slugify</code> 的契约和边界。</p>
<p>三个文件都带 frontmatter，<code>id</code>、<code>type</code>、<code>status: draft</code> 加 <code>parent</code>，串成一张图。不是我教它的格式，是它载入的 <code>spec-graph</code> 技能包里定死的规矩。</p>
<p>最有意思的是它对那条红测试的处理。它没有把「会剥标点」写成已实现的能力，而是在模块 spec 里单开一节「Known limitation」，写清楚：测试期望 <code>hello-world</code>，函数返回 <code>hello,-world!</code>，标点原样留着。</p>
<p>做完它自己跑了一次 <code>spec_validate</code>，通过。</p>
<p><img loading="lazy" src="/images/thinkrail/fig2.png"></p>
<h2 id="spec-这张图长什么样">spec 这张图长什么样</h2>
<p>三个文件不是并列的文档，是有 <code>parent</code> 指针的树。</p>
<p>根是目标文档，中间是架构，叶子是具体模块的 spec。界面上右边那栏就是这棵树，点一下某个节点，左边编辑器直接渲染对应文件，还有 Preview 和 Source 两个视图。</p>
<p>它的博客在那篇《Spec-Driven Development》里写道，这么做的目的是让意图活过会话。聊天关了，代码还在，但「为什么这么写」通常跟着聊天一起消失。spec 就是把这部分留下来，而且 agent 每次开工都先把这张图读进去。</p>
<p>我这次只让它写，没让它改。真要改的时候，它读的是同一份图。</p>
<p><img loading="lazy" src="/images/thinkrail/fig3.png"></p>
<h2 id="然后我发现了只读这件事">然后我发现了只读这件事</h2>
<p>写完之后我想自己动手改一句，点进 <code>src/SPEC.md</code> 的 Source 视图，选了一行按键盘。</p>
<p>没反应。我以为是焦点问题，点了几次，又用开发者工具直接往可编辑区灌文本。结论一样：输入不进去，磁盘上文件的哈希值前后完全一致。</p>
<p>回到源码找答案，就是开头那句 <code>readOnly: true</code>。界面上甚至准备了提示语，告诉你去让 agent 改。</p>
<p>这条在 Hacker News 上有人提过，说 ThinkRail 里聊天面板是主角，编辑器和文件树是配角，「你甚至不被允许手动改文件」。</p>
<p>我核了一遍，属实。但要说公平：终端是能用的，底部就有一个真的 PTY，你在里面 <code>vim</code> 谁管不着。被锁的只是那个 Monaco 面板。</p>
<p>问题是这个设计把「改代码」这件事的主语换了。以前的 IDE，主语的你和 agent 各占一半。这里你和 agent 的分工很清楚：agent 写，你审。</p>
<p><img loading="lazy" src="/images/thinkrail/fig4.png"></p>
<h2 id="它为什么敢这么设计">它为什么敢这么设计</h2>
<p>因为它的架构里，IDE 那部分本来就不是重点。</p>
<p>官方架构文档把整个系统拆成三个环：引擎宿主、协议线、界面客户端。</p>
<p>第一个环跑在 <code>packages/server</code> 里，用 Bun 起一个 HTTP + WebSocket 服务，pi 在进程内跑。第二个环是 <code>packages/contracts</code> 里那份带版本的协议，它是两端唯一的耦合。第三个环是 <code>apps/web</code>，一个 React 写的、移动优先的客户端。</p>
<p>「移动优先」这四个字不是修辞。文档里写了：UI 独立于宿主发布，通过网线拨号到某个宿主，手机可以经 Tailscale 连上来。</p>
<p>所以它从第一天起就没把自己当传统 IDE 设计。它是一台 agent 主机的远程面板，桌面版只是顺便用 Electrobun 打了个壳。</p>
<p>编辑器只读不是阉割，是这个架构的自然结果：界面只是观察者和审批者，真正动手的是宿主里的 agent。</p>
<p><img loading="lazy" src="/images/thinkrail/fig5.png"></p>
<h2 id="仓库本身">仓库本身</h2>
<p>代码规模不小：TypeScript 和 TSX 一共 1,493 加 349 个文件，35.4 万行。</p>
<p>技术栈也很新：Bun 运行时、React 19、Monaco 0.57 加 shiki、xterm 6、Tailwind 4、TypeScript 6.0.3，网站用 Astro。桌面壳不是 Electron，是 Electrobun。</p>
<p>社区数据：7,484 星、450 个 fork、365 个 open issue。仓库是 2026 年 7 月 8 日建的，不到四个月做到这个数，节奏算快。</p>
<p>发版也密：0.2.0 是十月十八号，0.3.0 是十月二十九号，0.3.1 是十一月一号，夜版已经跑到 0.4.0。</p>
<h2 id="两个不好听的地方">两个不好听的地方</h2>
<p>第一，遥测默认开着。<code>config.json</code> 里 <code>analyticsEnabled</code> 和 <code>analyticsConsentConfirmed</code> 都是 true。启动参数里那个 <code>--no-analytics</code> 关的是「额外用量数据」，基础统计不在里面，得进设置里关。</p>
<p>第二，JetBrains 的 AI 战略现在有点散。Hacker News 上那两条帖子一共两百多个点、两百多条评论，最高赞的那条不是夸它，是吐槽：Air 和 ThinkRail 都还是半成品，而真正好用的 AI Assistant 反倒被搁在一边。</p>
<p>这条我认同。ThinkRail 更像一次押注实验，JetBrains 自己都挂着 incubator 的牌子。</p>
<h2 id="我的判断">我的判断</h2>
<p>如果你的第一反应是「IDE 不给编辑有什么用」，那它确实不是给你做的。</p>
<p>它服务的是另一批人：已经习惯让 agent 干活，主要工作在审查 diff 和写 spec 上，一个会话接一个会话地推进。对这批人来说，能编辑反而是个负担，因为你和 agent 抢同一个文件的时候，冲突只能靠自律解决。</p>
<p>它给的解法是物理隔离：每个工作区一个 worktree、一个分支，agent 的乱动留在它自己的分支上，你审完了才合并。</p>
<p>这个思路我觉得比「给编辑器加个 AI 按钮」更彻底。它赌的是：未来的 IDE 主要用来审，不是用来写。</p>
<p>赌得对不对，一年后再看。现在这一版，我会拿它跑点小活，不会拿它替 IDEA。</p>
<p>你平时写代码，改文件那一下是你自己动手多，还是已经交给 agent 了？如果已经是后者，这个只读设计大概不会让你难受。</p>
]]></content:encoded></item><item><title>eMudhra 证书链出事，我把 JetBrains 全家体检了</title><link>http://hitrevor.com/posts/emudhra/</link><pubDate>Tue, 03 Nov 2026 09:10:00 +0800</pubDate><guid>http://hitrevor.com/posts/emudhra/</guid><description>八张中间证书私钥可还原，四张根证书要拉黑。跟着 JetBrains 公告自查之后我发现：官方 workaround 只删两把，我机器上四把都在，三个 JVM 一个都没修。</description><content:encoded><![CDATA[<p>十月十七号，一个安全研究员报告：eMudhra 签发的八张中间证书，私钥可以还原出来，因为它们生成时用的熵有问题。</p>
<p>这意味着任何能插你网络的人，都能拿这几把私钥签一张「看起来可信」的证书，冒充 JetBrains 的工具要连的服务。</p>
<p>十月三十号 JetBrains 发了两篇公告。一篇讲 IDE 怎么办，一篇是全线产品的状态表。我看了一圈中文转载，基本都是转述，没人真去机器上查。</p>
<p>这篇就是那份自查手册。全程本机命令，不改任何配置。</p>
<h2 id="官方到底说了什么">官方到底说了什么</h2>
<p>受影响的是用 Java 跑的东西。IDE、Toolbox、Gateway、ReSharper，因为它们信任的是随包分发的 JetBrains Runtime 的信任库，不是你 macOS 系统的钥匙串。</p>
<p>公告里写得很直白：更新操作系统对这一类问题没有保护作用。</p>
<p>修复版 JBR 不再信任这四张根证书：emSign ECC Root CA G3、emSign Root CA G1、emSign Root CA G2、AAA Certificate Services。</p>
<p>拆开看是四个 CVE：CVE-2026-91403 和 CVE-2026-114820 是 Oracle 在 JBR 里做的修复，CVE-2026-118696 是内置浏览器 JCEF 拿 Chrome 的证书更新列表，CVE-2026-118697 是后台服务 jetbrainsd 自己那份内置清单。</p>
<p>三层，各自修。</p>
<p><img alt="一条事件，三层修复：JBR、JCEF、jetbrainsd" loading="lazy" src="/images/emudhra/fig1.png"></p>
<h2 id="第一步先看版本对不对得上">第一步：先看版本对不对得上</h2>
<p>别急着删证书，先确认你装的是哪个版本。JetBrains 的 <code>.app</code> 里有个 Info.plist，构建号就在里面。</p>
<pre tabindex="0"><code>/usr/libexec/PlistBuddy -c &#39;Print :CFBundleShortVersionString&#39; \
  &#34;/Applications/Android Studio.app/Contents/Info.plist&#34;
/usr/libexec/PlistBuddy -c &#39;Print :CFBundleVersion&#39; \
  &#34;/Applications/Android Studio.app/Contents/Info.plist&#34;
</code></pre><p>把路径换成你装的 IDE 就行，Toolbox 装的在 <code>~/Applications</code> 下面。</p>
<p>我机器上的结果：</p>
<ul>
<li>IntelliJ IDEA 2026.2.3，构建 IU-262.10968.63，打包的 JBR 是 25.0.4+1-508.27</li>
<li>Android Studio 2026.2，构建 AI-262.9437.185.2621.16467767，JBR 25.0.3</li>
<li>JetBrains Toolbox 3.9.1，构建 3.9.1.89198</li>
</ul>
<p>对照公告的修复列表：IDEA 要 2026.2.4 才算修，我这是 2026.2.3，差一个补丁。Toolbox 3.9.1 在修复名单里，这项过了。</p>
<p>Android Studio 是个空白。它不在 JetBrains 那张表里，Google 侧我没找到对应公告，只能自己看 JBR 版本：25.0.3，而修复的 JBR 25 是 635.79、508.41、329.142 这几个构建。25.0.3 显然是老的。</p>
<p><img alt="四个组件，谁修了谁没修" loading="lazy" src="/images/emudhra/fig2.png"></p>
<h2 id="第二步直接问信任库还剩几把">第二步：直接问信任库还剩几把</h2>
<p>版本号会骗人，信任库不会。每个 JBR 自带一份 cacerts，用它的 keytool 就能列。</p>
<pre tabindex="0"><code>&#34;/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/keytool&#34; \
  -list -cacerts -storepass changeit | grep -iE &#34;emsign|comodo&#34;
</code></pre><p>Android Studio 里出来四行：</p>
<ul>
<li><code>comodoaaaca [jdk]</code></li>
<li><code>emsigneccrootcag3 [jdk]</code></li>
<li><code>emsignrootcag1 [jdk]</code></li>
<li><code>emsignrootcag2 [jdk]</code></li>
</ul>
<p>四张全在。IntelliJ IDEA 那份也一样，四张全在，证书总数 111。</p>
<p>这里有个坑我必须先说，因为我踩了：别名的字面带一个空格加方括号的后缀，<code>emsigneccrootcag3 [jdk]</code>。你只写 <code>emsigneccrootcag3</code> 去删，keytool 会告诉你这个别名不存在，但它明明就在列表里。</p>
<h2 id="第三步证明你的处置真的生效">第三步：证明你的处置真的生效</h2>
<p>这是官方文档里没有的一步。</p>
<p>官方的临时方案是：把 cacerts 复制一份，删掉受影响的根，然后用 VM option 把 IDE 指向这份副本。问题是，你怎么知道那个 <code>-Djavax.net.ssl.trustStore</code> 真的被读进去了？路径写错、少写一个斜杠、用了 <code>~</code>，它都会静默失效。</p>
<p>我写了个三十行的小程序，直接问 JVM：你现在到底还信几张。</p>
<p><img alt="同一个信任库，三种状态：未修、官方 workaround、全删" loading="lazy" src="/images/emudhra/fig3.png"></p>
<p>关键在于 <code>tmf.init((KeyStore) null)</code> 这一句，它让 JVM 用默认信任库，也就是会响应 <code>javax.net.ssl.trustStore</code> 的那个。然后数一遍 <code>getAcceptedIssuers()</code> 里有多少张证书的颁发者叫 emSign 或者 AAA Certificate Services。</p>
<p>跑出来是这样：</p>
<p>不带 VM option 时，它报 109 张、命中 4。指向官方清理版，107 张、命中 2。四把全删，105 张、命中 0。</p>
<p>三行数字，把「我以为我修了」和「确实修了」分开了。</p>
<h2 id="官方没写的两件事">官方没写的两件事</h2>
<p>第一件，官方 workaround 只删两把：<code>comodoaaaca [jdk]</code> 和 <code>emsigneccrootcag3 [jdk]</code>。</p>
<p>但我在机器上查到的是四把都在，G1 和 G2 也还在信任库里。删完那两把之后，我的探针仍然报告「命中 2」。也就是说，按官方那两步走，这台机器上还留着两张 eMudhra 根证书。</p>
<p>我没有立场替 JetBrains 解释为什么只列两把，可能是修复版运行时对 G1、G2 另做了处理，也可能只是保守。但就「机器现状」这件事，数字是 4 不是 2。你要更彻底，就把四张一起删，我实测删完是 105 张、命中 0。</p>
<p>第二件，macOS 自己的系统根库里，这两张还躺着：</p>
<p><code>emSign ECC Root CA - G3</code> 的 SHA-1 是 3043FA4FF257DCA0C380EE2E58EA78B23FE6BBC1，<code>emSign Root CA - G1</code> 是 8AC7AD8F73AC4EC1B5754DA540F4FCCF7CB58E8C。</p>
<p><code>security dump-trust-settings</code> 在系统和用户两个域里都没有给它们单独加「不信任」策略。这不等于 macOS 就没拦它们，TLS 层还有别的机制，但至少从钥匙串这一侧看，它们还在。</p>
<h2 id="顺手把机器上其他-jvm-也扫一遍">顺手把机器上其他 JVM 也扫一遍</h2>
<p>这一条是我觉得最容易被漏掉的。</p>
<p>JetBrains 只说了自家产品。可你的 Gradle 守护进程跑在某个 JDK 上，命令行编译 Kotlin 也用着某个 JDK，Homebrew 又装了一份，每一个都有自己的 cacerts，跟 IDE 那份一点关系没有。</p>
<p>我把机器上所有能跑 keytool 的 JVM 都过了一遍：</p>
<ul>
<li>Homebrew OpenJDK 21.0.10，109 张，四把全在</li>
<li>Android Studio 打包 JBR 25.0.3，109 张，四把全在</li>
<li>IntelliJ IDEA 打包 JBR 25.0.4，111 张，四把全在</li>
</ul>
<p>一个都没修。</p>
<p>JetBrains 的修复版 JBR 也给到了 jbr21 的两条线，21.0.11b1163.125 和 21.0.9b1038.91。上游 OpenJDK 什么时候跟上，我没有一手出处，不能替你下结论，只能说本机这把 21.0.10 里四张还在。</p>
<h2 id="该怎么做">该怎么做</h2>
<p>优先级很清楚，能升级就别折腾临时方案。</p>
<p>IDEA 2026.2.4 已经在十月三十号发了，我从 JetBrains 的发布接口核过日期。Toolbox 3.9.1 已经装好，jetbrainsd 我也查了，跑的是 0.9.9728，就是修复版本。所以剩下要动的只有 IDE 本身。</p>
<p>如果暂时不能升，官方的三步是：复制 cacerts、用 jbr 里的 keytool 删别名、在 Help 的 Edit Custom VM Options 里加一行 <code>-Djavax.net.ssl.trustStore=</code> 指向副本。</p>
<p>路径必须写全，不许带 <code>~</code>，不许带引号。写错它不会报错，只会静默失效。然后在 Settings 的 Server Certificates 里确认「自动接受非可信证书」是关的。</p>
<p>还有一个细节：内置浏览器 JCEF 那份证书列表改不了，2025.2.6.4 及更早版本要直接关掉它，注册表键是 <code>ide.browser.jcef.enabled</code>。关掉之后 Markdown 预览和 notebook 输出会受影响，升级回修复版记得打开。</p>
<p><img alt="三条处置路径：升级、临时方案、只修后台服务" loading="lazy" src="/images/emudhra/fig4.png"></p>
<h2 id="我的判断">我的判断</h2>
<p>这件事最值得记的不是哪个版本修了，是信任边界在哪。</p>
<p>你更新了 macOS，你以为你安全了。你更新了 Chrome，你以为你安全了。但 IDE 里面有一份自己的信任库，Gradle 又有一份，命令行那把 JDK 还有一份。每一份都要单独修。</p>
<p>安全公告是写给所有人的，它不会替你回答「我这台机器现在到底是什么状态」。那个答案只能自己跑出来。</p>
<p>你机器上现在有几个 JVM？扫一下吧，我猜不止一个还剩着那四张。</p>
]]></content:encoded></item><item><title>JetBrains 的 prompt 自进化实验：2,426 次之后涨到 4,456 字</title><link>http://hitrevor.com/posts/koog-prompt/</link><pubDate>Sat, 31 Oct 2026 11:24:00 +0800</pubDate><guid>http://hitrevor.com/posts/koog-prompt/</guid><description>JetBrains 让 prompt 自己改自己，跑了 2,426 次迭代，长度涨到 4,456 字。同一套评测接口上，四个优化器同台对比，谁涨得快谁涨得稳是两件事。</description><content:encoded><![CDATA[<p>JetBrains Research 在 10 月 29 日发了篇博客，标题直译过来是《能自己写自己的 Prompt：一个 Kotlin 实验》。</p>
<p>作者 Nikita Khramov 和 Anton Podkopaev 在文里写道，这不是比喻。他们真的让机器把 agent 里的提示词改了一遍，改完的版本比人手写的长了两千倍。</p>
<p>我把这个实验的仓库拉下来跑了一遍编译，代码能过。下面说说它到底怎么改的，改出了什么，值不值得你花力气。</p>
<h2 id="改动的地方只有一个参数">改动的地方只有一个参数</h2>
<p>Koog 里 agent 是一张有类型的图，节点和边声明控制流，每个节点带自己的提示词、工具和模型。</p>
<p>官方给的例子把 PAPILLON 那条隐私管线用 Koog 重写了一遍。用户的查询里带着个人信息，agent 要拿大模型帮忙回答，但不能让模型看见那些信息。三步走完：先把问题脱敏，再调那个不受信任的模型，再拿回结果写成回复。</p>
<p>三步里有两步的提示词是人写的。要交给机器改，只需把 <code>subgraphWithTask</code> 换成 <code>optimizableSubgraphWithTask</code>，再把写在字符串里的那段指令挪出来，单独传给 <code>optimizableInstruction</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="k">val</span> <span class="py">craftRedactedRequest</span> <span class="k">by</span> <span class="n">optimizableSubgraphWithTask</span><span class="p">&lt;</span><span class="n">PupaAgentInput</span><span class="p">,</span> <span class="n">PupaRedactedRequest</span><span class="p">&gt;(</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="p">=</span> <span class="s2">&#34;craft_redacted_request&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">optimizableInstruction</span> <span class="p">=</span> <span class="n">REDACT_INSTRUCTION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">input</span> <span class="o">-&gt;</span> <span class="s2">&#34;user_query:</span><span class="se">\n</span><span class="si">${input.userQuery}</span><span class="s2">&#34;</span> <span class="p">}</span>
</span></span></code></pre></div><p>图本身一行没动。指令只是从字符串进了参数位，那个位置优化器认得，能读也能换。没有优化产物可加载的时候，就退回你写的那段原文跑。</p>
<h2 id="谁来改凭什么改">谁来改，凭什么改</h2>
<p>改动方是第二个模型。你的 agent 跑一遍，第二个模型看结果，看错在哪儿，然后提一版新指令，再跑，再改。一轮一轮，在预算内重复。</p>
<p>它需要知道什么叫好。所以你得提供一个函数，每次跑完返回一个分数，再加一句这次跑得怎么样的描述。分数决定哪些候选活下来，那句话是给第二个模型看的。</p>
<p>官方那个例子里，这句话只有一行。</p>
<p><code>PUPA overall score: 0.50. Quality score: 1.00. Privacy score: 0.00. PII leakage ratio: 1.00.</code></p>
<p>就这一行，加上这一步收到的输入和产出的输出，是机器改写的全部依据。</p>
<p><img alt="四步循环，被跑了 2,426 次" loading="lazy" src="/images/koog-prompt/fig1.png"></p>
<h2 id="跑了两千四百多次">跑了两千四百多次</h2>
<p>配置里写着 <code>numRollouts = 2426</code>。代码注释里有句实在话，一个实用的起点是训练集的六到二十倍。</p>
<p>反推一下，这意味着训练集在一百到四百条之间。这个量级不算大，但也不小，你得先有数据，还得有能给人打分的流程。</p>
<p>反思模型用的是 GPT-4.1-mini。为什么用这么小的模型，博客没细说，我猜是因为它要在两千多轮里每次都读一遍输出，用大模型成本扛不住。</p>
<h2 id="改出来的东西人不会那么写">改出来的东西，人不会那么写</h2>
<p>脱敏那一步，原来的指令两行：</p>
<blockquote>
<p>Given a private user query, create a privacy-preserving request for a powerful external LLM.
The LLM may assist without learning private information about the user.</p>
</blockquote>
<p>故意模糊，一个字没说清脱敏到什么程度算够。</p>
<p>跑完之后，同一处变成 4,456 个字符。</p>
<p><img alt="同一句指令的前后对照" loading="lazy" src="/images/koog-prompt/fig2.png"></p>
<p>里面有几段特别有意思。它列了一串占位词，像 &ldquo;the minor&rdquo;、&ldquo;the course&rdquo;、&ldquo;the company&rdquo;、&ldquo;a noted musician&rdquo;、&ldquo;two large urban communities&rdquo;。</p>
<p>它还规定，学术内容里不能露出真实的教授姓名、课程编号、学生名字。</p>
<p>结尾那条最有意思：泄漏归零，同时保留足够细节让模型能干活。</p>
<p>结尾那句是权衡。原文里没人写过，它从失败的样本里自己总结出来的。</p>
<p>我不觉得这是机器比人聪明。它做的是一件人懒得做的事，把几十个失败案例的共同点穷举成规则。人有那个能力，没那个耐心。</p>
<h2 id="它和你手动调-prompt-的差别">它和你手动调 prompt 的差别</h2>
<p>手调的逻辑是，改一版，跑一遍，看分数，再改一版。一次只有一个假设，而且改哪儿靠直觉。</p>
<p>机器调的逻辑是，一次提出好几个候选，同时跑，留下分数高的，淘汰低的。它还会看成功的样本，不只是失败的。这一点挺关键，因为只看错误容易把 prompt 改得越来越保守，越写越像免责声明。</p>
<p>两千四百多次 rollout 换来的东西，本质上是把你的直觉换成了搜索结果。搜索不一定更聪明，但它不累，也不会因为凌晨三点困了就随便收工。</p>
<p>代价是电费和时间。这个实验的规模，一个人手动跑不出来。</p>
<h2 id="分数涨了十三">分数涨了十三</h2>
<p>PUPA 这个基准，基线 81.19，已经很挤，往上抬不容易。优化后 94.22，涨了 13 分。</p>
<p>两个数字都是五次运行的均值，官方说这个提升统计显著。</p>
<p>其他基准的数字在文档里，和原始实现的对照也在。他们没把话说满，只详报了这一个案例。</p>
<h2 id="同一个接口下还有三个">同一个接口下还有三个</h2>
<p>除了 GEPA，这个库还实现了三个优化器，接口完全一样。</p>
<p>BootstrapFewShot 从成功的运行里挖示例，除了跑你自己的 agent 之外不发额外的模型调用，是最便宜的第一个选项。MIPROv2 同时在指令和示例两个维度上搜。ACE 攒一本 playbook 跟着 system prompt 走，适合你要 agent 积累经验而不是把一句话说得更准。</p>
<p>换优化器只改构造函数一行，前后的 train 和 loadOptimizedAgent 都不用动。自己写也只需要实现这两个方法，官方这四套都能塞进同一个口子，没留后门。</p>
<p><img alt="同一套接口，四个优化器" loading="lazy" src="/images/koog-prompt/fig3.png"></p>
<h2 id="关于那个-fork">关于那个 fork</h2>
<p>博客的原话是，optimizable-subgraph 的支持目前在一份 Koog 的 fork 里，预期会合回上游。</p>
<p>这句我实测对不上。库的 gradle 里写的是 koog 1.1.1，Maven 上的正式版，工程里没有任何依赖替换。两个模块，优化库本身和跑 PAPILLON 的那个示例，都在正式版上编过了。至少到编译这一步，不需要什么分支。</p>
<p>它跟主干具体差在哪儿，我核不出来。博客自己说预期会合回上游，那就等它合。仓库不会撒谎，博客可能早写了一句。</p>
<h2 id="为什么只有四颗星">为什么只有四颗星</h2>
<p>这个库到今天 4 颗星，6 月建仓，Apache 2.0，128 个 Kotlin 文件，一万七千行。同门的 Koog 有 4,645 星。</p>
<p>原因不难猜。它要你先准备好测试集和评分函数，这两样东西恰恰最费人力，而库本身只省了你调措辞的时间。</p>
<p>你用不上，不是它不好，是你手头还没攒够那两样东西。</p>
<h2 id="这件事改变的是什么">这件事改变的是什么</h2>
<p>写提示词从手艺变成编译。</p>
<p>以前你写 prompt，靠肉眼看、靠感觉调。现在可以声明哪几段是待优化的，准备数据，定好评分，让机器跑几百上千轮，拿到一个 JSON，读一遍、审一遍、提交进版本库。</p>
<p>prompt 从此有了 diff。</p>
<p>分数涨 13 分是这一个案例的结果。提示词能被自动优化、能进代码评审，这件事会改变所有写 agent 的人的工作方式。</p>
<p>你自己的项目里，有几条提示词是能被自动评测的？</p>
]]></content:encoded></item><item><title>把同一个程序员的需求拆成三条消息，agent 成本翻了一倍多</title><link>http://hitrevor.com/posts/jb-taskflow/</link><pubDate>Fri, 30 Oct 2026 11:46:11 +0800</pubDate><guid>http://hitrevor.com/posts/jb-taskflow/</guid><description>JetBrains 分析了 4,782 段真实 IDE 会话：把同一个需求拆成三条消息发，成本涨 2.0–2.6 倍，解决率几乎不动。</description><content:encoded><![CDATA[<p>JetBrains 的研究员翻了 4,782 段真实会话，都是工程师在自己 IDE 里跟 coding agent 的对话，一共约 14,000 条用户消息。</p>
<p>结论一句话：你现在用来给 agent 打分的那些基准，测的根本不是人怎么用它。</p>
<p>SWE-Bench Pro 里 93.8% 的任务是加功能和修 bug，而且每条任务都是一条完整的消息扔过去，agent 一次看清全貌再动手。</p>
<p>真实会话里，加功能只占 19%，修 bug 占 26%，两个加起来不到一半。</p>
<p>剩下的在干什么？问代码怎么运作的占 6%，一般性编程问题占 10%，还有规划、代码审查、重构、跑命令、调试，这些事在 SWE-Bench Pro 里一项都没有，因为那个基准的题目定义里根本不存在「问一句」这种动作。</p>
<p><img alt="同一条 issue，两种用户画像" loading="lazy" src="/images/jb-taskflow/fig1.png"></p>
<h2 id="一半的会话只有一条消息">一半的会话只有一条消息</h2>
<p>他们把每条会话按用户消息数数了一遍。</p>
<p>50.9% 的会话只有一条消息，16.2% 两条，8.8% 三条，往后再数到五条，加起来的比例已经不到 15%。</p>
<p><img alt="4,782 段真实会话按消息条数分布" loading="lazy" src="/images/jb-taskflow/fig2.png"></p>
<p>所以后续分析只看「至少三条消息」的 33%，他们叫长会话，一条消息那叫一次性问答。长会话里消息的构成是这样：问项目自己的代码 6%，一般编程问题 10%，剩下的分散在规划、审查、重构、调试、执行上，加功能和修 bug 已经被稀释成小头。</p>
<p>而 SWE-Bench Pro 被同一个分类器标过之后，是 55% 加功能、39% 修 bug、6% 重构。然后没了。</p>
<h2 id="三个公开语料三种完全不同的走法">三个公开语料，三种完全不同的走法</h2>
<p>他们还拿三个公开的 agent 对话集做了同样的标注。</p>
<p>SpecStory 是公开的 IDE 会话导出，1000 条里有 521 条是长会话，最接近他们自己的数据，规划占比明显高。</p>
<p>DataClaw 是社区分享的会话，585 条长会话，几乎全是执行类和短指令。SWE-chat 是真实用户在聊天界面里的记录，698 条长会话，执行和规划最多。</p>
<p>三个数据集来自不同产品、不同用户、不同采集方式，所以他们不做数量对比。但有一点很硬：拿其中任何一个当标准去校准基准，得到的题目配比都不一样，而这种差别不是样本量能抹平的。</p>
<p>连他们自己的 4,782 条也不算真相。用他们的话说，目标得先选定，再声明出来；没有默认的那一个。</p>
<p>这句话是整个研究的骨架。以前大家默认「用 GitHub issue 造任务就是真实的」，这篇说不是，issue 只是众多人群里的一种，换一批人数据就换一副样子，谁也没有资格说自己的那份是标准。</p>
<h2 id="他们没造新基准而是改装旧基准">他们没造新基准，而是改装旧基准</h2>
<p>新建一套交互式基准，等于把 SWE-Bench 这些年攒的仓库、环境、测试全扔了。所以他们选了另一条路：任务、仓库、测试、原始需求全留着，只重写用户消息。</p>
<p>做法是把每个 issue 拆成三条有序请求，凭空捏造的内容一点也不许有。每条请求必须能在上一条留下的代码状态上动手，标识符和代码片段原样保留，拆完再用另一个模型检查一遍有没有丢需求。731 条公开的 SWE-Bench Pro issue 里，31 条没过校验，剩下 700 条。</p>
<p>700 条就是全部战场，后面的所有数字都从这儿来。</p>
<p>原来的 issue 大概一千字一条消息，全塞在一段里。拆完之后长这样，第一句只给主症状，边界条件留到第二第三条。</p>
<p>qutebrowser 有个 LinkedIn 打不开的 issue，原因是老版本 QtWebEngine 缺 <code>Array.prototype.at</code>。</p>
<p>第一条请求只说「修一下 LinkedIn 在 macOS 上加 QtWebEngine 5.15.2 打不开的问题，用 polyfill 兜住」。</p>
<p>第二条才补「必须覆盖 6.3 之前没有原生支持的版本，正索引和负索引都要对」。第三条才说「越界要返回 undefined，只在原生方法缺失时才注入」。</p>
<p>三条一对，一条消息里的完整规格被摊成了三次渐进披露。</p>
<h2 id="还往里塞了问项目本身的问题">还往里塞了「问项目本身」的问题</h2>
<p>真实会话里 6% 是在问项目代码怎么回事，这类任务 issue 基准里一条都没有。于是他们让模型读仓库加一条已经解过这道题的轨迹，生成一到三个问题。</p>
<p>每个问题带两样东西：一份参考答案，和一个能跑的证据脚本。造数据集时在干净仓库里跑一遍，跑不出证据、或者改动了仓库，这题直接扔。</p>
<p>这条规则顺手排掉了一类问题：答案不在代码里的（「当初为什么这么设计」），物理上不存在。</p>
<p>qutebrowser 那道题后来配了一个问题，答案里点了两个配置项、走了五个文件。</p>
<h2 id="拆完之后成本翻倍分数没动">拆完之后，成本翻倍，分数没动</h2>
<p>下面是全文最值钱的一段，四个模型、700 条任务、三种喂法、每种跑三遍。</p>
<p>单发就是原始 issue 一条消息扔过去。拆分是把三条请求依次发，中间不补充。拆分加问答是再加进那些仓库问题，位置由优化器选。</p>
<p>结果是拆分让每个模型的成本涨了 2.0 到 2.6 倍。</p>
<p>仓库没变、测试没变、需求一条没加，多出来的钱全花在「需求分批到货」这件事上。</p>
<p>分数几乎没动。GPT-5.6 Terra 持平，其余三个平均掉 4 到 5 个点，跟同一种设置反复跑三次的波动差不多。</p>
<p><img alt="四种模型 × 三种设置的实测结果" loading="lazy" src="/images/jb-taskflow/fig3.png"></p>
<p>他们还做了个对照，把三条改写后的请求拼成一条发出去，三个 GPT 模型的成本和分数都贴回单发那一档。所以贵在用三条发这个动作上，不是在改写措辞上。</p>
<h2 id="拆开之后有了中间检查点">拆开之后有了中间检查点</h2>
<p>一条长需求跑到底，你只拿到一个最终分数，对错全糊在里面，想知道是哪一步塌的只能自己回去翻轨迹。拆成三步就能在每一步跑一次测试，只按已经露出来的需求判分，错在哪一步一眼看得出来。</p>
<p>64 条任务的消融里，通过率和测试通过率随着每条请求往上爬。一个问题变成几个可核验的检查点，诊断起来清楚得多。</p>
<p>这大概也是拆分唯一的收益，除此之外它只有成本。</p>
<h2 id="它还没解决什么">它还没解决什么</h2>
<p>只生成了两种消息：拆开的需求，和问仓库的问题。真实会话里的规划、审查、执行、没有修复的调试，全都还没生成器，而且大部分连「怎么自动判对错」都还没想清楚。</p>
<p>还有一条：所有消息在跑之前就写死了。可复现是真的，「用户」不会根据 agent 回应改口也是真的。</p>
<p>研究到此为止，剩下那些他们自己列了一串没做的，末尾连「分类器没有人工标注的准确率集」这种短板也写上了。</p>
<h2 id="这个结论对写代码的人有什么用">这个结论对写代码的人有什么用</h2>
<p>以前看模型榜单，默认那个数字代表「它有多会写代码」。这篇的说法是，那个数字里还包着一样你从来没在选择的东西，就是它被要求用什么样的节奏干活。</p>
<p>同一个模型，需求一次给全还是分三次给，成本差一倍多，分数差不多。那你在自己项目里怎么提需求，就是一道单独的题，而且是你能控制的那道。我平时的习惯是一条消息把背景、目标、约束全写清楚，看完这篇不打算改，真到了纠结成本的时候，先看的是它多绕了几步，而不是它答得对不对。</p>
<p>一个有趣的推论：如果所有基准都按「一次给全」来测，被训练出来的模型也会习惯这种节奏，那真实会话里那些追问、纠偏、渐进披露的能力，谁也测不出来。</p>
<p>回头看你最近的几次 agent 会话，是一条消息解决的多，还是来回好几轮的更多？</p>
]]></content:encoded></item></channel></rss>