博客.
博客.
(联系方式)

保持联系

如果你想联系我,无论是讨论合作机会、分享想法,还是只是打个招呼,都可以通过以下方式找到我。

(最后更新)

版权所有 / 2026

(快捷键)C
返回文章列表
2026年9月12日

AI 生成的东西,谁来兜底?

太长不读

过去一年,我做了好几个 AI 项目:用 AI 解析需求文档的、流式生成测试用例的、做实体关系抽取的,还有配套的 CLI 工具。每个项目都翻过车,翻到最后,解法收敛成了同一句话——别让模型直接产交付物,让它产中间语义层,交付物由脚本装配,契约由脚本校验。

整理下来,有几处体会和我原来想的不太一样:一是让模型「更可靠」基本是个伪命题,确定性只能靠架构收紧到代码里;二是看起来最笨的脚本兜底,反而是整个系统里最值得信赖的部分;三是规则系统很好用,但它会腐烂,得像代码一样治理。读完这篇,大伙对「AI 生成的东西谁来兜底」这个问题,应该能有些更清晰的感知。

矛盾本身:模型不确定,交付物必须确定

大模型最大的问题是什么?不够聪明?不是。太贵?也不是。

我的答案是:它什么都知道一点,但对你的业务一无所知。更要命的是,同一个问题问两遍,答案可能不一样。

跑通一个 demo 和做出一个能用的产品,完全是两回事。demo 里答错一次,笑笑就过去了;产品里答错一次,就是一次事故。

我一开始的思路是错的:调 temperature、堆 few-shot、换更贵的模型,想让模型「更可靠」。折腾一圈发现,这些只能降低翻车概率,不可能归零。让模型可靠是模型厂商的事,工程师能做的是另一件——在架构上把确定性收紧到代码里。

具体做法一句话:模型负责理解和生成语义,脚本负责装配和校验契约。这个分工很像编译器:

角色对应编译器干什么特点
模型前端把自然语言解析成语义对象(类似 AST)不确定,但理解力强
脚本后端把语义对象装配成终态产物,并校验完全确定,可 diff

没有哪个编译器会让前端直接吐机器码。同理,也不该让模型直接吐交付物。

AI 负责生成,脚本负责兜底

语义对象:模型只产中间层

拿「AI 解析需求文档」举例。最早的方案很「端到端」:prompt 里塞个模板,让模型直接输出最终文档。结果是,今天输出的字段和昨天不一样,引用的内容时不时是它自己编的,想改一个字就得全文重新生成,出了问题都不知道该怪谁。

后来把架构改了:模型不产文档,只产语义对象。

{
  "facts": [
    {
      "kind": "requirement",
      "statement": "订单支付超时 30 分钟自动取消",
      "source_index": { "doc": "PRD", "block": 42 }
    }
  ],
  "findings": [
    {
      "category": "risk",
      "statement": "超时取消与库存回滚的时序未定义"
    }
  ]
}

三个关键设计,每一个都是踩坑踩出来的:

  1. 封闭枚举的 kind。facts 的 kind 只有 6 种,枚举之外直接拒收;findings 的 category 是开放字符串,因为风险点没法预先穷举。该封闭的封闭,该开放的开放。
  2. source_index 证据锚定。每条事实必须带指向原文第几个 block 的索引。编造的陈述拿不出证据,「幻觉」就从难发现变成了可检测。
  3. 字段级权威排序。同一字段多处来源冲突时,PRD > URD > wiki > prompt,按优先级取。这个逻辑写在代码里,不靠模型判断。

终态 JSON 由装配脚本组装,契约校验脚本校验,流水线上还挂着一组门禁脚本,任何一步不过,直接红。

门禁红了怎么办?红了是好事,说明这次语义对象不合格,打回去重跑或者修 prompt。真正可怕的不是门禁红,是没有门禁——错误一路绿灯流到下游,最后在用户那里炸开。

这套下来,交付物拿到三个性质:结构化、可追溯、可证伪。模型还是那个不确定的模型,但产物是确定的。

顺带一个意外收获:语义对象本身也是进 git 的。prompt 改了、模型升级了,重新跑一遍,diff 的是语义对象而不是整篇文档——哪条事实变了、哪条是新增的,一目了然。这层中间层一旦存在,很多原本没法做的事就顺理成章了。

流式协议:自己定协议,自己写修复

第二个例子是 AI 生成测试用例,前端要实时展示生成过程。

第一个坑:让模型输出 JSON,前端没法流式渲染。JSON 必须完整闭合才能 parse,模型吐到一半,前端只能干等。

