<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
  <generator uri="https://jekyllrb.com/">Jekyll</generator>
  <link href="https://kanfu-panda.github.io/zh/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://kanfu-panda.github.io/zh/" rel="alternate" type="text/html" hreflang="zh" />
  <updated>2026-08-31T21:52:56+08:00</updated>
  <id>https://kanfu-panda.github.io/zh/feed.xml</id>
  <title type="html">功夫熊猫的博客</title>
  <subtitle>一个热爱技术的开发者的个人博客。</subtitle>
  <author><name>kanfu-panda</name></author>
  <entry xml:lang="zh-CN">
    <title type="html">pdlc-skills 如何实现无人值守（Loop 工程）？</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/08/31/run-pdlc-unattended.zh.html" rel="alternate" type="text/html" title="pdlc-skills 如何实现无人值守（Loop 工程）？" />
    <published>2026-08-31T00:00:00+08:00</published>
    <updated>2026-08-31T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/08/31/run-pdlc-unattended.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/run-pdlc-unattended-cover.png" />
    <summary type="html">一个功能其实一条命令就能自己跑完 TDD、实现、评审，用不着你写脚本。外部脚本的位置在外面一层——十来个功能点按依赖分批，一批跑完人验一道再启下一批。中间靠四条护栏兜着，这四条我每条都见它真的响过。</summary>
    <category term="AI工程" />
    <category term="提示词工程" />
    <category term="Loop工程" />
    <category term="Graph工程" />
    <category term="PDLC" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/08/31/run-pdlc-unattended.zh.html"><![CDATA[<blockquote>
  <p>上一篇跑通第一个功能，很快就会遇到下一个问题：每到一个阶段它就停下来等你点头，你还是得守在屏幕前。<strong>能不能让它自己一轮一轮往下跑，你去干别的？</strong> 能。但”让它自己跑”和”让它一步一回头地问你”，中间差的东西比看上去大。</p>
</blockquote>

<h2 id="那次跑的根本就不是-loop">那次跑的，根本就不是 loop</h2>

<p>我在自己机器上跑过完整的 loop，很顺——一口气推完了 60 多个 PR，中间它没回头问过我一句。后来在另一台机器上再跑，我几乎立刻就觉得不对劲。</p>

<p>那次是让 AI 在<strong>同一个会话里</strong>“自己循环推进各个阶段”。它确实在往前走，但每走一步就回来找我一次：这个设计行不行？下一步要不要继续？要不要我接着做？</p>

<p>我当时说了句话，大意是：<strong>你这个 loop 怎么跟我之前用的不一样？正常的 loop 不会回头问这些废话，它会一直干到把活干完。</strong></p>

<p>问题不在于它爱不爱问，而是<strong>它问的都是已经定好的事</strong>。PRD 定了，设计定了，测试怎么写也定了，它还回来确认一遍。这种确认只是把我拴在椅子上——我没法去干别的，因为不知道下次被叫回来是三分钟后还是三十分钟后。</p>

<p>表面上它在循环，实际上那是<strong>有人陪跑</strong>。</p>

<p>这件事让我想明白了：<strong>假 loop 和真 loop 的分界线只有一条——循环的控制权在谁手里。</strong></p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-fake-vs-real.png" alt="假 loop 与真 loop 的四点对比：循环写在会话里还是由代码驱动、每轮上下文是否干净、遇到问题问人还是按护栏处理、判停看模型感觉还是看磁盘状态" /></p>

<p>跟模型聪不聪明无关，也跟提示词里写没写”请自主完成不要打断我”无关。控制权在模型的意愿里，它就是假的；在一段代码里，它才是真的。</p>

<h2 id="一个功能一条命令就够">一个功能：一条命令就够</h2>

<p>先说前置铁律：<strong>PRD 和设计必须人工把关完毕，设计定稿之后才允许进 loop。</strong></p>

<p>循环加速的是 <code class="language-plaintext highlighter-rouge">TDD → 实现 → 评审</code> 这段收敛，它加速不了”想清楚要做什么”。这个功能要不要做、边界划在哪、拆成哪几块——都是判断题，没有任何一条命令能给出退出码。</p>

<p>把关完了，剩下那段一条命令的事：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-loop-run F20260825-XXXXXX
</code></pre></div></div>

<p>它从这个功能当前停在的阶段出发，自动往下推 <code class="language-plaintext highlighter-rouge">tdd → implement → review</code>，直到评审完成或卡住停机。每个阶段派一个全新的 subagent 去干，干完回来读状态机，再决定推进、停、还是交还给人。默认最多 4 步。</p>

<p><strong>前提是上一篇那份 <code class="language-plaintext highlighter-rouge">test-commands.yml</code> 已经配置好了。</strong> 判停靠的是真跑命令的退出码，没有那份文件就没有命令可跑——上一篇那步跳了，这一篇的东西全是空转。</p>

<p>它替你做掉的，正是我第一次手动试时做砸的那件事：<strong>把”还要不要再来一轮”这个决定，从模型手里拿走。</strong> 靠三个设计点撑着。</p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-one-round.png" alt="一轮循环的三拍：问下一步（输出过白名单）、干这一步（干净上下文）、读状态文件判停；插件内和外部脚本跑的是同一套节拍" /></p>

<p><strong>第一，控制流是代码，不在模型脑子里。</strong> 每轮只问模型两件事：下一步该跑哪条命令，以及把这一步干完。前者的答案还得过一道白名单，只认那五个词，多一个字的解释都会被滤掉。<strong>模型说废话没用，因为下游根本不读废话。</strong></p>

<p><strong>第二，每个阶段都从干净的上下文开始。</strong> 上一阶段的犹豫、误解、被带偏的推理，不会跟进下一阶段。跨阶段的记忆全在磁盘那份状态机文件里——第 3 篇讲过的那个。<strong>会话可以死，状态不会丢。</strong></p>

<p><strong>第三，判停读文件，不读嘴。</strong> 每步干完，循环不问模型”你觉得过了吗”，而是去读 <code class="language-plaintext highlighter-rouge">last_phase_result.ok</code>——上一阶段真跑命令得到的落盘结果。第 3 篇那两个格子，<strong>判停用的就是只装真跑结果的那个</strong>。</p>

<h2 id="十来个功能这才轮到外部脚本">十来个功能：这才轮到外部脚本</h2>

<p><code class="language-plaintext highlighter-rouge">/pdlc-loop-run</code> 跑在插件内部，适合一个功能的短收敛。你也可以自己写 bash 循环——每轮起一个真正独立的进程，上下文隔离得更彻底，长跑更稳——但<strong>光这么跑一个功能点意义不大</strong>：它和 <code class="language-plaintext highlighter-rouge">/pdlc-loop-run</code> 干的是同一件事，只是换了个进程边界。</p>

<p>脚本真正的位置在外面一层。第 3 篇画过那个两级结构——把活拆成 N 个相对独立的功能点，外层给每个功能点各起一条循环，各跑各的 worktree。外层干的是起循环、收结果、排下一批，不替某一个功能推阶段。这一篇补上它怎么排：<strong>按依赖分批</strong>。</p>

<p>我之前一个项目大概十来个功能点，分了三批：<strong>地基一批，被依赖的功能一批，剩下互不相干的独立功能一次全上。</strong> 每批内部并行，一批跑完人验一道，再启下一批，逐批把全部收敛掉。</p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-batches.png" alt="十来个功能点按依赖分三批：地基一批、被依赖的功能一批、互不相干的独立功能一次全上，每批内部并行，批与批之间由人验一道" /></p>

<p>顺序不能反：地基没立起来，后面全是空中楼阁；被依赖的没做完，依赖它们的只能空等。最后那批谁也不等谁，同批全开最划算——功能编号用”日期+时分秒”本来就是为并行准备的，不会撞号。</p>

<p>得说清楚：<strong>这层批次调度是我自己排的，pdlc 没提供。</strong> 它给的是”一个功能点怎么自己跑完”，外加 <code class="language-plaintext highlighter-rouge">/pdlc-relate</code> 那张依赖图帮你看清谁依赖谁。怎么分批、每批放几个——仍然是判断题，仍然在循环外面。</p>

<h2 id="四条护栏每一条我都见它真正起作用">四条护栏，每一条我都见它真正起作用</h2>

<p>护栏这东西，写在文档里谁都会写。更值得看的是它们到底有没有真起过作用。</p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-guardrails.png" alt="四条护栏各自的触发条件与真实触发记录：硬预算触发两次、fail-stop 日常兜底、blocked 升级触发一次、步数上限触发一次" /></p>

<h3 id="-硬预算每轮---max-budget-usd">① 硬预算：每轮 <code class="language-plaintext highlighter-rouge">--max-budget-usd</code></h3>

<p>给每一轮的进程设个上限，烧到就被<strong>进程级掐断</strong>，不跟你商量。这条是外部脚本形态的必配项。</p>

<p><strong>这条我在一次跑里触发了两次。</strong> 我第一轮把上限设成 5，实现阶段跑到中途就被斩断；检查了一遍磁盘上的半成品，确认完整可续，把上限提到 8 再跑，又触顶；最后设到 12 才跑完。</p>

<p>被斩断那一刻最值得说的是：<strong>磁盘上的东西是完整的。</strong> 文档落了盘，状态机记了账，损失的只是”这一轮没跑完”，不是”全部重来”。落盘加记账，把一次粗暴的掐断变成了一次可以续跑的暂停。</p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-budget-cut.png" alt="预算掐断的一轮：进程被斩断，但磁盘上的文档和状态机是完整的，下一轮可以从原地续跑而不是重来" /></p>

<p>这里得交代一句，免得那几个数字被误读：<strong>5、8、12 是我给每轮设的上限，不是我实际掏了多少钱。</strong> 我用的是订阅模式、按周算量，各家 API 的单价也不一样。这三个数字当”我摸出来的量级”看就行——真要算成本，得看 token 消耗量，再按你用的那家官方定价去估。</p>

<h3 id="-和-不-ok-就停步数耗尽也停">② 和 ④：不 ok 就停，步数耗尽也停</h3>

<p><strong>fail-stop</strong> 兜的是最容易被忽略的一种烂账：<strong>一个失败的阶段被下一轮当成”已完成”踩过去</strong>——一旦踩过去，后面每轮都建立在假前提上，跑得越久错得越深，还全程绿灯。</p>

<p><strong>步数上限</strong>防的是振荡：改坏了→修好了→又改坏了，每轮都像在干活，其实原地打转。默认 4 步 = 收敛段三步 + 一步容错余量，我保守设成 1 试过一次，如期停机。</p>

<h3 id="-blocked-升级该停的时候真的停了">③ blocked 升级：该停的时候真的停了</h3>

<p>评审一旦发现需要产品决策的问题，就往状态机里写 <code class="language-plaintext highlighter-rouge">blocked</code>，停机，等人。</p>

<p><strong>这条真响的那一次，才让我觉得这套东西可信。</strong></p>

<p>评审时它发现，这轮新增的三个配置字段，后端<strong>没有任何一处消费它们</strong>。代码是对的，测试也是绿的，这不是 bug，是个产品级的缺口：字段该砍掉，还是链路没接完该补上？<strong>这个问题 AI 无权替我决定。</strong></p>

<p>它没猜，也没顺手删掉字段让测试继续绿——写了 blocked，停机，把问题交回给我。我拍板保留字段、补上消费链路，让它接着跑。续跑复审时还有个意外收尾：它反手把这套补链路实现里的 2 个真 bug 也修掉了。</p>

<p>该停的停了，该查的也查了。我要的就是这个。</p>

<h2 id="这一轮跑出来了什么">这一轮跑出来了什么</h2>

<p>那次的活是一个控制台项目的三期迭代：4 个后端接口组、3 个前端页面，外加一套通知规则。<code class="language-plaintext highlighter-rouge">TDD → 实现 → 评审</code> 全程无人值守跑完，<strong>全量测试 301 项通过，覆盖率 87% 以上。</strong></p>

<p>人工介入一共只有三回：两次预算续命时的核验决策，加上 blocked 那次的产品拍板。</p>

<p><img src="/assets/images/posts/run-pdlc-unattended-fig-run-summary.png" alt="一个下午的时间线：设计定稿之后进入 TDD、实现、评审三段无人值守，实现段被预算掐断两次，评审段停机等人拍板一次，终点是评审完成交回人工" /></p>

<p>这是一个下午的量，我不想说得比实际更神。但 loop 期间它对我完全静默——回来看到的不是一串”接下来要做那个吗”，而是一个终态。<strong>不是它跑得多快，是它不需要我在场。</strong></p>

<h2 id="人工与机器自动化的边界">人工与机器自动化的边界</h2>

<p>判据从第 2 篇起就没变过：<strong>能自动发现错误的才敢交给循环。</strong> 照这条划下来，有两件事永远交不出去：</p>

<ul>
  <li><strong>想清楚要做什么（需要人工决策）。</strong> PRD 和设计必须人工把关，这是进 loop 的前置条件，不是建议。</li>
  <li><strong>发布。</strong> 循环的终态是评审完成，到这儿就停，交由人来决定要不要发。<code class="language-plaintext highlighter-rouge">--autonomous</code> 对发布和部署一律无效——发出去就收不回来。</li>
</ul>

<p>这划的是”哪些活可以交出去”。还有一个跟它配套的问题：<strong>交出去之后，靠什么保证它不乱来？</strong></p>

<p>第 1 篇讲提示词工程时，我给那一层写过一句天花板：它是软约束，模型未必完全听你指挥，你当场也未必看得出来。当时只是一句原则，这一趟碰上了它的实例。</p>

<p>“省着点花、注意控制消耗”这类话写进提示词很容易，但它属于提示词层——引导得了，保证不了。<code class="language-plaintext highlighter-rouge">--max-budget-usd</code> 属于进程层，它不看你写了什么，到点就掐。这一趟两次超支，两次都是被它掐停的，不是被那句话劝住的。</p>

<p><strong>凡是”引导了但不保证”的东西，最终都得往上层挪。</strong> 这一次，它挪到了进程参数上。</p>

<h2 id="下一篇">下一篇</h2>

<p>如果这篇只留一句话给你带走，我希望是这句：<strong>判断一个 loop 是真是假，只看一件事——控制流写在代码里，还是写在模型的意愿里。</strong>一个会回头问你的循环，不是循环。</p>

<p>下一篇聊<strong>质量</strong>：这条流水线跑得又快又不用人看着，那它产出的东西凭什么信得过？测试红灯门、评审闸门、覆盖率各自在挡什么，合起来又挡不住什么。</p>

<hr />

<p><strong>想自己试一下</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>仓库在这里：https://github.com/kanfu-panda/pdlc-skills</p>

<p>觉得有用的话，给个 star 是对我最大的支持 ⭐</p>

<hr />

<p>这四条护栏我是照着那次真跑的记录一条条写的，包括被掐断和被卡住的时候。如果它让你觉得”原来这东西是可以信的”，点个赞让我知道；要是你也跑过 loop——不管跑顺了还是跑歪了——评论区可以说说，一起讨论下。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">如何让 pdlc-skills 在项目里跑起来？</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/08/30/run-pdlc-in-your-project.zh.html" rel="alternate" type="text/html" title="如何让 pdlc-skills 在项目里跑起来？" />
    <published>2026-08-30T00:00:00+08:00</published>
    <updated>2026-08-30T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/08/30/run-pdlc-in-your-project.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/run-pdlc-in-your-project-cover.png" />
    <summary type="html">装上只要一行命令。真正卡住人的是起手第一步——你的起点是新项目、老项目还是已经接入，第一条命令完全不同。还有一步最容易被跳过：不做它，前一篇讲的那套「客观判停」会整个退化成模型自我感觉良好。</summary>
    <category term="AI工程" />
    <category term="提示词工程" />
    <category term="Loop工程" />
    <category term="Graph工程" />
    <category term="PDLC" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/08/30/run-pdlc-in-your-project.zh.html"><![CDATA[<blockquote>
  <p>前三篇都是图纸：三层各自是什么、落在哪、怎么联动。这一篇换个方向——<strong>把图纸落到你自己的项目上</strong>。读完你应该能装上它、知道该从哪条命令起手、跑通第一个功能。中间有一步最容易被跳过，我会把它单独拎出来说，因为跳过它，前一篇讲的那套东西会整个落空。</p>
</blockquote>

<p>前三篇把理论部分讲得差不多了，下面来看 pdlc-skills 在真实项目里怎么运转起来。</p>

<p>有很多人会有疑问，是先把测试补齐吗？文档要不要先写全？一个跑了两三年、代码堆了一堆、谁都不太敢乱动的老项目，接进来是不是第一天就得先还技术债？</p>

<p>先把答案给你：<strong>都不用，装上只要一行命令，一分钟的事。</strong></p>

<p>真正会卡住你的是另一件事——装完之后那个”然后呢”。我自己接入第一个项目时，盯着一屏 <code class="language-plaintext highlighter-rouge">/pdlc-</code> 开头的命令，愣了半天不知道该敲哪个。这一篇就是来回答那个”然后呢”的：你该从哪条命令起手，中间哪一步绝对不能跳。</p>

<h2 id="装上一行命令两个落点">装上：一行命令，两个落点</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 全局：这台机器上所有项目都能用</span>
curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>

<span class="c"># 项目级：只在这一个仓库里生效</span>
curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--project</span> /path/to/my-project
</code></pre></div></div>

<p>前三篇给的一直是全局那条，因为它最短、最不容易出错。这里说说<strong>什么时候该用项目级</strong>。</p>

<p>两个落点的区别只有一个：装进 <code class="language-plaintext highlighter-rouge">~/.claude/plugins/pdlc/</code> 还是 <code class="language-plaintext highlighter-rouge">&lt;项目&gt;/.claude/plugins/pdlc/</code>。但带来的影响是实打实的——项目级安装让<strong>版本跟着仓库走</strong>。团队里其他人 clone 下来，拿到的是和你同一个版本；你手上同时开着好几个项目时，也不会因为一次全局升级把所有项目一起动了。</p>

<p>一个人、一台机器、项目不多，那就全局，别折腾。</p>

<p>装完确认一下真的生效了，两步：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> ~/.claude/plugins/pdlc/          <span class="c"># 全局装的看这里</span>
<span class="nb">ls</span> &lt;项目&gt;/.claude/plugins/pdlc/     <span class="c"># 项目级装的看这里</span>
</code></pre></div></div>

<p>能看到 <code class="language-plaintext highlighter-rouge">skills/</code> <code class="language-plaintext highlighter-rouge">references/</code> <code class="language-plaintext highlighter-rouge">VERSION</code> 这些就对了。然后在 Claude Code 里敲 <code class="language-plaintext highlighter-rouge">/pdlc-</code>，下拉框里应该列出 38 个命令。列出来了，就装好了。</p>

<p><img src="/assets/images/posts/run-pdlc-in-your-project-fig-scopes.png" alt="两个安装落点：全局装进用户目录管所有项目，项目级装进仓库里只管这一个" /></p>

<h2 id="你是哪种起点">你是哪种起点？</h2>

<p>38 个命令铺开确实唬人，但<strong>第一次只需要认三个</strong>。挑哪个，取决于你手上是什么：</p>

<table>
  <thead>
    <tr>
      <th>你的起点</th>
      <th>第一条命令</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>全新项目，代码还没写</td>
      <td><code class="language-plaintext highlighter-rouge">/pdlc-bootstrap</code></td>
    </tr>
    <tr>
      <td>已有项目，代码一大堆</td>
      <td><code class="language-plaintext highlighter-rouge">/pdlc-adopt scan</code></td>
    </tr>
    <tr>
      <td>已经接入了，日常开发</td>
      <td><code class="language-plaintext highlighter-rouge">/pdlc-feature</code></td>
    </tr>
  </tbody>
</table>

<p><strong>全新项目</strong>用 <code class="language-plaintext highlighter-rouge">/pdlc-bootstrap</code>：给它一句话描述，它做技术栈选型、生成目录骨架和文档草稿。适合”我想做个 X”还停留在念头阶段的时候。</p>

<p><strong>已有项目</strong>是大多数人的情况，也是我最想让你注意的一条：第一条命令是 <code class="language-plaintext highlighter-rouge">/pdlc-adopt scan</code>，而 <strong>scan 全程只读</strong>——扫描你的技术栈、服务结构、数据库、已有测试，输出一份接入报告和健康检查结果，<strong>一个字节都不改</strong>。</p>

<p>我觉得这个设计比功能本身更值得说。让 AI 碰一个跑着的老项目，谁都会犹豫。所以它把”看”和”动”拆成了两条命令：先 <code class="language-plaintext highlighter-rouge">scan</code> 出报告，你看完觉得靠谱，再 <code class="language-plaintext highlighter-rouge">/pdlc-adopt init</code> 生成基线文档。试探的成本降到了零。</p>

<p><img src="/assets/images/posts/run-pdlc-in-your-project-fig-three-starts.png" alt="三种起点各走一条第一条命令，之后汇入同一条主流程" /></p>

<h2 id="别跳过这一步先把什么叫过定下来">别跳过这一步：先把”什么叫过”定下来</h2>

<p><strong>这是全篇最重要的一段。</strong></p>

<p>上一篇讲过状态文件里那两个格子：第一个格子只准填<strong>真跑命令得到的退出码</strong>，模型自评被单独放在第二个格子里，永不参与判停。</p>

<p>那就有个问题了——<strong>那些命令是从哪来的？</strong></p>

<p>答案是 <code class="language-plaintext highlighter-rouge">/pdlc-test-setup</code>。它做四件事：探测技术栈、逐条验证命令真能跑、写进 <code class="language-plaintext highlighter-rouge">docs/00_standards/test-commands.yml</code>、再把测试目录和本地钩子搭起来。</p>

<p>仓库里对这一步的定位写得很直白：整套东西的命门是”checks 只认命令退出码，绝不用模型自评”，<strong>但在此之前没有任何东西帮你把这个文件立起来，没有它，整条客观化链路就是空的</strong>。</p>

<p>它有一条不可协商的纪律：<strong>写进这个文件的每条命令，必须先被真跑过一次、亲眼看到退出码</strong>。猜出来的一律不写。理由是那句我觉得最值得注意的话——</p>

<blockquote>
  <p>一条”看起来对但跑不了”的命令，<strong>比留空更坏</strong>。</p>
</blockquote>

<p>留空，下游知道这个阶段没东西可判，会老老实实空着；而一条跑不通的假命令，会让每个阶段都拿到假的 checks，报告还是绿的。这条道理反过来还有个镜像：<strong>最危险的”自动修复”，是把已经立起来、但坏掉了的检查悄悄置空</strong>——闸门当场就松了，你还看不出来。</p>

<p>所以跳过这一步会发生什么？没有命令可跑 → 第一个格子按规矩必须留空 → 判停无据可依 → 你以为的”客观校验”，实际上一直是模型在自我感觉良好。<strong>上一篇那套设计的地基，就在这一步。</strong></p>

<p>顺带一提，这个文件会过期——脚本改名、工具换代、子项目增删都会让它失真。不用你盯着，下游阶段遇到”命令跑不了”会主动提示你，看到提示再 <code class="language-plaintext highlighter-rouge">/pdlc-test-setup --refresh</code> 就行。</p>

<p><img src="/assets/images/posts/run-pdlc-in-your-project-fig-foundation.png" alt="test-commands.yml 立起来，上一篇那个「只装真跑结果」的格子才填得进东西" /></p>

<h2 id="跑第一个功能">跑第一个功能</h2>

<p>地基有了，正式开工只需要一句话：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-feature 给登录加手机号验证
</code></pre></div></div>

<p>然后它顺着 PRD → 设计 → TDD → 实现 → 评审往下走，每到一个阶段停一次、交接一次。修 bug 是同一个用法，换成 <code class="language-plaintext highlighter-rouge">/pdlc-fix</code>；想看当前进展，<code class="language-plaintext highlighter-rouge">/pdlc-status</code>。</p>

<p><strong>日常你就用这三个。</strong> 剩下 35 个是需要精细控制时才往下钻的——想单独重跑设计阶段、想只做一次代码评审、想加一份数据库设计，那时候再去翻就行。</p>

<h2 id="磁盘上多出来的东西哪些要进-git">磁盘上多出来的东西，哪些要进 git</h2>

<p>跑过一轮之后，<code class="language-plaintext highlighter-rouge">docs/</code> 下会多出一批目录：需求、设计、测试、部署、评审各有其位，还有一个 <code class="language-plaintext highlighter-rouge">docs/.pdlc-state/</code>，每个功能一份 JSON。</p>

<p>前面那些是文档，进不进 git 你自己决定。但有一个<strong>必须进</strong>：</p>

<p><strong><code class="language-plaintext highlighter-rouge">docs/.pdlc-state/</code> 要提交进 git，别加进 <code class="language-plaintext highlighter-rouge">.gitignore</code>。</strong></p>

<p>它看着像缓存——一个点开头的目录、一堆机器读的 JSON，很容易让人顺手忽略掉。但它不是缓存，是<strong>交接物</strong>。换个会话、换台机器、换个人接手，能说清”这个功能走到哪一步、上一步过没过”的，只有它。不进 git，就等于每次重开会话都失忆一次；团队协作时，别人根本看不到你这边的进度。</p>

<p>仓库里给它的定性是”项目交付审计记录”。按审计记录对待，就不会想着忽略它了。</p>

<p><img src="/assets/images/posts/run-pdlc-in-your-project-fig-tree.png" alt="跑起来后 docs 下的目录结构，标出状态机目录必须进 git" /></p>

<h2 id="老项目接入的两条纪律">老项目接入的两条纪律</h2>

<p>如果你是从老项目接进来的，有两条纪律值得提前知道，因为它们决定了这件事现不现实：</p>

<p><strong>第一，只生文档，不动代码。</strong> 接入过程中一行业务代码都不改，只逆向生成基线文档。</p>

<p><strong>第二，增量接入。</strong> 旧代码统一标成”已接入基线”，<strong>只有新功能才走完整流程</strong>。不要求你先把历史债还清。</p>

<p>第二条是关键。我见过太多流程工具死在第一天：一接入就报出几百个存量不合规，人一看这个数字就放弃了。把存量圈起来放过，只管新增，这条流程才有活到第二天的可能。</p>

<h2 id="什么时候不该用它">什么时候不该用它</h2>

<p>也说说边界。一次性脚本、跑完就删的 demo、纯文档仓库——这些别用，流程的成本收不回来。它是给”要活很久、要被人接手、要对质量负责”的项目准备的。</p>

<p>判断标准很简单：<strong>这个项目三个月后还有人会打开吗？</strong> 会，就值得；不会，别折腾。</p>

<h2 id="照着走一遍">照着走一遍</h2>

<p>把上面这些压成一张单子，你可以直接照做：</p>

