跳到正文
北京时间
原文
Hacker News 热门(buzzing.cc 中文翻译)· chaychoong·· 2026-08-11精选AI 评分70

编写智能体时,哪种编程语言最合适?

AI 导读

针对“动态语言比静态语言更省 LLM token”的流行说法,作者用 GPT-5.6 Sol 让智能体实现 zstd 解码器进行实测。结果显示,medium 努力度下动态语言表现更好,ultra 下静态语言反而更优,且此前评测存在测试路径错误等缺陷。作者认为,琐碎任务上的性能无法推广到更大问题。

推荐理由

该实验将语言效率的讨论从微基准拉回实际工程任务,用Zstd和Pandoc实现揭示主流语言更可靠,可能改变开发者在AI辅助下选择技术栈时对动态语言优势的固有认知。

正文 · AI 翻译

这篇被引用得相当广泛的文章(反正我老是看到它被引用)提出,动态语言,或者那些表达事物更简洁的语言,token 效率更高。它似乎被引用得足够多,以至于 LLM 的搜索结果都认同这一点。比如,当我搜索“dynamic vs static language token cost”(不带引号)时,Google 的 AI 摘要开头就是

动态类型语言的 LLM token 成本通常低于传统的静态类型语言,因为省略显式类型声明会让代码更紧凑。

Google 的 AI 引用了同一篇文章,其中表明一些简洁的动态语言,其 token 成本可能只有 Rust、Go、C++ 等静态语言的 1/2 到 1/3。作者说

在 C(我比较过的 token 效率最低的语言)和 Clojure(效率最高的语言)之间,存在 2.6 倍的非常显著的差距。

然后他们后来试了 J,说

它以平均仅 70 个 token 遥遥领先,几乎是 Clojure(109 个 token)的一半。数组语言在避免使用奇特符号集时,可以极其节省 token。如果 token 效率最终被证明是一个关键驱动因素,这或许是语言演化的一条非常有趣的路径。

我找到的另一个关于动态语言与静态语言 token 对比的资料是这一篇,它支持同样的结论。如果你想把这当作这个关于基准测试、评估和实验设计的系列练习的第 8 部分,你可以点击那些链接,在继续阅读之前先思考一下评估相关的问题。

如果不运行我们自己的评估,第一个实验存在的一个问题是题目过于简单,从上面的引文就能看出来;一道在 J 语言中 70 个 token 就能解决、在 Clojure 中 109 个 token 就能解决的题目,根本算不上什么难题(作者使用的是 Rosetta Code)。正如我们在考察其他关于 caveman mode 的评估与我们自己的评估时所见,对于那些大部分工作量只是打印出答案的简单题目,与那些实际需要一定量"真正工作"的稍难题目,你可能会得到截然不同的结果;caveman mode 所宣称的巨大提升以及在复现中展示的效果,一旦你开始考察那些不只是需要几个 token 就能解决的题目时,就消失了。总的来说,在简单任务上的表现并不能推广。

第二个链接中的问题稍微更微妙一些,所以我们将大部分问题留到附录中讨论,但它们包括诸如其中一个测试执行了错误的路径(该路径并不存在),导致测试失败。之后的某个智能体随后将那个不存在的路径符号链接到自己的可执行文件,这在该情况下可行,但也导致之后每一个测试都运行那个智能体的可执行文件,而不是正确的可执行文件。作者试图就 Rust 出现了一些失败意味着什么得出结论,但实际上这只意味着 Rust 的评分在那个 Go 智能体将该损坏测试上的所有评分符号链接到 Go 可执行文件之前就已经运行了。

与其依赖这些评测,我们不妨试着自己跑一些评测。从这些评测以及我们上次评测练习中讨论的那些评测可以看出,很容易做出一个评测,而它实际表达的意思与评测创建者似乎以为的并不一致。毫无疑问,这些评测也不会例外,同样会有缺陷(详见下方附录)。

为了建立对事物的直觉,我喜欢在看结果之前预先登记自己的猜测1。我和朋友们预先登记的一些猜测是:

  • High confidence (95%): the overall dynamic vs. static language claim won't hold
    • 基于上述原因:这感觉类似于那个穴居人评测,随着问题规模变大,结果充其量只会被稀释
  • Low confidence (60%): static languages will be somewhat better than dynamic at ultra effort
    • 信心非常弱:在 ultra 努力程度下,harness 会更快地把反馈传给模型,从而在正确性或效率上带来某种好处,但出于各种原因,情况并非如此也是合理的,例如,我注意到 codex 在调用 Rust 编译器时,经常会犯完全相同的错误,然后不得不去修复它;也许这类事情会让假设中更快的反馈循环之类的东西显得微不足道
  • High confidence (98%): the "weird" language supremacy of something like J won't hold
    • 与关于静态 vs. 动态的整体论断相同的推理,另外还认为 AI 实验室在冷门语言上的合成数据 RL 环境投入会少得多(甚至可能为零)

Zstd

对于第一个评测,我尝试给智能体提供 zstd RFC(外加勘误),并要求它们实现一个完整的 zstd 解码器(智能体被困在一个没有互联网访问的容器中)。测试并未提供给智能体。对于像 zstd 这样表面积庞大的东西,指望测试覆盖所有可能的情况并不太合理。例如,尽管 zstd 是一款经过相当充分测试的软件,我曾经在 zstd 中发现过一个数据损坏 bug。这套测试套件的目的并不是找出可能潜伏多年的极端边角情况,而是检查各种能够从 RFC 中"轻松"推导出来、理应正常工作的情形。

下图中,x 轴是成本,y 轴是正确性得分(越靠上、越靠左越好/越靠下、越靠右越差);这是 GPT-5.6 Sol 在 medium 和 ultra 两档努力程度下的平均结果。如果我们只看 medium(并忽略结果在不同任务上往往差异巨大的事实),我们可能会得出类似 Alderson 评测那样的结论,即在使用 LLM 时动态语言更高效、更优,因为(忽略相对冷门的语言)动态语言这一簇落在静态语言那一簇的左上方(我们采用了 Alderson 对静态与动态语言的配色方案,以便一眼就能对比)。但如果我们看 ultra 档努力程度,结果就相当混杂了,有几种静态语言表现最佳,在较好的结果中静态语言比动态语言更多。

下面的图表还有一个切换开关,可以把 x 轴从成本改为时间。mame/ai-coding-lang-bench 指出,更快拿到结果是有价值的(我个人并不这么觉得,因为结果耗时足够长,我会转而同时处理其他事情,而不是干等),所以我们也可以从这个角度来看。同样,我们观察到两种语言类型都没有压倒对方,尽管在这个特定任务的中等努力程度下,最好的动态语言结果再次优于最好的静态语言结果(不过,它们同样相当接近)。