解法有点「野」:不让模型输出 JSON,自己定一个文本协议。

@@目录: /登录模块/异常场景
@@用例标题: 密码错误超过 5 次锁定账号

每行一条指令,来一个 token 解析一个,前端逐 token 增量构建用例树,不用等完整 JSON。用户能看到用例一条条长出来,体验好了很多。

第二个坑紧随其后:各家模型的输出变体太多。中英文括号混用、SSE 截断出的半个标识、莫名其妙的多重花括号——每个模型都有自己的口音,换个模型上线就是一场灾难。

对策是两个小组件:一个格式修复器,把已知变体归一化成标准协议;一个流式渲染缓冲器,把未闭合的 markdown 语法先缓冲住,闭合了再渲染,防止页面闪烁。

这套方案谈不上优雅,但可控。协议是契约,契约归我管,修复变体的成本也归我管。把希望寄托在「模型下次会输出标准格式」上,才是真的不可控。

代价也要说清楚:协议是自造的,就意味着文档、解析器、修复器都得自己维护,每接一个新模型都要回归一遍变体集。这笔账在立项时就得算进去,它不是免费的。

协议自己定,修复自己写

确定性操作:不给模型留入口

第三个例子来自配套的 CLI 工具,环节是物料占位替换:用例里的占位符 ${data_xxx},导入平台前要替换成真实值。

这事模型能干,还干得不错。但规矩是:禁止模型逐条替换,必须走脚本。

理由很朴素。这是 100% 确定性的查找替换,没有智能的用武之地。模型做一万次只要错一次,就得把一万次全量校验一遍——用模型干确定性的事,等于把 O(1) 的校验问题变成 O(n) 的抽检问题。

同样思路的是导入前的校验脚本。背景有点坑:平台四种用例类型,各自读的字段互不重叠;后端对缺字段只打 warn、照常落库。也就是说,类型一旦错配,导入显示「成功」,但用例是空壳,打开啥都没有。

这种坑靠人 review 防不住,靠模型更防不住——它自己就是不确定性的来源。最后只能 CLI 兜底,CLI 是最后一道防线。本地事务同理:fcntl 锁、原子写、内容指纹漂移检测,全是确定性代码,一行模型的活都不留。

确定性的事不给模型留入口

规则大于模型,但规则会腐烂

最后一个例子是技术文档的实体关系抽取。按主流思路,这活应该微调个小模型。

我们没有。全部外置成 40 多条规则配置:实体类型对映射、中英双语正则、置信度权重。原因很简单:小数据量的技术文档场景,规则比模型准,而且可解释、可 diff、可回滚,出了 badcase 改一行配置就行。

但规则有自己的坑。有一次 NER 把代码里的 submitOrder 误标成了 PERSON,一个方法名被当成了人名。模型改不了,就加了个后置修正器,本质是规则补丁。后来补丁越打越多,补丁叠补丁,那堆配置慢慢没人敢动了。

教训是:规则是对抗不确定性的好武器,但它会腐烂。补丁只增不减,配置越来越像祖传代码。治理办法和代码一样:定期合并、清理失效规则、给每条规则挂上对应的案例。别把规则当配置,把它当代码管——它本来就是代码,只是换了个文件格式。

这里还有一层更本质的边界问题:规则管的是「已知的已知」,模型管的是「未知的理解」。规则腐烂的根源,往往是拿规则去补模型该干的活——每补一条,就欠一笔债。补到第十条的时候,该停下来想想,是不是这个场景的数据量已经够训练一个像样的模型了。

为什么脚本比端到端更值得信赖

可能有人觉得,又是门禁又是脚本又是规则,看起来不够「AI Native」。

我以前也这么觉得。一年下来,结论反过来了:

维度端到端 AI脚本兜底
出错了只能怀疑人生确切知道哪条契约挂了,可证伪
变更管理行为藏在权重里,没法 review脚本在 git 里,可 diff
复盘追责「模型今天状态不好」,没法修「装配脚本第 42 行错了」,能修

确定性代码是系统里最便宜的组件:不会累、不会变、不会编。把确定的部分交给它,把理解和生成留给模型,各司其职。

最后

回头看这一年,最大的转变是:不再试图驯服不确定性,而是给它修围墙。墙内让模型尽情发挥,墙的边界上用脚本把契约焊死。

模型会越来越聪明,但「交付物必须确定」这一点不会变。这道墙还得继续修,这大概就是工程的意思。