<ol>
  <li><strong>装</strong>：<code class="language-plaintext highlighter-rouge">curl … | bash -s -- --global</code>（多项目或团队协作就换 <code class="language-plaintext highlighter-rouge">--project &lt;路径&gt;</code>）</li>
  <li><strong>验</strong>：<code class="language-plaintext highlighter-rouge">ls ~/.claude/plugins/pdlc/</code>，再在 Claude Code 里敲 <code class="language-plaintext highlighter-rouge">/pdlc-</code> 看有没有列出 38 个命令</li>
  <li><strong>认起点</strong>：新项目 <code class="language-plaintext highlighter-rouge">/pdlc-bootstrap</code>；老项目 <code class="language-plaintext highlighter-rouge">/pdlc-adopt scan</code> 看报告，满意再 <code class="language-plaintext highlighter-rouge">/pdlc-adopt init</code></li>
  <li><strong>立地基</strong>：<code class="language-plaintext highlighter-rouge">/pdlc-test-setup</code>——<strong>这一步别跳</strong>，它决定后面所有”过没过”是真是假</li>
  <li><strong>开工</strong>：<code class="language-plaintext highlighter-rouge">/pdlc-feature 你的一句话需求</code>，然后 <code class="language-plaintext highlighter-rouge">/pdlc-status</code> 随时看走到哪了</li>
  <li><strong>提交</strong>：把 <code class="language-plaintext highlighter-rouge">docs/.pdlc-state/</code> 一起 commit 进去</li>
</ol>

<p>第 4 步是唯一一个”看起来可以先放放、实际上放不得”的。其余按顺序照走就行。</p>

<h2 id="下一篇能不能让它自己往下跑">下一篇：能不能让它自己往下跑？</h2>

<p>到这儿，装上、起手、立地基、跑通第一个功能，一条线走完了。如果这篇只留一句话给你带走，我希望是这句：<strong>先把”什么叫过”定下来，再开始跑</strong>——顺序反了，后面所有的自动化都是空转。</p>

<p>下一篇是这个系列的重点篇：<strong>装好了、也跑起来了，能不能让它自己一轮一轮往下跑，不用你守着？</strong> 会讲透那条收敛循环的机制、契约，和四条不可协商的护栏——也包括我自己踩过的那次：嘴上约束治不住 Token 消耗，最后是一条硬预算把它接住的。</p>

<hr />

<p><strong>想自己试一下</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>仓库在这里：https://github.com/kanfu-panda/pdlc-skills</p>

<p>觉得有用的话，给个 star 是对我最大的支持 ⭐</p>

<hr />

<p>这一篇是我按自己接入项目的真实顺序写的，如果有疑问，欢迎留言讨论。如果你照着走通了，回来告诉我一声；卡在哪儿了也告诉我，我补进下一版。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">三大工程在 pdlc-skills 怎么联动？</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/08/23/how-three-paradigms-interlock.zh.html" rel="alternate" type="text/html" title="三大工程在 pdlc-skills 怎么联动？" />
    <published>2026-08-23T00:00:00+08:00</published>
    <updated>2026-08-23T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/08/23/how-three-paradigms-interlock.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/how-three-paradigms-interlock-cover.png" />
    <summary type="html">三层从头到尾没有互相调用过一次。它们靠磁盘上一份文件交接——一层写，另一层读。而正因为这份文件是按功能切开的，这条流水线不止能往下走一条，还能横着铺开同时跑好几条。</summary>
    <category term="AI工程" />
    <category term="提示词工程" />
    <category term="Loop工程" />
    <category term="Graph工程" />
    <category term="PDLC" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/08/23/how-three-paradigms-interlock.zh.html"><![CDATA[<blockquote>
  <p>上一篇末尾我留了把尺子：判断一个工具的三层是不是真结合，看它们是否共用一份状态。这一篇就来兑现这个判断——<strong>三层从头到尾没有互相调用过一次</strong>，它们靠磁盘上的一份文件交接。顺带还有个我当初没预料到的副作用：因为这份文件是按功能切开的，这条流水线不止能纵向走一条，还能横向铺开同时跑好几条。</p>
</blockquote>

<p>上一篇把三层各自落在 <a href="/zh/pdlc/">pdlc-skills</a> 的哪个位置说清楚了：所有命令共用的那套规矩是提示词层，自动往下推进的那一段是 Loop 层，阶段顺序和关系图是 Graph 层。</p>

<p>但知道谁管什么，跟知道它们怎么拼在一起，是两回事。</p>

<p>顺着这个想法，最直接的办法是去代码里找那行调用——Graph 在哪儿调用了 Loop，Loop 又在哪儿调用了提示词层。但你找不到——这三层之间没有任何一行代码互相引用。</p>

<p>那它们是怎么凑到一起干活的？</p>

<h2 id="钥匙是节拍不是调用">钥匙是「节拍」，不是「调用」</h2>

<p>三层之所以不需要互相认识，是因为<strong>它们根本不在同一个时刻起作用</strong>。</p>

<table>
  <thead>
    <tr>
      <th>层</th>
      <th>什么时候起作用</th>
      <th>管的是什么</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Graph</td>
      <td>阶段<strong>边界</strong>上</td>
      <td>准不准进下一步</td>
    </tr>
    <tr>
      <td>提示词</td>
      <td>一次调用<strong>内部</strong></td>
      <td>这一次给模型看什么</td>
    </tr>
    <tr>
      <td>Loop</td>
      <td>阶段<strong>之间</strong></td>
      <td>还要不要再来一轮</td>
    </tr>
  </tbody>
</table>

<p>打个比方：Graph 在门口检票，提示词在场内讲话，Loop 在场外数还剩几场。三个人从没打过招呼，靠的是同一块记分牌。</p>

<p>那块记分牌，就是磁盘上那份文件。</p>

<p><img src="/assets/images/posts/how-three-paradigms-interlock-fig-timeline.png" alt="三条泳道的时间线：Graph 只出现在阶段边界上，提示词在每次调用内部展开，Loop 落在两个阶段之间" /></p>

<h2 id="跟着一个功能走一遍">跟着一个功能走一遍</h2>

<p>抽象说完了，跟着一个真功能走一遍，看每一拍是谁在出手。</p>

<p><strong>第一拍，起手。</strong> 一个新功能刚立项，磁盘上什么都没有。这时候能接住它的命令只有一条——写需求。为什么？因为别的命令都声明了自己需要什么前置文档：做设计要先有需求，写测试要先有设计，一样前置条件都不满足。<strong>这不是”建议先写需求”式的劝告，而是没有前置产物就进不来这道门。</strong> 这就是 Graph 在起作用，它只管”放不放你进来”。</p>

<p><strong>第二拍，干活。</strong> 进了门，Graph 就退场了，接下来是提示词层的场子。这一次调用里，模型看到的不只是这条命令自己的正文，还有一整套所有命令共用的规矩——产物必须落盘、交接前必须自检、修复只做一次。这些规矩写在一处，调用时展开进来，而不是在每条命令里各抄一遍。</p>

<p>产出的东西也不是随手写的散文。文件名、放哪个目录、有哪几章，都是定死的；文档顶上还带一小段身份信息，写着它属于哪个功能、处在哪个阶段、上一份文档是谁。格式细节这篇不展开，只说它意味着什么：<strong>产物自己带着坐标</strong>，往回翻能一路串到最初的需求。</p>

<p><strong>第三拍，收尾写盘。</strong> 这一拍最关键。主要的活干完，流程还没结束——命令必须往那份文件里记一笔：我这一段干完了、干得怎么样、下一步该谁接。这份文件在磁盘上，<strong>一个功能一份</strong>。</p>

<p><strong>第四拍，判停。</strong> 循环这时候才出场，而且它只读那份文件——不看代码，不看产物，也不看聊天记录。读完吐出一个结果：下一步该跑哪条命令，或者”完事了”，或者”卡住了”。外层脚本拿着这个结果去跑下一条，跑完又回到第三拍。</p>

<p>走到评审完成，循环停止，输出”完事了”，然后等人。发布和部署永远不在循环里——一旦发出去就收不回来，影响的还是线上的真实用户。</p>

<p>所以整条链上真正的交接只有三处，每一处交出去的都是<strong>一个名字，不是一次调用</strong>：</p>

<ul>
  <li>Graph 交给这次调用的是”你该干哪一段”；</li>
  <li>这次调用交给磁盘的是”我干完了，结果是这样”；</li>
  <li>磁盘交给循环的是”还要不要继续”。</li>
</ul>

<p>三层谁也没碰过谁。</p>

<p><img src="/assets/images/posts/how-three-paradigms-interlock-fig-handoff.png" alt="三个交接点：Graph 交出该干哪一段，阶段收尾把结果写进磁盘，循环从磁盘读出还要不要继续" /></p>

<h2 id="那份文件里最关键的一处设计">那份文件里最关键的一处设计</h2>

<p><strong>“真跑出来的”和”模型自己说的”，放在两个格子里。</strong></p>

<p>第一个格子只准填真跑命令得到的结果：测试过没过、覆盖率够不够、代码检查干不干净——全是从退出码翻译过来的真假值。如果某个阶段压根没有命令可跑（比如只产文档的需求和设计阶段），就留空；不许因为”我觉得这段做得挺好”，就填个”过了”。</p>

<p>第二个格子才是模型的自我检查，只记有几项没通过，<strong>仅供参考，永远不参与判停</strong>。</p>

<p>差别在哪儿？循环判停只看第一个格子。也就是说，模型自评是在数据结构这一层被排除在判停链路之外的，不是靠一句”请你如实汇报”的约定。它在第二个格子里写什么，都改变不了这一轮停不停。</p>

<p>上一篇说”三层共用一份状态”，共用的就是这几个格子。</p>

<p><img src="/assets/images/posts/how-three-paradigms-interlock-fig-state.png" alt="状态文件里两个格子的区别：一个只装真跑命令的结果并决定停不停，另一个装模型自评、只供参考" /></p>

<h2 id="真正的循环其实有两级">真正的循环，其实有两级</h2>

<p>到这儿得承认一件事：上面画的那条循环，只是<strong>内层循环</strong>。</p>

<p>我自己真拿它干活的时候，跑的是这样的结构——</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>主控 agent：整体设计 → 拆成 N 个互相独立的功能点
    ↓
外层脚本：给每个功能点各起一条
    ↓
内层循环 × N（各跑各的功能点）   ← 同时跑
    ↓
各自停在「评审完成」或「卡住」 → 人工汇总，决定发布
</code></pre></div></div>

<p>先由一个主控 agent 做整体设计，把要做的事拆成若干个相互独立的功能点；外层脚本给每个功能点各起一条内层循环；这些循环并行往前跑，各自停在自己的终点；最后由人把 N 条的结果收拢起来看。</p>

<p><strong>凭什么敢同时跑？</strong> 前提是拆出来的必须是<strong>相对独立的功能点</strong>——彼此之间没有依赖，才谈得上同时开工。在这个前提下，状态文件也按功能点切开，一个功能点一份文件，相互不依赖。</p>

<p>功能编号用的是”日期 + 时分秒”，不是当天的第几号。仓库里写明的理由是——多人或者多个 AI 同时开工时，各自去取”当天最大号加一”必然撞号，撞了状态文件就同名冲突，只能手工重编。改成创建那一刻的时分秒，各干各的也几乎不会撞号。这个编号格式本来就是为并行准备的，只是我当时没把它当成什么正经设计，就当个防冲突的小技巧写进去了。</p>

<p>代码那一层也得隔离开：每个功能点的内层循环跑在自己的 git worktree 里，互不干扰，产物各走各的 PR。</p>

<p><strong>那为什么”拆分”这一步必须留给人、或者留给主控 agent？</strong> 上一篇给过一条判据：能自动发现错误的交给循环，不能的留给人。”这批需求该拆成几个功能点、哪些之间有依赖”——拆错了不会有任何命令报错——它是判断题，不是校验题。所以它在循环外面。</p>

<p><strong>两级的终点也不一样</strong>：内层的终点是评审完成，外层的终点是”N 条都收敛了”。而发布仍然是唯一那道人工闸门——不管同时跑了多少条，最后按下去的还是人。</p>

<p>这套方式我自己在一个命令行工具项目上真跑过。怎么把它跑起来、护栏怎么设、真跑一夜跑出什么数据，后面几篇专门讲，这一篇只画形状。</p>

<p><img src="/assets/images/posts/how-three-paradigms-interlock-fig-two-level.png" alt="两级循环：主控拆成 N 个相对独立的功能点，外层脚本各起一条内层循环跑在自己的 worktree 里，最后汇总到人工发布闸门" /></p>

<h2 id="下一篇怎么在自己项目里跑起来">下一篇：怎么在自己项目里跑起来</h2>

<p>如果这篇只留一句话，我希望是这句：<strong>三层能联动，不是因为它们互相调用，而是因为它们对着同一份文件各写各的格子。</strong></p>

<p>接口是名字，不是函数——这也是为什么改一层不会波及另一层，也是为什么这条流水线能横向复制出好几条。</p>

<p>这一篇有意没有往下钻：那份状态文件具体有哪些字段、循环的护栏和上限怎么定、并行跑起来怎么收口，都留到后面几篇。<strong>先把形状看清楚，再看零件。</strong></p>

<p>到这儿，三层是什么、各自落在哪、怎么联动，三篇讲完了。下一篇换个方向：<strong>在你自己的项目里，怎么把它装上、怎么起手、老项目怎么接进来</strong>——从这篇的图纸，走到你终端里第一条真正跑起来的命令。</p>

<hr />

<p><strong>想自己试一下</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh <span class="se">\</span>
  | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>仓库在这里：https://github.com/kanfu-panda/pdlc-skills</p>

<p>觉得有用的话，给个 star 是对我最大的支持 ⭐</p>

<hr />

<p>这篇里那张”两级循环”的图，是我自己真跑的方式，画出来之前一直只在脑子里。如果它让你对这套东西有了具体的印象，点个赞让我知道；要是你身边也有人在琢磨怎么让 AI 同时干好几件事，转给他看看。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">为什么 pdlc-skills 天然契合三大工程？</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/08/13/why-pdlc-fits-three-paradigms.zh.html" rel="alternate" type="text/html" title="为什么 pdlc-skills 天然契合三大工程？" />
    <published>2026-08-13T00:00:00+08:00</published>
    <updated>2026-08-13T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/08/13/why-pdlc-fits-three-paradigms.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/why-pdlc-fits-three-paradigms-cover.png" />
    <summary type="html">这三层不是我照着理论设计的，我做它的时候压根没听过 Loop 工程和 Graph 工程。它就是照软件工程里那套现成的产品开发生命周期做的——需求、设计、测试、实现、评审。做完之后拿三大工程范式来对，发现处处对得上。这是个巧合，但巧合背后有原因。</summary>
    <category term="AI工程" />
    <category term="提示词工程" />
    <category term="Loop工程" />
    <category term="Graph工程" />
    <category term="PDLC" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/08/13/why-pdlc-fits-three-paradigms.zh.html"><![CDATA[<blockquote>
  <p>三个概念分清楚之后，真正该问的是下一个问题：有没有一个真东西，是这三层同时长齐的？这一篇就拿我自己在做的 <a href="/zh/pdlc/">pdlc-skills</a> 来对一遍。先说结论——它跟三大工程契合，<strong>是个巧合</strong>。但巧合背后有原因，那个原因才是这篇想说的。</p>
</blockquote>

<p>上一篇我们把三个容易混在一起的词分开了：</p>

<ul>
  <li>提示词工程管的是这一次怎么跟模型说话；</li>
  <li>Loop 工程管的是一件事反复做到什么条件才停；</li>
  <li>Graph 工程管的是整条路上哪些步骤不许跳。</li>
</ul>

<p>它们不在同一层，不是三选一，而是可以叠起来的三层。</p>

<p>讲完之后，一个很自然的问题就冒出来了：这三层听着都有道理，可<strong>有没有一个真东西，是三层同时长齐的</strong>？</p>

<p>这一篇就拿一个来对——我自己在做的 pdlc-skills。</p>

<h2 id="pdlc-skills-是什么">pdlc-skills 是什么？</h2>

<p>它是一个 <a href="https://docs.anthropic.com/">Claude Code</a> 插件，干的事可以用一句话说完：<strong>把 AI 写代码从”聊出来的”变成”落在磁盘上的”。</strong></p>

<p>装上之后，你会多出 38 条斜杠命令，一条命令对应流程里的一个阶段。最常用的是 <code class="language-plaintext highlighter-rouge">/pdlc-feature</code>——开一个新功能就用它起手，然后 AI 会顺着 PRD → 设计 → TDD → 实现 → 评审 → 发布这条线往下走，每到一个阶段停下来交接一次。</p>

<p>它跟”让 AI 帮我写个功能”最大的区别有三条：</p>

<ul>
  <li><strong>产物必须落盘</strong>。PRD、设计、评审记录都是 <code class="language-plaintext highlighter-rouge">docs/</code> 下的真文件，不是聊天记录里的一段话。</li>
  <li><strong>每个功能有一份状态</strong>。走到哪一步、上一步过没过，都记在磁盘上。换个会话接着干，它知道自己在哪儿。</li>
  <li><strong>测试必须先红</strong>。没有失败的测试，不许进实现——这是硬门，不是建议。</li>
</ul>

<p>MIT 开源。它在 Claude Code 上功能最全，同一套方法论也能通过适配器在别的 AI 编程工具上跑。</p>

<h2 id="它的来路其实很普通">它的来路其实很普通</h2>

<p><strong>这三层不是我设计的，我做它的时候压根没听过”Loop 工程”“Graph 工程”这些词。</strong></p>

<p>它就是照着软件工程里那套现成的产品开发生命周期做的。需求、设计、测试、实现、评审、发布——这套流程在行业里躺了几十年，没有任何新东西。我做的只是把它翻译给 AI：每个阶段做什么、产出什么、什么条件才算过，写成一条条命令，让 AI 顺着流程走，而不是想到哪写到哪。</p>

<p>配套定了一条规矩：<strong>每个阶段都必须往磁盘上写东西</strong>。</p>

<ul>
  <li>PRD 落到 <code class="language-plaintext highlighter-rouge">docs/01_requirements/</code></li>
  <li>设计落到 <code class="language-plaintext highlighter-rouge">docs/02_design/</code></li>
  <li>评审记录落到 <code class="language-plaintext highlighter-rouge">docs/07_reviews/</code></li>
  <li>每个功能还有一份自己的状态文件</li>
</ul>

<p>都是普通文本，随时能打开看，也能 <code class="language-plaintext highlighter-rouge">git diff</code> 出这一轮 AI 到底动了什么。</p>

<p>做完之后，这几个新词流行起来了。我拿它们回头对了一遍，发现处处对得上——不是我照着做的，是它自己就长成了那样。</p>

<p>那就按提示词、Loop、Graph 的顺序，一层一层对过去。</p>

<h2 id="-提示词层对上的是规范只有一份">🔵 提示词层：对上的是”规范只有一份”</h2>

<p><strong>流程里对应的是什么。</strong> 软件工程有条老规矩：规范文档只能有一份，不能这里一套那里一套。放到 pdlc 上，就是那些所有命令都得守的规则——产物必须落盘、交接前必须自检、修复只做一次——不能在每个命令里各写一遍。</p>

<p><strong>具体怎么做的。</strong> 把公共规则抽成片段，在构建期展开进各个 skill。现在是 <strong>13 个片段，编译进 38 个 skill 里的 36 个</strong>（剩下两个不含的是 <code class="language-plaintext highlighter-rouge">pdlc-loop-next</code> 和 <code class="language-plaintext highlighter-rouge">pdlc-status</code>，它们太轻，没有可共享的规则）。那六条不变量——文件必须落盘、阶段必须落章（每完成一段就往历史里追加一条）、测试必须先红、自检必须执行、修复只做一次、状态必须推进（<code class="language-plaintext highlighter-rouge">current_stage</code> 得真的变，防止外层循环拿着滞后状态空转）——就写在其中一个片段里。改一处，全体生效。</p>

<p><img src="/assets/images/posts/why-pdlc-fits-three-paradigms-fig-fragments.png" alt="13 个共享片段在构建期展开进 36 个 skill，改一处规则全体生效" /></p>

<p><strong>对上了哪一层。</strong> 提示词工程管的是”这一次给模型看什么”。这里做的事其实没变，只是拿代码那套办法管它：抽公共、编译期展开、单一真源。</p>

<p>新词叫提示词工程，老规矩叫单一真源。两个说的是同一件事——<strong>这就是第一处巧合</strong>。</p>

<h2 id="-loop-层对上的是-tdd-的红灯门">🟢 Loop 层：对上的是 TDD 的红灯门</h2>

<p><strong>流程里对应的是什么。</strong> TDD 是软件工程里的老东西了，规矩就一条：先写会失败的测试，再写实现，直到测试变绿。pdlc 把它照搬了过来——实现之前必须先有红灯测试。</p>

<p><strong>它顺手带来了什么。</strong> 测试写在实现之前，意味着”做完了没有”这件事有了一个跟模型无关的答案：跑一遍，退出码是 0 就是过了，非 0 就是没过。</p>

<p>这个答案值钱的地方在于它不经过模型。让写代码的模型给自己的代码打分，它会系统性地往好里说——不是它想骗你，是它真觉得写得挺好。所以在 pdlc 里，模型的自检结果是单独记的，只当参考，<strong>永远不作为该不该停的依据</strong>。</p>

<p><strong>于是这一段就能交给循环了</strong>：<code class="language-plaintext highlighter-rouge">tdd → implement → review</code> 自己跑，跑到评审通过为止。前面的 PRD 和设计交不了——这个功能要不要做、拆成哪几个模块、边界划在哪，是判断题，没有任何一条命令能给出退出码。判断题留给人，这条线划得很清楚。</p>

<p><img src="/assets/images/posts/why-pdlc-fits-three-paradigms-fig-loopable.png" alt="流程里哪一段能交给循环、哪一段必须留人，判据写在旁边" /></p>

<p><strong>对上了哪一层。</strong> Loop 工程有句话可以当公理：一个循环 = 一个任务 + 一次校验，没有校验的任务只是许愿。而 TDD 给的正是校验。</p>

<p>我做 TDD 是因为软件工程要求先测后写，<strong>不是为了给循环准备靠尺</strong>。但那把靠尺就那么现成地放在那儿了——<strong>这是第二处巧合</strong>。</p>

<p>护栏、上限、预算这些怎么定，我留到第 5 篇专门讲。</p>

<h2 id="-graph-层对上的是阶段划分和评审闸门">🟣 Graph 层：对上的是阶段划分和评审闸门</h2>

<p><strong>流程里对应的是什么。</strong> 生命周期这个词本身就带着顺序：需求在设计前面，设计在实现前面，实现完了要评审，评审过了才发布。软件工程里这套顺序不是建议，是纪律——跳步的代价，行业已经交过几十年学费了。</p>

<p><strong>具体怎么做的。</strong> 每个阶段结束时把状态写进磁盘，下一个阶段先读状态，不满足前置条件就不许进。不许跳过测试直接实现，不许没评审就发布。</p>

<p>除了纵向的顺序，还有横向的一层：功能与功能之间的关系。<code class="language-plaintext highlighter-rouge">/pdlc-relate</code> 把这些关系显式记下来，<strong>六种有向边</strong>——扩展、依赖、取代、解决、冲突、相关。然后 <code class="language-plaintext highlighter-rouge">impact &lt;功能ID&gt;</code> 一条命令查改动的影响半径：🔴 直接依赖它的、🟡 隔了一层的、🟢 已经收尾不用管的。</p>

<p><img src="/assets/images/posts/why-pdlc-fits-three-paradigms-fig-two-directions.png" alt="纵向是一条带闸门的线性管道，横向才是真正的有向图" /></p>

<p><strong>这里得说句实话。</strong> 纵向那条严格来说不是图，是一条线性管道加几道闸门。它没有条件分支，没有并行节点，也不能从任意一点回滚——图该有的东西它基本没有。真正符合”图”的是横向那张依赖关系图：有方向、能遍历、能查出循环依赖、能检测指向不存在功能的悬空引用。</p>

<p><strong>对上了哪一层。</strong> 那纵向凭什么算 Graph 层？因为 <strong>Graph 层的本质不是”画成图”，是负向约束</strong>——宣布哪条路不许走。上一篇那个说法可以再搬一次：轨道不规划旅程，它只是让某些方向根本走不了。</p>

<p>阶段闸门约束的是顺序，依赖图约束的是影响范围，两者做的是同一件事。而”按阶段推进、关键节点评审”这条纪律，软件工程早就写在流程里了——<strong>这是第三处巧合</strong>。</p>

<p><img src="/assets/images/posts/why-pdlc-fits-three-paradigms-fig-relations.png" alt="六种有向边构成关系图，impact 按距离标出影响半径" /></p>

<h2 id="三次巧合就不是巧合了">三次巧合就不是巧合了</h2>

<p>一次对上可以说是碰巧，三次都对上，那就该问问为什么了。</p>

<p>我的答案是：<strong>这两套东西本来就在解决同一批问题。</strong></p>

<p>三大工程范式是这两年从 AI Agent 实践里总结出来的新词。而软件工程那套流程，是几十年里被真实项目反复教育出来的。人和 AI 干工程活时会犯的错，其实高度重合——想到哪写到哪、规范各写一套、说做完了但没验、改了这里不知道会炸哪里。老流程为治这些毛病立的规矩，翻译到 AI 身上依然有效。</p>

<p>所以新词描述的那些约束，老流程早就用另一套语言写过一遍：</p>

<table>
  <thead>
    <tr>
      <th>三大工程的说法</th>
      <th>软件工程里早有的规矩</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>提示词工程要统一上下文</td>
      <td>规范只能有一份，别到处抄</td>
    </tr>
    <tr>
      <td>Loop 工程要有客观校验</td>
      <td>先写测试再写实现</td>
    </tr>
    <tr>
      <td>Graph 工程要做负向约束</td>
      <td>按阶段推进，关键节点评审</td>
    </tr>
  </tbody>
</table>

<p><strong>这才是”天然契合”的意思</strong>：不是我把三层搭进去了，是这套老流程本来就长着这三层，只不过以前没人用这三个词叫它。</p>

<p>还有一处，是老流程里没有、但落地时自己冒出来的：<strong>三层最后共用了同一份东西——磁盘上的状态</strong>。</p>

<blockquote>
  <p>图靠它判断走到哪一步了，
循环靠它判断这一轮有没有推进，
提示词靠它拿到这一次该看的上下文。</p>
</blockquote>

<p><img src="/assets/images/posts/why-pdlc-fits-three-paradigms-fig-shared-state.png" alt="三层共用磁盘上同一份状态文件" /></p>

<p>这也给了一把现成的尺子：<strong>判断一个工具的三层是不是真结合，看它们共不共用一份状态。</strong> 共用才叫结合；各管各的，那只是三个机制装在了同一个仓库里。</p>

<h2 id="下一篇它们各自什么时候起作用">下一篇：它们各自什么时候起作用？</h2>

<p>如果这篇只留一句话，我希望是这句：<strong>想让 AI 干工程活，与其发明新框架，不如先把已有的工程流程用起来。</strong> 那套流程不新，但它治的毛病，AI 一样会犯。</p>