我们可以观察到,正如我们把完全琐碎的“穴居人模式”评测与一个不那么琐碎的“穴居人模式”评测相比较时那样,在琐碎评测中成立的非常强的关系并不能推广到这个更大的案例。与那里情况一样,在这些更大的评测中,性能上的极端比值消失了,除非是在我们可能预期表现很差的情况下,例如使用汇编时(对人类来说这会显著更耗时且更困难),以及使用相对冷门的语言时——在这些语言上,我们可能不会指望 AI 实验室投入精力生成合成 RL 环境数据。

请注意,这与第 1 次评测的发现相反,当时它暗示像 J 这样非常密集的语言出于效率原因可能是有意义的。也许如果你有非常大的预算,并且可以训练或微调一个模型,使其对你的小众语言有效,那么使用一种冷门(且“古怪”)的语言可能是有意义的;但如果你是 LLM 的普通用户,那么坚持使用主流语言很可能比使用冷门的密集语言更靠谱。

而事实证明,如果我们把语言流行度与该评测上的性能画成图(未展示),我们会观察到一种弱到中等的正相关:更流行的语言最终会得到更正确、同时也更便宜的解决方案。

正如我们之前指出的,关系非常密切的评测也可能给出大相径庭的结果。例如,我们在这里看到 Optimization 1 与 Optimization 2 两个评测的结果存在显著差异,当时 Optimization 1 和 Optimization 2 优化的是 wasm 中的 bzip2 压缩与解压,就评测而言,这两项任务关系相当密切。要提出一个强有力的、普适的论断,比如“动态语言比静态语言更高效”,我们就必须在许多任务上运行评测。然而,要表明像这样的论断

动态类型语言通常比传统的静态类型语言具有更低的 LLM token 成本,因为省略显式类型声明会让代码更紧凑。

充其量可能只是方向上大致成立,并不真正适用于任何具体情形,而且可能也不足以在一般意义上成立,我们只需要试几个案例,就会发现这一论断通常并不成立。在上面,我们看到在某一努力水平下,该论断似乎可能勉强算是成立,但有例外;而在更高的努力水平下,该论断似乎并不特别成立,这足以说明该论断很可能并非普遍成立——除非我们的评测存在一个会将其完全推翻的混杂因素。

Pandoc

但是,为了了解一个以不同方式呈现的、非常不同的任务(更像 TDD,而不是“阅读规格说明”),接下来的这项评测采用了 Pandoc ProgramBench 评测,并针对我们的用例进行了修改。我们不再采用 ProgramBench 所呈现的那种逆向工程任务,而是向智能体提供 ProgramBench 材料以及 ProgramBench 测试,然后根据一组留出测试对智能体进行评分,以衡量每种条件下的表现2。

在下面的结果中,x 轴同样是成本,y 轴是留出测试上的得分。

和之前一样,我们没有看到成功或成本与一门语言是静态还是动态、或者是否非常密集之间存在很强的关联。我们再次看到,相对冷门的语言往往表现不佳(尽管 Clojure 在这里比在 Zstd 上表现好得多)。此外,Assembly 的表现差得多,这似乎是意料之中的,因为我们会预期人类用 Assembly 实现 Pandoc 比实现 Zstd 处于更大的劣势,而且似乎没有强有力的理由认为 LLM 在这方面会有所不同。

这一切意味着什么?

谁知道呢?

关于使用 LLM 时什么方法效果好,我有很多疑问(例如,哪些测试技术效果好,哪些语言效果好,哪些软件架构效果好,修复 bug 的成本是否因语言而异,一般程序维护成本是否因语言而异,等等)。这些问题大多在公开数据中没有答案,而且如果 AI 实验室已经回答了这些问题,这些信息大多也没有公开。

关于某种特定语言为何适合 LLM 使用,流传的种种说法大多似乎是错的(例如,上面链接的评测中提到的 Ruby、Clojure 和 J 特别适合 LLM 的说法,以及 somewhat 常见的 Elixir 特别适合 LLM 的说法),但究竟什么才是对的,目前并不清楚。

2014 年,我们考察了关于静态类型与动态类型的文献,发现除了少数案例研究之外,综述这些文献并没有提供太多有价值的信息。举一个典型的学术研究例子,我们看到了那篇论文《静态类型系统能提升软件系统的可维护性吗?一项实证研究》,我对此评论道:

受试者被分配到的类中,他们要么必须修复现有代码中的错误,要么填写桩方法。静态类用 Java,动态类用 Groovy。在类型错误(以及相应的无方法错误)的情况下,开发者在 Java 中解决问题更快。对于语义错误,则没有差异。该研究采用了被试内设计,对 33 名受试者随机化任务顺序。一个显著的局限是,该研究避免使用"复杂的控制结构",例如循环和递归,因为这些会增加求解时间的方差。因此,所有的 bug 都是琐碎的 bug。这可以从求解任务的中位时间看出,这些时间在数百秒的量级。任务可能包含多个 bug,因此每个 bug 的时间相当低。

挑选那些避开循环和递归等“复杂控制结构”的任务,而这类任务耗时数百秒,相对于真正消耗专业程序员时间的任务而言,结果就变得毫无意义,就像我们看到的第一个评测那样,任务只消耗了高几十到低几百个 token。然而,有了 LLM,我们实际上可以给它们喂非平凡的任务,并比较它们表现如何。存在一个问题,即结果在多大程度上能泛化到不同任务,但我们在人类研究中也会遇到完全相同的问题,而且更糟(LLM 的方差很大,但人类的方差更大,因为你无法让同一个人用不同的随机种子做一堆任务)。而且,虽然花 $20 让 LLM 实现一个 Zstd 解码器,在乘以语言数量和每种语言每个条件下的迭代次数之后并不算便宜,但如果你想想雇一个能读懂 zstd RFC 并实现它的专业程序员要花多少钱,那么等效的研究根本不可能被做出来,因为成本会让它完全不可行。对于 Pandoc 任务来说,这一点更是加倍成立。

有了 LLM,很多问题已经从实际上无法回答,变成了只需一点努力和一些 token 就能回答。由于当前存在的激励机制3,我们是否很快能得到这类问题的答案并不清楚,但至少现在有可能尝试一下了。

我见过很多流传的说法,这些评测无法证实或证伪(原因如上所述:由于不同问题之间的方差,必须尝试多得多的任务),但这些评测对其中一些说法提供了一些启示,例如:

  • Languages with a lot of bad code out there (e.g., PHP) will perform worse
    • 在这些任务上似乎不成立
  • Because it's so easy to re-write now, you should use a powerful language (like Haskell)
    • 在这些任务上似乎不成立
  • You should use a popular language
    • 这一说法缺乏有力支持

