donumAI
软件交付的阶段引擎 —— 智能体负责撰写,但在你点头之前什么都不会往前走
donumAI 源于一个非常具体的不适:那些承诺「用 AI 写项目文档」的工具,产出的是看上去合理的文字,却没有任何办法判断它是否仍然成立。三月生成的架构文档就那样躺着,和第一天一样令人信服,而到了五月需求已经变了,却没有人察觉。donumAI 从相反的假设出发:难的不是生成,而是在项目一旦推进时,把六份彼此矛盾的文档维系在一起。产品是一台引擎,带着一个软件项目走过六个阶段——需求发现、估算、领域设计、架构、实施计划、测试计划——并在每一次交接时,把一个人放在智能体所写内容的面前。
## 六个阶段与关卡
每个阶段产出一份带版本的产物,由若干章节组成。智能体负责撰写,但并不负责收尾:评审是逐章节进行的,对每一节都可以批准、评论或要求修改。只有当某个阶段的关卡获得批准,下一个阶段才会解锁——这不是走形式,而是一把真正的锁。这正是「一个会产出的助手」与「一个会推进的流程」之间的区别。
## 当上游发生变化时
donumAI 背后的赌注既狭窄又机械:为每份文档留版本,记录其中每一部分来自哪个决策,并在某个输入发生变化的那一刻,把下游所有依赖它的内容标记为「已过期」。偏移并不会因此消失——它只是不再隐形,而这恰恰是问题的绝大部分。追溯链接不是由模型推断出来的:它们由数据投影而来,因为推断出的链接自带错误率,而从那一刻起整条链路都会背上这个错误率。
## 确定性规则先于评分量表
每份产物在抵达关卡之前都要经过一层校验。原则是:任何能够以类型化字段捕获的属性,都要脱离模型的判断,进入结构性规则——是否存在、基数、类型、是否属于某个封闭集合。结构性规则不消耗任何一次调用,不会在两次运行之间发生变化,也不会被写得漂亮的文字说服。只有剩下的部分——那些无法再化约的语义判断——才交给评分量表。
## Markdown 永远不是输入
这是支撑其余一切的不变式,也是我最看重的一项技术决定。Worker 不产出文本:它通过一次强制的工具调用产出类型化对象。规则运行在对象之上。Markdown 只是一个投影,由一个纯渲染器生成,下游没有任何组件会去读它。如果在这个项目里我发现自己在为散文写解析器或正则,那就说明方向走错了。抽取层是一种带错误率的推断,下游所有规则都会继承这个错误,也就不再是确定性的。甚至还有一个测试在 AST 上验证这条不变式——依据返回类型而不是函数名——并且通过识别出仍留在旧模块中的历史解析器,证明它并不是瞎的。
## 方法指南
每个阶段背后都有方法文档,而这些文档可以在产品内部直接阅读:一个学习板块,解释某份产物为何是现在这个样子、什么样的决策才谈得上可锁定、以及它将依据哪些标准被评判。它们用意大利语写成,而界面其余部分是英语,这让排版成了一个实实在在的问题:长篇意大利语散文的行宽和节奏,和一个高密度界面并不相同。
## 设计与视觉识别
界面是为那些一待就是几个小时的技术人员设计的:密集的表格、冗长的文档、关卡上的决策。密度和易读性优先于装饰。这套系统叫「Slate & Ferrous」,接近单色,只有一个蓝色强调色:近黑色标记主要操作,蓝色标记状态——这样一块摆满卡片的看板依然可读,而不必让颜色同时干两份活。
## 技术栈
项目是一个 TypeScript 单仓库,其中控制面与执行面被刻意分开:API 接收请求并持有状态,而由一个独立的 worker 执行可能持续数分钟的运行任务。
donumAI 是一个个人项目,由我独自开发,并按照我给自己定下的顺序推进:先完成类型化的接线,再调用评审者并测量它的方差,然后才是其余部分。到目前为止写下的 154 条规则,在真正跑到实际生成的产物上之前,都还只是假设;而我真正在意的那张表有两列:从不触发的规则,和总是触发的规则。它们的诊断截然相反,必须放在一起看——一条总是触发的规则并不是校验层的成功,而是更上游某处需要修正的症状。