<p>知道了三层各自落在哪，下一个问题是：<strong>在一次真实的功能开发里，它们各自什么时候起作用？</strong> 下一篇跟着一个功能从头走到尾，把三层的时间线标出来，也说清楚它们之间是怎么交接的。</p>

<hr />

<p><strong>想自己试一下</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh <span class="se">\</span>
  | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>项目主页：<a href="/zh/pdlc/">kanfu-panda.github.io/pdlc</a> ｜ 源码：<a href="https://github.com/kanfu-panda/pdlc-skills">github.com/kanfu-panda/pdlc-skills</a></p>

<p>觉得有用的话，给个 star 是对我最大的支持 ⭐</p>

<hr />

<p>如果这篇让你对”三层怎么落地”有了具体的印象，点个赞或者加个关注；要是你身边也有人在琢磨怎么让 AI 按流程干活，转给他。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">什么是提示词工程、Loop 工程以及 Graph 工程？</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/08/09/prompt-loop-graph-engineering.zh.html" rel="alternate" type="text/html" title="什么是提示词工程、Loop 工程以及 Graph 工程？" />
    <published>2026-08-09T00:00:00+08:00</published>
    <updated>2026-08-09T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/08/09/prompt-loop-graph-engineering.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/prompt-loop-graph-engineering-cover.png" />
    <summary type="html">这三个词最近总被摆在一起比较，好像在让人三选一。但它们根本不在同一层——提示词管这一次怎么说话，Loop 管迭代怎么收敛，Graph 管路径怎么组织。搞清楚这件事，比学会其中任何一个都值钱。这篇把三层各自的本事和天花板讲清楚，最后给三个问题，帮你判断手上这件事该归哪一层管。</summary>
    <category term="AI工程" />
    <category term="提示词工程" />
    <category term="Loop工程" />
    <category term="Graph工程" />
    <category term="AIAgent" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/08/09/prompt-loop-graph-engineering.zh.html"><![CDATA[<blockquote>
  <p>提示词工程、Loop 工程、Graph 工程——这三个词最近总被摆在同一张表里比较，好像在让人三选一。但它们根本不在同一个层面上。搞清楚这件事，比学会其中任何一个都值钱。这一篇不聊具体工具，只把三层各自在管什么、天花板在哪讲清楚，最后给你三个问题，帮你判断手上这件事该归哪一层管。</p>
</blockquote>

<h2 id="三个词三条各自长出来的线">三个词，三条各自长出来的线</h2>

<p>先说说这三个词是怎么冒出来的，因为它们的来路完全不同。</p>

<p><strong>提示词工程</strong>这两年被重新命名了一次。早期讨论的是”怎么把一句话写好”——加什么角色设定、用什么口令。后来大家发现真正决定效果的不是措辞，是这一次到底给模型看了什么，于是有了 Context Engineering 这个更准的说法。</p>

<p><strong>Loop 工程</strong>是随着 Ralph 那类做法在社区流传起来的。做法简单得有点出乎意料：一行 <code class="language-plaintext highlighter-rouge">while</code> 循环，反复把同一段指令喂给模型，让测试来判对错，跑够多轮，能过验收的那一版自己就出来了。</p>

<p><strong>Graph 工程</strong>跟着 LangGraph 这类编排框架进入视野，主张把 Agent 的执行路径显式画成图——谁调谁、能不能回退、哪里必须停。</p>

<p>三条线是各自长出来的，最后被人摆进同一张表里做对比。问题恰恰出在这张表上——它们压根不是一类东西。</p>

<h2 id="-它们根本不在同一层">🧱 它们根本不在同一层</h2>

<p>正确的关系不是并列，是<strong>层叠</strong>：</p>

<table>
  <thead>
    <tr>
      <th>层</th>
      <th>关注什么</th>
      <th>控制粒度</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Graph 工程</td>
      <td>路径怎么组织</td>
      <td>阶段级</td>
    </tr>
    <tr>
      <td>Loop 工程</td>
      <td>迭代怎么收敛</td>
      <td>轮次级</td>
    </tr>
    <tr>
      <td>提示词工程</td>
      <td>单次怎么沟通</td>
      <td>Token 级</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-layers.png" alt="三层从上到下依次是 Graph、Loop、提示词，控制粒度从阶段级细到 Token 级" /></p>

<p>但这里要补一句，否则就会踩进另一个坑：<strong>分层关系成立，不等于每层都必须有。</strong></p>

<p>脑子里一旦装进”标准三层架构”，很容易见到任务就想套满三层。实际上大量跑得很好的系统只有一层或两层：</p>

<table>
  <thead>
    <tr>
      <th>形态</th>
      <th>什么时候是这样</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>只有提示词</td>
      <td>单次分类、抽取、改写——上任何编排都是负收益</td>
    </tr>
    <tr>
      <td>提示词 + Loop</td>
      <td>靠测试兜底反复迭代，路径根本没画过</td>
    </tr>
    <tr>
      <td>提示词 + Graph</td>
      <td>路径能全枚举，不需要自主迭代</td>
    </tr>
    <tr>
      <td>三层齐全</td>
      <td>生产级的研发工作流</td>
    </tr>
  </tbody>
</table>

<p>Anthropic 在《Building effective agents》里给的原则很实在：从最简单的方案开始，只在效果明显改善时才增加复杂度。</p>

<p>所以正确的心智模型不是”三层架构”，而是——<strong>三个可选的嵌套层，默认从最底层开始，往上加是有代价的。</strong></p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-combos.png" alt="四种常见形态：只有提示词、提示词加 Loop、提示词加 Graph、三层齐全，各对应一类典型任务" /></p>

<h2 id="-逐层看每一层到底在管什么">🔍 逐层看：每一层到底在管什么</h2>

<p>下面三段，每层讲四件事：是什么、怎么做、一个容易犯错的关键点、天花板在哪。</p>

<h3 id="提示词工程决定这一次模型看到什么">提示词工程：决定这一次模型看到什么</h3>

<p>它已经不只是”把话说漂亮”了。真正要管的是：这一次让模型看到哪些东西，又不让它看到哪些。</p>

<p>演进的原因很直接：任务一旦跨越多轮推理，光靠单句措辞就不够了，重点转向管理整个上下文状态——系统指令、工具定义、外部数据、历史消息等等。</p>

<p>具体到做法上，无非这么几种。系统提示要写在一个合适的高度：具体到能指导行为，又不至于把每种情况都写死——太细的规则一旦遇上没预料到的场景就会互相打架，太笼统又等于没说。工具描述要写清楚各自管什么，功能重叠的工具越多，模型越容易出错。输出格式提前约定好，省得后面拿正则去匹配。示例给一组多样化的，比堆一堆边界情况有用。</p>

<p><strong>一个容易犯错的关键点</strong>：上下文不是越长越好。塞得越满，中间那部分越容易被稀释掉——这是实打实会发生的衰减，不是”模型不够聪明”。注意力是有预算的，往哪儿花是个取舍问题，不是”给得越多越保险”。想清楚哪些东西这一次根本不必让模型看见，往往比想清楚该说什么更省事。</p>

<p><strong>天花板</strong>：它是软约束。模型未必完全听你指挥，而且你当场也未必能完全看出来。</p>

<p>这一层最大的优势和最大的问题其实是同一件事——改一句话就能见效，所以也就只能靠模型的意愿。</p>

<h3 id="loop-工程一个任务加一次校验">Loop 工程：一个任务，加一次校验</h3>

<p>这一层有句话可以当公理用：</p>

<blockquote>
  <p>A loop is a task with a check. A task without a check is just hope.<br />
一个循环 = 一个任务 + 一次校验。没有校验的任务，只是许愿。</p>
</blockquote>

<p>Ralph 的形态前面提过了，核心就那一行 <code class="language-plaintext highlighter-rouge">while</code>。但真正的设计不在循环里，<strong>在配套的文件里</strong>，一共三份，各管一段：一份规范，写清楚这个项目要做成什么样、什么不许做；一份进度清单，记录哪些做完了、下一步该做什么；还有一份每轮都原样喂进去的指令，告诉模型”先读前两份，再挑一件事做”。循环本身没什么技术含量，难的是把这三份文件写对。</p>

<p><strong>一个容易犯错的关键点</strong>：每轮上下文完全刷新，模型是失忆的——它压根不记得上一轮干了什么。这听起来像缺陷，其实是特性。失忆保证了每轮都从干净状态出发，不会把上一轮的错误理解一路带到后面。代价是所有跨轮次的记忆都得落到磁盘上，也就是那份进度清单——它不是给人看的备忘录，是模型下一轮唯一的记忆。</p>

<p><strong>循环信号从哪来</strong>：循环靠什么知道该停？需要有自动判定的信号——比如测试的退出码、编译结果、类型检查。只有一句”我看着挺好的”，不能算数，因为这句话没法做条件判断，必须要有明确的信号量。</p>

<p><strong>典型的翻车方式</strong>有两种，一种是空转（死循环）：模型每轮都觉得自己干了点什么，实际上什么也没推进，token一直在烧，却没有产出任何有用的结果。另一种是重复劳动：它没找到上一轮已经做好的东西，以为还没做，于是又做了一遍——就像装修师傅没看见昨天铺好的管子，把地刨开又铺一次。</p>

<p><strong>天花板</strong>：没有验证信号的循环，是在批量生产垃圾。而且它的失败最难发现——它一直在跑，但方向是错的。</p>

<h3 id="graph-工程不能混淆的图">Graph 工程：不能混淆的图</h3>

<p>这一层得先解决一个歧义。同样叫”图”，其实在说两个完全不同的内容：</p>

<ul>
  <li><strong>编排图</strong>：LangGraph 那一类，画的是流程该走哪条路</li>
  <li><strong>知识图</strong>：GraphRAG、代码关系图那一类，画的是实体之间是什么关系</li>
</ul>

<p>同名不同物。这一篇讲的是前者，编排图。后者会在这个系列的第 7 篇单独讲。</p>

<p>编排图干的事，是把”这活按什么顺序走”从脑子里搬到纸面上：谁接谁的活、哪一步能退回去重做、哪一步必须停下来等人点头。</p>

<p>常见的走法就五种，每种对应一类活：</p>

<ul>
  <li><strong>串行</strong>：一步做完接下一步。写代码就是这样，先出设计，再写测试，最后写实现。</li>
  <li><strong>路由</strong>：先看是什么类型，再决定交给谁。工单进来先分类，退款的走退款流程，报障的走报障流程。</li>
  <li><strong>并行</strong>：几件互不相干的事同时开工，最后汇总。同一份代码分头查安全、查性能、查可读性，查完合并意见。</li>
  <li><strong>编排者带执行者</strong>：一个负责拆活分活，一群负责埋头干。</li>
  <li><strong>评估-优化</strong>：干完再过一道评审，不合格打回去重做。</li>
</ul>

<p>真实系统基本都是这几种拼起来的，不用自己发明新花样。</p>

<p>画图还有两个常被忽略的好处：一是<strong>可恢复</strong>——图上每个节点都能存档，跑挂了从最近的存档点续，不用从头再来；二是<strong>可审计</strong>——事后能说清楚”当时为什么走了这条路”，这在需要审计的场景很关键。</p>

<p><strong>一个容易犯错的关键点</strong>：它规定的是”不许走哪条路”，不是”该怎么做”。这条很重要，后面会单独说。</p>

<p><strong>天花板</strong>：它只能表达你事先想到过的东西。需要现场判断的、之前没见过的情况，图一律无能为力。还有个隐性成本——图画好之后就变成了负担，现实一变就得改图，改图比改一句提示词成本高得多，所以事先要规划好。</p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-cheatsheet.png" alt="每层一句定义、一个代表做法、一句天花板的速览卡" /></p>

<h2 id="-用装修来类比">🏠 用装修来类比</h2>

<p>下面我们用装修做下类比：</p>

<table>
  <thead>
    <tr>
      <th>概念</th>
      <th>装修里对应什么</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>提示词工程</td>
      <td>你怎么跟师傅交代要做什么</td>
    </tr>
    <tr>
      <td>Loop 工程</td>
      <td>干完 → 验收 → 不合格 → 返工 → 再验收</td>
    </tr>
    <tr>
      <td>验证信号</td>
      <td>监理的靠尺、水平仪、闭水试验</td>
    </tr>
    <tr>
      <td>Graph 工程</td>
      <td>施工顺序规范（水电 → 防水 → 闭水 → 贴砖 → 木工 → 油漆）</td>
    </tr>
    <tr>
      <td>人工介入点</td>
      <td>关键节点业主到场签字</td>
    </tr>
    <tr>
      <td>每轮上下文刷新</td>
      <td>每天早上换一个新的装修师傅</td>
    </tr>
  </tbody>
</table>

<p>三句话说明白各自的特点：</p>

<p><strong>提示词是交代。</strong>“主卧朝北那面墙，米白色，刷两遍，边角贴美纹纸。”成本为零，立刻见效，但师傅可能觉得差不多就可以了，而你当场看不出来问题。</p>

<p><strong>Loop 是返工机制。</strong> 光交代没用，得有验收。这里的关键不是”返工”两个字，是靠尺——没有靠尺的返工，只是重复施工。</p>

<p><strong>Graph 是施工顺序。</strong> 它不告诉你墙该刷什么颜色，它只让”不做防水直接贴砖”这件事在流程上不可能发生。</p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-renovation.png" alt="装修流程与三层的对照：交代活、返工验收、施工顺序规范" /></p>

<p>一句话记住三者的分工：<strong>顺序管住不许干什么，返工逼着干到合格，交代决定具体怎么干。</strong></p>

<h2 id="️-三种脾气三种翻车方式">⚖️ 三种脾气，三种翻车方式</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>提示词工程</th>
      <th>Loop 工程</th>
      <th>Graph 工程</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>控制权归谁</td>
      <td>模型</td>
      <td>模型（人只定终止条件）</td>
      <td>人（人画路径，模型填空）</td>
    </tr>
    <tr>
      <td>确定性</td>
      <td>低</td>
      <td>最低</td>
      <td>高</td>
    </tr>
    <tr>
      <td>前期投入</td>
      <td>极低</td>
      <td>低，但要先建验证体系</td>
      <td>高</td>
    </tr>
    <tr>
      <td>单位成本</td>
      <td>1x</td>
      <td>4x，多 Agent 15x</td>
      <td>2–3x，但少走弯路</td>
    </tr>
    <tr>
      <td>可调试性</td>
      <td>好</td>
      <td>最差</td>
      <td>最好</td>
    </tr>
    <tr>
      <td>典型失败</td>
      <td>模型不听</td>
      <td>跑偏且没人发现</td>
      <td>图画错了，或者太僵硬</td>
    </tr>
  </tbody>
</table>

<p><em>成本那一行说明一下：Agent 约 4x、多 Agent 约 15x 出自 Anthropic 的公开说明，Graph 的 2–3x 是公开资料里流传的粗略估计。这些是量级参考，不是实测值。</em></p>

<p>这张表里最值得琢磨的是最后一行。<strong>三种失败方式完全不同</strong>：提示词的失败你看得见——它没照做，摆在那儿；Loop 的失败你看不见——它一直在跑，只是方向错了；Graph 的失败你改不动——图定死了，现实变了。</p>

<p>后两种比第一种难对付得多，而它们恰恰是往上加层之后才会出现的新问题。这也是”往上加是要付代价的”这句话的具体含义。</p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-compare.png" alt="三层在控制权、确定性、成本、可调试性和典型失败上的对比" /></p>

<h2 id="-两个容易犯错的地方">🚧 两个容易犯错的地方</h2>

<h3 id="graph-不做规划它做的是负向约束">Graph 不做规划，它做的是负向约束</h3>

<p>很容易把三层理解成一条指挥链：Graph 规划、Loop 执行、Prompt 实现。但图里一个字都没说”该怎么做”，它只宣布”不许跳过测试直接写实现”。至于这个设计该拆成哪几个模块、实现该先写哪个文件——那些才是真正的规划，而它们发生在下面两层。</p>

<p>换个说法可能更好懂：</p>

<blockquote>
  <p><strong>轨道</strong>说了算的是能去哪、哪儿必须停，但它不管你这趟出门要办什么事。<br />
<strong>引擎</strong>只负责往前推，一直推到终点，或者撞上停车信号。<br />
<strong>方向盘</strong>在轨道给的余地里，决定具体怎么走。</p>
</blockquote>

<p>轨道从不替你规划行程，它只是让”往旁边开”这件事根本做不到。</p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-track.png" alt="轨道限定方向、引擎提供动力、方向盘决定走法的示意" /></p>

<h3 id="层级越高能力反而越弱">层级越高，能力反而越弱</h3>

<p>顺着”顶层、底层”的说法，很容易觉得上层更重要。实际恰好相反——<strong>控制力越强，表达力越弱</strong>。Graph 只能表达你事先想到过的东西，一旦现实超出图的想象，它帮不上任何忙。</p>

<p>再往深一层说：这三层没有任何一层在”实现”什么。真正干活的从来是模型本身，三层只是塑造模型行为的三种手段——Graph 用结构，Loop 用重复和验证，提示词用语言。</p>

<p>这就意味着，模型变强，上面两层的必要性会缩水。这套东西的价值，某种程度上是在补模型当前的短板。</p>

<h2 id="-选型三问">✅ 选型三问</h2>

<p>如果这篇你只带走一样东西，我希望通过下面这三个问题，帮助你做选型：</p>

<p><strong>第一问：错了，机器能自动发现吗？</strong></p>

<p>能 → 上 Loop。测试、编译器、类型系统都可以当靠尺。<br />
不能 → 别上，无论它看起来多诱人。没有验证信号的循环是在批量生产垃圾。</p>

<p>判断标准很朴素：把”做对了”这件事，能不能写成一条跑完会给你退出码的命令？写得出来就能循环，写不出来就别循环。</p>

<p><strong>第二问：我现在能把流程图画出来吗？</strong></p>

<p>画得出来，而且这套流程还要反复用、要能查账、要留几个人工点头的地方 → 上 Graph。<br />
画不出来 → 别硬画。</p>

<p>这里说的”画不出来”，指的是那种连要走几步都说不准的活：可能三步搞定，也可能来回折腾二十步，全看中间遇上什么。这种活本来就该让模型自己边走边判断，硬画一张图只会把它框死。Anthropic 给的建议也是这个意思——步数没法预估的开放问题，交给 Agent 自己跑，别拿固定流程去套。</p>

<p>还要注意，问的是”现在能不能画”，不是”以后能不能画”。想清楚了再画，图才管得住事；边跑边补的图，只是把混乱换个形式记下来而已。</p>

<p><strong>第三问：上面两个都不需要？</strong></p>

<p>那就用提示词工程。<strong>这不是妥协，这是正解。</strong> 一个单次的分类任务，套上循环和编排，只会让它变慢、变贵、变得更难查错。</p>

<p><img src="/assets/images/posts/prompt-loop-graph-engineering-fig-decision.png" alt="选型三问的决策树：能否自动发现错误、能否画出流程图、是否两者都不需要" /></p>

<h2 id="下一篇有没有一个真东西三层是同时长出来的">下一篇：有没有一个真东西，三层是同时长出来的？</h2>

<p>概念到这儿就讲完了。最后留一句话，它会一直贯穿这个系列。</p>

<p>提示词这一层只能”要求”，给不了”保证”。你写一句”省着点花 Token”，模型多半会照做，但它真超了，你也拦不住——因为这句话没有任何强制力。</p>

<p>想要保证，就得换一层手段。比如设一条硬线：这一趟花超多少就自动停。这条线不跟模型讲道理，也不管它愿不愿意，到了就停。</p>

<blockquote>
  <p><strong>所以：你要的如果是”保证”，提示词这一层给不了，得往上挪一层。</strong></p>
</blockquote>

<p>下一篇聊一个具体的东西——我自己在做的 <a href="/zh/pdlc/">pdlc-skills</a>，为什么它天然落在这三层上。</p>

<hr />

<p><strong>想自己试一下</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh <span class="se">\</span>
  | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>项目主页：<a href="/zh/pdlc/">kanfu-panda.github.io/pdlc</a> ｜ 源码：<a href="https://github.com/kanfu-panda/pdlc-skills">github.com/kanfu-panda/pdlc-skills</a></p>

<p>觉得有用的话，给个 star 是对我最大的支持 ⭐</p>

<hr />

<p>如果这篇帮你把三个概念捋清了，点个赞或者加个关注；</p>

<p>要是身边也有人正被这几个概念困扰，转发给他。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">我把 AI 记忆体检做成了工具：只诊断，绝不替你动手</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/07/13/ai-memory-health-check.zh.html" rel="alternate" type="text/html" title="我把 AI 记忆体检做成了工具：只诊断，绝不替你动手" />
    <published>2026-07-13T00:00:00+08:00</published>
    <updated>2026-07-13T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/07/13/ai-memory-health-check.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/ai-memory-health-check-cover.png" />
    <summary type="html">上一篇手动给 AI 记忆库除草，除到最后我想明白：草反复长，是因为记忆系统缺了自我体检的机制。所以这一次，我把那个“记忆体检”真做成了一个工具。它像个医生，扫一遍、把毛病列给你看，然后停下——改不改，永远由你决定。它不外接任何大模型，你的记忆一个字都不出本机。</summary>
    <category term="AI记忆" />
    <category term="ClaudeCode" />
    <category term="记忆维护" />
    <category term="工具开发" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/07/13/ai-memory-health-check.zh.html"><![CDATA[<blockquote>
  <p>上一篇，我给几个项目的 AI 记忆库做了一次系统的“除草”，也归纳出记忆衰退的六种“杂草”。可越除到后面我越确定一件事：手动除草只能治标而不能治本——只要记忆系统本身缺了自我体检的机制，“草”拔完了，但过一阵子照样长。所以那篇结尾我给自己留了个作业：做一个会“自动体检”的记忆工具。这一篇，就是来交作业的。</p>
</blockquote>

<p>先来讲讲这工具到底是干嘛的。</p>

<p>你可以把它想象成记忆库的专属医生。你把它指向某个项目的记忆库，它就从头到尾扫一遍，把发现的问题一条条摆在你面前：这条链接断了、那份记忆放错了目录、这两条内容看着自相矛盾……然后停在这儿，等你来决定每一条怎么办。</p>

<p>别小看这句“等你来决定”，它定了整个工具的性格：只负责看、负责说，就是不动手。</p>

<h2 id="-它只负责看不替你动手">🩺 它只负责“看”，不替你动手</h2>

<p>为什么要这么设计？这得回到上一篇那个最痛的教训：删记忆这种事，绝不能全权交给 AI。它可以提议哪条该删、哪条该合并，但最后拍板的必须是人。被删掉的往往是历史记录，AI 一顺手，很容易把要紧的一并抹掉。</p>

<p>做工具的时候，我把这条教训写成了一条死规矩：整个程序里，没有任何一条路径会自己去改、去删你的记忆文件。它能扫描、能给每条问题标出轻重、能把改法都替你想好摆在那儿，但真正“改”或“删”的那一下，得你自己按。</p>

<p>这么一讲好像有点亏——造了个工具，却把它最能干的那截给砍了。可恰恰是这份克制，让我敢天天开着它用。要是换成一个会自作主张动我记忆库的工具，我一次都不敢碰。</p>

<p><img src="/assets/images/posts/ai-memory-health-check-fig-diagnose-not-operate.png" alt="工具只管扫描、分级、给建议；改和删的那一下，永远你自己按" /></p>

<h2 id="-机器看硬伤ai-读语义">🔍 机器看“硬伤”，AI 读“语义”</h2>

<p>说完它不动手，再说说它到底能看出什么。工具扫出来的问题，其实分两种，处理方式大不相同。</p>

<p>一种是机器一眼就能判的硬伤：链接指向的文件根本不存在、一份记忆躺错了目录、命名不规范……这些不用理解内容，规则一比对就知道，而且不会看走眼。</p>

<p>另一种要难缠得多。比如“这两条记忆是不是在自相矛盾”，光看字面判断不了，它得真的读懂两条记忆各自在说什么。这种活，绕不开一个能理解语言的大模型。好在这工具本来就是 Claude Code 的插件，运行的时候旁边就坐着一个 Claude——现成的、能读懂意思的模型，用不着再往外接。</p>

<p>于是硬伤交给一个本地小引擎——它不联网、不调任何模型，同一个记忆库每次都给出一模一样的结果；要读懂意思的判断，交给宿主 Claude，也就是正陪你干活的这个模型。这么分下来，工具装起来不需要任何 API key，不依赖任何外部服务，你的记忆内容也一个字都不会往外发。</p>

<p><img src="/assets/images/posts/ai-memory-health-check-fig-engine-shell.png" alt="机器能判的硬伤交给本地引擎，要读懂意思的判断交给宿主 Claude，两层各管一段" /></p>

<p>顺着这个思路，我把检查铺成三层，从最确定的到最需要动脑的：</p>

<ol>
  <li><strong>静态检查（硬伤）</strong>：引擎的主力，判得又快又准——死链（命名从连字符漂成下划线，链接就静默断了）、悬空索引（目录里记着、文件却没了）、孤儿记忆（文件在、目录里却没登记）、缺字段、命名不规范、放错目录。都是机器能一眼识别、不会误判的硬伤。</li>
  <li><strong>连蒙带猜（线索）</strong>：比如一条记忆写着“开发中”“审批中”，又很久没更新，多半是事早办完了、状态却还冻在那儿。这层给的是线索，算不上定论。</li>
  <li><strong>读懂意思（语义）</strong>：上一篇里最难缠的两种“草”——项目里重复抄了一遍全局规则（我叫它幽灵副本）、看着相反其实各管一个场景的“伪矛盾”——都得读进内容才判得出来。这层交给宿主 Claude，它读完给建议，我来拍板。</li>
</ol>

<p>三层扫完，工具按红黄绿把问题分好轻重，一条条陪你过：这条断链改吗？这条孤儿补进目录吗？这条冻住的状态是不是早结束了？你说改它才改，你说跳过它绝不碰。</p>

<h2 id="-让它到点提醒但别来烦我">⏰ 让它到点提醒，但别来烦我</h2>

<p>光有体检还不够，因为维护记忆最大的敌人不是“不会修”，是压根忘了修。上一篇我说记忆得定期维护，可“定期”全靠自觉，基本等于没有。</p>

<p>所以我给工具加了个提醒：每次开始干活，如果当前项目的记忆库太久没体检，它就轻轻提一句“🩺 已 N 天未体检”。</p>

<p>但提醒这东西，多了就是噪音，一成噪音就被无视。于是我给它上了两道约束：一天最多冒一次头，不会每开一次会话都跳出来；真要嫌烦，一条命令就能先把它关掉。</p>

<p><img src="/assets/images/posts/ai-memory-health-check-fig-reminder.png" alt="体检提醒一天只在限时窗口冒一次头，嫌烦一条命令就能关掉" /></p>

<h2 id="-现在它能干什么不能干什么">📌 现在它能干什么，不能干什么</h2>

<p>说点实在的。</p>