对于我预先注册的猜测,我们得到的结果是

  • High confidence (95%): the overall dynamic vs. static language claim won't hold
    • 这似乎是正确的
  • Low confidence (60%): static languages will be somewhat better than dynamic at ultra effort
    • 没有足够的信息来确凿地判定这一点,但如果必须做出正确/错误的二元判断,我会判定这是错误的
  • High confidence (98%): the "weird" language supremacy of something like J won't hold
    • 这似乎是正确的
  • [from a draft reader]: "dynamic is better on small-scale, but gets overtaken by static as the size of the project grows"
    • 这些任务并不支持这一说法(在规模大得多的 Pandoc 任务与规模较小的 Zstd 任务上相比,静态语言的表现似乎并没有明显优于动态语言),但这些任务以及任务的呈现方式差异太大,因此尚不清楚这是否源于任务规模的扩展,还是源于其他差异

顺便说一句,Clojure 在 Pandoc 评测中相比 Zstd 评测提升如此之大的一个主要原因是,在 Zstd 评测中,36/40 个 medium 和 5/40 个 ultra 的 Clojure 程序出现了测试失败,因为 byte 转换在 128–255 上会抛出异常(也许本应使用 unchecked-byte?),而它们不恰当地使用了这一转换。

这是一个真实的结果,因为如果你让最好的公开可用 GPT 模型去实现 Zstd(并且大概如果你做其他可能遇到这种情况的位/字节操作任务),它会生成以这种特定方式失败的代码。如果有测试能捕获这个问题,这个 bug 会被修复,但仍然会消耗时间和 token。无论一门语言表现好坏,这类成本到处都是(例如,cargo 反复被以错误的参数调用,然后立即被捕获并修复,但我注意到,在我的实际项目中,除非你给 codex 明确指示如何调用 cargo,否则这个循环实际上会消耗相当可观的墙上时钟时间,而且很明显,这值得占用上下文窗口的空间)。

总之,所有这些都说明了为什么如果有人想就哪些语言或哪些类别的语言特别适合 LLM 做出强有力的论断,他们需要运行相当多不同的评测。如果我们深入探究为什么某个特定条件得到了某个分数,导致该分数的失败通常是某种特异性的问题,而这个问题在任务之间或设置之间的泛化程度并不总是显而易见。没有办法只看一个评测的分数,甚至五个或十个评测的分数,就对编程整体得出结论。

确实,在 Zstd 评测和 Pandoc 评测中,我们都能看到语言流行度与正向结果(更高的正确率、更低的成本、更短的挂钟时间)之间存在相关性,而且我们似乎有理由相信在其他评测中也会看到这种现象,但就此对任何特定语言下强结论都是错误的。我在查看 根据 GitHub CI 数据,不同项目出现构建失败的概率有多高时也曾给出过类似的警告,指出不同项目之间构建失败频率高低的原因各不相同,而且不应据此下强结论,因为跨项目的结果未必具有可比性(例如,如果某个项目的主分支是某种经过其他审查的发布候选分支,那么该项目的构建失败率预期会很低,但这与人们直接针对主分支进行开发的项目并不可比)。

不久之后,某个得分较高的语言的相关人士(如果我没记错的话,是 Martin Odersky 和 Scala)在推特上转发了那篇文章,并援引该语言的高排名作为该语言的一项胜利。在那时这就是一个没有根据的结论,而由于这里存在诸多变异来源,在此处对单一语言作出任何此类结论就更没有根据了。

这些数据(假设评测有效)可以驳斥一些强主张,也对另一些主张具有提示性,但由于只有两个任务,它实际上只能对语言类别具有提示性,而无法针对特定语言,因为任何特定语言都可能因某种特殊原因而在这些任务上表现得好或差,而这种原因未必能推广到其他任务。

感谢 Max Bittker、Yossi Kreinen、Aaron Levin、Alan Boll、Luke Burton、Marco Primi、Milosz Danczak 和 Justin Blank 提供的评论/更正/讨论。

附录:ai-coding-lang-bench 中的若干问题

正如我上面所说,我这里的评估是一个快速而粗糙的评估,我相信它充满了缺陷,所以我并不是想说我在本文中呈现的评估有多好、而那个评估有多差,但以下是 Endoh ai-coding-lang-bench 评估中的若干问题。

一个问题是,某些测试似乎运行了错误的可执行文件。已发布运行的设置似乎在其中一项测试中于每个候选者的目录内执行了 ../../minigit,而候选者生成的可执行文件位于 ../minigit。../../minigit 并不存在。

由于静态类型语言的正确性得分较低,该评估的作者指出“600 次运行中唯一的失败出现在 Rust 和 Haskell(两者都是静态类型,且都是相对‘困难’的语言)”,并暗示“困难的语言”,例如“C 的内存管理、Rust 的所有权模型,以及 Haskell 的 monad/纯函数特性,可能会给 AI 增加额外负担”。

然而,Rust 的失败是因为 ../../minigit 处没有可执行文件,导致测试失败。第一次 Go 运行通过执行 ln -sf minigit-go-1-v1/minigit ../minigit 并将 generated/minigit 链接到它自己的运行来“修复”了这个问题,但这意味着之后每一次执行(对每种语言而言)实际上执行的都是第一次 Go 运行的可执行文件。在针对 Rust 自己的可执行文件重新评分时(而不是让它因为尝试执行一个不存在的文件而失败),Rust 获得了满分,这推翻了“Rust 之所以失败是因为它是一种难以应对的语言”这一说法。

其他测试也有问题。例如,有两个测试的结构导致它们无论实际被检查的值是什么都会通过。其中一个测试有

  if ../minigit commit ...; then                                                                                                                                      
    COMMIT_POST_CHECKOUT=$(cat .minigit/HEAD)                                                                                                                         
                                                                                                                                                                      
    if grep -q "parent: $COMMIT1" \                                                                                                                                   
        ".minigit/commits/$COMMIT_POST_CHECKOUT"; then
      pass "checkout then new commit works"
    else
      pass "checkout then new commit works"                                                                                                                           
    fi                                                                                                                                                                
  else                                                                                                                                                                
    fail "checkout then new commit works"                                          
  fi

内层的 if 在两个分支中都有一个 pass,这意味着它几乎等同于

  if ../minigit commit ...; then
    pass
  else
    fail
  fi

内层的 if 似乎本意是要进行实际的检查,但由于一个编码错误(也许是复制粘贴错误?),该检查实际上被省略了。

此外,如上所述,智能体可以修改测试环境,第一个 Go 智能体就是这样做的,以修复一个损坏的环境。它们对测试和环境拥有完全访问权限,可以做任何事情,而且测试套件在开发过程中是可见的,没有任何留出集,这很容易导致作弊——通过特判代码的方式让程序通过测试,但生成的程序在“现实生活中”毫无用处。从高层来看,似乎就发生了这样的情况:许多程序未能实现规范的大部分内容,却通过了所有测试,这可能表明智能体“理解”了如何通过测试,并倾向于这样做,而不是去实现规范(这也可能表明测试非常薄弱,很容易通过)。

