1. 从 TypeSafe CEO 的一次访谈说起:为什么“可靠 AI”成了工程团队的新焦虑
前阵子跟几个做后端的朋友吃饭,聊到 AI 辅助编程,桌上立刻分成两派。一派觉得现在写代码已经离不开 AI 补全,另一派则吐槽 AI 生成的代码“看着对、跑起来崩”,尤其是类型系统稍微复杂一点的项目,AI 给的代码经常在编译期就炸出一堆红。这个分歧其实指向同一个问题:我们到底该怎么让 AI 在真实工程里变得可靠?
TypeSafe 这家公司可能很多做 Scala 的朋友不陌生,它背后的核心人物长期在类型系统和编译器工具链里深耕。当这样一位 CEO 出来谈 System One Model 与可靠 AI 的时候,值得认真听一听。因为这不是又一个“AI 将改变世界”的泛泛之谈,而是一个搞了半辈子类型系统的人,在思考 AI 生成代码这件事到底缺了什么。
我自己的判断是:AI 编程工具目前最大的短板,不在于它写不出代码,而在于它写出的代码缺乏“可验证性”。你让 AI 写一个排序算法,它大概率能写对;你让它在一个有几十个模块、类型依赖错综复杂的项目里加一个功能,它就开始胡编 API、乱传参数、忽略边界条件。这不是模型不够大,而是它缺少一个能约束它输出的“骨架”。System One Model 这个概念,恰恰就是在讨论这个骨架应该长什么样。
这篇文章我会围绕 TypeSafe CEO 谈到的几个核心点展开,结合我自己在类型系统、AI 编程工具链、以及实际项目里踩过的坑,把“可靠 AI”这件事拆开来讲。不管你是刚接触 AI 编程的新手,还是已经在团队里推 AI 工作流的老手,应该都能从中找到一些能直接用的思路。
2. System One Model 到底在说什么:不是模型架构,而是约束层
2.1 先厘清一个容易混淆的点:它和“系统一/系统二”思维不是一回事
很多人看到 System One Model 这个词,第一反应是心理学里那个“系统一快思考、系统二慢思考”的框架。但 TypeSafe CEO 谈的 System One Model,跟那个没有直接关系。这里的“System One”更接近于“统一系统模型”的意思,指的是用一个统一的、形式化的模型来描述整个软件系统的结构和行为,让 AI 在这个模型的约束下工作,而不是在一个自由文本的海洋里瞎猜。
我刚开始也搞混了,后来仔细想了想,这个命名其实挺讲究。它强调的是“One”——一个统一的模型。传统开发里,我们有代码、有文档、有类型定义、有测试、有配置文件,这些东西散落在不同地方,彼此之间靠人的记忆和约定来维持一致。AI 进来之后,它看到的是这些碎片化的信息,自然就容易产生幻觉。System One Model 的思路是:能不能把这些东西统一到一个模型里,让 AI 只在这个模型允许的范围内生成内容?
这个思路其实和类型系统的哲学一脉相承。类型系统本质上就是一种约束,它告诉你“这个位置只能放这个类型的东西”,编译器会帮你检查。AI 生成代码的时候,如果有一个类似的约束层,它就不能随便乱来了。
2.2 为什么说“约束”才是可靠 AI 的关键,而不是“更大的模型”
现在行业里有一种默认的叙事:模型越大越可靠。参数从百亿到千亿到万亿,好像只要够大,幻觉就会消失。但实际用下来,你会发现一个很反直觉的现象:在类型严格的代码库里,小模型加上强约束,往往比大模型自由发挥更可靠。
我拿自己项目里的一个例子来说明。我们有一个 Scala 项目,里面用了大量的 type class 和 implicit 参数。有一次我让一个当时最火的 AI 助手帮我加一个 JSON 序列化实例,它洋洋洒洒写了一大段,看起来结构很对,但编译的时候报了一堆“could not find implicit value”。原因很简单:它不知道我们项目里已经有一个自定义的 Encoder 派生机制,它按自己的理解重新发明了一套。
后来我换了一个思路:先把项目里相关的类型定义和已有的实例整理成一个精简的上下文,再让 AI 在这个上下文里生成。结果同一个模型,生成的代码一次就编译通过了。这个对比让我意识到,AI 的可靠性很大程度上取决于你给它多少“约束信息”,而不是模型本身有多聪明。
TypeSafe CEO 在访谈里也提到类似的观点:与其指望模型自己学会所有规则,不如把规则显式地表达出来,让模型在规则内工作。这其实就是 System One Model 的核心价值——它不是一个更聪明的模型,而是一个更严格的“工作台”。
2.3 从类型系统到 AI 约束层:一条被忽视的传承线
如果你写过几年强类型语言,你会发现类型系统解决的就是“可靠性”问题。动态类型语言里,你写错一个字段名,可能要到运行时才报错;静态类型语言里,编译器当场就拦住你。类型系统用一套形式化的规则,把大量低级错误消灭在编译期。
AI 生成代码面临的困境,和动态类型语言面临的困境惊人地相似。AI 就像一个“运行时才报错”的程序员,它生成的时候信心满满,跑起来才发现问题。而 System One Model 想做的,就是给 AI 装上一个“编译期”——在它输出之前,就用一套模型来检查它的输出是否合法。
这个类比我觉得特别有助于理解。类型系统不是让程序员变聪明,而是让程序员犯错的空间变小。同样,System One Model 不是让 AI 变聪明,而是让 AI 犯错的空间变小。这个思路的转变很关键:我们不需要一个全知全能的 AI,我们需要一个在明确边界内不会乱来的 AI。
3. 可靠 AI 在真实项目里的三道坎:类型、上下文、验证
3.1 第一道坎:类型信息在 AI 提示里几乎总是缺失的
大部分 AI 编程工具的工作方式是:你打开一个文件,它看到这个文件的内容,然后根据你的注释或光标位置生成代码。问题在于,一个文件里的类型信息是极其有限的。你调用了一个函数,但这个函数的定义在另一个文件里;你使用了一个类型,但这个类型的完整定义散落在好几个 trait 和 case class 里。AI 看不到这些,它只能猜。
我做过一个实验:同一个功能,分别用“只给当前文件”和“给当前文件加相关类型定义”两种方式让 AI 生成。前者的编译通过率大概三成,后者能到八成以上。这个差距不是模型能力问题,纯粹是信息完整度问题。
TypeSafe 的工具链在这方面有天然优势,因为它本来就做编译器相关的东西。编译器最清楚一个项目里有哪些类型、它们之间是什么关系。如果能把编译器的类型信息暴露给 AI,AI 的生成质量会有质的提升。这也是为什么我觉得 TypeSafe CEO 谈这个话题特别有说服力——他不是在空谈,他手里有编译器这个“类型信息金矿”。
3.2 第二道坎:上下文窗口再大,也装不下整个项目的语义
现在很多模型都号称有超长上下文,几十万 token 随便塞。但实际用下来,上下文长不等于理解深。你把整个项目塞进去,模型反而可能抓不住重点,生成出来的东西东一榔头西一棒子。
我自己的经验是:给 AI 的上下文要“精”不要“多”。与其把十个不相关的文件塞进去,不如把三个最相关的文件加上一份精简的类型摘要。这个摘要可以是你手动整理的,也可以是用工具从编译器里提取的。关键是让 AI 看到它真正需要的信息,而不是被噪音淹没。
System One Model 的思路在这里就体现出优势了:如果有一个统一的系统模型,你就不需要每次手动整理上下文,模型本身就知道哪些信息是相关的。这就像有一个随时待命的架构师,你问它任何问题,它都能从全局视角给你答案,而不是只盯着眼前这一亩三分地。
3.3 第三道坎:生成之后的验证环节,大多数团队是缺失的
就算 AI 生成了一段看起来不错的代码,你怎么知道它真的对?大部分人的做法是:编译一下,能过就行。但编译通过不等于逻辑正确,更不等于符合项目规范。
我在团队里推过一个简单的验证流程:AI 生成的代码,必须经过三层检查。第一层是编译器,这个自动跑;第二层是已有的测试套件,这个也自动跑;第三层是人工 review,重点看 AI 有没有引入项目里不存在的模式。这三层下来,能拦住绝大部分低级问题。
但说实话,这个流程还是偏手动。理想情况下,验证也应该是自动化的、模型驱动的。比如,能不能让 AI 自己生成测试用例来验证自己生成的代码?能不能用形式化方法证明生成的代码满足某些性质?这些方向都有研究在推进,但离日常可用还有距离。TypeSafe CEO 谈可靠 AI,我觉得验证这一环是他最关心的,因为类型系统本质上就是一种“生成时验证”。
4. 把 System One Model 落地到日常开发:几个可以立刻试的做法
4.1 做法一:给 AI 建一个“类型上下文文件”
这个做法特别简单,但效果立竿见影。在你的项目根目录建一个文件,比如叫ai-context.md,里面放几样东西:核心类型定义的精简版、常用的工具函数签名、项目里约定俗成的模式。每次让 AI 生成代码之前,把这个文件的内容贴到对话开头。
我自己的ai-context.md大概长这样:开头是项目用到的几个核心 trait 和 case class,每个只保留字段名和类型,去掉实现细节;然后是几个高频使用的函数签名,比如数据库访问、日志、配置读取;最后是几条“项目禁忌”,比如“不要用 var”“不要引入新的依赖”“异常必须用 Either 包装”。
这个文件我维护了大概两个月,现在已经成为团队里 AI 工作流的标准配置。新来的同事第一件事就是学会怎么用这个文件。实测下来,AI 生成代码的返工率至少降了一半。
4.2 做法二:用编译器输出反向约束 AI
这个做法稍微进阶一点,但也不复杂。当你让 AI 生成代码之后,如果编译报错,不要直接把错误信息扔回给 AI 让它“再试一次”。更好的做法是:先把错误信息里的类型不匹配点提取出来,然后把这些类型信息补充到你的提示里,再让 AI 重新生成。
举个例子,AI 生成的代码报错说“found: String, required: UserId”。你不要只说“编译报错了,请修复”,而是说“UserId 是一个包装 String 的 case class,定义是case class UserId(value: String),请确保所有用到用户 ID 的地方都用 UserId 而不是 String”。这样 AI 就能理解问题的本质,而不是盲目地加一个.toString或.toUserId。
这个做法背后的逻辑就是 System One Model 的思路:用类型系统的信息来约束 AI 的输出。编译器是类型信息的权威来源,把它用起来,AI 的可靠性会明显提升。
4.3 做法三:把“生成”和“验证”拆成两个独立的 AI 调用
很多人习惯让一个 AI 对话同时负责生成和检查,比如“帮我写一个函数,然后检查它有没有问题”。但实际效果往往不好,因为同一个模型在同一个上下文里,很难客观地检查自己刚写的东西。
我的做法是拆成两步:第一步,用一个对话专门生成代码,提示里只包含需求和类型上下文;第二步,开一个新的对话,把生成的代码和类型上下文一起给它,让它专门做 review,提示是“你是一个严格的代码审查者,请找出这段代码里所有类型不匹配、边界条件遗漏、以及不符合项目规范的地方”。
这个拆分看起来多了一步,但 review 的质量明显更高。因为第二个对话没有“我刚写的代码肯定没问题”这种心理惯性,它就是一个纯粹的检查者。这个思路其实也是 System One Model 的延伸:生成和验证用不同的“系统”,互相制衡。
5. 那些没人告诉你的坑:我在推 AI 工作流时踩过的雷
5.1 坑一:过度依赖 AI 生成,团队的类型设计能力在退化
这个坑是我最近才意识到的,而且有点严重。我们团队用 AI 生成代码大概半年之后,我发现一个现象:新来的同事在定义类型的时候,越来越随意。他们习惯让 AI 根据使用场景反推类型,而不是先想清楚领域模型再写代码。
结果就是,项目里的类型定义越来越碎,一个本来可以用一个 sealed trait 表达的概念,被拆成了五六个不相关的 case class。AI 倒是能应付,因为它只看局部,但人看起来就非常痛苦。后来我强制要求:所有核心类型必须手写,AI 只能用来生成类型的使用代码,不能用来生成类型定义本身。
这个坑的教训是:AI 可以帮你写代码,但不能帮你做设计。类型设计是领域建模的核心,这部分必须由人来把控。System One Model 再强,也得有人先定义好这个“One”是什么。
5.2 坑二:AI 对“隐式约定”的破坏力被严重低估
每个项目都有一些不成文的约定,比如“所有数据库操作必须走 Repository 层”“所有外部调用必须加超时”“日志里不能出现用户敏感信息”。这些约定不会写在类型里,编译器也检查不出来,但它们对项目健康至关重要。
AI 在生成代码的时候,完全不知道这些约定的存在。它会很自然地直接在 Service 里写数据库查询,或者调用外部 API 时不加超时。如果你不仔细 review,这些代码就会悄悄混进代码库,慢慢侵蚀项目的架构。
我的应对方法是:把这些隐式约定显式化,写进前面提到的ai-context.md里。虽然不能保证 AI 百分百遵守,但至少给它一个提醒。另外,在 CI 里加一些静态检查规则,比如用 Scalafix 或类似的工具,自动检测这些约定的违反情况。AI 可以犯错,但 CI 必须能拦住。
5.3 坑三:以为“类型正确”就等于“逻辑正确”
这是一个思维上的陷阱。因为我们在推类型驱动的 AI 工作流,团队里慢慢形成了一种“编译通过就万事大吉”的心态。但类型正确只是最低要求,逻辑正确才是目标。
我遇到过一个典型案例:AI 生成了一个转账函数,类型完全正确,编译一次通过。但 review 的时候发现,它在扣款和入账之间没有加事务边界,如果中间失败,钱就凭空消失了。这个 bug 类型系统完全检查不出来,因为类型只关心“这个函数接收什么、返回什么”,不关心“这个函数执行过程中发生了什么”。
所以我的建议是:类型约束是可靠 AI 的必要条件,但不是充分条件。在类型检查之外,必须有逻辑层面的 review 和测试。尤其是涉及状态变更、并发、外部调用的代码,人工 review 不能省。
6. 从工具链视角看:TypeSafe 这类公司为什么在这个时间点谈可靠 AI
6.1 编译器厂商的独特位置:它们手里有 AI 最缺的“结构化知识”
TypeSafe 这类公司做编译器、做工具链,它们对代码的理解方式和纯 AI 公司完全不同。纯 AI 公司看到的是 token 序列,编译器厂商看到的是抽象语法树、类型图、依赖关系。这两种视角的差异,在 AI 编程这件事上会被放大。
编译器厂商手里有一个巨大的优势:它们能精确地知道一个项目里有哪些类型、哪些函数、它们之间怎么调用。这些信息如果能够以合适的方式喂给 AI,AI 的生成质量会有质的飞跃。而且这种信息是结构化的、可验证的,不像自然语言那样模糊。
我觉得 TypeSafe CEO 谈 System One Model,本质上是在说:我们不应该让 AI 在文本层面瞎猜,而应该让它在结构层面工作。这个思路如果走通了,AI 编程的可靠性问题会有一个根本性的改善。
6.2 为什么“可靠”比“强大”更难做,也更值钱
现在 AI 编程工具的宣传口径基本都是“能写多少种语言”“能生成多复杂的代码”。但真正在工程里用过的人都知道,可靠比强大重要得多。一个只能写简单函数但从不犯错的助手,比一个能写复杂系统但经常埋雷的助手有价值得多。
可靠这件事难做,是因为它需要的不只是模型能力,还需要工程上的配套:类型信息怎么提取、上下文怎么管理、验证怎么做、错误怎么反馈。这些都不是单纯堆模型参数能解决的。TypeSafe 在这个方向上有多年的积累,它们谈可靠 AI,比一般公司更有底气。
从商业角度看,可靠也是更值钱的。个人开发者可能愿意为了“强大”忍受一些不可靠,但企业团队不会。企业要的是可预测、可维护、可审计。谁能先把可靠 AI 这件事做扎实,谁就能拿下企业市场。
6.3 对普通开发者的启示:别等工具完美,先建立自己的约束习惯
TypeSafe 的工具可能不是每个人都在用,但 System One Model 的思路是通用的。你不需要等一个完美的 AI 编程工具出现,现在就可以在自己的工作流里加入约束。
比如,每次让 AI 生成代码之前,先花两分钟整理一下相关的类型定义和项目约定。比如,生成之后不要直接接受,而是开一个新对话专门做 review。比如,把常见的错误模式记录下来,下次提示的时候提前规避。这些习惯不需要任何新工具,但能显著提升 AI 生成的可靠性。
我自己的体会是:AI 编程的可靠性,一半靠工具,一半靠习惯。工具会越来越好,但习惯得你自己养。等工具完美了再开始,你就落后了。
7. 我个人的几个实操心得:让 AI 在类型严格的项目里少犯错
先说一个最实用的:把类型定义写成 AI 容易理解的格式。Scala 的 implicit 和 type class 对 AI 来说很难理解,但如果你把它们“翻译”成更直白的描述,AI 的准确率会高很多。比如,与其给 AI 看implicit val userEncoder: Encoder[User] = deriveEncoder,不如告诉它“User 类型已经有一个 JSON 编码器,你不需要再定义,直接用就行”。
第二个心得:给 AI 的提示里,永远包含一个“反例”。比如你要让它生成一个解析函数,除了告诉它输入输出类型,再补一句“注意:输入可能为空字符串,这种情况应该返回 None 而不是抛异常”。这个反例往往能拦住 AI 最容易犯的那类错误。
第三个心得:不要在同一对话里让 AI 连续生成多个不相关的功能。上下文会互相污染,后面生成的东西会莫名其妙地带上前面功能的影子。一个功能一个对话,生成完就关掉,下次重新开。
第四个心得:定期清理你的ai-context.md。项目在演进,类型在变化,如果上下文文件不更新,AI 会基于过时的信息生成代码,反而更危险。我一般每个 sprint 结束的时候花十分钟过一遍这个文件,把废弃的类型删掉,把新增的核心类型加进去。
最后一个,也是我觉得最重要的:把 AI 当成一个需要明确指令的初级工程师,而不是一个能读懂你心思的资深专家。你给初级工程师派活的时候,会把需求、约束、边界条件都说清楚。对 AI 也应该这样。你越把它当专家,它越容易让你失望;你越把它当需要详细指导的新人,它反而越靠谱。
这个心态转变之后,我对 AI 编程的满意度提升了很多。不是因为它变强了,而是因为我不再期待它做它做不到的事。System One Model 也好,可靠 AI 也好,归根结底都是在帮我们建立这种合理的期待和配套的工作方式。工具在进步,我们的用法也得跟着进步。