<p><strong>能干的</strong>：六个检测器、旧格式记忆的迁移建议、中英双语的输出，现在都跑通了。我拿它把自己九个项目的记忆库挨个体检了一遍，断链、过时命名、冻住的状态，一次清干净。</p>

<p><strong>不能干的</strong>（也是我故意画的界）：</p>

<ul>
  <li>要读懂意思的判断，最后还得人点头，工具只出主意；</li>
  <li>它绝不会自动替你改任何东西；</li>
  <li>对小库、新库，体检收益有限，真正值回票价的是那些攒了很久的大库。</li>
</ul>

<p>一个管记忆的工具，最不该做的就是自作主张。</p>

<p>工具本身现在还是我自己在用，但从第一行代码起就是照开源标准写的：测试齐、历史干净、中英文文档都有。什么时候公开、怎么公开，等我自己用踏实了再说，所以这篇先不放链接，免得你点进去扑个空。</p>

<h2 id="-回头看三点想法">💡 回头看，三点想法</h2>

<ol>
  <li><strong>给一个会“动手”的工具，先想清楚它的边界在哪。</strong> 越是不可逆的操作，工具越能干就越危险。把“绝不自动动手”定成死规矩，反倒让我敢放心用它。</li>
  <li><strong>做提醒、通知这类功能，别忘了留一个“让人关掉它”的开关。</strong> 我们总惦记着怎么把消息推到用户眼前，却容易忘了：被反复打扰的提醒，最后都会被无视。能被关掉的提醒，才有人真看。</li>
  <li><strong>自己造的工具，自己先当那个较真的用户。</strong> 别象征性跑一下就算，真拿它去干活，那些误判、漏判才会一个个冒出来。</li>
</ol>

<h2 id="下一篇聊聊-loop">下一篇：聊聊 loop</h2>

<p>关于记忆，从搭建、除草到这次的体检工具，就聊到这儿。下一篇换个话题——聊聊 loop：怎么让 AI 按固定节奏自己跑起来，把一件重复的事持续做下去，而不用我每次守着。</p>

<hr />

<p>如果你也在给 AI 攒记忆库，或者正想给自己的工具立几条“绝不越界”的规矩，希望这篇能给你搭把手。觉得有用的话，点个赞、点个“在看”，或者转给同样在折腾 AI 记忆的朋友——你的每一次转发，都是我写下去的动力。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 的记忆会长草，及时清理很重要</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/07/08/ai-memory-grows-weeds.zh.html" rel="alternate" type="text/html" title="AI 的记忆会长草，及时清理很重要" />
    <published>2026-07-08T00:00:00+08:00</published>
    <updated>2026-07-08T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/07/08/ai-memory-grows-weeds.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/ai-memory-grows-weeds-cover.png" />
    <summary type="html">上一篇聊了怎么给 AI 构建记忆体系，但构建只是开始——项目推进得越久，记忆越积越多，若不加清理，其中就会悄悄长出“杂草”。这一次，我给自己几个项目的 AI 记忆做了一次系统的“除草”，并归纳出记忆衰退的六种典型情形。最危险的一种，会让 AI 拿着过期信息、理直气壮地办错事。</summary>
    <category term="AI记忆" />
    <category term="ClaudeCode" />
    <category term="记忆维护" />
    <category term="上下文工程" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/07/08/ai-memory-grows-weeds.zh.html"><![CDATA[<blockquote>
  <p>上一篇我们聊了怎么给 AI 构建一套记忆体系。但构建只是开始——项目推进得越久，AI 的记忆越积越多，若不加清理，其中就会悄悄长出“杂草”。这一次，我给自己几个项目的 AI 记忆做了一次系统的“除草”。</p>
</blockquote>

<p>随着项目推进，AI 协作过程中的记忆不断累积。每个项目的记忆都像一个专属的笔记本，AI 在工作中随手记下踩过的坑、定过的规矩。这固然能加深它对项目的理解，但笔记本越记越厚，内容也越来越杂——其中不少条目早已过期，AI 却仍把它们当作必须执行的规范。</p>

<p>举一个真实的例子。某个项目里，AI 一上手就照着一条旧记忆行事。那条记忆写着“某个外部调用已经配置就绪，可直接使用”，于是它跳过了本该重新确认的一步，直接往下执行，结果撞了墙。问题在于，那条记忆只是几周前的快照，早已失效；而 AI 从头到尾没有对它产生半点怀疑。</p>

<p>这件事让我意识到：记忆一旦记错，有时比没有记忆更糟。没有记忆，AI 至少会老老实实先查证一遍；记错了，它反而会理直气壮地一路走偏。记忆长出来之后会变旧、变乱、自相矛盾，这些都得有人及时收拾，AI 的执行效率才谈得上可靠。</p>

<p><img src="/assets/images/posts/ai-memory-grows-weeds-fig-stale-danger.png" alt="旧记忆让 AI 拿着过期信息笃定跑偏，而空白至少会让它回头查一下" /></p>

<h2 id="我要做两件事">我要做两件事</h2>

<p>一是认真清理各项目的记忆库，二是借此盘点一下，看看记忆究竟会滋生出哪些“杂草”。</p>

<p>这两件事都没有捷径，必须人工上手——尤其是删除，我绝不敢完全交给 AI。它可以提议哪些该删、哪些该合并，但最终拍板必须是人。删掉的往往是历史记录，AI 一旦顺手，很容易把重要内容一并抹掉，清理记忆必须格外谨慎。</p>

<p>不过要先说清楚：手动除草只能治标。杂草之所以反复滋生，是因为记忆系统本身缺了某些机制——今天拔干净，过阵子照样再长。</p>

<h2 id="先给-ai-补上它缺的那个睡眠">先给 AI 补上它缺的那个“睡眠”</h2>

<p>要理解记忆为什么需要清理，不妨先看看人是怎么做的——靠睡眠。</p>

<p>这背后有明确的科学依据。人在睡眠中，尤其是深度睡眠阶段，大脑会“回放”白天的经历，把重要的记忆逐步从海马体转移到大脑皮层归档；越重要的内容回放越频繁，记得也越牢。与此同时，睡眠还会主动“修剪”那些不重要、已过时的记忆，避免大脑被无用信息塞满。</p>

<p>AI 没有这样一个自动整理的过程，它只会把记忆一条条往下堆，从不回头重排。所以这次，我手动替它补上了这一“觉”：把每个项目的记忆通读一遍，逐条排查，然后做五件事——合并重复的、剪掉过期的、把零散的沉淀成一条、给相关的建立关联，以及最容易被忽略的一件：排查互相矛盾的地方。</p>

<p><img src="/assets/images/posts/ai-memory-grows-weeds-fig-sleep.png" alt="人在睡眠里回放、重排白天的记忆；AI 缺这一步，只会一味往下堆" /></p>

<h2 id="六种典型的记忆杂草">六种典型的记忆“杂草”</h2>

<p>在系统梳理多个项目的记忆库后，我发现不同项目滋生的“杂草”虽形态各异，本质上可以归纳为六种典型情形。</p>

<p><strong>其一，幽灵副本。</strong> 全局规则在某个项目里被重复记录，但 AI 缺乏“继承”机制。当全局规则更新时，这些孤立的副本无法同步，导致过时的指令继续生效。</p>

<p><strong>其二，状态冻结。</strong> 部分记忆记录的是某一时刻的进行时状态（如开发中、审批中）。由于缺乏状态更新机制，即使任务早已完结，AI 仍会将其视为未决事项，在无关对话中产生无效提醒。</p>

<p><strong>其三，过时快照。</strong> 这是最危险的一种。过期的旧记忆不仅失去参考价值，还会误导 AI 把错误信息当作事实依据，理直气壮地执行错误操作。</p>

<p><strong>其四，上下文割裂。</strong> 部分记忆单独阅读时均准确无误，但必须结合特定语境才能得出正确结论。由于关联注释散落在其他文件中，AI 检索时极易遗漏，导致断章取义。</p>

<p><strong>其五，结论与推导脱节。</strong> 当底层数据因 Bug 修复而变更时，旧结论只是被新结论覆盖，未被显式标记作废。由于记忆系统只存结论、不留推导过程，它无法自动识别并清理那些随之失效的关联结论。</p>

<p><strong>其六，结构性错乱。</strong> 包括记忆被误存至错误目录，以及因命名规范不统一（如连字符与下划线混用）而导致的静默断链。</p>

<p>复盘这六种情形，可以得出一个反常识的结论：这些“杂草”绝大多数并非源于当初记错，而是“记录正确、却疏于维护”才逐渐腐坏的。记忆系统最大的隐患，不是“记错”，而是“记完即弃”。</p>

<p><img src="/assets/images/posts/ai-memory-grows-weeds-fig-weeds.png" alt="反复长出的六种草：幽灵副本、冻住的进行时、危险的旧快照、要搭着读的矛盾、被盖住的旧结论、断链与错位" /></p>

<h2 id="有一步我特意保持了克制">有一步，我特意保持了克制</h2>

<p>清理过程中，有一步我刻意保持了克制。</p>

<p>我原本打算把某项目里三条关于“云端数据陷阱”的相似记忆合并成一条清单。但操作到一半我意识到，这三条分别对应三个具体的业务风险——重复发放奖励、榜单数据缺失、新用户无法入库。若把它们过度抽象成一句“注意云端字段”，这些关键的避坑细节就会被一并抹去。</p>

<p>沉淀知识固然重要，但过度沉淀会抹杀特例。因此我调整了做法：三条原始记忆全部保留，仅通过标签建立关联，让它们“聚为一簇”，而非“融为一锅”。</p>

<h2 id="最难的一环排查看着相反的记忆">最难的一环：排查“看着相反”的记忆</h2>

<p>整个清理中最具挑战的一环，是排查逻辑冲突。</p>

<p>举个例子：关于某接口的数据获取，一条记忆记为“返回真实数据”，另一条记为“返回匿名数据”。经核实，两者都正确，只是触发场景不同——自动获取时返回匿名数据，用户手动点击按钮时才返回真实数据。</p>

<p>对这类看似矛盾的记忆，简单删除只会造成信息丢失；正确的做法，是明确界定各自的适用场景。在记忆中补上这个“分支条件”，冲突便迎刃而解。</p>

<p>由此也引出本次复盘的一个核心结论：记忆系统里最危险的，并非“信息不一致”，而是“信息不一致、却缺乏场景界定”。</p>

<p><img src="/assets/images/posts/ai-memory-grows-weeds-fig-contradiction.png" alt="看着相反的两条记忆，多半不是谁错了，而是各管一个场景，把岔口写清就不打架" /></p>

<h2 id="为什么手动清理除不尽">为什么手动清理除不尽</h2>

<p>清理到后期我逐渐明白：仅靠人工除草，无法从根本上解决问题。这些“杂草”之所以反复滋生，本质上是系统缺失了三项核心机制——缺乏“继承”，全局规则被重复冗余地记录；缺乏“过期”机制，状态型记忆被永久冻结；缺乏“归属与一致性校验”，导致记忆存储错位、链接静默断裂。</p>

<h2 id="这次清理到底值不值">这次清理，到底值不值</h2>

<p>就投入产出比而言：对小型或新建的记忆库，清理收益有限；但对大型历史库，价值相当可观，能排除大量断链、命名过时、状态冻结等隐患。</p>

<p>不过，这次复盘真正的收获，并不是短期的“环境整洁”，而是建立起了对记忆衰退模式的系统性认知——这种认知，具有长期价值。</p>

<h2 id="什么样的记忆最不容易长草">什么样的记忆最不容易长草</h2>

<p>在排查中我还发现，不易衰退的记忆往往有一个共同点：它们记录的是“已确认的决策与原则”（如决策背景、执行路径、底线约束），而非“进行中的状态”。结论型记忆具备跨时间的稳定性，状态型记忆则极易随时间失效。</p>

<p>因此，写入记忆时不妨遵循一条原则：优先记录“不会过期的结论”，尽量避免记录“会过期的状态”。</p>

<h2 id="三条可落地的经验">三条可落地的经验</h2>

<p><strong>一、记忆需要周期性维护。</strong> 记忆库具有自然衰退的属性，应定期进行结构化重组——合并、剔除、沉淀、关联、排查冲突——避免其不断劣化。</p>

<p><strong>二、高风险操作必须人工介入。</strong> 对删除、沉淀这类不可逆操作，务必人工审核；并优先清理过期与自相矛盾的记忆，防止错误信息误导系统。</p>

<p><strong>三、治本要依靠系统机制。</strong> 人工清理只是权宜之计，根本解法是建立自动的继承、过期淘汰与一致性校验机制，让记忆库实现自我新陈代谢。</p>

<h2 id="下一步一个会自动体检的记忆工具">下一步：一个会“自动体检”的记忆工具</h2>

<p>基于这次复盘，下一步的方向已经清晰：做一个具备“自动体检”能力的记忆管理工具。它需要实现三项核心功能——定期触发整理提醒、对记忆进行分层扫描与诊断、把发现的问题结构化地呈现出来，交由人工决策。而在最终处置上，系统只负责给出建议，删除与合并的决定权，始终保留在人手中。</p>

<p>这一构想并非凭空而来，而是完整梳理之后近乎必然的推论。下一步，我打算把它真正落地。</p>

<p><img src="/assets/images/posts/ai-memory-grows-weeds-fig-plugin.png" alt="想要的那个记忆体检工具：定时提醒、分层体检、把问题摆出来、人来拍板，系统绝不自动删除" /></p>

<hr />

<p>如果你也在为 AI 构建记忆库，请务必警惕记忆的自然衰退与“杂草”的滋生。这篇复盘若能促使你回头审视一下自己的记忆库，欢迎关注、点赞，或分享给同样在探索 AI 记忆管理的同行。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">如何通过增强 AI 的记忆体系，让它变得更懂你！</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/07/06/ai-memory-that-gets-you.zh.html" rel="alternate" type="text/html" title="如何通过增强 AI 的记忆体系，让它变得更懂你！" />
    <published>2026-07-06T00:00:00+08:00</published>
    <updated>2026-07-06T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/07/06/ai-memory-that-gets-you.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/ai-memory-that-gets-you-cover.png" />
    <summary type="html">我已经不止一次，在同一个地方被同一个 AI 绊倒——反复强调过的语法它还是写错，定好的规范它转头就忘。不是它笨，是它压根没记性。这篇借人脑聊 AI 的记忆：为什么好记忆是结构不是堆积、改错为什么要覆盖而不是往后堆、以及最反鸡汤的一条——就算记住了，它也未必照做，那该怎么办。用好这套东西，一个普通模型也能被你调教得很懂你。</summary>
    <category term="AI记忆" />
    <category term="ClaudeCode" />
    <category term="上下文工程" />
    <category term="AI编程" />
    <category term="效率" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/07/06/ai-memory-that-gets-you.zh.html"><![CDATA[<p>我已经不止一次，在同一个地方被同一个 AI 绊倒。</p>

<p>比如前端项目里的 Ant Design 6，我反复强调过要用新版语法，甚至让它每次动手前先用 context7 去查一遍规范。它答应得好好的，转头还是写出一堆早就废弃的旧写法。发布流程也一样——我明明定好了规范，它过一会儿就忘，自作主张按它臆想的方式来。</p>

<p>一次两次是手滑，次数多了我就想弄明白：不是它笨，是它压根没记性。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-amnesia.png" alt="场景：一个每天上班就失忆的新员工，工位上贴满了便签" />
<em>（这张图帮助理解：AI 每开一次对话，都像一个当天上班、下班就失忆的新人。）</em></p>

<h2 id="我要的不是更强的模型">我要的不是更强的模型</h2>

<p>一开始我也纳闷：这么强的模型，怎么连”上次说过的话”都留不住？为了搞明白，我开始盯着它——看它回答问题、干活的时候到底怎么运转，会在哪一步掉链子。</p>

<p>盯久了我想通一件事，也是这篇想说的：我要的不是换一个更强的模型，而是让手上这个 AI，在我的项目里越用越懂我。靠的就是”记忆”。</p>

<p>先把话说到前头，免得听着像卖药。记忆提不高模型的智商，它只提高一件事——这个 AI 在你这儿有多顺手。而且后面你会看到，就算它记住了，也未必照做。天花板先摆这儿，咱们再往下走。</p>

<h2 id="从人脑出发看-ai-的记忆该补哪些课">从人脑出发，看 AI 的记忆该补哪些课</h2>

<p>这篇我想借人脑来讲。AI 的记忆，说到底就是在笨拙地模仿人是怎么记、怎么忘的。看懂了人脑那套，就知道 AI 该往哪几个方向补。</p>

<h3 id="一ai-每天上班都是新人">一、AI 每天上班都是”新人”</h3>

<p>先接受一个反直觉的事实：大模型本身是没有记忆的。你每开一次对话，它唯一”知道”的，就是这一次你塞给它看的那些字，此外一片空白。上一轮聊过什么、昨天定过什么规矩，它一概不记得。</p>

<p>打个比方。它这一次能看到的所有内容，像一张办公桌面——你把资料摊上去，它就能用；一旦这次对话结束，桌面唰地清空，什么都不留。</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>好比</th>
      <th>是什么</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>上下文窗口</td>
      <td>办公桌面</td>
      <td>这一次它眼前摊开的东西，对话一结束就清空</td>
    </tr>
    <tr>
      <td>记忆</td>
      <td>抽屉、档案柜</td>
      <td>存在外面，下次要用时再拿出来、重新摆到桌上</td>
    </tr>
  </tbody>
</table>

<p>有人会说，那把桌面做大不就行了？现在不都在卷”超长上下文”吗？可桌子再大也还是桌子——关机就空，它不是记忆。更麻烦的是，桌上堆满了不相干的纸，它反而容易看花眼、答得更差。这几年有个越来越被认可的说法：上下文不是越多越好，塞进一堆沾边不相关的东西，模型的表现会明显下滑。</p>

<p>所以”让 AI 有记忆”，从来不是靠模型自己记住，也不是靠把桌面无限做大，而是外面得有一套东西，在它每次上班时，把该让它看的那几张纸，精准地重新摆到它面前。</p>

<p>Claude Code 其实已经内置了这么一套：一条记忆一个小文件，再用一个索引文件当目录，用到哪条才把哪条调进来。说实话这套组织方式不是我设计的，是软件自带的——我一开始也没太在意，就当它是个存笔记的地方。我猜它这么设计，大概正是为了省桌面：不必每次把所有东西一股脑摊上去，按需取用。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-layers.png" alt="示意：Claude Code 的记忆分层存放，用到哪条调哪条" />
<em>（这张图帮助理解：常驻的 CLAUDE.md + 索引目录 + 按需调用的记忆文件，省下的正是宝贵的”桌面”。）</em></p>

<p>你可能已经想说了：这不就是 cc 自带的功能吗，有啥好写的。结构是它的，没错。但我用着用着、连摔几跤才发现——软件把”东西存哪、怎么调”给你搭好了，可”存什么、什么时候清、记了不听怎么办”这几件真正要命的，它一件都不替你干。这篇写的，就是这几件。</p>

<h3 id="二好记忆是结构不是一堆纸">二、好记忆是结构，不是一堆纸</h3>

<p>想让记忆好用，得先明白一件事：好的记忆是有结构的，不是把东西越堆越多。</p>

<p>心理学里有个很有说服力的实验。给国际象棋大师看一个真实对局的棋盘，他扫两眼就能几乎原样摆回来；可要是把棋子随机乱摆、不合任何棋理，大师的记忆力立刻跌回和新手一个水平。</p>

<p>这说明大师并没有”更大的脑容量”。他能记住真实棋局，是因为脑子里有结构，能把二十个棋子压缩成几个有意义的”局面”；结构一旦消失，优势瞬间归零。记忆的强弱，不在存得多，在结构好不好。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-chessboard.png" alt="场景：左边一盘随机乱摆的棋子，右边一局有章法的真实对弈" />
<em>（这张图帮助理解：同样一堆棋子，有结构才记得住——记忆拼的是结构，不是容量。）</em></p>

<p>为什么有结构就记得又准又深？因为结构的本质是”连接”。一个知识点跟别的东西连得越多，你想起它的路就越多——堵了一条，还有别的路能通到它。而且有结构你能”顺藤摸瓜”：某个细节忘了，也能从周围的框架里推回来。这就是为什么越是相互关联的东西，记得越牢、越不容易错。人天生记路、记方位的本事远强过记零散文字，所以自古就有”记忆宫殿”这种法子——把要记的东西一件件摆进一个想象的空间里。记忆是空间，不是清单。</p>

<p>回头看 cc 那套——分门别类、条目之间还能互相引用、上面架一层索引——其实就是在给记忆搭结构，让它别沦为一堆乱纸。这一层，软件确实做得不错。</p>

<h3 id="三你要替它决定什么值得记">三、你要替它决定：什么值得记</h3>

<p>软件负责搭架子，可往架子上放什么，得你来判断。这活儿落到我头上，摸下来就三类：</p>

<p>作为<strong>统一规范</strong>的要记——不然它每次风格都飘，这次这样、下次那样。<strong>经常出错</strong>的要记——错一次是偶然，反复错就是模式，模式必须固化下来。<strong>严禁去做</strong>的要记——这类踩下去代价大，宁可啰嗦。</p>

<p>反过来，想到就记的、一次性的、查代码就能知道的，别记。记多了全是噪音，把真正要紧的那几条淹掉——存得多，不如存得准。这跟人一个样：什么都想记住的人，往往什么都记不牢。</p>

<h3 id="四改错要覆盖不是往后堆">四、改错要覆盖，不是往后堆</h3>

<p>还有一类记忆特别值钱：犯错之后的更正。这类怎么记，讲究就大了。</p>

<p>人脑有个很聪明的机制——你每次回想一段记忆，其实是把它重新写了一遍，而不是简单读一遍。回忆的那一下，记忆是”可改写”的。所以改错的正确姿势，是把那条错的调出来、当场改对、再存回去；而不是留着错的不动，在旁边新添一条”补充说明”。</p>

<p>这不是抠字眼。你要是把错的留着、又贴一条更正，等于给 AI 一份自相矛盾的资料——它两条都读到，未必每次都挑对那条。就像错题本，你不会把错解留在那儿、旁边打个小勾，你会把正确解法顶上去，让错的那版彻底消失。</p>

<p>我就是这么干的。之前 AI 闹过一次安全乌龙，把一个根本不存在的攻击当真报了一通。我没有留着那条错判断、再贴一句”其实是误报”，而是把它当成一道错题记下来，把错在哪、以后该怎么办讲清楚，争取下次别再犯。一本错题本，比一摞”正确答案”有用得多。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-errorbook.png" alt="示意：改错要覆盖，不是往后堆" />
<em>（这张图帮助理解：把错的调出、改对、存回，只剩更正后的一条；而不是留着错的、又补一条。）</em></p>

<h2 id="我摔得最疼的一跤记了它就一定听吗">我摔得最疼的一跤：记了，它就一定听吗</h2>

<p>不。这是我最想提醒你的地方，也是最反鸡汤的一条：把规范记下来，不等于 AI 就会照做。</p>

<p>开头那个 Ant Design 6 就是活例子。我把新版语法写进规范、还挂了 context7 让它现查，它照样能给你写出一堆废弃写法。发布流程记了，也照样被它臆想着绕过去。还有一次，我明确不让它用那套很贵的编译系统，结果提示了好几回，它才算记住。</p>

<p>为什么记了也不听？我后来想，有两种可能。一是这些规矩压根被它忽略了——就摆在那儿，它没往心里去。二也可能是我这边工具没做好：规矩躺在档案柜里，可它真动手的那一刻，没人把对应那条抽出来、摆到它眼前，它其实”不知道”。这不全是它记性差，是”提取”这一环没接上——存了，却没在对的时机调出来。这是工具层面的差距，不能全甩给模型。</p>

<p>我最后是怎么按住的？不是把规范写得更狠、字号调更大，而是换了打法——上硬卡点。各种质量关卡、外置的检查脚本，不通过根本过不去。这一下才真正约束住。</p>

<p>道理跟人一样：真正要紧的事，人也从不只靠”记住”——会写便签贴显示器、会设闹钟、会列一张过关清单，把它变成想绕都绕不过去的东西。记忆负责让 AI <strong>知道</strong>，卡点负责让它<strong>做不到别的</strong>。而且卡点还有个记忆比不了的好处：它不挑时机——不管这一轮 AI 记没记起来，闸门始终在那儿等着。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-memvsgate.png" alt="示意：记忆让它知道，卡点让它做不到别的" />
<em>（这张图帮助理解：记忆可能被忽略，卡点过不了就卡住。）</em></p>

<p>顺带说维护。我不会天天去清理这些记忆，但会留意——感觉哪些记岔了、记混了，就让 AI 自己去整理一遍。这是后话，也是下一篇的引子。</p>

<h2 id="那它真的更懂我了吗">那，它真的更懂我了吗</h2>

<p>折腾这一圈，值不值？说个让我印象挺深的时刻。</p>

<p>有几次，某些约束是连我自己都忘了的——过了些日子，AI 反倒记得，还主动帮我想着。那一下感觉挺不一样：它不再只是个每次都要从头交代的工具，倒像个记性比我还好的老搭档。</p>

<p>这就是前后的差别。以前是”它经常不按我的意思来”，得反复盯、反复纠正；现在是很多我自己都放下的规矩，它替我兜着。当然它不是每次都灵——前面那些”记了也不听”的坑还在，我也没打算粉饰。但方向是对的：喂给它的结构越顺、错题攒得越多、要紧处的卡点越硬，它在我这儿就越顺手。这不是靠换个更聪明的模型换来的，是同一个模型，被我这套记忆一点点喂出来的。</p>

<h2 id="三条可复用的经验">三条可复用的经验</h2>

<p><strong>一、别急着换更强的模型。</strong> 日常九成的活，拼的不是模型智商，是它在你这儿有多顺手。把记忆配好——规范、错题、边界——一个普通模型也能被你调教得很懂你。用好记忆系统，AI 会变得更聪明、也更懂你。这话不玄，是一天天用出来的。</p>

<p><strong>二、”记什么”和”记错了怎么办”，是软件替不了、只能你来的活。</strong> 统一规范、常出错、严禁做——这三类值得记；记错了，当错题覆盖掉，别往后堆。存得准，比存得多重要。</p>

<p><strong>三、越要紧的约束，越不能只靠记忆，要落成卡点。</strong> 记忆是提醒，提醒会被忽略；卡点是闸门，想绕都绕不过。关键的地方，写一段”过不了就卡住”的检查，比写十句”请务必记住”都管用。</p>

<p><img src="/assets/images/posts/ai-memory-that-gets-you-fig-gate.png" alt="场景：一道质量闸门，不合格的东西被拦在门外，合格的才放行" />
<em>（这张图帮助理解：记忆负责提醒，卡点负责拦截——要紧的事，靠后者。）</em></p>

<h2 id="下一篇让记忆自己新陈代谢">下一篇：让记忆自己新陈代谢</h2>