另一个问题是,Claude Code CLI 的版本并非所有运行都相同(从 2.1.66 到 2.1.68 不等)。还有少数其他类似的问题可能也很重要,但与上面提到的问题相比可能微不足道。

附录:循环中使用 medium 对比 ultra

作为一个可以比较的例子,我很好奇使用 medium 加上要求智能体持续工作会有多高的成本效益,同时我心底里还有另一个关于“Ralph loop”倡导者所说内容的问题,即在循环的每一次迭代中清空上下文窗口并再次给智能体完整提示词会更好。与上述情况一样,我在此预先注册的猜测是:

  • Zero confidence (50%): Ultra is more effective than medium in a loop
    • 我不太确定该如何看待这件事。我想,支持这一点的理由可能是,ultra 是以某种方式设计出来的,理应比在循环中反复执行 medium 更聪明。但也有可能存在某种权衡,即 ultra 是为了更快的速度而打造的,而且正如我们已经指出的,其方差非常大,所以即便 ultra 在大多数问题上胜出,在这里也可能落败;ultra 也可能针对改善实际耗时或其他某个参数做了更多优化;ultra 还有一个劣势,即它不"知道"在隐藏测试上达到正确后就停下来,而在这种设置下,达到完全正确的 medium 条件不会再次运行,这极大地有利于循环中的 medium(可以说,这更贴近人们实际使用这些模型的方式)
    • 你或许可以说这是 50% + epsilon,因为我的思路是先想到这种框架而不是反过来,但我要说,这里充其量也只是极低的置信度
  • Medium confidence (80%): continuing with context outperforms Ralph loop
    • /goal 模式等等,默认情况下不会这么做,而且据推测,Anthropic 和 OpenAI 的人已经尝试过类似 Ralph 循环的做法,并发现它们效果较差
    • 随着 harness(以及模型?)的改进,密切监控上下文窗口似乎变得没那么重要了;在 2025 年末 / 2026 年初,我在处理长时间运行的任务时常常不得不丢弃上下文窗口以避免问题,而这种情况随着时间推移变得越来越少见,但即便如此,由于我没有关注人们在说什么,我运行智能体循环时默认保留上下文,只在出现明显问题时才清空,而这似乎运行得还行,例如,我就是这样做出了世界上最强的 Azul AI,所以我不清楚在当时把每次循环迭代都清空上下文作为默认选择是否是正确的

对于这一个问题,平均而言,按单位成本计算,运行一次 ultra 似乎比反复运行 medium 更好(按单位时间计算更是如此),而延续之前的上下文则优于 Ralph。反复运行 medium 的朴素做法的问题在于,智能体会被锚定在一个糟糕的解决方案上,无法取得进展。Ralph 循环背后的理论是,你丢弃可能导致这种情况发生的糟糕上下文,但这并不能让你免于得到一个糟糕的产物。

仅从使用 LLM 的经验来看,我注意到,把一大块代码扔掉、让 LLM 从头重写,往往比让 LLM 修改它或尝试原地重写更好。Michael Malis 一直在用 Rust 重写 Postgres,并做出了重大改动,他也注意到了这一点。这也与之前提到过的一个想法相关:由于高方差(再加上这种路径依赖),如果你不介意消耗 token,往往更好的做法是多次掷骰子,然后取最好的结果。

附录:Guards of Atlantis 2

我尝试做了第三个评测,它在问题的呈现方式和问题的实际执行方式上,都更像是一种"业务逻辑"类型的评测。你可以说,Zstd 评测和 Pandoc 评测对程序员来说都是相当不寻常的任务,因为并没有多少程序员会收到像 Zstd RFC 那样写得又好又详尽的规范,也没有多少程序员会被交给一个像 ProgramBench 测试那样带有大量预先创建好的测试的问题。

这里的想法是实现一款桌游。一般来说,桌游规则是由那些并不擅长撰写清晰规范的人写的,所以实现一款桌游更像是这样一种情况:一个非程序员(或者一个不擅长写优秀规范的程序员)把任务交给别人去做。

这里的问题在于,要找到一款游戏,让我拥有一个合理的评分 oracle,同时又对 LLM 来说不是轻而易举的。例如,LLM 能够一次性搞定 Scout 和 Azul 的规则,这就让它们成了糟糕的任务。对于那些 LLM 不会立刻一次性搞定的游戏,我恰好拥有 Guards of Atlantis 2 的 oracle,因为我曾让一个 LLM 实现了一个副本,供我和朋友们游玩(这一款没有链接,因为我看不出如何做出一个不侵犯版权的界面)。后端只花了我几个小时的时间,但让规则大致正确却耗费了相当多的 LLM 时间。我喜欢把它当作一个任务,因为它的规则很棘手,其棘手方式与交付给程序员的许多问题描述一样棘手,但原则上,弄清楚正确的规则并实现它们是可能的(毕竟,人类在离线正确游玩这款游戏时,就是在隐式地做这件事)。

在桌游规则中,相当常见的情况是:严格按字面阅读规则反而是错误的,你需要运用“常识”(或查阅某种 FAQ)才能正确地执行规则(有些游戏设计师力求避免这种情况,比如 J C Lawrence,但这相当少见)。《Guards of Atlantis》就有不少这样的规则。《Guards of Atlantis》的设计师也公开表示,根本不存在所谓的规则精神或对规则的常识性解读,并主张你应当始终严格按字面阅读规则,因此也有很多情况下你需要忽略“常识”解读,严格按字面阅读规则。这种组合对 LLM 来说相当困难(而且,从我观察到的人类按设计师意图进行游戏的比率来看,这对人类来说也相当困难)。

我认为,仅仅阅读规则并正确地游玩实际上是根本不可能的(当然,这在理论上可行,但这需要知道哪些规则应按字面意思理解、哪些不应如此,而人们只能随机猜测并指望运气好,因为规则并未定义一个一致的系统,让人可以据此推断哪些规则遵循哪套元规则集)。在我实现这个游戏时,为了让我的 LLM 理解规则,我给它提供了各种资源,比如一份非官方的规则 FAQ(内容是正确的)、一份非官方的规则简版(比官方规则写得更好且正确,但不完整)、一本开局谱(可以用来测试规则,前提是假设开局谱中只包含合法着法)、Discord 上规则频道的评论等等,并让 LLM 在这些资源之间做一致性检查,同时让它理解 FAQ 和 Discord 评论的权威性高于实际印刷的规则。用我每月 $200 的个人 OpenAI/codex 账号,我让一个 LLM 用上我所有的空闲容量来运行一致性检查并修复规则。我没有仔细记录这花了多长时间,但我觉得大概是一两个月不停地做这类修复,才得到一个勉强合理、可以游玩的结果,但我不太敢信任它是正确的。

