moat

别再用制度文档守你的护城河 (3/5)

制度型护城河只要来一位新人工程师、或者赶上一次深夜重构,就会开始悄悄烂掉。lodos 每次提交都跑两条断言:拒绝机制一旦倒退,构建失败;教训一旦被忘掉,wiki 的自查通不过。两条断言的记录都写进同一条 HMAC 链。

1 分钟阅读

大多数 AI 产品靠制度文档来守自己的护城河,而这类文档只要来一位新人工程师、或者赶上一次深夜重构,就会悄无声息地烂掉。我守的方式不一样:每次提交都会跑两条硬断言。一条盯着我发誓永远不做的那些东西,只要它们真的出现在代码里,构建当场失败;另一条盯着我拿代价换来的教训,只要它变得查不到、或者和别的页面自相矛盾,wiki 就通不过自己的检查。两条断言的记录都写进同一条 HMAC 链,谁也改不动。这样得来的护城河才是只进不退的:每一次拒绝、每一条教训,都让这套系统更难倒退一步,也更好用一分。

每次提交,我都会跑两个脚本。

第一个脚本把整个代码库 grep 一遍,找的就是我发誓永远不做的那几样东西:任何形式的动态求值、会把明文密钥原样交回来的工具、绕开防线直接调模型厂商接口的裸模块,以及能让 AI 把数据偷偷带出去的那套致命三件套。只要其中任何一样露头,构建立刻失败,没有余地。

左右两列结构对称,共用同一条 HMAC 链:拒绝这一侧,代码先过一遍 grep,查的是不该存在的东西,一旦查到就让构建失败;知识这一侧,wiki 先过一遍 lint,查的是不该缺席的东西,一旦查出问题就让完整性校验失败;两侧都把各自的事件写进同一份防篡改日志。

第二个脚本扫的是 wiki,问的是完全相反的问题:这里本该有、却没有的是什么?一条找不到出处的论断。一个没有任何页面链接进来的概念页。两篇隔了几个月写下、结论却互相打架的文章。第一个脚本不让这套系统变成一个 CVE,第二个脚本不让它把学过的东西又忘掉。

lodos 就是围着一条规则搭起来的:重要的拒绝,都要落成构建里的一次 grep;重要的知识,都要落成一个带版本、能被查询的页面。

两者都写进同一条 HMAC 链,任何一处被动过手脚,链子当场就断。把你拒绝加进来的东西写下来,把你拒绝忘掉的东西也写下来,然后让构建在缺了任何一样时就拒绝发版。大多数 AI 产品写下的是自己想成为什么,再拿制度去守;少数产品写下的是自己拒绝成为什么,拿代码去守;最少见的那一类还会写下自己拒绝忘掉什么,再拿一个 wiki 去守:页面一走样,wiki 自己的完整性校验就通不过。只有最后这一类,做出来的护城河才会复利。

制度型护城河与棘轮型护城河的对照。左边,一句写在纸上的承诺依次经过一位新人工程师、一次重构和一段没人察觉的悄悄走样,信任曲线一路衰减到接近零;右边,grep 和 wiki lint 在每次提交时都会大声报错,同样是十二个月,信任曲线一级一级往上走。

什么会烂掉,什么不会

制度型护城河是悄悄死掉的。“我们绝不记录密钥。”“工具输出一律沙箱隔离。”“数据进 LLM 之前一定先校验。”这些句子写在 SOC 2 文档里,写下的那天全是真的。然后新人加了一行调试日志。一次重构把清洗输入的那段代码挪到了功能开关后面。句子还留在文档里,系统的行为已经对不上号了。没有一条检查报错,也没有一个人察觉。

知识型护城河死得更快。创始人凌晨两点终于想明白某个客户为什么流失,把这条判断丢进 Slack,一周之内忘得干干净净。这条教训没有落在任何查得到的地方。三个月后同一个坑再出现一次,学费重新交一遍。这是最贵的一种腐烂,因为交学费的人根本不知道自己在重复交。

两种腐烂用的是同一味药:把它绑在一个结构上,它一坏,这个结构就跟着报错。拒绝机制一旦倒退,构建就失败;wiki 里的页面一旦变成孤岛、跑偏或者自相矛盾,lint 就通不过。解法不是流程,而是一条断言:系统每一次都拿它对着自己跑一遍。

三种操作构成的循环:录入把原始资料变成一份待审的页面提案,先在差异对照弹窗里过一遍审阅,才能成为正式内容;查询给出带出处的答案,而这些答案可以直接存回成页面;lint 则负责揪出孤岛页面、没有出处的论断和互相打架的说法。下方列着 lodos 有意做的三处改动:加密的 SQLite 存储、只提案不落笔的自主权,以及一条 HMAC 链式审计日志。

Karpathy 那套模式的商业化版本

知识这一半不是我原创的。Andrej Karpathy 描述过一个只有三种操作的 LLM wiki,还打过一个比方:wiki 是可执行文件,原始资料是源码,LLM 是编译器,lint 是测试,查询是运行时。

