<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitHub on Vibe Coding 笔记</title><link>https://8-152-7-248.sslip.io/tags/github/</link><description>Recent content in GitHub on Vibe Coding 笔记</description><generator>Hugo -- gohugo.io</generator><language>zh_CN</language><lastBuildDate>Tue, 03 Nov 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://8-152-7-248.sslip.io/tags/github/index.xml" rel="self" type="application/rss+xml"/><item><title>快照看得见，菜单点不动</title><link>https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/cover.png" alt="Featured image of post 快照看得见，菜单点不动" /&gt;&lt;p&gt;2026 年 11 月 2 日，有人给 vercel-labs/agent-desktop 提了个 issue：在 Chrome 里右键一个链接，菜单弹得出来，快照里 17 个菜单项都带着 ref，点「复制链接地址」却失败了。&lt;/p&gt;&#10;&lt;p&gt;返回的 JSON 写着 actual_app &amp;ldquo;Chrome&amp;rdquo;，expected_app &amp;ldquo;Google Chrome&amp;rdquo;。同一个窗口，同一个进程，差别只在名字。&lt;/p&gt;&#10;&lt;p&gt;提 issue 的人换过四种打法：重新快照再点、用 find 按 role 和 name 精确匹配、加上 &lt;code&gt;--app &amp;quot;Google Chrome&amp;quot;&lt;/code&gt;、改成 &lt;code&gt;--app &amp;quot;Chrome&amp;quot;&lt;/code&gt;，全都返回同一个 STALE_REF。&lt;/p&gt;&#10;&lt;h2 id="一张一次性的通行证"&gt;一张一次性的通行证&#10;&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/vercel-labs/agent-desktop" target="_blank" rel="noopener"&#10; &gt;vercel-labs/agent-desktop&lt;/a&gt; 是个用 Rust 写的命令行工具，挂在 Vercel Labs 名下，9,139 个 star（2026-11-03 04:10 拉 API 核的），Apache-2.0。它跟别的 computer use 工具不一样的地方，不在于看得准，而在于看不清的时候它肯说。&lt;/p&gt;&#10;&lt;p&gt;README 里有一句加粗的话，是整个设计的支点：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;Refs belong to their snapshot.&lt;/strong&gt; A bare &lt;code&gt;@e8&lt;/code&gt; is rejected with &lt;code&gt;INVALID_ARGS&lt;/code&gt;. When the UI changes, an action returns &lt;code&gt;STALE_REF&lt;/code&gt; or &lt;code&gt;AMBIGUOUS_TARGET&lt;/code&gt;: take a new snapshot and use its refs.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;ref 属于它诞生的那次快照。只写一个 &lt;code&gt;@e8&lt;/code&gt; 会被 &lt;code&gt;INVALID_ARGS&lt;/code&gt; 挡回来，因为 &lt;code&gt;@e8&lt;/code&gt; 里没有快照编号，谁也不知道它是哪次观察里的第几个元素。界面一变，旧 ref 就作废，动作返回 STALE_REF 或 AMBIGUOUS_TARGET，让你重新快照。&lt;/p&gt;&#10;&lt;p&gt;所以 ref 不是窗口里某个元素的永久身份证，是一张只对那一次观察有效的通行证。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="一个 ref 的四段生命" class="gallery-image" data-flex-basis="256px" data-flex-grow="106" height="1010" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/01-ref-lifecycle.png" srcset="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/01-ref-lifecycle_hu_76aa8141a082b42c.png 800w, https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/01-ref-lifecycle.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="快照长什么样"&gt;快照长什么样&#10;&lt;/h2&gt;&lt;p&gt;ref 这条路上没有像素匹配，也不靠截图认元素。命令走的是操作系统的无障碍树，macOS 上是 AX API，Windows 上是 UIA，Linux 上是 AT-SPI。&lt;/p&gt;&#10;&lt;p&gt;一次快照返回这样的信封：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2.5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;snapshot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Finder&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;window&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;w-204749&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Downloads&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;snapshot_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s8f3k2p9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ref_count&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;complete&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tree&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;window&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Downloads&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;children&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;treeitem&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Documents&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;ref_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@s8f3k2p9:e8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;available_actions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Click&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;snapshot_id 是 s8f3k2p9，元素编号是 e8，拼起来才是 &lt;code&gt;@s8f3k2p9:e8&lt;/code&gt;，两半缺一不可。每个节点还带一个 available_actions，写明它能被怎么点、怎么改值、怎么展开，代理照着挑就行。&lt;/p&gt;&#10;&lt;p&gt;对上之后，动作长这样：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install -g agent-desktop&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;agent-desktop version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;agent-desktop snapshot --app Finder -i &lt;span class="c1"&gt;# interactive elements, each with a ref&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;agent-desktop click @s8f3k2p9:e8 &lt;span class="c1"&gt;# act on a ref from that snapshot&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;agent-desktop snapshot --app Finder -i &lt;span class="c1"&gt;# observe again to verify&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;观察、对着 ref 动作、再观察一次验一遍。循环留在你的代理里，工具只负责把每次观察和每次动作的结果说清楚。&lt;/p&gt;&#10;&lt;p&gt;第一次上手先跑 &lt;code&gt;agent-desktop status&lt;/code&gt;，它会报平台、权限和 supported_surfaces。macOS 要先给辅助功能权限，&lt;code&gt;agent-desktop permissions --guide&lt;/code&gt; 会带着走一遍；Windows 同等完整性级别的应用不用额外授权；Linux 需要图形会话和 at-spi2-core。&lt;/p&gt;&#10;&lt;h2 id="ref-有四种死法"&gt;ref 有四种死法&#10;&lt;/h2&gt;&lt;p&gt;ref 会过期，过期的方式还不止一种。仓库里有 48 个开放 issue，有几个把死法写得明明白白。&lt;/p&gt;&#10;&lt;p&gt;STALE_REF 是最常见的一种：ref 在活的界面里找不到对应的东西了。开头那个 Chrome 就是。&lt;/p&gt;&#10;&lt;p&gt;AMBIGUOUS_TARGET 是 ref 还对得上，但活着的界面里不止一个候选。有人在 macOS 上给「语音备忘录」提了个 issue（&lt;a class="link" href="https://github.com/vercel-labs/agent-desktop/issues/494" target="_blank" rel="noopener"&#10; &gt;#494&lt;/a&gt;）：一个 CoreGraphics 窗口底下藏着两个 AX 窗口，snapshot 能列出 23 个 ref，scroll 和 click 却全挂，报 source_window_number_ambiguous。&lt;/p&gt;&#10;&lt;p&gt;SNAPSHOT_NOT_FOUND 是快照 id 本身过期了。&lt;/p&gt;&#10;&lt;p&gt;最容易被忽略的是 POLICY_DENIED。有人在 Windows 上把记事本最小化，snapshot 回来 complete: true、17 个 ref，看着一切正常；可点那个文档控件，报错说 live offscreen state is true。那棵快照树里，窗口根节点和菜单项都标了 offscreen，主内容区和标题栏按钮没标（&lt;a class="link" href="https://github.com/vercel-labs/agent-desktop/issues/486" target="_blank" rel="noopener"&#10; &gt;#486&lt;/a&gt;）。运行时明明知道这元素离屏了，快照却没说。&lt;/p&gt;&#10;&lt;p&gt;这一种最费时间，因为失败只能靠一个个动作试出来。&lt;/p&gt;&#10;&lt;h2 id="重试之前先看-disposition"&gt;重试之前先看 disposition&#10;&lt;/h2&gt;&lt;p&gt;报不报错是次要的，报了之后该重试还是该换招才是关键。agent-desktop 的每个错误都带一个 disposition，两个字段：delivery 和 retry。&lt;/p&gt;&#10;&lt;p&gt;delivery 有五种取值：unknown、not_delivered、delivery_uncertain、delivered_unverified、delivered_verified。retry 只有三种：unknown、safe、unsafe，而且是从 delivery 推出来的，只有 not_delivered 才算 safe。&lt;/p&gt;&#10;&lt;p&gt;翻译成人话：只有当它明确告诉你东西没送出去，你才可以原样重试。其余任何一种，都意味着这次动作可能已经生效了，重来一遍可能等于点两次、打两次，或者把一个开关又切回去。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="四种死法与 disposition" class="gallery-image" data-flex-basis="294px" data-flex-grow="122" height="880" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/02-error-and-disposition.png" srcset="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/02-error-and-disposition_hu_4b8264fc7281a75e.png 800w, https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/02-error-and-disposition.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;STALE_REF 长这样：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2.5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;STALE_REF&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@s8f3k2p9:e7 not found in current RefMap&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;suggestion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Re-run a snapshot to obtain fresh refs, then retry with the new ref (CLI: snapshot [--skeleton]; FFI: ad_snapshot then pass the printed qualified ref to ad_execute_by_ref)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;recovery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;strategy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;refresh_snapshot_then_retry_original&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;retryable&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;requires_fresh_snapshot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;disposition&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;not_delivered&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;retry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;safe&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;retry 写死成 safe，recovery 直接告诉你下一步是刷新快照再重试原来那条动作。错误码一共 16 个，PERM_DENIED、APP_NOT_FOUND、WINDOW_NOT_FOUND、ACTION_NOT_SUPPORTED 都在里头，完整的表在 &lt;a class="link" href="https://github.com/vercel-labs/agent-desktop/blob/main/docs/json-output.md" target="_blank" rel="noopener"&#10; &gt;docs/json-output.md&lt;/a&gt;。&lt;/p&gt;&#10;&lt;h2 id="60-个命令4-个不肯跑"&gt;60 个命令，4 个不肯跑&#10;&lt;/h2&gt;&lt;p&gt;命令一共 60 个名字，56 个能真跑。剩下 4 个是 key-down、key-up、mouse-down、mouse-up，它们要等到有 daemon 能释放一个被按住的键或按钮时才允许执行，现在一律拒绝。README 没解释为什么偏偏是这四个。&lt;/p&gt;&#10;&lt;p&gt;其余的分三块堆着。观察类是 snapshot、find、get、is、screenshot、list-surfaces，动作类有 click、type、set-value、select、toggle、check、expand、scroll，窗口、通知、剪贴板各占一列。&lt;/p&gt;&#10;&lt;p&gt;二进制还自带一份文档，跟着版本走：&lt;code&gt;agent-desktop skills get desktop&lt;/code&gt; 是循环、ref、验证和重试规则，&lt;code&gt;agent-desktop skills get platform&lt;/code&gt; 是你这台系统上的差异。那份技能文档比 README 还硬：它说永远不要为了绕开 headless 失败而切到 headed，说固定 sleep 是竞态，该用 wait 就用 wait。&lt;/p&gt;&#10;&lt;h2 id="我拿它点过一个-tauri-应用"&gt;我拿它点过一个 Tauri 应用&#10;&lt;/h2&gt;&lt;p&gt;11 月 2 日我在一个 Tauri 写的桌面程序上用它干过活，有几件事是上手才知道的。&lt;/p&gt;&#10;&lt;p&gt;先说 PowerShell。ref 必须加引号写成 &lt;code&gt;'@s8f3k2p9:e8'&lt;/code&gt;，不加就报错。裸的 &lt;code&gt;@token&lt;/code&gt; 在 PowerShell 里是 splatting 运算符，README 和技能文档各单独提醒了一次。&lt;/p&gt;&#10;&lt;p&gt;角色名字也不是想当然的。我那个应用里，触发筛选条展开的按钮不是 button，是 menubutton；被禁用的开关是 switch；下拉展开后的选项是 option，只在展开之后才出现在树里。拿 button 去找等于什么都没找，得看节点上的 available_actions 有没有 Click、Expand、SetValue。Tauri 用的 WebView2 暴露的是 ARIA 角色，原生 Windows 应用那边是另一套，#486 那个记事本的快照树里写的是 button、menuitem、scrollbar、textfield。&lt;/p&gt;&#10;&lt;p&gt;还有一次 find 一个几十张卡片的树，直接 TIMEOUT 了，默认 5 秒遍历预算不够，得加 &lt;code&gt;--timeout-ms&lt;/code&gt; 或者用 &lt;code&gt;--root&lt;/code&gt; 缩小范围。&lt;/p&gt;&#10;&lt;p&gt;写字有两条路。type 和 set-value 走的不是一条路：type 要目标暴露可读的 Text pattern 才能确认写进去了，条件不够就直接拒，文档里那句是 &amp;ldquo;type is verified only when the focused field exposes a readable Text pattern selection&amp;rdquo;。set-value 走 UIA 的 SetValue，能直接写进 React 这类受控输入框。我那次 type 被拒，set-value 写进去了。&lt;/p&gt;&#10;&lt;p&gt;锁屏会让一半命令失效。前台进程是 LockApp 的时候，&amp;ndash;headed 的物理输入会被拒，headless 下的 focus 报 POLICY_DENIED，但 snapshot、find、screenshot &amp;ndash;app 照样能用。&lt;/p&gt;&#10;&lt;h2 id="仓库现在是谁在写"&gt;仓库现在是谁在写&#10;&lt;/h2&gt;&lt;p&gt;9,139 个 star，626 个 fork，17 个人 watch，48 个开放 issue，另外还挂着 75 个开放 PR。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="仓库现状" class="gallery-image" data-flex-basis="288px" data-flex-grow="120" height="900" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/03-repo-now.png" srcset="https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/03-repo-now_hu_d736e458ced8d901.png 800w, https://8-152-7-248.sslip.io/p/agent-desktop-ref-lifecycle/images/03-repo-now.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;contributors 列表上 lahfir 一个人 214 次提交，ctate 14 次，Railly 2 次，剩下的基本是 bot。语言构成是 Rust 12.5 MB、PowerShell 2.3 MB、Python 1.0 MB、MDX 0.36 MB。&lt;/p&gt;&#10;&lt;p&gt;时间线有点怪。2026 年 2 月 19 日建库，10 月 28 日先出 1.0.0，两个多小时后又是 1.0.1，10 月 30 日发了 1.0.2，中间还夹着一次搬家。v1.0.2 的发布说明里有一句 &amp;ldquo;remove the personal funding link, badges and name references before the org transfer&amp;rdquo;，原来的地址是 lahfir/agent-desktop。同一批说明里还有一条 &amp;ldquo;tell npm users when the node launcher slows each call&amp;rdquo;，用 npm 装的同学每次调用都过一层 node launcher，会慢，1.0.2 起会提醒你。&lt;/p&gt;&#10;&lt;p&gt;Windows 10 上有人提过一条 issue（&lt;a class="link" href="https://github.com/vercel-labs/agent-desktop/issues/478" target="_blank" rel="noopener"&#10; &gt;#478&lt;/a&gt;）：list-apps 只列出 7 个拥有顶层窗口的进程里的 2 个，UWP 宿主和普通 Win32 程序都漏掉，snapshot 直接报 APP_NOT_FOUND。那个错会把调用者指向一个永远不存在的名字。&lt;/p&gt;&#10;&lt;h2 id="往哪儿读"&gt;往哪儿读&#10;&lt;/h2&gt;&lt;p&gt;要把它接进代理循环，先读两处。&lt;code&gt;agent-desktop skills get desktop&lt;/code&gt; 里的循环、ref、验证和重试规则跟二进制同一个版本；&lt;a class="link" href="https://github.com/vercel-labs/agent-desktop/blob/main/docs/json-output.md" target="_blank" rel="noopener"&#10; &gt;docs/json-output.md&lt;/a&gt; 里是 16 个错误码和 disposition 的完整定义，那张表比 README 细。&lt;/p&gt;&#10;</description></item><item><title>代理烧掉的 token，有多少花在用不上的工具上</title><link>https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/cover.png" alt="Featured image of post 代理烧掉的 token，有多少花在用不上的工具上" /&gt;&lt;p&gt;微软研究院和 UC Berkeley 的一群人，把三个命令交到你手里：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/microsoft/TokenEconomizer.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;claude plugin marketplace add &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/TokenEconomizer&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;claude plugin install tokeneconomizer@tokeneconomizer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;装完的东西不换模型、不改代理、也不碰你的凭据，它只想解决一件事：你在同一个仓库里反复跑代理，那些每次都重新付一遍的钱。&lt;/p&gt;&#10;&lt;p&gt;仓库 2026 年 10 月 13 日建的，算到今天 20 天，3,757 个 star，66 次提交。README 顶上的数据块写得很硬：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;Our measurement on SWE-Bench tasks: &lt;strong&gt;18.4–30.9% lower mean cost&lt;/strong&gt; across five agent–model combinations, cheaper in &lt;strong&gt;all 30&lt;/strong&gt; repository × combination cells, and Pass@1 up in &lt;strong&gt;4 of 5&lt;/strong&gt;.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;在 SWE-Bench 任务上量的：跨五组「代理 × 模型」组合，平均成本低了 18.4% 到 30.9%；30 个「仓库 × 组合」的单元格全都变便宜；Pass@1 五组里有四组上升。&lt;/p&gt;&#10;&lt;h2 id="省下来的钱长什么样"&gt;省下来的钱，长什么样&#10;&lt;/h2&gt;&lt;p&gt;五组组合并排放：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;代理 × 模型&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;平均成本&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;Pass@1&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Claude Code + Sonnet 5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-23.6%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;+1.3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Claude Code + Opus 4.8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-30.9%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-1.0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Codex + GPT-5.6 Terra&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-24.7%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;+0.9&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Codex + GPT-5.6 Sol&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-26.1%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;+1.6&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Copilot CLI + Sonnet 5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;-18.4%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;+2.6&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;最省的那一组，也是唯一一组变差的那一组：Opus 4.8，成本掉了将近三成，通过率掉了 1.0。这两个数挨着摆在 README 最显眼的位置，作者没把那一格挪进脚注。&lt;/p&gt;&#10;&lt;p&gt;实验规模是 277 个 SWE-Bench 任务、6 个仓库，每个任务一个会话，同一仓库的任务按顺序跑，开头时什么都不记得。成本按缓存价算，且算上了插件自己发的那些模型调用。落到每个仓库内部、每一组组合里，降幅是 5% 到 40%；Web-Bench 上是 16% 到 21%，两个安全审计基准上是 6% 到 20%，后者每个任务都在此前没进过的新仓库里。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="三组基准与五组组合的降幅" class="gallery-image" data-flex-basis="324px" data-flex-grow="135" height="800" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/02-benchmarks.png" srcset="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/02-benchmarks_hu_84471f544173d18d.png 800w, https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/02-benchmarks.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="三种浪费三种修法"&gt;三种浪费，三种修法&#10;&lt;/h2&gt;&lt;p&gt;&lt;img alt="三种浪费与三个机制" class="gallery-image" data-flex-basis="341px" data-flex-grow="142" height="760" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/01-waste-shapes.png" srcset="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/01-waste-shapes_hu_574a195290679228.png 800w, https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/01-waste-shapes.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;省 token 通常有两条路：让代理少说话，或者让代理少读东西。这个仓库走的是第三条——让代理别为同一件事反复付钱。&lt;/p&gt;&#10;&lt;p&gt;代理在一个仓库里反复跑，浪费有三种形状。&lt;/p&gt;&#10;&lt;p&gt;第一种是每次都重新认一遍仓库。会话之间不留记忆，于是「这个项目要先跑哪一步构建」「测试命令到底该写 &lt;code&gt;python -m pytest&lt;/code&gt; 还是 &lt;code&gt;pytest&lt;/code&gt;」「哪个环境变量必须先设」这些事，每开一个新会话就得重新发现一次。&lt;/p&gt;&#10;&lt;p&gt;micro-skills 把这些事实攒起来，来源有两个：跨会话反复出现的失败，和一个每 k 次会话复盘一次近期会话、提出事实的模型。k 默认是 3，一条事实被三个会话确认过，就在之后每次会话的开头陈述，后面被推翻就丢掉。&lt;/p&gt;&#10;&lt;p&gt;第二种是从不用的工具，定义照发。每一个加载进来的工具，它的定义都会跟着每一次请求送出去。从来没人调过的 Notebook、Browser，定义照样在账单上。&lt;/p&gt;&#10;&lt;p&gt;tool pruning 的判定是两个条件满足其一：一个工具在跨至少 m 个会话的 p 次 prompt 里没用过，或者在 s 个任意长度的会话里都没用过。默认 p=10、m=2、s=5，判定成立后这个工具就从后续会话里剔掉，走的是代理自己的设置文件，不要求代理学会任何新东西。一条命令能还回去。&lt;/p&gt;&#10;&lt;p&gt;第三种是卡在同一个错里。一个 turn（代理处理一个 prompt 的全过程）反复撞同一个失败时，judge 模型来判断它是不是卡住，把握度到 c=0.7 才动手，给代理塞一条重定向提示，每个 turn 最多 r=1 次。改完还是卡，它只告诉你怎么停，自己绝不终止 turn。&lt;/p&gt;&#10;&lt;h2 id="参数开关和一个-60-分钟的等待"&gt;参数、开关，和一个 60 分钟的等待&#10;&lt;/h2&gt;&lt;p&gt;机制的实现方式是走代理自己的设置，而不是教代理新本事。同一个 tool pruning，在不同代理里能剔掉的粒度并不一样。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;代理&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;tool pruning 剔掉什么&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;写进哪&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Claude Code&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;单个工具&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Codex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;整个 capability package&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;.codex/config.toml&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Copilot CLI&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;整个 MCP server&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;.github/copilot/settings.local.json&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Pi&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;不剔，它只加载四个工具&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;opencode&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;目前还不剔&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;默认参数里有个 q=60：deny list 不是立刻写下去，要等这个仓库里的会话空闲满 60 分钟，或者最迟一天，才落盘。README 给的理由写在 Settings 一节：会话恢复后工具变了，整段历史就得重新写进 prompt cache 而不是从 cache 里读，而空闲一个小时之后，那个 cache 本来也已经过期了。&lt;/p&gt;&#10;&lt;p&gt;也就是说这 60 分钟不是在等用户，是在等缓存自己过期，好让工具变更和缓存失效落在同一段时间里。&lt;/p&gt;&#10;&lt;p&gt;开关是逐项的：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/tokeneconomizer:config set microskills=on # 打开 micro-skills&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/tokeneconomizer:config set toolprune_evidence=20 # p = 20：等二十次没用过&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/tokeneconomizer:config set loopguard_nudges=2 # r = 2：重定向两次再告诉你&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两个模式也值得知道：&lt;code&gt;toolprune=propose&lt;/code&gt; 只把 deny list 提出来，不动任何东西，等你跑 &lt;code&gt;toolprune apply&lt;/code&gt; 才真正生效；&lt;code&gt;microskills=envfacts&lt;/code&gt; 只从反复出现的失败里学事实，一次模型调用都不发。&lt;/p&gt;&#10;&lt;p&gt;默认状态不是全开。tool pruning 和 loop guard 默认开，micro-skills 默认关，它的提议器要额外发模型调用，省多少取决于这个仓库和你干的活儿。&lt;/p&gt;&#10;&lt;p&gt;几个日常命令记一下就够：&lt;code&gt;/tokeneconomizer:status&lt;/code&gt; 看它学到了什么、这次会话花了多少；&lt;code&gt;/tokeneconomizer:toolprune&lt;/code&gt; 看剔掉了哪些工具以及凭什么剔的；&lt;code&gt;toolprune restore&lt;/code&gt; 把工具还回去，还回去之后不会再被剔。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;/tokeneconomizer:toolprune refresh-catalog&lt;/code&gt; 每台机器上跑一次，Claude Code 更新之后还要再跑一次，它会把每次会话加载过哪些工具记下来，整个过程不发模型调用。&lt;/p&gt;&#10;&lt;p&gt;实在不想让它介入，启动时代理前面加个环境变量 &lt;code&gt;TE_DISABLE=1 claude&lt;/code&gt; 就行。学到的东西落在你自己机器的 &lt;code&gt;~/.tokeneconomizer&lt;/code&gt;，插件自己发的模型调用走的是你自己的代理账号和套餐，费用算在它自己的报告里。&lt;/p&gt;&#10;&lt;h2 id="仓库里现在是什么样"&gt;仓库里现在是什么样&#10;&lt;/h2&gt;&lt;p&gt;&lt;img alt="仓库结构与提交分布" class="gallery-image" data-flex-basis="308px" data-flex-grow="128" height="840" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/03-repo-layout.png" srcset="https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/03-repo-layout_hu_e8ac21866949295a.png 800w, https://8-152-7-248.sslip.io/p/tokeneconomizer-token-waste/images/03-repo-layout.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;66 次提交，contributors 列表上是 8 个人：aaron-jx-li 一个占 53 次，剩下 7 位加起来 9 次。而论文的作者名单上排着 Ion Stoica、Jianfeng Gao、Michel Galley。这两件事不矛盾，但说明这个仓库基本是一个人扛的。&lt;/p&gt;&#10;&lt;p&gt;语言构成：Python 1.9 MB、Shell 74 KB、TypeScript 29 KB、JavaScript 1.2 KB。README 里那句「The package is standard-library only」说的是 &lt;code&gt;tokeneconomizer/&lt;/code&gt; 这个 Python 包，TypeScript 是 Pi 扩展和 opencode 插件用的。&lt;/p&gt;&#10;&lt;p&gt;它还很新，边角也确实毛。issue 里能看到一批 Windows 和 Codex 上的边界问题：&lt;code&gt;toolprune refresh-catalog&lt;/code&gt; 清临时目录报 WinError 32（#86），Codex 把每个可信项目都报成不可信（#83），Claude Code 的 side call 一直挂着全部 MCP server，导致一次 Haiku 调用摊到约 57 万等效 token（#87）。这三个都是 10 月 30 日提的，现在都已经关掉了。&lt;/p&gt;&#10;&lt;p&gt;开放 issue 只剩 3 条，开放的 PR 倒有 10 个。其中一条是 #85「Will other agents be supported?」，问的是除了 Claude Code、Codex、Copilot CLI 和 Pi，还会不会接别的代理。opencode 是实验性支持，README 只在 1.19.5 和 2.2.0 上测过。&lt;/p&gt;&#10;&lt;h2 id="那些数字是怎么跑出来的"&gt;那些数字是怎么跑出来的&#10;&lt;/h2&gt;&lt;p&gt;论文用的代码没有混进插件，两边只共享 &lt;code&gt;tokeneconomizer/&lt;/code&gt;。要自己跑一遍的话，&lt;code&gt;experiments/&lt;/code&gt; 目录里是完整的实验台：277 个 SWE-Bench 任务、102 个 Web-Bench 任务，加上逐个 agent × model 的 arm 定义。&lt;/p&gt;&#10;&lt;p&gt;跑它要一台 Linux x86_64 机器、能免 sudo 用 Docker、约 200 GB 磁盘，以及 Anthropic 和 OpenAI 两把 key。Claude Code 和 Copilot CLI 记第一把，Codex 记第二把。&lt;/p&gt;&#10;&lt;p&gt;不想跑实验的话，读 &lt;code&gt;docs/PLUGIN.md&lt;/code&gt; 更划算，那份文档记录了每个代理的 hook 和设置行为是怎么被验证的。想看数字怎么算的，&lt;code&gt;experiments/README.md&lt;/code&gt; 里是一行一条结果的启动命令。&lt;/p&gt;&#10;</description></item><item><title>一个 3.4 MB 的 exe，做到了 Trello 10 美元档的事</title><link>https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/</link><pubDate>Sun, 01 Nov 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/cover.png" alt="Featured image of post 一个 3.4 MB 的 exe，做到了 Trello 10 美元档的事" /&gt;&lt;p&gt;把一份 PRD 变成能排期的任务，手里的工具多半已经换过好几轮。Trello 的&lt;a class="link" href="https://trello.com/pricing" target="_blank" rel="noopener"&#10; &gt;定价页&lt;/a&gt;上，Custom Fields 和 Saved searches 排在 Standard 档，每人每月 5 美元；Calendar、Timeline、Table、Dashboard、Map 这些视图排在 Premium 档，10 美元。Notion 的&lt;a class="link" href="https://www.notion.com/pricing" target="_blank" rel="noopener"&#10; &gt;免费版&lt;/a&gt;对个人近乎不设限，代价是数据在别人服务器上。&lt;/p&gt;&#10;&lt;p&gt;我给自己写了个 Windows 桌面看板：MIT 许可，安装包 3.4 MB，不要账号，不联网，全部数据就是硬盘上一个 &lt;code&gt;.db&lt;/code&gt; 文件。最近两个版本刚把自定义字段和保存的筛选视图加上去——正是 Trello 要收 5 美元的那两项。&lt;/p&gt;&#10;&lt;p&gt;它叫潮线，仓库名 tideline。GitHub 描述栏就一句话：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;A local-first project management desktop app with Board, Table, Gantt and Calendar views, built with Tauri 2 + Rust + React.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;本地优先的项目管理桌面应用，带看板、表格、甘特、日历四种视图，用 Tauri 2 + Rust + React 写的。&lt;/p&gt;&#10;&lt;h2 id="一份-prd怎么变成一棵任务树"&gt;一份 PRD，怎么变成一棵任务树&#10;&lt;/h2&gt;&lt;p&gt;需求文档粘进去，任务树自己就出来了，全程不需要 AI 读懂任何一个字。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="需求拆解" class="gallery-image" data-flex-basis="294px" data-flex-grow="122" height="880" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/01-requirement-split.png" srcset="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/01-requirement-split_hu_f6058d8c225b12f7.png 800w, https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/01-requirement-split.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;它靠的是 Markdown 自己写出来的结构。&lt;code&gt;#&lt;/code&gt; 到 &lt;code&gt;######&lt;/code&gt; 六个级别的标题就是六个层级，&lt;code&gt;1.&lt;/code&gt; &lt;code&gt;1.1.&lt;/code&gt; &lt;code&gt;1.1.1.&lt;/code&gt; 这样的编号按编号层级嵌套，&lt;code&gt;-&lt;/code&gt; &lt;code&gt;*&lt;/code&gt; &lt;code&gt;+&lt;/code&gt; 这种无序列表按缩进嵌套，剩下的普通段落归到离它最近的那个节点，原样保留成任务描述。代码块整块留着。&lt;/p&gt;&#10;&lt;p&gt;拆解深度能选 1 到 4 层。选浅了不要紧，超出深度的内容会折叠进任务描述里，不会丢掉。拆法有两种：一种是一个任务带着一串子任务，另一种是按章节拆成一堆平级任务。文档里只有一个一级标题的时候，那个标题会被当成文档标题上提一层，让下面的章节直接变成顶层任务。&lt;/p&gt;&#10;&lt;p&gt;目标看板里已经有同名任务时，解析结果会提前标出来，默认跳过，不会造出一堆重复条目。&lt;/p&gt;&#10;&lt;p&gt;这部分代码是 &lt;code&gt;src/lib/requirementParser.ts&lt;/code&gt; 里的一个纯函数，文件头第一句就写着：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;纯前端、纯函数：把 Markdown / 纯文本需求文档解析成层级树，不调用任何 AI 服务，也不触碰数据库。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;它的测试用例是这样的：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;按编号层级解析有序列表&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseRequirementDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1. 用户登录&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.1. 输入手机号&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.2. 输入验证码&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2. 退出登录&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;用户登录&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;退出登录&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;四行输入，两条顶层任务，编号和层级都保住了。&lt;/p&gt;&#10;&lt;p&gt;不调 AI 是有道理的。AI 拆需求是理解之后再翻译，它会给你补出文档里根本没有的条目，改一轮的时间比自己拆还长。解析 Markdown 是照着结构走，文档里没写的东西不会凭空长出来。&lt;/p&gt;&#10;&lt;h2 id="同一批任务五种看法"&gt;同一批任务，五种看法&#10;&lt;/h2&gt;&lt;p&gt;拆完的任务落到看板上，就是一条卡片：&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="看板视图" class="gallery-image" data-flex-basis="354px" data-flex-grow="147" height="731" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/02-board.png" srcset="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/02-board_hu_45428df900149fd4.png 800w, https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/02-board.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;同一批数据还能换三种看法。表格视图按列表分组，任务和子任务是三层缩进，子任务能直接勾掉；甘特视图有周和日两档缩放、今日线、周末底色，任务条上带进度；日历是 6×7 的月历，按截止日期归格，拖到别的日期就改期。第五个入口是筛选器，关键词、完成状态、优先级、标签可以叠加着用。&lt;/p&gt;&#10;&lt;h2 id="甘特图上那条线是有类型的"&gt;甘特图上那条线，是有类型的&#10;&lt;/h2&gt;&lt;p&gt;&lt;img alt="甘特视图" class="gallery-image" data-flex-basis="321px" data-flex-grow="133" height="806" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/03-gantt.png" srcset="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/03-gantt_hu_8b37a67299c38061.png 800w, https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/03-gantt.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;任务之间的关系分了三种：前置是阻塞关系，前置没完成后面的就算被挡住，而且要查循环依赖；相关只表示这两件事有关系，不产生阻塞；重复是同一件事记了两遍。&lt;/p&gt;&#10;&lt;p&gt;三种关系画在图上不一样：前置是阻塞虚线箭头，相关是细点线，重复是点划线。关系还能跨看板建立，A 看板的交付物卡住 B 看板的任务，这种事真实存在。仪表盘上的「被阻塞」只认前置那一种。&lt;/p&gt;&#10;&lt;h2 id="自己定义字段把常用的看法存下来"&gt;自己定义字段，把常用的看法存下来&#10;&lt;/h2&gt;&lt;p&gt;看板可以定义自己的字段结构，按看板隔离，只有两种类型：&lt;/p&gt;&#10;&lt;p&gt;单选下拉和数值。改一个选项的名字不会让已有任务的值失效，因为选项有稳定的 id，改的只是显示名。删除字段的时候，它挂在任务上的取值会一起清掉，不留孤儿数据。字段名在同一个看板里不能重名，重了会明确提示。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="仪表盘" class="gallery-image" data-flex-basis="271px" data-flex-grow="112" height="956" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/04-dashboard.png" srcset="https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/04-dashboard_hu_b00ddb10c262f502.png 800w, https://8-152-7-248.sslip.io/p/tideline-local-first-kanban/images/04-dashboard.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;0.13.1 加的保存视图是另一个方向。你把常用的观察角度存下来，视图会连同筛选条件、排序方式和看板视图类型一起记住，切回来不会排序错乱。内置的「全部 / 我的任务 / 最近更新」三个视图不能改名也不能删除，免得用户把自己锁死，自己建的可以随时改名或者删掉。&lt;/p&gt;&#10;&lt;p&gt;筛选条里还多了一个「字段」入口，单选字段可以多选，数值字段可以填上下限区间，跟关键词、状态、优先级、标签叠着用。字段或者选项被删掉的时候，引用它们的视图会降级成忽略这个条件，而不是弹一个错误出来。&lt;/p&gt;&#10;&lt;h2 id="全部数据就一个文件"&gt;全部数据就一个文件&#10;&lt;/h2&gt;&lt;p&gt;后端是 Rust + rusqlite，数据库开在 WAL 模式，文件默认落在 &lt;code&gt;%APPDATA%\com.lemostic.tideline\tideline.db&lt;/code&gt;。数据目录和安装目录是分开的，升级、卸载都不会动你的库；备份就是把这个文件复制走，恢复就是复制回来：&lt;/p&gt;&#10;&lt;p&gt;安装包 3.4 MB，NSIS 简体中文向导，默认装到 &lt;code&gt;C:\Program Files\Tideline&lt;/code&gt;。前端 React 18 + TypeScript + Vite，前后端通过 77 个命令通信，MIT 许可。&lt;/p&gt;&#10;&lt;p&gt;整个应用对外只有一次网络请求，是检查 GitHub 上有没有新版本，未签名的安装包不会静默下载安装。&lt;/p&gt;&#10;&lt;p&gt;说缺点也得说，三条：&lt;/p&gt;&#10;&lt;p&gt;目前只在 Windows 上构建验证。看板和列表是硬删除，只有任务进回收站。提醒开关属于设备偏好，不跟着 JSON 备份走。&lt;/p&gt;&#10;&lt;p&gt;仓库是 10 月 29 日建起来的，到今天 65 次提交、46 个版本，还是个 0 star 的新项目。想试试的话在 &lt;a class="link" href="https://github.com/Lemostic/tideline" target="_blank" rel="noopener"&#10; &gt;github.com/Lemostic/tideline&lt;/a&gt;，Releases 页里有 Windows 安装包；想先看它怎么拆需求的，从 &lt;code&gt;src/lib/requirementParser.ts&lt;/code&gt; 开始读，那个文件只有几百行。&lt;/p&gt;&#10;</description></item><item><title>一个交易员的螃蟹仓库，作者说别用</title><link>https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/cover.png" alt="Featured image of post 一个交易员的螃蟹仓库，作者说别用" /&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/ultraworkers/claw-code" target="_blank" rel="noopener"&#10; &gt;ultraworkers/claw-code&lt;/a&gt; 有 194,974 个 star，2,138 条 issue，开放的只有 1 条。&lt;/p&gt;&#10;&lt;p&gt;那条 issue 叫 &amp;ldquo;GJC compatibility campaign: Rust fork-in-spirit of gajae-code&amp;rdquo;，要把这个仓库改造成 gajae-code 的 Rust 分身。这条改造需求，是仓库主人给自己开的？&lt;/p&gt;&#10;&lt;p&gt;同时它是这次拉下来对比的一批仓库里 fork 数最离谱的一个：107,649 个 fork，fork/star 比例 55.2%。同一批其余仓库最高的也就 17.7%，claude-code 17.0%，最低的 lazycodex 只有 6.4%。&lt;/p&gt;&#10;&lt;p&gt;作者自己在 README 顶上一段就写明：别拿它干活。&lt;/p&gt;&#10;&lt;h2 id="仓库自己写的免责声明"&gt;仓库自己写的免责声明&#10;&lt;/h2&gt;&lt;p&gt;README 靠前的位置有一个 IMPORTANT 块，原话是这样的：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Claw Code is not the serious production project here. This repository is closer to a museum exhibit than a product pitch, a crustacean-run artifact kept alive by clawed gajaes, swept and labeled by agents, and automatically maintained according to the harnesses above.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;翻译过来：&lt;strong&gt;这里真正的生产项目不是 Claw Code，这个仓库更接近博物馆展品，不像产品推介&lt;/strong&gt;。它形容自己是 &amp;ldquo;a crustacean-run artifact&amp;rdquo;，由一群叫 gajae 的螃蟹（gajae-code 的主角）看守，被 agent 扫过、打标签、按上面那套 harness 自动维护。&lt;/p&gt;&#10;&lt;p&gt;同一段后面接了一句更实在的：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;If you want to actually run work, start with LazyCodex or Gajae-Code. If you want to inspect the strange little fossil of the Claw Code moment, continue below.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;要真干活，去 LazyCodex 或 Gajae-Code。想看看 Claw Code 那个瞬间留下的奇怪化石，往下翻。&lt;/p&gt;&#10;&lt;p&gt;GitHub 描述栏写的是同一件事：&amp;ldquo;An agent-managed museum exhibit, built in Rust with Gajae-Code / LazyCodex — developed and maintained with no human intervention.&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;作者把「这不是给你用的」写在了最显眼的位置。这是一次主动的取舍，不是疏忽：他们要展示的是那套让 agent 互相协作的系统，而不是这个仓库本身好不好用。&lt;/p&gt;&#10;&lt;p&gt;这个取舍的代价很直白：star 数是 LazyCodex 的五十倍、gajae-code 的六十四倍，可它连一行 &lt;code&gt;cargo install&lt;/code&gt; 都给不了你。crates.io 上那个同名包是个只会打印改名提示的空壳，作者甚至专门在 README 里加粗警告别用它。&lt;/p&gt;&#10;&lt;h2 id="195-万-star107-万-fork"&gt;19.5 万 star，10.7 万 fork&#10;&lt;/h2&gt;&lt;p&gt;把同期几个同类仓库拉到一起，fork/star 比例的差距非常直观：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;仓库&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;star&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;fork&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;fork/star&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ultraworkers/claw-code&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;194,974&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;107,649&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;55.2%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Yeachan-Heo/clawhip&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;937&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;166&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;17.7%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;anthropics/claude-code&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;153,851&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;26,082&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;17.0%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;openai/codex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;131,564&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;20,785&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;15.8%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Yeachan-Heo/gajae-code&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;3,037&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;428&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;14.1%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;anomalyco/opencode&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;223,014&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;29,900&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;13.4%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;obra/superpowers&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;308,375&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;27,427&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;8.9%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;code-yeongyu/oh-my-openagent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;70,474&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;5,817&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;8.3%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Yeachan-Heo/oh-my-codex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;33,664&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;2,547&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;7.6%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;code-yeongyu/lazycodex&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;3,849&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;247&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;6.4%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;55.2% 是整张表第二名的三倍多，第二名 clawhip 也只有 937 个 star、17.7%。换成 star 量级相当的 claude-code（17.0%）和 opencode（13.4%），都不到 claw-code 的三分之一。fork 意味着「我拿一份去改」，star 才是「我留着以后看」。&lt;/p&gt;&#10;&lt;p&gt;再看它的 issue 和 PR：2,138 条 issue 里 2,137 条已关闭，开放的只有开头那一条；440 个 PR 合并了 202 个，同时开着的 PR 是 0 个。&lt;/p&gt;&#10;&lt;p&gt;这两组数字放在一起，说的不是同一件事。19.5 万 star，但几乎没人给它提问题：clone 下来自己研究的人很多，愿意回来提 issue 的人几乎没有。这正是一个展品该有的样子：被围观，不被使用。&lt;/p&gt;&#10;&lt;p&gt;再看一眼 fork 本身。最新一批 fork 全部创建于 2026 年 10 月 28、29 日，仓库体积都是同样的 26,811 KB，star 数一律为 0。它们不是被改过的副本，是整整齐齐的复制品。&lt;/p&gt;&#10;&lt;p&gt;而版本号停在一个奇怪的位置：1,858 次提交，7 个 release，从 10 月 12 日的 v0.1.3 排到 10 月 28 日的 v0.1.9，17 天发 7 版，号还一直是 0.1.x。7 个 release 里只有 v0.1.4 写了正文，其余 6 个的 release notes 全是空的。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="fork 比例对比" class="gallery-image" data-flex-basis="270px" data-flex-grow="112" height="960" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/01-fork-ratio.png" srcset="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/01-fork-ratio_hu_29b5ec52289e7fcb.png 800w, https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/01-fork-ratio.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="谁在提交谁在评审"&gt;谁在提交，谁在评审&#10;&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/ultraworkers/claw-code/blob/main/PHILOSOPHY.md" target="_blank" rel="noopener"&#10; &gt;PHILOSOPHY.md&lt;/a&gt; 里有一节叫 &amp;ldquo;Stop Staring at the Files&amp;rdquo;，意思是别只盯着生成出来的文件看，那一层是产物，不是要看的东西。&lt;/p&gt;&#10;&lt;p&gt;它把整套系统拆成三块。OmX（&lt;a class="link" href="https://github.com/Yeachan-Heo/oh-my-codex" target="_blank" rel="noopener"&#10; &gt;oh-my-codex&lt;/a&gt;）负责把一句话变成可重复的工作协议：一句话进来，规划、执行模式、持续验证循环、并行多 agent 工作流都从这生成。clawhip 负责监听和转发，它盯着 git 提交、tmux 会话、GitHub issue 和 PR、agent 生命周期事件，目标是把这些动静挡在编码 agent 的上下文窗口外面，好让 agent 专心写代码而不是忙着汇报。OmO（&lt;a class="link" href="https://github.com/code-yeongyu/oh-my-openagent" target="_blank" rel="noopener"&#10; &gt;oh-my-openagent&lt;/a&gt;）处理多 agent 之间的协调，Architect、Executor、Reviewer 三者意见不合时，结构由它给出，循环才有地方收敛。&lt;/p&gt;&#10;&lt;p&gt;这套分工，PHILOSOPHY.md 里用两句话概括：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;humans set direction; claws perform the labor.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;人定方向，螃蟹干活。另一句话是这套东西的落点：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;The code is evidence. The coordination system is the product lesson.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;代码只是证据，协调系统才是要教的东西。&lt;/p&gt;&#10;&lt;p&gt;仓库里还能看到这套流程留下的痕迹。&lt;a class="link" href="https://github.com/ultraworkers/claw-code/blob/main/AGENTS.md" target="_blank" rel="noopener"&#10; &gt;AGENTS.md&lt;/a&gt; 顶部标着 &amp;ldquo;Generated: 2026-08-16&amp;rdquo; 和一个 commit 号，内容是一张自动生成的知识库表：11 个 Rust crate 的位置、55 个工具的定义表在哪一行、120 多个斜杠命令的规格表在哪一行，全列了出来。根目录的 TRIAGE_SUMMARY.md 是一份 issue #3283 的分诊报告，连下一步动作都写好了。&lt;/p&gt;&#10;&lt;p&gt;1,858 次提交里最新那批的作者名是 Bellman、Bougie、twtrubiks、Wu Shuwen，还有个 clawdbot 专门发 release。Bellman 是仓库主要提交者 Yeachan-Heo 在 GitHub 上显示的名字，他的个人简介写着 &amp;ldquo;Algo Trader / Bloated AI Slop Creator&amp;rdquo;；clawdbot 则不是人，是个 2026 年 2 月才建、至今 0 个公开仓库的组织账号。&lt;/p&gt;&#10;&lt;p&gt;那条唯一开着的 issue，正文最后一行署名是 &amp;ldquo;repo owner&amp;rsquo;s gaebal-gajae (clawdbot)&amp;quot;，后面跟一只龙虾。它上面还写着参考实现的本机绝对路径 &amp;ldquo;/home/bellman/Workspace/gajae-code&amp;rdquo;，而这份 issue 自己的验收标准里恰好有一条：公开文档不能包含内部路径和私有基础设施。&lt;/p&gt;&#10;&lt;h2 id="cratesio-上的那个废弃壳"&gt;crates.io 上的那个废弃壳&#10;&lt;/h2&gt;&lt;p&gt;README 里有一段 WARNING，写得比大多数项目的安装说明都重：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;&lt;strong&gt;&lt;code&gt;cargo install claw-code&lt;/code&gt; installs the wrong thing.&lt;/strong&gt; The &lt;code&gt;claw-code&lt;/code&gt; crate on crates.io is a deprecated stub that places &lt;code&gt;claw-code-deprecated.exe&lt;/code&gt; — not &lt;code&gt;claw&lt;/code&gt;. Running it only prints &lt;code&gt;&amp;quot;claw-code has been renamed to agent-code&amp;quot;&lt;/code&gt;. &lt;strong&gt;Do not use &lt;code&gt;cargo install claw-code&lt;/code&gt;.&lt;/strong&gt;&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;crates.io 上确实有 &lt;code&gt;claw-code&lt;/code&gt; 这个包，但它是个 2026 年 4 月 1 日发布的废弃壳，只有 0.1.0 一个版本，累计下载 318 次，描述就一句 &amp;ldquo;DEPRECATED: This crate has been renamed to agent-code.&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;对比一下就知道这个空壳有多不起眼：真正在用的上游包 &lt;code&gt;agent-code&lt;/code&gt; 已经到 0.30.0，累计下载 5,302 次，描述是 &amp;ldquo;An AI-powered coding agent for the terminal, written in pure Rust&amp;rdquo;。&lt;/p&gt;&#10;&lt;p&gt;所以这个仓库只能从源码构建。README 给的路径是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. Clone and build&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/ultraworkers/claw-code&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; claw-code/rust&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cargo build --workspace&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. Set your API key (Anthropic API key — not a Claude subscription)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sk-ant-...&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. Verify everything is wired correctly&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./target/debug/claw doctor&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. Run a prompt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./target/debug/claw prompt &lt;span class="s2"&gt;&amp;#34;say hello&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 5. Start an interactive session&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./target/debug/claw&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上游那个真正在用的二进制是另一个包，装它会得到一个名字对不上的命令：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cargo install agent-code &lt;span class="c1"&gt;# upstream binary — installs &amp;#39;agent.exe&amp;#39; (Windows) / &amp;#39;agent&amp;#39; (Unix), NOT &amp;#39;agent-code&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;包名叫 agent-code，装出来的命令却叫 agent。多绕一道，没有任何地方解释为什么。&lt;/p&gt;&#10;&lt;p&gt;另外，claw 走的是 API key，Claude 订阅登录不是支持的认证方式——README 里那句 &amp;ldquo;Claude subscription login is not a supported auth path&amp;rdquo; 就写在那儿。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="构建路径与 crates.io 的壳" class="gallery-image" data-flex-basis="288px" data-flex-grow="120" height="900" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/02-install-trap.png" srcset="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/02-install-trap_hu_c3fc19515c8e9590.png 800w, https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/02-install-trap.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="readme-让你去用另外两个"&gt;README 让你去用另外两个&#10;&lt;/h2&gt;&lt;p&gt;既然作者明确说了「要真干活去另外两个」，那就看看这两个到底是什么。&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://github.com/code-yeongyu/lazycodex" target="_blank" rel="noopener"&#10; &gt;code-yeongyu/lazycodex&lt;/a&gt; 的自我描述是 &amp;ldquo;The one and only agent harness for complex codebases&amp;rdquo;，3,849 个 star，JavaScript 写的，装法一行：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx lazycodex-ai install&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/Yeachan-Heo/gajae-code" target="_blank" rel="noopener"&#10; &gt;Yeachan-Heo/gajae-code&lt;/a&gt; 是 TypeScript，3,037 个 star，装法是拉一个安装脚本跑一下：&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/main/scripts/install.sh | sh&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;两个加起来 star 不到 7000，claw-code 一个抵它们的二十多倍。装法却是反过来的：lazycodex 一行 npx，gajae-code 一行 curl。claw-code 要 clone、要装 Rust 工具链、要 &lt;code&gt;cargo build --workspace&lt;/code&gt;、要自己准备 ANTHROPIC_API_KEY，还要先跑一次 &lt;code&gt;claw doctor&lt;/code&gt; 确认接通了。&lt;/p&gt;&#10;&lt;p&gt;再看归属关系，这三个更像是同一件事的三个切面。LazyCodex 的 README 写明它是 OmO 在 Codex 上的发行版，装它的命令实际是 &lt;code&gt;npx --yes --package oh-my-openagent omo install --platform=codex&lt;/code&gt; 的简写；OmO 本身在 GitHub 上有 70,474 个 star，比这两个都高，只排在 claw-code 后面。gajae-code 则是 claw-code 的 issue 里写明的复刻对象，那条唯一开着的 issue 目标就是「成为 gajae-code 精神上的 Rust 分叉」。&lt;/p&gt;&#10;&lt;p&gt;作者把三个仓库都放在自己的主页上：claw-code 属于 ultraworkers 组织，另外两个是他个人账号下的。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="三个仓库的关系" class="gallery-image" data-flex-basis="370px" data-flex-grow="154" height="700" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/03-three-repos.png" srcset="https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/03-three-repos_hu_a2e3af177b9ede74.png 800w, https://8-152-7-248.sslip.io/p/claw-code-museum-exhibit/images/03-three-repos.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;想动手的话，从 &lt;code&gt;PHILOSOPHY.md&lt;/code&gt; 那节 &amp;ldquo;Stop Staring at the Files&amp;rdquo; 开始读，比直接翻代码快得多。这句话是作者留给后来人的提示。&lt;/p&gt;&#10;</description></item><item><title>335 行变 10 行：它不教 AI 写得更聪明，只教它别写</title><link>https://8-152-7-248.sslip.io/p/ponytail-lazy-code/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/ponytail-lazy-code/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/cover.png" alt="Featured image of post 335 行变 10 行：它不教 AI 写得更聪明，只教它别写" /&gt;&lt;p&gt;39 个任务，每个跑 5 次，同一个模型（Opus 5.5）。&lt;/p&gt;&#10;&lt;p&gt;唯一的变量是 agent 身上挂没挂一份提示词。&lt;/p&gt;&#10;&lt;p&gt;挂上的那个版本：代码行数少 53%，输出 token 少 45%，成本少 26%，耗时少 41%。&lt;/p&gt;&#10;&lt;p&gt;然后有个数字方向完全相反——带风险的逻辑有测试覆盖的比例，从 68% 涨到了 98%。&lt;/p&gt;&#10;&lt;p&gt;这个仓库叫 &lt;a class="link" href="https://github.com/DietrichGebert/ponytail" target="_blank" rel="noopener"&#10; &gt;DietrichGebert/ponytail&lt;/a&gt;。GitHub 描述栏里，它给自己写的是：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;让 AI 像屋里最懒的资深工程师那样思考。最好的代码，是你没写的那段。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;它现在有 179,020 个 star，10 月这一个月在 GitHub Trending 榜上涨了 32,214 个 star，是当月涨得最多的那个。MIT 协议，JavaScript 写的，最后一次 push 是 2026 年 10 月 18 日。&lt;/p&gt;&#10;&lt;h2 id="先把数字摆出来"&gt;先把数字摆出来&#10;&lt;/h2&gt;&lt;p&gt;README 顶部就写着：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Ponytail 5: rebuilt from the ground up. -53% code · -41% time · -26% cost · -45% tokens. And yet: 98% of risky logic ships with a test. Without Ponytail: 68%.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;方法也写得很清楚：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Benchmarked in Claude Code, the same agent with and without the skill: 39 tasks including a real FastAPI + React repo, Opus 5.5, 5 runs each.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;同一个 agent，开着和不开这个 skill 各跑一遍。39 个任务里有一个是真的 FastAPI + React 仓库，不是玩具项目。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;指标&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;Ponytail 5 占基线&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;变化&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;代码行数&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;47%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;−53%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;输出 token&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;55%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;−45%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;成本&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;74%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;−26%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;耗时&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;59%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;−41%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;代码少了一半，成本少了四分之一多。&lt;/p&gt;&#10;&lt;p&gt;但另一组数字方向是反的：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;质量指标&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;不开&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;开着&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;带风险的逻辑有测试覆盖&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;68%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;&lt;strong&gt;98%&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;agent 自写的测试能抓出的注入 bug&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;46%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;&lt;strong&gt;66%&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;还有一组安全任务：SQL 注入、路径穿越、伪造 token、限流、畸形 CSV 行、缓存，六个任务 30 次全过。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;on the six security tasks (SQL injection, path traversal, forged tokens, rate limiting, malformed CSV rows, caching) it passed all 30 runs: less code, no less safe.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;代码更少，安全没打折。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="benchmark 数字" class="gallery-image" data-flex-basis="294px" data-flex-grow="122" height="880" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/02-benchmarks.png" srcset="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/02-benchmarks_hu_cf82836ae1c787e1.png 800w, https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/02-benchmarks.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;顺带一提，他们还跟自己的上一版比了个盲测：新版回复赢了旧版 110 比 67。&lt;/p&gt;&#10;&lt;h2 id="335-行变-10-行"&gt;335 行变 10 行&#10;&lt;/h2&gt;&lt;p&gt;这张表还是抽象，仓库自己给了个更具体的例子：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;You ask for a date picker. Without Ponytail, the agent installs a date picker library or builds a whole calendar by hand: 335 lines. Ponytail 5 first looks at what is already there: the repo has an &lt;code&gt;Input&lt;/code&gt; component, and every browser has a date picker. It puts the two together. 10 lines.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;你要一个日期选择器。默认做法是装个日期选择器库，或者干脆手搓一个日历，335 行。&lt;/p&gt;&#10;&lt;p&gt;开了 Ponytail，它先看仓库里已经有什么：有个 &lt;code&gt;Input&lt;/code&gt; 组件，浏览器自带日期选择器。两样一拼，10 行。&lt;/p&gt;&#10;&lt;p&gt;差的不是编码速度，是它&lt;strong&gt;没动手之前先翻了仓库&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;h2 id="七级梯子"&gt;七级梯子&#10;&lt;/h2&gt;&lt;p&gt;机制画在一张叫 &lt;code&gt;ladder.png&lt;/code&gt; 的图里，README 把它转成了文字：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Before writing code, stop at the first rung that holds: 1 does this need to exist, 2 already in this codebase, 3 does the standard library do it, 4 a native platform feature, 5 an installed dependency, 6 can it be one line, 7 only then the minimum that works, plus one small test if it has logic.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;翻译过来是七级：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;这东西需要存在吗&lt;/li&gt;&#10;&lt;li&gt;代码库里已经有了吗&lt;/li&gt;&#10;&lt;li&gt;标准库能做吗&lt;/li&gt;&#10;&lt;li&gt;平台原生特性呢&lt;/li&gt;&#10;&lt;li&gt;已装依赖呢&lt;/li&gt;&#10;&lt;li&gt;能写成一行吗&lt;/li&gt;&#10;&lt;li&gt;才轮到「能跑起来的最小实现 + 一条小测试」&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;img alt="七级梯子" class="gallery-image" data-flex-basis="275px" data-flex-grow="114" height="940" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/01-ladder.png" srcset="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/01-ladder_hu_eff444081e9dbe47.png 800w, https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/01-ladder.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;有两个细节比梯子本身重要。&lt;/p&gt;&#10;&lt;p&gt;第一，&lt;strong&gt;梯子在理解完问题之后才跑&lt;/strong&gt;，不是拿来替代理解。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;The ladder runs &lt;em&gt;after&lt;/em&gt; it understands the problem, not instead of it: it reads the code the change touches and traces the real flow before picking a rung. Lazy about the solution, never about reading.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;它对方案偷懒，对读代码不偷懒。挑第几级之前，它得先读完这次改动碰到的那条真实流程。&lt;/p&gt;&#10;&lt;p&gt;第二，&lt;strong&gt;「懒」有明确边界&lt;/strong&gt;。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Lazy, not negligent: trust-boundary validation, data-loss handling, security, and accessibility are never on the chopping block.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;信任边界的校验、数据丢失的处理、安全、无障碍——这四样永远不在砍掉的范围里。&lt;/p&gt;&#10;&lt;p&gt;还有一句是专门堵「那不就是省 token 嘛」这个误会的：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;The rule was never &amp;ldquo;fewest tokens.&amp;rdquo; It is: write only what the task needs, and never cut validation, error handling, security, or accessibility. The code ends up small because it is necessary, not golfed.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;规则从来不是「token 最少」。是「只写任务需要的，绝不砍校验、错误处理、安全和无障碍」。代码变小是因为必要，不是在玩代码高尔夫。省下来的成本和延迟只是副作用。&lt;/p&gt;&#10;&lt;h2 id="本体只有一份提示词"&gt;本体只有一份提示词&#10;&lt;/h2&gt;&lt;p&gt;这是它跟这阵子那些重量级技能包最大的区别。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Ponytail is one prompt: &lt;code&gt;skills/ponytail/SKILL.md&lt;/code&gt;. The compact version, for agents that read a rules file, is &lt;code&gt;AGENTS.md&lt;/code&gt;. Everything else in this repo loads that prompt into different agents.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;本体就是一份提示词。给读规则文件的 agent 用的压缩版是 &lt;code&gt;AGENTS.md&lt;/code&gt;。仓库其余部分，全是在把这份提示词塞进不同的 agent。&lt;/p&gt;&#10;&lt;p&gt;所以你甚至可以不用插件——把 &lt;code&gt;AGENTS.md&lt;/code&gt; 拷进项目根目录，或者让 agent 把 &lt;code&gt;skills/ponytail/SKILL.md&lt;/code&gt; 当 skill 装上。&lt;/p&gt;&#10;&lt;p&gt;同一阵子 GitHub 上还有 &lt;code&gt;affaan-m/ECC&lt;/code&gt;（291,637 star，71 个 agents 加 311 个 skills）、&lt;code&gt;pbakaus/impeccable&lt;/code&gt;（92,343 star）、&lt;code&gt;Nutlope/hallmark&lt;/code&gt;（40,509 star）。Ponytail 的本体轻到你可以手抄一遍。&lt;/p&gt;&#10;&lt;p&gt;要是你不想装插件，只想改改自己项目的规则，那七级梯子抄成下面这样就行：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 示意拼装：把 Ponytail 的行为规则抄进自己项目的 AGENTS.md&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 字段依据：README「How it works」小节的 ladder +「Lazy, not negligent」一句&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ladder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;这东西需要存在吗&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;代码库里已经有了吗&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;标准库能做吗&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;平台原生特性呢&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;已装依赖呢&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;能写成一行吗&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;能跑起来的最小实现 + 一条小测试&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;never_cut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;信任边界校验&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;数据丢失处理&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;安全&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;无障碍&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;leave_a_test_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;逻辑里有分支、循环、解析器、金额或安全&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="装起来两行"&gt;装起来两行&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Claude Code —— README 给的是两条独立提示词，分开发&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/plugin marketplace add DietrichGebert/ponytail&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/plugin install ponytail@ponytail&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Codex&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex plugin marketplace add DietrichGebert/ponytail&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codex plugin add ponytail@ponytail&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Codex 装完还有一步：打开 &lt;code&gt;/hooks&lt;/code&gt;，信任它的两个生命周期 hook，然后开个新会话。&lt;/p&gt;&#10;&lt;p&gt;其他 agent 按 &lt;a class="link" href="https://github.com/DietrichGebert/ponytail/blob/main/INSTALL.md" target="_blank" rel="noopener"&#10; &gt;INSTALL.md&lt;/a&gt; 走，Copilot、Cursor、OpenCode、Gemini 都写了。&lt;/p&gt;&#10;&lt;p&gt;命令一共这些：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;命令&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;干什么&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail [lite | full | ultra | off]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;设强度，或者关掉&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail-review&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;像被 on-call 的资深工程师那样审 diff&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail-audit&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全仓体检，按该先修什么排序&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail-debt&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;收割你欠下的 &lt;code&gt;shortcut:&lt;/code&gt; 注释&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail-gain&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;展示 benchmark 成绩&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail band [on | off]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;显示 / 隐藏成本条（Claude Code 专属）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail band import&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;把过去的会话导进看板&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;/ponytail-help&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;速查&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;img alt="安装与命令" class="gallery-image" data-flex-basis="281px" data-flex-grow="117" height="920" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/03-install.png" srcset="https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/03-install_hu_b1858e8b9091df84.png 800w, https://8-152-7-248.sslip.io/p/ponytail-lazy-code/images/03-install.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;README 说每条发现都要回答四件事：这段代码干什么、出了什么问题、怎么修、不修会怎样。仓库自己贴过一条真实发现（&lt;code&gt;fix&lt;/code&gt; 那行原文没展开）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[must fix] Atom feed now crashes on every request&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; what: the change renamed a field&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; problem: an untouched file, src/routes/feed.js, now crashes&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fix: ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if skipped: the Atom feed now crashes on every request&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它举的例子是：这次改动改了个字段名，一个没动过的文件 &lt;code&gt;src/routes/feed.js&lt;/code&gt; 现在会崩。&lt;/p&gt;&#10;&lt;h2 id="成本条挂在输入框上"&gt;成本条挂在输入框上&#10;&lt;/h2&gt;&lt;p&gt;5.3 之后，Claude Code 输入框上方多了一条 band：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;In Claude Code, ponytail now shows a band above the prompt: what the current task costs, how many tokens and how much agent time it used, how often it reused existing code instead of writing new code, and whether the tests passed after the last change.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;本次任务花了多少钱、用了多少 token 和多少 agent 时间、复用现成代码的比例、上次改动之后测试过没过。&lt;/p&gt;&#10;&lt;p&gt;board 是另一个页面，把所有项目的开销摊在一起看：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;By default, everything stays on your machine, in &lt;code&gt;~/.config/ponytail/band/&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;默认数据全在本机。&lt;code&gt;/ponytail band import&lt;/code&gt; 能把你过去的 Claude Code 会话一次性读进来，带成本、agent 时间和 token——v5.3.2 这个版本刚加的。&lt;/p&gt;&#10;&lt;h2 id="坑有四个"&gt;坑有四个&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;第一，这组 benchmark 是作者自己跑的。&lt;/strong&gt; 方法、逐任务表格、限制都放在仓库里（&lt;code&gt;benchmarks/results/2026-10-07-agentic.md&lt;/code&gt;），这点比不少项目透明，但它是自测，不是第三方评测。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;第二，band 有版本门槛，也有盲区。&lt;/strong&gt; 需要 Claude Code 2.1.290 或更高（终端或桌面端）；VS Code 聊天面板、云端会话、&lt;code&gt;claude -p&lt;/code&gt; 里都不显示。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;第三，它会往你的代码里塞 &lt;code&gt;shortcut:&lt;/code&gt; 注释。&lt;/strong&gt; 这是它的记账方式，&lt;code&gt;/ponytail-debt&lt;/code&gt; 靠这个收账。嫌烦可以在项目的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 或 &lt;code&gt;AGENTS.md&lt;/code&gt; 里改词。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;第四，它本质是一份会跟你项目里既有规则叠在一起的提示词。&lt;/strong&gt; 同时挂着别的「行为约束型」skill 时谁赢，README 没写。&lt;/p&gt;&#10;&lt;h2 id="适合谁"&gt;适合谁&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;适合&lt;/strong&gt;：天天用 Claude Code 或 Codex 写代码、已经能看懂 agent 交出来的 diff；觉得 agent 啰嗦但说不清啰嗦在哪；对 token 成本敏感；希望 agent 每次先翻仓库再动手。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;不太适合&lt;/strong&gt;：一次性的改几行——配置成本不划算；真正需要大段自研的活儿——协议实现、解析器、算法，梯子第 7 级才是「能跑起来的最小实现」，复杂活儿上它会跟你较劲；空仓库——第 2 级「代码库里已经有了吗」在空仓库里是空的，复用现成代码这条最省事的路用不上，它得直接走到第 7 级。&lt;/p&gt;&#10;&lt;h2 id="最后"&gt;最后&#10;&lt;/h2&gt;&lt;p&gt;装它只要两行命令。装完之后，你要接受 agent 交给你 10 行，而不是 335 行。&lt;/p&gt;&#10;&lt;p&gt;README 的 FAQ 里有一条，写得比谁都损：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;What if I really need the 120-line cache class? You don&amp;rsquo;t. Insist anyway and he&amp;rsquo;ll build it. Slowly. Correctly. While looking at you.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;你坚持要那个 120 行的缓存类，他还是会给你写出来。慢慢地。正确地。一边写一边看着你。&lt;/p&gt;&#10;&lt;hr&gt;&#10;</description></item><item><title>一份 SPEC.md，和一个把引擎让给别人用的 IDE</title><link>https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/cover.png" alt="Featured image of post 一份 SPEC.md，和一个把引擎让给别人用的 IDE" /&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/JetBrains/thinkrail" target="_blank" rel="noopener"&#10; &gt;JetBrains/thinkrail&lt;/a&gt; 是个 agentic IDE。今天 GitHub 日榜第 4 位，5,636 个 star。&lt;/p&gt;&#10;&lt;p&gt;但它最有意思的文件不是 README，是仓库根目录下的 &lt;code&gt;goal-and-requirements.md&lt;/code&gt; 和 &lt;code&gt;architecture.md&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;前者定义这个产品要解决什么，后者定义它怎么搭。这两份文档在 &lt;code&gt;main&lt;/code&gt; 分支上，和代码一起改。&lt;/p&gt;&#10;&lt;p&gt;先看这个。IDE 本身是 Electron 老路子的那套：Monaco 编辑器、文件树、终端、git diff，JetBrains 做了二十多年。但代理这一层，它没写。&lt;/p&gt;&#10;&lt;p&gt;底下跑的是 &lt;code&gt;pi&lt;/code&gt;（&lt;a class="link" href="https://github.com/earendil-works/pi" target="_blank" rel="noopener"&#10; &gt;earendil-works/pi&lt;/a&gt;），一个 MIT 协议的 TypeScript 项目，118,701 个 star。这个数字是 thinkrail 的 21 倍。&lt;/p&gt;&#10;&lt;p&gt;所以准确的说法是：&lt;strong&gt;JetBrains 造了个 IDE，引擎是奥地利一个开发者写的。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;那家组织叫 Earendil Works，注册地奥地利，2025 年 4 月建。贡献榜第一位 &lt;code&gt;badlogic&lt;/code&gt;，本名 Mario Zechner，GitHub 账号 2010 年 12 月注册，8,007 个关注者，在 pi 上提交了 4,045 次。&lt;/p&gt;&#10;&lt;h2 id="目录结构"&gt;目录结构&#10;&lt;/h2&gt;&lt;p&gt;仓库是 monorepo，三个包各管一层，README 叫它 three rings：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;packages/server Bun.serve 的 HTTP+WS 宿主，每个 chat 一个进程内 pi 会话&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;packages/contracts 宿主与 UI 之间的带类型协议&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apps/web React 19 + Zustand + Tailwind v4&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;packages/contracts&lt;/code&gt; 这层是刻意留的。UI 独立发布，只依赖 contracts，不依赖 server。想换宿主实现，UI 不用动。&lt;/p&gt;&#10;&lt;p&gt;桌面包不在 web 也不在 server 里，它用 Electrobun 2.0.5 打包，不是 Electron。这项目在 JetBrains 自己的技术栈之外。&lt;/p&gt;&#10;&lt;h2 id="那份-specmd"&gt;那份 SPEC.md&#10;&lt;/h2&gt;&lt;p&gt;每个模块旁边放一个 &lt;code&gt;SPEC.md&lt;/code&gt;，用 frontmatter 连成一张图。README 里的原话：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;A connected graph of &lt;code&gt;SPEC.md&lt;/code&gt; files lives beside the code; the agent reads, searches, and maintains it through dedicated &lt;code&gt;spec_*&lt;/code&gt; tools, so intent survives the session.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;代理能读、能搜、能维护这张图。工具是 &lt;code&gt;pi-spec-graph&lt;/code&gt; 扩展给的，名字就叫 &lt;code&gt;spec_*&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;维护规则写得很死：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;ThinkRail is developed spec-first: a &lt;code&gt;SPEC.md&lt;/code&gt; sits beside every module, and a change that moves a boundary, contract, or decision updates the spec in the same change.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;改动跨了边界、契约或者决策，同一个改动里就得更新 spec。&lt;/p&gt;&#10;&lt;p&gt;这套规则是自指的。&lt;code&gt;packages/contracts&lt;/code&gt; 自己有 SPEC.md，官网 thinkrail.ai 也是一个模块，也有一份。这项目拿自己当自己的第一个用例。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="规格图" class="gallery-image" data-flex-basis="294px" data-flex-grow="122" height="880" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/01-specs.png" srcset="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/01-specs_hu_9e79656f44a2c0e8.png 800w, https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/01-specs.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="装起来"&gt;装起来&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://raw.githubusercontent.com/JetBrains/thinkrail/main/install.sh &lt;span class="p"&gt;|&lt;/span&gt; bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Windows 用 &lt;code&gt;irm ... | iex&lt;/code&gt;。装完 &lt;code&gt;thinkrail ~/code/my-repo&lt;/code&gt; 直接开一个仓库。&lt;/p&gt;&#10;&lt;p&gt;要求就两条：PATH 上有 &lt;code&gt;git&lt;/code&gt;，有一个认证过的 provider。首次启动会让你连 provider、选项目；没有现成仓库的话，「Start a new project」会给你建一个空 git 仓库，放在 &lt;code&gt;~/thinkrail&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;桌面版也可以单独下，macOS 是 DMG，Windows 是 ZIP，Linux 是 tarball，发布页带 &lt;code&gt;SHA256SUMS&lt;/code&gt; 自己核对。Linux 桌面包要自己补 GTK 3、WebKitGTK 4.1、Ayatana AppIndicator 3、librsvg 2，glibc 得 2.38 以上，Ubuntu 24.04 起。&lt;/p&gt;&#10;&lt;h2 id="工作区"&gt;工作区&#10;&lt;/h2&gt;&lt;p&gt;代理最容易搞砸的就是把你正在改的东西改乱。thinkrail 的做法是让每个工作区都是真的 &lt;code&gt;git worktree&lt;/code&gt;：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/code/my-repo 项目&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└─ ~/.thinkrail/worktrees/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ workspace-1 feature/auth 一个 agent 在写&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ workspace-2 fix/flaky-test 另一个在修&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ workspace-3 spike/ideas 你自己在试&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;分支、目录、文件、diff、终端、聊天，全按工作区隔离。README 写的是 &amp;ldquo;merge the good branch, delete the rest&amp;rdquo;。&lt;/p&gt;&#10;&lt;p&gt;干完的不一直挂着。PR 合掉或关掉，或者一周没人动，它自己落到项目专属的 shelf 上；回到这个项目时原样回来。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="工作区" class="gallery-image" data-flex-basis="288px" data-flex-grow="120" height="900" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/02-worktree.png" srcset="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/02-worktree_hu_d54338387ebdfbb3.png 800w, https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/02-worktree.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="写这篇的时候仓库里几件事正在发生"&gt;写这篇的时候，仓库里几件事正在发生&#10;&lt;/h2&gt;&lt;p&gt;写这篇的时候是 2026 年 10 月 29 日晚。仓库里有几件事正在发生：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;发版很凶。&lt;/strong&gt; 稳定版是 10 月 18 日的 &lt;code&gt;v0.2.0&lt;/code&gt;，之后每天一个 nightly，到 10 月 29 日早上已经是 &lt;code&gt;v0.3.0-nightly.10&lt;/code&gt;。中间那一次 &lt;code&gt;v0.2.0&lt;/code&gt; 一次发了 80 多个 PR。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;签名没齐。&lt;/strong&gt; JetBrains 签了 Windows 的 CLI 和桌面安装程序，macOS 的 CLI 签了名但没做公证，Gatekeeper 有可能拦。Linux 产物完全没签。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;收集使用数据。&lt;/strong&gt; 默认走 PostHog，采集启动、建会话、发消息、连 provider 这类事件，绑一个随机安装 ID。README 承诺不上传 prompt、代码、文件、凭据、账号身份，错误报告也不含文件路径和 URL。要关掉：&lt;code&gt;thinkrail --no-analytics&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;262 个开放 issue&lt;/strong&gt;，今天一天就新开了十几个。有个标题是「terminal: tests write their shell commands into the developer&amp;rsquo;s zsh history」。&lt;/p&gt;&#10;&lt;p&gt;还有个细节没解释清楚：&lt;code&gt;v0.2.0&lt;/code&gt; 的 release notes 里有一条 &lt;code&gt;chore(deps): upgrade pi to 1.2.0&lt;/code&gt;。引擎的升级节奏不由 JetBrains 定，这一条就说明问题。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="版本与现状" class="gallery-image" data-flex-basis="301px" data-flex-grow="125" height="860" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/03-status.png" srcset="https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/03-status_hu_9034f2ff3dc09072.png 800w, https://8-152-7-248.sslip.io/p/thinkrail-agentic-ide/images/03-status.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="我的看法"&gt;我的看法&#10;&lt;/h2&gt;&lt;p&gt;IDE 部分是 JetBrains 的老本行，做得顺理成章。&lt;/p&gt;&#10;&lt;p&gt;真正有意思的是它把「造代理」这件事整个让出去了，然后靠三样东西把别人的代理变成自己的产品：spec 图管意图，worktree 管隔离，contracts 管边界。引擎是别人的，但这三样是自己的。&lt;/p&gt;&#10;&lt;p&gt;代价也写在 issue 里：nightly 天天换，签名不齐，扩展有边界（改 Pi 终端界面的扩展在 ThinkRail 里不生效，它有自己的主题系统），底座升级节奏不由自己控制。&lt;/p&gt;&#10;&lt;p&gt;值不值得现在上手，看你已经在用什么。如果你手上是 &lt;code&gt;pi&lt;/code&gt;，ThinkRail 是现成的图形界面；如果你用的是别的代理，它也能读 &lt;code&gt;~/.claude&lt;/code&gt;、&lt;code&gt;~/.codex&lt;/code&gt;、&lt;code&gt;~/.gemini&lt;/code&gt; 里的 skills。&lt;/p&gt;&#10;&lt;p&gt;内置语言有英文、日文、简体中文、西班牙语。&lt;/p&gt;&#10;&lt;p&gt;真要挑一个地方从头看，我建议看 &lt;code&gt;architecture.md&lt;/code&gt;。一份把架构讲清楚的 SPEC.md，本身就是好文档。&lt;/p&gt;&#10;</description></item><item><title>你的 AI 代理不是变笨了，是被自己的工具输出撑爆了</title><link>https://8-152-7-248.sslip.io/p/context-mode-sandbox/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/context-mode-sandbox/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/cover.png" alt="Featured image of post 你的 AI 代理不是变笨了，是被自己的工具输出撑爆了" /&gt;&lt;p&gt;Claude Code 干到一半开始忘事，跟模型换没换代没关系。&lt;/p&gt;&#10;&lt;p&gt;真正的原因是它的上下文窗口，被 MCP 工具吐出来的原始数据吃掉了。&lt;/p&gt;&#10;&lt;p&gt;一次 Playwright 快照 56 KB，二十条 GitHub issue 59 KB，一份访问日志 45 KB。这些都不是你写的代码，是工具调用带回来的东西。它们一个字节不落地进了上下文窗口。&lt;/p&gt;&#10;&lt;p&gt;三十分钟下来，40% 的窗口没了。&lt;/p&gt;&#10;&lt;h2 id="原始数据这四个字是问题所在"&gt;「原始数据」这四个字是问题所在&#10;&lt;/h2&gt;&lt;p&gt;代理要的是一个结论——哪个文件有多少行、哪个 issue 还没解决。但 MCP 给它的是整个页面的 HTML、整份日志的每一行。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Every MCP tool call dumps raw data into your context window. A Playwright snapshot costs 56 KB. Twenty GitHub issues cost 59 KB. One access log — 45 KB. After 30 minutes, 40% of your context is gone.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;窗口满了，触发压缩。&lt;/p&gt;&#10;&lt;p&gt;压缩会把什么一起压掉？「我刚才在改 &lt;code&gt;src/parser.ts&lt;/code&gt;」这件事也在里面。&lt;/p&gt;&#10;&lt;p&gt;所以代理不是变笨了，是它忘了自己正在干什么。&lt;/p&gt;&#10;&lt;h2 id="有个-3-万星的仓库在解这个问题"&gt;有个 3 万星的仓库在解这个问题&#10;&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/mksglu/context-mode" target="_blank" rel="noopener"&#10; &gt;mksglu/context-mode&lt;/a&gt;，32,651 star，本月涨了 8,586，是 GitHub 月度 Trending 榜上跟编程代理最相关的一个。2026 年 10 月 28 日当天它还在提交代码。&lt;/p&gt;&#10;&lt;p&gt;它的做法是把工具输出关进沙箱。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;&lt;strong&gt;Context Saving&lt;/strong&gt; — Sandbox tools keep raw data out of the context window. 315 KB becomes 5.4 KB. 98% reduction.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;沙箱在本地跑，模型只收到结果。315 KB 的原始输出，进到模型眼前的是 5.4 KB。&lt;/p&gt;&#10;&lt;p&gt;README 里给了一张实测表，八个场景：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;场景&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;原始体积&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;进上下文&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;省下&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Playwright 快照&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;56.2 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;299 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;99%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;20 条 GitHub issue&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;58.9 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.1 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;98%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500 条访问日志&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;45.1 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;155 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;100%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Context7 的 React 文档&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5.9 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;261 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;96%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;500 行分析 CSV&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;85.5 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;222 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;100%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;153 条 git log&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;11.6 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;107 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;99%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;30 个测试套件输出&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6.0 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;337 B&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;95%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;子代理做仓库调研&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;986 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;62 KB&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;94%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;img alt="实测数据" class="gallery-image" data-flex-basis="324px" data-flex-grow="135" height="800" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/02-benchmarks.png" srcset="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/02-benchmarks_hu_223a9677c2bdebea.png 800w, https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/02-benchmarks.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;最扎眼的是最后一行。子代理做一次仓库调研，原始数据 986 KB，压到 62 KB，还是省了 94%。&lt;/p&gt;&#10;&lt;p&gt;整场会话算下来：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Over a full session: 315 KB of raw output becomes 5.4 KB. Session time extends from ~30 minutes to ~3 hours.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;同一场会话，从 30 分钟撑到 3 小时。&lt;/p&gt;&#10;&lt;h2 id="省空间是第一步记住事是第二步"&gt;省空间是第一步，记住事是第二步&#10;&lt;/h2&gt;&lt;p&gt;每次文件编辑、git 操作、任务状态、报错、你做过的决定——全部写进 SQLite。&lt;/p&gt;&#10;&lt;p&gt;会话被压缩的时候，它不把这些数据倒回上下文（那样窗口又满了），而是索引进 FTS5，用 BM25 检索，只捞回当前相关的部分。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="工作原理" class="gallery-image" data-flex-basis="316px" data-flex-grow="131" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/01-how-it-works.png" srcset="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/01-how-it-works_hu_4ede47531509cfcc.png 800w, https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/01-how-it-works.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;FTS5 是 SQLite 的全文检索虚拟表。仓库里用到了几个实用特性：porter 词干还原让 &amp;ldquo;caching&amp;rdquo; 能匹配 &amp;ldquo;cached&amp;rdquo;，trigram 分词让 &amp;ldquo;useEff&amp;rdquo; 能找到 &amp;ldquo;useEffect&amp;rdquo;。排序用 BM25，再叠一层 Reciprocal Rank Fusion 和 proximity 重排。&lt;/p&gt;&#10;&lt;p&gt;比省 token 更值得说的，是它的第三个动作：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;&lt;strong&gt;Think in Code&lt;/strong&gt; — The LLM should program the analysis, not compute it. Instead of reading 50 files into context to count functions, the agent writes a script that does the counting and &lt;code&gt;console.log()&lt;/code&gt;s only the result. One script replaces ten tool calls and saves 100x context.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;让模型写脚本去算，而不是读 50 个文件进上下文数函数。README 里的对比是 47 次 &lt;code&gt;Read()&lt;/code&gt; 吃 700 KB，换成 1 次 &lt;code&gt;ctx_execute()&lt;/code&gt; 只剩 3.6 KB。&lt;/p&gt;&#10;&lt;p&gt;这个思路的价值不止于省 token——它把代理从「数据搬运工」变成了「写代码的人」。&lt;/p&gt;&#10;&lt;p&gt;有一件事它明确不管：输出的啰嗦程度。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Aggressive brevity prompts have been shown to degrade coding/reasoning benchmarks&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;作者还专门引了 Moonshot AI 的 &lt;code&gt;kimi-k2.5&lt;/code&gt; 基准说这事。上下文管理归上下文管理，输出风格归输出风格，不搅在一起。&lt;/p&gt;&#10;&lt;h2 id="装起来其实就三行"&gt;装起来其实就三行&#10;&lt;/h2&gt;&lt;p&gt;Claude Code 走插件市场，装完自动生效：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 加插件市场&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/plugin marketplace add mksglu/context-mode&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 装插件&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/plugin install context-mode@context-mode&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 重启 Claude Code，或执行 /reload-plugins&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前置条件是 Claude Code v1.0.33 以上。如果 &lt;code&gt;/plugin&lt;/code&gt; 这个命令不认，先升级（&lt;code&gt;brew upgrade claude-code&lt;/code&gt; 或 &lt;code&gt;npm update -g @anthropic-ai/claude-code&lt;/code&gt;）。&lt;/p&gt;&#10;&lt;p&gt;装完先跑一次体检：&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;/context-mode:ctx-doctor&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所有检查项都该显示 &lt;code&gt;[x]&lt;/code&gt;。它会校验 runtime、hooks、FTS5 和插件注册。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="安装三步" class="gallery-image" data-flex-basis="316px" data-flex-grow="131" height="820" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/03-install.png" srcset="https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/03-install_hu_40a09a93d9eef296.png 800w, https://8-152-7-248.sslip.io/p/context-mode-sandbox/images/03-install.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;想先试试水、不想要 hook 自动拦截，可以只装 MCP 部分：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;claude mcp add context-mode -- npx -y context-mode&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样 11 个 MCP 工具都在，但模型不会被主动引导去用它们。它想直接调 Bash，那就直接调 Bash。&lt;/p&gt;&#10;&lt;h2 id="支持哪些平台效果差多少"&gt;支持哪些平台，效果差多少&#10;&lt;/h2&gt;&lt;p&gt;仓库自己写的是 17 个平台（README 兼容表列了 18 列，Antigravity 和 Antigravity CLI 分开算）。效果分两档，差别来自 hook 能力：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;平台&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;生效方式&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;节省&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Claude Code / Gemini CLI / Cursor / Codex CLI / Copilot CLI 等&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;hook 自动路由&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;约 98%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Antigravity / Zed&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;只能手抄 &lt;code&gt;AGENTS.md&lt;/code&gt; / &lt;code&gt;GEMINI.md&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;约 60%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Antigravity 和 Zed 不支持 hook，这是平台限制，仓库解决不了：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Antigravity and Zed do not support hooks. They rely solely on manually-copied routing instruction files (&lt;code&gt;AGENTS.md&lt;/code&gt; / &lt;code&gt;GEMINI.md&lt;/code&gt;) for enforcement (~60% compliance).&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;差了三成多。不在这两个平台上的话，装 Claude Code、Cursor、Codex CLI、Copilot CLI 里的任意一个，效果都是完整的。&lt;/p&gt;&#10;&lt;h2 id="两个必须说清的坑"&gt;两个必须说清的坑&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;第一，license 不明。&lt;/strong&gt; GitHub API 返回的 SPDX 标识是 &lt;code&gt;NOASSERTION&lt;/code&gt;，仓库 README 上的徽章写的是 Elastic License 2.0。商用之前，自己去确认一下 LICENSE 文件原文。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;第二，还在预发布。&lt;/strong&gt; 最新版本号是 &lt;code&gt;v2.0.0-next.8&lt;/code&gt;，2026 年 10 月 28 日发布。next 阶段的东西，接口可能变。&lt;/p&gt;&#10;&lt;p&gt;另外这个项目还在高频迭代：光是 10 月 27 日一天就发了 &lt;code&gt;next.5&lt;/code&gt;、&lt;code&gt;next.6&lt;/code&gt;、&lt;code&gt;next.7&lt;/code&gt; 三个版本，第二天又发了 &lt;code&gt;next.8&lt;/code&gt;。追新版本别追太紧。&lt;/p&gt;&#10;&lt;h2 id="适合谁"&gt;适合谁&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;适合&lt;/strong&gt;：每天用 Claude Code 或 Cursor 干活、会遇到「它开始忘事」的；已经在用 MCP、装了一堆 server 的；做长任务（比如一次改十几个文件）的。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;不太适合&lt;/strong&gt;：只是偶尔问几句的小任务——装完的收益抵不上配置成本；或者你压根不用 MCP 工具。&lt;/p&gt;&#10;&lt;p&gt;仓库还带一个 &lt;code&gt;/context-mode:ctx-insight&lt;/code&gt;，会打开一个托管的 dashboard 看团队级的 token 节省统计。有团队的话值得看一眼。&lt;/p&gt;&#10;&lt;h2 id="最后"&gt;最后&#10;&lt;/h2&gt;&lt;p&gt;上下文窗口的容量这几年一直在涨，但代理消耗它的速度涨得更快。&lt;/p&gt;&#10;&lt;p&gt;结果就是你越来越频繁地遇到同一件事：写到一半，它开始忘。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;context-mode&lt;/code&gt; 的答案是把「读数据」这件事挪出模型的视野，让模型只看见结论。上下文不是靠塞得更满来管的，是靠不往里塞来管的。&lt;/p&gt;&#10;&lt;p&gt;你现在的代理，有没有出现过「干到一半忘事」？评论区说说，你是怎么处理的。&lt;/p&gt;&#10;&lt;hr&gt;&#10;</description></item><item><title>59% 的重复标记、五十多轮的僵局：Anthropic 公开了 AI 审 AI 的真实数字</title><link>https://8-152-7-248.sslip.io/p/agent-review-convergence/</link><pubDate>Tue, 27 Oct 2026 00:00:00 +0800</pubDate><guid>https://8-152-7-248.sslip.io/p/agent-review-convergence/</guid><description>&lt;img src="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/cover.png" alt="Featured image of post 59% 的重复标记、五十多轮的僵局：Anthropic 公开了 AI 审 AI 的真实数字" /&gt;&lt;p&gt;你有没有这么干过：让 AI 改一段代码，它改完提交；审查代理也发现问题，AI 再改；审查代理又发现问题……&lt;/p&gt;&#10;&lt;p&gt;转着转着，一个小改动跑了十几轮。&lt;/p&gt;&#10;&lt;p&gt;2026 年 10 月 15 日，Anthropic 第一次把 Claude Code 团队的代码审查流程连同内部数字一起公开。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Noise costs more when the author is an agent. A person can wave off a nit in seconds. An agent fixes it, which means another commit and another round of review.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;翻译过来：&lt;strong&gt;作者是 AI 的时候，噪音的代价被放大了。&lt;/strong&gt; 人看到一个小 nit，几秒钟挥手就过了；AI 看到一个小 nit，它会去修，修完就是一次新提交、一次新审查。&lt;/p&gt;&#10;&lt;p&gt;所以「审查必须收敛」不是修辞，是硬约束。&lt;/p&gt;&#10;&lt;h2 id="90-的代码由-ai-写审查早就不是人干的了"&gt;90% 的代码由 AI 写，审查早就不是人干的了&#10;&lt;/h2&gt;&lt;p&gt;先看背景。Claude Code 团队公开的数字里，第一句就说明了一切：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;We&amp;rsquo;ve said before that Claude writes about 90 percent of Claude Code&amp;rsquo;s own code.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;Claude 写 Claude Code 自己约 90% 的代码。&lt;/p&gt;&#10;&lt;p&gt;在这个前提下，6 月到 9 月，Claude Code 仓库合并的 PR 数量翻了一倍多，而需要回滚的比例下降了超过 40%。&lt;/p&gt;&#10;&lt;p&gt;同时 Anthropic 单体仓库每天落地的变更量，从 5 月的约 2,500 条涨到 9 月的 30,000 多条。&lt;/p&gt;&#10;&lt;p&gt;审查这件事的位置也变了。以前它是路尽头的关卡：写代码、开 PR、等同事。现在它持续跑着，更像写作时的拼写检查，大部分改动在人被叫来看之前，已经被 Claude 审过并修过至少一轮。&lt;/p&gt;&#10;&lt;p&gt;效果是：研究预览版上线后，内部 PR 拿到实质性审查评论的比例从 16% 升到 54%。&lt;/p&gt;&#10;&lt;h2 id="59反复被标记的其实是第一轮就见过的旧代码"&gt;59%：反复被标记的，其实是第一轮就见过的旧代码&#10;&lt;/h2&gt;&lt;p&gt;Anthropic 的做法很朴素。他们跑了 &lt;code&gt;git blame&lt;/code&gt;，想知道审查者反复标记的那些东西到底是什么。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;When we ran git blame on what the reviewer kept flagging, about 59% of it was code that had been there since the reviewer&amp;rsquo;s first pass. The reviewer was rarely finding new bugs, it was finding a few more old ones each round.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;约 59% 是审查者第一轮就已经看过的旧代码。&lt;/strong&gt; 审查者很少在找新 bug，它是在每一轮里多找出几个旧 bug。&lt;/p&gt;&#10;&lt;p&gt;还有一个更极端的案例：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;One CLI PR went through more than fifty rounds of review and fixes overnight before its author stopped it.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;一个 CLI 的 PR，一夜之间来回改了五十多轮，最后是作者自己叫停的。&lt;/p&gt;&#10;&lt;p&gt;这个局面 Anthropic 有一半是自己造成的。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;We made it worse ourselves. We&amp;rsquo;d gotten used to running several rounds by default, since each pass catches something new.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;他们习惯了默认跑好几轮，因为每一轮好像都能捞到点新东西。但每多一轮，作者就多提交一次，审查就多跑一次。&lt;strong&gt;审查的产出被当成了 KPI，而收敛才是目标。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h2 id="他们怎么量噪音看作者对每条评论的反应"&gt;他们怎么量噪音：看作者对每条评论的反应&#10;&lt;/h2&gt;&lt;p&gt;Anthropic 怎么定义「噪音」，不看模型自评，看人的行为。&lt;/p&gt;&#10;&lt;p&gt;他们把审查者留下的每条评论，按 PR 上那个人的反应分类：改了 / 说这条是错的 / 说这个行为是有意的 / 说无所谓。另外还在已知干净的 PR 上跑一遍审查者，数它报出来的东西有多少真会让人做出有害改动。&lt;/p&gt;&#10;&lt;p&gt;优先级是写下来的：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Our priority, written down, is catching real bugs first and cutting review rounds second.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;先抓真 bug，再砍轮次。&lt;/strong&gt; 顺序反了，就会为了轮次好看而放过真问题。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="审查从关卡变成持续回路，噪音在 AI 作者身上被放大" class="gallery-image" data-flex-basis="324px" data-flex-grow="135" height="800" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/01-review-loop.png" srcset="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/01-review-loop_hu_619f656b88d6bbad.png 800w, https://8-152-7-248.sslip.io/p/agent-review-convergence/images/01-review-loop.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;h2 id="四条收敛改造和它们的效果"&gt;四条收敛改造，和它们的效果&#10;&lt;/h2&gt;&lt;p&gt;四条，全是可以照抄的：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;轮次上限。&lt;/strong&gt; 超过三轮，审查者不再继续挑错，而是让作者停下来重新考虑方案。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;只重审 diff。&lt;/strong&gt; 前几轮之后就只看新增的 diff。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;标记 bug 的类别，不是单行。&lt;/strong&gt; 作者一次修掉所有同类实例，而不是修一行、下一轮又冒出一行同类的。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;不重复已发过的发现。&lt;/strong&gt; 已经提过的评论不再提第二次。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;效果是三组数字：&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="收敛改造前后的三组指标对比" class="gallery-image" data-flex-basis="341px" data-flex-grow="142" height="760" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/02-convergence.png" srcset="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/02-convergence_hu_875aaf60368a03eb.png 800w, https://8-152-7-248.sslip.io/p/agent-review-convergence/images/02-convergence.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;指标&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;改造前&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: right"&gt;改造后&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一轮审过&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;65%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;78%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;需要五轮以上&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;8.9%&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;1.5%&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;平均通过所需轮次&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;2.08&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: right"&gt;1.32&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;需要五轮以上的从 8.9% 降到 1.5%，跌掉八成多。平均轮次从 2.08 降到 1.32。&lt;/p&gt;&#10;&lt;p&gt;还有一条容易被忽略：他们现在把「到通过为止的轮次」和「召回率」一起追踪。&lt;strong&gt;光砍轮次不够，得同时确认没漏掉真 bug。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h2 id="人还留在哪档位由改动定不由人定"&gt;人还留在哪：档位由改动定，不由人定&#10;&lt;/h2&gt;&lt;p&gt;自动化不是把人全撤走，是把人放到该在的地方。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;风险&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;例子&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;需要的人工批准&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;低&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;文档、文案、只在内部用的功能、flag 后的新增改动&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;自动审查与安全检查全绿后不需要人&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;中&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;常规产品改动，以及缓存、自动更新、系统提示词等敏感区域&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一人&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;最高风险代码，加上一小撮永远需要两人看的路径&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;两人&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;四条原则比表格更重要：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;The change sets the tier, not the author.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;档位看改动，不看作者。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;An agent&amp;rsquo;s approval never counts as a person&amp;rsquo;s, either. Automated review can clear a low-risk change, but it can&amp;rsquo;t fill a slot that calls for a human.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;代理的批准永远不算人的批准。自动审查可以放行一个低风险改动，但它填不了那个必须由人承担的格子。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Whoever prompted the change is on the hook for it.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;指挥代理的那个人，等于自己敲的代码，一样要担责。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;An approval covers one specific commit.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;批准只对那一个 commit 生效。&lt;/p&gt;&#10;&lt;h2 id="你今天就能抄的三件事"&gt;你今天就能抄的三件事&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;一、把重复的意见固化成文件，而不是每次重复提。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Anthropic 的做法是两张纸：&lt;code&gt;CLAUDE.md&lt;/code&gt; 说清项目是什么（技术栈、约定、目标与非目标），每次审查都会加载；&lt;code&gt;REVIEW.md&lt;/code&gt; 由代码 owner 写，告诉审查者这块代码里什么才算问题。&lt;/p&gt;&#10;&lt;p&gt;关键的机制是这一句：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;When the automated reviewer keeps flagging the same thing, we fix it at the source: in the skill, with a lint rule, or with updates to instructional files such as:&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;反复出现的同一个意见，要在源头修掉，不是在评论里重复。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;REVIEW.md&lt;/code&gt; 该长什么样，原文给了很具体的要素：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# REVIEW.md&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 这块代码坏了会表现成什么样&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 文档类：它会撒谎 —— 改了行为但描述没跟上&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 配置类：它会误路由 —— 值改了但引用没改&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 约束&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 每条发现必须给出 文件:行号，给不出就不要提&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; nit 最多 3 条，超出的归为非阻塞&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 这些路径不用看：vendor/、generated/、*.lock&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 摘要怎么写&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 第一行给数字：本次共 N 条发现&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 没问题就直说「本次没有发现需要修的地方」&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;二、给审查配上收敛开关。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;轮次上限、只重审 diff、标类别不标单行、不重复已发的发现，这四条可以直接落成配置：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;metric&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rounds_to_pass&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;guards&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;max_rounds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;on_exceed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;停止修复，让作者重新确认方向&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;re_review&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;after_round&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;diff_only&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;finding_policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;report&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bug_class&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;repeat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;suppress&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;baseline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pass_in_one_round&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;need_five_or_more&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.089&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;mean_reviews_to_pass&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.08&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;顺带把安全审查也接上。Anthropic 把安全审查做成了开源的 GitHub Action：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Security Review&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull-requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Needed for leaving PR comments&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;security&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ github.event.pull_request.head.sha || github.sha }}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fetch-depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;anthropics/claude-code-security-review@main&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;comment-pr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;claude-api-key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.CLAUDE_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它自己的 README 里写了一条警告，别忽略：这个 Action 没有针对 prompt injection 加固，只应该用来审查可信的 PR。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;三、拿事故和回滚当评测集。&lt;/strong&gt;&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Turn incidents and reverts into an eval set, and rescore the reviewer whenever its prompts, tools or model change.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;团队把出过事故的 PR 连同修复和回滚收集起来，让懂那段代码的工程师逐个确认 bug 和位置，审查者每天对着这批数据跑。&lt;strong&gt;审查者换了提示词、换了工具、换了模型，都得在这套评测上重新计分。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h2 id="开源这边同一个问题的三种解法"&gt;开源这边，同一个问题的三种解法&#10;&lt;/h2&gt;&lt;p&gt;Anthropic 的数字给的是运营视角，开源社区这阵子给出的答案是工程视角。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;openqodex/openqodex&lt;/strong&gt;（820 star，Apache-2.0）：代理写的每次改动，在 push 前先由一个单独的审查代理检查，支持 Claude Code、Codex、Cursor、Cline，不需要自己的账号或 API key。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;anthropics/claude-code-security-review&lt;/strong&gt;（6,559 star）：把安全审查做成开箱可用的 GitHub Action，上面的配置就是它的 Quick Start。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;jwuthri-cloud/Tracely-ai&lt;/strong&gt;（1,190 star，MIT）：另一个角度，把线上真实失败冻结成回归测试。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Tracely 的主张很直接：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;The recorded run &lt;em&gt;is&lt;/em&gt; the test. Tracely freezes that trace into a hermetic regression case and replays it on every PR.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;它还带一个 fail-to-pass 契约：从失败轨迹提升成用例时，附带一条硬约束，&lt;strong&gt;旧代码必须失败、新代码必须通过&lt;/strong&gt;，否则不予采信。&lt;/p&gt;&#10;&lt;p&gt;不过最有意思的其实是 openqodex 的 issue #239，一个用来把关 AI 的工具自己越权了：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;Staged review ends incomplete: verifier session fails twice, reviewer reads outside the snapshot and uses Bash it was not given&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;审查者读了快照之外的内容，还用了没被授予的 Bash 工具。&lt;strong&gt;给审查者的权限边界，和给写作代理的权限边界，是同一个问题。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="四条收敛改造与读者可落地的三件事" class="gallery-image" data-flex-basis="288px" data-flex-grow="120" height="900" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/03-four-levers.png" srcset="https://8-152-7-248.sslip.io/p/agent-review-convergence/images/03-four-levers_hu_a7d90eb6a406d85f.png 800w, https://8-152-7-248.sslip.io/p/agent-review-convergence/images/03-four-levers.png 1080w" width="1080"&gt;&lt;/p&gt;&#10;&lt;p&gt;Anthropic 最后那句话，我认为是整篇里最该抄的：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&amp;ldquo;When a review misses something, we ask what the model didn&amp;rsquo;t have in front of it and put the fix into a CLAUDE.md, a skill or a new check.&amp;rdquo;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;漏掉了什么，就问模型当时手上缺什么，然后把补上的东西写进文件、skill 或检查项。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;最后问一句：你上一次让 AI 改代码，是不是也在一个改了一轮又一轮、但每轮说的都是同一类问题的循环里？&lt;/strong&gt;&lt;/p&gt;&#10;</description></item></channel></rss>