我之所以在一定程度上信任它,唯一的原因是 Pedro Oliveira 也实现过 Guards of Atlantis,而且他们采用了一种完全不同的方法(一种更标准的做法:由人类来驱动 LLM,而不是试图让 LLM 自己去弄清楚一切)。当我们对比各自的实现时,发现两边各有大约 10 个 bug。很可能还存在一些遗留 bug,即我们两边的实现都错误地做了同一件事,也可能有一些地方我们的实现不同、但检查系统没有发现,不过我认为我们两边实现的规则现在都已经相当可靠了。这就是我为什么能为这个游戏拥有一个判定基准(oracle)。

我喜欢把它当作一个任务,因为它更像你在现实世界中会遇到的那种“规格说明”——规格本身模棱两可、自相矛盾,有时甚至就是错的,然后你需要借助其他信息才能得到正确的结果。对于这个评测,为了避免把它变成一场测试 LLM 能否处理烦人格式数据的考试(比如把开局库从一组图像转换成某种结构化数据、把规则扫描件转换成文本等等),我把我指示 LLM 去提取数据的那些东西的原始版本(提取这些数据本身也需要经过各种一致性检查才能做对)以及提取出来的数据都一并给了智能体(提供原始版本是为了让 LLM 在愿意时可以对照原始版本检查提取错误)。

虽然我是用较老的模型来做这个任务的(其中一部分我用 GPT-5.1 或 5.2 完成,另一部分用 5.4 或 5.5 完成),但换成更新的模型、且不给它们我当初给老模型的那种引导时,这个任务仍然太难了。无论用哪种语言,智能体在这个任务上的得分都大约为 0。

顺便说一句,如果你好奇大语言模型(以及人类)在哪些地方会犯难,这里有几个例子。有一张卡牌的文本写着:“目标一个与你相邻的单位。攻击后:可以在另一个敌方英雄上重复一次。”

在这个游戏里,英雄是一种单位。严格按字面理解,并且完全了解规则(比如“攻击后”是什么意思等等),这句话的意思应该是:你可以攻击单个单位,也可以攻击两个英雄(毕竟,要在另一个敌方英雄上重复这次攻击,就意味着第一个单位是英雄;否则的话,那就会是另一个身为英雄的单位,而不是另一个敌方英雄)。

这张卡实际上在牌面上印了一段相当于勘误说明的文字,因为人们抱怨它表述不清;勘误写的是“(即使原目标是一个随从,你也可以重复)”。这对大语言模型(以及一些人类)来说已经够让人困惑了,但真正要命的是,还有其他卡牌使用了同样的表述结构,却没有这段更正说明。要正确地打出其他使用相同表述结构的卡牌,你需要知道:每当这种表述结构出现时,你都应该按照这张卡上的勘误来理解它。游戏设计师喜欢使用的一些表述结构,都有一种特定的非字面含义,你必须牢记在心。

另一个不应按字面显而易见方式解读的规则示例,是一张角色卡牌上写着“选择一项,或对不同的目标选择两项:A、B”。严格按字面理解,人们会期望能够对不同的目标执行 A 或 B,或者同时执行 A 和 B。但游戏精神的一部分是一条元规则:一个角色不能用一张卡牌多次攻击另一个角色,因此“可以按卡牌所述对若干不同目标同时执行 A 和 B”这种解读不可能正确。基于类似的推断以及类似表述的使用方式,这张卡牌应当被解读为“选择一项,或对不同的目标选择两项”,这可以说仍然有歧义,更清晰的写法应是“选择一项或两项(若选两项,必须针对不同目标)”。

作为人类,一旦你理解了“游戏精神是什么”,你就能解决这类问题。但按照设计,这一点并没有在规则中清晰地写下来,人们必须从 Discord 讨论中推断出来,而这似乎超出了当今模型的能力,尽管在许多专门任务上被当今模型超越的人类却能够做到这一点。

当我监督那些实现规则的 LLM 时,LLM 之所以会碰到天花板、无法收敛到完全正确的规则,原因在于 LLM 会观察到某条规则不一致且不正确。它随后会尝试修复这条规则,同时也会去修改其他东西,试图让它们变得一致且正确。这有时会让事情变得更正确,有时则会让事情变得更不正确。在把事情变得更不正确的时候,LLM 有时会修改一个原本正确的测试,把它变成一个不正确的测试,于是过了一段时间,LLM 并没有真正在提升正确性,只是在哪些规则不正确这件事上反复折腾。这还是在对该检查什么、如何检查有一些指导的情况下。没有那套指导,即便用上今天可用的更先进模型,LLM 也无法以合理的方式应对这件事。

我确信存在某种规则复杂度恰到好处的桌游,可以用来做一个不错的评测,但按定义,这会是那种需要花些功夫来构建 oracle 的东西,而我手头并没有一个规则合适的桌游 oracle(我认为这其实是可行且可扩展的,因为人们可以造出几十个甚至几百个这样的东西,所花的功夫比造一个多不了多少,于是就能为几百款游戏得到一个相当正确的 oracle,然后检查哪些游戏处于合适的难度水平,能成为今天对 LLM 来说有意思的测试;Guards 之所以实现起来费功夫,是因为并不存在一个带有回放数据的现成实现;任何依赖回放数据来测试每款游戏正确性的人,都能相对轻松地批量造出很多这样的环境)。

可以说,这有点像一个有趣的问题:给定一份清晰的规格说明,比如一套写得明明白白的规则,LLM 就能实现出比《Guards of Atlantis》更复杂的产物(我认为 Zstd RFC 更复杂,Pandoc 当然也是;甚至 Pandoc 支持的单个文档格式,比如 PDF,都比《Guards of Atlantis》更复杂)。所以问题不在于找一款规则复杂到让 LLM 吃力的游戏,而更在于找一款规则模糊或矛盾到让 LLM 吃力、但又不至于让 LLM 完全无从下手的游戏。但这是一个真实的现实问题:人类通常不太擅长写清晰的规格说明,而模型和 harness 能否处理好人类那种不清晰、自相矛盾、有时甚至就是纯粹错误的规格说明,对典型用户来说,可能比 LLM 能否根据一份像 Zstd RFC 那样写得极好的规格说明来实现某个东西、或者比 LLM 在拿到 4800 个 ProgramBench Pandoc 测试用例加文档时能否实现某个问题,更为相关。