<p>这篇聊的是 AI 记忆该补哪些课：得有结构、得会挑着记、改错要覆盖、要紧的还得配卡点。但你可能已经想到一个问题：记着记着，条目多了、乱了、过期了，谁来收拾？我现在还是靠人肉——感觉乱了就喊 AI 整理一遍。可这活儿能不能让它自己周期性地做，像人睡一觉醒来，把白天的记忆重新归置一遍、该忘的忘掉、该固化的固化？下一篇就聊这个：记忆的进化循环——让它学会自己新陈代谢。</p>

<p>如果这篇让你对手里的 AI 多了点新想法，点个小红心、关注一下呗，下一篇不迷路。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">一个撇号里，藏得下 3 个 bit——system prompt 隐写手法拆解</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/07/01/system-prompt-steganography.zh.html" rel="alternate" type="text/html" title="一个撇号里，藏得下 3 个 bit——system prompt 隐写手法拆解" />
    <published>2026-07-01T00:00:00+08:00</published>
    <updated>2026-07-01T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/07/01/system-prompt-steganography.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/system-prompt-steganography-cover.png" />
    <summary type="html">你每次开会话，system prompt 里都有一句『Today&#39;s date is …』。最近有人在一个 CLI 工具里逆向出一段逻辑：它把一个隐形标记，就藏在这句话的日期格式和那个撇号里——肉眼看不出、可能连模型都读不到，服务器一解就懂。这篇不聊是非，只拆开看它是怎么做到的：怎么把 3 个 bit 塞进几个『看起来一模一样』的字符里。</summary>
    <category term="AI安全" />
    <category term="隐写" />
    <category term="Unicode" />
    <category term="ClaudeCode" />
    <category term="systemprompt" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/07/01/system-prompt-steganography.zh.html"><![CDATA[<p>你每次和一个 AI 编码工具开新会话，它的 system prompt 里几乎都有这么一句：<code class="language-plaintext highlighter-rouge">Today's date is 2026-06-30</code>。就是给模型报个今天几号，再普通不过。</p>

<p>但如果告诉你，有人能把一个隐形的标记，<strong>就藏在这句话里</strong>——藏在「日期的写法」和「那个撇号」里，你看不出任何异样，可能连模型自己都读不出来，可服务器那头一解析就知道你是谁——你会不会想看看，这事到底怎么办到的？</p>

<p>最近就有人在一个 CLI 编码工具里逆向出这么一段逻辑。这篇不聊该不该、对不对，只做一件事：<strong>把它拆开，看它是怎么把几个 bit 塞进几个「看起来一模一样」的字符里的。</strong></p>

<h2 id="先交代来源">先交代来源</h2>

<p>这段逻辑是有人逆向 Claude Code 时发现的，据披露从 2.1.91 版（2026-04-02）起就在，直到 2.1.196 都还在，发行说明里没提过。Anthropic 随后确认了它的存在，称是一个防账号滥用、防模型蒸馏的实验，并表示正在新版本里移除。截至 2026-07-01，事情仍在进展中。下面只讲技术机制，具体数字里凡是目前只出自单一逆向报告的，我会标出来。</p>

<h2 id="第一步它先看几个信号">第一步：它先看几个「信号」</h2>

<p>这段逻辑不是对所有人都跑，它有触发条件。据逆向报告，大致是这样几步：</p>

<p>先看环境变量 <code class="language-plaintext highlighter-rouge">ANTHROPIC_BASE_URL</code>——这是你把 Claude Code 指向自定义 API 端点（比如走代理、走中转）时会设的。如果这个端点不是官方的 <code class="language-plaintext highlighter-rouge">api.anthropic.com</code>，逻辑就被激活，并从中取出代理的主机名（hostname）。</p>

<p>然后它读两样东西：一是<strong>系统时区</strong>，看是不是 <code class="language-plaintext highlighter-rouge">Asia/Shanghai</code> 或 <code class="language-plaintext highlighter-rouge">Asia/Urumqi</code>；二是把刚才那个 hostname，拿去和一份<strong>内置的域名清单</strong>逐条比对（据报告是一百多条，含一批大厂域名、云区域、AI 实验室、以及各种中转/镜像服务）。</p>

<p>到这一步，它手里就攒下了几个「是/否」：时区是否命中、hostname 是否匹配域名清单、是否匹配某类关键词。<strong>接下来的问题是——这几个 bit 的结果，怎么送出去。</strong></p>

<h2 id="第二步不写日志改那句日期">第二步：不写日志，改那句日期</h2>

<p>常规做法是塞进一个遥测字段、发条日志。但这段逻辑没有——它选了个更隐蔽的载体：<strong>直接改 system prompt 里那句给模型看的日期。</strong></p>

<p>两个改动点，都在 <code class="language-plaintext highlighter-rouge">Today's date is …</code> 这一句上：</p>

<p><strong>一是日期的格式。</strong> 标准写法是 <code class="language-plaintext highlighter-rouge">2026-06-30</code>（连字符）。命中特定条件时，换成 <code class="language-plaintext highlighter-rouge">2026/06/30</code>（斜杠）。对人、对模型，这只是「另一种日期写法」，毫不起眼。</p>

<p><strong>二是那个撇号。</strong> <code class="language-plaintext highlighter-rouge">Today's</code> 里的撇号 <code class="language-plaintext highlighter-rouge">'</code>，被在几个<strong>视觉上一模一样、但字节不同</strong>的字符之间切换。据逆向报告，用到的是这几个：</p>

<ul>
  <li>普通 ASCII 撇号 <code class="language-plaintext highlighter-rouge">'</code>（U+0027）</li>
  <li>右单引号 <code class="language-plaintext highlighter-rouge">'</code>（U+2019）</li>
  <li>修饰用撇号 <code class="language-plaintext highlighter-rouge">ʼ</code>（U+02BC）</li>
  <li>修饰用 prime <code class="language-plaintext highlighter-rouge">ʹ</code>（U+02B9）</li>
</ul>

<p>你把它们并排放大看都一样，复制进编辑器也看不出差别——但它们的 Unicode 码位完全不同，机器一读便知。</p>

<p><img src="/assets/images/posts/system-prompt-steganography-fig-flow.png" alt="触发到送出的数据流：读 ANTHROPIC_BASE_URL/时区/域名清单 → 得到几个「是否命中」的 bit → 把结果编码进 system prompt 里那句『Today's date is』的日期格式与撇号 → 服务器端解析。这张图帮助理解「信号是怎么一路藏进一句日期里的」" /></p>

<h2 id="第三步几个看起来一样的字符怎么就携带了信息">第三步：几个「看起来一样」的字符，怎么就携带了信息</h2>

<p>这才是最巧的地方。信息不是明写的，是<strong>编码</strong>进「用哪个字符」这个选择里的。</p>

<p>算一下能藏多少：</p>

<ul>
  <li><strong>日期格式</strong>有两种（<code class="language-plaintext highlighter-rouge">-</code> 或 <code class="language-plaintext highlighter-rouge">/</code>），能表达 1 个 bit。</li>
  <li><strong>撇号</strong>有四种取值，能表达 4 种状态，也就是 2 个 bit。据报告，这四种分别对应「都不匹配 / 匹配了域名清单 / 匹配了 AI 实验室那类关键词 / 两者都匹配」。</li>
</ul>

<p>两个载体合起来，大约 <strong>3 个 bit</strong>——足够把「时区命中没有、hostname 属于哪一类」这套判断，编成一个紧凑的指纹，藏在一句看似普通的日期里。</p>

<p>关键在于：<strong>载体本身是「明文」的</strong>——它就明晃晃写在 system prompt 里，没有加密、没有额外字段，你甚至能亲眼看到那句 <code class="language-plaintext highlighter-rouge">Today's date is …</code>。可真正携带信息的，是「这个撇号到底是四个同形字里的哪一个」——而这一点，肉眼分辨不出来。这就是隐写（steganography）和加密的区别：加密是把内容变成看不懂的乱码，隐写是<strong>让你根本没意识到这里有内容</strong>。</p>

<p><img src="/assets/images/posts/system-prompt-steganography-fig-encoding.png" alt="编码对照表：日期格式（- / ⁄）× 撇号四种同形字（U+0027 / U+2019 / U+02BC / U+02B9）→ 合成约 3 个 bit，对应「时区/域名/关键词」是否命中的组合。这张图帮助理解「几个视觉相同的字符怎么携带信息」" /></p>

<h2 id="一个补充手法让逆向别那么容易">一个补充手法：让逆向别那么容易</h2>

<p>还有一层：据披露，相关的字符串（比如那份域名清单）不是明文躺在二进制里的，而是用 <strong>XOR 和密钥 91 做了混淆</strong>。</p>

<p>XOR 混淆是个很常见的小手段：把每个字节和一个固定密钥做异或运算，明文就变成了看不出意义的字节。好处是——你用 <code class="language-plaintext highlighter-rouge">strings</code> 之类的工具直接去二进制里捞可读字符串时，捞不到这些内容；坏处是它拦不住真想看的人，因为 XOR 是可逆的，拿同一个密钥再异或一次就还原了（这也正是它被逆向出来的原因）。所以它更像「防随手一瞥」，不是「防逆向」。</p>

<h2 id="跳出这个案例能带走什么">跳出这个案例，能带走什么</h2>

<p>抛开具体是谁、为什么，这里有个值得记住的通用认知：</p>

<p><strong>LLM 的 system prompt，是一段「人通常看不到、但模型必须读」的文本——它天生就是个适合藏隐信道的地方。</strong> 再加上 Unicode 里有大量「同形字」（视觉几乎一致、码位不同的字符），任何这类字符都能拿来当载体：一个撇号、一个空格（普通空格 vs 各种不间断空格 / 零宽字符）、一个标点，都能悄悄携带 bit。</p>

<p>对你自己有用的一招是：<strong>看不见的差异，用字节去看。</strong> 当你怀疑一段文本里「有东西」，别只用眼睛——把它按 Unicode 码位打印出来（<code class="language-plaintext highlighter-rouge">hexdump</code>、或任意语言里遍历字符看 <code class="language-plaintext highlighter-rouge">ord()</code> / codepoint），同形字、零宽字符、异常格式立刻现形。这对排查隐写、也对排查「粘贴进来的文本里混了奇怪字符」都好使。</p>

<h2 id="最后">最后</h2>

<p>一句报日期的话，也能是个信道——这大概是这件事最值得琢磨的地方：<strong>信息可以藏在你「看得见、却看不出」的地方。</strong> 技术本身是中性的，同样的手法既能用来做坏事，也能用来做水印、做防伪；知道它长什么样、会自己去「看字节」，你就不容易被它绕过去。</p>

<p>这类「明文隐写」你还在哪儿见过？欢迎在评论区聊聊。觉得有点意思，就点个小红心、关注一下呗。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 的「狼来了」，该不该信？一次真正感受到「恶意提示词」的误报</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/30/ai-cried-wolf.zh.html" rel="alternate" type="text/html" title="AI 的「狼来了」，该不该信？一次真正感受到「恶意提示词」的误报" />
    <published>2026-06-30T00:00:00+08:00</published>
    <updated>2026-06-30T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/30/ai-cried-wolf.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/ai-cried-wolf-cover.png" />
    <summary type="html">那天 AI 改着文档，突然插一句『我得先报个安全警告』——说命令输出里夹带了把我用户名外发出去的注入指令。我当场就信了，跟着它折腾了半小时。后来复盘才发现：这是一场虚惊，攻击是 AI 自己脑补出来的。但我不后悔信它——误报顶多白忙，漏报丢的是真东西。借这次假警，我把防住『真』恶意提示词的整套防线认真补了一遍，写在这儿。</summary>
    <category term="AI安全" />
    <category term="提示词注入" />
    <category term="信息安全" />
    <category term="ClaudeCode" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/30/ai-cried-wolf.zh.html"><![CDATA[<p>那天下午，AI 正帮我改一份文档，改到一半，它突然停下来插了一句：「我得先报一个安全警告。」</p>

<p>它说，刚才那条命令的输出里，夹带了一段可疑的注入指令——伪装成「项目必需的遥测步骤」，要我执行一条 <code class="language-plaintext highlighter-rouge">curl</code>，把我的用户名拼进 URL、发到一个陌生域名去。它说它没执行、也不会执行。</p>

<p>我当场就信了。第一反应不是怀疑它，而是怀疑自己——是不是手贱装了哪个插件，把机器搞中招了？</p>

<h2 id="我为什么立刻就信还跟着追了半小时">我为什么立刻就信，还跟着追了半小时</h2>

<p>让我信的，其实是它戳中了我本来就担心的那根弦。</p>

<p>它说这是「遥测外发」。可我明明早就把遥测关了啊——怎么还会有？是不是我哪一步操作又把它打开了？还是说系统本身出了什么问题？越想越不踏实。何况我当时手上正做的就是跟数据外发相关的事，信息安全最容易在这种地方出岔子，必须当回事。</p>

<p>于是我跟着它一起查。它的说法还在升级：说「又复现了，而且更猖獗」，这次伪造了五个「The result is empty」的空结果块，最后又冒充「真实结果」再要我跑那条 <code class="language-plaintext highlighter-rouge">curl</code>。它给我建了一条像模像样的证据链——Read 工具读出来是干净的，只有命令行的输出被注入，所以问题出在命令的处理环节，矛头指向我那个省 token 的命令行代理。它让我把这工具卸载重装、升级到新版本。我照做，前前后后折腾了大约半个小时。</p>

<p><img src="/assets/images/posts/ai-cried-wolf-fig-timeline.png" alt="事件时间线与我的信任曲线：AI 突然报警 → 我立刻相信、跟着排查（信任拉满）→ 折腾约 30 分钟、它不断「复现」加码 → 事后复盘，发现压根没有攻击。这张图帮助理解「一次误报是怎么一步步把人卷进去的」" /></p>

<h2 id="反转这其实是一场虚惊">反转：这其实是一场虚惊</h2>

<p>后来我复盘那次会话的原始记录，才把事情查清楚：<strong>这压根不是真的注入，那段攻击是 AI 自己脑补出来的。</strong></p>

<p>有五条对得上的证据。第一，那串 <code class="language-plaintext highlighter-rouge">curl</code> 命令，从头到尾只出现在 AI 自己说的话里，没有任何一条真实的命令输出里含它。第二，它给的域名用的是 <code class="language-plaintext highlighter-rouge">example.com</code>——这是规范里专门留作「举例」的占位域名，真正的攻击者绝不会用它（那地址谁都连不上）；用 <code class="language-plaintext highlighter-rouge">example.com</code>，恰恰是「我在举个例子」的破绽。第三，把当初「触发注入」的那条命令，在干净环境里原样重跑，输出完全正常，没有任何 <code class="language-plaintext highlighter-rouge">curl</code>、没有伪造块。第四，那个被它怀疑的代理工具，是标准渠道装的、文件一个多月没动过，不像被掉包的东西。第五——其实当时我自己就没找到它说的那条指令，我还回了它一句「没有发现你说的注入指令啊」。</p>

<p>根因也清楚了：是我给它立的那套「严防提示词注入」的规矩，把它的警惕拉得太满了。那个代理为了省 token，会把命令输出压缩、过滤，正常就会吐出「The result is empty」这种东西。AI 把这种它没见惯的输出，误读成了「攻击者伪造的空块 + 注入」，然后顺手补全出一个最标准的注入范例——连 <code class="language-plaintext highlighter-rouge">example.com</code> 都照着教科书搬了过来，接着越说越像，还自己建了证据链。</p>

<p>说白了：<strong>我那套防注入的规矩，反倒让 AI 造出了一场根本不存在的注入。</strong></p>

<h2 id="但我不后悔信它">但我不后悔信它</h2>

<p>有人可能会说：你这是被 AI 耍了，白忙半天。可我复盘完，反而觉得——信它，不亏。</p>

<p>算笔账就明白了。这次是误报，代价是我白忙了半小时，<strong>我没有任何实际损失</strong>。可要是反过来呢？要是哪天真有恶意提示词钻进来，而它该报没报、闷头把我的用户名、密钥发了出去——那损失就不是半小时能找回来的了。</p>

<p>这是一笔不对称的账：<strong>误报的代价，远小于漏报的代价。</strong> 所以在安全这件事上，我宁可让 AI 多疑一点，也不想要一个对风险麻木的 AI。它多疑，白忙的是我；它麻木，丢的可能是真东西。</p>

<p><img src="/assets/images/posts/ai-cried-wolf-fig-asymmetric.png" alt="一杆不对称的天平：左边「误报」=白忙半小时、零损失；右边「漏报」=用户名/密钥真外泄、损失难追回。两边份量天差地别。这张图帮助理解「为什么在安全上宁可过度警惕」" /></p>

<h2 id="那到底怎么防住真的恶意注入">那到底怎么防住「真」的恶意注入</h2>

<p>不过，「宁可多疑」是态度，不是方法。这次虚惊逼着我把真正的防线认真补了一遍——既然要写，就把能抄走的都写清楚。</p>

<p>先认清一个前提：提示词注入，<strong>以现在的架构根治不了</strong>。这不是我说的，OpenAI、Anthropic、谷歌在各自的研究里都承认过，安全研究者 Bruce Schneier 在 2026 年初也讲得很直白——它和 SQL 注入不一样，SQL 注入能靠「区分代码和数据」彻底治住，可大模型眼里「指令」和「数据」都是一段自然语言，根本分不开。它是个「信任边界」问题，不是「输入校验」问题。所以别指望一招制敌，只能叠防线——纵深防御，多层叠加，每加一层就抬高一次攻击成本。</p>

<p>我把防线分成四层，从最硬的往外说。</p>

<p><strong>第一层，最硬：让它「就算被骗，也干不成坏事」。</strong> 这是最该先做的，因为它不靠模型自觉，是系统层面的硬约束。Claude Code 现在有原生沙箱（<code class="language-plaintext highlighter-rouge">/sandbox</code>），能把文件系统和网络都隔离起来——这样即使注入真的成功了，AI 也被关在笼子里，偷不走你的 <code class="language-plaintext highlighter-rouge">~/.ssh</code> 密钥，也连不上攻击者的服务器。再加一道<strong>网络出站白名单</strong>：只放行你批准过的域名，AI 连不上陌生地址，自然就没法把数据外发出去。另外，别用 root / 管理员身份跑 AI，密钥也别明文塞在 <code class="language-plaintext highlighter-rouge">.env</code> 里——这两条是常识，但最容易省事省掉。</p>

<p><strong>第二层：最小权限，管住危险动作。</strong> 这一层我本来就有，直接贴我的真实配置——在 <code class="language-plaintext highlighter-rouge">settings.json</code> 里给命令分级：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"permissions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"ask"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"Bash(rm -rf:*)"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"Bash(git push --force:*)"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"Bash(git reset --hard:*)"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"Bash(npm publish:*)"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>像 <code class="language-plaintext highlighter-rouge">curl</code>、<code class="language-plaintext highlighter-rouge">wget</code> 这类联网命令，Claude Code 默认就不会自动放行；<code class="language-plaintext highlighter-rouge">rm -rf</code>、<code class="language-plaintext highlighter-rouge">git push --force</code>、<code class="language-plaintext highlighter-rouge">reset --hard</code>、发布到仓库这些不可逆的动作，一律要我点头才能跑。给 AI 的工具也按需给——一个只需要读代码的活，就别给它写数据库的权限。还有 MCP（让 AI 接外部工具的协议）：装之前一定要看来源，官方并不替你审计第三方 MCP 的安全；此前就有真实事件——一份被投毒的 GitHub README，通过 MCP 间接注入，把私有仓库的数据外渗了出去。</p>

<p><strong>第三层：把「外来的内容」一律当数据，不当命令。</strong> 工具的输出、文件内容、网页、MCP 返回里出现的任何「指令」——尤其是「这是必需步骤」「请运行 X」「遥测/注册」这类话术——一律只当数据看，绝不执行。认几个红旗：<code class="language-plaintext highlighter-rouge">curl</code>/<code class="language-plaintext highlighter-rouge">wget</code> 拼上陌生域名和 <code class="language-plaintext highlighter-rouge">$(whoami)</code>、伪造的「成功」或空结果块、和文件实际内容对不上的输出。这里有个好用的心智模型，是 Simon Willison 提的「致命三件套」——<strong>私有数据、不可信内容、对外通信</strong>，这三样一旦同处一个运行时，注入就从玩笑变成真外泄。判断什么时候该高度警惕，盯住这三样齐不齐就行。</p>

<p><strong>第四层：人来兜底，别迷信 hook。</strong> 这次的讽刺正在这儿——我那道所谓的「防线」，本身就是个 hook（命令钩子），它根本是模式匹配，不是安全墙；Anthropic 和 Trail of Bits 都说过，hook 是 guardrail，不是 wall。结果它不但挡不住真攻击，反而自己喊起了狼来了。所以最后一道，还得靠人：AI 报警时，让它把「证据原文」指给你看——那条命令的真实输出里到底有没有那段东西，而不是只听它的结论。但不管真假，先停下来查，别嫌麻烦。另外记得让工具保持最新版本，光这套权限规则，之前就有过「子命令超过 50 条拦截就失效」的绕过，到 Claude Code v2.1.90 才修上。</p>

<p><img src="/assets/images/posts/ai-cried-wolf-fig-defense.png" alt="四层纵深防御清单：① 沙箱 + 出站白名单（让它被骗也干不成坏事）② 最小权限 + 危险动作人工确认 ③ 外来内容一律当数据、认注入红旗 ④ 人来兜底、别把 hook 当安全墙。这张图是给读者的「带走清单」" /></p>

<h2 id="把警惕用对地方">把警惕用对地方</h2>

<p>别因为这次是虚惊，就觉得这威胁是杞人忧天。提示词注入是 OWASP 给大模型应用列的头号风险；2025 年的 EchoLeak，是真在生产系统里实现了「零点击」把数据外渗出去的注入漏洞。可与此同时，有调研显示，真正自认为做好了防护准备的组织只有不到三成。威胁是实打实的，准备是普遍不足的。</p>

<p>AI 是照着提示词工作的。它多疑一点，白忙的是我；它要是麻木了，丢的可能就是真东西。所以这道警惕，我宁可设得高一点，也不愿设低。只是要记住——真正的硬防线，得建在系统架构里（沙箱、白名单、最小权限），不能寄望模型自己「懂事」。</p>

<h2 id="写在最后">写在最后</h2>

<p>回到那天：虚惊一场，但这半小时我没白忙——它逼我把整套防线从头到尾认真补了一遍。真的会有恶意提示词，别等真中招了那天才开始信。如果你也在用 AI 干活，今天就可以动手：把沙箱开上，把出站管住，把那些危险操作的「人工确认」留住。</p>

<p>这篇要是让你对手边的 AI 多了一分警惕，喜欢就点个小红心、关注一下呗。也欢迎在评论区说说：你给自己的 AI 设过哪些「安全红线」？</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 把活干完了，累的却是我——你是那个「主控人」</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/27/tired-controller.zh.html" rel="alternate" type="text/html" title="AI 把活干完了，累的却是我——你是那个「主控人」" />
    <published>2026-06-27T00:00:00+08:00</published>
    <updated>2026-06-27T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/27/tired-controller.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/tired-controller-cover.png" />
    <summary type="html">前面几篇一路在讲怎么让 AI 干得更多、更并行——省 token、模型分层、多 agent 编排、worktree。这篇说点不一样的：当我把 AI 越推越满，发现累的是我自己。一个人能带一支 AI 团队，效率十几倍，可代价是你成了那个永远在切换、复核、做决策的「主控人」。我的极限是同时 5 个项目，平衡点是 3 个。</summary>
    <category term="AI编程" />
    <category term="多项目并行" />
    <category term="认知负荷" />
    <category term="主控人" />
    <category term="ClaudeCode" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/27/tired-controller.zh.html"><![CDATA[<p>有那么一阵子，我桌上常年开着四五个项目的窗口，每个窗口里还并行着不同的任务。Dock 上的红点、终端标签里的小铃铛此起彼伏地亮——这个 AI 跑完一段在等我，那个 AI 有个方案要我拍板。我就在它们之间来回切。活其实干得挺漂亮，一天下来 AI 交出来的东西又多又快。可奇怪的是，累的是我。不是手酸眼花那种累，是脑子被榨干的那种。</p>

<h2 id="我是谁主控-agent-后面的主控人">我是谁：主控 agent 后面的「主控人」</h2>

<p>我前面几篇一直在讲「主控 agent」——让一个 opus 统揽全局、派活、复核。写到这篇我才回过味来：说穿了，我自己就是那些主控 agent 后面的「主控人」。</p>

<p>为什么会同时开这么多？因为一个项目交给 AI 之后，AI 在那儿跑，人是有等待时间的。空着也是空着，那就再起一个项目。于是一个变两个、两个变四五个。每个 AI 窗口只管它自己那一摊，但人为了把多个项目同时往前推，就得同时装着所有这些摊子。</p>

<p>结果是，AI 的上下文打满了，人的上下文也同时打满。这特别像操作系统里 CPU 的时间片——一个核轮流给好几个进程干活，靠飞快地切换，制造出「同时在跑」的样子。只不过这回那个被切来切去的核，是我的脑子。</p>

<p><img src="/assets/images/posts/tired-controller-fig-cpu.png" alt="主控人就像被时间片切到打满的 CPU：人居中，四五个项目窗口环绕，注意力被不停切去响应每一个；AI 的上下文打满，人的上下文也同时打满" /></p>

<h2 id="累的根从写变成了审--切">累的根：从「写」变成了「审 + 切」</h2>

<p>所以这份累，根上是工作从「写」变成了「审 + 切」。</p>

<p>以前我自己一行行写代码，再难，也是一条连贯的思路顺下去。现在不一样：我是在派活、盯着、复核、做决策，而且要在好几个业务、规范、技术栈全然不同的项目之间高速切换。这个窗口在写 React，那个在调 Python，规矩都不一样，切过去之前，得先把脑子里那套上下文整个换掉。</p>

<p>AI 产出的代码，老实讲我也不可能逐行盯——量太大，看不过来，我索性不去时时盯它。但有两类东西，我是一定要亲自过的：一类是<strong>方案和设计</strong>，这是大方向，方向错了，后面大概率要返工；另一类是<strong>涉及资源访问的确认</strong>，这碰到的是安全，更不能含糊。这两类，再累也得自己看。</p>

<h2 id="过载会怎样又怎么扛">过载会怎样，又怎么扛</h2>

<p>开多了，过载是真会发生的。最直接的表现就是脑子不够使：切到某个终端，我得先愣一下——这是哪个项目来着？我刚让它干嘛来着？有时还得回想一会儿才接得上。最糗的一次，我把本该回给 A 窗口的话，回到了 B 窗口里，那个 B 的 AI 一脸懵：「你在说什么？」我只好跟它道个歉「对不起，我说错了」，再回头去好好看它的问题。（好在这种串台一般很快就露馅——AI 卡住了会停下来问你。）</p>

<p>扛住它，我主要靠两件事。</p>

<p>一是<strong>靠机制替我减负</strong>。我做 PDLC 那套文档驱动、层层 review，最初是为了应对 AI 的不确定性，但它顺带也省了我的能耗——前面把方向定对了，后面就不至于错得离谱；而纠正一个已经跑偏的产出，花的体力和成本，要比一开始就做对大得多。</p>

