编译器是你运行过最廉价的评估 (Eval)
正在构建编程智能体(coding agents)的团队在验证上投入了真金白银。针对每一次模型升级运行精心策划的任务评估套件;对 diff 进行打分的 LLM 裁判;每次迭代都要消耗数分钟算力的沙箱测试。这一切的存在都是为了回答一个问题:模型写出的代码能运行吗?
与此同时,这些团队大多能接触到的最便宜的评估方式就躺在他们的工具链中,而他们在十年前配置它时根本没有考虑过模型。它就是编译器。一个严格的类型检查器是一个免费、即时、确定性的验证器,它在智能体循环中的每一次编辑时运行——而你是否拥有它取决于你的语言选择,而不是你的评估预算。
这种重构带来了一个令人不安的后果。你的团队在多年前确定的技术栈决策——为了速度选择动态语言、类型可选、以测试作为安全网——是针对人类作者优化的。当作者变成模型时,权衡的优先级会重新排序。你团队最擅长的语言可能不再是你的智能体最安全的语言。
循环中的每一次检查都是一次评估——请按评估 定价
剥离掉术语,智能体循环就是一个“生成-验证”循环。模型提出一次编辑;某个东西对其进行检查;结果作为下一次尝试的上下文进行反馈。任何处于“检查”位置的东西都是评估,无论你是否这样称呼它。而评估在三个维度上有所不同,这些维度在循环内部会产生剧烈的复合效应:成本、延迟和确定性。
LLM 裁判消耗 token,耗时数秒,并给你一个概率性的意见。测试套件消耗算力,耗时数秒到数分钟,且只能覆盖到有人想到去编写的行为。运行时 QA——部署并观察——的代价是线上事故。而类型检查器每次运行的成本为零,在几毫秒到几秒内返回,并且是完全确定性的:相同的代码每次都会产生相同的结论,并附带文件、行号和原因。
最后一个维度比看起来更重要。智能体是不断迭代的。一个有 5% 概率出错的验证器并不会让你的循环效率降低 5%——它会让智能体去追踪虚假的失败,或者更糟,让受损的代码过关,从而不得不由后面更昂贵、更靠后的阶段来捕获。确定性的验证器会在迭代中积累信任;概率性的验证器则会积累噪音。
研究支持了这种便宜的评估能覆盖多少失败面。关于类型约束生成(type-constrained generation)的研究发现,利用类型系统信息引导模型可以将编译错误减少一半以上,并在合成、翻译和修复任务中将函数正确性提高 3.5 到 5.5%——这一结论在 2B 参数到 34B 参数的模型上均成立。模型所犯的错误在比例上极高地属于类型检查器能立即捕获的那一类 。在动态语言中,这些同样的错误并不会消失。它们只是被推迟了——运气好推迟到测试套件,运气不好则推迟到生产环境。
类型检查器知道哪些你的测试套件不知道的事
测试套件验证的是有人想到去编写的行为。而类型检查器验证的是代码库中每一次有类型的交互,包括那些没人预料到的交互——而这恰恰是模型编写的代码失败的地方。
人类作者在两次编辑之间大脑中存有代码库的心智模型。而模型只有一个上下文窗口。它会自信地调用上个 sprint 刚更名的函数,在需要经过验证的 ID 的地方传入一个原始字符串,或者只处理了数据可能具有的四种变体中的三种。这些不是逻辑错误,而是连贯性错误(coherence errors)——即编辑内容与周围代码现实之间的不匹配。连贯性正是类型系统所检查的,而且是详尽且免费的。
还有一个不那么明显的次要好处:错误消息就是反馈,而有类型的工具链能产生显著更好的反馈。从业者一致报告了相同的模式——将智能体连接到语言服务器(language server),它能在几次迭代中通过编译器错误自行纠正,因为“第 42 行,预期为 UserId,实际为 string”是一个具有可操作性的指令。相比之下,动态语言的运行时回溯(traceback)只能告诉你代码在哪里崩溃了,而不能告诉你错误是在哪里发生的——通常相隔好几个调用帧和一个序列化边界。一条错误消息能定位修 复方案;另一条则开启了一场调查。
这也是为什么“类型会降低你的速度”的论点在模型作者面前发生了反转。静态类型的历史成本是人类的按键次数和耐心——要写的注解、要处理的泛型、要满足的编译器吹毛求疵。模型不会不耐烦,它们生成注解的速度和其他任何内容一样快。类型系统权衡中的成本方以前一直是以人类的工作量来计算的。而这种货币刚刚贬值了。
你在 2015 年确定的技术栈排名刚刚重新排序了
十年前,选择 Python、Ruby 或 JavaScript 而不是 Java 或 Haskell 是一个合理的赌注:开发迭代速度占主导地位,类型仪式是开销,安全缺口可以通过测试和代码审查(review)来弥补。该等式中的每一项都假设作者是人类。改变作者,等式就会重新定价。
当模型编写大部分代码时,哪些价值上升了:
- 每一次按键的验证(Verification per keystroke)。 对人类来说吹毛求疵的严格性,对智能体来说是免费的信号。编译器强制执行的每一个约束都是模型无法跳过且你无需编写的检查。
- 构建延迟(Build latency)。 编译步骤现在处于一个每个任务运行数十次的循环中。Go 语言近乎瞬时的构建意味着智能体在几秒钟内就能得到结果;而构建需要数分钟的语言会让每一次迭代都变成智能体花你的钱去喝咖啡休息。运行智能体工作流(agentic workflows)的从业者报告称,在类 似任务上,这是几分钟和一小时会话之间的区别。
- 诊断质量(Diagnostic quality)。 Rust 著名的解释性编译器错误是为了教导人类而设计的。事实证明,它们对模型的引导效果同样出色——精确的诊断就是高质量的下一个 prompt。
- 惯用的唯一方式(One idiomatic way)。 强制格式化且只有一种主导风格的语言(如带有 gofmt 的 Go),产生的训练语料库变异更小——当编写同一事物的可能方式更少时,模型生成的可靠性更高。
哪些价值下降了:
- 简洁性(Terseness)。 当按键变得免费时,更少的按键就不再重要了。
- 运行时灵活性(Runtime flexibility)。 猴子补丁(Monkey-patching)、动态分发、元编程——这些是人类专家的强大工具,但它们恰恰在你智能体需要的静态分析中捅出了漏洞。流经你代码库的每一个
Any都是这种最便宜评估无法触及的盲区。 - “我的团队在 X 语言上最快”。 对于审查(review)来说仍然相关——人类阅读智能体编写的内容——但不再是杀手锏,因为边际代码行越来越多地不再是由团队亲自输入的。
这一切并不会决出单一的赢家。Rust 最大化了约束的丰富性,但在构建时间和较小的训练语料库上付出了代价。Go 最大化了循环速度和统一性,但每次编译检查的内容较少。TypeScript 拥有巨大的语料库和不错的严谨性,但前提是你真正开启了它。重点不在于哪种语言胜出,而在于**智能体可读性(agent legibility)**现在是对比表中的一等公民,而这在 2015 年甚至连一个考量维度都算不上。
平衡力量:为什么你不该用 Haskell 重写一切
在这篇文章读起来像是在号召把所有东西都迁移到最严格的类型语言之前,有三股反向的力量——而且它们非常强大。
训练数据依然主导着原始生成的质量。 模型生成的是概率最高的代码,而概率来自于曝光。Python 和 JavaScript 海量的公开语料库意味着模型对它们的习语、库以及失败模式的了解,远胜于任何小众语言。一个写 OCaml 的模型拥有出色的编译器但缺乏先验知识;而一个写 Python 的模型拥有丰富的先验知识但只有薄弱的编译器。最佳平衡点在于语料库规模与静态校验的交集——这在很大程度上解释了为什么在重度使用 Agent 的公司中,TypeScript、Go 和 Rust 势头正盛。
人类依然需要审查代码。 理解债是真实存在的:如果你的团队无法流利地阅读 Agent 编写的代码,你只是把校验问题转换成了一个更糟糕的问题——即一段没人能审计的“看起来像那么回事”的代码。如果你的团队看不懂一套表达力极强的类型系统,它就无法通过评审环节,而评审是唯一无法通过自动化取代的评估(eval)。
验证存在于编译器之外的更多层级。 类型检查器验证的是连贯性,而非正确性。它会欣然批准一个类型正确但计算逻辑错误的函数。你依然需要通过测试来确保行为正确,通过评判模型或人工评审来确保意图准确,以及通过监控来观察现实运行情况。编译器是最廉价的评估手段,但不是唯一的——它的职责是确保那些昂贵的评估 手段永远不会遇到机器在几毫秒内就能捕获的失败模式。
如果你无法更换语言,那就调整配置参数
语言选择固然是核心决策,但大多数团队并无选择余地——他们已有现成的代码库。好消息是,“在循环中引入更廉价的评估”主要是一个你可以在现有技术栈中进行调整的参数。
如果你使用 TypeScript,默认模式与严格模式(strict mode)之间的距离,就是“装饰性类型”与“校验性类型”之间的距离。开启 strict 模式,在 CI 中禁止逃生舱类型(escape-hatch types),并精确定义模块边界的类型——这样 Agent 接触的每一个接口都变成了自我检查的。如果你使用 Python,像 pyright 或 mypy 这样的严格类型检查器,配合对公共函数的规范注解,可以将一部分运行时失败转化为即时的静态反馈。它虽不如 Rust,但能以毫秒级的成本提供确定性的信号,这比你能买到的任何评判模型都强。
无论你使用什么语言,以下三个动作都是通用的:
- 将检查器放入 Agent 的循环中,而不只是放在 CI 里。 Agent 立即看到的诊断信息是一种自我修正;而二十分钟后在 CI 运行中出现的相同诊断则是一个失败的任务。语言服务器(LSP)集成是提升 Agent 质量杠杆率最高的基础设施工作。
- 在边界处使非法状态无法表达。 使用校验过的 ID 类型代替原始字符串,使用穷尽型枚举(exhaustive enums)代替字符串类型的变体 ,在模块边缘定义狭窄的接口。每一个改动都能将一类模型错误从“运行时惊吓”转化为“带有定位的编译错误”。
- 将构建速度视为评估延迟的预算。 增量编译、局部化重新构建的模块边界、缓存——任何缩短“修改到判定”间隔的手段,都会在 Agent 执行任务的每一次迭代中产生乘数效应。
当你确实有新项目(Greenfield)的机会时——比如一个新服务、一个新工具、或者一个正要剥离的组件——请将编译器作为评估预算的一部分进行权衡。一个在每次迭代中能更快速验证更多内容的语言,是你在该代码库上运行多年 Agent 任务的基础设施。
业界在过去两年意识到 Agent 的质量本质上是一个验证问题,然后开始用最昂贵的材料构建验证体系:评判模型、沙箱、人工评审。而你工具链中确定性的检查器能以零成本完成相当一部分工作——且具备毫秒级的延迟和完美的稳定性,覆盖每一次修改。
过去,语言选择关乎团队编写代码的速度。现在,它正演变为你的工具证明代码错误的成本。内化这一点的团队将运行更多的迭代,更早地捕获失败,并且相比那些仍在为编译器免费提供的功能支付 Token 费用的团队,他们交付的 Agent 编写的代码将包含更少的意外。
- https://alexn.org/blog/2025/11/16/programming-languages-in-the-age-of-ai-agents/
- https://arxiv.org/abs/2504.09246
- https://appliedgo.net/spotlight/the-best-language-for-ai-assisted-coding/
- https://getbruin.com/blog/go-is-the-best-language-for-agents/
- https://arxiv.org/abs/2508.11126