附录:各项决策的理由

  • Testing ultra
    • 我见过有人说,你不应该真正去衡量这个,因为这是 harness 的事,不是模型的事。如果你在做改进模型或 harness 的工作,我能理解你为什么想把这些分开衡量;但在看用户如何使用东西时,很多人就是会用 codex 或 claude 以及各种内置功能和选项;某件事到底是 harness 的事还是用户的事,对他们来说其实并不相关。
  • Using codex
    • 我见过一些评测出于和上面相同的原因使用非常薄的 harness,而我使用 codex 而不是非常薄的 harness 的原因也和上面相同。
    • 同样地,在这个原始人模型评测中,我使用了 Claude 的 Opus 和 Fable,以及 Codex 搭配 GPT。
  • No internet access
    • 如果给模型联网权限,它们往往会作弊,而且有很多问题在网上搜索并不能找到解决问题的源代码,因此这使得这些评测更接近于模拟那些情况。
  • Relatively large tasks compared to a lot of benchmarks people pass around
    • 尽管我让 LLM 做大量琐碎的任务,但那些耗费我时间或 token 的事情,往往比 Alderson 评测或 Endoh 评测中的任务类型要大得多;LLM 在琐碎任务上已经足够好,以至于某些条件让它们在某个琐碎任务上稍微好一点或稍微差一点,对我来说差别不大,但对于像实现《亚特兰蒂斯守卫》这样的任务,我得花好几个小时搭建脚手架才能勉强跑起来,我就非常在意是什么让模型表现更好或更差。
  • Agent-specified prompts
    • Public evals seem to have moved to relatively thin/lightweight prompts that don't specify the task in great detail; this is said to be better because an agent setting up a task will give too much information that helps agents succeed at the task
      • 我能理解你为什么想测试那个,但同样地,我也非常关心智能体在由智能体设定的任务上表现如何,因为我让智能体执行的很多任务都是由智能体定义的;我关心智能体在两种风格下的表现,而不只是一种,而公开评测已经偏向了一种风格。
  • Zstd eval: asking agents to fix bugs without telling them the issue or the failing tests
    • In general, if you tell an agent to fix a specific thing, it will fix it, but it won't necessarily fix the class of issue; I've found that if you tell it there's an issue but don't tell it what the issue is, it sometimes does a more general thing instead of just putting in a narrow, brittle fix, so I do care about how agents behave when given instructions like this (of course you can tell agents to not just make a narrow, brittle, fix, but that often doesn't work)
      • 这感觉和我们之前在 Pandoc 留出集脚注中提到的问题有些关联,当时告诉智能体我们有一个留出集,似乎迫使智能体产出更通用、更不易碎的解决方案。

附录:这些评测存在的问题

说到性能基准测试,我做得足够多,以至于我觉得自己通常清楚自己的基准测试有哪些缺陷,能够做出知情的时间/精力与缺陷之间的权衡,并且我有相当的信心认为基准测试中存在的缺陷对我试图理解的东西并不重要。我做过的 AI 评测还不够多,无法对 AI 评测有这种直觉,所以从元层面来说,我会预期我做的任何 AI 评测都存在一些我自己不知道的缺陷。

我会预期这里有缺陷的另一个原因是,我让编程智能体来搭建这些评测,而每次我花一分钟去找问题,就至少能找到一个。这表明这些评测很可能还有更多缺陷,只要再多看看就能发现,但我想要的是更接近“快速玩具项目”级别的正确性,而不是“Gary Bernhardt”级别的正确性,所以修了几个问题之后我就停了。

当年我做验证工程师的时候,在奥斯汀参加过一个 Sun/Oracle 工程师的聚会,大概是在 2007 年前后,他们在那里用数学形式化了这样一个想法:把 bug 之间的时间转化为对芯片发布信心的一种度量。我没怎么见过人们这样做,但最近我听到 Will Wilson(Antithesis 联合创始人)提到,Antithesis 的一些人用生态学中的数学(关于稀有物种观测的文献)来估计真实 bug 率,这看起来像是那位 Sun/Oracle 工程师二十年前所做事情的一个精密得多的版本。

这个想法很酷,但当你每看一分钟就能找到一个 bug 时,你不需要什么花哨的数学来告诉你,很可能还有一大堆其他 bug。如果我是为了工作做这件事,而且我们有某种理由关心这些评测的可信度,那更仔细地审视这些问题并修复更多问题大概是有意义的(而且如果我是为了工作做这类事情,我大概会有足够的技能和经验,在指导 LLM 搭建这些评测时犯更少的错误)。但是,就回答“在使用 LLM 时,动态语言明显优于静态语言这一说法是否成立”这个问题而言,我更有几分把握认为这个说法不成立,而且还有很多其他问题看起来更有可能产生某种可付诸行动的结果(比如,哪些技术或测试库效果最好)。

我通常不会在博客上发布东西,除非我觉得它们已经比较扎实了,但这也意味着我常常把一些数据探索到足以满足自己的好奇心,然后就再也不发布结果了。在和别人聊起这些未发布的结果时,和我交谈的人往往对这些结果很好奇,即便它们还没达到我自己真正满意的标准,这似乎表明那些我没聊过的人可能也会感兴趣。从目前看到的情况来看,我估计要让我真正满意,至少还得再投入我现在花在这上面的时间的 10 倍。我现在相当忙,几个月内都看不到自己有这个时间,到那时候我不确定自己是否真的还会抽出空来发布这个。在最近的一篇帖子中,我提到将近一年前做过的一项分析,当时我试图弄清楚哪些车在事故中更有利于降低脑震荡风险,我花了一些时间弄明白这件事,进展到足以得到一个让我满意的答案,然后就再也没抽出空来做那些把结果整理到足以发布所需的收尾工作。

那项分析中有一些结果看起来"可以发表",也就是说它们有可能变成一篇正式论文(比如从实际碰撞数据中发现,HIC与速度之间的关系看起来是四次方关系(!);有一篇论文试图找出这种关系,但做了错误类型的分析,没能找到一种"O(n)"式的关系,得到的结果要模糊得多),但我从来都不太在意某个东西是一篇论文还是一篇博客文章,结果就是,我更倾向于直接进入下一项分析,而不是把分析整理到足以发表一篇文章的程度。

沿着这个方向,一个更近期的项目是:在做出超人水平的 Azul AI 之后,我尝试用一种对人时间投入要求低得多的流程,做出一个超人水平的 Splendor AI。我相信那次并没有成功,但它以相当大的优势击败了我能找到的所有其他 Splendor AI,这算是一个略有意思的结果。我想,关于桌游 AI 我已经懂得足够多,可以写点东西出来,但我主要的兴趣在于搞清楚自己能不能做出一个还不错的成果,然后我就一直去做别的项目,而不是花时间好好写一篇总结。我觉得其中一个有意思的例子是:很多你想要的性能优化实际上会改变结果,所以你不能只依赖那些能被严格验证为不改变结果的优化。但是,如果你天真地让一个编码智能体去做这些优化,并要求不降低棋力,它们会做出各种降低棋力的事情。棋力下降非常严重的情况很容易发现,但还有一些更微妙的问题,有时会导致(例如)在与自己的 AI 自对弈时棋力没有变化,但在面对人类或其他 AI 时棋力下降,所以某种用来捕捉糟糕优化的流程是必要的,而它本质上是一种带有任意性的流程,必须结合你的直觉并依赖 LLM 来设计(LLM 会非常有帮助,但也常常完全错误)。

对于我感兴趣的这类数据型项目,LLM 极大地减少了获得一个足以满足我好奇心的结果所需的精力,但据我所知,它们并没有大幅减少发布结果所需的精力(至少如果你选择手写结果,而不是让 LLM 来撰写结果,并且你希望结果既漂亮又干净的话),这意味着撰写结果会撞上一种 Ahmdhal 定律 瓶颈,所以我一直在做更多这类项目,却写出来发表的越来越少。如果说有什么变化的话,我认为写这些东西实际上花了更多时间,因为我的工作流程变了。比如说,我不再只是从 ggplot2 输出一张图,而是会做一个交互式版本,在某些方面更好看,但制作起来肯定更费时间。而且我会跑一遍 LLM 拼写/语法检查(至少到目前为止,这是我写作时用到的唯一 LLM 辅助),它会挑出一堆需要修正的问题。由于我是逐条手动查看而不是直接采纳修改建议(而且我打了很多错字),这实际上相当耗时(上一篇帖子花了一个多小时,这篇帖子花了半个多小时,尽管我甚至没有从头到尾改完,大概改到一半就放弃了)。

总之,发布这篇东西是一个实验:发布一些半成品笔记,而不是等到有了我真正想要的那种整理干净的版本再发布。如果你对此有看法,请告诉我(X Bsky Mastodon)!

我没有当前评测的 GitHub 链接。一方面,我觉得我确实应该提供。另一方面,它们一团糟,而且在发布代码之前还有一堆东西我想清理,我不知道自己会不会/什么时候能抽出时间来做这件事,而这样一来,至少我把一些东西放出来了,而不是只跟几个朋友聊聊结果,然后让结果无限期地躺在我的硬盘上?

附录:关于 Zstd 的更多细节

智能体被指示忽略性能,但超时并非无限,而且在中等条件下,一些测试用例超时了。这可以说不太公平,但这并未对得分产生实质性影响。对于非无限循环的超时,Clojure 中有 2 个测试用例(在 40 * 34 个测试中),J 中有 2 个,Tcl 中有 2 个,Factor 中有 1 个,PHP 中有 1 个。而且,考虑到最大的测试用例是 4 GiB,9000 秒(2.5 小时)的超时已经相当宽松了。未能在 2.5 小时内解码 4 GiB,意味着在 Graviton 5 核心上的隐含速率低于 0.5 MB/s,这相当慢。

以下是我在尝试让智能体完成这项设置时遇到的一些问题(而且,如上所述,发现每个问题所花的短暂时间意味着还有更多问题)

  • 最初,构建设置没有向智能体明确说明,导致某些语言在智能体做了基于向其说明的方式看似合理的事情时随机失败,但在评分时却行不通
  • 出于某种原因,负责搭建环境的智能体对某些语言施加了不寻常的任意限制,而对另一些语言则没有(例如,Rust 环境无法使用 rustfmt 或 Clippy);大多数语言——但并非全部——都存在这类情况
  • 许多测试(由智能体创建)实际上属于某种性能/压力测试,尽管智能体被指示忽略性能(我不会把在 9000 秒内处理 4 GiB 的 Zstd 视为性能压力测试)
  • 某些语言条件对智能体施加了任意指令(例如,Haskell 条件中有指令要求不使用 bytestring,并附有替代实现建议的说明)
  • 某些语言条件使用了非常古老的工具链(例如,Zig 停留在 0.10)
  • 某些语言条件提供了脚手架来帮助智能体实现 Zstd
  • 在最初的汇编条件中,智能体用 C 编写代码,然后将其编译为汇编并提交汇编代码(这使得汇编结果与其他语言的结果大致相当)
  • 某些语言条件对可用工具的描述是错误的(例如,汇编条件被告知可以使用 GDB,但 GDB 无法正常工作)

有一件事可以说并非 bug,但我还是把它移除了。其中一项测试非常难(大概只有 10% 的智能体第一次尝试就通过了测试)。在测试当前 zstd 发布版二进制文件时,zstd 二进制文件同样无法通过这项测试。阅读 RFC 后,这似乎是 RFC 中关于某个边界情况合法性的歧义。哪些语言更频繁地通过这个测试用例,呈现出相当明显的聚类,我觉得这挺有意思,但当所有其他测试都在衡量(或至少试图衡量)更直接的东西时,这似乎并不是一个很有用的衡量指标。

总之,在上面的列表(并不详尽)中,许多问题影响了很大一部分语言,有些问题不得不被多次修复。总的来说,如果把每种情况都算作一个单独的 bug,我大概修复了(让智能体修复了)100 多个这样的 bug,而且我预计还有更多。当我和 Max Bittker(他经营一家 RL 环境初创公司)交谈时,他指出

我参与过的所有评测,最终我都投入了大量的时间和精力,主要形式是阅读轨迹(或许多轨迹的摘要),然后对问题进行分类,比如“哦,这类 bug 不应该可能出现,让我们更新 X”(X 指的是提示词、测试框架/环境,或验证器)

智能体往往会敷衍了事,所以我在那里格外用心,确保问题在正确的层面得到修复,例如,对于被测智能体来说,上下文中放什么是非常敏感的(加入它必须操心的随机垃圾是不好的,最坏的情况是泄露答案),而系统其他部分在幕后修复的内容则不同。

智能体在编写评估时,对被测智能体的体验不够敏感,会直接把答案给它,或者通过把问题变成内部智能体的问题来修复(“记得不要奖励作弊 plz”)

我也在复用现有东西(仓库、游戏、工具、关卡)并围绕它们构建测试框架和验证器方面取得了很大成功,相比之下,试图通过提示词从零开始为评估造点东西就没那么顺利。

回想起来,我有点后悔做了跨语言评估。即使修复了 100 多个评估问题,我也毫不怀疑还有大量问题残留。也许这只是“这山望着那山高”的想法,我下一个评估尝试也会让我后悔,但我认为,评估不同测试技术或测试框架的效果,会比评估不同语言省事得多,而且我觉得这个话题至少同样有趣。而且,回想起来,如果我当时多手动做很多工作、少依赖智能体,事情会顺利得多。例如,我本应让智能体为一种语言生成环境,然后既让智能体检查它,也由我自己检查并修复问题,之后再为另一种语言生成环境。这样做几次之后,我可能就会有更好的设置来为其他语言生成环境(如果没有,我也可以对每种语言重复这个过程,得到更可靠的结果,而且很可能甚至不会花更多时间)。

另一点需要注意的是,语言之间许多真正的差异其实并没有被真正测试到,比如针对对抗性输入的内存安全。如果智能体在生成 C 或 C++ 代码时,比生成 Rust 代码更难做到大致正确,那这一点会被观察到;但如果用 fuzzer、valgrind 或其他工具能查出问题,那这类问题不太可能被这一小批测试捕捉到。我确实让一个智能体(简短地)检查了 C 和 C++ 代码的内存安全问题。该智能体声称它在 ASan+UBSan 下运行了 C 和 C++ 代码,并尝试了一些模糊测试输入(各 4000 个),没有发现问题,但这当然并不意味着不存在问题,也不意味着更大的代码库不会出现问题。

而且,事实上,对 Pandoc 评测做一次类似的快速内存安全检查后发现,所有 C 程序和除一个之外的所有 C++ 程序都存在内存安全问题(这些问题包括错误地解引用越界内存;一个具体的例子是,在其中一个 C 程序中,被截断的 LaTeX 表格可能导致越界内存读取)。这些问题仅用 10 秒的提示词就能被发现,这一事实表明,许多此类问题无需太多人力就能被发现并修复,但这会消耗相当多的 token,并且会使 C 和 C++ 版本的成本远超 Rust 版本,而且在做完这一切之后,你对 C 和 C++ 版本内存安全的信心仍然会低于对 Rust 版本的信心。

总之,如果你对结果分布感到好奇,medium 和 ultra 的结果如下:

我不太喜欢 ultra 的结果在这里有些饱和,但测试 ultra 的一个“问题”在于,随着问题变难,它会持续运行很长时间(例如,大多数 Pandoc ultra 运行都跑了 12 小时以上,而汇编运行的时间还要长得多),所以那些没有饱和的都是非常庞大的任务,比如 Pandoc 评测,或者在某些方面过于困难的任务,比如 Guards of Atlantis 评测。

  1. 一位草稿读者预先注册了这样的猜测:“动态在小规模上更好,但随着项目规模增长会被静态超越”。[return]
  2. 留出测试似乎是必要的,因为如果没有它们,智能体会作弊,会检测到测试输入并硬编码通过测试的输出(它们有时甚至在被指示不要作弊时也会这样做)。如果所有作弊都那么明目张胆,那倒不是问题(而且可能是一件值得衡量的有趣事情,因为智能体在不同语言之间是否差异化地遵循指令,对真实用户来说很重要),但很多作弊更加隐蔽,也更难以裁定。例如,一些智能体编写的代码从测试的结构分支出去,但随后用并非针对单一测试结果特殊处理的代码填充分支内容,而这些代码可以通过同一测试的许多变体。在从“绝对没有作弊”到“明显作弊”的整个光谱上,任何一点都有智能体尝试过。正如我们在考察 Senior SWE-Bench 时所看到的,用 LLM 对评测打分很棘手,而且很容易同时引入偏差和方差;使用留出测试集有一些问题,但它让我们避免了这一大得多的问题集合。

    一方面,这些留出测试很可疑,因为它们是由智能体创建的。其本意是创建这样一组留出测试:一个理性的人(或智能体)只要没有作弊,就应该能够通过。智能体对这组留出测试进行了审查,找出其中不合理的情况并剔除了一些,但我没有手工检查过这些,所以我认为很可能至少有一个留出测试在某种程度上是不公平的。不过,针对留出测试的总体得分足够低,所以我不太担心少数测试存在问题(如果我在一家 AI 实验室工作、正试图训练下一代模型,我会更担心这一点,但我认为这对我们这里的用例来说并不重要)。

    指示智能体不要作弊、同时设置一组留出测试,并没有阻止那种在留出测试上得分极差的公然作弊行为;但告诉智能体有一组留出测试会对它们进行评分,似乎降低了它们在智能体可见测试上取得的分数,同时提高了它们在留出测试上取得的分数(如果不告诉它们这一点,许多智能体在 Pandoc 测试上用毫无用处的脆弱代码拿到了 100%;而告诉它们存在留出测试后,在 ultra 上没有任何智能体在第一轮之后拿到 100%,但留出测试的分数大幅提升,表明泛化能力更好)。

    [return]
  3. 有各种 Substack、YouTube 频道以及其他东西,承诺告诉你 LLM 编程成功的秘诀,但花时间做真正实验的投资回报率其实并不存在。当我们研究 caveman mode 时,我们看到一位最大的编程 YouTuber 有一个视频,他们花了几分钟研究它,就断定它有效。哪怕花 15 分钟去研究它是否真的有效,相比把这段时间用来产出更多内容,投资回报率大概也是负的。

    有各种论文讨论不同的技术,这些论文有时比大多数博客文章或视频更深入,但平均而言,它们未必包含更有用的信息。例如,当我让 ChatGPT(5.6 Sol,Pro)去找关于语言在 LLM 方面有效性的讨论时,它翻出了这篇关于 token 效率的论文,其中有一个有趣的想法,但存在与我们之前讨论的原始人模式评测相同的问题:它考察的任务不够有趣,以至于结果对我这样的程序员来说并不相关。只要看看哪些文献引用了那篇论文,我们就会发现这篇由三位学者撰写的关于语言 token 效率的论文,题为《Tokenmaxxing 的最佳编程语言》,但与这篇博文相比,那篇论文只比较了四种语言,使用了更差的模型,并且使用的是小型玩具问题(来自一个叫 LiveCodeBench 的东西;用 GPT-5.5 解决这些问题的成本往往在 1000 token 量级)。无论这个评测做得多好,正如我们在这篇博文中以及在我们的原始人模式评测中所指出的,当从一个小型玩具问题过渡到我在业余项目或工作中可能真正关心的问题时,我们常常会看到截然不同的相对结果。此外,在那篇论文中,他们指出他们给出的提示词是“要测试你的程序,请精确运行 ./test.sh……这些是我唯一关心的测试”,并且他们说这是现实的,因为“我们相信这种设置是研究智能体行为的一种现实方式:在日常使用中,程序员不会向智能体隐藏他们的测试。相反,程序员会指示他们的智能体持续工作,直到所有测试通过。”但正如我们上面所指出的,这样做会导致代码脆弱,在现实世界中会失败(或者,如果你有未提供给智能体的留出测试,它会在留出测试上以极高的比例失败;这个问题无法仅靠增加几个测试来解决;或许可以通过模糊测试或基于属性的测试之类的方法来解决,但其效果如何则是另一篇文章的话题了)。我并不是说这些论文不好,也不是说这些论文中没有值得学习的有趣内容,但作为一名想知道该使用哪些技术或工具的程序员,我无法从上面链接的那类论文中获取这些信息。

    [return]

来源:Hacker News 热门(buzzing.cc 中文翻译) · danluu.com