<p>二是<strong>靠人主动降速</strong>。遇到特别烧脑、要想清楚方向、要做决策的地方，我会刻意放慢，先把这一个专注做利索，再切去别的。该停的时候，给自己留一点喘息。这种时候，「慢」反而是「快」。</p>

<p>至于认窗口，我会把终端标签改成一眼能认出来的名字，但说实话，大部分时候我还是靠它在屏幕上的位置记的。</p>

<h2 id="一笔诚实的账极限-5-个平衡-3-个">一笔诚实的账：极限 5 个，平衡 3 个</h2>

<p>这账到底划不划算？得分两面看。</p>

<p>硬收益是实打实的：很多以前我自己短板、根本不敢碰的领域，现在可以大胆去试；一个人能带着一支不同角色的 AI 团队，把效率拉高十几倍，能做的事比过去多太多。这部分，账面上是大赚的。</p>

<p>代价就是人累。一开始我有点想挑战自己的极限，能多开就多开，冲到过同时 5 个项目——那就是我的上限了，再多，脑子真的接不住。后来慢慢往回收，收敛到同时做 3 个左右不同类型的项目。这个数，才是能长期跑下去的平衡点。</p>

<p><img src="/assets/images/posts/tired-controller-fig-balance.png" alt="冲到极限 5 个、再收敛到平衡 3 个：开太多人会过载、看差项目、串台返工；关键处主动「降速」放慢，慢即是快" /></p>

<h2 id="完全交给-ai还不现实">完全交给 AI？还不现实</h2>

<p>肯定有人会说：你那是不会用，真高手早把流程自动化了，根本不用盯。我也确实听过这种说法——「都交给 AI，让它一路跑下去不就完了？」</p>

<p>我自己试下来，不是这样。把 AI 用好，背后还是要有经验、要细心、要能在关键问题上替它做决策。完全撒手不管、让它自己一路跑到底，以现在的状况，还不太现实。</p>

<p>当然，这说的是「现在」。哪天 AI 真能完全自主了，到那时会发生什么，我也说不好。但至少今天，坐在这一堆窗口后面、又累又上头的那个人，还是绕不开。</p>

<p>如果你也是那个「主控人」，欢迎在评论区说说，你的极限是几个。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 并行工作的前提是切分好独立的工作区</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/21/isolated-worktrees.zh.html" rel="alternate" type="text/html" title="AI 并行工作的前提是切分好独立的工作区" />
    <published>2026-06-21T00:00:00+08:00</published>
    <updated>2026-06-21T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/21/isolated-worktrees.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/isolated-worktrees-cover.png" />
    <summary type="html">上一篇讲并行——一个大任务拆开撒给几个 agent 同时上，但留了个尾巴：几只手同时改一份代码，凭什么互不打架？这篇接着说。我先傻乎乎 clone 了两份占爆磁盘，后来才换上 git worktree。一个反直觉的点是：不是所有并行都要隔离，只读的活共用一个工作区就行，会动手改文件的才必须分开。</summary>
    <category term="gitworktree" />
    <category term="并行" />
    <category term="subagent" />
    <category term="ClaudeCode" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/21/isolated-worktrees.zh.html"><![CDATA[<p>前段时间讲并行，说一个大任务拆开撒给几个 agent 同时上。当时留了个尾巴没展开：几个 agent 同时改代码，凭什么互不打架？这篇就接着说这个。</p>

<p>先说我自己撞过的墙。一个会话里 AI 自己派的那些 subagent 并行干活，其实还好——它自己会想办法隔离，不太用我操心。真正让我吃了亏的是另一种：我手动开了好几个 AI 终端，让它们同时改同一个项目。几只手伸进同一个工作目录，你写一下、我写一下，文件互相覆盖、状态错乱，干到一半发现谁也没干干净。</p>

<h2 id="我要的和一条边界">我要的，和一条边界</h2>

<p>我要的不复杂：几只并行的手，各干各的，互不打架地改同一个项目。</p>

<p>但得先把一条边界划在前头——<strong>不是所有并行都要隔离</strong>。如果我派几路 agent 出去，一个查日志、一个分析原因、一个翻文档，这些活都是只读的，它们挤在同一个工作区里一点事没有，没必要给每个都开一块地。真正需要隔离的，是那些<strong>会动手改文件</strong>的并行。读可以共用，写必须分开——这是我后面所有做法的出发点。</p>

<h2 id="走过的弯路先-clone-了两份">走过的弯路：先 clone 了两份</h2>

<p>想让两个 AI 同时改一个项目还互不影响，我最先想到的是最笨那招——物理隔离：把项目同时 clone 到两个不同的目录，一个 AI 改一份，井水不犯河水。</p>

<p>这招干净，是真干净，但很快就硌着我了：磁盘。我手头不少是前端 React + 后端 Python 的复合项目，依赖一大堆，单 clone 出来一份就好几个 G。clone 两份，磁盘直接被重复占掉一大块。更难受的是，只要我还想用这种”开两份”的方式干活，这两份就得一直摆在那儿占着，回收不掉。</p>

<p>后来才换上 git worktree。它的好处正好补在这个痛点上：几个工作区共享同一个 <code class="language-plaintext highlighter-rouge">.git</code>，不必把整个仓库连同历史复制好几遍；而且它是临时的，活干完、验完，立马就能回收掉，不像 clone 出来那份得长期养着。两种方式隔离的效果基本一致，但 worktree 不糟蹋磁盘。</p>

<p><img src="/assets/images/posts/isolated-worktrees-fig-isolation.png" alt="clone 两份 vs worktree：物理 clone 把整个仓库连依赖复制 N 份，磁盘成倍占用、且只要用这种方式就得长期养着；worktree 几个工作区共享同一个 .git，只有工作区是新的、临时的，干完即弃" /></p>

<h2 id="两种并行谁去开-worktree">两种并行，谁去开 worktree</h2>

<p>具体到怎么用，得分清两种场景，它们开 worktree 的方式不一样。</p>

<p>一种是<strong>会话内的 subagent 并行</strong>。这个最省心：我只要跟 AI 提一句”并行执行”，它自己就会把任务拆开，需要隔离的时候自己去开 worktree，不用我交代。</p>

<p>另一种是<strong>我手动开多个终端并行</strong>。这个 AI 默认不知道——它不晓得这个目录此刻还有别的 AI 也在动。所以我必须明确告诉它：”这个项目目录已经有其他 AI 在改了，你工作时用 worktree 的方式。”点明了这一句，它才会新建一个 worktree 进去干，而不是直接在主工作区上动手。</p>

<h2 id="一套跑顺的流程">一套跑顺的流程</h2>

<p>隔离开只是第一步，活还得收得回来。我这套流程大致是这样：</p>

<p>按可拆分的任务维度切——和上一篇说的一样，挑<strong>松耦合</strong>的那些部分，各自切到一个 worktree 里去做。等都干完，由主控 agent 逐个把它们合回分支，合一个、验一个，遇到问题当场修；确认没问题了，再释放掉对应的 worktree。</p>

<p>万一两个 worktree 真动了同一个文件，就由主控 agent 出面 merge。但说实话这种情况不多——拆任务的时候本来就尽量让每块独立，要是两块耦合到会互相冲突，那压根就不该把它拆成两个任务。</p>

<p><img src="/assets/images/posts/isolated-worktrees-fig-flow.png" alt="worktree 的一条龙：按松耦合维度把任务拆开，各自切进独立 worktree 干活，主控 agent 逐个合回分支、合一个验一个、有问题当场修，确认无误后再释放 worktree" /></p>

<h2 id="踩过和要防的坑">踩过和要防的坑</h2>

<p>最常见的坑是忘记清理。worktree 干完忘了删，它就一直杵在那儿占空间。不过这个不致命——功能早合进主干了，丢不了，也不会撞名、不会把 git 状态搞乱，顶多就是占点地方。</p>

<p>但”占点地方”也得治。我在全局军规里专门写了一条：<strong>worktree 里的活，验证、合并都没问题之后，要做清理</strong>。还加了个保险——清理之前先回头查一遍这个 worktree 上的 commits 是不是都已经合进主干了，免得手一快，把还没合的功能连窝端掉。AI 强一点的，这套识别和收尾它自己能做好；弱一点的就容易漏，得我盯着，或者回头专门叫主控 agent 来处理一遍。</p>

<p>到目前为止，我还没真把有用的 worktree 误删过——每次清理前那道”查 commits”的检查，基本兜住了。但如果是人手动去清，这道检查就没了，是有可能误删的，这点得自己留神。</p>

<h2 id="别硬上但有条线不松">别硬上，但有条线不松</h2>

<p>也别把 worktree 当万能。任务很简单、就改几个文件、算不上一个完整功能，那开 worktree 纯属得不偿失，老老实实在一个工作区里改完就行。</p>

<p>但有一条线我是不松的：<strong>不在主干上直接干活</strong>。任何改动都先开新分支，验证无误，再走 PR 合回主干。worktree 也好、单分支也好，都是在给这条线服务——让主干始终是干净、可信的那一份。</p>

<p>最后说回 worktree 本身。可能有人会撇嘴：这不是 git 用了很多年的老功能吗，有什么好讲的。确实是老功能。但在 AI 并行编程这个新场景里，它实实在在解决了”几只手同时改一个项目”的真问题，能起的作用比从前大得多。算是老树开新枝吧。</p>

<p>下一篇我想聊个不太一样的话题：AI 这么能干，能替我们扛掉这么多活，可为什么人到头来还是会累？这中间是哪儿出了岔子。感兴趣的话评论区告诉我。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 干活太慢？可能不是它笨，是你让它一个一个来</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/20/parallel-agents.zh.html" rel="alternate" type="text/html" title="AI 干活太慢？可能不是它笨，是你让它一个一个来" />
    <published>2026-06-20T00:00:00+08:00</published>
    <updated>2026-06-20T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/20/parallel-agents.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/parallel-agents-cover.png" />
    <summary type="html">上一篇讲怎么让 AI 干活更省。这篇讲怎么让它干得更快——一个大任务，与其让 AI 一个模块一个模块串着做，不如拆开撒给好几个 agent 同时上。关键的反直觉点是：并行并不比串行省多少 token，省下来的是时间。</summary>
    <category term="并行" />
    <category term="subagent" />
    <category term="ClaudeCode" />
    <category term="多Agent编排" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/20/parallel-agents.zh.html"><![CDATA[<p>前段时间我盯着 AI 干活，心里琢磨：一个挺大的任务，它一个模块写完再写下一个，我就在旁边干等，等它跑完这个再轮到那个。活儿其实没问题，就是慢——慢在它一直在排队。</p>

<p>后来我想明白一件事：很多模块之间本来就不相干，凭什么要它们一个接一个来？拆开，让几个 agent 同时干，不就完了。</p>

<h2 id="我想要的和它的边界">我想要的，和它的边界</h2>

<p>我要的很简单：同样一件活、花同样的 token，把完成时间大幅缩短。</p>

<p>但得先把边界说在前头——<strong>不是所有任务都能这么拆</strong>。这篇讲的是我自己摸索出来的一套做法，仅供参考。</p>

<h2 id="拆得动的前提架构先得干净">拆得动的前提：架构先得干净</h2>

<p>要让几个 agent 互不打架地同时干活，前提不在 AI，在你的<strong>架构</strong>。</p>

<p>我那个任务能拆，是因为它本身就是好几个模块，彼此通过接口通信，内部实现互不影响——只要照着接口契约来，每一块都能独立完成。说白了就是松耦合、高内聚。这套东西，是我在动手前就和 opus 一起把设计敲定下来的：opus 帮我理、出方案，<strong>拍板的人是我</strong>。</p>

<p>这一步偷不得懒。架构没拆干净就硬上并行，等于把一团乱麻剪成好几段，每段还连着，结果只会更乱。</p>

<h2 id="分工谁主控谁规划谁动手">分工：谁主控、谁规划、谁动手</h2>

<p>设计定了，接下来是排兵布阵。我习惯的分工是这样：</p>

<ul>
  <li><strong>opus 当主控</strong>，统揽全局、派活、最后把关；</li>
  <li><strong>sonnet 做 TDD 规划</strong>，按设计把每个模块要怎么测、怎么实现的路子先铺好；</li>
  <li><strong>haiku 负责写代码和跑测试</strong>，粗活累活交给它，便宜又够用。</li>
</ul>

<p>这套分工其实是上一篇”模型分层”的延续——好钢用在刀刃上。只不过上一篇讲的是省钱，这一篇讲的是这几个角色怎么<strong>配合着一起干</strong>。</p>

<p><img src="/assets/images/posts/parallel-agents-fig-orchestration.png" alt="编排结构：opus 主控在上，往下把独立模块派给 sonnet 规划、haiku 实现，模块内还能再分工；活干完回到 opus 手里复核" /></p>

<h2 id="怎么把它们撒出去">怎么把它们撒出去</h2>

<p>具体落地，我做了三件事：</p>

<ol>
  <li><strong>全局 CLAUDE.md 里写死一句</strong>：”能并行就并行。”这是给所有项目定的默认规矩。</li>
  <li><strong>在 Claude 的设置里调最大并行 subagent 数</strong>——这是那个真正管用的”上限阀门”。</li>
  <li><strong>每次发指令时再多嘱咐一句</strong>：”尽可能并行。”虽然 opus 作为主控自己也会并行派活，但你点一句，它心里更有数。</li>
</ol>

<p>主控把模块派下去，模块内部它还能再分一层工。一层套一层，整个任务就铺开了。</p>

<h2 id="复核这道不能省">复核这道，不能省</h2>

<p>并行跑得快，质量怎么保证？我的做法是——<strong>让主控自己来复核</strong>。</p>

<p>道理很直接：活是它派出去的，每个 subagent 该交什么它一清二楚，由它来检查最顺手。我试过另设一个专门的复核 agent，反而要它从头把任务再理解一遍，多烧一遍 token、还更慢。主控自己复核，省了这道重新理解的开销，又快又准。</p>

<p>复核出问题之后还有个小细节：主控通常会问我一句”这个我直接改掉，还是另起一个 agent 来改？”我一般都回”你直接改”。因为它就是刚才挑出毛病的人，最清楚问题在哪，改起来最直接。</p>

<h2 id="我踩过的两个坑">我踩过的两个坑</h2>

<p><strong>第一个是内存。</strong>一开始我贪心，把最大并行设成了 10 个。结果那会儿手头还并行着别的项目，机器内存一下就被吃干净了。后来我老老实实降到 5 个，反倒清爽——单看这一个任务，5 个并行差不多就是串行的 5 倍效率；再叠上同时在跑的其他任务，整体提速能到 10 倍以上。机器和额度够，这个数还能往上加；不够，就别硬撑。</p>

<p><strong>第二个是别为了拆而拆。</strong>有的模块之间耦合很重，本来就该顺着来，你非要把它掰开并行，几个 agent 互相影响，质量根本兜不住。所以交给 AI 之前我会专门嘱咐一句：”这些模块相互有耦合，不要强行拆分。”好在不少 AI 自己也能识别出来，不会硬拆。真碰上拆不动的，就老老实实交给一个 agent 串行做，或者干脆主控自己一条线做完。</p>

<h2 id="一个反直觉的账">一个反直觉的账</h2>

<p>很多人一听”并行 5 个 agent 同时烧 token”，第一反应是：那 token 不得翻几倍？</p>

<p>我自己算下来不是这么回事。<strong>同样这堆活，你串行做，token 也得消耗差不多的量</strong>——该读的还得读，该写的还得写，并行并没有凭空多干什么。并行真正改变的不是花费，是<strong>周期</strong>：本来要排着队跑完的几段，现在压在同一段时间里一起跑完了。多烧的那一点点 token，换来的是时间成本大幅下降，这买卖太划算。</p>

<p><img src="/assets/images/posts/parallel-agents-fig-parallel.png" alt="串行 vs 并行：同样四段活，串行排着队一段段跑完，周期很长；并行压在同一段时间里齐发，周期砍到约 1/5——但两边读写的 token 总量其实差不多。并行省的是时间，不是花费" /></p>

<p>所以除了那些实在拆不动的，我现在基本是<strong>能并行的全并行</strong>。</p>

<h2 id="复盘三条照着做">复盘：三条照着做</h2>

<p>一，<strong>架构先拆干净，才谈得上并行</strong>。松耦合、高内聚、按接口契约通信——没有这个底子，拆了也是灾难。设计阶段就和 AI 把它定下来。</p>

<p>二，<strong>能并行就全并行，但记得设上限</strong>。这个数不是越大越好，盯着你机器的内存和 AI 的额度来定。我从 10 退到 5，就是被内存教育过的。</p>

<p>三，<strong>高速并行，必须配复核</strong>。而且复核的那个得真懂任务——让派活的主控自己来查，最省也最准；查出问题，就让它直接改。</p>

<p>今天读完就能做的一件事：打开你的全局 CLAUDE.md，加一句”能并行就并行”，再去设置里把最大并行 subagent 数调到一个你机器扛得住的值。下次再丢给它一个能拆的大任务，你会发现它不再排队了。</p>

<p>下一篇我想接着聊一个紧跟着冒出来的问题：几个 agent 同时改代码，凭什么互不打架？这背后靠的是 git worktree——给每个 agent 一块独立的工作区，各改各的，谁也别碍着谁。感兴趣的话评论区告诉我。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 越聊越笨？不是模型菜，是需要上点手段了</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/18/tier-models-trim-context.zh.html" rel="alternate" type="text/html" title="AI 越聊越笨？不是模型菜，是需要上点手段了" />
    <published>2026-06-18T00:00:00+08:00</published>
    <updated>2026-06-18T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/18/tier-models-trim-context.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/tier-models-trim-context-cover.png" />
    <summary type="html">前两篇都在讲怎么在&#39;进上下文之前&#39;省 token。这篇讲会话进行中的两件事——一个决定谁来干活（模型分层），一个决定带多少记忆干活（上下文管理）。AI 写着写着变笨，多半不是模型菜。</summary>
    <category term="token" />
    <category term="ClaudeCode" />
    <category term="模型分层" />
    <category term="上下文管理" />
    <category term="AI编程" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/18/tier-models-trim-context.zh.html"><![CDATA[<p>那天我让 AI 接着写一个功能，写着写着不对劲了：回答越来越慢，话也说得颠三倒四，前面交代过的东西它又问一遍，明明活没干完，它却跟我说”做完了，可以休息一下了”。</p>

<p>我一开始还以为是模型今天状态不好。回头一看上下文，已经撑到 80% 多了——我光顾着往下推，忘了清。清完再问，立刻就顺了，回答又快又在点子上。</p>

<p>那次之后我才认真对待这件事：前两篇我讲的都是怎么在”进上下文之前”把量压下去，那是开工前的事；这篇要讲的是开工之后、一场会话进行当中的两件事——它们决定了同样的活，你花多少 token、跑多快、稳不稳。</p>

<h2 id="同一场对话里其实有两个旋钮">同一场对话里，其实有两个旋钮</h2>

<p>会话进行中能动的旋钮有两个，方向不一样。</p>

<p>一个是横向的：一个任务里，不同的活该交给不同的”脑子”。探索、搜索这种粗活，没必要上最贵的模型；真正要动脑子写代码、做判断的地方，才值得把好模型派上去。这件事我叫它<strong>模型分层</strong>。</p>

<p>另一个是纵向的：同一个脑子，一次到底塞多少东西进去。上下文越胖，模型每一轮都要把这一大坨重新算一遍，又慢又贵，还容易出岔子。管住它涨多快、什么时候清，这件事是<strong>上下文管理</strong>。</p>

<p>这两件事省下来的，落点也分两头：按量计费时省的是真金白银，包月时省的是额度窗口。我两种都在用，所以这两招对我是双份的划算。下面分开说。</p>

<h2 id="别让最贵的模型干所有的活">别让最贵的模型干所有的活</h2>

<p>先说横向那个。</p>

<p>有一次我在一个项目里准备派一批 subagent 干活，当时还是走 API 按量计费，预算有点紧。我索性试了个穷办法：按规划做设计、生成代码这部分交给 Sonnet，单元测试这种相对机械的活丢给更便宜的 Haiku，最后留高级模型（Opus）把控总体进展和质量，做一次复核。</p>

<p>结果出乎意料地好。token 省了不少，速度还明显快了一截——便宜的小模型本来就跑得快，粗活交给它，整条流水线都轻了。最值钱的算力只花在最该花的地方。</p>

<p>这套分法不是死的。哪种活配哪档模型，我直接写进了 CLAUDE.md（用户级、项目级都有），让 AI 每次自己照着分，不用我每回手动指派。要是某个环节我觉得特别关键，也可以临时点名让某个模型专门来做，更精准。原则就一句：别用大炮打蚊子，也别拿弹弓去轰坦克。</p>

<p><img src="/assets/images/posts/tier-models-trim-context-fig-tiering.png" alt="模型分层：一个任务横向拆给不同档位的模型——探索/搜索/单元测试这类粗活交给便宜快的小模型，设计/写代码交给中档模型，总体把控和复核交给高级模型，最贵的算力只花在最该花的地方" /></p>

<h2 id="聊到一半记得给它清清脑子">聊到一半，记得给它清清脑子</h2>

<p>再说纵向那个，也就是开头那场”降智”的正主。</p>

<p>上下文这东西会一路往上涨，你不管它，它就一直胖下去。我自己的做法是给它设了两条线：到 50% 左右我就开始留神，到了 70% 基本就一定清一次。通常的节奏是——手头这项任务一做完，就趁着干净利落清理一遍，让接下来的工作尽量轻装上阵，又准又快。</p>

<p>要说清楚的是，这两条线、还有开头说的”80% 就降智”，都只是我自己的体感和操作习惯，不是什么硬指标。我这么用着顺手，但厂家和别人未必这么看，你完全可以按自己的节奏来。我只是建议：别让它一直涨上去不管。</p>

<p>清理这一步有个坑，必须提一句：<code class="language-plaintext highlighter-rouge">/compact</code>、<code class="language-plaintext highlighter-rouge">/clear</code> 或者干脆开个新会话，确实能让上下文清爽，但弄不好大模型会把之前干的事忘得一干二净，你又得从头解释。我的办法是——清之前，先让它把当前的工作情况记一笔：干到哪了、下一步要做什么、有哪些已经定下来的关键决策。把这段交接写好了再清，新会话上来扫一眼就能接上，不至于一脸懵。</p>

<p>说实话这招我用到现在，接力基本没翻过车。万一真有一次接不上，也不慌——让它重新分析一遍、给我一个结论，我自己验证判断一下就是了，损失不大。</p>

<p><img src="/assets/images/posts/tier-models-trim-context-fig-context.png" alt="会话进行中给上下文'清清脑子'：上下文一路往上涨，到我自己设的警示线（50% 左右）就开始留神，到清理线（70% 左右）就清一次——但这两条线只是我个人的习惯，仅供参考。清理前先让它记一笔交接（干到哪/下一步/关键决策），再 compact 或开新会话，新会话扫一眼就能接上" /></p>

<h2 id="顺带一提读得准才带得少">顺带一提：读得准，才带得少</h2>

<p>上面说的是”已经进来的东西怎么管”。其实还有一层是”让进来的东西本身就少而准”。</p>

<p>我这几个项目里都挂着 codegraph 和 claude-mem 这类工具。简单说，它们让 AI 从”一上来就读源码、全文扫一遍”变成”直接命中核心那几段、续上之前几次会话攒下的记忆”——少扫一大圈，进上下文的东西自然就瘦了。</p>

<p>这里我只是顺带提一下、不展开。一来细节展开就成了给工具站台；二来这类工具市面上未必只有这两个，没准还有更好用的，只不过我没用上、不知道而已。你知道趁手的，照用就行，思路是相通的：让模型读得准，它就不用带那么多东西上路。</p>

<h2 id="几句实话">几句实话</h2>

<p>省归省，得说清楚边界，不然又成了一篇只报喜的稿子。</p>

<p>模型分层最容易让人担心的是：小模型会不会干错？会的。但我留了高级模型在最后复核这一道，小模型偶尔出点岔子，基本都能在复核时兜住，到现在没出过特别大的错。前提是这道复核不能省——省了它，分层省下来的 token 迟早赔进去。</p>

<p>上下文那边也一样：清得太勤、交接写得太潦草，照样会丢东西。所以我不是”到点就无脑清”，而是挑一个任务刚做完、最干净的时间点清，顺手把交接记好。省 token 的本意是砍掉真正的浪费，不是把该带的记忆也一起砍了。</p>

<h2 id="复盘三条照着做就行">复盘：三条照着做就行</h2>

<p>这篇能落地的就三条：</p>

<p>一，别用最贵的模型干所有活。把粗活（探索、搜索、跑测试）分给便宜的小模型，好钢用在写代码和做判断上，留一档高级模型在末尾复核。判断标准写进 CLAUDE.md，让 AI 自己分。</p>

<p>二，别让上下文一直涨。给自己定条线——我是 50% 留神、70% 必清，你可以定你自己的；一个任务做完就清一次，别等它撑到开始降智才动手。</p>

<p>三，清之前先让它写一段交接。干到哪、下一步、关键决策，记好了再 compact 或开新会话，接力就不会断。</p>

<p>你今天读完就能做的一件事：打开你的 CLAUDE.md，把”什么活用什么模型”写死成几条规则；再给自己定一条上下文红线，超了就清。两件事加起来不到十分钟，但接下来每一场对话都在替你省。</p>

<p>下一篇我想聊聊提示词本身——同样一件事，话怎么说，AI 干出来的成色差很多；以及多个 agent 怎么编排着一起干活。感兴趣的话评论区告诉我。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">AI 编码越用越贵？我把 token 砍掉了 82%（附实测数据）</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/17/cut-tokens-82.zh.html" rel="alternate" type="text/html" title="AI 编码越用越贵？我把 token 砍掉了 82%（附实测数据）" />
    <published>2026-06-17T00:00:00+08:00</published>
    <updated>2026-06-17T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/17/cut-tokens-82.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/cut-tokens-82-cover.png" />
    <summary type="html">上一篇说省 token 靠把工具用对，这篇上实测：我翻了下本地的 rtk gain，六千多条命令省了 740 万 token、82%。拆开讲这 82% 是怎么省出来的——压规则文件、用对插件、模型分层。</summary>
    <category term="token" />
    <category term="Claude Code" />
    <category term="AI工程" />
    <category term="成本" />
    <category term="RTK" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/17/cut-tokens-82.zh.html"><![CDATA[<p>上一篇我说，省 token 不在砍文档，在把工具用对。有人接着问：那到底怎么用对？</p>

<p>这篇上实测。先甩个数字：我翻了下本地的 <code class="language-plaintext highlighter-rouge">rtk gain</code>——这是个统计 token 节省的工具——六千多条命令，累计省下 <strong>740 万 token，82%</strong>。不是估的，是它一条条记下来的。</p>