这套模式我照抄了,但有三处是故意改的。

  • 存储。 Karpathy 用的是一堆 markdown 文件加 git。我用的是 SQLite,外加一个按分区分别加密的保险库。这笔买卖是拿互通性换机密性。真想要 Obsidian 的时候,导出一份干净的 markdown 就行。
  • 自主权。 Karpathy 让 LLM 直接落笔。我这边每一次改动都得先走提案、再走审批:改动停在一个差异对照弹窗前,AI 永远碰不到正式页面本身。每次审批我花五到十秒,换来的是它没法在我不知情的时候改写我的行为。
  • 审计。 Karpathy 留的是一份纯 markdown 日志。我留的是一条 HMAC 链,用主密码派生出的子密钥签名。日志一旦被改,下次启动时就会以断链的形式暴露出来。

这三点不是理念之争,而是把一套个人知识管理的玩法改造成商业产品时必须做的适配。骨子里的东西没变,但能卖出合规买家愿意付的价格。

两边转的是同一根棘轮

拒绝这一侧和知识这一侧,形状一模一样。一侧用 grep 找绝对不该出现的东西,另一侧用 lint 找绝对不该缺席的东西。说到底两边查的都是缺不缺:前者要求禁忌之物必须缺席,后者要求该有之物一件都不能少。两边都会大声报错,记录也都写进同一条 HMAC 链。

我向 wiki 提一个问题,如果答案够好,界面上会出现一个“存成页面”的按钮。我一点,这个答案就沉淀下来了:下次会话能找到它,同一个问题不用再问第二遍。我盯的指标是保存率,这个数字会一路往上走。整套系统里,只有 wiki 是用得越多、就实打实变得越强的那一部分。

拒绝那一侧也是一个道理。每来一个可能动摇某条不变量的新功能,就往校验脚本里加一条新的不变量。八个月里,这个脚本从七条检查长到了三十二条。每上线一项能力,都得连着一条 grep 一起上:那条 grep 专门堵死它被滥用的那条路。功能越多,代码库里被明令禁止的东西只增不减。

大多数团队把这两件事当成两码事。其实是同一套纪律,用在两种缺失上:什么绝不该存在,什么绝不该被忘掉。

它的代价,以及为什么代价本身就是重点

我被拖慢的地方很集中,就两类:和拒绝机制硬碰硬的功能,以及想绕开 wiki 的重写。有位重度用户想要一个内联的表达式求值器,不行。有位客户想要一个能把明文密钥取回来的工具,不行。将来的我一定会嫌审批循环慢、想把它拆掉,构建不会答应。

我的发布说明和任何一个竞品都长得不一样:提到拒绝了什么的次数,和提到做了什么一样多。我讲不出那种漂亮干净的增长故事:那种故事得把我有意不做的事全部略过。

我换回来的东西放不进落地页,只在一场跟受监管客户的销售对话里管用。对那位客户来说,“我们做不到”和“我们保证不会”是两种完全不同的话。合规负责人看一条防篡改的 HMAC 链,和看一份 markdown 日志,态度也是两样。今天在写 wiki 的这位创始人,是在写给六个月后急着要答案的自己看。这些人里,没有一个是靠营销话术能打动的。

五个零件先摊成卡片,再连成一张图:代码仓库接上拒绝这一侧的不变量校验脚本,wiki lint 守着知识这一侧,两边都写进共用的那条 HMAC 链;而在只提案不落笔的循环里,任何一次改动都要先过差异对照弹窗,才能成为正式内容。

五个你可以直接搬走的零件

它们互相不依赖,谁都能单独拿走。合在一起,才是完整的那套打法。

  1. 一个声明式的工作流引擎,里面不含任何形式的表达式求值。
  2. 一个双存储的密钥保险库,AI 在机制上根本无法把明文密钥交出来。
  3. 一份构建期运行的不变量校验脚本,每加一条检查,都要先故意破坏一次、确认它真的会拦下来。
  4. 一个 wiki,实现 Karpathy 的那三种操作,日志封进 HMAC 链。
  5. 一套只提案、不落笔的学习循环,AI 永远改不了自己的行为。

顺序随便。它们唯一共通的是那道纪律:把你拒绝上线的东西和你拒绝忘掉的东西都写下来,在构建里对两者都下断言,然后故意去违反一次,看断言是不是真的会拦住你。剩下的全是工程活。

下一个循环画成六步链条:AI 观察对话,发现自己有一处技能缺口,起草一份提案;提案停在一个标着“关卡”的左右对照差异弹窗前,由人来批准或驳回,之后才可能成为正式内容并上线;每一份提案、每一次决定,都会写进 HMAC 链。

下一篇预告

最后那个循环,是最难做对、也花时间最久的一个。AI 一边听着对话,一边留意自己的技能清单哪里该补,然后提交一份修改提案。我批准,或者驳回。这次修改走的是和每一份 wiki 提案完全相同的那道差异对照弹窗。AI 永远不能自己改写自己的行为。提案和批准之间的那道关卡,才是真正的护城河。下一篇就讲这个循环,还有它行为提示词里的一小段话:正是那段话,让整项工程投入变得值得。

加入等候名单

moatwiki安全architecture

在你自己的设备上运营你的公司。

加入候补名单,lodos 开放时第一时间使用。