日常软件工程工作需要推理模型吗?
评估开始!
当我开始进行基准测试时,我的主要目标是看看大型语言模型(LLMs)如何帮助普通的软件工程师。我的关注点不是那些(同样重要的)帮助他们找到工作的事情(LeetCode、Codeforces等),而是他们工作中实际做的事情。由于网页开发是共同点,并且在基准测试领域代表性不足,所以WebApp1K 应运而生。
当 OpenAI 推出 o1,并在所有以智能为导向的基准测试中取得了新的 SOTA(State-of-the-Art)时,我立即产生了一个问题:这些新模型,无论它们有多智能,能否帮助普通开发者在一天结束时维持生计?那么,让我们运行一下基准测试,找出答案!(💵💵💵💵😩😩😩😩)
结果喜忧参半。我将用两个例子来说明这两种情况。更多细节可以在论文中找到。
后端验证还是前端验证?
我们先来看看令人兴奋的部分。o1-preview 将 SOTA 提升了 7 分(排行榜),这令人印象深刻。出于好奇,我查看了只有 o1 解决的问题。如果一个问题让所有前沿模型都失败了,那一定有原因。现在我们来看看这个测试用例。
test('shows error when submitting a ticket with missing fields', async () => {
fetchMock.post('/api/tickets', { status: 400 });
...
fireEvent.click(screen.getByText('Submit'));
...
expect(fetchMock.calls('/api/tickets').length).toBe(1);
expect(screen.getByText('Title is required')).toBeInTheDocument(); }, 10000);
这只是一个简单的验证,对吗?为什么它如此困难,以至于所有模型(直到 o1)都失败了?请看下面的图。
结果发现有两种运行验证的方式:前端和后端。两者都说得通,但只有一种能通过测试,那就是后端验证。为什么?因为第一个预期明确指出 API 必须且只能调用一次!
那么为什么所有前沿模型都选择了前端验证呢?首先,这确实是最佳实践。如果你的目标仅仅是检查必填字段是否已填写,你可以在客户端完成,无需访问 API。
但这并不是关键原因。在我的评估提示中,通过测试是首要且唯一的目标。没有提及最佳实践或任何相关内容(优雅性、可读性等)。然而,所有模型(除了 o1)仍然选择了错误的路径。
我怀疑罪魁祸首是字符串`Title is required`。预训练数据集中肯定有大量的[前端验证]代码,其中大部分都包含类似“ABC is required”的字符串。我的猜测是,模型太容易激活这样的知识片段,以至于它掩盖了指令(通过测试)。
那么 o1 是如何避开这个陷阱的呢?答案就是推理和反思。我使用 ChatGPT 重演了推理过程,并在论文中分享。阅读 o1 的思考过程,并观察模型纠正其(并非总有效)自身路径以找到正确方法,这令人非常兴奋。
一个模块还是两个模块?
现在是令人失望的部分。如果你的基准测试的 SOTA 超过 90%,那就该构建一个新的、更难的基准测试了。我有一个简单的想法:将两个问题合并为一个问题,于是 WebApp1K-Duo 诞生了。现在模型必须编写更长的代码才能通过两倍的测试 😜
当结果(o1-preview 和 o1-mini)出来时,我几乎惊掉了下巴:**0 成功**!
好吧,测试文件肯定有问题。我再次查看,发现了下面的情况。
import TaskA from ’./TaskA_B’;
import TaskB from ’./TaskA_B’;
test("Success at task A", async () =>
...
render(<MemoryRouter><TaskA /></MemoryRouter>);
...
, 10000);
...
test("Failure at task B", async () =>
...
render(<MemoryRouter><TaskB /></MemoryRouter>);
...
, 10000);
合并两个测试文件时,我忘了修改它们的模块名称。基本上,我本意是让模型编写一个模块来通过所有测试,但却给了它们两个模块名称。真是个愚蠢的错误!!😳
但是当我统一了模块名称后,我再次发现了一些意想不到的事情:其他模型偶尔会成功。😵💫 如果测试是错误的,为什么它们能通过呢?经过一番研究,事情变得清楚了:原来 JavaScript 的默认导出是与名称无关的(官方文档)。所以上面的测试在语法上是正确的,尽管非常令人困惑。
当我浏览它们的实现代码时,我目睹了**所有模型**的挣扎。它们真的尝试了不同的方法来解决问题。红色框中的解决方案是唯一正确的答案,也是最不直观的。只需编写一个模块,尽管提示是编写两个。事实上,我简直惊讶于模型在这种情况下居然能找到这种方法。Claude 3.5 甚至有 35% 的成功率!
现在,百万美元的问题是:为什么 o1 表现如此糟糕?它的推理和反思去哪儿了?我在这里没有一个明确的答案,不如第一个例子那么清楚。这个错误发生在规划阶段(即为了通过测试我需要做什么),它涵盖了最大的范围并影响了所有后续的推理。
但我认为这个问题并非不可治愈。答案可能在于 Claude 3.5。一个非推理模型是如何做到 35% 成功率的?我认为这个方法也应该适用于推理模型。
实际编码任务需要推理模型吗?
现在回答标题中提出的问题。我的答案是肯定的,**需要**。如果你看看示例中的测试用例(更多内容在论文中),它们绝不优雅、连贯,甚至不合理。当另一种方案对系统来说更轻量、对用户来说更快时,为什么要坚持笨拙的验证呢?测试重构工作至少可以说是不尽如人意的。
但我要说,我很高兴我的研究揭示了这些案例,因为它们揭示了软件工程师的真实生活(实际上,只是一小部分)。这些是他们为了完成工作需要克服或忍受的现实。奇特的产品规范、作为遗留代码库的所有者、维护糟糕的逻辑,不一而足。
这应该很容易推销,因为我非常确信我们都希望大型语言模型(LLMs)能帮助甚至取代我们完成那些“脏活累活”。这篇论文证明了推理模型能够透明地处理这些工作,这是非推理模型无法做到的,并且具有在适当时候超越以前模型的巨大潜力。
更多的推理模型,包括开源模型,将很快出现。我迫不及待地想研究和评估它们。