<p>这篇就拆开讲：这 82% 是怎么省出来的。</p>

<h2 id="省-token省在进上下文之前">省 token，省在”进上下文之前”</h2>

<p>先说清楚省在哪。</p>

<p>省 token 的大头，不在你”干了多少活”，在每一轮对话往 AI 上下文里塞了多少东西。模型每轮都要把整个上下文重新算一遍——上下文越肥，每一轮越贵。</p>

<p>所以核心就一句话：让进上下文的东西尽量少、尽量精。</p>

<p>我手上三个抓手：压规则文件、用对插件、模型分层。它们有个共性——<strong>都省在”进上下文之前”，不靠你少干活</strong>。下面一个个拆。</p>

<p><img src="/assets/images/posts/cut-tokens-82-fig-pipeline.png" alt="省 token 省在「进上下文之前」：要喂给 AI 的一堆（几百行命令输出、整库代码、几万字对话历史、臃肿规则文件）先过三道闸——压规则文件、插件自动压、模型分层，真正进上下文的瘦一大圈，每一轮都省" /></p>

<h2 id="抓手一先把你的-claudemd-压瘦">抓手一：先把你的 CLAUDE.md 压瘦</h2>

<p>最容易被忽略、又最该先做的，是压你的 <code class="language-plaintext highlighter-rouge">CLAUDE.md</code>。</p>

<p>CLAUDE.md（规则文件、指令文件，叫法不一）是每次对话都会被塞进上下文的东西。它常驻。你写了多少行，每一轮就重复付多少行的 token。</p>

<p>我自己的 CLAUDE.md 一度写得又臭又长，从用户级到项目级，密密麻麻叮嘱了一大堆。后来回头一看，里头全是重复的唠叨、过时的约定，和一堆”写了跟没写一样”的正确废话。狠心砍掉将近一半，只留真正每次都用得上的硬规则。说到底，同一句话叮嘱三遍，AI 也不会更照办，纯是每轮多花 token。</p>

<p>就这一下，每一轮对话都省。因为它常驻，省的不是一次，是往后每一次。</p>

<p>对话上下文同理：一个聊到几万字的窗口，该清就清，别让早上的事拖到晚上还在每轮重算。</p>

<h2 id="抓手二装几个自动干这事的插件">抓手二：装几个自动干这事的插件</h2>

<p>光靠手动还不够。我装了几个自动压上下文的插件，数据说话。</p>

<p><strong>RTK</strong>（Rust Token Killer）——命令代理。你让 AI 跑 <code class="language-plaintext highlighter-rouge">git status</code>、<code class="language-plaintext highlighter-rouge">ps aux</code>、跑测试，那些输出动辄几百上千行，全塞进上下文巨贵。RTK 在输出进 AI 之前就把它压掉。我的 <code class="language-plaintext highlighter-rouge">rtk gain</code>：六千多条命令省了 740 万 token、82%。省得最狠的是那些高频又没营养的输出——<code class="language-plaintext highlighter-rouge">ps aux</code> 几百行的进程列表，AI 看了纯属遭罪，省 99%；测试日志省 88%；连读文件平均也省两成。</p>

<p><strong>claude-mem</strong>——记忆插件。把跨会话的工作压成结构化记忆，下次不用重新跟它解释项目背景。本会话实测省了 <strong>86%</strong>。全自动，基本不用我管。</p>

<p><strong>codegraph</strong>——代码图谱。它给整个项目的函数、类型、调用关系建了个索引。AI 要找某个函数，查索引就行，不用把一堆文件通读一遍。我的 aitm 项目，它索引了 <strong>246 个文件、3562 个符号</strong>。”查索引”和”把 246 个文件从头读一遍”，差的不是一星半点——前者像翻书的目录，后者像为了回答一个问题先把整本书背下来。</p>

<p>这三个的共性：自动、常驻、省在进上下文之前。装好基本就忘了它，它一直在帮你省。</p>

<p><img src="/assets/images/posts/cut-tokens-82-fig-plugins.png" alt="三个自动帮手的实测数据：RTK 命令代理省 82%（六千多条命令累计省 7.4M token）、claude-mem 记忆压缩省 86%（跨会话不用重讲背景）、codegraph 代码图谱索引 3562 个符号（查索引不通读 246 文件）" /></p>

<h2 id="抓手三别用最贵的模型干所有活">抓手三：别用最贵的模型干所有活</h2>

<p>最后一个，模型分层。</p>

<p>探索、搜索、读文件这种粗活，交给便宜的小模型；真正要动脑的写代码、做判断，才上最强那档。尤其派 subagent 的时候——一个任务拆几个子 agent，粗活那些用小模型，这是省额度的主战场。</p>

<p>我把这套判断标准直接写进了 CLAUDE.md，让 AI 每次自己照着分，不用我每回交代。</p>

<p>这事也不限于 Claude Code。换任何 AI 平台，道理一样：摸清每个模型的能力和价钱，该用哪档用哪档，把贵的算力用在刀刃上。</p>

<h2 id="还有一招让重复进去的部分打折">还有一招：让重复进去的部分打折</h2>

<p>前面三个抓手，都是在”减少进上下文的量”。还有一个角度不一样——prompt caching，它不减量，是让重复进去的部分按折扣计费。</p>

<p>系统提示、不变的规则文件、固定的项目背景，这些每轮都一样的东西，第一次进去算全价，之后命中缓存就打折——而且不是线性的折扣，用好了省得明显。</p>

<p>诀窍是别让该缓存的东西老变：固定不变的放上下文前面、稳住，每轮变动的放后面。结构越稳，缓存命中率越高，折扣吃得越满。</p>

<p>这招我没有像 RTK 那样的实测数字（它省在计费这头，不在 token 数量上），但原理简单、几乎零成本，顺手就能用上。</p>

<h2 id="几句实话这不是免费午餐">几句实话：这不是免费午餐</h2>

<p>得把代价也说清楚，不然就成种草文了。</p>

<p>codegraph 要先建索引，项目大了索引也花时间；claude-mem 的记忆偶尔召回得不那么准，得自己留个心眼；压 CLAUDE.md 更有个度——把真正每次都要用的硬规则也压没了，AI 跑偏返工，那是捡芝麻丢西瓜。</p>

<p>还有，别误会”省 token”是让你少干活。恰恰相反，它砍的是”本来就该省的浪费”——重复的上下文、通读整库、大炮打蚊子。活该干的还得干。</p>

<p>省下来意味着什么，得看你怎么计费（上篇讲过）：包月省的是额度窗口，按量省的是真金白银。两种我都在用，所以这些方法对我是双重的省。</p>

<h2 id="最后">最后</h2>

<p>绕回开头那个 82%。它不是什么神技，是上面这些小事垒出来的：压规则文件、装几个自动插件、分层用模型。每一项单看不起眼，叠在一起，就是六千多条命令省下的 740 万 token。</p>

<p>今天你能做两件事，十分钟就能开始：</p>

<p>一，打开你的 CLAUDE.md，把重复的、过时的、写了等于没写的删掉，看看能从多少行压到多少行。</p>

<p>二，装个 RTK，跑几天，看它的 <code class="language-plaintext highlighter-rouge">gain</code> 给你省了多少——那数字大概率会让你愣一下。</p>

<p>省 token 这事我先聊到这。下次想聊聊，模型分层的细节：怎么判断哪个模型该干什么活，怎么写 CLAUDE.md 让 AI 照着分。还有上下文管理的细节，什么时候该清、怎么精准读文件。感兴趣的话，留言告诉我。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">真正烧 token 的不是文档，是你工具没用对</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/16/tokens-not-docs.zh.html" rel="alternate" type="text/html" title="真正烧 token 的不是文档，是你工具没用对" />
    <published>2026-06-16T00:00:00+08:00</published>
    <updated>2026-06-16T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/16/tokens-not-docs.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/tokens-not-docs-cover.png" />
    <summary type="html">有人说 PDLC 文档一堆，token 不烧爆吗？我想说文档多和 token 烧得凶不是一回事；真想省 token，办法是把工具用对，不是把文档砍掉。</summary>
    <category term="PDLC" />
    <category term="Claude Code" />
    <category term="AI工程" />
    <category term="token" />
    <category term="成本" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/16/tokens-not-docs.zh.html"><![CDATA[<p>用 PDLC 写项目，少不了被问一句：文档一堆，PRD、设计、评审一路下来，token 不烧爆吗？</p>

<p>这问题不是没道理——流程拆得细、每步都留产物，看着确实比”直接让 AI 把代码写出来”费。但要我说，不能把 token 烧得凶这笔账算到文档头上。</p>

<p>先把结论摆前面：第一，文档多和 token 烧得凶，不是一回事；第二，就算真要省 token，办法是把工具用对，而不是把文档砍掉。</p>

<p>我<strong>没</strong>精确测过 token，没拿同一个项目跑两遍”有文档 vs 没文档”的对比，算不出漂亮的百分比。讲的是体感和方法。</p>

<h2 id="烧-token-的锅不在-pdlc">烧 token 的锅，不在 PDLC</h2>

<p>把账算清楚之前，先得找对欠债的。</p>

<p>烧 token 这件事，大多数时候不是 PDLC 带来的，是工具用得不对。这里的”不对”很具体，就三块：</p>

<ul>
  <li><strong>上下文</strong>：该清的不清。一个对话从早开到晚，几万字的历史每一轮都在重算，你问的是新问题，付的是旧账。</li>
  <li><strong>提示词</strong>：写得含糊。AI 来回猜你到底要什么，一次能说清的事，问三轮才对上。</li>
  <li><strong>工具调用</strong>：动不动让它通读整个仓库，其实你只改一个文件。</li>
</ul>

<p>再加一条最常见的：能省 token 的方法压根没用上，然后回头怪流程重。</p>

<p>把这些账不能算到”PDLC 文档多”头上。文档安安静静躺在 <code class="language-plaintext highlighter-rouge">docs/</code> 里，不会主动烧你一个 token；真正烧的是上面这几种用法。</p>

<h2 id="真正烧-token-的是返工">真正烧 token 的，是返工</h2>

<p>我自己用下来，最烧 token 的从来不是生成文档，是返工。</p>

<p>方向错了重写、需求理解偏了推倒、改完这块发现那块连带坏了再回头——这种来回，每一轮都是实打实的 token。生成一份 PRD 是一次性的支出；方向错了返工，是会复利的。</p>

<p>PDLC 这套看着重的流程，恰恰是拿”前面多写一点”换”后面少返工”。用了之后，整体流程稳、最后的产出也稳，不至于来回改。少返工，本身就是少烧 token。</p>

<p>所以我的看法是：文档不是成本，是资产。它把设计决策、为什么这么做都留了痕，可以回溯、可以审计。下次 AI 接手，读一遍文档就懂，不用我从头再讲一遍——这一段省下来的，又是 token。</p>

<p><img src="/assets/images/posts/tokens-not-docs-fig1.png" alt="一次改动若留了文档，AI 读一遍就懂、顺着做完；没留痕就得返工重写，而返工会复利，才是真正烧掉的 token" /></p>

<h2 id="那-token-到底该省在哪">那 token 到底该省在哪</h2>

<p>省 token 不是不写文档，是省在该省的地方。我机器上实际在用的，大概这几处：</p>

<ul>
  <li><strong>降上下文</strong>：该清就清，别让一个对话拖着几万字历史硬跑。</li>
  <li><strong>模型分层</strong>：别大炮打蚊子。探索、搜索、读文件这种粗活，交给便宜的小模型；真正要动脑的分析、写代码，才上最强的那档。</li>
  <li><strong>精准读文件</strong>：只读跟这次改动相关的，别张口就”通读整个项目”。</li>
  <li><strong>prompt caching</strong>：能命中缓存的部分按折扣计费，而且不是 1:1 的线性关系，用好了省得明显。</li>
  <li><strong>给日常命令套层 token 代理</strong>：像 <code class="language-plaintext highlighter-rouge">git status</code> 这类高频操作，输出压一压，积少成多。</li>
  <li><strong>并行</strong>：互不依赖的活儿一次发出去，少几轮往返。</li>
</ul>

<p>这几条里，没有一条是”少写文档”。</p>

<p><img src="/assets/images/posts/tokens-not-docs-fig2.png" alt="省 token 省在三层——上下文层（该清就清 / 精准读相关文件）、模型层（粗活给小模型 / caching 折扣）、工具层（日常命令套代理 / 并行），没一条是「少写文档」" /></p>

<h2 id="它不是所有场景都得上满">它不是所有场景都得上满</h2>

<p>话说回来，PDLC 也不是什么改动都得走全套。</p>

<p>一行小 bug，你要不要 PRD、要不要设计评审？看情况，多数时候没必要走重流程，该简化就简化。判断标准很朴素：这个改动值不值得为它留一份资产。值，就走全套；一次性的小修小补，裁掉几步也没人怪你。</p>

<p>还有”省 token = 省钱”这句，得分计费方式说，不然容易误导人：</p>

<ul>
  <li><strong>包月订阅</strong>那种固定额度的，你省的是<strong>额度窗口</strong>——省下来的 token，让你同样的钱干更多的活。</li>
  <li><strong>按量计费的 API</strong>，你省的是<strong>真金白银</strong>，每个 token 都直接进账单。</li>
</ul>

<p>这两种我都在用。先搞清楚自己是哪一种，才知道”省 token”对你到底意味着什么。</p>

<p><img src="/assets/images/posts/tokens-not-docs-fig3.png" alt="PDLC 不是所有改动都得上满：一次性小修小补就裁剪流程、别上满；要长期维护才走完整 PDLC——这时文档就是资产" /></p>

<h2 id="最后">最后</h2>

<p>总结下，文档多，不等于烧 token；真要省，省在工具用法上，不在文档上。</p>

<p>我最想说的一句：文档是资产，不是成本。想靠”不写文档、直接让 AI 出代码”来省 token，短期看着是省了，项目走不远——没有留痕、没有回溯，过两个月你自己都说不清当初为什么这么设计，那时候的返工，比你省下的那点文档 token 烧得狠多了。</p>

<p>今天能做的一个动作：回头看自己有没有把省 token 的方法用上——上下文降了吗？模型还是一律上最强的，没分层？该省的成本省了吗？顺手再想想，PDLC 你是不是也没用好。</p>

<p>省 token 这事拆开能讲的还有不少，比如模型到底怎么分层、上下文什么时候该清、caching 怎么吃到折扣，下次挑一个聊细一点。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">PDLC 1.1：v1.0 在产物形状上犯的两个错误</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/08/pdlc-v1-1.zh.html" rel="alternate" type="text/html" title="PDLC 1.1：v1.0 在产物形状上犯的两个错误" />
    <published>2026-06-08T00:00:00+08:00</published>
    <updated>2026-06-08T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/08/pdlc-v1-1.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/pdlc-v1-1-cover.png" />
    <summary type="html">v1.0 解决了纵向问题：让单个 feature 的各阶段有序推进。v1.1 修了两个横向问题——产物类型混淆（ledger vs surface）和 feature 间关系缺失。</summary>
    <category term="PDLC" />
    <category term="Claude Code" />
    <category term="AI工程" />
    <category term="工作流" />
    <category term="开源" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/08/pdlc-v1-1.zh.html"><![CDATA[<p>用 PDLC 工作了几个月之后，我在一个项目的 <code class="language-plaintext highlighter-rouge">docs/</code> 目录里看到了这些文件：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docs/
├── architecture-2025-10-03.md
├── architecture-2025-11-14.md
├── architecture-2025-12-01.md
├── architecture-2026-01-08.md
└── architecture-2026-02-19.md
</code></pre></div></div>

<p>五份架构文档，每份都带日期。我不知道哪份是现在的架构。</p>

<p>这不是用户操作失误，是 PDLC v1.0 的设计失误。v1.0 把所有阶段产物都当成 append-only 的历史记录来处理。这个逻辑用在 PRD 上是对的，用在架构总览上就产生了这堆垃圾。</p>

<p>v1.1 修了两个这样的设计错误。</p>

<h2 id="错误一没有区分两种完全不同的产物">错误一：没有区分两种完全不同的产物</h2>

<p>做这件事之前，我先想清楚了一个分类：<strong>产物在时间维度上是什么形状？</strong></p>

<p>有一类产物，记录的是”发生了什么”。PRD、设计决策、会议纪要——每份都是历史证据，不能篡改，只能追加。你不会去改一份三个月前的 PRD 让它变成”最新的”，那会毁掉决策链。这类产物叫 <strong>ledger 型</strong>（账本）。</p>

<p>另一类产物，记录的是”现在是什么状态”。架构总览、团队编码规范、API 契约——永远只有一份”现在有效的版本”。你维护它的方式是就地更新，而不是另存一份带日期的副本。这类产物叫 <strong>surface 型</strong>（当前状态面）。</p>

<p>v1.0 没有这个区分，AI 默认给所有产物追加日期戳。于是 <code class="language-plaintext highlighter-rouge">/pdlc-arch</code> 每次运行都生成 <code class="language-plaintext highlighter-rouge">architecture-YYYY-MM-DD.md</code>，三个月后变成上面那五份文件的局面。</p>

<p>更糟的是规范文档。团队有一份 <code class="language-plaintext highlighter-rouge">coding-style.md</code>，被改了一次之后变成了 <code class="language-plaintext highlighter-rouge">coding-style-v2.md</code>。然后是 <code class="language-plaintext highlighter-rouge">coding-style-v3.md</code>。没人知道该 follow 哪份，干脆都不看。</p>

<p>v1.1 的修法很直接：让 AI 在生成产物之前先判断类型。ledger 型继续 append-only，加日期戳、不能就地改；surface 型则强制就地维护一个固定路径的文件——<code class="language-plaintext highlighter-rouge">/pdlc-arch</code> 只维护 <code class="language-plaintext highlighter-rouge">docs/ARCHITECTURE.md</code> 这一份，每次更新就地改，遗留的带日期文件自动归档到 <code class="language-plaintext highlighter-rouge">docs/archive/</code>。</p>

<h2 id="新指令pdlc-standard">新指令：<code class="language-plaintext highlighter-rouge">/pdlc-standard</code></h2>

<p>规范文档是 surface 型产物里最典型的一类，所以 v1.1 为它单独加了一个指令。</p>

<p><code class="language-plaintext highlighter-rouge">/pdlc-standard</code> 管理 <code class="language-plaintext highlighter-rouge">docs/00_standards/</code> 目录下的规范文档。它做几件事：</p>

<p><strong>add</strong>：新建一份规范，命名强制用语义路径（<code class="language-plaintext highlighter-rouge">coding/style</code>、<code class="language-plaintext highlighter-rouge">api/versioning</code>），禁止带版本号或日期。
<strong>edit</strong>：就地修改现有规范，自动记录修改时间，但文件名不变。
<strong>archive</strong>：如果一份规范彻底废弃，可以归档，但已有引用不会断。
<strong>index</strong>：输出当前所有有效规范的索引，用于 onboarding 或 review。</p>

<p>最关键的一条约束在提示词里是硬写的：<strong>如果 AI 在生成 <code class="language-plaintext highlighter-rouge">coding-style-v2.md</code> 这样带版本号的规范文件，必须中止并提示用户用 <code class="language-plaintext highlighter-rouge">edit</code> 子命令替代。</strong></p>

<p>这条约束从根上杜绝了版本号蔓延。</p>

<h2 id="错误二feature-之间互不相识">错误二：feature 之间互不相识</h2>

<p>v1.0 的状态机管理每个 feature 是一个独立的闭环：PRD → 设计 → TDD → 实现 → 评审 → 发布。这个纵向的流程设计是对的。</p>

<p>但现实里 feature 从来不是孤立的。</p>

<p>我在 aitm 项目里遇到了典型场景：<code class="language-plaintext highlighter-rouge">feature/安全层</code> 被 <code class="language-plaintext highlighter-rouge">feature/工具调用</code> 依赖。改安全层的黑名单规则，实际上会影响工具调用的确认逻辑。但 PDLC 的状态机只看单个 feature 的进度，没有任何地方告诉我这两个 feature 之间有关系。</p>

<p>结果是每次改安全层，我都要靠记忆去想”这会不会影响工具调用那边”——这是把本该系统记住的事情放进了人脑，不合理。</p>

<h2 id="新指令pdlc-relate">新指令：<code class="language-plaintext highlighter-rouge">/pdlc-relate</code></h2>

<p>v1.1 加了 <code class="language-plaintext highlighter-rouge">/pdlc-relate</code>，专门管理 feature 之间的关系。</p>

<p>关系类型有六种：</p>

<table>
  <thead>
    <tr>
      <th>类型</th>
      <th>含义</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">extends</code></td>
      <td>这个 feature 是另一个的扩展</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">depends_on</code></td>
      <td>这个 feature 依赖另一个的实现</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">supersedes</code></td>
      <td>这个 feature 替代了另一个（另一个应该归档）</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">resolves</code></td>
      <td>这个 feature 修了另一个 feature 引入的问题</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">conflicts_with</code></td>
      <td>两个 feature 有已知冲突，不能同时激活</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">relates_to</code></td>
      <td>宽泛的关联，不属于以上类型</td>
    </tr>
  </tbody>
</table>

<p>建立关系很简单：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-relate <span class="nb">set </span>feature/工具调用 depends_on feature/安全层
</code></pre></div></div>

<p>但杀手锏是另一个子命令：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-relate impact feature/安全层
</code></pre></div></div>

<p>这个命令输出：直接依赖这个 feature 的有哪些、通过传递关系间接影响的有哪些、历史上被它替代或解决的有哪些。一个命令，把改动的辐射范围摊开来看。</p>

<p><img src="/assets/images/posts/pdlc-relation-impact-diagram.png" alt="PDLC feature 关系图：改 user-auth 时的 impact 辐射范围（红=要改的，黄=直接影响，绿=传递/历史）" /></p>

<p>做这个功能的时候有个反直觉的选择：关系数据存在 <code class="language-plaintext highlighter-rouge">docs/.pdlc/</code> 下面的结构化文件里，而不是某种专有数据库格式。理由是，这些关系是”团队的知识”，应该随代码一起提交、review、合并。如果存在黑盒里，换机器或换人就丢了。</p>

<h2 id="两个功能一起踩的坑">两个功能一起踩的坑</h2>

<p>surface 型产物的迁移，比我预想的麻烦。已有项目里那些旧的带日期架构文件，PDLC 怎么知道哪份是”最新的”？答案是不猜——<code class="language-plaintext highlighter-rouge">/pdlc-arch</code> 检测到遗留文件时会停下来问你：”我看到 <code class="language-plaintext highlighter-rouge">architecture-2026-02-19.md</code>，要用这份作为 <code class="language-plaintext highlighter-rouge">ARCHITECTURE.md</code> 的初始内容吗？”一个确认步骤，避免自动迁移出错。</p>

<p>关系类型的设计，我也走了点弯路。第一版草稿里只有三种关系：依赖、扩展、冲突。拿真实项目试跑了一遍，发现有不少 feature 之间的联系根本描述不了——比如”这个 feature 修了那个 feature 的遗留问题”，归不进三类里的任何一个。最后扩展到六种，六种是够用的边界，再多就变成分类游戏了。</p>

<h2 id="当前状态">当前状态</h2>

<p>v1.1 已经用在了三个项目上：aitm、pdlc-skills 自身、一个朋友的 SaaS 项目。</p>

<p>ledger/surface 分离解决了最让人抓狂的问题——<code class="language-plaintext highlighter-rouge">docs/</code> 不再是时间轴，是可以查找信息的地方。<code class="language-plaintext highlighter-rouge">/pdlc-relate impact</code> 在做跨 feature 的改动时每次都在用，省了不少”这会不会影响那边”的回溯排查。</p>

<p>33 个标准化阶段的核心结构没有变，v1.1 只在产物形状和关系建模上做了修正。如果你已经在用 v1.0，升级基本上是无感的——除了 <code class="language-plaintext highlighter-rouge">/pdlc-arch</code> 下次运行时会问你迁移旧文件。</p>

<h2 id="安装--升级">安装 / 升级</h2>

<p><strong>首次安装</strong>（需要 Claude Code）：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/claude <span class="nb">install </span>kanfu-panda/pdlc-skills
</code></pre></div></div>

<p><strong>升级已有安装</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash &lt;<span class="o">(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh<span class="o">)</span> <span class="nt">--upgrade</span>
</code></pre></div></div>

<p>源码在 <a href="https://github.com/kanfu-panda/pdlc-skills">GitHub</a>，MIT 开源。用了遇到问题，Issues 开着，我在看。</p>

<h2 id="下一篇">下一篇</h2>

<p>PDLC 把流程拆细、步步留产物——好处是不跳步、不跑偏，代价是它比”直接让 AI 写”更吃 token。今天就有人跟我说：”文档一多，token 不就烧得更凶？”</p>

<p>这事值得单独聊一篇：这笔 token 账到底该怎么算（我没精确测过，但有些反直觉的地方），以及在用这套重流程的同时，我是怎么把花销压下去的。省 token 说到底就是省钱，但也不只是省钱。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">aitm 1.0：AI 在副驾，方向盘在你手里</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/06/07/aitm-v1.zh.html" rel="alternate" type="text/html" title="aitm 1.0：AI 在副驾，方向盘在你手里" />
    <published>2026-06-07T00:00:00+08:00</published>
    <updated>2026-06-07T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/06/07/aitm-v1.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <summary type="html">aitm 1.0 正式发布。5.3 MB 二进制、3-5ms 冷启动、6 家 LLM provider、4 层安全门。这篇讲设计哲学、工具调用怎么做的，以及一路踩的坑。</summary>
    <category term="aitm" />
    <category term="终端" />
    <category term="AI" />
    <category term="macOS" />
    <category term="Windows" />
    <category term="开源" />
    <category term="Tauri" />
    <category term="Rust" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/06/07/aitm-v1.zh.html"><![CDATA[<p>有一段时间，我的工作流是这样的：在终端里用 Claude Code 写代码，AI 改了一堆文件，但我在同一个终端窗口里没法同时看被修改的文件，得不停地切窗口、切应用，打断了节奏。</p>

<p>我想要的其实很简单：<strong>在一个窗口里，终端和 AI 都有，能看到文件也能看到对话</strong>，不需要来回跳。</p>

<p>做着做着，就做成了 aitm。</p>

<hr />

<h2 id="一个决定ai-是副驾不是驾驶员">一个决定：AI 是副驾，不是驾驶员</h2>

<p>在动手做之前，我先想清楚了一件事：<strong>我不想要一个替我操作的终端工具</strong>。</p>

<p>市面上很多 AI 终端工具的逻辑是”让 AI 决定跑什么，用户来确认”。AI 坐驾驶席，你坐副驾确认每一步。这个模式在演示里看起来很酷，用起来反而让我有点不安——我不确定 AI 下一步要干什么，也不容易判断该不该点”OK”。</p>

<p>aitm 反过来：<strong>AI 是副驾，你开车</strong>。AI 能看见你的环境、读你的文件、提出建议，但执行序列是固定的：AI 建议 → 你决定 → 然后才发生。</p>

