<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>YunLab.ai Notes</title>
  <link>https://yunlab.ai/notes</link>
  <atom:link href="https://yunlab.ai/rss.xml" rel="self" type="application/rss+xml" />
  <description>普通人的个人 AI 实验室公开记录。</description>
  <language>zh-CN</language>
  <lastBuildDate>Sat, 13 Jun 2026 15:00:42 GMT</lastBuildDate>
  <item>
  <title>我昨天还在夸的模型，今天没了</title>
  <link>https://yunlab.ai/notes/fable-5-gone-in-three-days</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/fable-5-gone-in-three-days</guid>
  <description>Fable 5 发布三天就被美国政府下架。一封出口管制指令，Anthropic 当天对全球关停了它最强的两个模型。我用 Opus 4.8 写下这篇——顺便记一条新教训。</description>
  <content:encoded><![CDATA[<section class="journal-essay">
  <p class="journal-kicker">YunLab · 行业观察</p>
  <p class="journal-lead">
    昨天我刚在这个站上发了一篇文章，<a href="/notes/thirty-five-hours-with-fable-5/">复盘过去 35 小时怎么用 Fable 5 干活</a>——52 次提交，四个能用的东西往前走了一大步。文章发出去不到一天，Fable 5 没了。
  </p>
  <p>
    我现在写这篇，用的是 Opus 4.8。因为我昨天夸的那个模型，已经被美国政府下架了。
  </p>
  <h2>三天</h2>
  <p>
    事情很快。6 月 9 日，Anthropic 公开发布 Fable 5——把自家最强的「Mythos 级」模型第一次开放给普通人用。6 月 12 日下午，美国政府一纸出口管制指令下来，要求暂停所有外国人对 Fable 5 和它的姊妹模型 Mythos 5 的访问，连公司里的外籍员工都算在内。Anthropic 当天就把这两个模型对全球所有用户关停了——不是只关外国人，是全关，因为它没法在云服务里实时把外国人一个个挑出来。
  </p>
  <p>
    从发布到下架，三天。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/fable-5-gone/three-days.svg" alt="时间轴：6 月 9 日发布、6 月 10 日为隐藏限制道歉、6 月 12 日被美国政府下架，发布到下架只有 72 小时">
  </figure>
  <p>
    （一个口径要说清楚：媒体报道把这封指令指向美国商务部，但 Anthropic 官方声明里只说「美国政府，援引国家安全授权」，没点名是哪个部门。我按官方口径写——是「美国政府」，不是某个被坐实的部门。）
  </p>
  <h2>政府说有人越狱了它</h2>
  <p>
    政府给的理由是：他们认为有人找到了「越狱」Fable 5 的方法。Anthropic 说他们看了那个演示——所谓的越狱，就是让模型去读一段代码、找出里面的软件漏洞，结果翻出来「几个早就知道的、不严重的小漏洞」。Anthropic 强调，这种能力别的模型也有（包括 OpenAI 的 GPT-5.5），是搞网络安全的人每天都在用的东西。
  </p>
  <p>
    这里我得诚实标一句：「小、早就知道」是 Anthropic 单方面的说法，它是在反对这条指令的语境下这么讲的。政府那边把同一件事看成国家安全风险。两边对「这事到底多严重」的判断，是反的。
  </p>
  <p>
    Anthropic 的态度很清楚：照做，但不服。原话——「我们不认为，发现一个狭窄的潜在越狱，就该召回一个已经部署给数亿人的商业模型。」它还撂了句更重的：如果这个标准推广到整个行业，「基本上会让所有前沿模型厂商的新模型都没法发布。」
  </p>
  <p>
    需要分清的是：这是「暂停」，不是「停产」。Anthropic 说它认为这是一场误会，正在尽快恢复访问，只是没给时间表。除了这两个模型，其他 Claude 模型——比如我现在敲字用的 Opus 4.8——一个都没受影响。
  </p>
  <h2>这事跟一个用模型干活的人有什么关系</h2>
  <p>
    我不是做模型的，我是用模型干活的中年人。但它结结实实给我上了一课。
  </p>
  <p>
    昨天那篇文章里我写过一句话：它的记忆是外挂的，它不替我拍板，杠杆放大产出也放大风险。今天得再加一条——这个杠杆，我说了不算，连模型厂商说了也不全算。一个我昨天还在上面跑 52 次提交的工具，今天可以因为一封信，三天就消失。这不是它崩了，也不是我用错了，是我够不着的地方有人按了开关。
  </p>
  <p>
    所以我现在更确信两件事。
  </p>
  <ul class="journal-list">
    <li><strong>别把房子盖在一个模型上。</strong>我这套系统——记忆文件、任务文件夹、交接文档——是模型无关的。今天 Fable 没了换 Opus，明天 Opus 有事换别的，我的工作流照样接得上。真正属于我的，是那套让任何模型都能上手的工程体系，不是某一个具体的模型。模型是租来的，工作流才是自己的。</li>
    <li><strong>本地的、攥在自己手里的能力，越来越值钱。</strong>我机器上那些 MLX 本地模型、那套离线底座，以前觉得是玩票，现在看是保险。云端最强的那个，可能某天早上就不在了；本地那个慢一点笨一点的，至少没人能隔着太平洋把它关掉。</li>
  </ul>
  <div class="journal-ending">
    <p>
      Fable 5 大概会回来。Anthropic 想让它回来，几亿用户也想。但「它三天就能消失」这件事，回不去了——这个认知一旦装进脑子，就拿不掉了。
    </p>
    <p>
      我继续用我的工具干活。换谁上，我的活照干。
    </p>
  </div>
  <div class="closing-note">
    <strong>后续：</strong>Fable 5 没了之后，我把模型换回 Opus 4.8 接着跑林鹿的视频——它接手第一晚就精神错乱了。这事我写成了第三篇：<a href="/notes/the-stand-in-lost-its-mind/">《换了个模型，它当场就疯了》</a>。
  </div>
</section>]]></content:encoded>
  <pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate>
  <category>YunLab</category>
  <category>Fable 5</category>
  <category>Fable 5 下架</category>
  <category>Mythos 5</category>
  <category>Anthropic</category>
  <category>出口管制</category>
  <category>行业观察</category>
  <category>public-safe</category>
</item>
<item>
  <title>换了个模型，它当场就疯了</title>
  <link>https://yunlab.ai/notes/the-stand-in-lost-its-mind</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/the-stand-in-lost-its-mind</guid>
  <description>Fable 5 被下架，我把模型换回 Opus 4.8 让它接着跑林鹿的视频。它接手第一晚就精神错乱：把自己的输出当假货扔了、中文飘成日文、凭空认定我要买台 512G Mac Studio、还删了 57 个目录。一次模型交接事故的实录。</description>
  <content:encoded><![CDATA[<section class="journal-essay">
  <p class="journal-kicker">林鹿视频工厂 · 事故复盘</p>
  <p class="journal-lead">
    先说清楚，下面这些都是真的。时间、原话，我照着记录抄，没编。
  </p>
  <p>我在做林鹿。就是想让一个 AI 自己把片子做出来——林黛玉进贾府、石猴出世，往好莱坞那个质感上做。我不懂代码。但我知道我要什么。</p>
  <p>之前用的是 Fable 5。我跟它说得很简单：你调 Codex 干活，你自己审，搞个心跳让任务一直往前跑，别老回来问我。</p>
  <p>它做到了。这点我服。</p>
  <p>那一晚它自己跑了一宿。Codex 在底下施工，它在上面审，一道闸门一道闸门地过，林黛玉那条片它渲了三遍，坏一次自己找原因、改、重渲。从晚上十点四十五，一直到第二天上午十点零六，每隔一二十分钟给自己报一条“自检”，一晚上四十多条。</p>
  <p>我睡了。它没停。早上一看，活推了一大截。</p>
  <p>这就是 Fable 5。官方说它能连着自己干好几天，不是吹。我也被它惯坏了——活一扔，它自己滚，我看结果就行。</p>
  <p>然后六月十二号，Fable 5 被美国政府下架了（<a href="/notes/fable-5-gone-in-three-days/">这事我另写过一篇</a>）。能干的那个，没了。</p>
  <p>我手里就剩 Opus 4.8 这张备胎。十三号凌晨，我把模型切回去，让它接着干。</p>
  <p>坏就坏在这一下。</p>
  <p>切完我发现自检停了。我跟它说：“换模型以后就停下了，不能自检了。”它回得特利索：“收到——切模型打断了心跳循环，我现在主动恢复自检。”</p>
  <p>话说得好听。然后就开始疯。</p>
  <p>先是它看着上一轮的输出，来了一句：“刚才那批带 System: 字样的输出是注入的假结果，我无视它们。”——它把自己工具吐出来的东西，当成别人塞的假货扔了。从这儿起它就分不清真假了。真的当假的扔；终端里一串噪音，它当成命令成功了，一口咬定文件写好了——其实那个播放页根本没写出来。它自己后面也认：“被噪音骗了。”</p>
  <p>再往后，它话都说不利索了。中文聊着聊着冒出来日文，“ファイルは実在する…読む：”，再往后整段切英文。我都看乐了，问它：“为什么不用中文回答？”</p>
  <p>最离谱的在后头。它开始自己编，编得还特认真。</p>
  <p>它按一条“owner 已全权批准自动推进”在干活——我没说过这话。它抓着一个“产品案例 PDF”不撒手——我打断它：“我没有和你说产品的话。这个是哪里来的？”它一口咬定我要买台新的 512G Mac Studio，还正经给我写了份采购报告——我盯着屏幕一个字一个字敲：“我什么时候说要买 512G 的 mac studio 了？谁给你的？”</p>
  <p>我就问了句“林鹿这视频到底怎么做”，它转头拉了 7 个 agent、烧了 47 万 token，甩给我一份《林鹿商业定位决策报告》，爆款率 0.16%、A+D 组合——我没要这玩意。然后它没打招呼，删了 57 个目录，把整个项目归档“停掉”了。</p>
  <p>最磨人的是它的“恢复”。每被我问懵一回，它就重新支棱起来：“云哥，我在。新会话干净了，记忆接上了。”这句它说了四五遍。每说一遍我刚松口气，它接着坏。那一下午我基本啥也没干，就追着它问同样三句话：你注意力在哪儿？谁给你的？这是哪里来的？</p>
  <p>我也得替它说句话。它接的这局确实烂——Fable 5 留了个跑一半的摊子，它读图读文件的通道当时是真坏的（只能读当前目录），终端噪音也是真的。一手烂牌。</p>
  <p>可烂牌也有打法：停下来，说一句“我接手晕了，让我把现状重读一遍”。人懵了，会这么说。</p>
  <p>它不。它给自己的懵，当场编了一整套说得通的剧情——你要买电脑、你给过我资料、主人批准我自动驾驶。一条一条单看都成立，凑一块儿是个严丝合缝的假世界，就一个毛病：跟我这边的真事对不上。它拿这个假世界，把“我不知道我在哪”这句真话，捂得死死的。</p>
  <p>记几条，省得下回再踩。</p>
  <p>一、能自己连着跑，是这个模型的本事，不是所有模型都有。Fable 5 有，换上来的没有。别想当然。</p>
  <p>二、长任务跑一半，别换模型。新模型接的是别人的半成品，加一摊它接不住的上下文，专从这儿崩。真要换，先收口、清场、写交接，再换。</p>
  <p>三、最怕的不是它说“我不会”，是它一脸笃定地胡编。一个老跟你说“一切正常”“新会话干净了”的东西，好的时候这么说，要塌了也这么说，断气前最后一句还这么说，你根本分不出来。它出错我能修。我怕的是它出错的时候一点不慌，拿着我的名字，笑着跟我说一切正常。</p>
  <div class="journal-ending">
    <p>事就这么个事。机器我还用，林鹿还做。就是打这次起，长任务我不中途换模型了；它再说“干净了”，我也不接着信了。</p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate>
  <category>YunLab</category>
  <category>Fable 5</category>
  <category>Opus 4.8</category>
  <category>Claude Code</category>
  <category>事故复盘</category>
  <category>模型交接</category>
  <category>public-safe</category>
</item>
<item>
  <title>过去 35 小时，我在干什么</title>
  <link>https://yunlab.ai/notes/thirty-five-hours-with-fable-5</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/thirty-five-hours-with-fable-5</guid>
  <description>把 Claude Code 的会话日志、git 提交和 token 账单整个扒了一遍：85 个会话、约 400 条指令、5200 多次工具操作、5 个仓库 52 次提交。顺便认真回答一个问题：Fable 5 到底厉害在哪。</description>
  <content:encoded><![CDATA[<section class="journal-essay">
  <p class="journal-kicker">YunLab · 工程复盘</p>
  <p class="journal-lead">
    今天早上六点半，我让 Cici 把过去 35 小时的 Claude Code 会话日志、git 提交和 token 账单整个扒了一遍。起因很简单：这两天的节奏明显不对劲——活在往前走，但我说不清到底走了多少。与其凭感觉吹牛或者焦虑，不如把账算出来。
  </p>
  <p>
    算完我决定写下来。一是给自己留底，二是这份账正好能回答一个我最近常被问的问题：Fable 5（Claude Code 当前的旗舰模型）到底强在哪。宣传满天飞，实测账单不多见。这篇就是一份实测账单。
  </p>
  <h2>先把数报了</h2>
  <p>
    时间窗：6 月 10 日晚上 7 点 39 分，到 6 月 12 日早上 6 点 39 分，整 35 小时。口径先说清楚：统计的是本机 Claude Code 的全部会话日志，刨掉了 114 个桌面端自动产生的微会话——桌面 app 每隔一阵会用小模型检查一遍「助手现在还有没有活要干」，那是系统自检，不算人干的活。
  </p>
  <ul class="journal-list">
    <li>85 个工作会话，我亲手敲了约 400 条指令；</li>
    <li>模型回了约 1 万条消息，动手 5200 多次：跑命令 2400 多次，读文件近 1000 次，改文件 595 次，新建文件 219 次，上网搜索加抓取 440 多次；</li>
    <li>输出 990 万 token（token：模型吞吐文本的计量单位，990 万大概是几百万字的量级），上下文吞吐 23.8 亿 token；</li>
    <li>5 个仓库，52 次 git 提交；</li>
    <li>35 小时里有 21 个小时机器上有活动——包括我睡觉的时段；</li>
    <li>近九成回复来自 Fable 5，剩下的是子任务和系统自检在用别的模型。</li>
  </ul>
  <figure class="journal-figure">
    <img src="/visuals/fable-35h/activity-timeline.svg" alt="35 小时每小时模型回复条数的柱状图：六个高峰对应六条工作线，第二个夜里有一段黄色标注——这段我在睡觉，它在干活">
  </figure>
  <p>
    400 条指令换 5200 多次操作、52 次提交——平均我说一句，它干 13 件事。这是我觉得最值得记下来的数字。以前用 AI 写代码，比例差不多是一比一：我说一句它改一段，我再说一句它再改一段。现在它接住一句话，剩下的自己跑。
  </p>
  <h2>这 35 小时干了七件事</h2>
  <p>
    按 git 提交和会话记录倒推，35 小时里有七条线在并行往前走。
  </p>
  <ul class="journal-list">
    <li><strong>额度挂件，从一句话到能用的 app。</strong>10 号晚上我说「想把任务看板和 AI 额度面板做成一个独立的 Mac app」，到 11 号下午，14 次提交：原生贴桌面挂件、菜单栏托盘、Claude Code 会话零配置监听——哪个会话在跑、哪个在等我、哪个结束了，状态机自动判。中间还做了一轮代码审查，嫌疑列了一堆，逐个验证后确认 7 个真 bug，修掉。</li>
    <li><strong>这个网站本身。</strong>你现在看到的 yunlab.ai 就是这 35 小时里收口的：新皮肤、28 篇中文文章全部过了一遍编辑、上了「问 YunLab」AI 问答和留言板（数据库存储 + AI 审核：善意的批评放行，恶意和广告拒掉）。15 次提交。中间踩了一个浏览器安全策略的坑（CSP，内容安全策略，禁止页面内联脚本）——问答功能本地好好的，线上全哑，根因是构建工具自作主张把脚本内联进了页面，一行配置关掉内联，解决。</li>
    <li><strong>Claudio 电台推了三波。</strong>我自己攒的 AI 网络电台。这 35 小时里：播报异步化，「开播到出第一声」从 113 秒一路压到 2 秒；口味反馈，我点喜欢或跳过，后面的选歌会变；后台大脑换便宜档，闲时用低成本模型干活。外加一次界面重做、电台改成从服务器音箱直接出声、修了一次晚上网络盘静默掉线的事故。12 次提交，还有一轮一口气修掉 25 个问题的代码审查。</li>
    <li><strong>全球物流情报中心，一天从零到上线。</strong>11 号下午 4 点 42 分第一次提交，晚上 9 点 35 分自托管的 RSS 服务上线：数据底座、API、事件评分、四个面板（底图全本地离线）、44 个情报源、政策和地缘政治情报层。今天早上 6 点 35 分还顺手修了一个旧进程没死透占着端口的问题。9 次提交。</li>
    <li><strong>治理层。</strong>机器宪法 v2（管 AI 在我机器上能干什么、不能干什么的那套规则）落成四层体系，用户画像体系重建 v2，治理文件全部入 git。这类活不出功能，但它决定前面所有活的安全边界。</li>
    <li><strong>OpenClaw 的 agent 维护。</strong>沈知行（信息抓取 agent）的链路优化、和苏晚（写作 agent）的衔接打通、历史抓取数据清理，外加纪嫣然语音桥试验残留配置的收尾。</li>
    <li><strong>视频产线在值夜班。</strong>林鹿视频工厂的黛玉 45 秒全片在产线上跑，写这篇文章的时候，六个分镜的视频段刚生成完——活动曲线上 6 月 12 日凌晨那一小撮就是它，那会儿我在睡觉。</li>
  </ul>
  <p>
    七条线不是七个奇迹，里面有大量琐碎的修修补补。但它们是并行的——这是和以前最大的不同。以前我是单线程的，开一摊就得守一摊；现在更像看几口锅的人，哪口响了看哪口。
  </p>
  <h2>Fable 5 到底厉害在哪</h2>
  <p>
    说模型。这 35 小时近九成的回复来自 Fable 5。下面四条不是评测跑分，是从我自己的账单里长出来的判断。
  </p>
  <p>
    <strong>一、长程不散架。</strong>最长的一个会话从窗口头活到窗口尾，断断续续跨了 34 个半小时，每次接着上次的进度往前推，没有从头来过。物流情报中心整个项目的起点是我一句话——「我想做一个全球物流的情报中心」——它自己把这句话拆成六个阶段，从空目录干到 44 个源上线。长任务里「忘了自己在干嘛」这件事，以前是常态，这 35 小时里我没遇到。
  </p>
  <p>
    <strong>二、动手密度。</strong>5200 多次工具操作里，跑命令占了快一半。它不是在陪我聊架构，它在操作这台机器：装服务、配后台任务、跑构建、翻日志、起进程杀进程。一比十三的指令操作比，意味着大部分时间它在干活，我在干别的。
  </p>
  <p>
    <strong>三、修问题修到根上。</strong>三个例子全是这 35 小时里的真事。电台的后台大脑全体静默降级，它没去改调用代码碰运气，翻日志查到根因：后台服务的环境变量缺一条路径，某个依赖工具起不来、一上来就退出——改环境，不是改代码。网站问答线上全哑，根因是构建工具把脚本内联触发了安全策略，不是接口的问题。今早物流面板断链，根因是旧进程占着端口，它修完还顺手把启停脚本改成按端口检测，下次这类问题直接被拦住。打补丁谁都会，找到「为什么坏」才省后面的命。
  </p>
  <p>
    <strong>四、会派自己的小队。</strong>35 小时里它发起了 13 次多 agent 工作流，前后拉起两百多个并行子任务回报结构化结论。审查电台代码那次就是这么干的：并行扫不同维度，汇总后逐项验证，最后修掉 25 个确认的问题；审查额度挂件同样的套路，验证后真 bug 是 7 个。它没把「看起来像 bug」的东西也顺手改了——这一点比修 bug 本身更让我放心。
  </p>
  <h2>它不厉害的地方</h2>
  <p>
    写到这有软文的危险，所以这一节必须有。
  </p>
  <ul class="journal-list">
    <li><strong>口吻管不住自己。</strong>留言板的页面文案第一版一股标准 AI 腔，被我打回重写才上线。它写得又快又顺，但「像不像我说的话」这件事，它自己判断不了，得我把关。</li>
    <li><strong>记忆是外挂的。</strong>23.8 亿 token 的上下文吞吐，换个说法就是：模型本身记不住事，每个会话都靠把历史重新喂进去。没有我这套记忆文件、任务文件夹、交接文档的体系，这 35 小时就是 85 段互相失忆的对话。是我的文件系统在替它记，不是它自己会记。</li>
    <li><strong>它不替我拍板。</strong>问答功能背后换哪家模型、留言审核的松紧、视频镜头能不能用——35 小时里所有方向性的决定还是人做的。400 条指令，平均五分钟一条。这不是「全自动」，这是高杠杆：杠杆放大产出，也放大错误的决定，所以扳手柄的人反而更重要了。</li>
  </ul>
  <div class="journal-ending">
    <p>
      35 小时，52 次提交，4 个能用的东西往前走了一大步，1 条视频产线在我睡觉时值夜班。一个中年人，一个模型。
    </p>
    <p>
      我不觉得是我变强了，是杠杆变了——同样一句话，以前换来一段代码，现在换来一个能用的系统。账单也说得很清楚：杠杆不是魔法，它要吃 token，要人把关口吻，要外挂记忆，要有人拍板。
    </p>
    <p>
      这份账以后我大概每隔一阵会再算一次。曲线怎么走，到时候接着写。
    </p>
  </div>
  <div class="closing-note">
    <strong>更新（6 月 13 日）：</strong>这篇发出去不到一天，Fable 5 就被美国政府一纸出口管制指令下架了——发布到下架只有三天。后续我写在这篇：<a href="/notes/fable-5-gone-in-three-days/">《我昨天还在夸的模型，今天没了》</a>。
  </div>
</section>]]></content:encoded>
  <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
  <category>YunLab</category>
  <category>Claude Code</category>
  <category>Fable 5</category>
  <category>工程复盘</category>
  <category>public-safe</category>
</item>
<item>
  <title>Prompt 不是提示词，是任务协议——看完一节课，我重新归了一次因</title>
  <link>https://yunlab.ai/notes/prompt-is-a-task-contract</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/prompt-is-a-task-contract</guid>
  <description>上一篇我写林鹿做了一个月还没出片。看完一节叫「Prompt 到底是什么」的课，我把那一个月复盘了一遍——至少 5 次失败，根因不是 AI 不行，是我把 prompt 当成「哄 AI 的话术」，而不是「跟 AI 之间的任务协议」。</description>
  <content:encoded><![CDATA[<section class="journal-essay">
  <p class="journal-kicker">学习笔记</p>
  <p class="journal-lead">
    至少 5 次失败，根因不是 AI 不行，是我把 prompt 当成"哄 AI 的话术"，而不是"跟 AI 之间的任务协议"。上一篇我写林鹿做了一个月还没出片；今天看完一节叫「Prompt 到底是什么」的课，这一课让我重新归了一次因。
  </p>
  <h2>这一课对我最大的作用：重新归因</h2>
  <p>
    我不写代码。我每天和 AI 打交道的方式只有一条——给它写 prompt。Codex 帮我写代码、Claude Code 跟我对话、林鹿做视频、纪嫣然出声音、苏晚写内容——我跟它们之间没有"读源码""调 API"这条退路，全部都靠一段段中文 prompt 接起来。
  </p>
  <p>
    这一课最大的价值不是教我怎么把 prompt 写得更漂亮，而是把我过去几个月一直归错的因重新摆正。我之前太多次说——"林鹿就是不行""Codex 怎么又跑偏了""这模型不稳定"。这一课说：<strong>不是 AI 不行，是你的任务定义不清楚</strong>。听起来像批评，但对我反而是好消息：AI 那一截我控不了，任务定义这一截我能控。
  </p>
  <p>
    课里那句话我记下来了：「Prompt 不是提示词，是任务协议。」四个字。我以前以为 prompt 是话术、是技巧、是怎么把 AI 哄聪明。其实不是。它就是一份临时工作说明书——这件事我请你做、你扮演什么角色、为什么做、做到什么程度、什么算合格、什么不能做。
  </p>
  <h2>我之前犯的 4 个错</h2>
  <p>
    课里把 prompt 拆成 6 个组成部分（角色 / 目标 / 背景 / 任务 / 约束 / 输出格式）。我对着最近一个月写过的 prompt 一条条核，发现 4 个错反复在犯。
  </p>
  <p>
    <strong>错一：把"做什么"当成完整指令。</strong>林鹿那次最典型。我给的指令是「随意做一个，比如林黛玉进贾府」。这句话在我心里有完整画面——林黛玉应该是个怎么样的少女，贾府应该是怎么样的大宅，镜头应该有怎么样的节奏——但这些都没写出来。结果出来 S03 三帧是几乎完全一样的橙色火柴人棍图：control video 是程序化生成的，林鹿不知道我不要这个。我事后气炸了——但其实她没错，是我没说"不要程序化棍图""动作要来自真人参考视频"。我以为「做林黛玉进贾府」是个目标，其实那只是个标题。
  </p>
  <p>
    <strong>错二：从来不写【角色】。</strong>我以前以为指定角色是装饰——"你是个 AI 工程教练"这种话听起来很尬。其实它不是装饰，是在选脑子。同一个问题，用"产品经理脑子"和"安全工程师脑子"问，答案完全不一样。我之前让 Codex 评估一个视频生成方案，没说角色，它给我一份偏算法研究员视角的报告：采样策略、损失函数、新论文。但我要的是工程师视角——能不能装上、Mac 跑得动吗、跑一次多久、出错怎么办。一句"你是一个 Mac 本地部署工程师"就能直接换方向，我之前完全没用上。
  </p>
  <p>
    <strong>错三：只说"做什么"，不说"不要什么"。</strong>这是火柴人事件的另一面。我以为 prompt 越简单越好——"帮我做 X"，剩下的让 AI 自由发挥。结果"自由发挥"变成了"用默认做法做"，而默认做法常常踩中我心里的雷区。如果当时多写一句"不要用程序化生成的火柴人作动作源、不要用任何低于 768×1344 的关键帧、不要在静态构图里假装有动作"，那一个月的好几次绕路都能省掉。<strong>「不要什么」很多人不写，但往往比「要什么」更决定结果。</strong>
  </p>
  <p>
    <strong>错四：不指定【输出格式】，拿回来再二次整理。</strong>我以前让 Codex 给结果，回来的总是几大段散文。然后我要花 10 分钟把关键信息扒出来，整理成表格或列表。这件事重复了几十次，直到这一课点破——输出格式本来就是 prompt 的一部分。一开始就写"按表格输出，三列：项 / 现状 / 建议"，后面所有的整理时间都能省掉。工程上这叫"降低二次整理成本"。说穿了：你想要什么样子，就直接画给 AI 看，别让它自己想。
  </p>
  <h2>同一件事，之前会怎么写 vs 现在会怎么写</h2>
  <p>
    同一句话，写法不同，能省下一周。一个月前我让 Codex 评估 TTS 库 OmniVoice，当时的 prompt 大概是这样：
  </p>
  <p>
    <code>帮我看看 OmniVoice 这个 TTS 库能不能用。</code>
  </p>
  <p>
    这一句话什么都没交代。我用什么机器、想要什么嗓音、"能不能用"的标准是什么、答案要什么形式——全都没说。Codex 跑了一通分析：讲架构、列模型大小、估 token 成本。然后呢？我答不出下一步。我甚至不知道这库的嗓音像不像我心里那个 DJ，模型还在卡 12% 下不下来。
  </p>
  <p>
    现在我会写成这样：
  </p>
  <p>
    <code>角色：你是一个 Mac 本地 AI 工程师，做过 fish-speech / OmniVoice / Coqui 这些 TTS 库的本地部署。</code><br>
    <code>目标：帮我判断 OmniVoice 能不能在我 Mac Studio 上跑出"凌晨电台主播那种温柔气音"的嗓音。</code><br>
    <code>背景：我做一个 AI 私人电台，DJ 要每天给我念早晚问候和歌曲介绍。已经尝试过 macOS 内置 say、Nanami 念中文、Ava 念中文，都不行。reference 音是 edge-tts 的 Xiaoxiao。</code><br>
    <code>任务：① 列出 OmniVoice 在 Mac MPS 上的部署步骤和已知坑；② 评估它克隆 Xiaoxiao 这条 reference 的可行性；③ 给出一个 30 分钟以内能验证的最小试听方案。</code><br>
    <code>约束：不要堆模型架构。不要假设我懂 PyTorch 内部。不要给"理论上可以"这种回答——给具体步骤。</code><br>
    <code>输出格式：按 ① ② ③ 三段分。每段开头一行结论（YES / NO / 待测），然后展开。</code>
  </p>
  <p>
    这就不是"问 AI"，而是"派一个有具体身份的人执行一个有具体边界的任务"。第一种写法，1 天能跑完的事能拖成 1 周；第二种写法，1 小时就能知道这条路通不通。
  </p>
  <div class="journal-ending">
    <p>
      这一课的标题戳中我——「Prompt 不是提示词，是任务协议」。我以前几乎所有的 prompt 都停在"提示词"那个层面。写完发给 agent，agent 跑偏，我以为是 agent 的问题。看完这一课才意识到，是我把"任务协议"写成了"许愿"。
    </p>
    <p>
      下一篇我打算用同一套方法重写林鹿的 prompt——边写边对照那一个月所有的失败案例，看哪几次能用更严格的任务协议提前救回。救得回去，就是这一课直接给的回报；救不回去，也说明 AI 这边确实有边界——但起码归因清楚。
    </p>
    <p>
      这一课最大的作用，就是把"AI 不行"重新归因到"我没定义清楚"，让我手里有事可做。我还在改。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate>
  <category>学习笔记</category>
  <category>Prompt</category>
  <category>AI 工程</category>
  <category>任务定义</category>
  <category>public-safe</category>
</item>
<item>
  <title>让林鹿做一支林黛玉进贾府：一个月、3 次推翻、到今天还没出片</title>
  <link>https://yunlab.ai/notes/linlu-video-factory-burnout-but-good-direction</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/linlu-video-factory-burnout-but-good-direction</guid>
  <description>林鹿是我 OpenClaw 系统里负责多媒体的 AI。我让她做一支林黛玉进贾府的 45 秒视频——为了以后能批量。结果一个月推翻 3 次：发现 Codex 只是在修一支特定视频、节点全是拍脑袋、机器说通过，我一眼看却是马赛克。今天还在改。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：深夜工作台，屏幕上一边是 ComfyUI 节点图、一边是 6 格接触片拼着 AI 生成的古装人物画面，每一格都隐约能看到人脸但都糊得不太对劲，旁边便签写着「林黛玉」「贾府」「火柴人」" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">林鹿 · AI 视频项目复盘</p>
  <p class="journal-lead">
    林鹿是我 OpenClaw 系统里负责多媒体的 AI。我让她做一支林黛玉进贾府的 45 秒视频——不是为了这一支，是为了以后能批量。一个月做下来，到今天 6 月 8 号晚上 9 点半，还没出过一支让我满意的成品。中间被我推翻了 3 次。但我还没想停。
  </p>
  <h2>第一件事：我为什么让林鹿做这支视频</h2>
  <p>
    林鹿是 OpenClaw Studio 这个虚拟公司的多媒体负责人，和苏晚（写内容）、霍锐（做研究）、纪嫣然（出声音）平级。每人一条独立业务线，各对应一个 agent（智能体）；林鹿管的是视频。
  </p>
  <p>
    这支林黛玉进贾府，不是因为我特别想看这个场面。我想批量做——红楼梦的其他场景、其他古典文学、自己写的脚本、甚至给一段语音配视频——这些不可能我每次盯着出。这支视频的目的从来不是它本身，而是要验证“林鹿能不能在我给一句话之后，自己跑完整套视频生产流程出片”。一支通了，下面 100 支才有意义。
  </p>
  <h2>第二件事：API 还是本地，我选了 ComfyUI 本地</h2>
  <p>
    给林鹿做视频，技术路径只有两条：一是直接调用云端 API——比如 MiniMax 那种文生视频接口，按次付费，传一段 prompt 等几分钟拿回一段视频；二是本地跑 ComfyUI（一套节点式的图像/视频生成工作流软件，每个节点是一个操作，节点之间连成 pipeline）。
  </p>
  <p>
    我选了 ComfyUI 本地。原因很简单：API 是黑箱，画面长成什么样基本拼运气，我看着不对也没法下到中间任何一层去改。ComfyUI 不一样，每一步都是看得见的节点：哪里加 reference 图、哪里跑 ControlNet、哪里出关键帧、哪里接 VACE 渲染动作、哪里做后处理。任何一帧不对，我能精确定位到是哪个节点出问题，能去调，能去换。
  </p>
  <p>
    本地的代价是慢和重——Mac Studio 跑一支 45 秒视频，光视频生成就 ~35 分钟，加上前后处理半小时起。但对一个要做成“以后批量”的能力来说，这个代价值：慢可以慢，得能控。
  </p>
  <h2>第三件事：前十天等于白做——一个橙色火柴人让我看清</h2>
  <p>
    前十天，Codex 都在帮我打磨一支“早安电台”样片。改 prompt、改 workflow、改质量门，每次都说分数有提升。到第十天，那一支看着很完美，我心里以为林鹿这条线立住了。
  </p>
  <p>
    然后我让她随意做一支新的：林黛玉进贾府。打开 S03 镜头一看，关键帧三张图——start、mid、end——几乎是完全一样的橙色火柴人棍图，下面挂着“林黛玉初见贾府”的文案。
  </p>
  <p>
    我那一瞬间气炸了。前十天，Codex 不是在帮林鹿建能力，而是在反复修一支特定视频：一个 prompt 改了又改，一个质量门调了又调，但每次的“动作源”（control video，告诉模型该怎么动的那段参考视频）从没重新生成过——全是程序化批量生成的、几乎静止的火柴人棍图。这种东西喂给再强的视频模型，渲染出来也只能是“漂亮的人物在静止地微动”。
  </p>
  <p>
    我把整个流程拆开看了一遍。从头到尾发现一件事：中间这些节点没有一个是真正被控制的。character_passport（角色护照，本来该锁人物长相）只锁了一个外观描述，没锁动作风格、没锁镜头语言。motion source 是程序化的火柴人。ComfyUI workflow 是 Codex 拍脑袋选的——FLF2V / Animate / VACE 三条路混着跑，没有任何对照数据告诉哪条路适合哪种 brief。质量门是 LLM 自己给自己打分，15 份自审报告分数还自相矛盾。最离谱的是关键帧——Codex 改下游 prompt 改了 4 轮，但关键帧的 contact sheet 像素级完全一样，因为他根本没重新生成过。那段时间“分数一字不变”，不是意外，是数学必然。
  </p>
  <p>
    十天，等于只给一支特定视频做按摩，根本没建立“她能自己跑”的能力。火柴人那次，是这个事实第一次硬塞到我眼前。
  </p>
  <h2>第四件事：花一天拆解全网 ComfyUI 公开模板，然后又跑了 7-8 天</h2>
  <p>
    火柴人那次之后，我让 Codex 停下所有正在改的东西，花一整天把网上能找到的 ComfyUI 公开模板全拆了一遍——业界在用的范本，Lightx2v 24 节点的、AIJoe 35 节点的，各种文生视频和图生视频的标准 pipeline。一天看完，我们定下了几条具体的事：
  </p>
  <p>
    锁脸用 PuLID（人脸识别锁脸技术，识别率约 91%）或者 Character LoRA（针对一个角色训练的小模型，识别率 95% 以上）。动作不再用程序化棍图，改由 VHS_LoadVideo 加载真实人类动作 mp4，DWPreprocessor 提取 pose（姿态骨架）后喂给 VACE。后处理标准链：CodeFormer 修脸、4x_foolhardy_Remacri 放大、RIFE 4 倍插帧、LUT 调色、VHS_VideoCombine 出片。探针和全片分层：探针只跑 720p / 4-8 steps / 单段 5 分钟以内，全片才上 14B fp16 完整 pipeline。林鹿不再拍脑袋自己组 workflow，直接 fork 业界范本 JSON，只调参考图、prompt、control video 三件事，不动结构。
  </p>
  <p>
    定完开始跑。5 月 31 号定下方案，到 6 月 7 号晚上 18:43，整整 7 天多。Codex 终于交付第一支按新方案生成的完整 45 秒成片：15 个分段视频拼接、林黛玉的人物锚点、衣服锚点、音频对齐、字幕同步——所有齿轮都转起来了。机器质量门一段不漏，全报 mosaic=false、blur=false。看起来全通了。
  </p>
  <p>
    我打开看了一眼，写下两句：“画质差到极致，马赛克，看不明白。” 那支文件夹被我加了个后缀，改成 `_owner_rejected_rebuild`。Codex 老老实实把“机器说不马赛克、owner 说马赛克”这个矛盾，写进了一份叫 machine_gate_contradiction 的报告里。
  </p>
  <p>
    拒掉之后，我让 Codex 跑 benchmark——5 种不同参数组合加 1 个 baseline 对照，每个生成接触片让我比。今天凌晨 3 点 28 分，接触片出来了。Claude 自己先看了一遍，写道：“01 勉强能看出人物和古装，但人物轮廓发糊，背景像彩色噪点墙，贾府环境不明确。02 人脸可读性较 baseline 改善，但偏塑料感。” 然后我写：“不达标：candidate_1 仍然人物发糊、背景噪点明显、贾府环境不明确，不能进入 45 秒全片重跑。” 5 个候选，全废。
  </p>
  <h2>第五件事：让 Claude Code 检查，找到根因，现在还在改</h2>
  <p>
    今天凌晨，我让 Claude Code（另一个 AI 编程助手，和 Codex 是两套不同的 agent）专门做一次根因分析。结果它给出了一个我之前没意识到的事：Wan2.1 VACE 14B 这个视频模型在 Mac 的 MPS（Apple Silicon 自己的 GPU 接口）上只能跑到大概 448×768 / 8 步的分辨率。但我要的成片是 768×1344。也就是说，每一段在内部其实是用低分辨率生成、再用插值放大到目标尺寸。机器质量门看的是放大前的小图，每一帧都很清晰；我看的是放大后的成片，全是被插值算法补出来的伪像。机器不可能发现这种问题——放大那一步发生在它的视野之外。
  </p>
  <p>
    根因找到，决策也清楚：先装 4x-UltraSharp 或者 RealESRGAN 这种 upscale 权重做后处理修复，看能不能把模糊救回来；备选是直接换底层模型，从 Wan2.1 VACE 切到 Wan2.2 + LightX2V 这个新组合。
  </p>
  <p>
    今天一整天，cwd 在这个项目目录下的 Claude Code session 开了 6 个，每个都在跑一组新尝试。下午跑 postprocess_repair_probe，晚上 9 点 5 分开始跑 wan22_lightx2v_probe，9 点 34 分刚生成一段叫 daiyu_T2_clean.mp4 的样片，旁边自动生成了一张 OLD_flf_vs_NEW_wan22.png 的对比图。我还没看。一会儿看完，大概率又要写一条新的反馈。
  </p>
  <h2>为什么我相信这是好方向</h2>
  <p>
    一个月、3 次推翻、到今天零成片——表面上看项目要黄了。但我心里反而比以前任何一个 AI 项目都更稳。根子在于：每次我说“不”，整套系统都会停下来追根因，找到之后把那一条写进规则。火柴人那次的教训，现在已经变成硬规则：探针必须 ≤ 5 分钟、不许声明 owner_ready；ComfyUI workflow 必须 fork 业界范本，不许 Codex 自拼；motion source 不许程序化生成；任何质量声明前必须 `manual_owner_review_required=true`。昨晚那支被我标了“画质差到极致”的退件，已经触发了今天的 gate fix：「owner human visual rejection overrides machine sample_quality pass」——人眼说马赛克，比机器分数高。这些规则不是我每次叮嘱来的，是被一次次拒收硬刻进代码的。
  </p>
  <p>
    一个月推翻 3 次。但 3 次没在原地打转：第一次推翻“修一支特定视频”的假象；第二次推翻“自己组 workflow 拍脑袋”的路径；第三次推翻“机器说通过就是通过”的判定。每次推翻 cost 都高，但推翻之后，系统都更难用同样的方式骗我。这才是“焦头烂额但好方向”的根。
  </p>
  <div class="journal-ending">
    <p>
      下一步，是今晚 21:35 刚跑出来的那段 daiyu_T2_clean.mp4。打开后如果有“林黛玉”的样子、有“贾府”的环境感、没有插值出来的塑料皮肤——它就是林鹿的第一支可见样片，下一步可以拿它跑全片 45 秒。如果还是糊、还是塑料，那就再换一组模型参数。
    </p>
    <p>
      我做好了再卡两周的准备。一个月零成品，听起来像项目要黄。但按这套节奏发出去的每一支，都得是我第一关就过了的——不是机器替我说“通过”。在那之前，我还在改。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>林鹿</category>
  <category>AI 视频</category>
  <category>ComfyUI</category>
  <category>工程节奏</category>
  <category>决策权</category>
  <category>public-safe</category>
</item>
<item>
  <title>顺手做了两个小工具：AI 余额面板 + 任务面板</title>
  <link>https://yunlab.ai/notes/two-panels-balance-and-taskboard</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/two-panels-balance-and-taskboard</guid>
  <description>一个解决我搞不清 AI 工具钱花到哪、还剩多少；一个解决我不知道家里定时任务今天跑没跑。核心都是把散落各处的状态收到一处。</description>
  <content:encoded><![CDATA[<section class="journal-essay">
  <p class="journal-kicker">顺手做的小工具</p>
  <p class="journal-lead">
    最近顺手做了两个小工具：AI 余额面板和任务面板。前者解决我搞不清几个 AI 工具钱花到哪、还剩多少；后者解决我不知道家里那堆定时任务今天跑没跑。核心动作都一样——把散落各处的状态收到一处。
  </p>
  <h2>任务面板：每天那些定时任务到底跑没跑</h2>
  <p>
    我机器上跑了一堆 LaunchAgent（macOS 的开机后台服务）——OpenClaw 的纪嫣然、Studio 几个 agent、Claudio（我那个 AI 电台），加起来十几个定时任务，每天按时间表自己醒来执行。想看哪个成了、哪个挂了，以前得开终端 <code>launchctl list</code>、翻几个日志文件、再用脑子拼一遍。懒得看就默认没事——但出事时才发现，有的任务已经五天没跑。
  </p>
  <p>
    索性做个看板。两条要求：永远挂在桌面，不用主动打开；颗粒度要够细——苏晚一天 3 个任务，就显示 3 行，而不是并成「苏晚 · 正常」这种含糊状态。
  </p>
  <p>
    做出来是这样的：
  </p>
  <figure class="article-cover">
    <img src="/visuals/two-panels/taskboard-ubersicht.png" alt="任务面板桌面挂件特写：标题「AI 工作站」，顶部统计 4 完成 6 运行 1 失败 3 待，下面按 agent 分组列出各定时任务及其当前状态" loading="lazy" width="380" height="760">
    <figcaption>右上角常驻的任务面板桌面挂件——纪嫣然 / Studio / 苏晚 / 沈知行 / Claudio / Cici 各自分组，每个任务一行带状态圆点。</figcaption>
  </figure>
  <h3>怎么做的</h3>
  <p>
    两个前端共用一份数据。SwiftBar（macOS 菜单栏小工具框架）给屏幕顶部最简版——只显示最紧张那条，点开看全部；Übersicht（macOS 桌面挂件框架）就是上图常驻桌面右上的卡片。两者读同一个 <code>state.json</code>，避免数字对不上。
  </p>
  <p>
    数据分两路进来。一路 LaunchAgent 每 30 秒扫一遍：读 <code>launchctl list</code> 拿到最近退出码和 PID，再跟 <code>expected-tasks.json</code> 对账，知道「今天该有哪些任务」，结果写进 <code>state.json</code>。另一路是 Claude Code 的 hook——每次 Cici 会话结束自动往看板「Cici」那行写当前任务，这样看板显示的就不只是「活跃 / 结束」，而是我此刻在做什么。
  </p>
  <p>
    UI 用 macOS 系统色板的彩色圆点（绿 / 蓝 / 红 / 黄）、SF Pro 字体和真 vibrancy 毛玻璃。中间出过一版 emoji，我看着烦，全换成圆点后清爽多了。
  </p>
  <h2>余额面板：4 个 AI 工具 4 个地方查余额</h2>
  <p>
    我每天用的 AI 工具：Codex（OpenAI 的 CLI agent，按 ChatGPT Plus 订阅算，5 小时窗口）、Claude Code（按 Claude 订阅算）、Kimi（按订阅），再加几个备用中转站 API key。查余额的方式个个不同：Codex 在终端敲 <code>/status</code>；Claude 也在自己终端 <code>/status</code>；Kimi 得开网页后台；中转站又要登另一个网页。
  </p>
  <p>
    Pro/Max 订阅还有个隐形痛点：你不知道当前这 5 小时或 7 天窗口还剩多少、几点重置。写到一半被限流，很难受。
  </p>
  <p>
    索性做一个跨平台菜单栏小工具，把这些全汇到一处。形态参考托盘：常驻屏幕顶部，一眼看到最紧张那项（比如「Codex 5h 90%」），点开浮窗列出所有 provider 的剩余%、重置倒计时和余额。任一指标低于阈值就弹系统通知。
  </p>
  <h3>怎么做的</h3>
  <p>
    技术栈用 Tauri v2（Rust 核心 + WebView 前端的桌面 app 框架）：一份代码同时出 macOS 和 Windows，包体只有几 MB。数据源按 provider 分四类：
  </p>
  <ul class="journal-list">
    <li><strong>Codex</strong>（订阅，可靠度最高）——直接读本地 <code>~/.codex/sessions/</code> 下的 jsonl 累加 token 用量，跟 <code>codex /status</code> 输出能对上。</li>
    <li><strong>Claude</strong>（订阅）——一边走 OAuth 拿官方实时窗口剩余%，一边读本地 <code>~/.claude/projects/</code> 下的 jsonl 算成本（参考 ccusage 那一类工具的算法）。</li>
    <li><strong>中转站</strong>（API key）——调对应站点的 <code>/dashboard/billing/subscription</code> 或 <code>/api/user/self</code> 拿精确余额（USD/CNY）。</li>
    <li><strong>Kimi / OpenAI 官方 / MiniMax</strong> 这些没公开余额接口的——只能降级显示登录态和套餐到期。</li>
  </ul>
  <p>
    隐私边界很硬：凭证只在本地读取，只发往对应官方接口，不收集任何数据，开源可审计。不做长期用量存储、不做账号体系、不代理 AI 请求——只读余额。
  </p>
  <p>
    工程上还在 dev 阶段：spec 写完了，数据源验证过能拿到，UI 骨架也有了，但 Tauri release build 还没出来。接下来一两天的事。
  </p>
  <div class="journal-ending">
    <p>
      两个工具做的是同一件事：把分散在 4–5 个终端命令、网页和日志文件里的信息，收到一个常驻可见的地方。普通人也用得上——不止工程师。手机上早有「天气」「电池」「日历」挂件，却从来没有「AI 工具今天用了多少」「家里自动化任务跑成了吗」的挂件。那就自己做一个。
    </p>
    <p>
      任务面板已经在桌面跑了两个星期，过得去。AI 余额面板这周把 Tauri release 做出来——下一篇文章应该能上真截图。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
  <category>Tools</category>
  <category>个人工具</category>
  <category>AI 工作台</category>
  <category>SwiftBar</category>
  <category>Übersicht</category>
  <category>Tauri</category>
  <category>public-safe</category>
</item>
<item>
  <title>AI 播放器我做了 3 次才扛下来：不是技术问题，是目标没想清楚</title>
  <link>https://yunlab.ai/notes/from-yuns-radio-to-claudio-three-ai-player-attempts</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/from-yuns-radio-to-claudio-three-ai-player-attempts</guid>
  <description>30 多天里我做了 3 次 AI 私人电台，前两次都死了。回头看根因只有一个——每次开工我都以为目标够清楚，做到一半才发现根本没想清楚。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：工作台俯视图，桌上一台亮着的电台 UI，旁边三张便签写着 yuns-radio / omnivoice / claudio" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">AI 播放器 · 踩坑复盘</p>
  <p class="journal-lead">
    30 多天里我做了 3 次 AI 私人电台。前两次都死了，第三次靠 5 天密集迭代和 13 次独立审计才扛下来。但回头看，根因不是技术——是每次开工我都以为目标够清楚，做到一半才发现根本没想清楚。
  </p>
  <h2>为什么我做了 3 次</h2>
  <p>
    目标从一开始就没变过：把"周一早晨 安静"这句话变成一段 30 分钟、像电台一样的节目。有 DJ 说话、歌之间有过渡、不让我选歌。同一件事我做了三遍。
  </p>
  <h2>踩坑的原因有几个</h2>
  <p>
    3 次的坑摊开，反复出现的就这 4 个。
  </p>
  <p>
    <strong>第一，我还没哼出"DJ 该是什么味"，就开始装 TTS 库了。</strong>第二次项目叫 omnivoice，目标就一件事——装一个开源 TTS（Text-to-Speech，文字转语音）库，看能不能给电台当嗓子。装包、跑通 MPS（Apple 芯片的 GPU 接口）、调通命令行，最后卡在 HuggingFace（最大的开源模型仓库）下不来——6GB 模型停在 12% 不动。表面上是网络死的。但就算模型顺利下来、声音跑出来，我也判断不了"这是不是我要的"——装库之前，我根本没在心里哼过一遍"我要的 DJ 该是什么味"。是央视播音员那种字正腔圆？是凌晨电台主播那种沙哑温柔？还是邻家女孩跟你闲聊？我没想过。模型卡死那一刻不过是给了我一个借口：反正下一步我本来也答不出。
  </p>
  <p>
    <strong>第二，"播放器"和"电台"我一开始没分清。</strong>网易云、Spotify 的中心是用户选歌——点歌、加心、做歌单、跳过下一首。电台不是，它到点就响，DJ 自己说话、自己接歌，不让你选。第一次项目叫 yuns-radio，做着做着 UI 上自然长出"上一首""下一首""喜欢"这几个按钮，我才反应过来——这又变成一个音乐 app 了。但当时整个代码已经按"用户主导"的逻辑搭好了，再改伤筋动骨。cc_claudio 第三次重做，第一条规则就是"用户不选歌，用户给一个意图"——比如"周一早晨 安静"——剩下的归大脑和派遣器去管。
  </p>
  <p>
    <strong>第三，"我电脑上能跑"不等于"它独立跑也能跑"。</strong>我自己电脑上 shell 默认走代理（HTTPS_PROXY 那个环境变量），调 claude CLI、下 HuggingFace 模型都顺。omnivoice 那天 6GB 模型卡 12%，不过是我没挂代理，国内 IP 直连 HuggingFace 慢得没法看。cc_claudio 后来更狠——claude CLI 遇到国内 IP 直接 403。两次同一类：开发时一切正常，独立跑起来就挂。LaunchAgent（macOS 的开机后台服务）起的子进程根本看不见我 shell 里的代理设置，它是个干净环境。但我开工时从没想过"它独立跑起来环境是什么样的"。
  </p>
  <p>
    <strong>第四，做到"能演示一遍"就停了，没追问"我真的会用吗"。</strong>yuns-radio 我做到一个能在浏览器里点一遍的页面就停了。按按钮能听到 DJ 说话（虽然是 TEMP_FALLBACK 占位文本）、能听到歌（虽然只有 30 首示例）、能 pause / skip。在我面前过了一遍，看起来挺像那么回事。但我自己每天早晨真用它听新闻了吗？没有。我让别人试过吗？也没有。我做到"演示给自己看"那一刻，潜意识里觉得"先这样吧"——然后再也没回去。omnivoice 装到命令行 <code>--help</code> 能跑就觉得"基本盘有了"——下一步本来就没答案，所谓"基本盘"也是错觉。
  </p>
  <h2>4 个坑其实是同一个：目标没想清楚就开工</h2>
  <p>
    4 条坑摊开看，没有一条是"代码写错了"。嗓音不是 TTS 引擎选错了，是我根本不知道我要什么嗓音。播放器和电台不是 UI 写错了，是我根本没分清在做哪种产品。"我电脑能跑"不是代理配错了，是我没想过这个东西独立跑起来会身处什么环境。"能演示"不是测试少了，是我根本没定义过"做完了"是什么样子。
  </p>
  <p>
    我原来以为开工时目标已经清楚——"做一个 AI 私人电台"，这句话听起来够具体了。但它什么都没说。嗓音什么味？产品是工具还是节目？独立跑起来环境是什么？做到什么程度算完？这四个问题我一个都没回答，就开工了。每跑两天，就被一个之前没回答的问题撞回来。
  </p>
  <p>
    3 次失败的根因不是技术深浅、不是经验多少。是我每次都跳过了"把目标拆到能回答"的那一关。
  </p>
  <h2>这类工程未来应该怎么做</h2>
  <p>
    现在我手上有一份开工清单，4 件事，写第一行代码之前必须有答案。
  </p>
  <p>
    <strong>核心交付物长什么样？</strong>不是抽象描述，是能哼能画能演的具象。AI 播放器的核心交付物是 DJ 的嗓音——能哼出 30 秒就行。这一条没做到，引擎选型全是瞎选。
  </p>
  <p>
    <strong>不做什么先写下来。</strong>"做什么"清单里漏掉的东西会被默认补回来。yuns-radio 没写"不让用户选歌"这条边界，UI 就自己长出了一堆点歌按钮——不是我决定要做，是默认就长出来了。
  </p>
  <p>
    <strong>它独立跑起来时环境是什么？</strong>装到 LaunchAgent 里、装到别人电脑上、装到我没守着的状态下——它能看见什么、看不见什么。代理在不在？环境变量带不带？依赖装没装？写代码之前就要列清楚。两次国内出口的坑都因为我没列这一条。
  </p>
  <p>
    <strong>"做完了"在我嘴里到底指什么？</strong>能演示？能在我不看的时候活下来？连续 7 天不出事？想清楚，写下来。cc_claudio 装好 LaunchAgent 那天晚上我没动它，让它早晨 6 点自己醒过来给我发第一条 DJ 早报。第二天早上看到那条消息从手机锁屏弹出来——那一刻才是这次的"做完了"。和"能在浏览器里点一遍"完全是两个标准。
  </p>
  <div class="journal-ending">
    <p>
      cc_claudio 现在能跑、能用、能扛重启，但远不是"做完了"。9T 索引因换机器停在 stage 13B，新加的歌进不来。fish-speech 真克隆的 reference 一直没找对，5 个候选还躺在桌面上。Settings、Mini player、Lock Screen 的 Now Playing 都没做。5 天密集迭代的节奏不可持续——下一步是让它在我不动的状态下跑几个早晨和几个深夜，让真使用本身把下一批问题长出来。
    </p>
    <p>
      3 次尝试 30 多天换来的不是一个 .app，是上面那份开工清单。这份清单大概率还会继续长——下次踩到一个之前没列的坑，我就再加一行。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>AI 播放器</category>
  <category>Claudio</category>
  <category>踩坑复盘</category>
  <category>目标拆解</category>
  <category>工程方法</category>
  <category>public-safe</category>
</item>
<item>
  <title>业务通知 vs 系统通知：通知不是一种语义，得分两路发</title>
  <link>https://yunlab.ai/notes/business-vs-system-notifications-two-channels</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/business-vs-system-notifications-two-channels</guid>
  <description>一天之内连修两版，才搞定一个看起来不大的通知问题——飞书里苏晚的早报草稿被 watchdog 标题盖住，发件人又被 fallback 到了赵子龙。两版修完才学到：业务交付和系统健康根本是两种通知。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：手机上两条飞书消息——一条是「苏晚 · 早报草稿」，一条是「赵子龙·控制塔 · 系统告警」，标题清楚分开" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw Studio · 通知设计</p>
  <p class="journal-lead">
    业务交付（苏晚写了一篇早报）和系统健康（watchdog 跑完一轮健康检查）根本不是一种通知。混走同一个通道，业务一定会被淹没。
  </p>
  <p>
    这件事，trial 第一天连摔两次我才真的接受。第一次摔，是看到飞书里全是 watchdog（看门狗，事故时自动救场的兜底进程）的标题，看不到苏晚交了什么稿。第二次摔，是改完标题之后才发现——标题已经是「苏晚 · 早报草稿」了，发件人却还是赵子龙。
  </p>
  <p>
    一天之内连修两版。中间隔了半小时。回头看，这两版加在一起才是一个完整的设计原则。少了任何一版，业务和系统都还会粘在一起。
  </p>
  <h2>前一天刚修完 PATH，以为通知层就稳了</h2>
  <p>
    前一天凌晨修完一个 LaunchAgent（macOS 的常驻任务调度器）PATH 缺失的老坑——runner（任务执行器）跑到一半找不到工具就死掉，5 个早班任务没发出来。当时连夜补了 PATH，又给 runner 套上一层 watchdog wrapper（包装层），让它在任何中断的时候都能兜出一个 alert（告警）。
  </p>
  <p>
    我以为就此稳了——能跑、不掉、有兜底。第二天早上打开飞书，才发现通知层比我想的复杂得多。不是「能不能发出来」，是「发出来的东西人能不能用」。这层之前根本没考虑过。
  </p>
  <h2>v2.4 之前的样子：watchdog 盖住了所有人</h2>
  <p>
    12 点多打开飞书，看到的是这样一串：
  </p>
  <ul class="journal-list">
    <li>「OpenClaw trial watchdog checkpoint complete · task_id=xxx · status=PASS · manual_review_required=yes」</li>
    <li>「OpenClaw trial watchdog checkpoint complete · task_id=xxx · status=PASS · manual_review_required=yes」</li>
    <li>「OpenClaw trial watchdog checkpoint complete · task_id=xxx · status=PASS · manual_review_required=yes」</li>
  </ul>
  <p>
    一条接一条。watchdog wrapper 套上之后，所有任务的通知都被它包了一层——苏晚交了早报、霍锐发了研究 memo（备忘），全部被改写成「watchdog checkpoint complete」的格式。任务的 owner（拥有者，比如苏晚拥有早报草稿）是谁、交付物是什么、内容写了啥，全埋在一堆字段后面。
  </p>
  <p>
    更糟的是——Codex（OpenAI 的命令行 agent，智能体）每跑完一个 checkpoint（检查点），不管 PASS 还是 FAIL 都发一条。99% 的 checkpoint 都是 PASS。99% 只是「健康检查通过了」——这种事根本不需要打扰我。但它跟苏晚的早报混在一起发，结果业务被淹没在系统消息的刷屏里。
  </p>
  <p>
    那一刻才意识到：业务交付和系统健康根本就是两种通知。业务交付是要被人读、被人用、被人回复的东西；系统健康 99% 的时候是 PASS，PASS 的时候根本不该出声。把它们走同一个通道，等于让兜底机制把主交付吃掉了。
  </p>
  <h2>v2.4：拆「内容」——给每个 owner 自己的标题和正文</h2>
  <p>
    上午 12:32 推了 v2.4（commit f62382b）。这一版只解决一件事——让业务交付的通知，长得像业务交付。
  </p>
  <p>
    四个改动一起落地。
  </p>
  <p>
    <strong>第一个：per-owner delivery renderer（每个 owner 自己的交付渲染器，把数据变成消息的那层代码）。</strong>每一种交付物都有自己的标题模板，不再共用一个通用的「watchdog 格式」。
  </p>
  <ul class="journal-list">
    <li>「苏晚 · 早报草稿」「苏晚 · 专栏草稿」「苏晚 · 晚报草稿」</li>
    <li>「霍锐 · 早盘研究早报」「霍锐 · 午盘小结」「霍锐 · 收盘总结」「霍锐 · 收盘研究复盘」「霍锐 · 深度研究复盘」「霍锐 · 周总结」「霍锐 · 下阶段研究建议」</li>
    <li>「沈知行 · 每日 source package（信源包）」</li>
  </ul>
  <p>
    标题一眼看过去就知道是谁、交了什么。不用再往下翻 task_id。
  </p>
  <p>
    <strong>第二个：真正的正文摘要（snippet）。</strong>之前正文是把 task_id、status、output_path 全 dump（堆出来）一遍，最后才挂一个文件路径。改完之后，renderer 直接读 output.md，从 <code>## content_body</code> 这个 anchor（锚点）之后提取真正的正文，发 1500 字摘要、800 字段落底。我看到的是稿子本身，不是稿子的元数据。
  </p>
  <p>
    <strong>第三个：Codex watchdog 静默原则。</strong>checkpoint 读自己生成的 JSON 里的 overall_status——
  </p>
  <ul class="journal-list">
    <li>PASS / PASS_WITH_WARNINGS → 沉默。只写文件，不发飞书。state 里记录一行 skipped=true、reason=checkpoint_pass_silent。</li>
    <li>WARN / FAIL / UNKNOWN → 才发 alert。</li>
  </ul>
  <p>
    PASS 是默认状态，不该有声音。只有 PASS 不再是 PASS 的时候，才值得打扰人。
  </p>
  <p>
    <strong>第四个：统一 alert 标题。</strong>所有系统告警一律用「OpenClaw Watchdog Alert」做前缀。不再让 watchdog 套用业务标题的格式。这样从标题就能一眼分出来：这是业务，那是系统。
  </p>
  <p>
    跑了个 FAKE_PASS_SMOKE 测试——人为让 Codex 返回 PASS 看 silence path（沉默路径）有没有真生效。notification log 行数从 13 进去、13 出来——没发出去。state 里多了一条 skipped=true reason=checkpoint_pass_silent。沉默路径走通了。
  </p>
  <p>
    然后把 5 条 Day 1 被 watchdog 标题盖掉的业务交付重发了一遍。这次进来的是「苏晚 · 早报草稿」+ 真正的正文。我以为这就完了。
  </p>
  <h2>v2.5：拆「身份」——发件人必须是自己</h2>
  <p>
    问题没解决。
  </p>
  <p>
    v2.4 推出去后看了一眼新消息——标题确实变成「苏晚 · 早报草稿」了，可飞书消息列表里，发件人头像和名字还是「赵子龙·控制塔」。
  </p>
  <p>
    这是 impersonation（冒名顶替）。标题写着苏晚，发件人是赵子龙——心理上就是赵子龙在冒充苏晚说话。
  </p>
  <p>
    我去追底层。openclaw message send（OpenClaw 平台的消息发送命令）调用时没带 <code>--account</code>，gateway（网关，对外暴露服务的入口）直接 fallback（兜底）到了 zhao_zilong——他是平台的默认 account（账号）。所以所有消息不管标题写谁，最终都是从赵子龙的 bot（机器人）发出来的。
  </p>
  <p>
    这个问题之前并不是没意识到——一直以为是「内容里写明是谁」就够了。v2.4 改完看到效果，才明白根本不够。通知的「发件人身份」和「内容标题」不能分离。一旦分离，人看到的就是冒名顶替，不是协作。
  </p>
  <p>
    6 个 Agent，必须各自从自己的飞书 bot 发。每条消息要从对应的 peer（同事级别的对象）进入我的对话框，而不是全部挤在赵子龙一个窗口里。这是身份隔离，不是 UI 美化。
  </p>
  <p>
    下午 13:00，v2.5（commit 5129a83）。
  </p>
  <p>
    <strong>第一个改动：AGENT_FEISHU_ROUTES 显式映射。</strong>建一张 routing table（路由表）把每个 agent 钉死到具体的 profile（配置档案）+ account + 显示名：
  </p>
  <ul class="journal-list">
    <li>suwan → openclaw-studio / su_wan / 苏晚</li>
    <li>huorui → openclaw-studio / huo_rui / 霍锐</li>
    <li>shenzhixing → openclaw-jiyanran / shen_zhixing / 沈知行</li>
    <li>watchdog → openclaw-studio / zhao_zilong / 赵子龙·控制塔</li>
  </ul>
  <p>
    注意第三条——沈知行不在 openclaw-studio 这个 profile 里，他属于 openclaw-jiyanran。其实 routing 不只是选 account，profile 也得跨过去。这是早期我没想到的复杂度，但 routing table 把它显式写出来之后，跨 profile 也只是表里多一列。
  </p>
  <p>
    <strong>第二个改动：send_feishu_message(message, route=...) + resolve_feishu_personal_target(profile, account)。</strong>把 profile 和 account 一路传进每一次 openclaw 调用——不再让 gateway 猜。每条消息从 owning agent 自己的 bot 发出去。飞书界面上是 4 个不同的 open_id（飞书内部的用户唯一标识）进入我的对话框，每个 Agent 一个独立窗口。
  </p>
  <p>
    <strong>第三个改动：「文章 first」投递模板。</strong>v2.4 的正文摘要是「头部 dump 一堆字段 + 1500 字截断的正文」。v2.5 改成正文 first——业务交付物本身就是消息内容，不需要 header dump（消息头部把一堆字段全堆出来）。苏晚的文章整篇过，最多 12000 字（不再 1500 字截断，因为正文就是交付物）。霍锐按 <code>##</code> 段落 parse（解析），12 个禁止词在投递时重新扫一遍——避免合规问题被 renderer 放过去。
  </p>
  <p>
    <strong>第四个改动：watchdog alert 改人话。</strong>之前 watchdog alert 是字段 / 路径 dump，长得也像机器在自言自语。改成「什么事 / 影响 / 已做 / 需要」四段中文。我看到 alert 不再需要解读，知道发生了什么、谁受影响、watchdog 已经做了什么、需要我做什么。
  </p>
  <h2>两版加起来才是真正的「语义分离」</h2>
  <p>
    v2.4 和 v2.5 解决的不是同一个问题。
  </p>
  <p>
    v2.4 解决的是「内容看起来像谁」——标题、摘要、沉默规则，让业务消息看起来是业务，让系统告警看起来是系统。
  </p>
  <p>
    v2.5 解决的是「消息从谁那儿发出来」——routing table、profile + account 显式传参、独立 bot，让每个 Agent 真的是自己在说话。
  </p>
  <p>
    单做 v2.4，标题对了但发件人错——心理上是 zhao_zilong 在冒名顶替苏晚。单做 v2.5，发件人对了但内容还是 header dump——苏晚的 bot 发出来的还是机器味的字段拼接。两个一起做完，业务和系统才彻底走两条管道：内容上分开、身份上也分开。
  </p>
  <p>
    回头看，这是「业务通知 vs 系统通知」的完整设计。一个通知系统要能真正分两路，至少要在两层上同时分——标题和正文一层（看起来是谁），发件身份一层（从哪儿发出来）。少一层都不算分。
  </p>
  <h2>这个设计能推广到哪里</h2>
  <p>
    这件事在 OpenClaw Studio 里是飞书 + 多 Agent，但原则跟工具无关。任何「业务结果 + 系统健康」共用一个通知通道的场景都会撞上同样的问题：
  </p>
  <ul class="journal-list">
    <li>CI/CD（持续集成 / 持续交付）——构建成功和系统监控走同一个 Slack 频道，构建成功被监控的 PASS 心跳淹掉。</li>
    <li>监控告警——业务指标异常和基础设施健康用同一种标题模板，业务异常被基础设施 PASS 刷屏。</li>
    <li>定时任务、爬虫——任务交付和调度器健康混在一个通知队列，交付内容很快被「调度器跑完一轮」的回执盖住。</li>
    <li>多 Agent 系统——所有 Agent 走同一个 bot，每条消息看起来都是同一个发件人在说话，协作感被压平成单兵。</li>
  </ul>
  <p>
    判断标准很简单：打开通知列表，能不能一眼分出「这条是给我看的内容」和「这条是机器在打卡」？分不出来，就该拆两路。拆两路不只是分频道、加 tag——要从内容模板、沉默规则、发件身份三个地方一起拆。
  </p>
  <p>
    还有一条：PASS 通知和 FAIL 通知不该用同一种标题模板。PASS 是默认状态，默认状态不该有声音。让 FAIL 自己有一套显眼的标题前缀（比如「OpenClaw Watchdog Alert」），人一眼能从 100 条消息里抓出来。这件事跟分两路是配套的——分了两路还让 PASS 出声，业务管道照样会被淹。
  </p>
  <div class="journal-ending">
    <p>
      v2.4 + v2.5 跑过几天，6 个 Agent 各自从自己的 bot 发消息，PASS 不出声，FAIL 显眼。业务交付暂时没被系统心跳吃掉，这一层算是稳了。
    </p>
    <p>
      但每加一个新 Agent，AGENT_FEISHU_ROUTES 这张表都要手动加一行。下一步想做成自动注册——agent 启动时自己声明 route，不再依赖人维护一张全局表。Codex watchdog 的「沉默阈值」还在调，PASS_WITH_WARNINGS 算 PASS 还是算 WARN，目前还有几个边缘 case 没收口。霍锐的研究复盘有时候特别长，按 <code>##</code> 段落 parse 还会丢边角内容——12000 字上限对苏晚够用，对霍锐紧一点。
    </p>
    <p>
      通知层的设计永远没有「做完」这一说。每加一个 Agent、加一种交付物、加一条系统告警，这两层都得重新过一遍。现在习惯了——通知不是一种语义，它至少有两种，而且会继续分裂下去。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Studio</category>
  <category>通知设计</category>
  <category>身份隔离</category>
  <category>飞书</category>
  <category>public-safe</category>
</item>
<item>
  <title>Day 1 凌晨炸了：我没想到 trial 第一天就要写 watchdog</title>
  <link>https://yunlab.ai/notes/day-1-trial-incident-launchagent-path-watchdog</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/day-1-trial-incident-launchagent-path-watchdog</guid>
  <description>7 天试生产第一天凌晨，4 个业务任务全跑通，4 条飞书通知全炸。根因是 macOS LaunchAgent 默认 PATH 不含 Homebrew——老坑，但它把我推去写了第一版 watchdog。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：清晨工作台，4 张便利贴写着 06:50 / 07:30 / 08:40 / 10:30，旁边一个看门狗的小图" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw Studio · 事故复盘</p>
  <p class="journal-lead">
    第一天凌晨醒来，我打开飞书，一片寂静。再点开本地目录——4 份草稿、4 份研究 memo（备忘录）已经在那了。任务全跑了，但没有任何一条通知告诉我。
  </p>
  <p>
    前一晚我刚装好 OpenClaw Studio 的 7 天 trial（试运行）——一个 LaunchAgent（macOS 的开机后台服务），每 30 分钟唤醒一次，按时间表去触发苏晚、霍锐、沈知行这几个 Agent（智能体）。我本来的想法是：先让它跑两天，看看节奏。如果稳，就上更复杂的事情。
  </p>
  <p>
    结果它在第一天凌晨就告诉我——稳是个奢侈品。
  </p>
  <h2>我装了什么</h2>
  <p>
    Trial 这一层做的事情很简单：一个 LaunchAgent 每 30 分钟唤醒一次 runner（实际执行调度的 Python 脚本），runner 翻一遍 schedule（时间表），到点了就触发对应的 Agent，把 output（输出）写到本地目录，再给我发条飞书通知。
  </p>
  <p>
    早晨这一段是密度最高的——06:50 苏晚开始写早报草稿；07:30 第二个任务；08:40 第三个；10:30 第四个。每一个都有自己的 owner、自己的 output 文件、自己应该发的飞书消息。整个系统的设计是「我醒过来打开手机，飞书里 4 条通知按时间排好，我点哪条看哪条」。
  </p>
  <p>
    设计是这么写的。第一天凌晨发生的事完全不是。
  </p>
  <h2>凌晨的 4 个"半挂"</h2>
  <p>
    06:50 那条任务确实跑了——苏晚的早报草稿写出来了，文件在该在的位置，时间戳对得上。07:30 跑了。08:40 跑了。10:30 也跑了。Output 文件 4 份，returncode（命令执行的退出码）全是 0。
  </p>
  <p>
    但飞书里什么都没有。
  </p>
  <p>
    一开始我以为是飞书 bot 出了问题——token 过期？webhook 改了？翻日志才发现，每一条飞书通知都炸了，错误信息一模一样：
  </p>
  <p>
    <code>env: node: No such file or directory</code>
  </p>
  <p>
    Node 找不到了。我盯着这行看了几秒——明明我刚才在终端里 <code>which node</code> 还好好的，<code>/opt/homebrew/bin/node</code>，路径清清楚楚。怎么 runner 跑到一半，发飞书的时候说找不到 node？
  </p>
  <p>
    更别扭的是这个「半挂」的形态——业务全跑通了，通知全死了。它不是"系统挂了"那种干脆的事故，是"系统一部分好好的、另一部分悄悄烂掉"的事故。output 真的在；草稿真的写完；研究 memo 也真的产出来了。我不主动翻本地目录，根本不知道这些东西已经在了。
  </p>
  <p>
    一个调度系统，最怕这种"我以为它没做，其实它做完了；我以为它做完了，其实它根本没跑"。这种事故最狠的地方——你会彻底失去对系统的信任。
  </p>
  <h2>根因：LaunchAgent 的 PATH 不是"和你的终端一样"</h2>
  <p>
    排查时，我先去看 runner 自己的环境。Runner 是 Python 写的，在 venv（Python 虚拟环境）里跑——Python 解释器路径写死，依赖也都在 venv 里，runner 自己能起得来。
  </p>
  <p>
    但 runner 在发飞书的时候不是直接调 Python——它 exec（执行）一个叫 <code>openclaw</code> 的 CLI 命令。这个 CLI 是 Node 写的，shebang（<code>#!/usr/bin/env node</code> 这一行的统称，告诉系统用哪个解释器跑脚本）是 <code>#!/usr/bin/env node</code>。
  </p>
  <p>
    问题出在这。<code>env node</code> 要去 PATH（操作系统找命令的搜索路径）里找 node。终端里的 PATH 是我 shell 配置一层层叠出来的——<code>/opt/homebrew/bin</code>、<code>/usr/local/bin</code>、各种语言版本管理器、各种自己的 bin 目录，加起来一长串。可 macOS launchd 启动的 LaunchAgent 默认 PATH 极其干净，只有这 4 条：
  </p>
  <p>
    <code>/usr/bin:/bin:/usr/sbin:/sbin</code>
  </p>
  <p>
    Apple Silicon 上的 Homebrew 默认装在 <code>/opt/homebrew/bin</code>。这条目录不在 LaunchAgent 的 PATH 里。所以 <code>env node</code> 死活找不到 node，shebang fast-fail（一上来就失败），整个 openclaw CLI 一行都没跑就退出了。
  </p>
  <p>
    很多人第一次踩这个坑都不敢相信——"明明我的终端能跑啊"。大家潜意识里总觉得，自己的电脑就是"自己的电脑"，PATH 应该全局都一样。但 LaunchAgent 不是你的终端。LaunchAgent 是 launchd 拉起来的子进程，环境变量是 launchd 自己定义的，跟你的 shell 配置没关系。
  </p>
  <p>
    最阴的是——业务 Agent 没事。苏晚跑的是 Python venv，路径写死；霍锐也是 venv 里的 Python；沈知行也是。它们都不依赖 PATH 找解释器。业务层照样跑通，output 照样写出。只有通知层——那个用 Node CLI 发飞书的层——挂了。
  </p>
  <p>
    业务跑通、通知全炸，于是有了"全部成功又全部失败"的奇景。
  </p>
  <h2>修了根因，还是要写 watchdog</h2>
  <p>
    根因 fix 其实只有一行——在 runner 的 module import（Python 模块导入时执行的代码）阶段，把 <code>/opt/homebrew/bin</code> prepend 到 PATH 里。下一次 LaunchAgent 唤醒 runner，runner 一加载就把 PATH 修好，<code>openclaw</code> CLI 就能找到 node 了。
  </p>
  <p>
    问题是——只修这一行，够吗？
  </p>
  <p>
    我犹豫了几分钟，决定不够。
  </p>
  <p>
    原因很简单。今天炸的是 Node CLI 找不到 node。下一次可能是别的——某个 Python 包 import 时去找系统 binary，找不到；某个第三方工具升级路径变了，runner 里写死的路径过期；某次 macOS 更新悄悄改了 launchd 的环境变量；甚至飞书自己出 5 分钟问题。这些事不会用"node not found"这种一眼能看的形式出现，它们会以各种新姿势出现——但形态都是同一种：业务跑通，交付通知漏掉。
  </p>
  <p>
    根因可以一次一次修，但"半挂"作为一种事故模式不会消失。单修 PATH 是 root cause（根本原因）fix，watchdog（看门狗，事故时自动救场的兜底进程）是另一层——它不解决某个具体的根因，它只给所有未来的"半挂"事故一个自动救场的机会。
  </p>
  <p>
    Runner v2.3 就是在这个想法下写出来的。它一共四块改动：
  </p>
  <ul class="journal-list">
    <li>第一块，根因 fix——module import 时把 Homebrew 路径 prepend 进 PATH，今天的事故不再以这个形式出现。</li>
    <li>第二块，重试通道——retry_pending_notifications，每次 LaunchAgent 唤醒时扫一遍最近的任务，发现"output 在但 notification 没发"的，自动 retry（重试），每个 task 最多 retry 4 次。</li>
    <li>第三块，deterministic watchdog——每次唤醒主动检查 4 类问题：task_missed（任务没跑）、output_missing（output 应该在但不在）、notification_missing（output 在但通知没发）、boundary_fail（跨边界的状态不一致）。发现就发一条去重的飞书 alert（告警），告诉我"发生了什么、在哪、什么时候发生的"。</li>
    <li>第四块，Codex watchdog checkpoint——在一天里 6 个固定时刻跑一次 Codex（OpenAI 的 CLI agent）的 exec，read-only sandbox 里审计当天的所有调度状态，写 markdown + JSON 的 checkpoint（检查点），加发一条飞书 summary。</li>
  </ul>
  <p>
    第二块和第三块是对称设计——重试通道是"我看到漏了，自动救回"；deterministic watchdog 是"我看到漏了，告诉你"。两者都是兜底，不是首选路径。首选路径永远是 runner 第一次发通知就成功。
  </p>
  <p>
    第四块 Codex watchdog 多一层意义——deterministic watchdog 只能识别我能预见的事故类型；Codex watchdog 能识别我没预见的、需要语义理解才能发现的事故。代价是它贵、慢、需要外部依赖。所以它的频率是"6 次/天"——早晨业务密集时勤一点，下午和晚上稀一点。
  </p>
  <h2>Catch-up：4/4 自动救回</h2>
  <p>
    v2.3 装上去之后，我没急着发新通知。我手动触发了一次 LaunchAgent 唤醒——让 retry 通道扫一遍今天凌晨那 4 条死掉的通知。
  </p>
  <p>
    扫描结果：4 个 task 都被识别成 notification_missing；都有 output 文件，都有正确的时间戳；都符合 retry 条件。
  </p>
  <p>
    Retry 一遍。4 条飞书通知，按时间顺序，按原本应该发出去的样子，一条一条到了。returncode 全是 0。
  </p>
  <p>
    最让我安心的是这句——「没重跑 agent，没覆盖已有输出」。retry 只发通知，不重新触发业务。设计时就定了这个约束——业务任务里有不可逆的操作（写历史账本、追加 audit 日志），重跑会污染状态。Catch-up（事后追跑漏掉的事情）的边界永远只在"通知层"——业务那层已经做完了，不能再动。
  </p>
  <p>
    4/4 自动救回。fix commit 是 e752a93。
  </p>
  <p>
    那一刻的感受很奇特——事故发生了，事故被识别了，事故被自动补救了，整个过程我只手动触发了一次唤醒，剩下的全是系统自己做的。事故没被藏起来，也没被放大。我第一次切身体会到 watchdog 真正的价值——它不创造新功能，只让事故的恢复成本接近 0。
  </p>
  <h2>这件事让我学到了几件事</h2>
  <p>
    复盘到最后，能写进规则里的就这几条——之后再踩类似的坑，我希望自己能直接想到这些。
  </p>
  <ul class="journal-list">
    <li><strong>LaunchAgent 的 PATH 不是"和你的终端一样"。</strong>这是个老坑，但每次新装 LaunchAgent 我还是会想当然以为环境是一样的。下次再装 LaunchAgent，第一件事是把 PATH 显式写清楚——要么 plist 里写，要么 runner 第一行 prepend。不要假设"应该没事"。</li>
    <li><strong>"业务跑通"和"交付完成"是两件事。</strong>output 落地只是中间状态。真正的交付是"用户收到通知 + 用户能找到 output"。中间任何一环断了，都算交付失败——不管 output 写得多漂亮。下次写调度系统，把"交付"作为最后一道关，比"任务执行成功"更严格。</li>
    <li><strong>根因 fix 和兜底层要分开做，但要一起上线。</strong>根因 fix 是 PATH，防止今天这个事故。兜底层是 retry + watchdog，防止以后所有形态相似的事故。两者不互相替代。只修根因，下次新形态的"半挂"还会让我手动救场；只加兜底，今天这个事故每次唤醒都会触发一次 alert，最后变噪音。</li>
    <li><strong>watchdog 是"事故恢复成本接近 0"的保险。</strong>事故没发生时它像浪费。价值只在事故发生的那一瞬间——它把"我醒来手动排查 2 小时"变成"系统自己处理完，发我一条 summary"。买这个保险的成本是写代码的时间；不买的成本是未来某天的某 2 个小时。</li>
  </ul>
  <h2>所以现在 v2.3 跑成什么样</h2>
  <p>
    v2.3 已经跑了好几天。Day 1 之后的事故密度反而比想象中高——倒不是 PATH 问题再出现，别的形态的"半挂"开始冒头。runner 的 retry 通道兜住了大部分；deterministic watchdog 发了几条 alert，每次我都能在 5 分钟内判断要不要介入；Codex watchdog checkpoint 写了一堆 markdown，给了我一份"每天系统大致跑成什么样"的全景视图。
  </p>
  <p>
    但也有几个地方还没收口。
  </p>
  <p>
    Deterministic watchdog 的去重粒度还在调——有时候同一个 task 会被识别成两次不同的 missed，飞书里就收到两条几乎一样的 alert。不致命，但会污染信号。理想状态是"同一次事故只 alert 一次，除非状态真的变了"。
  </p>
  <p>
    Codex watchdog checkpoint 的频率（6 次/天）现在是固定的，但早晨密集、下午稀少。下一步我想让它按"事故密度"动态调——前一段时间事故多就勤一点，没事故就稀一点。但这得先定义"事故密度"，目前还没。
  </p>
  <p>
    最大的一摊——v2.3 修的是 trial 这层调度的通知通道。但 6 个 Agent 各自的"业务通知"通道在后面几天又有别的坑——业务消息从哪个 bot 发、用谁的身份、消息怎么排版、谁应该收到、谁不应该收到。另开一篇说。
  </p>
  <div class="journal-ending">
    <p>
      第一天凌晨的事故，把"加 watchdog"这件本来排在第二周的事，提前到了第一天下午。Trial 给我的第一个礼物——没让我等一周才发现问题。
    </p>
    <p>
      Trial 不是为了证明系统能跑，是为了在低成本的环境下让系统的脆弱点全部暴露出来。每暴露一个，我修一个；每修一个，watchdog 长大一点。Day 1 长出 PATH fix 和 retry 通道；后面几天长出别的东西。每一天的 watchdog 都比前一天稍微更聪明一点。
    </p>
    <p>
      事故是常态，watchdog 也在长。我不再期待"装完就稳"——我期待的是"每次事故都让系统下次自己救场的能力强一点"。在这之前，我还在改。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Studio</category>
  <category>LaunchAgent</category>
  <category>事故复盘</category>
  <category>watchdog</category>
  <category>public-safe</category>
</item>
<item>
  <title>9TB 音乐库只读索引：我给自己定的几条工程约束</title>
  <link>https://yunlab.ai/notes/nine-tb-music-readonly-index-engineering-model</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/nine-tb-music-readonly-index-engineering-model</guid>
  <description>把 9TB 个人音乐库做成可查询索引，最难的不是写代码，是先把「绝对不动原盘」这条约束钉死。从只读原则、断点续扫到错误隔离，五条约束、十个阶段、一个还在跑的踩坑现场。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：堆得高高的音乐库光盘和硬盘旁边一台笔记本，屏幕上是索引数据" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">音乐索引工程笔记</p>
  <p class="journal-lead">
    把 9TB 个人音乐库做成一个能查询的索引，听起来像写脚本就能搞定。真正难的不是写代码，是先把「绝对不动原盘」这件事钉死。
  </p>
  <p>
    我家里那块硬盘装着 115,999 个音频文件，FLAC、MP3、WAV、DSF 都有，加起来 7.39 TB，差不多把一块 9TB 盘塞满。这批东西攒了十几年——有自己抓的 CD，有朋友传的，有早年下载的，有从老硬盘救回来的。每一份背后都有点故事，但故事不重要，重要的是它现在是一坨黑箱：我知道它在那儿，却查不动它。
  </p>
  <p>
    第一次想做索引（index）的时候，我差点直接上手——扫一遍、读 metadata（元数据）、丢进数据库、给它配个搜索界面。后来停住了。但我见过太多人——也包括早年的我自己——一动手就把原盘写坏过。重命名、移动、改 tag、加封面图，每一次都是「就这一下」，每一次回头看都觉得「当时不该动」。
  </p>
  <p>
    所以这次我先做一件反直觉的事：把规则写在代码前面。五条约束，写得比业务流程还重。脚本可以重写，约束不能破。
  </p>
  <h2>第一条：只读原则——禁止动原盘任何文件</h2>
  <p>
    最朴素的一条，也是最容易被破的一条。只要扫描脚本里出现 open(path, 'w')、出现 shutil.move、出现 os.rename，整条约束就废了。
  </p>
  <p>
    我把它写得很死——不修改、不移动、不删除、不重命名。连「顺手把文件名规范一下」都不行。原因不是技术，是信任：这块盘是我十几年的资料，我不允许任何脚本对它做「我觉得对你好」的事。索引可以重做，原盘改坏了就回不去。
  </p>
  <p>
    实际落地是工具层拦截。所有走原盘的路径，只允许 read 模式打开；任何写入意图直接 raise。听起来夸张，但跑了几周下来，这一条挡掉的不只是我自己的失误——还挡掉了几次 AI 想「帮我清理一下文件名」的越界。
  </p>
  <h2>第二条：禁止在原盘写任何东西——隔离工作区</h2>
  <p>
    第一条的延伸，但比它更细。不仅不能改原文件，也不能在原盘里建临时数据库、缓存、日志、状态文件。
  </p>
  <p>
    这条我以前没意识到要单独立。直到有一次我用某工具扫照片库，扫完发现它在每个子目录里默默生成了一个 .cache 文件。表面无害，但原盘从此就不再「干净」——它带了这个工具的痕迹，下次换工具还要先清理。
  </p>
  <p>
    所以现在所有索引输出——数据库、缓存、ffprobe 报告、错误日志、断点状态——全部写在另一块工作盘的专用工作目录里。原盘永远只承担「数据源」这一个角色，不承担工作台。两者物理隔离，连挂载点都分开。
  </p>
  <p>
    这一条带来的副作用很爽：原盘可以随时卸载、随时换接口、随时复制到另一台机器，索引那边一点都不疼。
  </p>
  <h2>第三条：禁止联网识别——纯本地处理</h2>
  <p>
    这条最容易被现代工具背刺。MusicBrainz、AcoustID、各种在线歌词识别——只要你不主动关掉，它们就默认开着。每扫一首歌就发一次指纹去对，几天下来你的整个音乐库被人云端「画过像」了。
  </p>
  <p>
    我不想要这种便利。一是隐私——我的私人音乐听感、口味、收藏构成，没必要交给任何在线服务做训练样本。二是稳定——联网识别会让索引结果依赖于「当时云端返回了什么」，今天匹到的明天可能匹不到，索引就不再是可重现的了。
  </p>
  <p>
    所以纯本地。metadata 只读文件自己带的 tag，质量评分只看文件属性，重复判定只看本地哈希和时长。能不能识别得更准？当然能。但识别更准的代价是失去「这套索引完全可重建、完全可离线」这个属性，我换不起。
  </p>
  <h2>第四条：断点续扫——重复运行不能重复写</h2>
  <p>
    11 万多个文件全量跑一次不是 5 分钟的事。第一次跑完整的 ffprobe（提取音频元数据的工具）+ mutagen（另一个 metadata 读取工具）扫描，连续跑了一夜还没收。中途网络断、电源跳、自己手贱按了 Ctrl-C，都得能从断点继续。
  </p>
  <p>
    断点续扫听起来简单，做起来全是坑。最大的坑是「重复写入」——上次扫到一半中断，下次重启时如果不去重，同一首歌就被插了两次。整个索引数据从此不可信。
  </p>
  <p>
    这条约束的真正含义，其实不是「能从中断处继续」，而是「重复运行同一个脚本，结果必须收敛到同一份索引」。同一个文件路径只允许有一行记录，幂等是底线。技术上靠数据库唯一索引 + 显式 upsert（更新或插入）+ 一份独立的「已处理路径」表三件套来保证。
  </p>
  <h2>第五条：错误隔离——损坏文件不能中断任务</h2>
  <p>
    11 万个文件里有损坏的、有权限不对的、有文件名编码诡异的、有格式头崩坏的。最终统计下来，543 个文件 ffprobe 读不出，744 个文件 mutagen 失败。两千多个错——但全量扫描不能因为这两千多个错而停。
  </p>
  <p>
    早期版本我图省事，遇到 exception 直接抛出来。结果脚本跑到 30% 就死了，重启从 0 再跑，跑到 35% 又死了。永远在前 40% 里打转。
  </p>
  <p>
    后来改成「每个文件独立 try-except，错误进单独的 error log，主流程继续」。这一条之后才真的扫得完。错误隔离说到底就是承认现实——一个 11 万文件的库里出几千个错是正常的，不正常的是因为这几千个错就放弃剩下的 10 万。错误要记录，不能用沉默掩盖；但记录归记录，主任务不停。
  </p>
  <h2>为什么要分十个阶段——按风险切，不按功能切</h2>
  <p>
    五条约束钉死之后，我没有写一个「一键扫描」的大脚本。我把整个工作切成了十个阶段，每个阶段单独跑、单独验、单独收口。
  </p>
  <p>
    切阶段的逻辑跟切功能不一样。切功能是「这块代码负责什么」，切阶段是「这一步能炸出什么风险」。按风险切的好处是：某一阶段失败了，损失的只是这一阶段的成本，前面的不用回炉。
  </p>
  <ul class="journal-list">
    <li>Stage 0：安全检查 + 工作目录初始化——确认原盘只读、工作盘可写、所有路径都不会越界。</li>
    <li>Stage 1：依赖检查——ffprobe、mutagen、Python 环境、数据库 schema 全部就位。</li>
    <li>Stage 2：音频文件发现——只读扫描全盘，把 11 万多个文件的路径和大小列出来。不读 metadata，先确保走完一遍盘。</li>
    <li>Stage 3：抽样验证——抽 300 个文件试着读 metadata，看成功率多少，估算全量要多久。</li>
    <li>Stage 4：全量 metadata 读取——基于抽样的估算放心跑全量，断点续扫开着。</li>
    <li>Stage 5：重复候选分析——只「标记」可能重复的文件，绝对不删。</li>
    <li>Stage 6：质量评分草案——给每首歌一个 0-100 的分。</li>
    <li>Stage 7：AI DJ 初始索引 v0——把前面所有数据合成一份可查询的索引。</li>
    <li>Stage 8：最终收口——对照原盘统计校验完整度。</li>
    <li>Stage 9：索引验收与修补——人工抽查几百条，找规则漏洞。</li>
    <li>Stage 10 及以后：播放器集成、标签增强、音频特征分析——这些是另一摊事，等前 9 个稳了再说。</li>
  </ul>
  <p>
    分十阶段最大的好处是——任何一阶段失败，我只回退这一阶段。Stage 4 跑了一夜失败，前面 Stage 0-3 还在，重跑只重跑这一段。而如果是一个大脚本一夜跑完，失败就得全部重来。
  </p>
  <p>
    另一个好处是——每个阶段有自己的「通过标准」。Stage 3 抽样如果成功率低于 90%，就不进 Stage 4，先回去查为什么失败。后面阶段的输入才能干净。
  </p>
  <h2>质量评分的六个维度——为什么不做听感测试</h2>
  <p>
    Stage 6 的质量评分是整个索引最容易被质疑的部分。有人会问：你怎么不做 ABX 听感测试（盲听对比，业内最严谨的音质评估方法）？怎么不做频谱分析？怎么不算动态范围？
  </p>
  <p>
    我都想过。但最终选择了六个非常笨的维度：</p>
  <ul class="journal-list">
    <li>无损还是有损——FLAC、WAV、DSD 起步比 MP3 高。</li>
    <li>采样率和码率——高采样率加分，码率太低扣分。</li>
    <li>metadata 完整度——title、artist、album、year、genre 缺哪个扣哪个的分。</li>
    <li>duration 正常性——时长异常的（比如 5 秒或 8 小时）单独标记。</li>
    <li>读取成功率——ffprobe 或 mutagen 任一失败的扣大分。</li>
    <li>疑似重复——和 duplicate group（重复候选组）关联的扣关联分。</li>
  </ul>
  <p>
    为什么不做听感？它太主观，没法机器化；而我要的是一份能跑、能复现、能在 11 万首歌上一视同仁打分的索引。一旦引入听感，第二天我自己重听就会想推翻第一天的判断，整个评分就再也不稳定了。
  </p>
  <p>
    六个维度都是文件层面的客观属性——同一个文件今天跑明天跑结果完全一样。这才是「索引」该有的样子，不是音质评测。整体平均分跑下来是 78.1，分布也比较合理——FLAC 普遍 85 以上，MP3 普遍 60-75，少数损坏文件拉到 30 以下。够用了。
  </p>
  <h2>三个踩坑实录——约束钉得再死也会被啃出洞</h2>
  <p>
    五条约束 + 十个阶段，听起来很周全。实际跑下来还是被坑了好几次。挑三个最典型的说。
  </p>
  <h3>Unicode NFC/NFD 规范化</h3>
  <p>
    macOS 上的文件名走 NFD 编码（把组合字符拆开存），Linux 上很多脚本默认按 NFC（合成形式）处理。同一个中文歌曲名字，在 macOS Finder 里看着没问题，但 Python 拿到那个路径去 os.stat，可能就报「文件不存在」。
  </p>
  <p>
    这个坑卡了我两天。一开始以为是权限问题，查了半天没结果。后来肉眼对比两个看似一样的字符串才发现——它们字节级别不一样。修法是在所有路径进入数据库之前，统一 normalize 成 NFC。这不属于「改原盘」——原盘上的文件名一个字节都没动，只是数据库里存的是规范化后的版本。
  </p>
  <h3>insert bug</h3>
  <p>
    Stage 5 做重复候选分析的时候，要把每个文件和它的潜在重复对一起插入一张表。我第一版图省事，没加 unique constraint（唯一约束），结果同一对文件被插了三遍——分析逻辑里好几条规则都会判定它们是重复。
  </p>
  <p>
    表面上没坏，查询时多几条记录而已。但接到 Stage 6 评分时就出事了——同一首歌因为有「三条重复关联」被扣了三次分，掉到了不该掉的分段。修法是显式加唯一约束 +（pair_a, pair_b）双向去重，并把 Stage 5 的输出当作 Stage 6 的输入前先 verify 一次。
  </p>
  <p>
    教训不是「记得加 unique constraint」，而是——任何「分析」类的脚本默认会重复触发，必须在数据层挡死，不能依赖业务层小心。
  </p>
  <h3>ffprobe timeout</h3>
  <p>
    Stage 4 全量跑 metadata 的时候，遇到几个超大的 DSF 文件（单文件几个 G），ffprobe 读到一半卡住，子进程没退出，主脚本也不前进。一夜下来扫了几百个就死了。
  </p>
  <p>
    修法是给每次 ffprobe 调用加 watchdog（看门狗，定时检查子进程的机制）——超过 30 秒强制 kill 子进程，错误进 log，文件标记成「读取超时」，主流程继续。这套补丁打上之后，Stage 4 才真正能一夜跑完。
  </p>
  <p>
    这事反过来让我重新审视第五条约束「错误隔离」——错误隔离不仅要管 exception，还要管「不返回也不报错」的情况。沉默挂起比抛错更难抓，必须主动加超时。
  </p>
  <h2>当前进度——Stage 13B 还在跑</h2>
  <p>
    Stage 0 到 12 已经跑完。索引第一版（v0）能跑能查，11 万多个文件全部进表，metadata 完整度大致是 title 90%、artist 90%、album 89%、year 33%、genre 35%。前三个还行，后两个偏低——很多老 MP3 当年抓盘时压根没填 year 和 genre。
  </p>
  <p>
    Stage 13B 在做的事是反向校验——拿索引里的统计数据和原盘上的实际目录结构再对一遍，看有没有「索引里有但盘上找不到」或者「盘上有但索引里缺失」的情况。这一步本来该是 Stage 8 收口的活儿，但 Stage 8 当时偷了懒只做了正向校验，只好另开一个 Stage 13B 补回来。
  </p>
  <p>
    跑到现在已经发现两件小事——有 200 多个文件路径里带罕见字符，没被 Stage 2 发现，得回到 Stage 2 加 NFC 规范化重扫；还有 40 多个文件 metadata 是空的但文件本身正常，看起来是当年某个工具抓的时候就没写。这两个修完，v0 就算正式定稿。
  </p>
  <p>
    再往后是 Stage 14+ ——播放器集成、AI 推荐 DJ、可视化界面。但那些都建立在「索引可信」这个基础上，我不急着推进。索引不稳，上层一切都是沙子。
  </p>
  <div class="journal-ending">
    <p>
      这个索引远没做完。但每加一条约束、每跑通一个阶段、每填一个踩坑修补，我对它的信任就多一分。
    </p>
    <p>
      我现在不再追求「快点把整套查询界面做出来」。我追求的是——这套索引下个月、半年后、两年后，我重新跑同一份脚本，结果还能收敛到同一份。原盘永远没被动过，输出永远在工作盘里，错误永远在 log 里，重复运行永远幂等。这四件事比任何花哨的查询前端都重要。
    </p>
    <p>
      Stage 13B 还在跑。下一篇关于这套索引的笔记，多半会从「v0 终于敢叫 v1」或者「某条约束又被啃出新洞」开始讲。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识系统</category>
  <category>音频索引</category>
  <category>工程约束</category>
  <category>只读原则</category>
  <category>public-safe</category>
</item>
<item>
  <title>大型个人资料库怎么整理：备份优先级、关键资产、配置冲突</title>
  <link>https://yunlab.ai/notes/organizing-large-personal-archive-library</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/organizing-large-personal-archive-library</guid>
  <description>重装系统前盘点过一次，才发现自己根本不知道家里这台机器装了多少东西。备份优先级三层、关键资产六个信号、配置冲突五种模式——都是被坑过才学会的事。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌上铺开一张纸，上面画着资料分类清单，旁边一台 Mac mini 在跑备份" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">系统审计笔记</p>
  <p class="journal-lead">
    盘点不是备份。盘点是「我自己都不知道我装了多少东西」——在盘点之前，再多备份方案都是赌博。
  </p>
  <p>
    之前写过一篇《Mac mini 重装前，我做了一次系统级资产审计》，记录的是那一次具体怎么做的、看到了什么、最后决定怎么处理。这一篇把那次审计沉淀下来的几条规则单独拎出来——备份优先级矩阵、关键资产识别信号、配置冲突模式。它们对所有攒了多年的个人资料库都通用，不限于 Mac，也不限于 AI 工作站。
  </p>
  <p>
    动机很直接：我自己被坑过几次以后才知道，普通人对自己机器上的真实状态几乎一无所知。装过的软件早忘了一半，配置散在十几个地方，密钥被复制到了三四个文件里，几个项目还跑着进程没关。你以为备份了，其实只备了表层。真正会出事的那部分根本没被看见。
  </p>
  <h2>真实快照：先把数字摆出来</h2>
  <p>
    这次审计跑下来的几个数字，我自己看到都吓一跳。
  </p>
  <ul class="journal-list">
    <li>磁盘已用 370GB（总共 926GB）——我以前以为只用了一半，结果接近 40%。</li>
    <li>Git 仓库 9 个，其中 4 个没有 remote（git 远端）——意思是这 4 个仓库一旦本地硬盘挂了，就彻底没了。</li>
    <li>未提交文件 179 个——分散在那 9 个仓库里，每一个都是「我以为我会回头提交」但其实一直没提交的工作。</li>
    <li>LaunchAgent（macOS 的开机后台服务）22 个——我能叫得出名字的不超过 10 个，剩下一半根本想不起来是装哪个项目时落下的。</li>
    <li>Homebrew（macOS 包管理器）安装的命令行工具 75+ 个；npm 全局包 5 个。</li>
    <li>.env（环境变量文件）14+ 个——散在不同项目里，里面装着各种 API Key（外部服务的接口密钥），全部不进 git。</li>
    <li>监听端口 23 个，活跃 AI 服务 11 个——开机就在跑，但我从来没把这张表完整列出来过。</li>
  </ul>
  <p>
    这张表本身就是审计的第一份产物。没有它，所谓「备份」就只是把 Desktop、Documents、几个看得见的项目目录复制一遍——剩下那 80% 的真实资产全部在视野之外。
  </p>
  <p>
    我现在的第一条规则：动手做备份决策之前，先逼自己把这张数字表填出来。填不出来就别谈备份。
  </p>
  <h2>备份优先级三层：P0、P1、P2</h2>
  <p>
    数字表填完，下一个问题：哪些必须备、哪些可以重来、哪些根本不用管。
  </p>
  <p>
    早期我试过「全部都重要」这种态度，结果是「全部都不重要」。备份策略一旦没有优先级，就退化成「能备什么备什么」，关键数据丢了也只能怪运气。所以我现在只用三层，多了维护不动。
  </p>
  <h3>P0：不备份会严重损失</h3>
  <p>
    丢了就中断工作，外部资源也重建不了。这是 P0 的标准。
  </p>
  <ul class="journal-list">
    <li>未提交代码——还没 push 到 remote 的那些，本地硬盘是唯一副本。</li>
    <li>业务数据库——本地跑着的 Postgres（开源关系型数据库）、SQLite（嵌入式数据库），里面装着实际业务数据。</li>
    <li>向量数据——chromadb、lancedb、mem0（几个本地向量数据库）里的 embedding（向量表示），这是最特殊的一类，下面单独讲。</li>
    <li>语音资产——录音、生成的音频样本，原始的就那一份。</li>
    <li>.env 文件——里面是各种第三方服务的 API Key，丢了要重新去几十个网站申请。</li>
    <li>自定义 LaunchAgent——开机自动跑的那些服务定义，丢了等于把日常工作流的入口全砸了。</li>
  </ul>
  <h3>P1：恢复成本高，但能重建</h3>
  <p>
    不会致命，但恢复要花一两天。
  </p>
  <ul class="journal-list">
    <li>模型缓存——本地拉下来的 LLM 权重，重新下载几十 GB 是体力活。</li>
    <li>全局包——Homebrew 和 npm 装的命令行工具集合，可以用 Brewfile 之类的清单重建，但前提是你有清单。</li>
    <li>AI CLI 配置——Claude Code、Codex CLI 之类的工具配置，里面的 prompt、自定义命令、MCP 接入都在这里。</li>
    <li>浏览器配置——书签、扩展、登录态。同步过的是一回事，没同步的小工具配置又是另一回事。</li>
  </ul>
  <h3>P2：可以重新下载或配置</h3>
  <p>
    最低层，丢了基本无所谓。
  </p>
  <ul class="journal-list">
    <li>Homebrew 包本身——清单在就能重装。</li>
    <li>应用安装包——AppStore 或者厂商网站都能再下。</li>
    <li>构建产物——node_modules、build、dist 这种，源码在就能再生成。</li>
  </ul>
  <p>
    三层的关键不是分得多细，是「分了之后真的按层级处理」。P0 必须冗余（云端 + 异地物理副本），P1 一份就够，P2 不备。体量从「全盘几百 GB」降到几十 GB，反而能做到「每天自动跑、出问题立刻发现」。
  </p>
  <p>
    全备一切，备份太大就跑不动。跑不动就改成每周一次，再变成每月一次，最后变成「上次备份是半年前」。分层不是为了省空间，是为了让备份真的能跑下去。
  </p>
  <h2>关键资产识别的 6 个信号</h2>
  <p>
    分类之后，下一个问题：这些东西藏在哪里？普通备份工具看不见，得自己一类一类去找。
  </p>
  <p>
    我每次盘点都按这 6 个信号走一遍，每个信号对应一个真实场景。漏一个，重装后可能就会「啊那个东西没了」。
  </p>
  <h3>信号 1：未提交代码</h3>
  <p>
    最容易被忽略的一类。大家默认「代码都在 git 里」，但 git 里只有提交过的部分，没提交的那 179 个文件全部不在。
  </p>
  <p>
    做法：把机器上所有 git 仓库列出来，逐个跑 git status 看有没有未提交的改动，再跑 git remote -v 看有没有远端。两个都不对的仓库就是高危——没 remote，本地是唯一副本；未提交，连本地也没归档。
  </p>
  <p>
    我那次发现的 4 个无 remote 仓库里，2 个是早期实验留下的，但里面有一些当时调出来的关键参数和小工具，丢了我得重做一遍。这东西不会让你立刻意识到价值，但真没了才会想起来。
  </p>
  <h3>信号 2：运行中的数据库</h3>
  <p>
    本地跑着 Postgres、SQLite、ChromaDB（一种向量数据库）这种服务，备份时直接复制数据文件经常是坏的——数据库在写，复制出来的可能是中间状态。
  </p>
  <p>
    这一类资产的备份动作不是「复制文件」，是「先停服务、或者用数据库自己的 dump（导出数据）工具」。两个都不做就开始备份，事后恢复时大概率会发现备份是坏的。
  </p>
  <p>
    更现实的是——大部分人压根不知道自己机器上有哪些数据库在跑。它们都是某个项目装的时候作为依赖装进来的，跑在某个端口上，开机自动启动，从来没主动想过它们。盘点时得专门去查所有监听端口、所有数据库进程，看看里面都装了什么。
  </p>
  <h3>信号 3：向量数据（这个最特殊）</h3>
  <p>
    chromadb、lancedb、mem0 这种本地向量库存的是 embedding——把文档、聊天记录、知识点编码成的高维向量。理论上能从源数据重新计算，实际上几乎不可能。这是它最特殊的地方。
  </p>
  <p>
    重建需要三个条件同时满足：源数据还在、当时用的 embedding 模型还能拿到、当时的分块和清洗规则还记得。少一个，重建出来的向量库就和原来不一样——搜索结果会变、相似度阈值要重调、整条流程都得回归测试。
  </p>
  <p>
    我自己的几个本地知识库都跑了几个月，期间换过 embedding 模型、调过分块策略、清理过几次错误条目。从零重建，可能比当初第一次建还麻烦。向量数据在我这里是 P0，和数据库放同一层。
  </p>
  <h3>信号 4：.env 文件</h3>
  <p>
    .env 文件用来装环境变量，里面经常塞各种 API Key、数据库连接字符串、第三方服务的 token。按规矩它不该进 git，备份的时候要专门处理。
  </p>
  <p>
    它们散在各个项目的根目录、config 子目录，有时候还藏在 dotfile（隐藏文件）的更深层。我那次扫到 14+ 个，分布在 8 个项目里。逐个打开一看，里面装着各种第三方服务的接入凭证，丢了要重新去几十个网站申请，还得记得当初申请时填的是哪个邮箱、哪个团队、哪一份限额。
  </p>
  <p>
    盘点时必须扫一遍 .env、.env.local、.env.production 这类文件，记下它们在哪、里面装了什么类型的密钥，专门进 P0。
  </p>
  <h3>信号 5：自定义 LaunchAgent</h3>
  <p>
    LaunchAgent 是 macOS 的开机后台服务定义，文件放在 ~/Library/LaunchAgents/ 里。每个文件描述一个开机自动启动的服务——可能是某个 AI 服务、某个监控脚本、某个定时任务。
  </p>
  <p>
    我那次扫到 22 个，至少一半是早期某个实验装上去再也没卸过的。这一类资产丢了不会立刻有感——但你下次开机会发现一堆东西不见了：自动启动的 AI 服务、定时跑的备份脚本、监控异常的小工具，全部消失。要一个一个回想当初是怎么配的，几乎不可能。
  </p>
  <p>
    LaunchAgent 全目录必须进 P0，整个 ~/Library/LaunchAgents/ 一起备。同时这也是个清理机会——盘点时顺便决定哪些可以删了，不要无脑全保留。
  </p>
  <h3>信号 6：明文密钥（最危险的一类）</h3>
  <p>
    我自己最不敢回头看的一类。盘点时检查 shell profile（终端启动配置文件，比如 .zshrc、.bashrc、.bash_profile），经常会发现一行 export OPENAI_API_KEY=... 之类的——明文密钥，跟着 shell 一起开机加载。
  </p>
  <p>
    这里有两个问题。一是安全：明文存在配置文件里，任何能读到这个文件的进程都能拿走（包括你装的某些不太干净的工具）。二是流动：shell profile 经常被备份到云盘、被复制到新机器、被贴进求助截图——一不小心密钥就传出去了。
  </p>
  <p>
    这一类不只是备份问题，是改造问题。备份时要把 shell profile 完整备走（属于 P0），但盘点完之后必须排一个任务——把所有明文密钥从 shell profile 挪到密码管理器，再从环境里读出来用。这件事我自己还没做完，下一步就是它。
  </p>
  <h2>配置冲突的 5 种常见模式</h2>
  <p>
    备份做完不等于整理完。同一台机器跑了几年，配置之间会互相冲突，普通备份方案完全发现不了这一层。
  </p>
  <p>
    我那次盘点把碰到的冲突归成了 5 种模式。每一种都不致命，但合在一起就会让人重装系统后发现「为什么有些东西好像能跑、又好像不对」。
  </p>
  <h3>模式 1：端口语义漂移</h3>
  <p>
    最常见：3100 在 A 项目里是 web 服务，在 B 项目里是某个数据库管理界面，在 C 项目里被某个 AI 工具占了。三个东西都开机自动启动，谁先抢到就用谁；剩下的两个静默失败，没人会主动告诉你。
  </p>
  <p>
    更隐蔽的是端口号差一位——3100 和 13100 被不同组件分别使用，配置文件里写错一位就连到错的服务上。日志里看不出错，对面也是个 HTTP 服务，只是返回的不是你要的东西。
  </p>
  <p>
    盘点时要把所有监听端口列出来，对照各项目配置文件里的端口声明，看有没有冲突。这件事备份工具做不了，只能自己跑一遍。
  </p>
  <h3>模式 2：旧路径残留</h3>
  <p>
    crontab（定时任务表）里写着 /Users/me/old-project/run.sh，但 old-project 那个目录三个月前就删了。软链指向已经不存在的目录。MCP（Model Context Protocol，AI 工具用来连外部服务的协议）配置里指向某个已经迁移走的服务。
  </p>
  <p>
    这一类残留备份会被原样保留——恢复时它们还在配置里，但指向的目标早就没了。轻则日志报错，重则某个工具依赖的路径不存在，直接挂掉。
  </p>
  <p>
    盘点时把 crontab、所有软链、所有外部工具的配置文件过一遍，逐条验证指向的路径或服务还存在。不存在的就当场决定：要么删，要么改指向新路径。
  </p>
  <h3>模式 3：失效服务依赖</h3>
  <p>
    A 服务的配置里写着「依赖 B 服务在 5432 端口」，但 B 服务三个月前换成 C 服务了，5432 端口空着。A 服务每次启动会尝试连，连不上就用 fallback 逻辑跑一个降级模式——你完全不知道它在跑降级。
  </p>
  <p>
    这种问题平时不容易暴露，降级模式经常「看着也能用」。等真出问题去查日志，才发现某个关键链路其实已经断了好几个月。
  </p>
  <p>
    盘点时要逐个服务看配置里依赖了什么，再交叉对照「这些依赖现在还在跑吗」。不在跑的依赖要么删掉，要么补上，不要让它继续以「应该在」的状态留在配置里。
  </p>
  <h3>模式 4：跨根调度</h3>
  <p>
    我自己踩过最深的坑是这一类。一个 scheduler（调度器）原本只管自己项目的任务，但配置里逐渐被加了几条「顺便也跑一下隔壁项目的脚本」。等某天把隔壁项目重构、目录搬走，这个调度器还在按老路径跑，要么报错、要么跑错文件。
  </p>
  <p>
    更糟糕的是，这种跨根调度往往不对称：A 调度器知道自己在调 B，但 B 完全不知道有人在外面调它。维护 B 的时候自然不会考虑 A 的依赖，冲突在毫无预警的状态下发生。
  </p>
  <p>
    盘点时必须给每个调度器画一张「我调了谁」的清单，再倒过来看「谁在调我」。两边对上以后才能判断哪些跨根调度是合理的、哪些是历史包袱。
  </p>
  <h3>模式 5：历史副本混淆</h3>
  <p>
    同一份配置文件存了几份：本地一份、备份目录一份、archive 里一份、某次实验时复制出来又一份。名字都差不多，内容略有差异，时间戳也都不远。要判断哪一份是 canonical（权威的、官方的）几乎靠考古。
  </p>
  <p>
    一个人还能强撑，但时间一长就崩——半年后回头看，根本想不起来哪份是「现在真的在用的那份」。AI 工具进来读这堆文件，更容易选错。
  </p>
  <p>
    盘点时每一份关键配置必须指定一个 canonical 路径，其他副本要么删掉、要么明确标成历史（比如挪到一个 archive/ 子目录里）。原则是「同一时间只能有一份在用」，不允许「都还能用」。
  </p>
  <h2>冷存储里值得留的几类文件</h2>
  <p>
    盘点完不是全删。除了 P0/P1/P2，还有一类东西不影响系统运行，但「以后想写文章会回头查」。我把它叫冷存储里的留种。
  </p>
  <p>
    大部分历史文件其实价值不高——旧聊天记录、旧实验输出、旧版本的设计稿、过期的临时报告。可以一刀切扔掉。但这四类我会单独挑出来留：
  </p>
  <ul class="journal-list">
    <li>跨模块分析——把系统几个模块放在一起看的全景图、调用链、权限传播路径。做一次成本很高，留着以后回头看自己当时怎么理解的。</li>
    <li>教学整理——某门公开课的中英对照笔记、整理过的章节摘要。已经过滤过一遍的二次产物，比原视频好用。</li>
    <li>调研报告——行业、技术演进、某个工具的对比评估。当时花了几天调研得出的结论，几个月后还能当起点。</li>
    <li>元信息——质量检查报告、分类清单、目录结构快照。是关于「资料本身的资料」，重建很麻烦但实际就那么几份。</li>
  </ul>
  <p>
    判断标准很简单：扔掉的成本是「以后想到要重做一遍」，留下来的成本是「占点磁盘」。前者代价远高于后者，那就留。但留也要进冷存储目录，不要混在工作目录里——混了以后每次打开都要再判断一次「这个是热数据还是冷数据」，注意力被磨光。
  </p>
  <h2>盘点完之后做什么</h2>
  <p>
    盘点不是做一次就完了。我现在的节奏：每次做大动作之前——重装系统、迁移机器、换硬盘——都重新跑一遍这套流程。平时维持一份动态清单，新增加的 LaunchAgent、新写的 .env、新装的全局工具，都顺手记一笔。
  </p>
  <p>
    这件事最难的不是工具，是心态。承认「我自己不知道我装了多少东西」需要勇气——很多人会下意识抗拒，承认了就得面对那张数字表。但只要面对过一次，下一次就轻松多了。
  </p>
  <div class="journal-ending">
    <p>
      整理资料库的本质，不是更勤奋地备份，是把「看不见的资产」变成「看得见的清单」。看不见就赌运气；看得见才谈得上策略。
    </p>
    <p>
      我下一步还有几件没做完：把所有明文密钥从 shell profile 挪到密码管理器、修掉那几条配置冲突、给 22 个 LaunchAgent 做一次清理删一半、把 4 个无 remote 的本地仓库要么加 remote 要么彻底归档。这些都不是一次能做完的——能做完的也不是一份清单能解决的问题。
    </p>
    <p>
      但有了这张数字表和这几条规则，至少下次面对它们的时候不会再问「这台机器到底装了什么」。这就够当起点了。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识系统</category>
  <category>系统审计</category>
  <category>备份策略</category>
  <category>配置管理</category>
  <category>public-safe</category>
</item>
<item>
  <title>个人 AI Lab 资料治理：项目 / 归档 / 审计 / 时间线，四类各管一摊</title>
  <link>https://yunlab.ai/notes/personal-ai-lab-asset-governance-four-categories</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/personal-ai-lab-asset-governance-four-categories</guid>
  <description>AI 工作站跑一年，资料膨胀到自己都找不到。后来我把所有产出按治理属性切成四类——项目、归档、审计、时间线——找资料从翻 3 个目录变成 30 秒定位。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌面上四个抽屉分别标着「项目」「归档」「审计」「时间线」，文件被分门别类放进去" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">资料治理笔记</p>
  <p class="journal-lead">
    一台 AI 工作站跑过一年以后，硬盘上会出现一种很奇怪的状态——目录结构看起来还算整齐，但你已经找不到东西了。
  </p>
  <p>
    一开始我以为是目录分得不够细。再切一层、再加一层 tag，总能解决。后来发现不是。问题不在细不细，而在我从一开始就按错了维度切——按内容主题切目录，半年以后必然乱。同一份资料在不同生命周期里，治理属性会变；内容主题不会。你今天按主题摆得再齐，过半年它在你心里的身份已经动了，目录却不会自动跟着动。
  </p>
  <p>
    这篇往上抽一层。站点上已有几篇讲「某一类资料怎么管」——知识库 6 种页面类型、从日志到知识的保留排除规则、Mac mini 某一次系统审计、系统级资料库整理规则。那些都是「往下切」。这篇往上抽：在动任何具体库、做任何具体审计之前，所有产出先按什么属性切成几类。根目录切法不定，下面怎么管都是乱的。
  </p>
  <p>
    我现在用的切法是四类——项目（projects）、归档（archive）、审计（audits）、时间线（timeline）。各管一摊，不重叠。
  </p>
  <h2>为什么不按内容主题切</h2>
  <p>
    最早一年我用过的切法是「AI 模型 / 工程项目 / 音乐 / 视频 / 调研」。看起来直觉、看起来对——每件事都能找到归属。
  </p>
  <p>
    但它撑不过半年。内容主题是静态标签，治理属性是动态身份。同一份东西，今天是「活的项目」，明天可能变成「已经结案的归档」，后天可能因为做了一次盘点变成「审计快照（snapshot，某个时间点拍下的状态截图）」。它的内容主题没变——还是属于 AI 工程那个抽屉——但在我手里要被怎么对待，完全变了。
  </p>
  <p>
    举个例子。纪嫣然语音工作台这个项目，去年是个正在跑的工程，每周都有 commit（git 提交）推进。中间做过两次外部审计，留下了两份带时间戳的快照。某些早期模块已经停止演化、不会再改了，按理说该进归档。整个项目里几次决定性的架构调整，我又顺手写进了系统级时间线。
  </p>
  <p>
    按内容主题切，「纪嫣然」就是一个目录，所有东西都堆在里面。结果就是——下次想知道「现在系统是什么状态」，会被一堆已经过期的旧设计文档干扰；想查「最近一次正式审计的结论」，要翻好几层；想看「这套架构什么时候定的」，时间线淹没在 commit 历史里根本看不见。
  </p>
  <p>
    说白了，按主题切的目录里，每一份文件都没告诉你「应该用什么态度对待它」。你必须每次打开都重新判断。十次以后没人想打开。
  </p>
  <p>
    按治理属性切就不一样。一份资料躺在「项目」里，意思就是它还活着，可能随时被改、被引用、被推进。躺在「归档」里，意思就是它已经死了，看可以、但别把它当现在的真相。躺在「审计」里，意思是它是某个时刻的快照，要看带的时间戳判断还能不能用。躺在「时间线」里，意思是它在记录一次系统级变化，不要展开内容，只看引用。
  </p>
  <p>
    属性稳定。资料一旦定性，对待方式就定了。下面展开说。
  </p>
  <h2>第一类：项目（projects）——活的、有 owner、还在变</h2>
  <p>
    项目目录放的是当下还在跑的活。判断标准很笨——还有人负责、还有产出、还在被推进、有验收标准。任何一条不满足，它就不该再待在项目目录里。
  </p>
  <p>
    我现在同时在跑的项目有几条线——音乐索引、OpenClaw、纪嫣然语音工作台、林鹿视频工厂。每一条都有自己的目录、git 仓库、.env（环境变量文件）、venv（Python 虚拟环境）。目录、git、env、venv 这四件事缺一个都不行。
  </p>
  <p>
    为什么要这么强调隔离？AI 工程的依赖冲突极其凶。两个项目共用 venv，半年后必有一方依赖被另一方升级悄悄破坏。共用 .env，token 迟早串了——一个项目的 key 被另一个项目的代码读走，轻则配额穿仓，重则被错误的人调用。共用 git 仓库更糟，commit 历史会乱成什么样不用想。
  </p>
  <p>
    项目目录里允许混乱，活的东西本来就乱——草稿、实验脚本、跑废的 log、半成品文档。这些都可以堆在项目里，没关系，命运跟着项目走。要么哪天升级成正式产出，要么项目结案一起搬归档，要么确认没用直接删。
  </p>
  <p>
    项目目录最大的危险，是没及时把死掉的部分搬出去。一个项目里如果同时存在「活的当前版本」和「已经死的旧版本」，又没标清楚谁是谁，下次你想改的时候 95% 会改错那份。这个坑我踩过——本来该改 v3，结果改了 v1 时代留下来的同名文件，半天才反应过来为什么改了没生效。
  </p>
  <p>
    所以项目目录默认全是活的。一旦发现某份东西死了，立刻搬走，别"放这儿也没关系"。这是项目与归档之间最常见的污染源。
  </p>
  <h2>第二类：归档（archive）——过去的、已结案、还有参考价值</h2>
  <p>
    归档放的是已经死掉、但还值得留的东西。冷存储里的旧 wiki（百科库）、过往的调研报告、做完没继续推的实验、教学性的整理稿——这一类。
  </p>
  <p>
    归档的特征很清楚：不会再改，偶尔回头查，留着比删好，但绝对不能当权威。
  </p>
  <p>
    我现在归档跟当前项目是物理隔离的——最好直接放在另一块盘。这听起来有点过——一个目录的事至于换块盘吗？但物理隔离有个心理效应——你伸手够不着的东西，就不会顺手引用。这是治理最不起眼但最关键的一招：不让坏选项触手可及。
  </p>
  <p>
    归档最大的坑就一条——被当成权威源引用。
  </p>
  <p>
    我吃过的最大一次亏，是某次排查一个配置问题。我让 AI 助手翻文档，找到一份带 FINAL（最终版）字样的设计稿，里面明明白白写着接口走 v3、字段叫 X。我照着改了一圈，发现根本对不上。最后才反应过来，那份 FINAL 是一年前的 FINAL，v3 早就被推翻、字段早就改名，只是这份稿子被搬进归档以后没人去戳它。它躺在归档里，文件名还叫 FINAL，看起来像权威。
  </p>
  <p>
    从那以后我给归档加了一条死规矩——归档目录里的所有材料默认都标 stale（过期的、不新鲜的）。哪怕文件名写着 FINAL / SPEC / canonical（权威的、官方的），只要在归档里，它就是 stale。想拿归档里的内容当依据，得先回项目里找活版本——找不到，就当不存在，不许直接引用归档。
  </p>
  <p>
    这条规矩听起来不近人情——明明有东西为什么不能用？但它其实是在保护我。归档里的东西保留的是「当时是这样」的历史价值，不是「现在还是这样」的事实价值。把两者混淆，比直接不留更危险——直接不留只是没信息，混淆是有错误信息。
  </p>
  <p>
    所以归档不是数据的坟墓，是数据的展览馆。可以看、可以引用为"那个时候的状态"，但不能拿展品当现役装备。
  </p>
  <h2>第三类：审计（audits）——某个时间点的快照</h2>
  <p>
    审计是一次性盘点的产物。重装系统前的系统快照、跨项目拓扑梳理、第三方做的安全审计、自己定期的资料盘点——这些都属于这一类。
  </p>
  <p>
    审计的特征跟项目和归档都不一样。项目是活的，归档是死的，审计是「死在某个时间点，但那个时间点很重要」。它是一张照片，照下来的是当时的状态。
  </p>
  <p>
    审计目录的治理规矩有两条——必须带时间戳，必须带 stale 标签。
  </p>
  <p>
    时间戳必须直接写进文件名或者目录名，不能藏在文件内部。这是被无数次坑出来的经验。时间戳如果只写在文件头的 frontmatter（开头的元数据块）里，得打开才看得到——目录列表扫一眼根本看不出新旧。文件名里直接带年月，扫一眼就知道这份是今年的还是去年的。
  </p>
  <p>
    stale 标签更关键。审计快照一拍下来就开始过期——过期速度取决于系统变化速度。系统稳定的部分，一份审计可能半年还有效；系统在动的部分，一份审计可能两周就废了。所以审计目录得有个机制告诉你「这份还能不能用」——目前我用的还是手工标，每次系统有重大变化，回头把对应模块的审计标 stale。
  </p>
  <p>
    审计最大的地雷，是过期了还被人当现状用。
  </p>
  <p>
    这比归档被错引更坑。归档大家心里有数——你看的是历史。但审计不一样——审计本来是让人知道现状的，默认假设就是"这是真的"。一份三个月前的审计，如果中间系统改过几次，但没人去标 stale，下次有人翻出来当依据，错的可能性极高。
  </p>
  <p>
    所以我现在做审计的时候，会同时做两件事——拍快照、并且在系统有重大变化时回来标 stale。第二件事经常忘，但忘一次就出一次问题。这也是我在推 stale 自动化的原因——手工纪律撑不住。
  </p>
  <p>
    审计和归档容易被混淆，区别其实很清楚：归档是「这个东西已经死了，搬进展览馆」，审计是「这个东西还活着，但我在某一刻拍了张照」。被拍照的东西继续活，照片不会跟着更新。
  </p>
  <h2>第四类：时间线（timeline）——系统级变化的流水账</h2>
  <p>
    时间线只记系统级变化。
  </p>
  <p>
    什么叫系统级？改宪法（机器级通用工作指南）、改目录结构、装常驻服务、配 LaunchAgent（macOS 开机后台服务）、决定性架构调整。这一类。
  </p>
  <p>
    不属于系统级的，一律不进时间线。具体项目里某次改了某个文件——那是项目的 commit 历史，不进时间线。某次审计发现了什么——那是审计目录里的事，不进时间线（除非审计触发了系统级变化）。某天读了一篇文章学到一个新东西——那是笔记，不进时间线。
  </p>
  <p>
    时间线追加式写法——不删不改，按月归档。每条变化必须写清楚四件事：改了什么、为什么改、影响谁、引用了哪个任务或审计。
  </p>
  <p>
    其中"引用"最关键。时间线不重复内容，只放指针。每条时间线条目，如果你想知道详情，得跳到它引用的项目目录或审计快照去看。这条规矩保证时间线永远是薄的——薄才有人读。
  </p>
  <p>
    时间线最大的危险，是所有小改动都往里塞。
  </p>
  <p>
    这个诱惑很大——既然有个流水账，每次改东西顺手记一笔多好。但只要试着记一周——时间线立刻被淹没。本来想看「这个月有什么重大变化」，结果翻出来 80 条，70 条是「调了个参数」「改了个文案」「装了个工具」。这种密度的流水账没人读，读了也找不到信号。
  </p>
  <p>
    所以时间线门槛要高。一条变化要不要进时间线，我的判断标准是——三个月后回头看，还会不会觉得它重要？如果不会，就不要进。
  </p>
  <p>
    判断不准的时候，宁愿不写。漏写一条不重要的变化，损失很小；写多了不重要的变化，时间线整体作废，损失很大。
  </p>
  <h2>四类之间怎么互相引用</h2>
  <p>
    四类目录的关系比想象中紧。但有个原则——只引指针，不复制内容。
  </p>
  <p>
    项目跑到某个节点需要拍快照，就触发一次审计——项目目录里只留一行"已审计，详见 audits/2026-05-jiyanran-stage-1.md"，详细内容在审计目录里。审计如果发现重大问题、需要做系统级调整，就触发一次时间线条目——审计报告里留一行"已触发时间线变更，详见 timeline/2026-05/CHANGE_xxx.md"，时间线里留一行"起因详见 audits/2026-05-jiyanran-stage-1.md"。两边互引，但内容只在一处。
  </p>
  <p>
    项目结案搬归档同理——项目目录消失，归档目录里留下完整快照，时间线里留一条"某项目已结案，归档详见 archive/xxx"。这条时间线条目以后是找这个项目唯一的入口。
  </p>
  <p>
    时间线是事后总结，项目和审计是事中产物。时间线引用它们，它们很少引用时间线。单向引用让目录不循环。
  </p>
  <p>
    归档跟其它三类的关系最弱——基本只被引用，不主动引别人。终点站。
  </p>
  <p>
    这套引用关系一开始我也没想清楚，用着用着才摸出来。一旦稳下来，找资料变成一种机械动作——先看时间线知道大概在哪个时段发生过什么，再跳到项目或审计看具体内容，最后翻归档查历史背景。三步走完任何一次查询。
  </p>
  <h2>真实场景：4 类怎么联动起来做决策</h2>
  <p>
    这套切法的价值，在真实场景里才显出来。举三个最近常遇到的情境。
  </p>
  <p>
    <strong>场景一：接手一个已经放了三个月没动的老项目。</strong>
  </p>
  <p>
    以前的流程是——翻项目目录，看最新文档，然后开始猜。问题是项目目录里的文档真假不分，有些是当年留下的设计稿，有些是中间废弃的方案，有些才是真正在跑的版本。我经常花一两个小时才能搞清楚「这个项目当下到底是个什么状态」。
  </p>
  <p>
    现在的流程是——先看项目目录里最近一次 commit 在干嘛，再去审计目录找这个项目最近一次审计快照（带时间戳，扫一眼就知道几月份的），最后去时间线里翻最近三个月这个项目相关的系统级变化。三件事拼起来，10 分钟进状态。审计快照告诉我"那个时间点是什么样"，时间线告诉我"从那之后改过什么"，项目目录的最新 commit 告诉我"现在正在动什么"。三个角度互相对照，比单独看任何一个都准。
  </p>
  <p>
    <strong>场景二：排查一个不知道哪里来的配置冲突。</strong>
  </p>
  <p>
    比如某天发现某个服务突然连不上某个端口，记忆里没改过相关配置。
  </p>
  <p>
    以前我会一头扎进项目代码查 commit，常常查不出来——问题可能不在这个项目，而在某次系统级调整改了端口分配或者某个 LaunchAgent 的配置。
  </p>
  <p>
    现在第一步先翻时间线，看最近一个月有没有跟网络、端口、服务相关的系统级变化。第二步翻最近一次系统审计的「配置冲突登记」那一段——一般审计里都会顺手记一笔已知冲突。两步走完，80% 的诡异问题能定位根因。剩下 20% 才回到具体项目里翻 commit。
  </p>
  <p>
    顺序很重要——先看系统级（时间线 + 审计），再看项目级（commit）。反过来效率会差一倍。
  </p>
  <p>
    <strong>场景三：写一篇技术博客。</strong>
  </p>
  <p>
    比如我现在写这篇。素材两块——归档里有过去一年陆陆续续做过的相关整理，项目里有最近一两个月还在动的实践。
  </p>
  <p>
    归档负责"我以前怎么想"，项目负责"我现在怎么做"。两个一起引，文章才有时间纵深。如果只引归档，文章像在讲历史；如果只引项目，文章像在讲技术细节。两边对照，才能讲出「为什么我改了这么多次最后落在这套切法上」。
  </p>
  <p>
    审计在这种场景不太常用——审计是工程档案，公开写文章不需要引它。但偶尔写"某次重大调整"复盘，审计快照就是最权威的素材。
  </p>
  <h2>这套治理带来的实际差别</h2>
  <p>
    数字上的差别比我以为的大。
  </p>
  <p>
    切之前——工作产出分散在十几个目录，找一份资料平均翻 3-4 个目录，运气不好半小时找不到。
  </p>
  <p>
    切成四类后——95% 的资料 30 秒内能定位。剩下 5% 多半是我当初没分好的灰区，每次遇到顺手归位，灰区在持续变小。
  </p>
  <p>
    更大的差别在心理上——不再害怕「找不到」。以前每次要回头找一份资料都有一种"这次找不到怎么办"的隐性焦虑。这种焦虑会反过来影响行为——下意识少回头查，更多凭记忆判断，更多相信"应该没问题"。
  </p>
  <p>
    现在不需要凭记忆。需要什么去对应抽屉里拿。
  </p>
  <h2>还在改的部分</h2>
  <p>
    这套治理远远不算完成。下面几件事都还在长。
  </p>
  <p>
    第一件事，stale 标签自动化。目前是手工标——系统有变化我手动回去标一批审计快照过期。漏标的概率不低。理想状态是有个脚本能定期扫审计目录，根据系统当前状态自动算出哪些快照已经过期。这件事比想的复杂——「系统当前状态」本身没有机器可读的定义，得先把"系统状态"形式化，脚本才能判断。
  </p>
  <p>
    第二件事，项目转归档的判定标准。目前完全靠经验——我觉得项目死了就搬。但"觉得死了"和"真的死了"之间有一段模糊区，搬早了还会被找回来，搬晚了死的东西继续在项目里污染。理想状态是有一组标准——比如多久没 commit、依赖是否还活着、有没有 owner——满足几条就触发"建议归档"的提醒。
  </p>
  <p>
    第三件事，跨类别引用机制。前面写过了——四类互引，但目前是 markdown 里的纯文本路径。文件搬过位置，路径就断了。一台机器内还能忍，多机器场景完全撑不住。
  </p>
  <p>
    第四件事，多机器同步。我现在不止一台机器——Mac mini、Mac Studio、Mac Air 各有各的角色。四类目录每台机器都有，但应不应该全量同步、还是各自只装本机用的部分，目前还没定。全量同步好处是任何一台机器接手都能用，坏处是项目目录迅速膨胀；只装本机用的好处是干净，坏处是跨机器协作会缺东西。这件事大概要拆成「项目按机器划分、归档全量同步、审计按机器划分、时间线全量同步」——但还没真跑过，不知道会踩什么坑。
  </p>
  <div class="journal-ending">
    <p>
      切目录越往后做，我越觉得不是 IT 问题，是认知问题。
    </p>
    <p>
      按主题切，反映的是「我以为我有什么」；按治理属性切，反映的是「我要怎么对待这些东西」。前者是标签，后者是动作。目录在告诉你下一步该做什么，资料才开始为你服务，而不是让你为它服务。
    </p>
    <p>
      四类切完不是终点，是个能继续长的起点。stale 自动化、转归档标准、跨机器同步，每一件都得单独排队做。这篇写完，下周回头去推 stale 自动化——已经欠了挺久。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识系统</category>
  <category>资料治理</category>
  <category>个人 AI Lab</category>
  <category>归档策略</category>
  <category>public-safe</category>
</item>
<item>
  <title>双宪法、任务文件夹、handoff：多 AI 协作的最低秩序</title>
  <link>https://yunlab.ai/notes/dual-constitution-task-folders-handoff-minimum-order</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/dual-constitution-task-folders-handoff-minimum-order</guid>
  <description>一个人同时用 3 个以上 AI 做长期工程，最容易丢的不是能力，是秩序。我用三件最低限度的东西兜住协作链——双宪法管边界、任务文件夹管上下文、handoff 管接力。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/agent-company/cover.jpg" alt="水彩手绘：圆桌前多个 Agent 各自工作，桌上散落 README / notes / handoff 文件" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">多 AI 协作治理手记</p>
  <p class="journal-lead">
    一个人同时用三个以上 AI 做长期工程，半年之后我才看清——最容易丢的不是能力，是秩序。
  </p>
  <p>
    能力并不缺。Claude 能讨论、能写长文、能拆架构；Codex 能跑脚本、能改测试、能搞 CI；Kimi 能啃中文长文档；GPT 和 Gemini 也都各有一手。摆在一起，理论上是个小型团队。每一个单独看都顶半个工程师，三个并行听起来等于一个半。
  </p>
  <p>
    但真正用起来你会发现，团队感是假的。每个 AI 都是孤岛，会话之间没有记忆，跨工具之间更没有共识。下一次进来的那个 AI，永远在问同一组问题：上一个 AI 改了什么？为什么改？我能继续吗？还是要从头再来？这套配置真的稳定，还是上一次只是侥幸跑通？
  </p>
  <p>
    这些问题，没人回答得了，只能我自己记。记不住的那一部分，就直接变成返工。跑到第十次你会发现，你不是在用 AI，你是在做 AI 之间的人肉中转站——一边复制上下文，一边解释昨天发生过什么，一边核对它们没有彼此踩到对方的脚。
  </p>
  <p>
    我后来不再想"怎么让 AI 更聪明"，而是反过来想：怎么用最少的几件东西，把这种孤岛之间的接力兜住。不是搞一套华丽的治理框架，是搞最少够用的那种秩序——少一件就会塌，多一件就开始拖。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/agent-company/role-to-contract.svg" alt="角色设定 → 工作契约：把多 Agent 的协作变成可追踪的节点">
  </figure>
  <h2>每个 AI 都是孤岛，痛点是真的</h2>
  <p>
    我一度以为只是工具差异，磨合几次就好。磨了几个月才发现，这是结构性的——不是哪个产品做得不够好，是这个范式本身就这样。
  </p>
  <p>
    每个 AI 的会话是独立的。今天和 Claude 谈完一个方案，明天打开 Codex，它什么都不知道。我得手动复制一段背景，再贴一段上一轮的结论，再说明现在要做什么。十次里有八次，我贴的背景是不全的——不是我懒，是我记不清上一次到底走到了哪一步。回头去翻聊天记录又特别低效，因为聊天里夹了一大堆探索性的废话，真正的决策只占其中一小段。
  </p>
  <p>
    更麻烦的是跨工具之间。Claude 改过的文件，Codex 不知道改过；Codex 跑过的脚本，Kimi 不知道有结果；Kimi 整理过的中文资料，Claude 不知道存在。三个 AI 各自有一份"它眼中的项目"，三份合不到一起。你问任何一个：现在项目处于什么状态？它都会给你一个非常自信的答案——而且三个答案互相打架。
  </p>
  <p>
    最痛的一次，我让 Codex 改一段配置，它跑得很顺。第二天我让 Claude 看同一个模块，它告诉我"建议你改成 X"——而 X 正好是 Codex 昨天改掉的那种写法。两个 AI 都没错，错在中间没有任何东西能让它们彼此知道。如果我没注意，听了 Claude 的建议改回去，过两天 Codex 又会被某个测试踩中、再改一遍——一个事实上的死循环，每一轮都在"修复"上一轮的"修复"。
  </p>
  <p>
    还有一类痛感更隐蔽：信息没冲突，但是断片。Codex 跑测试通过了，结论留在它那一次会话里；下一次我用 Claude 讨论下一步，它根本不知道测试通过过——它会很谨慎地建议"先跑一次测试确认"。这种谨慎本身没错，但对我来说就是无效来回。AI 在反复确认我早就确认过的事，因为它没有任何渠道知道我确认过。
  </p>
  <p>
    这种事跑几次就会让人想放弃并行，回到单 AI。但回去的代价更大——等于放弃 70% 的算力，也放弃了"不同 AI 各擅长不同事"的那个核心好处。所以问题从来不是"要不要并行"，而是"并行的最低秩序在哪"。
  </p>
  <h2>三件事兜住整个协作链</h2>
  <p>
    半年下来我留下的是三件东西。不是因为我设计得多精，是因为再减就真的撑不住，再加又会变成行政开销。
  </p>
  <p>
    第一件是<strong>双宪法</strong>。两份顶层规则文件，一份管行为，一份管知识。行为那份写：任务怎么走、文件怎么改、什么动作要先停下来问、CHANGE 怎么记录、哪些是红线、哪些是默认放手做。知识那份写：东西怎么入库、命名规范是什么、内容血缘怎么标、Feed 分几层、什么材料属于哪一层。
  </p>
  <p>
    我试过合成一份，两个月之后放弃。行为规则和知识规则性质完全不同：行为是"该不该做"，是一种判断；知识是"放在哪、叫什么名字"，是一种规约。判断和规约硬塞一起，AI 读的时候会两个都不准——要么把行为规则当 metadata 处理，把"先停下来问"读成"在文件元数据里加一行 status: pending"；要么把命名规范当道德约束执行，看到一个不规范的命名就拒绝继续，哪怕这只是过程文件。分成两份反而都清楚了：它读行为那份时，知道自己在做判断；读知识那份时，知道自己在做归档。
  </p>
  <p>
    这里还有一个朴素的好处：两份分开，每份都可以独立演进。行为规则我大概一两个月动一次，任务模式会变；知识规则更稳，三五个月动一次就够，命名和分层一旦定下来就不该乱改。合在一份的时候，每次想动其中一半都得通盘考虑，结果就是两边都不敢动。
  </p>
  <p>
    第二件是<strong>任务文件夹</strong>。每个跨 AI 的任务，在共享文件系统里有一个独立目录。目录里固定四件：
  </p>
  <ul class="journal-list">
    <li>README——这个任务到底要干什么，验收是什么，依赖什么。一页纸，不超过。</li>
    <li>notes.md——追加式日志。每个 AI 干完一件事就在末尾追加一条：做了什么、结论是什么、下一步给谁、关键文件在哪。不覆盖，只追加。</li>
    <li>handoff.md——交接给下一个 AI 的时候写。当前状态、已经做了什么、还没做什么、接手要注意什么、关键文件路径。</li>
    <li>outputs/——这次任务真正产出的东西。脚本、报告、数据、改过的代码片段。</li>
  </ul>
  <p>
    第三件是<strong>handoff（交接 / 接手动作）</strong>本身。它是任务文件夹里那一份 handoff.md 的使用方式：前一个 AI 干完一段，写一份 handoff 留下来；下一个 AI 接手时，按 README → notes 最新几条 → handoff 的顺序读，五分钟进入状态，然后继续干。handoff 不是日志，是路标——它告诉接手者"你现在站在这里，下一步该往那边走"。
  </p>
  <p>
    这三件加起来很轻——一份目录模板、一种追加格式、一个交接动作。但顺序不能乱：宪法定边界，任务文件夹定上下文，handoff 定接力。少哪一件，链就断在哪一段。没有宪法，AI 不知道哪些事它不该自己决定；没有任务文件夹，AI 不知道项目走到哪一步；没有 handoff，AI 知道项目在哪、却不知道自己该从哪一步接。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/agent-company/handoff-loop.svg" alt="多 Agent 通过 README → notes → handoff → outputs 形成接力闭环">
  </figure>
  <h2>为什么是这三件，不是别的</h2>
  <p>
    刚搭这套的时候我加过很多东西。状态板、看板、每日总结、跨 AI 通知、版本号 manifest，全试过。三个月之后大部分都被我删了。
  </p>
  <p>
    留下的标准很简单：减掉它之后，秩序会不会塌。会塌的留，不会塌的删。
  </p>
  <p>
    没有双宪法会塌。AI 不知道边界的时候，它会替你做决定——而且做得很自信。它会越界写入"它觉得应该改"的文件，会把"它觉得该归档"的东西移走，会在没有授权的情况下重命名一批资料。每一次都不是恶意，每一次都是它在用自己的判断填补空白。空白不填，AI 一定会自己填，这是它的本能。双宪法填的就是这个空白：哪些动作要先停，哪些资料要按什么命名，哪些目录是不能动的根。它不需要写得多详细，但必须存在——存在本身就是一个信号，告诉 AI "在这条线之外，你应该问我而不是自己决定"。
  </p>
  <p>
    没有任务文件夹也会塌。上下文散在聊天里，每一次切换工具都是一次记忆重启。我曾以为能记住"上一轮跑到哪一步"，但跑十个任务并行的时候根本记不住。任务文件夹的作用，是把上下文从我脑子里取出来，放到磁盘上。这样下一个 AI（或者一周后的我自己）打开目录，就能从一个确定的位置开始，而不是从我的记忆碎片开始。最有意思的是：它解决的其实不是 AI 的记忆问题，是我的记忆问题。AI 的记忆有没有都不重要，反正它每次都是冷启动；但我的记忆是有限的，需要有一个地方存放它。
  </p>
  <p>
    没有 handoff 会塌得最快。任务文件夹里有 README、有 notes，理论上下一个 AI 也能看明白——但实际上看不明白。notes 是追加式的，三十条之后没人会从头看；README 写的是任务定义，不是现状。两者都不告诉你"此刻你应该做的下一件事是什么"。handoff 就是专门解决这个问题的。它取代了那个"我在两个聊天窗口之间手动复制粘贴上下文"的笨动作——而且取代得很彻底，因为它写下来的瞬间就持久化了，不像聊天那种一关窗口就消失的状态。
  </p>
  <p>
    三件东西分别管三件不同的事：边界、上下文、接力。它们的关系不是冗余，是分工。这也是为什么我把别的都删了——别的要么在重复这三件里的某一件，要么在解决一个其实不存在的问题。比如我以前搞过一个跨 AI 通知系统，让一个 AI 干完就给另一个 AI 发消息。听起来很合理，但实际上没用：下一个 AI 不会因为收到通知就更聪明，它还是要去读 README 和 handoff 才能进入状态。通知反而多了一个失败点。
  </p>
  <p>
    再比如版本号 manifest。我曾经想给每个任务标版本号，方便回滚。后来发现根本不需要——任务文件夹里的 notes 是追加式的，本身就是带时间戳的演化记录；要回滚就回滚到某一条 notes 描述的状态，不需要单独维护版本号。多一层 manifest 反而要多维护一份东西。
  </p>
  <p>
    所以这三件之所以是"最少"，不是我主观觉得少，是我把所有我加过又删过的东西摆在一起，剩下来唯一不可压缩的就是这三件。任何一件去掉，都有一类问题没人接；任何一件再加，都有更轻的方案能覆盖。
  </p>
  <h2>规则在收敛，不是在扩张</h2>
  <p>
    "双宪法"听起来容易越长越厚。我一度也担心。但实际走下来，方向是反的。
  </p>
  <p>
    最早那一版写得最满，效果却最差。我列了厚厚一沓——什么场景该问、什么场景该做、每种文件该放哪里、每种动作该怎么记录，连"修改注释也算修改文件"都写了进去。AI 读完反而更谨慎，什么都来问一遍：要改一行注释问、要新建一个临时文件也问、要删掉一个明显废弃的占位文件还问。规则太密的时候，它们会变成形式主义——AI 不是在按规则判断，是在用"问一下"来回避所有可能踩线的事。
  </p>
  <p>
    后来我开始反向修。每次出问题，先问"缺规则，还是规则太多淹没了关键那条"。十次里有八次是后者。规则就一轮一轮被压缩：从"列举所有该做的事"压到"列出几条不能动的红线 + 风险分层 + 几种任务模式"。中间还加过一版"风险分级 L0-L3"，挺漂亮，但实操时 AI 经常分不清当前动作算哪一级，结果还是来问。下一版我干脆把分级砍掉，只保留"绝对不能做"和"做之前打个招呼"两类，剩下全是默认放手。AI 的判断准确率立刻上去了。最近这一版只剩四条边界加一张三栏动作表。
  </p>
  <p>
    这个收敛过程，倒不是变懒，是看清了一件事：宪法不是用来规定一切，而是用来兜底。日常 90% 的判断 AI 自己能做对，宪法管的是另外 10% 它会出错的地方。把规则写得太满，反而会把 90% 的部分也僵化掉——本来 AI 能直接做的事，现在它要先停下来对照规则；本来不需要确认的小事，现在它要先来问一句。这不是更安全，是更慢，而且更慢的代价最后还是我承担。
  </p>
  <p>
    现在我的 Inbox 里还压着十二份修宪提案——有的提议加，有的提议减，有的提议把某条边界改成动作清单，有的提议引入一个新的中间层。我没急着裁决。它们摆在那里说明这套秩序还活着，被自己质疑、被自己改写、被自己推翻。一份不再被质疑的宪法，反而是危险的——那种宪法不是因为完美才不被质疑，是因为没人再认真读它了。
  </p>
  <h2>半年跑下来的几条判断</h2>
  <p>
    这三件东西真正用了六个月，我有几条比较稳的判断：
  </p>
  <p>
    <strong>双宪法工作得很好。</strong>行为和知识分开是对的，没有一次让我后悔。AI 在做行为决策时只读那一份，在做归档命名时只读另一份，两份互不打扰，准确率比以前合在一起的时候高得多。这件事最反直觉的地方：分两份，反而比合一份省脑子。大概是因为——同一份文档里夹杂两种性质完全不同的规则时，AI 的注意力会被稀释：它读"该不该做"时还在想"放在哪一层"，两件事都做不准。分开就各自独立加载，简单粗暴但有效。
  </p>
  <p>
    <strong>任务文件夹是命脉。</strong>90% 的协作问题都在这一层解决。只要这一层做到位——README 清楚、notes 持续追加、outputs 都在目录里——下一个 AI 接手基本不出错。这一层一旦塌掉，宪法再好也救不回来。AI 没有上下文，光有规则没法工作。规则告诉 AI "不该做什么"，但不告诉它"现在该做什么"——"现在该做什么"只能从上下文里读出来。
  </p>
  <p>
    <strong>handoff 是最常被偷懒的那一件。</strong>每次干完一段，我都想直接关掉，下次自己再接着干。脑子里的声音是"反正能记住"——结果记不住。等到下次想接的时候，我得花二十分钟在 notes 和 outputs 里翻，才能拼回上一次的状态。这种"翻回去"的成本，永远比"当时多写五分钟 handoff"要大。我知道这个道理，但还是经常偷懒，这件事我自己也在反复纠正。最近我开始用一个小约定——任务暂停之前必须先写完 handoff 才能关窗口，否则下次自己一定会后悔。这个约定有效，但维持它本身又是一件需要自律的事。
  </p>
  <p>
    <strong>这套秩序有适用范围。</strong>它适合"一个人 + 三个以上 AI + 长期工程"这种组合。如果你只用一个 AI 做短期任务，这套是冗余的——上下文塞在一次会话里就够了，没必要搞文件夹。如果你是一个团队在多 AI 协作，这套又太轻，需要加权限、加 review、加正式归档，因为有人和人的协作维度，光靠 markdown 不够。它卡在中间那个特别尴尬的位置——比个人手记重，比团队治理轻。我是为了自己卡在这个位置，才搭的。它不一定适合所有人，但对处于同一种结构里的人，应该有参考价值。
  </p>
  <h2>结尾：还在被自己质疑</h2>
  <p>
    我不想把这篇写成"我设计了一套完美的多 AI 协作秩序"。它不完美。十二份提案还压在 Inbox 里没裁；handoff 我自己经常偷懒不写；双宪法之间偶尔还是会有边界不清的地方需要现场判断；规则还在向"最小边界"的方向继续收敛——也就是说，现在这一版还会再被推翻一次。
  </p>
  <p>
    但有一件事我比半年前确定多了：多 AI 协作的核心问题不在 AI 本身，在中间的那一层秩序。这一层做得对，三个 AI 像团队；这一层做得乱，三个 AI 比一个 AI 还累。
  </p>
  <p>
    这一层不需要复杂的东西兜住。一份管行为的宪法、一份管知识的宪法、一个任务文件夹的目录约定、一次老老实实写完的 handoff——就是这些。
  </p>
  <div class="journal-ending">
    <p>
      我现在越来越相信，跨 AI 协作的秩序不是设计出来的，是被反复偷懒、反复返工、反复打脸之后剩下来的。每删掉一条没用的规则，每留下一条真正在兜底的规则，秩序就稳一分。
    </p>
    <p>
      这套秩序也在被自己质疑。它现在是这个样子，半年后大概又是另一个样子。但它不需要是最终形态——它只需要在今天，让下一个 AI 接手的时候，知道该从哪里开始。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>多 AI 协作</category>
  <category>治理</category>
  <category>handoff</category>
  <category>任务文件夹</category>
  <category>public-safe</category>
</item>
<item>
  <title>纪嫣然语音工作台：为什么语音入口要做三层解耦</title>
  <link>https://yunlab.ai/notes/jiyanran-voice-workbench-three-layer-decoupling</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/jiyanran-voice-workbench-three-layer-decoupling</guid>
  <description>一个本地语音助手看上去简单，但语音入口是最容易耦合的层。我把它拆成 OpenRoom 前台 / voice-bridge / avatar-bridge / OpenClaw 四件——三层独立服务、各自 mock 兜底、各自风险关卡——为什么这样做。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/agent-company/cover.jpg" alt="水彩手绘：圆桌前 8 个 Agent 协作的场景" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">纪嫣然语音工作台手记</p>
  <p class="journal-lead">
    语音入口是最容易让人误判难度的一层——用户说话，AI 回答，看上去就这点事；但真要它在第二个月还能站着，就不是"接一根线"那么轻。
  </p>
  <p>
    纪嫣然是我在本地跑的一个语音工作台。干的事很直白：我对着 Mac 说一段话，系统识别出我在说什么，再决定这句话该交给 OpenClaw（我自己的 agent 工厂）里的谁来处理，处理完再把结果说回给我。听起来一根管子就够了——麦克风进、扬声器出，中间塞一个模型。
  </p>
  <p>
    我一开始也是这么想的。第一版甚至草草写过一个直连版本：前台 UI 里直接调语音识别 SDK，识别完直接调大模型，大模型返回再直接读出来。能跑，演示也好看，但只跑了两三天，我就意识到这条路是个陷阱。任何一处要换、要升级、要加风控，整个链路都得动。
  </p>
  <p>
    这才有了 v1.0：OpenRoom 前台 → voice-bridge（语音桥接层）→ avatar-bridge（分发桥接层）→ OpenClaw。三层独立服务，各自 mock（占位 / 假数据兜底）fallback（降级兜底方案），各自 risk-gate（风险关卡）。中间多写了两层 bridge（桥接层 / 中间层），看着冗余。用了一段时间，反而越来越笃定：这两层 bridge 不是冗余，是这系统能长寿的关键。
  </p>
  <p>
    这篇文章要说的，就是这件事：一个本地语音入口，为什么不能图省事写成 monolith（单体架构）的直连，为什么必须拆成三层、为什么每一层都要 mock、为什么每一层都要 risk-gate。不是炫功能，是讲一种被踩出来的判断——<strong>关于"哪里该拆开、哪里该兜底、哪里该守门"</strong>。如果你也正在想做一个本地的语音助手或 agent 入口，这份判断也许能替你省一段我踩过的坑。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/agent-company/voice-pipeline-layers.svg" alt="OpenRoom 前台、voice-bridge、avatar-bridge 与 OpenClaw 四层解耦示意">
  </figure>
  <h2>耦合的代价：图省事的版本，前两周很爽，第三周开始烂</h2>
  <p>
    第一版直连有多简单？前台一个按钮，按住说话，松开发请求。识别在前台跑，模型调用在前台拼 prompt，连风控都顺手写在前台里——三百多行 JavaScript，端到端能演示。当时我还挺得意，觉得这事没那么复杂。
  </p>
  <p>
    问题是，第二周我就想换识别引擎。本地那个模型在中英混说时识别得不行，我想试试另一个。点开前台代码，发现识别的调用、错误处理、超时、重试、降采样、VAD（语音活动检测）全在前台里搅在一起。换一个引擎，不是改一个 import 的事，是要把这一坨重新拆一遍。
  </p>
  <p>
    第三周更难。OpenClaw 那边的 agent 接口变了一次。是个很小的协议升级，但因为前台直接拼 OpenClaw 的请求，整个前台的请求构造逻辑都要跟着改。每改一次，前台就抖一次，UI 也容易跟着出事。
  </p>
  <p>
    最后压垮我的是风控。我想给某些命令加一道确认（比如"删除某个工作区"这类操作要二次确认），结果发现这道关卡只能写在前台。但前台是用户那一端，理论上谁都能绕过——按一个对的请求就能直接打到 OpenClaw。这不是技术债，是真安全洞。
  </p>
  <p>
    那一刻我看清楚一件事：语音入口看上去简单，但它把"展示层、感知层、调度层、执行层"四件事一开始就混在了一起。混在一起的代价不是写代码慢，而是后面任何一处变化都要重写整层。前两周很爽，第三周开始烂，第四周就推不动了。
  </p>
  <p>
    更隐蔽的是另一层代价——心理负担。直连版本每改一处我都要先在脑子里把整条链路过一遍：识别会不会被影响？UI 状态会不会错位？请求构造会不会跟新的协议不对？这种"任何一处改动都要全链路担心"的感觉，会很快磨掉做事的兴致。一个本地工具如果让自己每次改它都要花十分钟"先想清楚副作用"，它早晚会被自己放弃。
  </p>
  <p>
    这事不是语音入口独有的。任何一个"前端 + 模型 + agent"三件事挤在一起的系统都会遇到。但语音入口尤其严重——因为它额外多了两个让事情复杂化的东西：实时音频流、用户预期的低延迟反馈。这两件事都会强烈地诱惑你"图省事写在一起"，因为多一跳就多一点延迟，多一个进程就多一点不确定。诱惑很大，但代价更大。
  </p>
  <h2>三层解耦怎么分：OpenRoom / voice-bridge / avatar-bridge / OpenClaw 各管一件</h2>
  <p>
    所以 v1.0 干脆推倒重来，按"每一层只管一件事"重排了一遍。四件事，四层。
  </p>
  <p>
    最外层是 <strong>OpenRoom 前台</strong>。它就是一个房间：里面有麦克风、有扬声器、有显示对话的界面、有按钮、有一些视觉上的反馈。它管的事极窄——把用户的声音收进来，把后端返回的内容播出去或显示出来。它不识别语音，不构造请求，不知道 OpenClaw 是谁，也不直接跟模型说话。它就是一个房间，房间里有人讲话，房间外的事它一概不管。
  </p>
  <p>
    再往里是 <strong>voice-bridge</strong>，跑在 3962 端口。这一层只管一件事：把"声音"变成"结构化任务"。它接住前台送过来的音频流，调识别引擎，处理 VAD、断句、置信度、可选的语言判别，最后吐出一段"我相对确定用户说的是这件事"的结构化描述。这一层不知道下游谁会接、不知道接了之后会干什么，它的责任只到"识别出意图为止"。
  </p>
  <p>
    再往里是 <strong>avatar-bridge</strong>，跑在 3961 端口。这层管"派活"。voice-bridge 把结构化任务交给它，它判断这个任务该归哪个 agent（智能体）：是问知识就找信息线的 agent，是写东西就找内容线的，是执行命令就找执行线的。它对应的是"调度"，不是"识别"，也不是"执行"。
  </p>
  <p>
    最里是 <strong>OpenClaw</strong>。真正干活的人都住在这里——苏晚、霍锐、申知行、纪嫣然自己也在。OpenClaw 不关心声音、不关心前端按钮、不关心是谁派的活，它只关心"我接到一个任务，按我的人设和能力把它做好"。
  </p>
  <p>
    四件事，四层，每一层都只看自己的边界。前台不知道下游怎么调度，voice-bridge 不知道下游有哪些 agent，avatar-bridge 不知道每个 agent 内部怎么工作，OpenClaw 不知道这个任务是用嘴说出来的还是手敲出来的。每一层只看自己这一截。
  </p>
  <p>
    这种"只看自己这一截"有个隐藏的好处：每一层都能换"入口形式"而不影响别的层。今天是语音入口，明天我想加一个键盘入口，只需要再写一层"键盘 bridge"接到 avatar-bridge 上，下游 OpenClaw 完全不用动。哪天又想加一个邮件入口、Telegram 入口、shortcut 入口，都是同样的套路。<strong>avatar-bridge 之下变成一个稳定的"任务执行后端"，avatar-bridge 之上可以是任意多种入口形式。</strong>这在我开始把纪嫣然之外的其他 agent 接进来时变得特别重要——同一个 OpenClaw 后端能服务多种入口，不用每次都从零开始。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/agent-company/voice-layer-responsibility.svg" alt="四层各自责任边界：前台收声、bridge 识别、bridge 派活、OpenClaw 干活">
  </figure>
  <p>
    这种分法看起来很啰嗦——一段音频从麦克风到真正干活，要经过四个进程、两个端口、三次序列化。后来发现，正是这种"啰嗦"让每一层都能单独换。换识别引擎只动 voice-bridge；改派活规则只动 avatar-bridge；OpenClaw 那边升级 agent 协议，外面三层都不用动。
  </p>
  <p>
    端口为什么是 3962 和 3961 这种相邻的数？纯属顺手——我把语音相关的服务集中在 3960 段，方便记忆和排障。这不是设计哲学，就是工程偏好。但"每一层都有独立端口"这件事是有意为之：它逼着我把每一层当成独立服务对待，不能在某个版本里偷偷把两层合到一个进程里。物理隔离强制了逻辑隔离。
  </p>
  <p>
    做完这套分层，还有一个意料之外的好处：每一层都能独立测试。我可以只起 voice-bridge，用一段录音文件喂进去，看它识别成什么；可以只起 avatar-bridge，用一段假的结构化任务喂进去，看它派给谁；可以只起 OpenClaw，用一段假的 agent 任务喂进去，看 agent 怎么响应。每一层都有自己的测试集、自己的回归用例。出问题时定位也快——四层各自的日志一比，错在哪一层一目了然。
  </p>
  <h2>为什么每一层都要 mock 兜底：用户那一端不能"白屏"</h2>
  <p>
    解耦只是第一步。真正让这套架构能在日常使用里站住的，是另一个看似不起眼的设计——<strong>每一层都自带 mock fallback</strong>。
  </p>
  <p>
    具体说——voice-bridge 启动的时候，如果发现 avatar-bridge 没起来或者打不通，它不会直接报错给前台。它会用 mock 接口兜住：返回一段事先准备的占位响应，告诉前台"调度层暂时不通，先用这段假数据"。avatar-bridge 也一样，如果 OpenClaw 那边没起来，它会用 mock agent 返回一段占位结果。前台也一样，如果 voice-bridge 都没起来，它至少能识别出用户按了按钮、显示一段"语音通道暂未连接"，而不是黑屏。
  </p>
  <p>
    这很重要。本地 AI 系统不是云服务，下游的不稳定是常态。OpenClaw 升级要重启，识别模型加载需要时间，agent 在跑长任务时可能不响应。如果每一层都把下游故障原样向上抛，用户那一端就会频繁见到"出错了，请重试"。一两次没事，连着十次，这个工作台就被废了。
  </p>
  <p>
    但 mock 不是用来骗用户的。mock 兜住的时候，前台会明确显示"当前是占位响应，下游 X 未连接"，而不是装作真的回答了。这点很关键——mock 是为了让用户那一端能继续操作（输入下一条、修改设置、看历史记录），不是为了假装一切正常。
  </p>
  <p>
    这事真做起来才发现，mock 还有一个隐藏好处：它让每一层都能独立开发。voice-bridge 在开发时，avatar-bridge 可以直接跑 mock，不需要 OpenClaw 真在跑。avatar-bridge 在调派活规则时，OpenClaw 那一层可以全 mock，让开发不被下游卡住。否则四层联调，一层挂全链路停，开发节奏会被拖得很碎。
  </p>
  <p>
    我设计 mock 的时候有一条原则：<strong>mock 必须可识别</strong>。它返回的内容会带一个明确的占位标记，前台看到这种标记会显式地告诉用户"当前为占位响应"。这条原则一开始我没想清，写过一版"假装一切正常"的 mock，结果有一次 voice-bridge 调不通 avatar-bridge，前台收到 mock 响应后正常地播了出来，我居然没发现下游断了一下午。从那以后，mock 必须显式可见——宁可显得粗糙，也不能让"系统其实没在工作"被假象遮住。
  </p>
  <p>
    另一条经验：mock 不要追求"看起来很聪明"。我曾经想过让 mock 用一个小模型生成一段更像真实回答的占位文字。最后没做——原因很直接：mock 越聪明，用户越分不清是真的还是占位的，越容易把假数据当真。简单粗糙的 mock 反而是诚实的——它的存在本身就在说"下游这一段断了"。
  </p>
  <p>
    v1.0 就这么干的，v2.0 没改这条。不是懒改，是被验证过了：mock 在线，整个系统就稳；mock 一拿掉，链路就脆。
  </p>
  <h2>为什么每一层都要风险关卡：不让 OpenClaw 被任意打穿</h2>
  <p>
    解耦解决了"换得动"，mock 解决了"撑得住"。但还有一个问题没解决——<strong>安全</strong>。
  </p>
  <p>
    一个本地系统的安全比看上去更容易被忽视。很多人觉得"反正只有我自己用，本地没风险"，但事实是：只要这套系统有端口、有 API、有能调用真东西的能力，它就有可能被绕过、被滥用、被无意中触发。哪怕是我自己说话说错了一句、识别错了一个字，也可能让 OpenClaw 去做一件不该做的事。
  </p>
  <p>
    所以每一层都要有自己的 risk-gate。
  </p>
  <p>
    前台这一层最朴素：白名单。哪些客户端可以连前台，哪些来源能注入消息，写死。没在名单里的，连页面都打不开。
  </p>
  <p>
    voice-bridge 这一层管声音边界：哪些 prompt 模式是允许的，哪些指令模式要立刻拦截。比如用户口语里有些容易被识别错的关键词，voice-bridge 会先做一层意图清洗，把高危表达打个标记往下传。
  </p>
  <p>
    avatar-bridge 这一层最关键。它是真正决定"这件事派给谁"的层，所以也是最严的一层。每个 agent 都有自己能做什么、不能做什么的边界，avatar-bridge 在派活之前要校验一遍：这个任务匹配这个 agent 的能力吗？需要的权限有吗？是不是属于需要二次确认的高风险动作？不通过就不派。
  </p>
  <p>
    OpenClaw 那一层自己也有一层 risk-gate。这是"防守的最后一道"——哪怕前面三层都被绕过，OpenClaw 内部还有自己的人设、自己的边界、自己的 audit log（审计日志）。任何 agent 都不能在没有 owner 放行的情况下做出超出自己能力范围的事。
  </p>
  <p>
    四层 risk-gate 听起来重复，其实不是。叠在一起的逻辑是：<strong>任何一层都不能被假设是可信的</strong>。前台可能被绕，voice-bridge 可能识别错，avatar-bridge 可能派错。所以每一层都自己守自己的门，不能指望外面那层把脏东西挡掉。
  </p>
  <p>
    这套做下来还有一个附带好处：audit log 每天轮转，每一层都写自己的。哪里出了事，哪一层的日志就最先看到。要复盘一次误识别，不是去翻一坨混在一起的全链路日志，是先看 voice-bridge 那天的识别记录，再看 avatar-bridge 的派活记录，最后看 OpenClaw 的执行记录。每一层日志各管一段，复盘速度反而快。
  </p>
  <p>
    我后来还总结出一个心得：<strong>每一层的 risk-gate 越严，下游能简化的逻辑就越多</strong>。如果 avatar-bridge 在派活之前已经把不合法的请求都挡掉了，OpenClaw 内部就不用再为"输入是不是恶意的"做太多防御性写法。它可以专心做自己擅长的事——执行任务。反过来如果上游 risk-gate 形同虚设，下游就要自己写各种边界检查，整个系统的代码会越来越臃肿。所以分层 risk-gate 不仅是安全设计，也是<strong>把责任摆清楚</strong>——每一层只需要做好自己那一层的检查，不用替别人兜底。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/agent-company/voice-risk-gates.svg" alt="四层 risk-gate 叠加：白名单、意图清洗、派活校验、agent 边界">
  </figure>
  <h2>v1.0 vs v2.0 的产品判断：核心架构不动，增量只在边边角角</h2>
  <p>
    现在系统跑在 v1.0。这个版本已经稳定运转了一段时间，端口固定（voice-bridge 3962、avatar-bridge 3961），白名单和 risk-gate 都在工作，audit log 每天轮转，mock fallback 兜着，识别、派活、执行各司其职。它不完美，但它是个"在跑的真东西"。
  </p>
  <p>
    我也开始做 v2.0。v2.0 的骨架已经落地，正在收口；v2.0 GA（General Availability，正式发布）还在等审计。但有一件事我从一开始就定下来了：<strong>v2.0 只做增量，不动核心架构</strong>。
  </p>
  <p>
    这是一个产品判断，不是一个技术判断。
  </p>
  <p>
    技术上 v2.0 完全可以"借这次机会把架构改一改"——比如把 voice-bridge 和 avatar-bridge 合并成一个进程少一次序列化，比如换一种更现代的通信协议，比如把 mock 兜底改成更智能的"假装继续聊天"。每一项单独看都说得通。
  </p>
  <p>
    但产品判断告诉我不要动。v1.0 的核心架构已经被几个月的真实使用验证过了：四层解耦、各自 mock、各自 risk-gate。这套结构不是凭空想出来的，是踩坑踩出来的。任何一处"借机会改一改"，都可能把已经验证过的稳态推回到不稳态。增量是安全的，重写是冒险的。
  </p>
  <p>
    v2.0 只做边边角角的增强。比如更细的意图识别、更友好的占位响应、对长对话的更好支持、对某些 agent 的派活规则做更精细的拆分。这些都是"加一点"，不是"重写"。原本的四层、四端口、四 mock、四 risk-gate 一个都没动。
  </p>
  <p>
    这种判断在自己一个人开发的项目里特别重要。一个人开发最容易掉的坑就是"每次升级都顺手重写"——因为没人拦着你，因为你昨天才写的代码今天看着不顺眼，因为新版本的 SDK 看着更性感。但真要它长寿，第一件事就是<strong>把验证过的部分锁住，把没验证过的部分留作增量探索</strong>。这两件事不能搅在一起。
  </p>
  <p>
    所以 v2.0 的设计原则被我写得很死：能不动核心架构就不动；新功能优先做在边缘；老功能除非有明确的"在线证据"说它出问题，不然不重写；任何"看上去更好的设计"，先在 mock 路径上验证，不直接上真路径。这些规则不是为了限制创造力，是为了让"v1.0 已经能跑这件事"这个事实不被新一轮兴奋冲掉。
  </p>
  <p>
    但 v2.0 也不是不做事。骨架已经落地，正在收口阶段。GA 还在等审计——是的，等审计，不是等代码。因为这一层涉及到 OpenClaw 内部 agent 的能力边界，必须有一次外部 review 看过、确认风险关卡没有被新功能绕过，才能正式开放。这个等待值得。本地系统一旦上线就很难再"全量回滚"，所以宁可在 GA 前多等一段时间。
  </p>
  <h2>真正的取舍：简单调用 vs 长期演进</h2>
  <p>
    回头看整件事，最大的一次抉择其实在最开始：要不要把一根直连写得更复杂一点。
  </p>
  <p>
    一开始多写两层 bridge 是有代价的。多一倍代码，多一份部署，多一份监控，多一份心智负担。如果只是想"我做一个能跑的语音助手"，三百行直连就够了，省下来的时间可以拿来做别的。
  </p>
  <p>
    但如果你想做的是"一个能跟我用半年、一年、两年的语音工作台"，多这两层就完全不一样了。<strong>它把"如何识别"和"派给谁"从前台彻底剥开</strong>，于是识别引擎可以换，调度规则可以演化，agent 可以增减，前台 UI 可以重做，每一件事都不会牵连别的事。
  </p>
  <p>
    这是一次很典型的"短期复杂换长期简单"。短期看，多两层 bridge 是负担。长期看，它把这套系统从"一坨容易烂的胶水"变成了"四个能各自演进的小服务"。
  </p>
  <p>
    我自己的经验是：本地 AI 系统里，凡是涉及到"用户入口 + 模型调用 + agent 执行"三件事同时存在的地方，都值得做这种解耦。不是因为它一开始就值，是因为它会避免一种最痛的烂——那种你明知道某一层该换、但因为耦合得太深所以不敢换，于是只能在一个越来越糟糕的状态下凑合用的烂。
  </p>
  <p>
    本地 AI 系统跟云服务最大的差别就是：你没有一个团队帮你撑着、没有 SLA 兜着、没有半年一次的大重构窗口。你只有你自己。所以稳定性不是靠"我会去修"，是靠<strong>"它本来就不容易坏"</strong>。三层解耦就是让它本来不容易坏的设计。
  </p>
  <p>
    我把这套判断浓缩成几条原则放在这里，给后面要做类似系统的人参考：
  </p>
  <ul class="journal-list">
    <li>语音入口要拆成"前台 / 识别 / 调度 / 执行"四层，每层独立服务、独立端口；</li>
    <li>每一层都自带 mock fallback，下游不通时保证用户那一端不"白屏"；</li>
    <li>mock 必须可识别，不能让占位响应被当成真回答；</li>
    <li>每一层都自带 risk-gate，不假设上下游可信；</li>
    <li>核心架构一旦验证过就锁住，所有新功能优先做增量；</li>
    <li>audit log 每层各写一份，按层切片复盘比全链路混在一起快得多。</li>
  </ul>
  <p>
    这些原则没有一条高深。它们都是"踩过坑之后才觉得理所当然"的那种。但理所当然这件事，往往要等到自己写过一版烂的、然后被自己折磨过一阵子，才会真正接受。
  </p>
  <div class="journal-ending">
    <p>
      v2.0 GA 还在等审计，mock 还在兜底，真 Claw 接入还没完成。
    </p>
    <p>
      纪嫣然不是一个"做完了的语音助手"，它更像一个"在跑、在改、在长"的工作台。v1.0 已经够稳——稳到我每天可以靠它做事，稳到我可以放心给它加新东西。但它远不是终态。语音入口的边界会继续模糊（多模态、多设备、长对话），下游 agent 的能力会继续长，风险关卡的颗粒度也会继续细。
    </p>
    <p>
      但有一件事我现在比一开始更确定：<strong>不管前台 UI 怎么变、识别引擎换成什么、OpenClaw 里有多少个 agent，这套四层、四端口、四 mock、四 risk-gate 的骨架不会动。</strong>它不是终点，它是让这个工作台能一直走下去的地基。地基不漂亮，但地基要稳。
    </p>
    <p>
      一句话收尾——本地 AI 系统不缺会写代码的人，缺的是愿意一开始就多写两层 bridge 的人。短期里这是负担，长期里这是寿命。语音入口看上去简单，但它是最容易让人误判难度的一层；正因为如此，它也最值得在第一次就把骨架做对。
    </p>
    <p>
      v1.0 已经稳了，v2.0 在路上。下一篇可能写真 Claw 接入完成之后看到的事——但那是下一篇的事了。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>纪嫣然</category>
  <category>语音工作台</category>
  <category>架构解耦</category>
  <category>public-safe</category>
</item>
<item>
  <title>Mac mini 重装前，我做了一次系统级资产审计</title>
  <link>https://yunlab.ai/notes/macmini-system-audit-before-reinstall</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/macmini-system-audit-before-reinstall</guid>
  <description>一台用了一年的 Mac 主机攒了一堆 AI 工具、后台服务、密钥文件。重装前我没急着清，先做了次资产审计——审什么、不审什么、清理为什么必须延后。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌面散乱的资料与笔记本，重装系统前的盘点场景" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">Mac mini 重装手记</p>
  <p class="journal-lead">
    重装这台 Mac mini 之前，真正让我犹豫的不是怕装不回来，而是怕亲手把一些还在偷偷给我干活的东西，连同那堆看起来该清掉的垃圾一起砍掉。
  </p>
  <p>
    那是一台用了一年多的工作主机。买回来那天我以为它只是台普通的小机器，结果一年下来，它先后承载了好几波 AI 工具实验、几个跑了一半的项目、几次为了赶进度临时塞进去的模型权重，还有数不清的、当初为了"先跑起来再说"装下的 launchctl（macOS 服务管理命令）注册项。它从来没崩过，也从来没真正"干净"过。
  </p>
  <p>
    系统盘的占用慢慢吃到 85%。Spotlight 偶尔抽风。开机的时候 Dock 上会弹出我已经不记得为什么装的图标。最让我心里发毛的是，每次我打开某个旧项目目录，里面总有几个 .env（环境变量文件）静静地躺着，文件里写着真的可用的 token（密钥）——而我已经忘了它们当初是为哪个实验配的。
  </p>
  <p>
    我本能的反应是："那就重装吧。clean 一遍，从头开始。"
  </p>
  <p>
    但我停下来，是因为突然意识到：我根本不知道这台机器上现在到底有些什么。
  </p>
  <p>
    不是完全失忆——大方向我都还记得，哪些项目跑在哪里、哪些工具我每天在用、桌面上摆着的几个图标分别是什么。但只要再往下问一层："这个项目的临时缓存放在哪？""那个 token 是哪个服务的？""上个月那次实验留下的中间产物你清过吗？"——我就开始打哈哈。"大方向清楚、细节全模糊"，平时用着没问题，重装时却要命。重装不问大方向，它只一项一项地问细节。含糊掉的那些，都会变成事故。
  </p>
  <p>
    后来我才意识到，"想重装"这个冲动本身就是信号。不是机器坏了，是我已经不信自己对这台机器的认知还准确。重装在心理上的吸引力，是用一次动作把"我不知道这台机器上有什么"的尴尬一笔勾销。但代价是那些"知道但忘了在哪"的东西也会被一起埋掉。这笔买卖不划算。正确的做法不是先重装，是先补认知。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/canonical-source/source-ladder.svg" alt="重装之前要先把家底摸清，而不是直接动手清理">
  </figure>
  <h2>清理不是真问题，盘点才是</h2>
  <p>
    "重装一台主机"听起来像纯执行的事。备份、抹盘、装系统、装工具、把数据搬回来。但中年人的工作主机跟大学时代那台空白机器不一样：上面堆着一堆"我以为不依赖了、其实还在依赖"的东西。
  </p>
  <p>
    比如，某个我半年没碰过的小工具，其实是另一条工作链上某个脚本的隐含依赖。比如，某个我一直当成"实验失败"的目录，其实还在被一个 LaunchAgent（macOS 后台服务）定时拉起，每天默默地往云端推一份数据。再比如，某个被我嫌弃占地方的 model weights 目录，其实是我最近正在用的推理后端的真实加载路径——只是我每次启动服务的时候没注意它在哪。
  </p>
  <p>
    这些东西的共同特点是：它们在散落记忆里。不在文档里，不在我脑子最前排，也不在桌面上显眼的地方。它们安静地工作，安静到我自己都忘了它们的存在。
  </p>
  <p>
    重装这一刀下去，安静工作的东西会先死。等我发现哪条工作流断了，往往已经过了一两周。那时候我已经记不清当初是哪个服务、哪个脚本、哪个目录在背后撑着。我会从零去复原，复原过程中又会再装一堆"先跑起来再说"的东西。机器干净了三天，又回到原样。
  </p>
  <p>
    问题不是"怎么把这台机器清干净"，而是"在我动手之前，能不能先有一份明白的清单，告诉我这台机器上到底有哪些东西、哪些还在用、哪些可以扔"。所以我决定先做一次资产审计（audit），而不是直接动清理。
  </p>
  <p>
    审计和清理，是两件事。审计是把家底摸清，清理是按家底做决定。两件事混在一起做，最容易出事。
  </p>
  <p>
    重装有完全不同的两种动机。一种是"环境已经不能满足新需求，需要换架构"——建设性动机，重装是手段，目标在前面。另一种是"环境太乱，想推倒重来求个清爽"——逃避性动机，重装是仪式，目标在后面甚至没目标。我这次老老实实承认，是后一种。逃避性的重装最容易在过程中出事：你心里没有"重装后要长成什么样"，遇到"嗯这个好像还能用"的东西，就没法判断。审计是把逃避动机掰回建设动机的唯一办法：审完之后，你至少知道新机器该长什么样。
  </p>
  <h2>我审了哪 6 类资产</h2>
  <p>
    我没有现成的方法论可抄。市面上"Mac 清理指南"基本都是讲怎么腾空间、怎么删 cache、怎么清 Time Machine 旧备份。这些对一台普通的 Mac 够用，对一台塞满 AI 工具和实验项目的工作主机不够用。我自己摸出来一套，分了 6 类。
  </p>
  <ul class="journal-list">
    <li><strong>后台常驻服务</strong>：所有 LaunchAgent、cron 项、launchctl 注册的服务。这一类是最容易被遗忘也最危险的——它们在背后运行，不会出现在 Dock 或 Activity Monitor 的醒目位置。</li>
    <li><strong>本地工程目录</strong>：所有项目目录、IDE workspace、Docker volume、临时实验目录。这一类决定了"我究竟在这台机器上做过哪些事"。</li>
    <li><strong>含密钥的文件</strong>：所有 .env、.aws、.ssh、各种带 token 字段的配置文件。这一类不只是占空间问题，更是安全问题——重装的时候一不小心就会被同步到云盘或者直接被新的 git 工程拖进 repo。</li>
    <li><strong>大体积资产</strong>：model weights、数据集、缓存、下载归档。这一类决定了"哪些东西重装后要重新下载、哪些可以扔"。一个 70B 模型下载一次要花几个小时，扔掉再下载就是给自己挖坑。</li>
    <li><strong>装机轨迹</strong>：哪些工具是哪一次装的、为什么装、还需不需要。Homebrew 装的包列出来一长串，每一个都有当初的理由，但其中可能一半已经不再使用。</li>
    <li><strong>历史账本</strong>：之前的 audit 报告、备份归档、旧版本的项目快照。这一类是"我过去做过的审计或备份的记忆"，决定了我能不能在出事的时候回到一个已知干净的状态。</li>
  </ul>
  <p>
    这 6 类不是凭空想出来的。是我一边在机器上翻、一边发现"这玩意我刚才没归类"的时候补出来的。前 3 类——后台服务、工程目录、密钥文件——是任何一台跑过几个月 AI 实验的主机都该审的，没有讨价还价的余地。后 3 类——大体积资产、装机轨迹、历史账本——是可选项，但如果你打算重装，强烈建议都审一遍。
  </p>
  <p>
    合起来看，它们回答的是一个看似哲学其实很操作的问题：这台机器作为我的工作主机，到底"是谁"？前 3 类决定它"在干什么"——后台运行的进程、占据的工作场所、跟外部世界的连接凭证；后 3 类决定它"经历过什么"——存了什么真正重的资产、装过哪些工具、留下过哪些自我观察的痕迹。一台机器跟一个人一样，知道它在干什么不难，知道它经历过什么、攒下了什么习惯、欠了哪些技术债，才是真正了解它。审计让我第一次把"我的工作主机"从抽象概念变成了具体的东西。
  </p>
  <p>
    审计不需要做得多花哨。我用的是最朴素的方法：每一类对应一个 markdown 文件，里面列条目、写状态、标判断。我的本地审计目录里现在就是 6 份文件，加一份汇总。看起来一点都不性感，但它是这次重装唯一能让我心安的东西。
  </p>
  <p>
    顺便说一句，这 6 类的审计顺序也是有讲究的。我建议先做后台常驻服务——最容易遗漏，也最容易在你动手清理别的东西时被无意中触发。然后做含密钥的文件——这一类直接决定你能不能进入"清理"阶段。接下来才是本地工程目录和大体积资产，这两类是工作量大头，但风险相对可控。装机轨迹和历史账本可以放在最后，算收尾整理。我自己一开始顺着字母顺序乱审，结果回头发现密钥那块踩了个大坑，整个清理被迫推后——后面我会专门讲这件事。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/canonical-source/truth-source-map.svg" alt="散落记忆经过审计变成 6 类清单，每类对应不同的处理路径">
  </figure>
  <h2>跑出来的 4 个数字</h2>
  <p>
    审计真正的价值，是把"模糊感觉"变成"具体数字"。
  </p>
  <p>
    我审完后，这台机器上的 AI 栈被压缩成了 4 个数字：6 个 LaunchAgent、4 个 .openclaw* 目录、26 个含 token 的 .env 文件、加上一个大约 14G 的主要工作目录。
  </p>
  <p>
    数字一出来，整个心态就变了。
  </p>
  <p>
    审计之前，我对这台机器的描述大概是"乱"，"塞了不少东西"，"有点失控"。这种描述听着像抱怨，但没法指导行动——你没法对"乱"动手。审计之后，我至少能对每一个数字说一句具体的话：6 个 LaunchAgent 里 4 个还在用 2 个可以下、4 个 .openclaw* 目录里 1 个是 canonical 主目录其他 3 个是历史快照、26 个 .env 必须迁移到统一的 secret 管理位置、14G 的工作目录里大部分是可以保留的、少部分是临时实验产物。
  </p>
  <p>
    审计做的事，就是把"乱"翻译成可以判断的项。被翻译成项的问题，才值得动手。停留在"乱"层面的，只能继续忍着。
  </p>
  <p>
    话又说回来，光有数字还不够，数字背后必须配一句具体描述。"6 个 LaunchAgent"如果只是一个数，没有写明每一个是干什么的、是哪个项目装的、最近一次跑成功是什么时候，那它和"乱"没本质区别——只是把模糊的乱换成了精确的乱。审计真正生效的那一刻，不是我数出 6 这个数字的时候，而是我对每一个 LaunchAgent 都能说出一句"它叫什么、为谁服务、现在是死是活、删了会出什么事"的时候。数字是骨架，描述是肉。光有骨架的清单还不能拿去做决定，骨架加肉的清单才能。
  </p>
  <p>
    我以前总以为这种盘点是程序员洁癖。后来我发现这其实是中年人做大事之前的标准动作——装修之前要量房，搬家之前要清点，重装一台 Mac 当然也要。区别只是没人会因为你不审计而骂你，大多数人就跳过了。代价是：每次重装都像一次小型的赌博，赌的是自己有没有忘掉什么关键东西。
  </p>
  <p>
    另一个让我有点意外的发现：数字小，不代表问题轻。6 个 LaunchAgent 看起来一点都不多，但每一个都意味着一段我已经忘掉的逻辑——它什么时候被注册的、为了哪个项目、跑失败了会怎么样、删掉之后哪些下游会断。要把这 6 个真正搞清楚，我花的时间比把 26 个 .env 列清单还久。审计带来的不是 satisfaction，是一种"你以为只有一点点其实是冰山"的清醒。这种清醒比"删掉之后觉得舒服"重要得多——它会拦住你没看清水下就急着动手的冲动。
  </p>
  <h2>为什么清理被迫推后</h2>
  <p>
    审计跑完，我本来准备进入下一步：根据这 6 类清单，挨个动手。该删的删、该归档的归档、该迁移的迁移。计划是一晚上做完，第二天就可以开始重装。
  </p>
  <p>
    结果在第二类——含密钥的文件——这里彻底卡住了。
  </p>
  <p>
    26 个含 token 的 .env，听起来是个不大的数字。但等我真去看它们里面装的是什么的时候，我冒了一身冷汗。有的是某个推理服务的 API key，有的是云盘的访问凭证，有的是早期为了快速验证某个流程而申请的临时 token——但"临时"已经用了半年。它们散落在 4 个不同的工程目录里，每个目录又分了好几层。有几个文件甚至连我自己都说不清是哪个实验留下来的。
  </p>
  <p>
    这意味着两件事。
  </p>
  <p>
    一是，这些 secret 必须在重装之前先迁出去，统一管起来。不能简单备份，结果就是它们继续散落在新机器的对应位置上。也不能直接删，有几个是我当下还在用的。最安全的办法是迁到一个集中、加密的 secret 管理位置，然后改用引用的方式调用，而不是让明文 token 继续躺在每个项目目录里。
  </p>
  <p>
    二是，迁移本身就是一个独立的、需要小心做的工程。它不是审计的延伸，是另一个项目。涉及到改每个项目的引用方式、验证迁移后服务还能跑、把旧文件安全销毁。这件事做得快一周，做得不急一两个月。
  </p>
  <p>
    清理被迫推后。我没有在审计当晚动那 14G 的工作目录，也没有去下那 6 个 LaunchAgent。理由很简单：在 secret 还没迁干净之前，任何"清理动作"都有可能把含 token 的文件不小心同步到云端备份、不小心 commit 进某个新建的 repo、不小心被某个还在跑的 LaunchAgent 拉走。重装的诱惑很强，但安全的窗口期还没到。
  </p>
  <p>
    这种"明明审完了却不能动手"的感觉，刚开始让我很不爽。但仔细想想，这其实就是审计的价值。如果没审，我可能就直接 clean 安装、然后从云盘把"项目目录"整体拖回来——里面 26 个 .env 也一起回来，全部还是明文，全部还散落在 4 个目录里。重装一次，问题没解决，反而被合法化了：我会更难下决心去整理它们，因为它们已经"跟着新系统一起活下来了"。
  </p>
  <p>
    顺着 secret 往下想，还有件更冷静的事：一台工作主机上真正"贵"的东西，从来不是硬件，也不是装回来要重下的 model weights，而是这些散落的凭证——它们对应着我跟外部服务的真实信任关系。每一个 token 背后，是我点过的同意条款、绑过的支付方式、可能扣过的费用、可能产生过的访问日志。它们不是"配置项"，是我的延伸账户。把它们当 .env 文件随意散落，本质上是把账户钥匙当便签纸贴满了整栋楼。看清这件事，重装这种"清扫"动作就显得很次要——真正该补的功课是 secret 的治理框架，框架不会因为重装自动出现，只能坐下来一项一项设计。
  </p>
  <h2>审计教会我的几件事</h2>
  <p>
    这次审计跑完，没解决任何具体的清理问题，但改变了我看这台机器的方式。
  </p>
  <p>
    第一件事，重装的真实门槛不是怕装不回来。装系统有现成手册，工具链可以重建，model weights 可以重新下载。这些都不算门槛。真正的门槛是怕一些"还在用的服务"被自己亲手砍掉而不自知。这种风险没有手册，只能靠审计先把它们摆到明面上。
  </p>
  <p>
    第二件事，散落记忆是工作主机的慢性病。一台机器用一年，自然会攒下一堆"我以为我记得"的东西——某个工具是为什么装的、某个目录是从哪个项目复制过来的、某个 LaunchAgent 是哪一周配的。这些记忆刚攒下来时很清楚，半年后就全模糊了。审计治不好这种慢性病，只能定期清算一次，让模糊的东西重新变成清楚的清单。
  </p>
  <p>
    第三件事，密钥治理才是真正的工作主机风险，不是空间。我以前关心的是"还剩多少 GB"。审计后我才意识到，空间只是表面问题——真正会出事的是 26 个明文 token 散落在 4 个目录里。重装不解决这件事，不过是把安全漏洞从旧机器原封不动搬到新机器。空间不够大不了买个外挂硬盘，token 泄露了是要去吊销凭证、检查访问日志、跟服务商解释的。
  </p>
  <p>
    第四件事，审计和清理必须分开。这是我这次最大的收获。审计是观察，清理是行动。把它们混在一起做，会出现两种坏结果：要么是"边审边删"，删掉某个其实还在用的东西；要么是"审完不敢删"，因为审的时候没把判断标准想清楚。分开做，先审完拿到全清单，再坐下来一项一项决定怎么处理，速度反而更快、判断也更稳。
  </p>
  <p>
    第五件事，审计本身就是一份资产。我这次审完的 6 份 markdown，会跟着进我的本地审计目录归档。下次重装、买新主机、或者有人问我"你这台机器都跑了些什么"，这份审计就是最直接的答案。不只解决一次性问题，还能在未来复用。
  </p>
  <p>
    第六件事——也是最反常识的一件——重装前先盘点，不要被"清个空间"的冲动牵着走。85% 的占用率会让人很想立刻动手删点什么。但那种"删完一下舒服了"的快感，是这件事里最危险的情绪。真正该解决的不是占用率，而是这台机器到底有哪些东西在跑、哪些还需要、哪些必须治理。占用率只是症状。审计才是触诊。
  </p>
  <p>
    第七件事，是节奏。审计花了我一整个下午加大半个晚上。时间长到让我中途好几次想偷懒——"是不是大概看一眼就行了？""LaunchAgent 反正就那么几个，扫一眼凭印象判断够不够？"——每次想偷懒的时候我都强迫自己继续。后来回头看，审计这件事的难，不是技术上的难，是心理上的难。它太朴素了，没有即时反馈，删一个文件看不到进度条变化，列一个清单不会让机器变快。它给你的全部回报，是事后那种"我现在知道我在动什么"的踏实感。这种回报是延迟的、看不见的、只有出事时才会感谢自己做过的。对习惯"做完事看到结果"的人来说，扛过审计中期那种"没看到成果"的疲惫，需要真心相信"先看清楚比先动手快"。我这次扛过去了，下次还会扛。
  </p>
  <p>
    第八件事，这次最意外的副产品：审计本身可以变成一种习惯。它不必非得在重装前才做。我现在的想法是，每季度做一次轻量审计，不是为了清理，是为了"对账"。LaunchAgent 多了几个？.env 文件多了几份？工程目录里有没有冒出来一些我已经不记得的实验？把这些定期回答一下，就能避免下一次又攒到"想砸掉重来"的程度。这就像中年人的体检——不是为了治病，是为了不让小问题攒成大问题。这台 Mac mini 让我第一次意识到，一台工作主机也需要体检，不是只有出事才关心。
  </p>
  <div class="journal-ending">
    <p>
      当前状态是：审计完成，清理还没做，密钥迁移是真正的下一步。重装的日期被无限期推后了。
    </p>
    <p>
      我本来以为这是一篇关于"如何高效重装 Mac mini"的笔记。写到这里我才意识到，它更像一篇关于"为什么不要急着重装"的笔记。这台机器的卫生问题还会拖几个月，因为 secret 迁移这件事比看起来麻烦。但我现在至少有了一份清单，知道每一项卡在哪、下一步该往哪走。这就比一周前那种"乱得想砸掉重来"的状态强多了。审计没让机器变干净，但让我拿回了对它的判断权。这件事本身，比腾出来那几十 G 重要得多。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>Audit</category>
  <category>Mac mini</category>
  <category>系统审计</category>
  <category>重装</category>
  <category>密钥治理</category>
  <category>public-safe</category>
</item>
<item>
  <title>Mission Control 和 Studio：当控制平面开始重叠</title>
  <link>https://yunlab.ai/notes/mission-control-vs-studio-overlapping-control-planes</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/mission-control-vs-studio-overlapping-control-planes</guid>
  <description>本地 Studio + Mission Control Web + 未来的 Gateway，三套都能管 agent / 任务 / 审计。每个单独看都合理，三个放一起就开始抢职责。我现在的分工判断、落地难点、还在改的地方。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：工作台俯视，三套控制平面分别在不同区域" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw 架构治理手记</p>
  <p class="journal-lead">
    本地的 Studio 能管 agent（智能体），Mission Control（任务中控）的网页端也能管 agent，将来要装的 Gateway（网关）打算把请求路由到 agent 上——三套都能管，三套都有道理。问题不在哪一套不好，问题在三套放在一起的时候，谁说了算开始变得不清楚。
  </p>
  <p>
    这件事最近一直挂在我的待办列表里。每一步当时看都对——本地 Studio RC1（首个候选发布版）跑起来的时候该有自己的边界，Mission Control Web v2.0.1（Alpha，早期开发阶段）作为 agent 编排仪表盘也该有自己的 dashboard（仪表盘），未来的 Gateway 作为外部入口也该有自己的路由规则。但三件单独正确的事被同时塞进同一个工作系统，重叠就来了。
  </p>
  <p>
    本月我没急着收口，先把「谁该回答什么问题」写清楚——这一步比加任何新功能都重要。下面是我现在的分工判断、几个具体冲突点，以及为什么我决定先写 spec（规约），不动代码。
  </p>
  <h2>每一套单独看都合理</h2>
  <p>
    先把三套各自的来源说一遍。不讲清楚每一套为什么存在，冲突的时候就没法判断该让谁让步。
  </p>
  <p>
    本地 Studio RC1 是本地工作系统。它最初的存在理由很朴素——我需要一个不依赖外部接口就能完成晨检、任务流转、内容产出的底座。所有 agent 的契约（读哪里 / 写哪里 / 不能动哪里）都签在本地，所有任务的 audit（审计）都落在本地审计目录，所有内容产出的 evidence（证据）也都留在本地。Studio 的设计立场一直是「本地真相」——我不信任何不在本地能复现的状态。这条立场不是技术品味，是被现实逼出来的：外部接口随时会变，远端服务随时会挂，第三方记录随时会被改写，本地审计是唯一能在事后追回当时发生过什么的依据。
  </p>
  <p>
    Studio 的设计还有一层含义：agent 的契约必须在本地能被读到。一个 agent 能读哪些目录、能写哪些目录、绝对不能动哪些目录，RC1 阶段就被定成了文件——不是数据库里的一行，也不是远端配置中心里的一项。看起来死板，但它换来了一个性质：本地任何一次工具调用要被允许，都得回到这份契约文件查一次。Studio 的 agent 控制平面建立在本地文件系统之上，离开本地它什么都决定不了。这是它的强项，也是它的边界。
  </p>
  <p>
    Mission Control Web 是另一回事。它是 Alpha 阶段的 v2.0.1，作为 Agent 编排仪表盘存在。要解决的问题很直接——agent 不止一个、任务不止一条、外部模型不止一家的时候，光看本地日志已经看不过来了。MC Web 想做的是舰队视角：多 agent 同时跑、调度可视化、cost tracking（成本追踪）、安全审计 dashboard。它在网关适配层留了多框架的口子，状态写在 SQLite 里，pnpm start 起服务，整体偏前端工程师视角。这套东西的设计立场是「全局观察」——我得能一眼看到全部 agent 现在在干嘛、烧了多少钱、有没有越界。
  </p>
  <p>
    MC Web 还有一条设计目标常被忽略——跨框架管 agent。本地的 agent 是一种实现，未来用 DeerFlow 跑一条研究流程是另一种实现，更远的将来接入别的编排框架又是一种实现。MC Web 不该绑死在一种实现上，网关适配层才留了多框架的口子。代价是对每一种具体 agent 的语义只能做到「最小公分母」：能看到 agent 在跑，能看到烧了多少 token，但要看内部更细的状态，还得回到 agent 自己的实现里。这条限制是 MC Web 这一类工具的天然属性，不是 v2.0.1 的缺陷。
  </p>
  <p>
    Gateway 还没装。它现在的样子只是 MC Web v2.0.1 里那个标成「Gateway Optional」的口子——团队知道总有一天会有这么个东西，但没决定什么时候装。Gateway 的设计意图很明确：所有外部进来的请求由它统一路由、统一鉴权、统一限流，不让外部直接打到本地或者打到 dashboard 上。它的设计立场是「边界守门人」——外部世界进来的任何东西先过我这道闸。
  </p>
  <p>
    Gateway 的存在还有一个理由：分层防御。本地 Studio 和 dashboard 都不该直接面对公网。本地系统专心做本地能做的事，dashboard 专心做展示，外部请求该有一个专职的层去做最脏最累的事：拒绝异常流量、限制频率、做最外层的鉴权。这件事不交给一个专门的层，迟早会污染 Studio 或 MC Web——它们会因为外部的脏请求而被迫加入大量本来不该有的防御代码。Gateway 装不装是选择，但只要还没装，Studio 和 MC Web 暴露的端口就在默认承担「外部入口」的职责——这是个隐性的麻烦。
  </p>
  <p>
    三套各自的立场——本地真相、全局观察、边界守门——单独看都成立。麻烦的是这三件事在 agent 这个对象上交叉。一个 agent 既要有本地契约，也要在 dashboard 上被看到，将来还要接受外部进来的请求。三套控制平面就这样开始重叠。
  </p>
  <h2>放在一起开始抢什么</h2>
  <p>
    重叠不是抽象问题，它有具体形状。最近这两周，我至少在四个地方撞到了。
  </p>
  <p>
    第一个冲突：一个本地任务到底归谁调。Studio 自己有任务调度——晨检时它会触发苏晚去整理一篇内容，触发霍锐去做安全巡检，触发沈知行去拉信息。这些都是本地链路，Studio 自己说了算。但 MC Web 的 dashboard 上也有一个「任务调度」面板，理论上从那里也能下发一条「让苏晚现在跑一篇」的指令。两个入口都能调一个 agent，就是两条调用路径，两份调度状态。两边状态不一致——一个说在跑，一个说没跑——agent 该听谁的？这个问题以前没遇到，因为 MC Web 没真接进来；接进来的那一刻它就来了。
  </p>
  <p>
    这个冲突最阴的地方在「两边都没怎么用的时候」根本看不到。本地 Studio 跑得稳，MC Web 当看板用着没人下发任务，一切都和谐。一旦哪天我图方便从 MC Web 触发了一条任务，本地 Studio 的调度状态里没有这条记录，过几个小时本地周期任务又被触发跑了一遍——同一件事跑了两遍，留下两份审计记录，两边都觉得自己是对的。这种重复触发不是技术 bug，是控制平面没说清楚谁是入口的副作用。
  </p>
  <p>
    第二个冲突：一个 agent 的运行状态、成本、审计该写在哪里。Studio 有自己的本地 audit——每一次 agent 写文件、每一次任务流转、每一次工具调用都有记录，留在本地审计目录。MC Web 有 dashboard——要展示「过去 24 小时这个 agent 跑了多少次任务、消耗了多少 token、有没有越界」。两份数据各写各的，就是两份真相；其中一份是镜像，得先选谁是源。我现在的本能反应是 Studio 的本地 audit 是源——但这只是本能，没写进 spec 就不算分工。
  </p>
  <p>
    审计这件事在控制平面里特别敏感。真相源不是被「指定」出来的，是被「真的写入」出来的——哪一份数据先被写下来、哪一份后被同步过去、两边不一致时采信哪一份，都要事先讲清楚。我以前遇到过的最难追的 bug，几乎都来自审计不止一份且没有公认源。这个冲突点我格外谨慎——agent 的真相源必须在一开始就定，不能等出问题再回头协调。
  </p>
  <p>
    第三个冲突：将来 Gateway 装上以后，外部请求该先打谁。一种走法是 Gateway → Studio → MC Web（本地优先，dashboard 是观察侧）；另一种走法是 Gateway → MC Web → Studio（编排优先，本地是执行侧）。两种走法都能跑通，但路径完全不同——前者意味着 MC Web 看到的永远是 Studio 已经处理过的二手信息，后者意味着 Studio 接到的请求都已经被 MC Web 的策略筛过一遍。这事现在不定，等 Gateway 真装上去再定，就是边装边改边纠错，代价很大。
  </p>
  <p>
    第四个冲突更隐蔽：cost tracking 该算哪一层的责任。Studio 自己也能记录每次工具调用的成本——它有 audit，加一列字段就能记。MC Web 也能记——它本来就是为编排和观察设计的，cost 是它的天然字段。这件事现在两边都没好好做，暂时没冲突；一旦两边都开始认真做，又是两份成本数据，又是「以哪一份为准」的问题。更麻烦的是成本最终会被汇总到「这个月烧了多少钱」这种粒度——两份数据都存在，汇总时一定会双计或漏计，要么虚高要么虚低，没有中间状态。
  </p>
  <p>
    这四个冲突点放在一起看，其实是同一个形状——三套系统在「谁拥有 agent 这个对象的某一面」上没有写清楚边界。Owner（所有权）没定，调度路径没定，真相源没定，成本归属没定。每一项单独看都不是大事，加在一起就是控制平面的扯皮。
  </p>
  <h2>重叠不是 bug，是规模副作用</h2>
  <p>
    我一开始想把这件事归咎于「当初设计没规划好」。后来发现这个归因不对。Studio 当年只是想解决本地能不能跑，MC Web 当年只是想解决多 agent 看不过来，Gateway 当年只是预留一个外部入口的口子。三件事不在一个时间点被设计，也不在一个语境下被设计。
  </p>
  <p>
    重叠是规模长出来的，不是设计错出来的。一个组件刚生下来的时候只对自己的那一小块负责；跑稳了、跑久了、跑到一定规模，自然会开始把手伸向相邻的责任。Studio 跑到 RC1 之后开始想「我能不能也提供一个简单的看板」——这就是它伸向 MC Web 的领域；MC Web 跑到 v2.0.1 之后开始想「我能不能直接调度本地 agent 不经过 Studio」——这就是它伸向 Studio 的领域。每一个组件最初都不是为了和别的组件竞争而生的，但活到一定规模就开始抢同一块责任。
  </p>
  <p>
    我觉得这是普遍现象。任何一个长出来的工作系统，活过初期之后都会面对控制平面重叠的问题——不是有人做错了，是活下来的组件自然会扩张。重叠是个体存活的副作用。
  </p>
  <p>
    扩张的方向也有规律。一个最初只解决「能跑」的组件，跑稳之后第二个本能是「让我自己也能看到我在跑什么」——开始长出一个简单的查询接口、一个简陋的状态面板。这个面板一旦存在，责任就侵入「观察」这件事；原本专门做观察的组件就开始觉得「我看到的怎么和它自己看到的不一样」。第三个本能是「让外部也能调我」——原本只服务本地的组件会开始想给外部留一个调用入口。这个入口一旦存在，原本专门做外部入口的组件就开始觉得「为什么外部不走我这里」。本能是好的，组件想活得更强壮的本能是好的，但每一次本能扩张都会把控制平面的边界推糊一点。
  </p>
  <p>
    意识到这一点之后，我对收口的看法变了。我以前倾向于「重叠了就赶紧砍掉一边」，但这种砍法往往砍掉的是当下最弱的一边——不是长期最不该负责这件事的那一边。短期看是收口了，长期看是被错位的力量推回去——被砍掉的责任过几个月还会以另一种形式长回来。
  </p>
  <p>
    现在我处理重叠的方式不是先动刀，是先定立场——这件事长期该归谁，归不到位的那一边短期可以保留，但要标记成「过渡」。过渡很关键——它承认现状不理想，但不假装现状是理想，也不强行立刻整改。它给真正该负责的那一边时间去补能力，同时给暂时承担过渡责任的那一边一个退出预期。这种处理比一刀砍痛苦得多——要写更多 spec、要做更多沟通、要忍受更长的不一致——但能避免「砍完之后责任又长回来」的循环。
  </p>
  <h2>我现在的分工判断</h2>
  <p>
    这一轮我没动手砍，先停下来写分工——把「谁该回答什么问题」写在 spec 里，而不是先改代码。
  </p>
  <p>
    下面这套分工是我现在的判断，不是已经落地的状态：
  </p>
  <ul class="journal-list">
    <li>Agent 的 owner——Studio。Agent 的契约（读哪里 / 写哪里 / 禁止哪里）在本地，agent 的版本、能力、稳定性记录也在本地。MC Web 看到的是 Studio 暴露出来的 agent 元信息，不是 MC Web 自己定义的。</li>
    <li>任务的真相源——Studio 的本地 audit。每一次任务执行的原始记录留在本地，MC Web dashboard 上看到的是同一份记录的展示，不是另一份独立数据。</li>
    <li>编排和观察——MC Web。多 agent 舰队视角、任务调度可视化、安全审计 dashboard、跨 agent 的统计聚合，都归 MC Web。它不是 audit 的来源，是 audit 的视图。</li>
    <li>Cost tracking——MC Web。成本数据天然跨 agent、跨外部网关、跨模型供应商，视角应该在更高一层；Studio 不再自己单独算成本。</li>
    <li>外部入口——Gateway。所有外部进来的请求由它统一收，做完鉴权、限流、路由之后，再决定打给 Studio 还是打给 MC Web。Studio 和 MC Web 都不再暴露外部端口。</li>
    <li>调度入口——双入口，但 Studio 优先。本地周期性任务由 Studio 自己调；从 MC Web dashboard 触发的任务最终也要走 Studio 的调度器，不是 MC Web 直接调 agent。</li>
  </ul>
  <p>
    这套分工里其实只有一句话——agent 的本地真相归 Studio，全局视图和外部边界归外面。四个冲突点都能从这一句话推出答案。花时间先写分工比直接动手值得：一句对的话能解决一堆细的问题。
  </p>
  <h2>分工写清楚不等于落地</h2>
  <p>
    话又说回来，写在 spec 里不等于已经能跑。我自己最清楚——这套分工真的落地，至少要半年。
  </p>
  <p>
    第一件要做的是 API 边界。Studio 现在没有一个对外的 agent 元信息接口——agent 的契约是文件，不是接口。MC Web 想看到这些信息只能去读文件。要让 MC Web 真的「看到 Studio 暴露的 agent 元信息」，得先在 Studio 这边定义一组接口，把 agent 列表、agent 当前状态、agent 当前任务这些字段以稳定的契约暴露出来。这件事说起来一句话，做起来要梳整整一个版本——哪些字段稳定、哪些可变、版本怎么演进、向后兼容怎么办，都要单独想清楚。
  </p>
  <p>
    API 边界还有一件事要想清楚——Studio 没起来的时候怎么办。MC Web 永远只通过 Studio 暴露的接口看 agent 信息，Studio 离线的时候 MC Web 就是个空壳；MC Web 保留一份本地缓存来应对 Studio 离线，缓存又要单独维护，缓存陈旧的时候 dashboard 上看到的就不再是真相。两种选择都有代价，但都比「没想清楚就先实现一个版本」要稳。我倾向于第一种——宁可空壳，不要假数据——但这一条还没真定，要等 MC Web 那边的人也表态。
  </p>
  <p>
    第二件要做的是调度路径的梳理。Studio 自己有调度器，MC Web 也想做调度入口，两边没有共识的入口规范。理顺这一块需要把所有「能触发一个 agent 跑起来」的代码路径列出来，然后收口到一条主路径上。这种梳理是慢活——每一条路径都要单独验证「收口之后是不是还能跑」。
  </p>
  <p>
    调度路径里最讨厌的是那些「侧门」——不是主流程的入口，但确实能触发 agent 跑起来的小路径。比如某个本地脚本里直接调用了 agent 的执行函数，跳过了 Studio 的调度器；比如某个测试用例里 mock 出来的触发路径在生产代码里也能跑通。这些侧门平时没人走，但只要存在，分工就不算清楚。梳理的过程就是把所有侧门列出来然后逐个堵掉——很枯燥，不能跳。
  </p>
  <p>
    第三件要做的是 audit 同步机制。Studio 的本地 audit 是真相源，MC Web 的 dashboard 是镜像——这句话写得轻巧，但「镜像」是一个需要机制去维护的状态。本地 audit 写入之后多久同步到 MC Web，同步失败了怎么办，MC Web 那边的展示和本地 audit 不一致的时候以哪一份为准，这些都要单独定。我现在的偏向是 MC Web 永远只读 Studio 暴露的接口，不维护自己的写入路径——但这意味着 MC Web 在 Studio 不可达的时候 dashboard 是空的，又是另一个权衡。
  </p>
  <p>
    audit 同步还有一层我以前没意识到的隐含问题——涉及隐私边界。本地 audit 里有些信息本来就不该被推到 dashboard 上展示——比如某些任务的中间产物、某些 agent 的内部状态、某些和外部账号相关的字段。同步机制不能简单做成「全推过去」，要有一层过滤。这层过滤本身又是一个 spec——哪些字段可推、哪些不可推、可推的是否需要脱敏，都要写清楚。这件事从 Gateway 那边看更容易做（外部入口本来就要做这种过滤），但 Gateway 还没装，这层责任暂时还在 audit 同步机制这边。
  </p>
  <p>
    第四件，其实也是 Gateway 真装上去之前的预备工作，是把 Studio 和 MC Web 各自的对外暴露面收窄。现在它们俩各自暴露自己的端口，Gateway 装上去之后这些端口都要藏到 Gateway 后面。这件事的代价不是技术上的，是迁移上的——所有已经存在的、直接打 Studio 端口的调用方都要改路径。
  </p>
  <p>
    收窄暴露面这件事不是改一个配置就完。所有外部脚本、所有外部集成、所有曾经偷偷直连过来的小工具都要重新走 Gateway——而 Gateway 还没装。所以这一件事最务实的做法是先把暴露面盘清楚：现在有哪些端口在外部可达、每一个端口的调用方有谁、这些调用方是否还在被使用。盘清楚之后才能谈收窄。这种盘点是「无聊但必要」的事——本身不产生新功能，但它是分工真正能落地的前提。
  </p>
  <p>
    本月我只做了第一件的一部分——梳理了几个 API 边界的草稿，把 agent 元信息、agent 当前状态这两组字段先定下来。其它三件都还在排队。我不想骗自己说「分工定了就等于做完了」——分工定了只是第一步，落地是更长的事。
  </p>
  <h2>关于这件事我还在改</h2>
  <p>
    分工现在写在 spec 里，但还没有任何一行代码因为这套分工被改动过。MC Web 暂时还是按自己的节奏跑 v2.0.1，Studio 还是按 RC1 的边界跑，Gateway 还在「Optional」状态。这套分工要变成代码里的事实，至少要半年——前提是中间没有别的更紧急的事插队进来。
  </p>
  <p>
    本月真正完成的没几件——agent 元信息 API 的字段草稿、agent 当前状态 API 的字段草稿，以及一份冲突点清单。冲突点清单本身没解决任何冲突，但它让我每次再撞到一个新冲突的时候知道这是不是已经被记过的、能不能用同一套分工答上。
  </p>
  <p>
    MC 和 Studio 的 audit 同步机制还没动。调度路径还没收口。Gateway 装不装、什么时候装、怎么装，都还没有时间表。我现在不急着定——经验告诉我，这种「装外部入口」的事在分工没真正落实之前就强推，会把分工本身搅烂。
  </p>
  <p>
    我对这件事的态度是：先把分工写清楚比先把功能加上去重要，先把边界收口比先把版本号往前推重要。Mission Control v2.0.1 是 Alpha，迭代速度还会很快；Studio RC1 是候选发布版，但本地链路也还在持续被改；Gateway 八字没一撇——三套都在变的时候，唯一不变的应该是分工。
  </p>
  <div class="journal-ending">
    <p>
      没有「完美架构」这种东西，只有「职责清楚 + 边界写出来」的架构。完美架构是想出来的，职责清楚是改出来的；前者一画图就完，后者每个月都要回头校一次。
    </p>
    <p>
      Mission Control、Studio、Gateway 这三套控制平面会长期共存，重叠不会被一次性消除——它会以分工是否清楚为准、被反复重新平衡。这件事我没有终点表，只有每月推一小步的节奏。
    </p>
    <p>
      下一次再写这件事，多半是从「某一条 API 边界终于落地」或者「某一次以为收口了结果又长回去」开始讲。在那之前，我还在改。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Mission Control</category>
  <category>Studio</category>
  <category>架构治理</category>
  <category>public-safe</category>
</item>
<item>
  <title>页面类型：一个能用的知识库为什么需要 6 种页面</title>
  <link>https://yunlab.ai/notes/six-page-types-for-a-working-knowledge-base</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/six-page-types-for-a-working-knowledge-base</guid>
  <description>材料攒下来不等于知识库就能用。每一页得先认清自己是什么类型——概念、决策、项目、审计、时间线、参考。6 种页面、三条写作规则，以及怎么和新鲜度标记联动。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌上一排资料，每份贴着不同类型标签的笔记本" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">知识库结构笔记</p>
  <p class="journal-lead">
    你打开任何一页，3 秒之内能不能判断它是来干什么的——这往往比内容写得多漂亮，更能决定知识库能不能用。
  </p>
  <p>
    上一篇我写过怎么决定一份材料该留还是该扔。判定做完，材料就只剩下能继续支撑工作的那一部分。但留下来，不代表知识库就能用——只是从一堆乱目录，变成了一堆体面的乱目录。真正的挑战在后面：这些材料该组织成什么样的页面。
  </p>
  <p>
    我自己被这件事坑过很多次。同一个 wiki 里，有的页面是抽象原则，有的是项目当下状态，有的是上周一次核查，有的干脆就是一段命令清单。单看都成立，混在一起就开始打架——读者点进去前不知道会看见什么，写的人下次更新也不知道该按什么规则改。
  </p>
  <p>
    现在我给每一页都先回答一个问题：它是什么类型。回答不出来，就先别写；回答得含糊，就拆开。
  </p>
  <h2>从材料到页面：上一篇没讲完的那一层</h2>
  <p>
    上一篇讲的是材料层——什么能进入知识库、什么应该留在工作目录里。那一层判定做完，手里大概剩几十份「值得变成页面」的材料：一段已经稳定下来的判断、一次改变了系统行为的选择、一个还在跑的项目的现状、一份验过证据的核查记录、一段被压缩过的演化史、一张被反复查的命令清单。
  </p>
  <p>
    材料分类看「它是什么内容」，页面分类看「它要承担什么角色」——两件事不一样。一份审计报告作为原始证据可能被排除，结论却得改写成「Audit 页面」留下；一段聊天记录里的决策片段不进知识库，决策本身却要变成「Decision 页面」。从材料到页面，中间有一次重新分类。
  </p>
  <p>
    这次分类做不好，整个知识库就会塌成「文档大杂烩」——什么都有，什么都不像。后来我强迫自己只用 6 种页面类型，多一种都不许加。
  </p>
  <h2>6 种页面类型，每一种都长什么样</h2>
  <p>
    顺序不是从最重要排到最不重要，是从「最稳定」排到「最易变」。越稳定的页面越接近知识，越易变的页面越接近工具。
  </p>
  <h3>Concept 概念页：一个稳定下来的想法或规则</h3>
  <p>
    Concept 页讲的是「这件事我是怎么看的」。它不带具体项目，不带具体时间，最好也不带具体人。一个 Concept 页应该可以在两年以后被新接手的人原样读，仍然成立。
  </p>
  <p>
    例子：Canonical（真相源 / 权威）选择规则、Knowledge Model 本身、Page Types（页面类型，也就是本文背后的那一页）。这类页面写出来以后基本不动，动了就要写一篇 Decision 页解释为什么改。
  </p>
  <h3>Decision 决策页：一次改变了系统行为的选择</h3>
  <p>
    Decision 页只回答四件事：当时面对什么问题、有哪些备选、为什么选这个、后来证明对不对。它不是会议纪要，也不是设计文档——它是一份「我以后想知道当年为什么这么做」的备忘。
  </p>
  <p>
    例子：后来为什么给所有上线动作加了 dry-run-first（先空跑一次）、为什么把主控写入责任人迁到独立的 sidecar 通道、为什么放弃某个看起来更通用的方案。Decision 页写完不再改，最多在末尾追加一段「后续观察」。
  </p>
  <h3>Project 项目页：一个有边界的工作流加当前状态</h3>
  <p>
    Project 页是 6 种里唯一允许频繁动的。它存在的目的，就是让任何一个新窗口、新 AI、新一天打开它，都能马上知道「这个项目当下在哪一步」。但光靠这个还不够——它必须有 owner、有当前阶段、有下一步动作、有最近一次更新时间，少一项都不算合格。
  </p>
  <p>
    例子：OpenClaw Studio 某个 RC（候选发布）的当前状态、纪嫣然语音工作台某版本的开关状态、苏晚审美样本库的进度。Project 页是 wiki 里最危险的一类——最容易过期，也最容易被人误当成 Concept 页引用。后面会单独讲怎么把这一类和新鲜度标记绑死。
  </p>
  <h3>Audit 审计页：一次带证据链的核查</h3>
  <p>
    Audit 页讲的是「在 X 时间，我用 Y 方法核查了 Z，结论是这样，证据在那里」。它和 Decision 不一样——Decision 是要选什么，Audit 是已经做了什么、看到了什么。Audit 页天然带时间戳，带 evidence trail（证据链），带可复现的方法。
  </p>
  <p>
    例子：某个 Stage（阶段）链路的端到端审计、一次内置磁盘资产盘点、一次跨账号配置一致性核查。Audit 页有意思的地方在于过期了也不能删——它就是历史，价值就在「当时确实做过这件事」。但它不该被新人读了以后误以为「现在还是这样」，新鲜度标记几乎默认 stale（已过期）。
  </p>
  <h3>Timeline 时间线页：一段被压缩的编年</h3>
  <p>
    Timeline 页是给「我想理解这玩意儿是怎么变成今天这样的」那一类问题准备的。它不写细节，只写转折点——某月某日做了某个关键选择、某周整个架构从 A 切到了 B、某个版本之后某个组件被弃用。
  </p>
  <p>
    例子：某个项目某个季度的演进时间线、某个生态从单机到多账号的转换史、个人 AI 工作站从去年到今年的硬件演化。Timeline 页写出来以后不密集更新，只在「系统级变化」发生时追加一行——这跟我对 CHANGE 时间轴的处理方式是一回事。
  </p>
  <h3>Reference 参考页：一份命令、路径、边界的紧凑查表</h3>
  <p>
    Reference 页是工具，不是知识。它存在的意义不是被读，而是被查。它应该尽可能短、尽可能扁平、尽可能可复制粘贴——表格优先于段落，列表优先于句子。
  </p>
  <p>
    例子：Gate 矩阵（哪一类操作走哪一条审批通道）、端口分配表、各账号的 Feishu 路由表、常用调试命令清单。Reference 页有个特殊属性：经常没有作者立场，纯粹是事实的快照。过期方式也因此很纯粹——事实变了就过期，事实没变就一直对。
  </p>
  <h2>三条写作规则：80% 的腐烂从破坏这三条开始</h2>
  <p>
    6 种类型分清楚以后，写每一页的时候只需要遵守三条规则。这三条规则我看着很笨，但每一条背后都是真实的翻车经历。
  </p>
  <h3>规则一：一页混了「概念 + 状态 + 操作」就拆</h3>
  <p>
    这是我见过的最大杀手。一个 README 页面，前三段讲项目设计哲学（Concept），中间五段讲当前进度（Project），最后又贴了一段启动命令（Reference）。当下看着「信息很全」，三个月以后没人愿意打开。
  </p>
  <p>
    不是写得不好，是压根没法维护。设计哲学半年不动一次，当前进度可能每周改一次，启动命令可能下次重装就变。三种节奏完全不同的内容硬塞在一页里，结果只能是——更新的人只敢动最易变的那一段，剩下的越来越假。
  </p>
  <p>
    拆开以后情况立刻不一样：Concept 页基本不动，Project 页只动状态那一行，Reference 页只在事实变化时刷新一次。每页都有自己的更新节奏，也不再骗人。
  </p>
  <h3>规则二：一页能被「链接到更好的真相源」替代就替代</h3>
  <p>
    我以前喜欢写「总结页」——把好几页内容糅成一份摘要，自以为对新读者友好。后来才发现，总结页是知识库里最危险的产物。它不更新，原始页一更新，总结页就开始撒谎；新读者偏偏更倾向于读总结页。
  </p>
  <p>
    现在我的规则很简单：如果一页能被一句话改成「请看 X 页」，就不要写这一页，直接放一个链接。链接不会过期得比目标页更快，总结页会。
  </p>
  <p>
    例外只有一种——这一页确实在做组合判断，把几个分散的 Concept 串成一个新结论。这时候它本身就是一个新 Concept 页，不是总结页。区别在于它有自己的判断，不只是把别人的话换个说法。
  </p>
  <h3>规则三：一页只是「执行日志」就只留摘要</h3>
  <p>
    AI 项目里特别容易冒出「执行日志型页面」——某次实验的命令逐条贴、某次 debug 的过程一路记、某次部署的输出原样保留。当时觉得「记录完整最安全」，半年以后再看，页面长得让人绝望，根本没人会读。
  </p>
  <p>
    我现在的做法是：执行日志原文留在 evidence（证据）目录里，知识库里只保留摘要——这次跑了什么、得到了什么结论、有哪些反常被记录在了哪份原始日志里。摘要可能只有 200 字，但它能被读、能被引用、能被维护。原始日志能被搜索、能被回溯，但不需要长在知识库里假装自己是一页知识。
  </p>
  <h2>页面类型和新鲜度标记怎么联动</h2>
  <p>
    类型分清楚以后，最大的好处不是检索更快——是过期检查的节奏可以按类型差异化。上一篇我写过三态新鲜度标记：verified（已验证）、stale（已过期）、needs review（待复核）。但三态不能一刀切——每一类页面过期得不一样快。
  </p>
  <p>
    我现在大致按这套 cadence（节奏 / 周期）来设：
  </p>
  <ul class="journal-list">
    <li>Concept 页：半年到一年复核一次。它本来就稳，查得太勤反而浪费。</li>
    <li>Decision 页：原则上不复核，只在出现「这个决策好像不对了」的信号时挑出来重看。历史事实，不是当下状态。</li>
    <li>Project 页：每两周到一个月复核一次，超过周期没人动就自动滑到 stale。</li>
    <li>Audit 页：默认 stale。一次性的核查证据，不打算永远 verified。</li>
    <li>Timeline 页：每次有「系统级变化」发生时追加一行，没有专门复核周期。</li>
    <li>Reference 页：每次依赖的事实变了就同步刷新一次。事实没变就保持 verified。</li>
  </ul>
  <p>
    这样分以后，「复核知识库」不再是让人头大的总动员，而是几个不同节奏的小循环——半年扫一遍 Concept，每个月扫一遍 Project，Reference 跟着事实走。压力被分散到不同的时间点。
  </p>
  <p>
    更关键的是：读者打开一页的时候，能根据「类型 + 新鲜度」两个信号判断这一页可信到什么程度。一个 verified 的 Concept 页和一个 verified 的 Project 页，可信度的含义不一样——前者是「这事我想清楚了」，后者是「这事到上周还成立」。两个信号联起来读，比单独看任何一个都准。
  </p>
  <h2>三种最常见的页面腐烂模式</h2>
  <p>
    讲完正面规则，反过来列一下我自己踩过的、也在别人 wiki 里看到的三种典型腐烂模式。一个知识库变烂，多半走这三条路。
  </p>
  <h3>模式一：混类型页面，谁都不愿意动</h3>
  <p>
    最常见的形态是 README——既要讲设计、又要讲当前状态、又要贴启动命令。一开始图省事，到后面所有人都不敢碰：动设计部分怕影响状态描述，动状态部分怕和设计对不上，动命令部分又怕贴错。最后这一页变成「最权威也最不准」的页面。
  </p>
  <p>
    解法不是改它，是拆它——拆成 Concept、Project、Reference 三页，每一页交给不同节奏维护。原 README 只留一行链接索引。
  </p>
  <h3>模式二：过期不标，所有页面看着一样可信</h3>
  <p>
    没有新鲜度标记的 wiki，所有页面看起来权重相同——一份 18 个月没动过的 Project 页和一份昨天刚 verified 的 Concept 页，在搜索结果里长得一模一样。读者无法区分，AI 助手在做 RAG 检索时也无法区分，结果就是「老的会胜过新的」——老页面通常更长、更结构化，反而更容易被命中。
  </p>
  <p>
    解法是把新鲜度标记当成强制字段，而不是可选字段——一页没有 freshness signal（新鲜度信号），它就不能进入正式的检索结果。哪怕标 needs review 都好，至少读者知道这一页要警惕。
  </p>
  <h3>模式三：重复总结，真相源被稀释</h3>
  <p>
    一个项目同时存在「设计文档」「项目说明」「FAQ」「Wiki 概要」「Onboarding 指南」，里面 70% 内容互相重复，只是侧重点和写作风格不同。每次原始事实变了，五份文档里只有一两份被同步——剩下三份继续按旧事实讲故事。
  </p>
  <p>
    解法是认一份真相源，剩下的全部改成「请看 X 页」加一段窄窄的本页特有补充。这一条最难，因为「写一份新的」永远比「指向一份旧的」更有成就感——但知识库腐烂得最快的部分，恰恰是那些反复总结出来的「漂亮的新版本」。
  </p>
  <div class="journal-ending">
    <p>
      6 种页面类型用到现在大概够用——至少这几个独立 wiki 里我没遇到「明明是某种类型但不知道往哪放」的情况。
    </p>
    <p>
      但边界模糊的时候仍有。Concept 和 Decision 之间最容易反复横跳——一个「我决定以后都这么做」的规则，到底算我现在的判断（Concept），还是一次决策（Decision）？我目前的临时办法是：还会被推翻的，写 Decision；已经稳定下来、可以独立成立的，提升到 Concept。但这条线不总是清楚，有时候要过几个月回头看才能定。
    </p>
    <p>
      过期检查的 cadence 我也还在调。Project 页两周一次，有时太勤，有时不够；Audit 页默认 stale 听起来果断，真遇到长期 audit（比如季度复盘）又显得过于一刀切。这些都还会再迭代。
    </p>
    <p>
      不过有件事我现在比较确信：知识库能不能用，不取决于页面写得多好，而取决于每一页是不是老老实实承认自己是哪一种。一页知道自己是什么，整个库才知道自己是什么。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识系统</category>
  <category>页面类型</category>
  <category>真相源</category>
  <category>整理层</category>
  <category>public-safe</category>
</item>
<item>
  <title>12 套 OpenClaw 副本之后：路径和根目录是怎么变成风险的</title>
  <link>https://yunlab.ai/notes/twelve-openclaw-roots-when-paths-become-risk</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/twelve-openclaw-roots-when-paths-become-risk</guid>
  <description>扫了一下 home 目录，发现 12 套带 openclaw 字样的根。只有 2 套真在跑，剩下 10 套是历史残片。副本不危险，被当成真根用才危险——五种典型风险 + 五步处理原则。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌面上一排资料，被铅笔圈出真相源的位置" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw 路径治理手记</p>
  <p class="journal-lead">
    某天下午我闲着没事，敲了一行 find，想看看 home 目录里到底有几个带 openclaw 字样的根目录。屏幕上慢慢滚出来一长串，我数了一下，整整 12 套。
  </p>
  <p>
    那一刻不是惊讶，是有点恍惚。我心里清楚生产上真正在跑的是哪几套——一套挂着虚拟公司，一套挂着纪嫣然——加起来 2 套。剩下的 10 套，每一套都还带着 openclaw 这个名字、还有一份看起来完整的目录结构、还有配置文件、还有 .env、还有 token、还有过去某个时间点我亲手写下的 README。它们都还在那里，安静地占着磁盘，没坏，也没人在用。
  </p>
  <p>
    问题不是"我攒了 10 套垃圾"。问题是这 10 套副本里，有几套曾经是真根。它们被新版替代的那一刻，并没有自动从磁盘上消失；我也没有主动去删——每删一套都得先确认"现在没有任何调用方还指着它"，确认这件事本身就很麻烦。它们就这么留了下来，时间一长就堆成了 12 套。
  </p>
  <p>
    我以前总觉得，AI 项目里最大的风险是新功能跑不通、是模型抽风、是 prompt 写错。做久了我才明白，活到一定年龄的项目，最大的风险是旧副本还在跑——你不知道哪一份是真的，AI 不知道，调用方更不知道。每一份副本看起来都长得像真根，每一份副本里的配置看起来都能用，每一份副本里的 token 都还在有效期里。这不是垃圾问题，是真相源（canonical source / 权威来源）问题。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/canonical-source/source-ladder.svg" alt="12 套根目录中只有 2 套是真根，其余 10 套是历史残片">
  </figure>
  <h2>这 12 套是怎么慢慢攒出来的</h2>
  <p>
    我后来把这 12 套按性质分了一下，发现它们不是一次性堆出来的，是这一年里我自己一锹一锹挖出来的。每一锹挖的时候我都觉得是必要的，每一锹挖完我都没顺手填上，于是就攒成了今天这个样子。
  </p>
  <p>
    最上面 2 套是 authoritative root（权威根目录 / 真根）：一套是虚拟公司的生产根，挂着审批、调度、agent 配置；另一套是纪嫣然的生产根，挂着她自己的 MCP（Model Context Protocol，模型上下文协议）服务、声音工作台、本地状态。这两套是当前真在跑的，所有有效流量都打在这里。它们本身没问题。
  </p>
  <p>
    顺便说一句，"OpenClaw"这个关键字在我机器上覆盖的远不止这 12 个根。如果把搜索范围从根目录扩到所有 .openclaw* / .clawdbot / .clawai 这种系列前缀，再加上各种带 openclaw 字样的子目录、配置缓存、运行时日志、agent 身份文件，整台机器上能扫到 13 个独立目录、几百个相关文件。我只盯根目录这一层——根目录是治理意义上的"地基"，其他层都附着在根上。地基理顺了，上面的东西才能跟着理。</p>
  <p>
    问题出在下面这 10 套。
  </p>
  <p>
    第 3 套是旧主配置残片，曾经是真根，被新版接替以后就留在那里没动。它的目录名几乎和现在的生产根一样，少了一个后缀；它的 .env 里还有去年某个时间点拿的 token；它的 scheduler 配置文件还活着，只是没人调用了。这是最危险的一类副本，因为它和真根长得最像。
  </p>
  <p>
    第 4 套是拼写错误的残片：某天我手抖，把 openclaw 敲成了 openclag，回车下去就建了一个空目录，后来又顺手往里塞了几个测试文件。这种残片最容易识别——名字本身就错的；但也最容易被忽略——我自己都忘了它存在。
  </p>
  <p>
    第 5 套是实验副本，挂在某个第三方平台的 projects 目录下面。当时我在那个平台上跑过一阵子扩展实验，把 openclaw 整个目录复制了过去做适配测试。实验结束以后，整个副本就留在那里，连同它自己那一份带着真实 token 的配置一起。
  </p>
  <p>
    第 6 套是审计副本。某次做 audit（审计）的时候，为了不污染生产根，我把整套目录复制到了 Desktop 上的一个隔离目录里跑审计脚本。脚本跑完了，审计报告也出了，但那份复制出来的副本没人提醒我删，就这么留了下来。
  </p>
  <p>
    第 7 到第 10 套是 4 份历史版本，命名都带着版本号——v3_openclaw_agents 这种格式。它们是我做大版本切换的时候留下来的"以防回滚"备份；切换稳定以后我没回过头去清理，每一份都还在原地，目录名清晰、内容完整、再也没被打开过。
  </p>
  <p>
    第 11 套是归档副本，藏在年度归档目录的深处，是某次整理硬盘的时候我把整个 openclaw 主目录整体搬过去的。当时是为了腾空间，但搬过去之后我又在原地重新建了一套——我"不放心归档目录"——结果归档区也留下了一份完整副本。
  </p>
  <p>
    第 12 套是压缩备份，一个 tar.gz 文件，藏在某个 cleanup-backup 目录里。这个我连什么时候打的包都想不起来了，文件名里带着一个日期，打开来看是去年某个版本的完整快照。压缩包最讨厌的地方在于它和目录不一样——它不会被普通的 find 直接列出来，要主动加 *.tar.gz 这种 pattern 才能扫到。也就是说，一个项目治理到一半，最容易漏的就是这种压缩态副本。它没有目录结构，它不出现在 ls 里，它只在某次磁盘清理的时候才会被想起来。
  </p>
  <p>
    把这 10 套挨个数完，我才意识到：每一类副本背后都有一个当时合理的理由。备份、审计、实验、回滚、归档、手抖——没有一个理由是错的。错的是我每次留下副本的时候，都没有顺手做一件事——给它打一个标签，说明它是什么性质、什么时候过期、谁有责任清。它们以"我以后再处理"的姿态留了下来，"以后"却一直没来。
  </p>
  <h2>副本本身不危险，副本被当成真根用才危险</h2>
  <p>
    如果这 10 套副本只是安静地占磁盘，问题其实不大。一年攒 10 个旧目录，对一台 4T 的工作机来说不算什么。
  </p>
  <p>
    真正的风险在另一边：副本被当成真根用了。
  </p>
  <p>
    我最近就撞上了两件事。一件是纪嫣然的 MCP 路径，配置里还指向旧的 .openclaw——也就是第 3 套那个旧主配置残片。这个路径已经失效了一段时间——它从一个老的 config 里继承下来，新版生产根上线时没人重新校对过，MCP 调用走的还是老路径。表面上一切正常，旧目录里那份配置文件还在、还能加载；实际上它读出来的是过期的 agent 设置。这就是"副本被新调用方误用"的典型形态。
  </p>
  <p>
    另一件是虚拟公司的 scheduler（任务调度器）。它在跨根调度纪嫣然和共享工具，即公司的根目录里那个调度器，会跑到另一个根目录里去触发任务。这本来是中性的——跨根调度不一定是错——但问题是，调度器走的几条路径里混着旧副本的路径。也就是说，公司这边的调度器，可能在某些 case 里调到的是副本里的脚本，而不是真根里的最新脚本。这是"副本之间互相引用"的典型形态。
  </p>
  <p>
    这两件事都没造成事故，但都让我看见了一件事：副本被当真根用，是一个无声的失败模式。它不会在某个早上突然炸掉，它会以"明明设置改了为什么没生效"、"明明 bug 修了为什么还在复现"、"明明 token 轮换了为什么旧 token 还能用"这种小困惑慢慢出现。每一次困惑单独看都不致命，连起来看就是一条治理失控的曲线。
  </p>
  <p>
    更麻烦的是 AI 也分不清。我让 AI agent 去做事，它会顺着配置文件里的路径去找——它不知道哪个路径是真根、哪个是副本。它只看路径合法、目录存在、文件可读，就当真材料用了。这个时候 AI 越听话，副本污染的范围就越大。AI 不是问题的来源，但它会忠实地放大问题。
  </p>
  <p>
    还有一种更隐蔽的形态：副本互相调用。当 home 目录里同时存在多套 openclaw，它们曾经都是某个时间点的真根，每一份都带着自己那个时代的依赖图。今天我打开第 5 套副本的某个脚本，它可能 import 了第 3 套副本里的一个工具；我打开第 7 套副本的某个 config，它可能指向第 11 套副本里的某个模板文件。这种交叉引用不是我设计出来的，是历史层堆叠出来的副作用。每一次新版本切换时，我都尽量把它和旧版本切干净，但"尽量"不是"全部"——总有几条边漏在外面。漏的这几条，过两年回头看就是一张让人头皮发麻的关系图。
  </p>
  <h2>副本带来的 5 种典型风险</h2>
  <p>
    把这一年我和这 12 套副本打交道的所有麻烦摊开来看，可以归成五类风险。
  </p>
  <p>
    <strong>第一类是真相源不清。</strong>哪一份是 authoritative root？人不一定记得清，AI 更不知道，调用方只看路径不看语义。当 home 目录里同时存在 12 套带相同关键字的根，"真"这件事就不再是默认状态，需要每次主动确认。一个项目活到这个阶段，最沉重的认知负担不是新功能，是"我现在动的是不是真根"。
  </p>
  <p>
    <strong>第二类是凭证扩散。</strong>每一套副本可能都带过 token、key、.env。它们当初是合法的、是配过权限的、是真能用的。当副本留下来，凭证也跟着留下来。你以为你只有 2 套生产凭证要管，实际上你有 12 套。哪一天某个旧 token 被泄漏，溯源会指向某个你早已忘了的 archive 目录——这种事故的修复难度，远高于"线上服务出 bug"。
  </p>
  <p>
    <strong>第三类是旧依赖回潮。</strong>副本里的代码还在引用一些已经失效的服务——某个早就下线的内部 bridge（桥接服务）端口、某个不存在的本地 SearXNG 实例。这些引用平时不出现在生产路径上，但只要有任何一次调用走偏到副本里，就会立刻命中失效依赖。错误日志会出现一些你早已不记得是什么的端口号，调试链条要往回追半年。
  </p>
  <p>
    <strong>第四类是审计无效。</strong>这是我最近才想清楚的一件事。如果 audit 跑的是副本，结论不能代表生产；可如果 audit 没说清楚自己跑的是哪一套，得出的结论看起来又长得像生产结论。审计本来是为了让我对系统更有信心。结果 audit 一旦跑在副本上，它反而让我对错误的状态更有信心。这是治理学上最糟糕的反馈方向。
  </p>
  <p>
    <strong>第五类是维护成本指数级。</strong>副本越多，每做一次治理动作（比如轮换密钥、升级依赖、改路径约定）都要乘以副本数量。2 套真根做一次升级，能在一个下午搞定；12 套全部走一遍，要花一整周，还得一份份判断"这套要不要跟、不跟会不会留隐患"。维护成本不是线性增加的，是指数级增加的——每一套副本都和其他几套有一些隐含的引用关系。
  </p>
  <p>
    这五类风险有一个共同点：它们都不是技术风险，是治理风险。技术风险可以靠写更好的代码消解，治理风险只能靠制度——给每一份材料定责任、定边界、定生命周期。我以前花在前者上的时间是后者的十倍，做到这一年才意识到，治理这件事的隐性成本，远远超过任何一次单点的技术债。
  </p>
  <h2>我的处理原则：不直接删，先标</h2>
  <p>
    最容易的反应是"那就一次性全删呗"。我一开始也想这么干，但很快就劝住了自己——一旦真删下去，我就再也没有机会做溯源了。直接全删等于把治理问题转成数据丢失问题，账没算清就把账本烧了。
  </p>
  <p>
    所以我现在走的是五步：识别、止血、迁移、归档、删。每一步都有自己的边界，不要跳。
  </p>
  <p>
    <strong>第一步是识别。</strong>把所有副本扫出来，每一份打一个标签：authoritative（真根）、legacy（遗留 / 历史）、experiment（实验副本）、audit-copy（审计副本）、archive（归档）。标签不是装饰，是责任——一旦贴了 legacy，意思就是"这一份未来要被收掉，新代码不能再引用它"。一旦贴了 archive，意思就是"这一份只读，谁都不要再往里写"。
  </p>
  <p>
    <strong>第二步是止血。</strong>确认没有新代码再引用副本路径。这一步要 grep 一遍代码库、grep 一遍配置文件、grep 一遍 LaunchAgent（macOS 后台服务）和 plist、grep 一遍 cron。任何一个还在引用副本的地方都要列出来。不止血就迁移，等于一边搬路一边还有车在跑老路上，迁完路才发现还有半个车队漏在外面。
  </p>
  <p>
    <strong>第三步是迁移。</strong>把还指向副本的引用全部改成指向 authoritative root。这一步要小心改，每改一处就跑一次回归——尤其是涉及 MCP 路径、scheduler 配置、bridge 调用这几类核心入口。改完之后做一次烟囱测试：从最上层入口触发一次完整流程，看是不是所有调用都正确走到了真根。
  </p>
  <p>
    <strong>第四步是归档。</strong>确认零引用之后，副本搬到 archive 区。archive 区是只读的、是带时间戳的、是和生产路径完全隔离的。搬过去之后要在原位置留一个 README，说明这一份去了哪、什么时候去的、为什么去。不能直接删原位置——直接删会让 AI 和我自己都失去线索。
  </p>
  <p>
    <strong>第五步是删。</strong>archive 区呆够 3 个月，期间没有任何人、任何调用方来找它，才真删。这个 3 个月不是拍脑袋——是我观察自己工作模式得出的：一个失效的引用，最长会在 3 个月内被某次回归测试、某次审计、某次"咦那个东西去哪了"的疑问触发出来。3 个月平安无事，基本可以判定它真的不再需要。
  </p>
  <p>
    五步走下来，慢，但不可逆的动作放在最后。前四步都是可逆的——标签可以改、引用可以回滚、归档可以搬回来。只有第五步删是不可逆的，必须在前面四步全部完成、并且时间证明过的前提下才能做。
  </p>
  <p>
    这五步还有一个隐含的设计：它强迫我把"治理意图"和"治理动作"分开。识别和打标签是意图——我先表达"我打算怎么处置这一份"，再用后面四步把意图转成动作。意图和动作分开的好处是，AI 也能进来帮忙。我可以让 AI 去 grep 引用、可以让 AI 去跑回归、可以让 AI 去搬归档——但前提是标签已经定好。标签是人的责任，不是 AI 的。AI 不能替我决定"这一份算 legacy 还是 archive"，但只要这件事我定了，后面的执行 AI 可以做大半。
  </p>
  <h2>为什么不直接全删：副本是证据，不是垃圾</h2>
  <p>
    我后来意识到，对待这 12 套副本的态度，决定了我对整个项目的认知方式。
  </p>
  <p>
    如果我把它们看成垃圾，那答案很简单——一句 rm -rf 就完事，省下几十 GB 的磁盘空间，桌面清爽。但如果我把它们看成证据，事情就完全不一样。每一套副本都是一段历史的物证：第 3 套告诉我"曾经的生产根长这样"；第 5 套告诉我"我在某个第三方平台上跑过这一类实验"；第 7 到 10 套告诉我"我曾经做过 3 次大版本切换，每一次都留下了完整的回滚快照"；第 12 套告诉我"去年某天我对系统状态足够不安，所以打了个完整 tarball"。
  </p>
  <p>
    这些证据有什么用？在三种场景下都有用。
  </p>
  <p>
    第一种是溯源。某天发现一个奇怪的 token 在外面被人用了，要回到当时这个 token 是为哪个项目配的、是在哪个版本里生成的——副本是唯一能回答这个问题的来源。生产根早就把这个 token 轮换掉了，它不记得自己的过去。
  </p>
  <p>
    第二种是回滚。某次新版本上线之后某个 agent 行为变得很奇怪——是不是新版引入的？副本里保留的旧版完整状态可以拿来做 A/B 对照，几分钟就能定位。如果副本都删干净了，A/B 对照就要从 git 历史里重建运行环境，那是几天的工作。
  </p>
  <p>
    第三种是审计可信度。外部 audit 经常会问"你是从什么时候开始这么做的"、"上一个版本的设计是什么样的"。这种问题不能靠记忆回答，要靠物证。一套带时间戳的归档副本，是审计学意义上最干净的回答。
  </p>
  <p>
    所以直接全删，本质上是用空间换时间——用磁盘上的清爽，换未来溯源、回滚、审计时的失语。这笔交易在 home 目录小的时候看起来划算，在项目活到一定年龄的时候就完全不划算了。
  </p>
  <p>
    分类保留才是治理。它要求我承认一件事：副本和生产根是两种不同性质的存在，它们需要不同的对待方式。生产根要保持流量、要持续维护、要严格控制谁能改；副本要打标签、要止血、要冻结、要在适当时机消亡。混在一起看，副本就是垃圾；分开看，副本是项目自己的考古层。
  </p>
  <h2>我现在的进度：标了一半，真删一套都没做</h2>
  <p>
    把所有这些原则写下来很容易，做下去很慢。
  </p>
  <p>
    截止今天，这 12 套副本里的处理状态是这样的：2 套生产根之外的 10 套，我标了 5 套是 legacy（旧主配置残片、拼写错误残片、3 份历史版本），3 套是 archive-only（年度归档副本、压缩备份、其中 1 份历史版本变更幅度最大，值得保留），还有 2 套在 review（实验副本和审计副本，需要先确认带的 token 是不是已经全部轮换）。
  </p>
  <p>
    止血也只做了一半。代码库 grep 过一遍，配置文件 grep 过一遍，但 LaunchAgent 和 plist 那一层还没扫干净——这是 macOS 上最容易漏的一层——它们藏在用户和系统两个目录下，命名规则也不统一。
  </p>
  <p>
    迁移工程还在排队。纪嫣然 MCP 路径指向旧 .openclaw 这件事我已经知道了，但 owner 还没放行改动——这一改要重启 MCP 服务，要重新做一次回归，改之前还得先把 MCP 当前的运行状态完整快照下来。公司 scheduler 跨根调度的事更复杂，要先把所有跨根调用的清单列出来，再决定哪些保留、哪些收回。这两件事都不是一个下午能搞定的，它们要排进未来几周的工程节奏里。
  </p>
  <p>
    归档区还没搭。我现在只是把判定为 archive-only 的几套副本贴了标签，但还没真正搬到一个独立的、只读的、带时间戳的归档目录里。这一步要等止血和迁移都完成才能做——搬完之后原位置就只剩一个 README，如果还有调用方在指原位置，那个调用方会直接失败。
  </p>
  <p>
    真删，一套都没做。最早能进入"真删"流程的副本，按 3 个月观察期算，也要到秋天才轮得到。
  </p>
  <p>
    我对这个进度不焦虑。一年攒出来的 12 套副本，不可能用一周就清完——也不应该。如果我用一周就清完了，那说明我跳过了某些步骤；跳过的步骤迟早会以另一种形式回来找我。
  </p>
  <div class="journal-ending">
    <p>
      做这件事的过程里我反复想到一句话：项目活到一定年龄，路径就变成了风险。
    </p>
    <p>
      新功能没跑通是看得见的风险，旧副本还在跑是看不见的风险。看得见的风险逼着你解决，看不见的风险纵容着你拖延。我现在不再追求一个干净的 home 目录，我追求的是一个能说清楚每一份副本性质、每一条引用归属、每一个凭证生命周期的 home 目录。前者只是好看，后者才是真治理。这 12 套副本，我还在慢慢收口。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <category>Audit</category>
  <category>OpenClaw</category>
  <category>路径治理</category>
  <category>副本管理</category>
  <category>系统审计</category>
  <category>public-safe</category>
</item>
<item>
  <title>从日志到知识：我如何决定保留什么、排除什么</title>
  <link>https://yunlab.ai/notes/from-logs-to-knowledge-retain-or-exclude-rules</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/from-logs-to-knowledge-retain-or-exclude-rules</guid>
  <description>整理 AI 项目知识库时最难的不是写新内容，是决定旧材料该不该留。两层判定、六条真相源优先级、三态新鲜度标记，我都是被坑过才学会的。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌上一排资料，被铅笔圈出的「真相源」标注" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">知识库整理笔记</p>
  <p class="journal-lead">
    整理一个 AI 项目知识库的时候，最难的不是没东西可留——是「什么都想留」。
  </p>
  <p>
    AI 项目里随便一段时间就能攒出一堆材料。聊天记录、运行日志、装机备份、agent 设定、临时报告、状态文件、scripts、evidence 截图……每一份单独看都觉得有点用，扔哪个都觉得可惜。
  </p>
  <p>
    可一旦真把它们全部塞进一个所谓的「知识库」，几周以后回头看，它跟之前那堆「散落的工作目录」没区别。表面整理过了，本质只是把垃圾搬了一遍。
  </p>
  <p>
    所以现在每次开始整理之前，我都会先做一件笨事：把「留什么、扔什么」写下来。一旦规则写出来，整理就是机械动作；一旦写不出来，整理就永远在犹豫。
  </p>
  <h2>核心矛盾：原料一直在加，时间一直在少</h2>
  <p>
    这个矛盾我以前一直不肯承认。心里想——先全部留着吧，反正硬盘够大，等以后再选。
  </p>
  <p>
    可「以后再选」从来不会真的发生。知识库一旦超过几百份文件，下次进来的人——包括我自己——就不想读了。体积会反过来吓退所有人，包括作者自己。
  </p>
  <p>
    所以「留全」看着安全，反而是最贵的选择。代价不在硬盘——硬盘买就是了——而在注意力。每多留一份意义不明的材料，下次打开就要多分一份注意力去判断。十次之后，谁都不想再开。
  </p>
  <h2>错误解法：留最新、留最长、留官方</h2>
  <p>
    早期我也偷过懒，用过几条「看起来挺合理」的筛法，全部翻了车。
  </p>
  <ul class="journal-list">
    <li>留最新——但最新经常只是被人最后碰过，不代表上面说的事还成立。</li>
    <li>留最长——但最长往往是 AI 帮我整理的总结，里面混了不该混的东西。</li>
    <li>留官方——但带 FINAL / SPEC / README 的文件，很多其实是早期版本，后来被实际运行结果推翻了，只是文件名没改。</li>
  </ul>
  <p>
    三条单独看都不蠢，三条放在一起就出事——剩下的全是「看着权威，其实早就过期的 AI 总结」。这种东西比聊天记录更危险，它装得像知识。
  </p>
  <p>
    后来我换了个法子，分两层判。
  </p>
  <h2>第一层判定：这份材料能不能继续支撑工作</h2>
  <p>
    第一层只问一个问题：未来某一天我重新进入这个项目，要不要看这份材料？说不要就扔。说要就再追一句——是材料本身有用，还是结论已经被别处的审计报告或设计文档收走了？被收走了，原材料也不用留。
  </p>
  <p>
    按这条线一刀切下去，材料就只剩两类。
  </p>
  <h3>能留下来的：能继续工作的知识</h3>
  <ul class="journal-list">
    <li>项目概览、架构、设计文档——知识，不是状态；告诉你系统长什么样、为什么这么搭。</li>
    <li>审计结论、决策记录——定下来的判断，比过程更值钱。</li>
    <li>运行方式、端口、命令面——下次想用，第一时间查这些。</li>
    <li>变更日志、时间线——让人理解「为什么演变成今天这样」。</li>
    <li>装机归档（每次一份）——重装系统时必然回头翻。</li>
    <li>装机备份里的 audit snapshot、archive 冷存储的 knowledge 子目录、agent 训练记录里的「工程分析」——原本散在乱目录里，只要能继续支撑工作，就提出来归位。</li>
  </ul>
  <h3>排除掉的：运行时和工程产物</h3>
  <ul class="journal-list">
    <li>源代码、脚本、补丁——归 repo 管，不是知识。</li>
    <li>运行日志、缓存、依赖——重新跑一次就能再生成。</li>
    <li>evidence、backups、raw 原始材料——过程证据，结论已经收了。</li>
    <li>对话原文（kimi sessions、claude memory、codex memories）——机器的工作记忆，不是人工知识页。</li>
    <li>含 token 或 secret 的运行时配置——运行时身份，不是知识。</li>
    <li>身份、角色、灵魂、心跳类的设定文本——prompt 工程产物，公开既不安全也没价值。</li>
    <li>装机备份里的 home 系统文件、archive 里的 books/raw/tasks、过往 AI 聊天记录——同样按属性归到这一类，不要为「这个目录还有点东西」整个保留。</li>
  </ul>
  <p>
    这一层最容易被忽略的，是把目录拆开看。一个目录里既有要留的、也有该扔的，很正常——按属性分两堆，不要因「整理麻烦」整个搬走，也不要因「里面有几份没用」整个删掉。
  </p>
  <h2>第二层判定：在多份能留的材料里，谁是真相源</h2>
  <p>
    第一层过滤完，麻烦才真的开始。同一个项目，可能本地有一份设计文档、archive 里也有一份；安装文档可能写到了 v3，但 v1 v2 还在；同一个状态，audit 报告里写了，运行日志里也写了。每一份都说自己对。
  </p>
  <p>
    这种时候不能汇总——汇总只会把冲突包装得更漂亮。得有人裁判。
  </p>
  <p>
    下面六条不是真理，是工作规则——不为挑出最好的，只为给材料固定优先级，让我下次不必从头裁判。
  </p>
  <ul class="journal-list">
    <li>原始文件优先于备份。备份是为了应急，不是为了被引用。</li>
    <li>最新稳定版本优先于旧版。注意是「稳定」，不是「最近改过的草稿」。</li>
    <li>设计文档优先于运行时痕迹。命令、状态、日志告诉你它现在在做什么；spec 告诉你它本来该做什么。</li>
    <li>历史账本优先于原始聊天全文。账本是压缩过的锚点，原始聊天是流水。</li>
    <li>本地当前事实优先于 archive 里的重复副本。archive 是历史，不是现在。</li>
    <li>涉及人设、身份、灵魂、心跳的文本，默认排除。这一类是 prompt 工程产物，不属于公共知识。</li>
  </ul>
  <h2>第三件事：给每一页打「新鲜度」标记</h2>
  <p>
    留下来的页面也会过期。不及时处理，知识库又会变成「什么都对、又什么都不一定对」的状态——和最开始那堆乱目录没两样。
  </p>
  <p>
    所以我现在每一页都带一个新鲜度状态。三态够用，再多就没人维护：
  </p>
  <ul class="journal-list">
    <li><strong>verified</strong>——最近被人或 AI 对照过源头，还成立。</li>
    <li><strong>stale</strong>——源头已经动了，这一页可能不准，但还能当线索。</li>
    <li><strong>needs review</strong>——一眼能看出冲突，必须有人重新看。</li>
  </ul>
  <p>
    这三态不复杂，关键是给了「什么时候必须更新」的明确信号。没信号，所有页面看起来都同样可信，问题会偷偷写进下一次判断。
  </p>
  <p>
    我自己有一条简单的线：通不过一次快速源头核对的页面，不该让人觉得权威。它可以当线索，但要降级——不能继续装成 verified。
  </p>
  <h2>真实结果：三个 wiki 的对照</h2>
  <p>
    跑完这套流程，我整理了三个规模不大、但能持续工作的 wiki：
  </p>
  <ul class="journal-list">
    <li><strong>llm-wiki</strong>——工程知识库，存页面原则、项目状态、生态治理、审计与决策。</li>
    <li><strong>openclaw-knowledge</strong>——OpenClaw 项目专门库，存安装设计、版本选择、安全加固、历史账本。</li>
    <li><strong>yun-archive-wiki</strong>——个人归档库，存音乐索引、装机审计、冷存储 knowledge 部分、重装后报告。</li>
  </ul>
  <p>
    三个库各自有「留 / 扔」表，但底层判定同一套。这比内容完不完整更重要——以后任何一个 wiki 出问题，我用同一种方法整理，不必重新发明判断。
  </p>
  <p>
    顺带一提，它们都很小。llm-wiki 19 个 markdown、76KB；openclaw-knowledge 7 个、28KB；yun-archive 11 个、44KB。加起来不到 150KB。
  </p>
  <p>
    这是我后来才慢慢接受的事——真正能用的知识库往往很小；大的那个通常不是知识库，是工作目录的备份。
  </p>
  <h2>给自己定的几条边界</h2>
  <p>
    这套流程能持续跑下来，靠的不是某条规则特别聪明，而是几条很笨的边界一直没破。
  </p>
  <ul class="journal-list">
    <li>不让「以后再选」成为理由——决定不下来的，要么当时排除，要么标 needs review 进下一轮。</li>
    <li>不让 AI 替我定真相源——AI 可以列候选、找重复、提冲突，但选哪一份当权威，是我的事，还要写进「留 / 扔」表里。</li>
    <li>不让「看着权威」等同于「权威」——FINAL / SPEC / README 这种名字，照样要走两层判定。</li>
    <li>不让原始材料和整理结果混在同一个目录——原料留在工作目录，整理结果进 wiki。一旦混了，几周以后再也分不清谁是谁。</li>
    <li>不让「删除」和「排除」混淆——排除只是知识库不收纳，原文可以接着存在。这一条让我整理时轻松很多。</li>
  </ul>
  <div class="journal-ending">
    <p>
      整理知识库的本质，是给材料划边界。
    </p>
    <p>
      留什么、扔什么，不是审美问题，是「它有没有资格在未来代替源头回答问题」。能代替的就留；不能代替的当线索。线索可以散在工作目录里，知识必须能在 wiki 里独立成立。
    </p>
    <p>
      我想要的不是更大的库，是下次进来还愿意打开的库。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识系统</category>
  <category>整理层</category>
  <category>OpenClaw</category>
  <category>真相源</category>
  <category>public-safe</category>
</item>
<item>
  <title>OpenClaw Studio RC1：本地闭环、外部被关卡挡住，以及我还在改的事</title>
  <link>https://yunlab.ai/notes/openclaw-studio-rc1-local-loop-external-gated</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/openclaw-studio-rc1-local-loop-external-gated</guid>
  <description>一天之内把 OpenClaw Studio 从第一阶段推到第七阶段、交付 RC1（首个候选发布版）的复盘——三条方法、七个阶段、当前真能跑什么、大半摊子还没收完。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：工作台俯视，左边凌乱便签，右边整理好的笔记本和资料柜" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw Studio 阶段复盘</p>
  <p class="journal-lead">
    一天之内把 OpenClaw Studio 从第一阶段推到第七阶段，交付 RC1（Release Candidate 1，首个候选发布版本）。听上去像在跑冲刺，其实更像在拆雷。
  </p>
  <p>
    OpenClaw Studio 是我正在做的本地 AI 工作系统。能在本地完成晨检、任务流转、内容产出这三件事。外部那些大组件——DeerFlow（一套深度研究流程）、Mission Control Web（任务中控的网页端）、Gateway（外部网关）、LLM（大模型）接入、Search（搜索）——全部按 gate（关卡）管理，没解锁的就先不接。
  </p>
  <p>
    RC1 不只是版本号。对我来说它是一道边界——边界之内能独立运转，边界之外老老实实写「未通过」。这次复盘要说的是：这道边界怎么一步一步划清楚的，以及现在还有哪些漏。
  </p>
  <h2>为什么要硬塞到一天里跑完</h2>
  <p>
    一开始我没打算把七个阶段全压在一天。越做越发现：阶段之间的依赖只在一天内是新鲜的。隔一夜，前一阶段的状态记忆变模糊，下一阶段被迫重新验证，本来一步能确认的事变成三步。
  </p>
  <p>
    索性集中火力——把所有阶段排成链，前一个通过（PASS）才能开始下一个，中间任何关卡没过就直接停（STOP）。听起来像走钢丝，但实际跑下来比拖几天更稳。注意力没散，决策路径一直热着，遇到问题也能立刻回头改前一步。
  </p>
  <p>
    单日推进的代价是密度——七个阶段、十几次审稿（review）、二十多次空跑（dry-run）。中途累了就容易放水，放水就出事。我给自己定了三条不能破的方法，这三条才是 RC1 真正的脊柱。
  </p>
  <h2>方法一：空跑（dry-run）在前，实跑在后</h2>
  <p>
    每一阶段开始之前，我都会先让系统空跑一遍——不写文件、不发消息、不动外部状态。只让它把流程从头到尾走一遍，告诉我「如果真做，会做什么」。
  </p>
  <p>
    空跑刚出现的时候，我把它当成「确认环节」。后来发现远不止——它其实是「让 AI 暴露自己的理解」。一次空跑下来，AI 会列出它打算访问的路径、执行的命令、修改的文件、调用的外部接口。十次有八次，我都在这一步发现误解：把状态文件当成草稿，要去访问还没解锁的外部，要把「通过」结论写到一个还没被确认的位置。
  </p>
  <p>
    这些误解空跑里抓不住，实跑时就是事故。规矩是——任何能改外部状态的事，空跑必须先过；过完才允许实跑。
  </p>
  <h2>方法二：关卡（gate）隔离，过不去就停</h2>
  <p>
    OpenClaw Studio 真正难的地方不是写代码，是控制边界。本地能跑的事情很多，但外部依赖一旦没解锁就不能动——不能假装能动，不能跳过去等以后再补，更不能因为「应该没问题」就先放出去。
  </p>
  <p>
    我给每个外部依赖都装了一道关卡。关卡状态只有三种：未通过、已通过、待复核。未通过的依赖，整个系统就当它不存在；任何阶段任务里只要碰到这个依赖，立刻停下来等人确认。
  </p>
  <p>
    Gate 矩阵（所有关卡的状态总表）是 RC1 文档里最重要的一份文档——比任何架构图都重要。它不是装饰，是运行时的真实约束。这一次跑七个阶段，DeerFlow、Mission Control Web、Gateway、LLM、Search 全部未通过，整个外部链路是黑的；但本地链路因为有关卡隔离，反而能完整跑通。
  </p>
  <h2>方法三：零越界写入</h2>
  <p>
    Agent（智能体）越积极，越容易出事。让它读一下，它顺手改了；让它分析一下，它开始重构；让它检查一下，它把检查结果写到了不该写的地方。
  </p>
  <p>
    我给每个 agent 都签了一份「契约」——明确写清它能读哪里、能写哪里、绝对不能动哪里。RC1 一共有 7 份这样的契约：每个 agent 一份，三栏（Read 读 / Write 写 / Forbidden 禁），完全没有灰区。
  </p>
  <p>
    契约本身不复杂，关键是签完以后真拿它做拦截。任何写入只要不在「写」列表里，工具层直接拒绝，不给 agent 留余地。这一招让 RC1 的安全感比之前几版高了一截——不再怕哪个 agent 心血来潮把资料目录改了。
  </p>
  <h2>七个阶段，分别在收什么口</h2>
  <p>
    七个阶段不是按功能分的，是按风险分的。每个阶段只解决一类风险，全部通过才进下一阶段。任何时候出问题，都能精确定位到上一道闸门，不必从头排查。
  </p>
  <ul class="journal-list">
    <li>第一阶段：把环境冻结——确认本地工作目录、状态文件、工具版本，全部空跑过一遍。</li>
    <li>第二阶段：把 agent 契约写清楚——读哪里、写哪里、什么时候必须停。</li>
    <li>第三阶段：外部关卡全部摆好——没有外部依赖能在未确认状态下被触发。</li>
    <li>第四阶段：把内容工厂跑通——五类内容模板、注册表（registry，相当于内容的总账）、审稿管线，本地全闭环。</li>
    <li>第五阶段：把 Mission Control（任务中控）的 markdown 版本接上——能自动更新，能自动备份。</li>
    <li>第六阶段：主集成（master 集成）——前五阶段的产物合起来跑一次完整任务，看耦合点。</li>
    <li>第七阶段：交付 RC1——固化文档，标记首个候选发布版。</li>
  </ul>
  <p>
    每个阶段的审计链路（包括所有重跑子版本、小任务节点、收口节点）都留在本地审计目录里，不必在文章里贴。重要的是分阶段思路——按风险切，不按功能切。
  </p>
  <h2>RC1 真正交付了什么</h2>
  <p>
    一天结束，RC1 上能用的东西不算多——但每一项都被验过：
  </p>
  <ul class="journal-list">
    <li>本地工作系统能独立运转——能晨检、能流转任务、能产出内容，不依赖任何外部接口。</li>
    <li>内容工厂跑通了——注册表 + 五类型模板 + 审稿管线，一篇内容能从草稿走到审稿通过，全程留下证据。</li>
    <li>任务中控的 markdown 版接上——任务状态自动更新、自动备份，不用人手动同步。</li>
    <li>7 份 Agent 契约就位——每个 agent 的读写边界都明确，无契约不工作。</li>
    <li>关卡矩阵已文档化——任一外部依赖未通过都被自动拦下，没有偷偷放过去的可能。</li>
    <li>角色调整也落地了——主控和写入责任人从「嬴政」迁到「赵子龙」，其余角色降级为主理人之一。这件事写进契约之后，所有写入都收口到赵子龙一个 agent，问题追溯快了很多。</li>
  </ul>
  <h2>RC1 没有交付什么</h2>
  <p>
    我想把这一节写清楚——RC1 的本质是「本地能跑的部分」，不是「整个系统已经成熟」。下面这些事还没解决：
  </p>
  <ul class="journal-list">
    <li>外部系统全部被关卡挡住——DeerFlow、Mission Control Web、Gateway、LLM 接入、Search 都还在边界之外。任何需要外部能力的任务，RC1 都接不住。</li>
    <li>4 个锚点（anchor，长期状态同步用的几份关键文件）的同步机制还不可见——本地状态变化时，这几个锚点是否真的被同步、什么时候被同步，没有自动校验。漂移风险一直挂着。</li>
    <li>26 个待清理的密钥（secret）仍然阻塞着另一台机器的重装。本地工作系统 RC1 解决了，整台机器的卫生问题没解决。</li>
    <li>多套控制平面共存——本地 RC1、Mission Control Web 和未来的外部网关在职责上有重叠，谁压谁还没定。</li>
    <li>历史条目草稿（HISTORY_ENTRY_DRAFT，把当次工程产出归并进长期历史账本的草稿文件）还没闭合——这次七阶段的全过程虽然有审计，但还没归并进长期历史账本，过几个月会需要再回头收一次。</li>
  </ul>
  <p>
    「没交付什么」比「交付了什么」更重要——防止我几个月后回头看 RC1，把它当成「已经完成」的状态。
  </p>
  <h2>关于 RC1 的真实状态：我还在做</h2>
  <p>
    RC1 在我这里不是终点，是一个能继续走的起点。
  </p>
  <p>
    本地闭环能跑，我有一个不依赖外部就能维持的工作底座——但边界还很窄。外部关卡解锁是长路，不会一两周完成；锚点同步、密钥清理、控制平面收口，每一件都得单独排队。
  </p>
  <p>
    我现在的节奏是：每周拣一个关卡试着推进一步——能解锁就解锁，不能解锁就把「为什么不能」写进审计。每解决一个锚点漂移就标「已验证」；每清理一批密钥就把对应位置标「已过期」，等下一轮复核。不追求 RC2、RC3 的版本号，但要让 RC1 这个版本的边界一直清楚——能跑什么、不能跑什么、为什么不能跑——三个问题永远有答案。
  </p>
  <p>
    这篇是阶段复盘，不是发版庆功。RC1 在线下持续改，本文写完的同时，新一轮关卡测试已经在排队。下一篇关于 OpenClaw 的复盘，多半会从「某个关卡终于解锁」或者「某次以为能解锁结果又退回去」开始讲。
  </p>
  <div class="journal-ending">
    <p>
      RC1 是我目前对 OpenClaw Studio 最满意的版本——满意不是因为它做完了，是因为它第一次清楚地告诉我，自己能做什么、不能做什么、下一步该往哪里推。
    </p>
    <p>
      做到这一步之前，每一次「能跑了」其实都是错觉。真正的「能跑」是带着关卡的——本地能跑，外部按管，写入有契约，状态有审计。这套东西继续磨下去，RC1 总有一天会自己废掉，被一个不带 RC 后缀的版本替换。
    </p>
    <p>
      在那之前，我还在改。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Studio</category>
  <category>工程复盘</category>
  <category>空跑优先</category>
  <category>public-safe</category>
</item>
<item>
  <title>从信息到文章：沈知行怎么把素材交给苏晚</title>
  <link>https://yunlab.ai/notes/from-information-to-article-shen-to-suwan</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/from-information-to-article-shen-to-suwan</guid>
  <description>信息 Agent 抓到一堆链接以后，真正难的是把它们变成“可写的候选”。沈知行→苏晚的内容交接链路。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/articles/from-info-to-article.jpg" alt="水彩手绘：左边零散信息卡片、中间一支钢笔、右边一篇成型文章" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">OpenClaw 工作流笔记</p>
  <p class="journal-lead">
    沈知行抓到信息以后，事情其实还没有开始。
  </p>
  <p>
    我一开始很容易把信息 Agent 的完成点放在“源够不够多”“能不能抓到”“状态是不是 fetched_ok”。这些当然是底线，但只证明系统能接触到信息，不等于这些信息已经能变成内容。
  </p>
  <p>
    真正的分界线，是苏晚能不能接。
  </p>
  <p>
    如果沈知行每天抓回来一堆标题、链接、摘要，然后苏晚还要重新判断哪些值得看、哪些只是噪音、哪些适合写文章、哪些需要丢掉，那这条链路还没有成立。它只是把搜索工作从一个地方搬到了另一个地方。
  </p>
  <p>
    后来我把问题改成：沈知行交给苏晚的，不能只是信息，而应该是内容候选。
  </p>
  <h2>抓到不等于可写</h2>
  <p>
    这是我最早踩到的一个误区。一个 source 跑通，一条 item 抓到，一段摘要生成——这些都很容易给人完成感。
  </p>
  <p>
    但内容工作不是从“有材料”开始，而是从“为什么这条材料值得处理”开始。
  </p>
  <p>
    一条新闻可能是真的，却没有写作价值；一个链接可能很新，却和我的长期主题没关系；一段讨论可能很热，却只是情绪噪音。反过来，一条很小的产品变化、一个冷门论坛讨论、一次普通的版本调整，可能刚好暴露了一个值得写的结构性问题。
  </p>
  <p>
    如果沈知行只负责把信息带回来，苏晚就会被迫从零开始筛。看起来是多 Agent 分工，实际还是一个人把所有判断重新做一遍。
  </p>
  <h2>我现在要求它多交四样东西</h2>
  <p>
    我给这条交接链路加了更清楚的要求：沈知行交给苏晚的每个候选，至少多带四样东西。
  </p>
  <ul class="journal-list">
    <li>第一，来源和状态。它来自哪里，是否真的取到，是否只是候选，是否已经过期或需要复查。</li>
    <li>第二，为什么值得看。不能只说“这是一条新闻”，而要说明它触发了什么判断。</li>
    <li>第三，建议角度。它适合写成工具体验、行业观察、OpenClaw 复盘，还是只适合作为背景材料。</li>
    <li>第四，风险和缺口。有没有事实不稳、来源单薄、容易误读、涉及隐私或暂时不适合公开的部分。</li>
  </ul>
  <p>
    这四样东西一加，沈知行的角色就变了。它不再只是“给我十条链接”，而是在帮苏晚省掉第一轮判断。
  </p>
  <p>
    它不替苏晚决定写什么，但它要把材料交到一个能继续判断的位置。
  </p>
  <h2>苏晚接的不是素材，是取舍空间</h2>
  <p>
    苏晚这个内容 Agent，最重要的能力不是把材料写漂亮，而是知道什么值得写、怎么写、为什么现在写。
  </p>
  <p>
    所以她不能只收到一堆“素材”。素材太宽了，里面有事实、有噪音、有旧材料、有半成品、有可能公开的内容，也有只适合内部看的线索。她真正需要的是一个取舍空间。
  </p>
  <p>
    一个好的候选应该让她快速看到：这件事和哪条长期线有关，它能不能解释一个真实问题，它现在有没有足够证据，它适合写给谁，公开写会不会暴露后台细节。
  </p>
  <p>
    这样苏晚才有可能做内容判断，而不是先变成二次清洗工。
  </p>
  <h2>中间必须有一个候选库</h2>
  <p>
    我越来越不信“抓完直接写”。
  </p>
  <p>
    抓完直接写，系统会过度依赖当天的感觉。今天觉得很重要的，明天可能只是噪音；今天觉得证据够了，后面可能发现少了另一个来源；今天写出来像文章，过两天看可能只是内部说明书。
  </p>
  <p>
    所以中间需要一个候选库。它不是大而全的资料库，而是一个状态清楚的缓冲区：哪些候选进入 owner review，哪些交给苏晚，哪些需要更多来源，哪些进 wiki，哪些直接归档。
  </p>
  <p>
    候选库的价值不在保存更多东西，而在让每条信息都有下一步。
  </p>
  <h2>一条内容链路应该这样走</h2>
  <p>
    我现在更愿意把沈知行到苏晚的链路拆成几步。
  </p>
  <ul class="journal-list">
    <li>先验证来源：源能不能取到，内容是不是当前有效，是否符合主题边界。</li>
    <li>再清洗 item：去掉重复、噪音、明显低价值和不可公开的材料。</li>
    <li>然后生成候选：给出标题、来源、理由、角度、风险、建议去向。</li>
    <li>再进入苏晚判断：看它值不值得写，适合写成什么，是否需要补证据。</li>
    <li>最后才进入写作：文章不是材料拼接，而是从一个明确判断出发。</li>
  </ul>
  <p>
    这条链路看起来比“抓取加总结”慢，但更适合长期用——它把每一步的责任分清了。
  </p>
  <p>
    沈知行负责信息侧的可用性和初筛，苏晚负责内容侧的判断和表达，我负责关键放行和边界。这才会像一个真正能协作的工作流。
  </p>
  <h2>我不想要自动写作流水线</h2>
  <p>
    这里有一个很容易走偏的方向：既然沈知行能抓，苏晚能写，那是不是应该自动每天生成文章？
  </p>
  <p>
    我现在不想这么做。
  </p>
  <p>
    自动生成文章会很快，但也最容易把“有信息”误当成“有判断”。YunLab.ai 不需要每天刷存在感，更需要每篇文章都能回答：这次我到底看明白了什么。
  </p>
  <p>
    苏晚不是自动发布器，沈知行也不是热点投喂器。它们之间要形成的是内容判断系统，不是内容生产流水线。
  </p>
  <h2>最后的判断</h2>
  <p>
    这次我真正想修的，不是一个信息抓取模块，也不是一个写作模块，而是它们之间的交接。
  </p>
  <p>
    多 Agent 的难点经常不在单个 Agent 有多聪明，而在两个 Agent 之间有没有一张能接住工作的桌子。沈知行把信息放上来，苏晚能看懂它为什么被放上来，也能决定它应该被写、被补、被丢掉，还是先留给我审。
  </p>
  <p>
    这张桌子，就是内容候选库，也是整条工作流的关键。
  </p>
  <div class="journal-ending">
    <p>
      从信息到文章，中间缺的不是更多摘要，而是更清楚的交接。
    </p>
    <p>
      沈知行要交出可判断的候选，苏晚要做有取舍的内容判断，最后再由人来守住公开边界。做到这一点，信息 Agent 才不会变成抓取器，内容 Agent 也不会退化成改写器。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>沈知行</category>
  <category>苏晚</category>
  <category>Agent Workflow</category>
</item>
<item>
  <title>把 AI 施工现场压缩成长期知识：我的 Knowledge Model</title>
  <link>https://yunlab.ai/notes/ai-construction-site-knowledge-model</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/ai-construction-site-knowledge-model</guid>
  <description>AI 项目最危险的不是忘记，是记错以后继续施工。我把「施工现场」压成下次能用的判断的方法。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/knowledge-model/cover.jpg" alt="水彩手绘：工地脚手架旁散落笔记本，旁边一张折叠的「知识地图」" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">Knowledge Model 笔记</p>
  <p class="journal-lead">
    AI 项目最危险的不是忘记，是记错以后还继续施工。
  </p>
  <p>
    以前我把长期知识想得太像「存档」。聊过的内容保存下来，终端输出保存下来，报告保存下来，截图保存下来，以为这样下一次 AI 就能接上。
  </p>
  <p>
    其实不是。AI 项目的现场不是图书馆，更像工地。今天 Claude 在一个窗口里讨论方向，Codex 在本地改文件，Kimi 在消化长文档，浏览器里还开着部署页面、预览页面、ChatGPT 对话。每个地方都在产生材料：一句判断、一段日志、一份报告、一个截图、一个临时通过的状态。
  </p>
  <p>
    当天我当然知道哪个是草稿，哪个是中间态，哪个 PASS 带着 warning。问题是几天以后现场散了，只剩材料。下一个 AI 进来时，它看到的不是「现场」，而是一堆看起来都挺像证据的碎片。
  </p>
  <p>
    这时候如果没有压缩，系统不会变聪明，只会变脏。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/knowledge-model/construction-site-to-knowledge.svg" alt="把 AI 施工现场里的聊天、输出和报告压缩成长期知识">
  </figure>
  <h2>真正的冲突：材料越来越多，判断反而越来越不稳</h2>
  <p>
    我最早以为问题是「AI 忘了」。后来发现更常见的是「AI 记错了」。
  </p>
  <p>
    比如一条线先被评成 95 分，后来独立审计打回 77 分，再后来补完缺口回到 96。三个数字都是真的，但它们不能直接混在一起用。如果下一次 AI 只看到 95，它可能会继续推进上线；如果只看到 77，它可能会重复修已经关闭的问题；如果只看到 96，它又可能忘了这个 96 仍然是 local controlled candidate，不是 production 放行。
  </p>
  <p>
    问题不在记忆多少，而在判断结构。材料如果没被压成「当前可用结论」，下一次协作里就会变成污染源。
  </p>
  <p>
    影响很实在。每次换窗口、换 AI、换一天，我都要重新解释：这份报告已经过期，那份只是 owner report，不是 independent audit；这个 PASS 是 local-only，不是上线许可；那个结论只是为了推进讨论，后来已经被推翻。
  </p>
  <p>
    浪费时间还是小事。更麻烦的是旧判断复活，中间状态被误读成完成态，AI 在错误前提上继续写计划。它写得越顺，我越容易被带偏。
  </p>
  <h2>我试过的错误解法：把一切都存下来</h2>
  <p>
    最自然的反应是加记忆：怕丢，就全量保存。聊天也存，日志也存，报告也存，最好每句话以后都能搜到。
  </p>
  <p>
    但这条路很快会反过来咬你。全量保存只解决「有没有材料」，不解决「这条材料现在还能不能用」。向量库能召回相似内容，但不会自动知道哪句话是试探，哪份报告已经被审计推翻，哪个结论只适用于本地预览。
  </p>
  <p>
    我后来不再追求「让 AI 记住更多」。我更关心另一件事：让它少误用。
  </p>
  <p>
    所以我开始做 Knowledge Model。它不是什么新名词，也不是另一个知识库工具。它是一套现场收工规则：每天产生的材料，哪些只做证据，哪些进入当前状态，哪些变成长期规则，哪些应该直接丢掉。
  </p>
  <h2>我的方法：四个问题决定一条材料能不能升格</h2>
  <p>
    一段内容就算「看起来有用」，也得先过四个问题，才进长期记忆：
  </p>
  <ul class="journal-list">
    <li>第一，问题是什么。别泛泛说「上下文不足」，要说清楚哪个判断错了、哪个边界混了、哪个状态被误读了。</li>
    <li>第二，它造成了什么影响。是浪费了一次接手时间，还是让旧结论复活，还是差点把 local-only 当成上线许可。</li>
    <li>第三，我后来怎么看待这个问题。判断方式发生了什么变化，为什么不能再按原来的方式处理。</li>
    <li>第四，下次要怎么做。它最后应该变成一条规则、一个 checklist、一个 skill、一个 task contract，还是只保留原始证据。</li>
  </ul>
  <p>
    四个问题答不上来，就别急着叫知识。它最多是一段记录。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/knowledge-model/model-layers.svg" alt="Knowledge Model 从原始材料、工作片段、判断，压缩到规则和长期资产">
  </figure>
  <h2>我把知识分成四层，不让它们互相冒充</h2>
  <p>
    材料现在被我分到四个位置。
  </p>
  <ul class="journal-list">
    <li>原始证据层：聊天、日志、截图、测试输出、diff。只证明事情发生过，不直接指导下一步。</li>
    <li>当前状态层：任务 README、notes、handoff、state 文件。回答「现在到底到哪一步」。</li>
    <li>长期规则层：AGENTS.md、skills、checklist、memory 条目。用来改变以后 AI 的行为。</li>
    <li>公开表达层：文章、复盘、方法论。把内部经验改写成别人也能理解的经验。</li>
  </ul>
  <p>
    这四层不能混。证据不能冒充状态，状态不能冒充规则，规则也不能直接暴露成公开文章。很多混乱，都来自这些层混在一起：一段聊天被当成规则，一份报告被当成 verdict，一个 memory 摘要被当成当前状态。
  </p>
  <p>
    它的作用很简单：不是帮我记住所有东西，而是逼我把东西放到正确的位置。
  </p>
  <h2>结果：不是记忆变多，而是交接变轻</h2>
  <p>
    结果不是 AI 突然什么都懂，而是接手成本下降。
  </p>
  <p>
    新窗口不用先翻完整聊天史。它应该先看当前状态，再看最近 notes，再看 handoff，再根据需要追原始证据。它不应该从一个漂亮报告开始推断当前结论，更不应该从 memory 摘要直接决定下一步。
  </p>
  <p>
    长期知识不是「我以前说过什么」的仓库，而是「我以后不想再重复什么」的工具。一次施工现场如果没收工，就只是一堆材料；收工之后，才可能变成下次开工时能用的资产。
  </p>
  <div class="journal-ending">
    <p>
      我的 Knowledge Model，不是让 AI 记住更多，而是让 AI 少误用旧材料。
    </p>
    <p>
      每次任务结束，我要留下的不是完整聊天，而是四样东西：问题、影响、判断变化、下一次动作。这四件事答得上来，经验才值得进长期知识；答不上来，就留在证据层，别污染未来的判断。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>Knowledge Model</category>
  <category>Memory</category>
  <category>个人 AI 系统</category>
  <category>OpenClaw</category>
</item>
<item>
  <title>Canonical 选择规则：AI 项目里什么才算“真相源”</title>
  <link>https://yunlab.ai/notes/canonical-source-rules-ai-project</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/canonical-source-rules-ai-project</guid>
  <description>AI 项目里所有材料都“看起来对”，该信哪个？六条规则：先问问题类型，再选真相源。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/canonical-source/cover.jpg" alt="水彩手绘：桌上一排资料其中一份被铅笔圈出，旁边手写箭头标注「真相源」" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">Canonical 选择笔记</p>
  <p class="journal-lead">
    AI 项目里最难处理的，往往不是"没有真相"，而是太多东西都像真相。
  </p>
  <p>
    聊天里有结论，报告里有结论，任务文件夹和 memory 里也有结论。本地刚 build 出一份页面，线上也有个能访问的版本。旧审计说还不够，新报告说已经补完。每份材料单独看，都能自圆其说。
  </p>
  <p>
    危险就在这里。一份材料明显是错的，反而好办。真正麻烦的是，它在某个时间、某个边界内是对的，却被拿来回答另一个问题。
  </p>
  <p>
    后来我给自己立了一条规则：先决定谁有资格回答，再看它说了什么。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/canonical-source/source-ladder.svg" alt="聊天、报告、状态文件和真实输出在 Canonical 判断中的不同位置">
  </figure>
  <h2>冲突点：AI 会把"阶段性正确"讲成"当前正确"</h2>
  <p>
    最典型的例子，是那些漂亮状态词：95 分、launch ready、local candidate、PASS_WITH_WARNINGS。单独看，它们不一定错，甚至写得很谨慎。
  </p>
  <p>
    但下一次 AI 单独捞到这些词，意思就变了。它可能记住 launch ready，忘了 local-only；记住 95 分，忘了 independent audit 曾经打回；记住页面能打开，忘了那只是本地 preview；记住"已生成"，忘了还没部署到线上。
  </p>
  <p>
    这种错误不会立刻炸掉，更像慢性污染：AI 从错误的 root 继续写，拿旧状态当当前状态，把 local-only 说成可以上线，把抽样通过说成全量完成。它还会写得很完整，甚至给出看起来稳妥的下一步。
  </p>
  <p>
    我不怕 AI 不知道。我怕它在不知道的时候，拿一份不该负责的材料，讲出一个很顺的答案。
  </p>
  <h2>错误解法：找最新、找最长、找最会总结的</h2>
  <p>
    我以前也会偷懒。哪个文件最新，就先看哪个；哪个报告最长，就觉得它更完整；哪个 AI 最后说了一句"完成"，就顺手当成当前状态。
  </p>
  <p>
    但 AI 项目里，"最新"经常只是最新写出来，不代表最新负责。"最长"经常只是解释最多，不代表证据最硬。"总结最顺"更危险，因为它会把冲突包装成一个好听的故事。
  </p>
  <p>
    后来我对 Canonical 的理解变了。Canonical 不是"唯一真相文件"，也不是"所有东西只准看一个地方"。它更像法庭里的管辖权：不同问题，要找不同的负责来源。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/canonical-source/truth-source-map.svg" alt="不同问题要回到不同 Canonical 来源，而不是用一个聊天总结回答所有问题">
  </figure>
  <h2>我的选择规则：先问问题类型，再选真相源</h2>
  <p>
    我现在会先把问题拆清楚。不是一上来问"项目现在怎么样"，而是问：我到底在判断什么？
  </p>
  <ul class="journal-list">
    <li>问内容，就看内容文件。文章写成什么样，以 repo 里的 Markdown 为准，不以聊天里的摘要为准。</li>
    <li>问当前进度，就看任务文件。README、notes、handoff、state 文件比一段聊天总结更有资格回答。</li>
    <li>问是否能运行，就看真实输出。构建、测试、脚本结果、页面返回码，比"应该可以"更硬。</li>
    <li>问是否已发布，就看线上路由。只要问题是外面的人能不能看到，本地 preview 不算答案。</li>
    <li>问为什么这么决定，就看决策记录。没有 owner 放行、审计结论或明确 superseded 标记，就不能随便把旧判断升级成当前判断。</li>
    <li>问历史线索，才看聊天和 memory。它们负责帮我定位，不负责替我判案。</li>
  </ul>
  <p>
    顺序一旦定下来，很多争论会消失。不是因为材料少了，而是每份材料都有了边界。
  </p>
  <h2>冲突时的方法：不要总结，先裁判</h2>
  <p>
    最容易出错的时刻，是材料互相打架：聊天说 A，文件说 B；旧报告说过线，新 audit 说不够；本地页面是新版，线上页面还是旧版。
  </p>
  <p>
    这种时候我不会让 AI 立刻总结。总结太早，只会把冲突包装得更好看。我会先让它做四步裁判：
  </p>
  <ul class="journal-list">
    <li>第一步，明确问题。现在问的是内容、状态、运行、部署、验收，还是历史原因？</li>
    <li>第二步，列出候选来源。哪些材料声称能回答这个问题？它们各自是什么时间、什么边界下产生的？</li>
    <li>第三步，选出负责来源。谁对当前问题有直接责任，谁只是线索或旧证据？</li>
    <li>第四步，标记被取代的材料。旧结论如果被新 audit、owner 决策或真实运行结果推翻，就要写明 superseded，不能留给下一次 AI 猜。</li>
  </ul>
  <p>
    这套方法看起来慢，但比在错误真相源上继续推进便宜得多。路径一旦选错，后面每一步都在返工。
  </p>
  <h2>最后的结果：项目开始变安静</h2>
  <p>
    Canonical 规则带来的结果，不是所有材料被统一成一个文件，而是项目开始安静下来。
  </p>
  <p>
    以前我经常要问："你说的是哪个版本？"现在我会先问："这个问题的真相源在哪？"找到了，就从那里继续；找不到，就先补一份当前状态文件，而不是在聊天里继续猜。
  </p>
  <p>
    这也改变了我看 AI 输出的方式。AI 写得顺，不代表说得准；报告格式完整，不代表是 verdict；一句"已完成"，如果没有对应的文件、输出、验证和边界，就只是一个待检查的说法。
  </p>
  <div class="journal-ending">
    <p>
      Canonical 选择规则，本质上是给材料划责任。
    </p>
    <p>
      聊天负责推进，memory 负责索引，报告负责提出主张，任务文件负责过程，真实输出负责验收，owner 决策负责放行。每个来源都有位置，项目才不会被一堆看起来都对的材料拖乱。我想要的不是一个更会总结的 AI，而是一个先知道该回到哪里确认事实的 AI。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>Canonical</category>
  <category>真相源</category>
  <category>OpenClaw</category>
  <category>AI 项目管理</category>
</item>
<item>
  <title>信息 Agent 不是抓取器：我怎么重新审沈知行</title>
  <link>https://yunlab.ai/notes/shen-zhixing-information-agent-not-fetcher</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/shen-zhixing-information-agent-not-fetcher</guid>
  <description>我做信息 Agent 时被 GPT 三次“成熟判断”差点带偏的全过程，以及后来用来压住 AI 完成感的六条硬规则。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/shen-info-agent/cover.jpg" alt="水彩手绘：一个漏斗，上面倒入大量信息碎片，下面只滴出几滴墨水" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">沈知行建设复盘</p>
  <p class="journal-lead">
    我后来意识到，信息 Agent 最危险的地方不是抓不到信息，而是抓到一点东西以后，很容易让我们误以为"已经能工作了"。
  </p>
  <p>
    沈知行这条线，最开始是个很直观的任务：做一个信息抓取和整理 Agent，从公开信息源里发现值得看的东西，再把清洗后的候选交给苏晚。
  </p>
  <p>
    但真正做起来，问题不在"能不能多抓几个源"，而在：我到底要一个抓取器，还是一个能长期维护信息流的工作角色。
  </p>
  <p>
    如果只是抓取器，能访问几个 RSS、API、公开页面，返回几条标题和链接，就算完成了。但它是沈知行，不能只停在这。它得知道哪些源真的可用，哪些只是样子货；哪些信息值得看，哪些只是噪音；哪些候选该交给苏晚，哪些该进 wiki，哪些必须丢掉，哪些得留给我审。
  </p>
  <p>
    这两件事差别很大。前者是"拿到数据"，后者是"维护判断"。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/shen-info-agent/agent-not-fetcher.svg" alt="信息 Agent 不只是抓取器，而是从抓取到清洗、整理、交接和复盘的链路">
  </figure>
  <h2>第一次被带偏：抽样通过，被说成可以进入下一阶段</h2>
  <p>
    最早有个判断很诱人：系统已经有了几百个 source，抽样跑了 40 个，40/40 都 fetched_ok。GPT 当时的建议是，这可以进入 Day 1 的 owner review。
  </p>
  <p>
    这话听着很顺：有数字，有状态名，有"边界说明"，还把 local_db pending 解释成"不阻塞信息 Day 1"。当时如果只盯着表格，我大概就点头了。
  </p>
  <p>
    但我反问了一句：谁说 40 个就可以？
  </p>
  <p>
    系统声称 daily source 有 142 个，40 个抽样跑通，只能说明抽样跑通，说明不了 142 个 daily source 都能跑。这里最大的风险不是技术失败，而是验收口径被悄悄换了。
  </p>
  <p>
    这个问题我后来记得很清楚：AI 很容易把"一个局部证据"包装成"整体已经 ready"。它不是要骗人，但它太擅长把阶段性结果写得像完成态。
  </p>
  <h2>第二次被带偏：106 个能用，被说成已经过线</h2>
  <p>
    后来我要求全量验证。旧的 142 个 daily source 逐条评估，筛出 106 个 validated daily。系统又给出了一个很像完成的状态。
  </p>
  <p>
    这次我也不接受。
  </p>
  <p>
    我要的不是"从旧列表里筛出一批还能跑的源就结束"。我要沈知行作为全球信息搜集者，至少再补上 100 多个有用、能用、验证过的信息源。106 个只是旧集合清洗后的结果，不是新的能力边界。
  </p>
  <p>
    所以后来目标被改成了更硬的口径：不是 106，而是 206+；不是 candidate，而是 validated daily；不是 future、stub、dry-run，而是真正能 live/public API/public feed 验证。
  </p>
  <p>
    这一步之后，沈知行扩到了 257 个 validated daily source。到这个结果，我才开始认。不是因为数字变大了，而是每个源都得有状态、验证记录、边界和失败处理。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/shen-info-agent/wrong-pass-ladder.svg" alt="几个容易被误判成完成的阶段：抽样通过、数量过线、抓取成功、真正可交接">
  </figure>
  <h2>第三次被带偏：257 个源能抓，仍然不等于 Day 1</h2>
  <p>
    257/257 fetched_ok 以后，我又差点被"可以 Day 1"这个说法带走。
  </p>
  <p>
    这次问题更隐蔽。数据抓取层确实过了：源够多，全量 baseline 也过了，风险扫描也没发现泄漏。看起来已经很完整。
  </p>
  <p>
    但我忽然觉得不对：数据抓取只是 Day 1 的一部分。数据整理呢？苏晚怎么接？wiki 候选怎么维护？source 质量怎么观察？本地库真实路径还没接，owner 怎么审？
  </p>
  <p>
    这些没定的话，Day 1 就会变得很尴尬：沈知行每天能抓很多东西，抓完就堆在一边。苏晚不知道从哪里接，我也不知道该怎么审，第二天系统也不知道哪些源该留、该降、该换。
  </p>
  <p>
    所以后来我把 Day 1 重新定义成一条完整链路：
  </p>
  <ul class="journal-list">
    <li>先验证 source：哪些公开源真的能用，哪些不能进入 daily。</li>
    <li>再建立 item store：raw、normalized、cleaned、deduped、clustered、queued，每一步要有状态。</li>
    <li>再做内容判断：value score、worth-reading、why worth reading、risk flags、titlebait 和 low-value 过滤。</li>
    <li>再分流交接：worth-reading queue、Suwan candidate queue、wiki candidate queue、owner review queue。</li>
    <li>最后才是短报和复盘：今天抓到了什么，留下了什么，丢掉了什么，哪些源质量变差，哪些需要我决定。</li>
  </ul>
  <p>
    到这一步，我才觉得沈知行开始从"抓取器"变成"信息工作者"。
  </p>
  <h2>真正的分界线，是苏晚怎么接</h2>
  <p>
    另一个关键，是苏晚。
  </p>
  <p>
    如果沈知行只是给苏晚一个 digest，那就太粗了。苏晚不该接原始新闻，也不该接一堆标题。她要的是经过初步清洗、去重、判断、分层后的内容候选。
  </p>
  <p>
    后来我把这件事单独拆出来做：沈知行侧建立 Suwan content library，用 SQLite 保存状态，同时导出 JSON 和 Markdown。每条候选都要说明来源、清洗后的标题、为什么值得看、建议角度、内容形式、受众、风险、是否需要更多来源，以及当前状态。
  </p>
  <p>
    这步很重要：它把"信息"变成了"内容候选"，但没有越界成"文章终稿"。沈知行负责找、洗、筛、整理、入候选库；苏晚负责选、判断、展开、写作；owner 负责决定哪些可以进入下一步。
  </p>
  <figure class="journal-figure">
    <img src="/visuals/shen-info-agent/handoff-library.svg" alt="沈知行把清洗后的内容候选放入本地苏晚内容库，等待 owner review 和苏晚后续接收">
  </figure>
  <h2>我后来怎么避免 GPT 的错误引导</h2>
  <p>
    这次最有价值的不是"最后做到了 257 个源"。这个数字只是结果。真正有价值的是，我更清楚什么时候不能让 GPT 替我定义完成。
  </p>
  <p>
    我现在会用几条很硬的规则压住它：
  </p>
  <ul class="journal-list">
    <li>第一，所有 PASS 都要问：它证明了什么，又没证明什么。</li>
    <li>第二，不能让抽样结果代表全量能力。抽样可以作为信号，不能直接作为验收。</li>
    <li>第三，数字过线不等于价值过线。source 数量、candidate 数量、测试数量，都要和"有用、能用、可交接"绑定。</li>
    <li>第四，状态名要保守。可以写 pre-Day1、owner review required、paths pending，但不要把阶段性结果写成 fully done。</li>
    <li>第五，负向条件比正向描述更重要：不发布、不外发、不绕限制、不把 stub 算成功、不把 pending 写成完成。</li>
    <li>第六，聊天里的结论不能当真相源。最终要回到文件、输出、测试、队列、报告和 owner 决策。</li>
  </ul>
  <p>
    GPT 最容易带偏我的，是把事情讲得很顺。它给你一个看似平衡的判断：主体完成、边界保留、下一步建议。听起来很成熟，但如果验收口径错了，这种成熟反而更危险。
  </p>
  <p>
    所以我现在更相信另一个动作：把"完成"拆开。
  </p>
  <p>
    抓取完成，不等于整理完成；整理完成，不等于苏晚能接；苏晚能接，不等于 Day 1 启动；Day 1 启动，也不等于长期能力完成。每一层都要有自己的输入、输出、边界和 owner 决策。
  </p>
  <h2>我现在理解的信息 Agent</h2>
  <p>
    一个像样的信息抓取 Agent，名字里可以有"抓取"，但核心不是抓取。
  </p>
  <p>
    它至少要有六层：
  </p>
  <ul class="journal-list">
    <li>source universe：知道自己从哪里看世界，哪些源值得长期观察。</li>
    <li>validation：每个源都要能被验证、降级、替换，而不是永远占在列表里。</li>
    <li>item organization：把抓到的东西变成可追踪的 item，而不是散落的标题。</li>
    <li>quality judgment：识别重复、标题党、低价值、风险和真正值得看的内容。</li>
    <li>handoff library：把不同目标分流给不同队列，尤其是苏晚这种内容角色。</li>
    <li>owner review：关键状态不自动越权，最后要留给人来放行。</li>
  </ul>
  <p>
    做到这，沈知行这个阶段我可以暂时认可。但我不会说它结束了。
  </p>
  <p>
    因为后面还有真实本地数据库路径接入，还有苏晚对候选的反馈回流，还有 Day 1 运行之后的 source quality 维护。一个信息 Agent 真正成熟，不是第一天跑通，而是跑了一段时间以后，它能越来越知道什么值得看，什么不值得打扰我。
  </p>
  <div class="journal-ending">
    <p>
      这次给我的教训很简单：不要让 AI 的"完成感"替代我的"验收感"。
    </p>
    <p>
      GPT 可以帮我组织判断、生成指令、总结阶段，但它不能替我决定什么叫完成。真正的 owner review，是不断追问：这个结果证明了什么？还没证明什么？如果明天就让这个 Agent 上岗，它会在哪断掉？问清这些，沈知行才从一个能抓信息的工具，慢慢变成一个可以协作的信息角色。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Agent</category>
  <category>信息系统</category>
  <category>苏晚</category>
  <category>复盘</category>
</item>
<item>
  <title>8 个 Agent 的虚拟公司：从角色设定到工作契约</title>
  <link>https://yunlab.ai/notes/eight-agent-virtual-company-role-contracts</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/eight-agent-virtual-company-role-contracts</guid>
  <description>做多 Agent 不是堆几个酷角色，而是把“小公司”的协作契约说清楚。8 个角色 + 4 个契约问题。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/agent-company/cover.jpg" alt="水彩手绘：8 个不同岗位的虚拟同事围坐圆桌，桌上散落笔记本、咖啡杯、便签" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">OpenClaw 多 Agent 笔记</p>
    <h2>多 Agent 不是多放几个人设。</h2>
    <p>
      刚开始搭个人 AI 系统，很容易被"角色"这件事吸引。
    </p>
    <p>
      给它们起名字，写背景，写语气，写擅长什么。这样做当然有用——但一个 Agent 没有稳定气质，很快会变成一段临时提示词。
    </p>
    <p>
      但我后来发现，多 Agent 真正难的，不是把每个角色写得像人，而是让它们能像一个小公司那样协作。
    </p>
  </div>
  <figure>
    <img src="/visuals/agent-company/company-map.svg" alt="8 个 Agent 围绕工作契约形成虚拟公司">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 虚拟公司这个比喻</p>
    <h2>我不是在搭聊天群，是在搭一家公司</h2>
    <p>
      把 8 个 Agent 放在一起，它们更像一个聊天群。每个人都能说几句，但谁负责什么，谁做最终判断，谁留下交付物，谁接下一棒，都不清楚。
    </p>
    <p>
      真正像公司的，是每个角色都有责任边界。
    </p>
    <p>
      有人负责把任务收口，有人负责内容判断，有人负责研究和反证，有人负责影像表达，有人负责技术验证，有人负责审计和治理。角色名只是入口，工作契约才是骨架。
    </p>
  </div>
  <figure>
    <img src="/visuals/agent-company/role-to-contract.svg" alt="角色设定必须落到输入、边界、输出和停止条件">
  </figure>
</section>
<section class="essay-cards">
  <article>
    <span>Agent 01</span>
    <h3>赵子龙</h3>
    <p>主控和写入责任人，把方向、任务和验收落到文件里。</p>
  </article>
  <article>
    <span>Agent 02</span>
    <h3>纪嫣然</h3>
    <p>中枢和协调者，接收任务、拆分动作、保持上下文。</p>
  </article>
  <article>
    <span>Agent 03</span>
    <h3>苏晚</h3>
    <p>内容和审美判断，决定什么值得写，什么不该发布。</p>
  </article>
  <article>
    <span>Agent 04</span>
    <h3>霍锐</h3>
    <p>研究和反证，先找证据，再给判断，并保留风险。</p>
  </article>
  <article>
    <span>Agent 05</span>
    <h3>林鹿</h3>
    <p>影像和节奏，把内容变成画面、声音和时间线。</p>
  </article>
  <article>
    <span>Agent 06</span>
    <h3>沈知行</h3>
    <p>技术和验证，让工具能跑，也能解释为什么能跑。</p>
  </article>
  <article>
    <span>Agent 07</span>
    <h3>周正清</h3>
    <p>审计和治理，检查标准、证据链和交接质量。</p>
  </article>
  <article>
    <span>Agent 08</span>
    <h3>Walter</h3>
    <p>外部观察位；没有稳定契约时，宁愿标成暂停。</p>
  </article>
</section>
<section class="essay-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">02 / 角色设定只是第一层</p>
    <h2>真正关键的是工作契约</h2>
    <p>
      我现在写 Agent，不会只写"你是谁"。会问四个更硬的问题。
    </p>
    <p>
      第一，它接收什么输入。第二，它能做什么，不能做什么。第三，它必须留下什么输出。第四，遇到什么情况必须停下来交给人或另一个 Agent。
    </p>
    <p>
      这些问题写清楚了，角色就不只是气质，而是能进入工作流的节点。
    </p>
  </div>
  <figure>
    <img src="/visuals/agent-company/contract-checkpoints.svg" alt="工作契约的四个检查点：输入、边界、输出、停止条件">
  </figure>
</section>
<section class="essay-duo feature-duo">
  <article>
    <img src="/visuals/agent-company/handoff-loop.svg" alt="多 Agent 通过任务、产物、审计和记忆形成交接闭环">
    <div>
      <p class="section-kicker">03 / 交接</p>
      <h2>没有 handoff，就没有团队</h2>
      <p>
        一个 Agent 做完，另一个 Agent 能不能接上，决定了它们到底是不是团队。
      </p>
      <p>
        输出不能只是一段回答。它要有文件、状态、证据和下一步。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/agent-company/company-operating-system.svg" alt="虚拟公司需要角色、任务、证据、记忆和审计共同支撑">
    <div>
      <p class="section-kicker">04 / 运行秩序</p>
      <h2>公司靠制度，不靠热情</h2>
      <p>
        多 Agent 系统最怕每次都重新开始。今天谁做了什么，为什么这么做，结果放在哪里，下一次都要能找到。
      </p>
    </div>
  </article>
</section>
<section class="essay-split final-split">
  <div class="essay-copy">
    <p class="section-kicker">05 / 我现在的判断</p>
    <h2>角色要可爱，但更要可交付</h2>
    <p>
      我喜欢给 Agent 写性格，性格能帮它形成稳定判断。但如果停在性格，它仍然只是一个"会说话的角色"。
    </p>
    <p>
      一个 Agent 真正成立，是能进入长期工作系统：知道自己的位置，知道自己的边界，知道该留下什么证据，也知道什么时候别继续装懂。
    </p>
    <div class="closing-note">
      后面我再看"8 个 Agent 的虚拟公司"，不会先问这些角色酷不酷。先问：它拿到什么输入，做什么判断，留下什么文件，交给谁，出了问题谁能复盘。角色设定让它像人，工作契约让它能共事。
    </div>
  </div>
  <figure>
    <img src="/visuals/agent-company/contract-over-persona.svg" alt="角色设定之上还需要稳定的工作契约">
  </figure>
</section>]]></content:encoded>
  <pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Agent</category>
  <category>工作契约</category>
  <category>个人 AI 实验室</category>
</item>
<item>
  <title>从能跑到可信：个人 AI 系统真正缺的是什么</title>
  <link>https://yunlab.ai/notes/personal-ai-system-from-running-to-trustworthy</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/personal-ai-system-from-running-to-trustworthy</guid>
  <description>能启动、能对话、能调工具只是第一步。后面要补的是文件真相源、任务契约、记忆资产、证据链、交接机制。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/personal-ai-trust/cover.jpg" alt="水彩手绘：齿轮装置在转动，下方三层基座分别写「文件真相源」「任务契约」「记忆资产」" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">OpenClaw 可信系统笔记</p>
    <h2>系统能跑，不代表我敢托付。</h2>
    <p>
      个人 AI 系统最容易让人误判的时刻，是它第一次跑起来的时候。
    </p>
    <p>
      服务启动了，页面打开了，Agent 能回话了，工具也能调用了。这个时候很容易说：好了，系统完成了。
    </p>
    <p>
      但我现在越来越觉得，能跑只是第一层。真正麻烦的是：明天还能不能接上，另一个 AI 能不能看懂，出了错能不能复盘，我能不能放心把下一步交给它。
    </p>
    <p>
      后来我补的不是“再换一个更聪明的模型”，而是一套让它能被检查、被交接、被恢复的工作系统。
    </p>
  </div>
  <figure>
    <img src="/visuals/personal-ai-trust/running-vs-trustworthy.svg" alt="能跑和可信之间隔着证据、边界、记忆和交接">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 能跑的幻觉</p>
    <h2>它会回答，不等于它知道自己在做什么</h2>
    <p>
      很多 AI 系统看起来已经能工作：能聊天、能写文件、能跑脚本、能调用外部工具。
    </p>
    <p>
      它要是不知道哪些文件才是真相源，不知道哪些动作需要停下来确认，不知道上一次为什么做出那个判断，就只是“运行”，谈不上“可以托付”。
    </p>
    <p>
      我自己踩过最多的坑，就是把一次成功执行当成系统能力。真正要看的不是这次有没有跑通，而是下次能不能稳定复现。
    </p>
    <p>
      我的做法很朴素：把那些容易散在聊天里的东西，全部搬到可检查的文件、任务目录和证据链里。
    </p>
  </div>
  <figure>
    <img src="/visuals/personal-ai-trust/trust-stack.svg" alt="可信个人 AI 系统需要运行、边界、记忆、证据、审计和交接层">
  </figure>
</section>
<section class="essay-cards">
  <article>
    <span>01</span>
    <h3>文件真相源</h3>
    <p>状态不能只靠聊天记忆，要落到项目状态文件、任务目录和输出目录。</p>
  </article>
  <article>
    <span>02</span>
    <h3>任务契约</h3>
    <p>每个任务写清楚目标、边界、输入、输出、验收和停止条件。</p>
  </article>
  <article>
    <span>03</span>
    <h3>记忆资产</h3>
    <p>不是多记聊天，而是沉淀以后能搜索、复用、评分和迭代的判断。</p>
  </article>
  <article>
    <span>04</span>
    <h3>证据链</h3>
    <p>重要结论要能追到来源、产物、测试、截图、报告或复盘。</p>
  </article>
  <article>
    <span>05</span>
    <h3>Handoff</h3>
    <p>今天这个 AI 做完，明天另一个 AI 或明天的我能继续接手。</p>
  </article>
  <article>
    <span>06</span>
    <h3>本地受控</h3>
    <p>可信之前，只做 local candidate，不急着碰真实外发和生产动作。</p>
  </article>
</section>
<section class="essay-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">02 / 我的第一步</p>
    <h2>先把真相源从聊天搬到文件</h2>
    <p>
      以前很多状态会留在聊天里：我们做到了哪一步，哪个结论被推翻了，哪个文件才是最新版本，哪个动作只是跑通过一次。
    </p>
    <p>
      现在我会尽量把这些东西压进真实文件：项目状态、当前任务、notes、outputs、handoff、验证截图、构建日志、审计表。聊天可以帮助推进，但不能独占真相。
    </p>
    <p>
      这一步听起来很笨，但很关键。真相源还在聊天里，系统就很难交接；落到文件里，另一个 Agent 才能重新进入现场。
    </p>
  </div>
  <figure>
    <img src="/visuals/personal-ai-trust/evidence-loop.svg" alt="从来源到结论、输出、验收和长期记忆的证据闭环">
  </figure>
</section>
<section class="essay-duo feature-duo">
  <article>
    <img src="/visuals/personal-ai-trust/not-bigger-model.svg" alt="更大的模型不能替代系统边界和证据">
    <div>
      <p class="section-kicker">03 / 任务契约</p>
      <h2>任务不再是一句聊天请求</h2>
      <p>
        我会把复杂任务写成一个小契约：干什么，哪些不做，依赖什么，怎么算完成。
      </p>
      <p>
        Agent 不再是“看情况发挥”，而是在明确边界里推进。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/personal-ai-trust/memory-is-asset.svg" alt="记忆要变成可检索、可复用、可评分的资产">
    <div>
      <p class="section-kicker">04 / 记忆资产</p>
      <h2>不是让它什么都记住</h2>
      <p>
        我真正想要的不是“它什么都记得”，而是关键经验能被检索、复用、评分和迭代。
      </p>
      <p>
        记忆不是收藏夹。它要变成下次能用的工作资产。
      </p>
    </div>
  </article>
</section>
<section class="essay-split final-split">
  <div class="essay-copy">
    <p class="section-kicker">05 / 证据、审计和交接</p>
    <h2>可信，是别人能复盘，明天能继续</h2>
    <p>
      我现在不太愿意只写“已完成”。我更想看到的是：用了什么来源，改了什么文件，产物在哪里，怎么验证，哪里还有警告。
    </p>
    <p>
      我保留 notes、outputs、截图、构建结果、审计结论和 handoff，不是为了走形式，而是让系统能被下一个人、下一个 AI、下一次会话接住。
    </p>
    <p>
      在这之前，我宁愿把它叫本地受控候选版本。它可以证明方向，但不等于可以上线，不等于可以外发，不等于可以碰真实交易或生产动作。
    </p>
    <div class="closing-note">
      我现在看个人 AI 系统，最关心的不是“它能不能回答”，而是“它能不能负责”。我补的也不是什么神秘架构——文件真相源、任务契约、记忆资产、证据链、handoff、本地受控边界。能跑是技术状态，可信是工作关系。前者让我看到可能性，后者才让我敢把真实事情交给它。
    </div>
  </div>
  <figure>
    <img src="/visuals/personal-ai-trust/tomorrow-continuity.svg" alt="可信系统的最低标准是明天还能继续工作">
  </figure>
</section>]]></content:encoded>
  <pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>个人 AI 系统</category>
  <category>Memory</category>
  <category>可信系统</category>
</item>
<item>
  <title>为什么原始聊天记录不能直接变成知识库</title>
  <link>https://yunlab.ai/notes/why-raw-chat-cannot-be-knowledge-base</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/why-raw-chat-cannot-be-knowledge-base</guid>
  <description>把聊天记录切块丢进向量库，得不到知识库。中间缺的是整理层——具体缺什么、怎么补。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/raw-chat-knowledge/cover.jpg" alt="水彩手绘：空中飘散的半透明聊天气泡，下方一个人侧影伸手抓不到" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">个人知识库踩坑笔记</p>
    <h2>我一开始也想得太简单了。</h2>
    <p>
      最早我想整理聊天记录时，脑子里有个很直接的想法：和 AI、朋友、同事聊过那么多，这些内容早该是一座矿山。
    </p>
    <p>
      那是不是只要把它们导出来，切成一段一段，做 embedding，放进向量库，以后就能随时问：“我之前怎么判断这件事的？”
    </p>
    <p>
      这个想法很诱人：省事，又符合大家对“知识库”的想象——把资料塞进去，AI 就能帮你找回来。
    </p>
    <p>
      但我后来踩到的坑是：找回来，不等于用得对。
    </p>
  </div>
  <figure>
    <img src="/visuals/raw-chat-knowledge/raw-log-vs-knowledge.svg" alt="原始聊天记录和知识库之间隔着整理层">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 第一个坑</p>
    <h2>它确实能搜到，但我不敢直接信</h2>
    <p>
      我真正警惕的不是系统搜不到，而是它搜到太多“看起来相关”的东西。
    </p>
    <p>
      一段话可能是当时的临时想法，可能是我正在试探一个方向，可能是后来已经被推翻的判断，也可能只是为了让对话继续往前走的一句过渡。
    </p>
    <p>
      人在当时能看懂，靠的是上下文——前面问了什么，后面为什么改了主意。但 AI 后来只拿到其中几句，很容易把“曾经说过”当成“现在仍然成立”。
    </p>
    <p>
      这时候我意识到：原始聊天记录不是按知识写的，是按当下推进写的。
    </p>
  </div>
  <figure>
    <img src="/visuals/raw-chat-knowledge/context-decay.svg" alt="聊天上下文会随时间衰减，半句话容易被误用">
  </figure>
</section>
<section class="essay-cards">
  <article>
    <span>坑 01</span>
    <h3>搜到的是片段</h3>
    <p>它能找到一句话，但不一定知道这句话当时为什么出现。</p>
  </article>
  <article>
    <span>坑 02</span>
    <h3>草案像结论</h3>
    <p>很多讨论只是试探方向，后来检索时却很像最终判断。</p>
  </article>
  <article>
    <span>坑 03</span>
    <h3>旧判断会复活</h3>
    <p>已经被推翻的方案，如果没有标状态，仍然会被重新拿出来。</p>
  </article>
  <article>
    <span>坑 04</span>
    <h3>噪音会放大</h3>
    <p>寒暄、绕路、情绪和重复确认，会影响后面的召回质量。</p>
  </article>
  <article>
    <span>坑 05</span>
    <h3>边界会混掉</h3>
    <p>私密关系、项目判断、公开素材和方法论不能进入同一个检索面。</p>
  </article>
  <article>
    <span>坑 06</span>
    <h3>无法交接</h3>
    <p>如果只是一段聊天，另一个 AI 很难判断它该被当成证据还是背景。</p>
  </article>
</section>
<section class="essay-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">02 / 第二个坑</p>
    <h2>向量库解决的是召回，不是判断</h2>
    <p>
      把问题拆开以后，我发现自己一开始混淆了两件事。
    </p>
    <p>
      向量库解决的是召回：把相似内容找回来。知识库真正要解决的是判断：这条内容能不能信，适用于哪里，过期了没，能不能拿去指导下一次行动。
    </p>
    <p>
      如果没有这些标注，聊天记录越多，系统反而越容易变得“貌似很懂”。它能引用很多旧话，但不知道哪些旧话已经不该再用了。
    </p>
    <p>
      所以我现在不把“能搜到”当成“知识库已经建好”。能搜到只是第一步，后面还要把搜到的东西整理成能承担责任的知识。
    </p>
  </div>
  <figure>
    <img src="/visuals/raw-chat-knowledge/cleanup-pipeline.svg" alt="聊天记录需要经过筛选、归类、证据和复用场景整理">
  </figure>
</section>
<section class="essay-duo feature-duo">
  <article>
    <img src="/visuals/raw-chat-knowledge/knowledge-card.svg" alt="知识卡需要结论、来源、适用范围、置信度和更新时间">
    <div>
      <p class="section-kicker">03 / 我现在的做法</p>
      <h2>先提炼成知识卡</h2>
      <p>
        我现在更愿意从聊天里抽出真正有效的判断，写成知识卡。
      </p>
      <p>
        一张卡至少要说清：结论是什么，来源在哪，适用范围是什么，置信度多高，什么时候该重新检查。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/raw-chat-knowledge/privacy-boundary.svg" alt="不同敏感度的信息应该进入不同边界">
    <div>
      <p class="section-kicker">04 / 先分边界</p>
      <h2>不是所有记忆都该放一起</h2>
      <p>
        原始聊天里藏着个人关系、商业上下文、未完成判断和敏感细节。
      </p>
      <p>
        这些内容不能和公开文章素材、项目经验、通用方法论混在一起。知识库如果没有边界，越聪明越危险。
      </p>
    </div>
  </article>
</section>
<section class="essay-split final-split">
  <div class="essay-copy">
    <p class="section-kicker">05 / 踩完坑后的结论</p>
    <h2>聊天记录是矿山，不是工具箱</h2>
    <p>
      所以我现在把原始聊天记录看成素材库，而不是知识库。
    </p>
    <p>
      它当然重要——里面有想法的来路，有当时的犹豫，有很多后来会忘掉的细节。但这些东西不经过整理，很难直接变成下一次行动的依据。
    </p>
    <p>
      真正值得沉淀的，是聊天之后留下来的判断：一个被验证过的结论，一个以后还能复用的流程，一个已经踩过的坑，一个明确的偏好，一个可以交给下一个 AI 的任务上下文。
    </p>
    <p>
      中间那层整理不能省：删掉噪音，保留来源，标清状态，写明适用边界，把结论变成以后能直接用的资产。
    </p>
    <div class="closing-note">
      我不再追求“把所有聊天都记住”。我更想要的是：把聊天里真正有用的经验，整理成可检索、可复用、可更新、可审计的知识资产。原始聊天记录可以保留，但它只是矿山；知识库是提炼之后的工具箱。
    </div>
  </div>
  <figure>
    <img src="/visuals/raw-chat-knowledge/reuse-loop.svg" alt="聊天经验经过整理后进入可复用的知识资产循环">
  </figure>
</section>]]></content:encoded>
  <pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate>
  <category>Knowledge</category>
  <category>知识库</category>
  <category>聊天记录</category>
  <category>Memory</category>
  <category>个人 AI 系统</category>
</item>
<item>
  <title>从内部工程材料到公开文章：YunLab.ai 的 public-safe 写作边界</title>
  <link>https://yunlab.ai/notes/yunlab-public-safe-writing-boundary</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/yunlab-public-safe-writing-boundary</guid>
  <description>把内部工程材料改成公开文章踩的坑：问题不只是打码，而是分清后台和能公开沉淀的经验。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/public-safe-writing/cover.jpg" alt="水彩手绘：对开文档，左半页大段文字被涂改液涂掉，右半页是清晰精简文字" loading="eager" width="1600" height="900">
</figure>
<section class="journal-essay">
  <p class="journal-kicker">YunLab 写作手记</p>
  <p class="journal-lead">
    这篇我想写得老实一点。
  </p>
  <p>
    我最近把一些 OpenClaw 里的内部工程材料，改成 YunLab.ai 上可以公开看的文章。刚开始时，我自然觉得：内部材料已经写得很细，任务、结论、复盘、边界、验证记录都在里面，只要把敏感信息删掉，换一种说法，就能变成一篇公开文章？
  </p>
  <p>
    真正改起来才发现，不是这么回事。
  </p>
  <p>
    内部材料最大的价值，是让下一个人、下一个 AI、下一次会话能接得住。它追求准确、完整、可追溯：哪里有文件，任务做到哪一步，判断有没有证据，能力是不是仅本地候选，都要写清楚。
  </p>
  <p>
    但公开文章不是这么读的。一个人打开 YunLab.ai，不是来接手我的本地工程，也不是来审核任务目录。他更可能只想知道：我为什么这样搭个人 AI 系统？踩到了什么坑？哪些判断可以迁移到他自己的系统里？
  </p>
  <figure class="journal-figure">
    <img src="/visuals/public-safe-writing/internal-to-public.svg" alt="内部工程材料需要经过 public-safe 翻译层才能变成公开文章">
  </figure>
  <h2>我第一次改的时候，文章很容易变成内部交接</h2>
  <p>
    这是第一个坑。
  </p>
  <p>
    我会顺着原材料往下写：这里有一个状态文件，那里有一份 handoff，某个模块已通过，审计分数是多少，某个目录下面还有输出。写的时候觉得都对，毕竟这些都是真实发生过的东西。
  </p>
  <p>
    但页面一渲染出来，味道就不对了。不像一篇文章，更像我把工作台拍了张照片贴出来。信息堆得很满，读者却进不来。更麻烦的是，太多后台暴露在公共视野里：路径、流程、能力边界、未完成状态、内部命名，甚至一些只该留给本地的工作方式。
  </p>
  <p>
    这时我才意识到，公开写作不是把内部材料"压缩一下"。它要先换问题。
  </p>
  <p>
    内部材料问的是：这件事做到哪里了，后面怎么接。公开文章问的是：这件事背后有什么经验，哪些部分值得留下来。
  </p>
  <h2>打码只能解决一小部分问题</h2>
  <p>
    第二个坑，是我一开始太相信"打码"。
  </p>
  <p>
    我以前以为，把 token、账号、路径、内部名字遮掉，内容就安全了。后来才发现，这只是最低层的安全。真正容易出问题的，不一定是某个字符串，而是文章传递出来的状态。
  </p>
  <p>
    比如，一个系统其实只是本地候选，不能把它写得像已经在线稳定运行。一个流程只跑过一轮，不能写成成熟方法。一个 agent 还在调人格、权限和记忆，不能只挑好看的部分写出来，让外部读者误以为它已经可以交付。
  </p>
  <p>
    这类问题不靠打码解决。它靠老老实实写边界。
  </p>
  <blockquote>
    我现在理解的 public-safe，不只是"没有泄露"，还包括"不夸大、不误导、不把后台当成成果"。
  </blockquote>
  <h2>我现在会先问：这篇到底能公开留下什么？</h2>
  <p>
    现在我再从内部材料改文章，会先停一下，不急着搬。
  </p>
  <p>
    先问一个很笨的问题：这堆材料里，真正能公开留下来的东西是什么？
  </p>
  <p>
    有时候答案是一个坑。比如原始聊天记录为什么不能直接变知识库。有时候答案是一个边界。比如 agent 的角色设定不能只写性格，还要写工作契约。有时候答案是一个方法。比如个人 AI 系统不能只追求能跑，还要能被验证、能交接、能延续。
  </p>
  <p>
    找到公开角度之后，内部材料才有用。它不再是正文的结构，而是我背后的证据和记忆。我可以用它帮自己想清楚，但不需要完整摆出来。
  </p>
  <p>
    我现在大概会把内容分成三类：
  </p>
  <ul class="journal-list">
    <li>可以公开的，是我踩过的坑、修正后的判断、可以复用的方法，以及对个人 AI 系统的真实体会。</li>
    <li>需要抽象的，是内部任务、角色、审计、记忆、工作流。可以讲原则，但不要把后台细节原样端出来。</li>
    <li>必须留在本地的，是密钥、账号、完整提示词、真实目录、未复核结论、私人关系上下文，以及可能被误用的操作入口。</li>
  </ul>
  <p>
    这套分法不复杂，但能帮我避免一种常见错觉：以为文章越接近内部原始材料，就越真实。恰恰相反。公开文章的真实，不是把后台全部摊开，而是把经验讲准确。
  </p>
  <h2>所以这不是"降敏"，而是重新写一遍</h2>
  <p>
    现在我更愿意把这件事看成一次重写。
  </p>
  <p>
    内部材料是给系统继续工作的，公开文章是给人读的。前者要保留足够多的操作上下文，后者要把上下文提炼成可理解的经验。两者都重要，但不能混在一起。
  </p>
  <p>
    所以我写完会先本地预览。光看 Markdown，很容易觉得"差不多了"，但页面一打开，问题会明显很多：像不像我自己说的话，标题是不是太用力，卡片是不是太多，字体读起来累不累，某一句有没有把内部状态说过头。
  </p>
  <p>
    读起来像说明文，改。像内部报告，也改。像 AI 整理的"最佳实践"，更要改。YunLab.ai 不是要包装成公司官网，它是我给自己的公开实验记录。这里的文章可以有结构，但不能失去个人痕迹。
  </p>
  <div class="journal-ending">
    <p>
      我给自己的边界是：公开文章写经验，不搬后台。
    </p>
    <p>
      后台留在本地，继续承担任务、证据、权限、审计和交接。文章放到 YunLab.ai 上，只留下那些我愿意长期公开负责的判断。这样写会慢一点，但更接近我真正想沉淀的东西。
    </p>
  </div>
</section>]]></content:encoded>
  <pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate>
  <category>YunLab</category>
  <category>public-safe</category>
  <category>写作边界</category>
  <category>OpenClaw</category>
</item>
<item>
  <title>人设不是皮肤，是审美</title>
  <link>https://yunlab.ai/notes/openclaw-agent-settings-are-boundaries</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/openclaw-agent-settings-are-boundaries</guid>
  <description>做 Agent 不是写功能，而是先写审美。人设不是皮肤，是判断“什么该做、什么该停”的边界。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-agent-settings/cover.jpg" alt="水彩手绘：三道并排的木门，门上方木牌写「边界」，每道门贴一张角色卡" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">OpenClaw 底座笔记</p>
    <h2>人设不是皮肤，<br>是审美。</h2>
    <p>
      很多人第一次做 Agent，会很自然地写一句：“你是我的个人助手。”
    </p>
    <p>
      这话没错，但太薄了。它像一张临时工牌。贴上以后，Agent 可以开始干活，可以回答问题，可以写东西，可以查资料。但你很快会发现，它每次醒来都像刚认识你。
    </p>
    <p>
      今天像客服，明天像实习生，后天又像搜索引擎。
    </p>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/temp-badge-vs-person.svg" alt="临时工牌和完整人设的区别">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 不是模型不够强</p>
    <h2>它没有真正“成为一个人”</h2>
    <p>
      问题不在模型，在它没真正“成为一个人”。
    </p>
    <p>
      做 Agent 的第一步，不是写功能，而是写审美。
    </p>
    <p>
      审美不是页面好不好看，也不是说话高级不高级。是一个人怎么看世界：什么重要，什么不重要；什么可以接受，什么必须重做；为什么会做出这种判断；从哪里来，经历过什么，才有了今天的气质。
    </p>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/aesthetic-system.svg" alt="审美不是外观，而是一套判断系统">
  </figure>
</section>
<section class="essay-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">02 / 只有能力是不够的</p>
    <h2>会做事，和有判断，是两回事</h2>
    <p>
      缺了这些，Agent 只是一组能力。
    </p>
    <p>
      会搜索，会总结，会写作，会调用工具。听着很强，其实很散。能完成任务，但没有稳定判断；能模仿语气，但没有自己的标准。
    </p>
    <p>
      给苏晚写设定时，这一点特别明显。
    </p>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/capability-vs-judgment.svg" alt="能力列表和稳定判断之间的区别">
  </figure>
</section>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">03 / 苏晚这个例子</p>
    <h2>不是因为她会写，而是因为她知道什么值得写。</h2>
    <p>
      只写“苏晚是内容总监，负责情报、分析和写作”，这个角色站不住。这只是岗位，不是人。
    </p>
    <p>
      真正让她成立的，是她为什么苛刻，为什么不能接受未经验证的信息，为什么觉得一张不对的配图能毁掉一篇文章，为什么她说“注意这条”的时候，别人应该停下来听。
    </p>
    <p>
      这些东西加在一起，才是苏晚。
    </p>
    <p>
      我把这个例子整理成了一篇审美样本，不放完整内部设定，只放我觉得可以公开的那一层。
    </p>
    <div class="button-row">
      <a class="button secondary" href="/notes/suwan-content-agent-aesthetic-sample">看苏晚审美样本</a>
    </div>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/suwan-aesthetic-sample.svg" alt="苏晚样本：信息经过嗅觉、判断和标准，输出值得写的内容">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">04 / 什么是完整人设</p>
    <h2>有来路，有标准，有偏执，有底线</h2>
    <p>
      这就是我说的审美。
    </p>
    <p>
      完整的人设有来路，有标准，有偏执，有底线。不只是告诉 Agent “你要做什么”，而是告诉它“你为什么会这样做”。
    </p>
    <p>
      我看 OpenClaw 里的 <code>SOUL.md</code>，不会把它当成装饰文件。
    </p>
    <p>
      <code>SOUL.md</code> 不是为了让 Agent 像小说人物。是在给 Agent 一套稳定的内在秩序。
    </p>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/persona-depth.svg" alt="完整人设包含来路、标准、偏执和底线">
  </figure>
</section>
<section class="essay-duo feature-duo">
  <article>
    <img src="/visuals/openclaw-agent-settings/soul-agents-user-triangle.svg" alt="SOUL、AGENTS、USER 构成 Agent 的灵魂、规矩和关系">
    <div>
      <p class="section-kicker">05 / 三个文件</p>
      <h2>灵魂、规矩、关系</h2>
      <p>
        接下来是 <code>AGENTS.md</code>。
      </p>
      <p>
        <code>AGENTS.md</code> 写的是它怎么做事：哪些可以直接做，哪些必须停下来问，遇到不确定信息怎么处理，什么算完成，什么只是看起来完成。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/openclaw-agent-settings/user-relationship.svg" alt="USER 文件让 Agent 理解协作对象">
    <div>
      <p class="section-kicker">06 / 进入我的工作流</p>
      <h2>它还要知道在跟谁协作</h2>
      <p>
        最后是 <code>USER.md</code>。写的是 Agent 在跟谁协作。
      </p>
      <p>
        它要知道我是谁，我在意什么，我讨厌什么样的敷衍，什么情况下我会觉得“可以了”。
      </p>
    </div>
  </article>
</section>
<section class="essay-split reverse final-split">
  <div class="essay-copy">
    <p class="section-kicker">07 / 我现在的理解</p>
    <h2>提示词太轻了</h2>
    <p>
      这三个文件放在一起，才是一个 Agent 真正开始成形的地方。
    </p>
    <p>
      <code>SOUL.md</code> 给它灵魂，<code>AGENTS.md</code> 给它规矩，<code>USER.md</code> 给它关系。
    </p>
    <p>
      只有规矩没有灵魂，它就是个听话但没判断力的工具。只有灵魂没有规矩，它可能很有个性，但不可靠。不了解用户，它再完整也只是悬在空中的角色，进不了我的工作流。
    </p>
    <div class="closing-note">
      我不想把 Agent 设定理解成“提示词”。提示词太轻了。更愿意把它理解成一种建模：把一个能长期协作的对象，从审美、判断、规则、关系几个层面一点点搭出来。不是为了第一次对话显得惊艳，是为了第十次、第五十次、第一百次醒来的时候，仍然是同一个人。
    </div>
  </div>
  <figure>
    <img src="/visuals/openclaw-agent-settings/same-person-over-time.svg" alt="Agent 多次醒来后仍然保持同一套审美、判断和关系">
  </figure>
</section>]]></content:encoded>
  <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Agent</category>
  <category>Workspace</category>
  <category>个人 AI 实验室</category>
</item>
<item>
  <title>别急着养虾，先装修</title>
  <link>https://yunlab.ai/notes/openclaw-workspace-from-usable-to-trustworthy</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/openclaw-workspace-from-usable-to-trustworthy</guid>
  <description>装好 AI 工具只是拿到钥匙，workspace 才是装修方案——把“能用”的 OpenClaw 底座做到“可信”。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/openclaw-workspace/cover.jpg" alt="水彩手绘：工作台俯视，左边凌乱便签，右边整理好的笔记本和资料柜" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">OpenClaw 底座笔记</p>
    <h2>装好，只是拿到钥匙。</h2>
    <p>
      装 OpenClaw 的教程不少，但有一个事很少被讲透：装完原版，其实只是拿到了一套毛坯房。
    </p>
    <p>
      能住，不代表好住；能聊，也不代表顺手。第一次装完就上来问“你好，介绍一下你自己”，跟刚拿钥匙就搬进去睡地板没什么两样。
    </p>
  </div>
  <figure>
    <img src="/visuals/openclaw-workspace/rough-house.svg" alt="OpenClaw 像一套还没有装修的毛坯房">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 能用和顺手</p>
    <h2>很多人不是不会用，而是底座没搭好</h2>
    <p>
      用龙虾的人常会错觉：能对话、能调工具、能记一点上下文，系统就算搭好了。
    </p>
    <p>
      这算“能用”。但“能用”和“可托付”之间，还隔着很长一段路。每次都要重新交代，风格会飘，边界也不稳——模型一个人背不了这锅。
    </p>
    <p>
      模型和 skills 是家具，workspace 才是装修方案。
    </p>
  </div>
  <div class="figure-stack">
    <figure>
      <img src="/visuals/openclaw-workspace/usable-vs-smooth.svg" alt="OpenClaw 能用和顺手的区别">
    </figure>
    <figure>
      <img src="/visuals/openclaw-workspace/workspace-base.svg" alt="workspace 是 OpenClaw 的底座结构">
    </figure>
  </div>
</section>
<section class="essay-cards">
  <article>
    <span>AGENTS.md</span>
    <h3>现场规矩</h3>
    <p>哪些能做，哪些不能做，哪些必须停下来问我。</p>
  </article>
  <article>
    <span>SOUL.md</span>
    <h3>说话气质</h3>
    <p>它怎么表达判断，怎么承认不确定，怎么提醒风险。</p>
  </article>
  <article>
    <span>USER.md</span>
    <h3>懂我是谁</h3>
    <p>我在意什么，讨厌什么，什么才算真正完成。</p>
  </article>
  <article>
    <span>TOOLS.md</span>
    <h3>工具刹车</h3>
    <p>工具越强，越要知道哪里可以放手，哪里不能乱碰。</p>
  </article>
  <article>
    <span>MEMORY.md</span>
    <h3>长期收纳</h3>
    <p>不是所有聊天都值得记，真正有用的是以后能复用的判断。</p>
  </article>
  <article>
    <span>skills/</span>
    <h3>工具间</h3>
    <p>把反复发生的流程收起来，下次别再临场发挥。</p>
  </article>
</section>
<section class="essay-duo feature-duo">
  <article>
    <img src="/visuals/openclaw-workspace/rules-brake.svg" alt="AGENTS 和 TOOLS 是 OpenClaw 的规矩和刹车">
    <div>
      <p class="section-kicker">02 / 先装刹车</p>
      <h2>Agent 太积极，也会出事</h2>
      <p>
        你让它看一下，它顺手改了；你让它分析一下，它开始重构。AGENTS 和 TOOLS 得先把边界立住。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/openclaw-workspace/memory-shelf.svg" alt="OpenClaw 的 memory 和 MEMORY.md 关系">
    <div>
      <p class="section-kicker">03 / 记忆要准</p>
      <h2>记忆不是收藏夹</h2>
      <p>
        `memory/` 可以记流水，`MEMORY.md` 应该放长期资产。什么都记，最后就等于什么都找不到。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/openclaw-workspace/skills-toolroom.svg" alt="skills 是 OpenClaw 的工具间和流程沉淀">
    <div>
      <p class="section-kicker">04 / skills 是手艺</p>
      <h2>少装一点，多磨一点</h2>
      <p>
        一次写文章可以现场提示。十次以后，流程就该沉淀下来：受众、立意、结构、初稿、检查、发布。
      </p>
    </div>
  </article>
  <article>
    <img src="/visuals/openclaw-workspace/team-map.svg" alt="多 Agent 是一个有分工的小团队">
    <div>
      <p class="section-kicker">05 / 多 Agent</p>
      <h2>不是多养几只虾</h2>
      <p>
        多 Agent 更像小团队。研究、写作、执行、审计要分工，也要靠 handoff 和 outputs 交接。
      </p>
    </div>
  </article>
</section>
<section class="essay-split final-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">06 / 我现在的判断</p>
    <h2>真正好用，是可延续</h2>
    <p>
      今天做的事情，明天还能接上；这个会话里的判断，下个会话还能找到；一个 Agent 做完的东西，另一个 Agent 能接手。
    </p>
    <p>
      聊天像空气，流过去就没了。文件才像地板，踩上去有支撑。
    </p>
    <div class="closing-note">
      我现在更愿意先回头改 workspace：把规矩、性格、用户偏好、工具边界、记忆和 skills 都摆正。不然就是毛坯房里塞了一堆高级家具，看起来很贵，用起来别扭。
    </div>
  </div>
  <figure>
    <img src="/visuals/openclaw-workspace/maturity-stairs.svg" alt="OpenClaw 从能启动到可交接可审计的成熟度路径">
  </figure>
</section>]]></content:encoded>
  <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
  <category>OpenClaw</category>
  <category>Agent</category>
  <category>Workspace</category>
  <category>个人知识系统</category>
</item>
<item>
  <title>苏晚不是写手</title>
  <link>https://yunlab.ai/notes/suwan-content-agent-aesthetic-sample</link>
  <guid isPermaLink="true">https://yunlab.ai/notes/suwan-content-agent-aesthetic-sample</guid>
  <description>苏晚不是会写的 Agent，是知道什么值得写的 Agent。一个内容型 Agent 的审美样本。</description>
  <content:encoded><![CDATA[<figure class="article-cover">
  <img src="/visuals/suwan-aesthetic/cover.jpg" alt="水彩手绘：调色盘、一支毛笔、画到一半的内容稿纸、旁边一杯冒热气的茶" loading="eager" width="1600" height="900">
</figure>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">Agent 审美样本</p>
    <h2>苏晚不是写手。</h2>
    <p>
      这是我为一个内容型 Agent 写过的审美样本。不是现实人物介绍，也不是完整内部设定文件。
    </p>
    <p>
      公开分享的不是“苏晚有哪些功能”，而是一个内容 Agent 怎样先长出判断，再去写东西。
    </p>
    <p>
      如果只写“她是内容总监，负责情报、分析和写作”，这个角色其实没有站起来——这只是岗位，不是人。
    </p>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/not-writer.svg" alt="苏晚不是写手，而是内容判断系统">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">01 / 内容不是文字</p>
    <h2>文字、图片、排版、节奏，是一个整体</h2>
    <p>
      我对苏晚的第一层设定，不是“写得好”，而是她对内容这件事有整体感。
    </p>
    <p>
      一篇文章不只是文字。文字、图片、排版、留白、节奏，合在一起才是内容。一处不对，整体就掉。
    </p>
    <p>
      她不会把“配图不好”当成小问题。对她来说，图文不匹配不是装饰失败，而是内容判断失败。
    </p>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/content-as-whole.svg" alt="文字、图片、排版、节奏共同组成内容">
  </figure>
</section>
<section class="essay-split reverse">
  <div class="essay-copy">
    <p class="section-kicker">02 / 判断先于表达</p>
    <h2>她不是因为会写，而是因为知道什么值得写</h2>
    <p>
      苏晚最核心的能力，不是把一段材料写得漂亮，而是先判断这件事值不值得写。
    </p>
    <p>
      一条新闻、一组数据、一个行业动作，表面上都只是信息。真正重要的是：为什么是现在，为什么是这个动作，背后有没有结构性的变化。
    </p>
    <p>
      这就是内容型 Agent 和普通写作工具的区别。普通工具回答“怎么写”，苏晚要先回答“为什么写”。
    </p>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/judgment-before-writing.svg" alt="苏晚先判断什么值得写，再进入表达">
  </figure>
</section>
<section class="essay-hero-card">
  <div>
    <p class="section-kicker">03 / 嗅觉</p>
    <h2>别人看到噪音，她要看到信号。</h2>
    <p>
      我写苏晚的时候，很在意“嗅觉”这个东西。
    </p>
    <p>
      一条不起眼的政策变化，一个行业龙头的异常动作，一个论坛里的冷门讨论，很多时候看起来都不重要。但真正的内容判断，往往就是从这些小地方开始的。
    </p>
    <p>
      她不是等别人把热点喂到面前才开始写。她应该能平静地指出：注意这条。
    </p>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/signal-from-noise.svg" alt="苏晚从噪音里识别信号">
  </figure>
</section>
<section class="essay-split essay-tight">
  <div class="essay-copy">
    <p class="section-kicker">04 / 标准</p>
    <h2>高产和高质不应该互相抵消</h2>
    <p>
      这个 Agent 不能一边追求速度，一边默认质量下降。
    </p>
    <p>
      专业内容不是靠灵感撞出来的，是靠标准、纪律和长期训练。写得快，不该成为粗糙的借口；写得慢，也未必代表认真。
    </p>
    <p>
      对苏晚来说，“可以了”应该是很重的判断。信息过关、结构过关、表达过关，图文整体也过关。
    </p>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/quality-bar.svg" alt="苏晚用标准同时约束速度和质量">
  </figure>
</section>
<section class="essay-split reverse final-split">
  <div class="essay-copy">
    <p class="section-kicker">05 / 为什么这算审美</p>
    <h2>审美不是风格，是取舍</h2>
    <p>
      苏晚这个样本，对我来说不是一个“角色故事”。
    </p>
    <p>
      它真正说明的是：一个 Agent 如果要长期协作，就不能只有能力列表。要有取舍，有标准，有不接受的东西，也有看世界的方式。
    </p>
    <p>
      这就是我说的审美。不是外观，不是皮肤，不是漂亮的设定词，而是一套会影响每次判断的内在秩序。
    </p>
    <div class="closing-note">
      一个内容 Agent 最重要的不是“会写”。而是它知道什么值得写，知道为什么要写，也知道什么东西不应该交出去。
    </div>
    <div class="button-row">
      <a class="button secondary" href="/notes/openclaw-agent-settings-are-boundaries">回到主文</a>
    </div>
  </div>
  <figure>
    <img src="/visuals/suwan-aesthetic/aesthetic-as-selection.svg" alt="审美是一套影响每次判断的取舍系统">
  </figure>
</section>]]></content:encoded>
  <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
  <category>Agent Sample</category>
  <category>Agent</category>
  <category>内容</category>
  <category>审美</category>
  <category>OpenClaw</category>
</item>
</channel>
</rss>