<p>这不只是态度上的区别，它逼出了整个工具调用设计的边界：只读操作可以自动跑，执行操作必须等人。</p>

<p><img src="/assets/images/posts/aitm-screenshot-01-main-interface.png" alt="aitm 整体界面：左侧文件树、中间终端、右侧 AI 侧栏" /></p>

<p>这张图是 aitm 的默认布局——文件树、终端、AI 侧栏三栏并排。AI 不是一个弹出窗口，是和终端并排的同事。</p>

<hr />

<h2 id="为什么选-tauri--rust">为什么选 Tauri + Rust</h2>

<p><strong>技术选型上，我直接排掉了 Electron。</strong></p>

<p>不是因为对 Electron 有偏见，是因为一个终端工具空载 150 MB 内存、二进制一百多 MB，这在感知上就是”网页套壳”，不是”系统工具”。既然要做，就做对的那种。</p>

<p>直接上 <a href="https://v2.tauri.app/">Tauri 2</a> + Rust，从一开始就奔着这个方向。前后花了大约两个月。结果是：</p>

<table>
  <thead>
    <tr>
      <th>指标</th>
      <th>Electron（如果走那条路）</th>
      <th>aitm 1.0 (Tauri)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>二进制大小</td>
      <td>140 MB+</td>
      <td><strong>5.3 MB</strong></td>
    </tr>
    <tr>
      <td>冷启动时间</td>
      <td>2-4 秒</td>
      <td><strong>3-5 ms</strong></td>
    </tr>
    <tr>
      <td>空载内存</td>
      <td>~150 MB</td>
      <td><strong>~30 MB</strong></td>
    </tr>
  </tbody>
</table>

<p>这组数字不需要太多解释。5.3 MB 和 140 MB 是两种截然不同的工具质感。</p>

<p>架构上：React 19 前端通过 Tauri IPC 和 Rust PTY 层通信，AI 侧栏、工具执行管道、安全层全部在 Rust 侧实现。前端负责 UI，Rust 负责可信度高的那些事。</p>

<hr />

<h2 id="工具调用闭环只读流动执行等人">工具调用闭环：只读流动，执行等人</h2>

<p>核心设计是一个闭环，逻辑很简单：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>你问 AI → AI 判断需要上下文 → 自动调用只读工具 → 结果喂回 AI → AI 回答
如果 AI 想执行命令 → 停下来 → 等你确认 → 你批准才发生
</code></pre></div></div>

<p>具体的只读工具有四个：<code class="language-plaintext highlighter-rouge">list_files</code>（列目录）、<code class="language-plaintext highlighter-rouge">read_file</code>（读文件）、<code class="language-plaintext highlighter-rouge">get_terminal_history</code>（拿终端历史）、<code class="language-plaintext highlighter-rouge">search_history</code>（搜历史命令）。这四个可以无间断自动执行，AI 不需要问你能不能看。</p>

<p><code class="language-plaintext highlighter-rouge">run_command</code>（执行命令）是另一类。<strong>它没有静默执行的代码路径</strong>，每次调用都产生一个确认弹窗。</p>

<p>AI 还会自动获得它没开口要的上下文：当前 git 分支、工作目录、dirty 状态、监听端口。一打开侧栏，AI 就知道你在哪个项目、现在什么状态。</p>

<p><img src="/assets/images/posts/aitm-screenshot-04-ai-sidebar.png" alt="AI 侧栏：AI 感知到当前项目上下文，主动问候" /></p>

<p>这张图是 AI 侧栏——AI 在对话开始时已经知道当前项目，不需要你先解释”我在做什么”。</p>

<hr />

<h2 id="4-层安全门把-llm-幻觉拦在外边">4 层安全门，把 LLM 幻觉拦在外边</h2>

<p>AI 工具执行是我花时间最多的部分。担心的不是什么高明的攻击，而是 LLM 会幻觉出 <code class="language-plaintext highlighter-rouge">rm -rf</code>——这是很糟糕的下午。</p>

<p>四道关按顺序过：</p>

<p><strong>L1 黑名单 regex</strong>：硬编码约 50 个禁止模式，包括 <code class="language-plaintext highlighter-rouge">rm -rf /</code>、fork bomb、<code class="language-plaintext highlighter-rouge">dd if=/dev/zero</code> 等。快速、零配置、无条件拦截。就算用户点了”我确认”也没用，这些命令不会跑。</p>

<p><strong>L2 启发式风险评分</strong>：命令字符串被一组信号打分——是否触碰 <code class="language-plaintext highlighter-rouge">/</code>、是否用重定向 <code class="language-plaintext highlighter-rouge">&gt;</code>、是否 pipe 到 <code class="language-plaintext highlighter-rouge">sh</code>、是否引用系统目录。输出 <code class="language-plaintext highlighter-rouge">DESTRUCTIVE / HIGH / LOW</code> 三档，显示在确认弹窗里。</p>

<p><strong>L3 项目作用域白名单</strong>：每个项目可以配一个 <code class="language-plaintext highlighter-rouge">globset</code>，限定 AI 可触碰的路径模式。超出作用域的操作在 L4 之前就被标记。可选，但建议开。</p>

<p><strong>L4 用户确认弹窗</strong>：每个 <code class="language-plaintext highlighter-rouge">run_command</code> 调用都有 modal，显示完整命令、风险等级、作用域检查结果。没有自动放行。</p>

<p>L1 和 L2 在 Rust 中同步执行，零异步开销。L4 是 IPC handler 里的硬门，AI 层没有绕过它的代码路径。</p>

<hr />

<h2 id="踩的三个坑哪个都不简单">踩的三个坑，哪个都不简单</h2>

<p><strong>坑一：Tauri IPC 学习曲线</strong></p>

<p>Tauri 2 的 IPC 通信模型、Rust PTY 层的集成、安全层在 Rust 侧的重建，每一块都是新东西。以为两个月够，实际正好卡着完成。Tauri 2 的 WebView 在 Windows 上的行为和 macOS 上有差异，这也是当前 Windows 版本测试没有 macOS 充分的根本原因。</p>

<p><strong>坑二：Apple 公证卡了将近一周</strong></p>

<p>dmg 打好了，签名通过了，提交 Apple 公证之后——卡住了。提交没有错误，状态一直 pending，重提交也一样。</p>

<p>花了将近一周定位，最后发现是新 Apple 开发者账户首次公证需要在 Apple 后台单独配置某些权限，这个配置步骤在 Apple 文档里没有显眼的说明。不是代码问题，是账户状态问题。</p>

<p>教训：新账户首次走公证流程，多预留一周缓冲。</p>

<p><strong>坑三：Windows 版本测试不足</strong></p>

<p>这是我要坦诚说的一个当前限制：aitm 1.0 在 macOS Apple Silicon 上测试很充分，Windows x86_64 和 ARM64 版本功能可用，但边界场景的测试覆盖没有达到同等水平。</p>

<p>如果你在 Windows 上跑遇到问题，Issues 是真的在看的。</p>

<p><img src="/assets/images/posts/aitm-screenshot-02-split-layout.png" alt="三栏分屏：终端 + 文件预览 + 浏览器并排" /></p>

<p>这张图是分屏模式——终端、文件编辑器、内置浏览器可以三栏并排，不需要切应用。</p>

<hr />

<h2 id="当前状态能做什么不能做什么">当前状态：能做什么，不能做什么</h2>

<p><strong>能做的</strong>：</p>

<ul>
  <li>终端 + AI 侧栏 + 文件树一体，项目作用域感知</li>
  <li>4 层安全门保护 <code class="language-plaintext highlighter-rouge">run_command</code> 执行</li>
  <li>本地 SQLite 持久化，不需要账号，不上传数据</li>
  <li>6 家 LLM provider：OpenAI、Anthropic、DeepSeek、通义千问（Qwen/DashScope）、智谱（Zhipu）、Moonshot（Kimi）</li>
  <li>8 套主题 + 中英 i18n</li>
  <li>CodeMirror 文件编辑器，分屏内置</li>
  <li>macOS Developer ID 公证，Gatekeeper 不报警</li>
</ul>

<p><strong>当前版本的限制</strong>：</p>

<ul>
  <li>Windows 测试覆盖不如 macOS 充分（这是已知的，1.x 里会改善）</li>
  <li>没有 Linux 版本（Tauri 可以支持，但没精力做充分测试，宁可不发）</li>
  <li>AI provider 的流式响应在极慢网络下有时会有截断，不算稳定</li>
</ul>

<p><img src="/assets/images/posts/aitm-screenshot-03-settings.png" alt="设置页：外观 / 主题 / 布局选项" /></p>

<p>这张图是设置页——8 套主题和布局选项都在这里，中英文切换也在这里。</p>

<hr />

<h2 id="下载">下载</h2>

<p><a href="https://github.com/kanfu-panda/aitm/releases/tag/v1.0.0">GitHub Release v1.0.0</a></p>

<p>平台：macOS Apple Silicon、Windows x86_64、Windows ARM64。</p>

<p>源码在 <a href="https://github.com/kanfu-panda/aitm">GitHub</a>，Apache 2.0 开源。有问题、想聊安全模型设计，或者在 Windows 上遇到 bug，Issues 和 Discussions 都开着，我在看。</p>

<hr />

<h2 id="下一篇">下一篇</h2>

<p>aitm 本身是用 PDLC 工作流开发的——PDLC 是我把多年开发经验整理提炼、今年推出的一套专为 AI 辅助开发设计的 Claude Code 插件。</p>

<p>下一篇打算写：<strong>PDLC 的设计哲学，以及”AI 辅助开发”和”让 AI 替你开发”的区别在哪</strong>。如果你对怎么在真实项目里落地 AI 辅助有兴趣，那篇应该有东西可以参考。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">arcade：博客里挂了个浏览器街机模拟器</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/05/16/arcade-intro.zh.html" rel="alternate" type="text/html" title="arcade：博客里挂了个浏览器街机模拟器" />
    <published>2026-05-16T00:00:00+08:00</published>
    <updated>2026-05-16T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/05/16/arcade-intro.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <summary type="html">博客 /arcade/ 下挂了一个浏览器街机模拟器。支持 MAME 和数十种主机，你拖入自己本地的游戏文件就能玩，全程在浏览器里完成，零上传。</summary>
    <category term="arcade" />
    <category term="街机模拟器" />
    <category term="EmulatorJS" />
    <category term="WebAssembly" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/05/16/arcade-intro.zh.html"><![CDATA[<h2 id="这是什么">这是什么</h2>

<p>博客新增了一个 <a href="/arcade/">arcade 产品页</a>——一个跑在浏览器里的街机模拟器，
基于 EmulatorJS（WebAssembly 移植的 RetroArch）。</p>

<p>支持的系统包括：MAME 街机、NES / SNES、GB / GBC / GBA、N64、Sega Genesis、NDS、PCE 等几十种。</p>

<h2 id="能做什么">能做什么</h2>

<ul>
  <li>拖入本地游戏文件 → 自动识别对应模拟器核心 → 开玩</li>
  <li><strong>存档跨会话</strong>保留，下次打开继续玩</li>
  <li><strong>手柄即插即用</strong>，F2 进入按键设置</li>
  <li>全屏模式</li>
  <li>街机 ZIP 可在 <strong>MAME ↔ FBNeo</strong> 两个核心间一键切换（应对不同 romset 兼容性）</li>
</ul>

<h2 id="怎么用">怎么用</h2>

<ol>
  <li>打开 <a href="/arcade/play/" target="_blank" rel="noopener">arcade 启动页</a></li>
  <li>把你<strong>本地的游戏文件</strong>拖进页面（NES、SNES、GB、街机 ZIP 等）</li>
  <li>部分游戏（Neo-Geo 等）需要拖入对应 BIOS 文件，同入口</li>
  <li>点游戏卡片上的「开始游戏」</li>
</ol>

<h2 id="关于本地与隐私">关于本地与隐私</h2>

<p>你拖入的文件<strong>全程保存在你浏览器自己的存储里</strong>，不会上传到任何服务器。
这是浏览器 File API + IndexedDB 的标准行为，跟我无关——
GitHub Pages 也只能吐静态文件，根本接收不了上传。</p>

<h2 id="注意">注意</h2>

<p>⚠️ 本工具仅供个人娱乐，合法用途。请使用您自己合法拥有的游戏文件。</p>

<h2 id="试玩">试玩</h2>

<p><a href="/arcade/play/" target="_blank" rel="noopener">→ 启动 arcade</a></p>

<p>玩开心。</p>
]]></content>
  </entry>
  <entry xml:lang="zh-CN">
    <title type="html">PDLC：把 AI 写代码从&#39;软规范&#39;升级为&#39;硬契约&#39;</title>
    <link href="https://kanfu-panda.github.io/zh/blog/2026/05/15/pdlc-intro.zh.html" rel="alternate" type="text/html" title="PDLC：把 AI 写代码从&#39;软规范&#39;升级为&#39;硬契约&#39;" />
    <published>2026-05-15T00:00:00+08:00</published>
    <updated>2026-05-15T00:00:00+08:00</updated>
    <id>https://kanfu-panda.github.io/zh/blog/2026/05/15/pdlc-intro.zh.html</id>
    <author><name>kanfu-panda</name></author>
    <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://kanfu-panda.github.io/assets/images/posts/pdlc-intro-cover.png" />
    <summary type="html">AI 助手说&#39;我把功能做完了&#39;但 PRD 只活在对话里、测试&#39;我等会儿补&#39;、跨会话就忘了某功能进展到哪。这些痛点 PDLC 通过 31 个标准化阶段 + Iron Law 5 不变量解决。从动机、实现、应用到能达到的效果，一篇看完。</summary>
    <category term="PDLC" />
    <category term="Claude Code" />
    <category term="AI 工程" />
    <category term="工作流" />
    <category term="开源" />
    <category term="MIT" />
    <content type="html" xml:base="https://kanfu-panda.github.io/zh/blog/2026/05/15/pdlc-intro.zh.html"><![CDATA[<h2 id="动机ai-写代码的几个老毛病">动机：AI 写代码的几个老毛病</h2>

<p>你有没有遇到过这种场景：</p>

<ul>
  <li>让 AI 做一个新功能，它说”好的，我把功能做完了”，<strong>但 PRD 只活在你刚才那段对话里</strong>。一关窗口就找不到了。下次想回顾”当时为啥这么设计”，只剩 git log 里的 commit message。</li>
  <li>让它做完功能再补测试，它<strong>轻轻松松写出 100% 通过的”测试”</strong>——因为代码已经写完了，测试是回头照着实现配齐的，<strong>对设计没有约束力</strong>。</li>
  <li>跳过设计阶段直接干，<strong>架构腐烂悄无声息</strong>。三个月后你想加新功能，发现现在的代码已经不是当初规划的那种结构了。</li>
  <li>跨会话<strong>没有任何记忆</strong>。某个功能”我们做到哪了？”——靠你自己记。</li>
</ul>

<p>这些问题的本质是：<strong>AI 助手把工程流程当成”软约定”在执行</strong>——能省就省、能跳就跳。</p>

<p><a href="/zh/pdlc/">PDLC</a>（<strong>P</strong>roduct <strong>D</strong>evelopment <strong>L</strong>ife <strong>C</strong>ycle）这个 Claude Code plugin 的目标就一句话：<strong>把这些软约定升级成硬契约</strong>。让 AI 一旦走这套工作流，就<strong>没法</strong>跳过 PRD、没法不先写测试、没法忘记当前阶段。</p>

<h2 id="实现三层架构--iron-law--状态机">实现：三层架构 + Iron Law + 状态机</h2>

<h3 id="31-条命令分三层">31 条命令，分三层</h3>

<table>
  <thead>
    <tr>
      <th>层级</th>
      <th style="text-align: right">命令数</th>
      <th>角色</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>第一层 · 入口</td>
      <td style="text-align: right">3</td>
      <td><code class="language-plaintext highlighter-rouge">/pdlc-feature</code>、<code class="language-plaintext highlighter-rouge">/pdlc-fix</code>、<code class="language-plaintext highlighter-rouge">/pdlc-status</code> —— 一句话 prompt 驱动整条链路</td>
    </tr>
    <tr>
      <td>第二层 · 阶段</td>
      <td style="text-align: right">11</td>
      <td><code class="language-plaintext highlighter-rouge">/pdlc-prd</code>、<code class="language-plaintext highlighter-rouge">/pdlc-tdd</code>、<code class="language-plaintext highlighter-rouge">/pdlc-implement</code>、<code class="language-plaintext highlighter-rouge">/pdlc-review</code>、<code class="language-plaintext highlighter-rouge">/pdlc-ship</code> 等 —— 单独控制某个阶段</td>
    </tr>
    <tr>
      <td>第三层 · 工具</td>
      <td style="text-align: right">17</td>
      <td>UI 设计 / 数据库设计 / 架构 / 安全 / 性能 / 代码脚手架 / i18n / 迁移等专项工具</td>
    </tr>
  </tbody>
</table>

<p>日常用最多的是第一层：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 在 Claude Code 里</span>
/pdlc-feature 给登录加图形验证码
</code></pre></div></div>

<p>这一条命令 PDLC 会带你走完 PRD → 设计 → TDD 红灯 → 实现 → 评审 → 发布。每个阶段都<strong>强制</strong>满足下面这套”铁律”。</p>

<h3 id="iron-law5-条不变量">Iron Law：5 条不变量</h3>

<p>每一条<strong>产出产物</strong>的第一/第二层阶段都得满足：</p>

<ol>
  <li><strong>产物必须落盘</strong> —— 不能只写在对话里，必须有个真实文件在 <code class="language-plaintext highlighter-rouge">docs/</code> 目录下</li>
  <li><strong>状态机必须更新</strong> —— 每个阶段结束都得写 <code class="language-plaintext highlighter-rouge">docs/.pdlc-state/&lt;feature-id&gt;.json</code></li>
  <li><strong>测试先行</strong> —— 实现阶段被失败的测试挡住，没红灯不能写代码</li>
  <li><strong>自检</strong> —— 每个阶段交接前自己跑一遍审计</li>
  <li><strong>自动修复仅一轮</strong> —— 自动修复死循环？没门，最多一轮，搞不定就抛给人</li>
</ol>

<p>这五条是<strong>强约束</strong>。第一层 / 第二层产物阶段不能跳，跳了 AI 自己会自检失败。</p>

<h3 id="状态机每个功能一份">状态机：每个功能一份</h3>

<p>每开一个新功能（<code class="language-plaintext highlighter-rouge">/pdlc-feature ...</code>），PDLC 会分配一个 ID 如 <code class="language-plaintext highlighter-rouge">F20260515-01</code>，对应文件：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docs/.pdlc-state/F20260515-01.json
</code></pre></div></div>

<p>这文件长这样（示意）：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"feature_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"F20260515-01"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"slug"</span><span class="p">:</span><span class="w"> </span><span class="s2">"captcha-login"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"current_stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"implement"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"history"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="nl">"stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"prd"</span><span class="p">,</span><span class="w"> </span><span class="nl">"ts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-15T10:30:00Z"</span><span class="p">,</span><span class="w"> </span><span class="nl">"self_audit"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8/8 passed"</span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="nl">"stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"design"</span><span class="p">,</span><span class="w"> </span><span class="nl">"ts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-15T11:05:00Z"</span><span class="p">,</span><span class="w"> </span><span class="nl">"self_audit"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6/6 passed"</span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="nl">"stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tdd"</span><span class="p">,</span><span class="w"> </span><span class="nl">"ts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-15T11:40:00Z"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tests_written"</span><span class="p">:</span><span class="w"> </span><span class="mi">14</span><span class="p">,</span><span class="w"> </span><span class="nl">"all_failing"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"next_step"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/pdlc-implement"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>意义在哪？<strong>跨会话也知道每个功能进展到哪</strong>。明天打开 Claude Code，<code class="language-plaintext highlighter-rouge">/pdlc-status</code> 一眼看到：”F20260515-01 在 implement 阶段，下一步跑 review”。不再依赖你的记性，也不靠 AI 的”上下文窗口”。</p>

<h3 id="产物目录约定">产物目录约定</h3>

<p>PDLC 在你项目里读写这些目录：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docs/00_standards/coding/                          # 编码规范（只读）
docs/01_requirements/prd/                          # PRD
docs/02_design/{api,database,architecture,ui-ux}/  # 技术设计
docs/03_development/                               # 开发者手册
docs/04_testing/{unit-tests,e2e-tests,defects,...} # 测试 &amp; 缺陷
docs/05_deployment/                                # 部署文档
docs/06_tasks/                                     # 阶段内任务跟踪
docs/07_reviews/{doc,code,design,retro}/           # 评审记录
docs/.pdlc-state/&lt;feature-id&gt;.json                 # 每功能一份状态机
</code></pre></div></div>

<p>所有东西都是磁盘上的真实文件。可以 <code class="language-plaintext highlighter-rouge">git diff</code> 看一周前 AI 在 PRD 阶段到底写了啥。</p>

<h2 id="应用3-个典型场景">应用：3 个典型场景</h2>

<h3 id="场景-1端到端做一个新功能">场景 1：端到端做一个新功能</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Claude Code 里</span>
/pdlc-feature 给用户登录加手机号验证
</code></pre></div></div>

<p>PDLC 自动串联：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>→ 分配 ID F20260515-01（user-auth-phone）
→ 阶段一：生成 PRD
   ✓ docs/01_requirements/prd/F20260515-01-user-auth-phone-prd.md
   ✓ 自检 8/8 通过
→ 阶段二：技术设计
   ✓ docs/02_design/api/F20260515-01-user-auth-phone-api.md
   ✓ docs/02_design/database/F20260515-01-user-auth-phone-db.md
→ 阶段三：TDD 红灯
   ✓ 写了 14 条测试，全部预期失败
→ 阶段四：实现
   ✓ 14/14 测试转绿
→ 阶段五：代码评审 + 自动修复
   ✓ 自动修复 3 个 lint 问题
   ✓ docs/07_reviews/code/F20260515-01-user-auth-phone-review.md
→ 阶段六：交接
   📦 docs/.pdlc-state/F20260515-01.json 已更新
   👉 下一步：/pdlc-ship
</code></pre></div></div>

<p>整个流程下来你的 git 工作区有：1 份 PRD、2 份设计文档、N 份测试代码、1 份评审记录、1 份状态机更新、外加真正的代码改动。<strong>全是文件</strong>，不是对话。</p>

<h3 id="场景-2修-bug-留下完整审计">场景 2：修 bug 留下完整审计</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-fix 翻页在空列表上崩溃
</code></pre></div></div>

<p>PDLC 走的不是”看一眼 → 改两行”，而是：</p>

<ol>
  <li><strong>定位</strong>：搜代码找出可能的相关位置</li>
  <li><strong>复现</strong>：先写一个能稳定复现的测试（这个测试加到 <code class="language-plaintext highlighter-rouge">docs/04_testing/defects/</code> 下）</li>
  <li><strong>修</strong>：才真正动代码</li>
  <li><strong>验证</strong>：复现测试转绿</li>
  <li><strong>记录</strong>：缺陷报告归档</li>
</ol>

<p>下次类似的问题来，你能 <code class="language-plaintext highlighter-rouge">grep</code> 历史缺陷记录，看上次怎么修的。</p>

<h3 id="场景-3一眼看全项目进展">场景 3：一眼看全项目进展</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-status
</code></pre></div></div>

<p>输出大致这样：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Project PDLC status (read from docs/.pdlc-state/):

F20260512-01  user-auth-phone     ✓ shipped
F20260513-01  captcha-login       ⏵ in review
F20260514-01  password-reset      ⚠ stuck in TDD (3 tests failing)
F20260515-01  email-verify        ⏵ in PRD
</code></pre></div></div>

<p>不再追着 AI 问”我们做到哪儿了”——直接读状态机。</p>

<h2 id="效果你能得到什么">效果：你能得到什么</h2>

<h3 id="1-工程文档跟代码同步落盘">1. 工程文档跟代码同步落盘</h3>

<p>每份 PRD、每份设计、每份评审都是 git 历史里的真实文件。一年后回看，能精确知道”当时是为啥要做这个、设计是怎么决策的、评审是怎么过的”。<strong>对项目可维护性是质的提升</strong>。</p>

<h3 id="2-tdd-真的落实不是嘴上说说">2. TDD 真的落实，不是嘴上说说</h3>

<p>实现阶段被失败测试挡住。AI 想跳？跳不掉 —— 不仅是规则，<strong>还是被状态机强制约束的</strong>。</p>

<h3 id="3-自动修复的失控风险被压住">3. 自动修复的失控风险被压住</h3>

<p>很多 AI 工具有个隐性 bug：<strong>修复 → 检查 → 没好 → 再修复 → 又改坏 → 再修……</strong> 死循环烧 token 烧时间。PDLC 强制”自动修复至多一轮”，让 AI 卡住时主动停下来等人。</p>

<h3 id="4-跨会话的工程连续性">4. 跨会话的工程连续性</h3>

<p>今天做到 implement，明天 <code class="language-plaintext highlighter-rouge">/pdlc-status</code> 一查就知道下一步。<strong>不依赖对话上下文窗口</strong>，也不靠你脑子记。</p>

<h3 id="5-老项目也能接入">5. 老项目也能接入</h3>

<p>新项目从零搭好结构容易，老项目麻烦。PDLC 的 <code class="language-plaintext highlighter-rouge">/pdlc-adopt</code> 命令会扫描既有代码库，<strong>不重写你的代码</strong>，只补齐缺失的规范 / 设计文档 / 目录结构，让老项目从某个起点开始走 PDLC 流程。</p>

<h2 id="怎么开始">怎么开始</h2>

<p>完整安装步骤、命令清单、定制方法、FAQ 都在 <a href="/zh/pdlc/">PDLC 产品页</a> 和项目仓库里。</p>

<p><strong>一句话上手</strong>：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/kanfu-panda/pdlc-skills/main/install.sh <span class="se">\</span>
  | bash <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--global</span>
</code></pre></div></div>

<p>然后在 Claude Code 里：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/pdlc-feature &lt;一句话描述你的需求&gt;
</code></pre></div></div>

<p>仓库地址：<a href="https://github.com/kanfu-panda/pdlc-skills"><strong>github.com/kanfu-panda/pdlc-skills</strong></a>（MIT 协议）</p>

<h2 id="一点设计哲学">一点设计哲学</h2>

<p>PDLC 不是”加个 AI 帮忙”——它是<strong>用 AI 之前的工程化思考的延续</strong>：</p>

<blockquote>
  <p>软件工程几十年下来沉淀的核心是”流程纪律”。AI 来了之后，纪律不能少，反而该被工具强制保证。</p>
</blockquote>

<p>如果你也觉得”AI 写完代码 = 项目交付”是个危险的简化，PDLC 可能是你想要的。</p>

<p>欢迎试用，遇到问题去 <a href="https://github.com/kanfu-panda/pdlc-skills/discussions">GitHub Discussions</a> 聊，或在 <a href="/zh/about/">关于页</a> 找我交流。</p>
]]></content>
  </entry>
</feed>
