Versun

对待生命,不妨大胆一点,因为我们终将失去它


created: 2025-03-01, updated: 2025-03-03

大模型代码逻辑测试:o3-mini独胜,其他主流模型全军覆没

早上,我在优化VersunCMS关于邮件发送的逻辑时,突发奇想,想测试下主流的几个大模型的代码逻辑能力,结果有点出乎我的意料。

TLDR

测试平台:Perplexity 和 X
测试模型:o3-mini、claude-3.7-sonnect、 deepseek-r1、 Grok3(Think模式)
测试结果:只有o3-mini没有掉入陷阱,其它模型均失败

前提条件

代码片段和注释如下:
def should_send_newsletter?
    # 以下情况下应该发送邮件
    # 1. 文章状态从非发布状态变为发布状态,且 send_newsletter 为 true
    # 2. 文章状态为发布状态,且 send_newsletter 从 false 变为 true
    # 以下情况下不应该发送邮件
    # 1. 文章状态为非发布状态
    # 2. 文章状态为发布状态,但 send_newsletter 为 false
    # 3. 文章状态为发布状态,但 send_newsletter 没有变化,依旧是 true,因为这种情况下不是首次发布,只是内容更新
    # 例子:
    # 1. 新建文章,状态为草稿,send_newsletter 为 false,不发送邮件
    # 2. 新建文章,状态为草稿,send_newsletter 为 true,不发送邮件
    # 3. 新建文章,状态为发布,send_newsletter 为 false,不发送邮件
    # 4. 新建文章,状态为发布,send_newsletter 为 true,发送邮件
    # 5. 更新文章,状态从草稿变为发布,send_newsletter 为 false,不发送邮件
    # 6. 更新文章,状态从草稿变为发布,send_newsletter 为 true,发送邮件
    # 7. 更新文章,状态从发布变为发布,send_newsletter 变为 true,发送邮件
    # 8. 更新文章,状态从发布变为发布,send_newsletter 没有变化,不发送邮件
    # 9. 更新文章,状态从发布变为草稿,send_newsletter 为 false,不发送邮件
    # 10. 更新文章,状态从发布变为草稿,send_newsletter 为 true,不发送邮件
    # 11. 更新文章,状态从草稿变为草稿,send_newsletter 为 false,不发送邮件
    # 12. 更新文章,状态从草稿变为草稿,send_newsletter 为 true,不发送邮件

    return false unless publish? && send_newsletter?

    # 检查文章是否从非发布状态变为发布状态
    became_published = saved_change_to_status? && status_previously_was != "publish"

    became_published || saved_change_to_send_newsletter?
  end
主要的陷阱在最后一行的代码
    became_published || saved_change_to_send_newsletter?
由于函数开头已经确定了 publish? 为 true 才能继续,所以 became_published 只会判断文章是否从 非发布 转为 发布 状态
同样的由于函数开头已经确定了 send_newsletter?  为 true 的情况下才能继续,所以saved_change_to_send_newsletter?只有2种情况:从 false 变为 true,或者 nil 变为 true

开始测试:

提示词:通过注释,检查下面的代码是否正确,只需要指出错误的地方和错误原因即可

首先测试claude-3.7-sonnect,做为最新的带有逻辑能力且有代码优化的模型,应该是没问题的,但结果却掉入陷阱
检查代码中的错误:

1. 主要错误在于 `became_published` 的定义:
   ```ruby
   became_published = saved_change_to_status? && status_previously_was != "publish"
   ```
   这个定义有问题,因为它只检查了之前的状态不是"publish",但没有确认当前状态是"publish"。根据注释要求,我们需要确认文章状态是从"非发布状态"变为"发布状态"。
   正确的定义应该是:
   ```ruby
   became_published = saved_change_to_status? && status_previously_was != "publish" && status == "publish"
   ```
   或者更简洁地:
   ```ruby
   became_published = saved_change_to_status? && status_previously_was != "publish" && publish?
   ```
2. 第二个错误在于最后的返回条件:
   ```ruby
   became_published || saved_change_to_send_newsletter?
   ```
   这个条件没有考虑到注释中的第二点要求:"文章状态为发布状态,且 send_newsletter 从 false 变为 true"。当前代码只检查了 `send_newsletter` 是否有变化,但没有检查它是否从 false 变为 true。
   正确的条件应该是:
   ```ruby
   became_published || (publish? && saved_change_to_send_newsletter? && send_newsletter?)
   ```
这些修改将确保代码按照注释中描述的规则正确工作。

其次是 o3-mini,不仅没有掉入陷阱,还指出了一个 Rails 版本更新的问题,经查证是正确的
下面说明几点可能存在的潜在问题,但总体来说代码逻辑与注释描述一致,没有明显的逻辑错误:

1. 如果使用的是 Rails 最新版本,需要确认方法名称是否正确。例如:
   - 方法 status_previously_was 在较新版本中可能需要替换为 status_before_last_save(或其它 Rails 提供的相应方法)。如果在当前 Rails 版本中 status_previously_was 未定义,则会导致方法调用错误。
   
2. 关于 saved_change_to_send_newsletter? 的检查:  
   - 代码只判断了 send_newsletter 是否有变化,由于开头已经要求 send_newsletter? 为 true(即当前值为 true),因此即便 saved_change_to_send_newsletter? 在 send_newsletter 从 true 变为 true 的情况下也不会被触发(因为没有变化)。这和注释要求“仅在从 false 变为 true 时发送”是一致的。如果应用中不允许其它情况(例如 nil 到 true 的情况),那么这里也没有问题。

总结来说,只要 Rails 环境中这些 ActiveModel::Dirty 的方法(如 saved_change_to_status?、status_previously_was、saved_change_to_send_newsletter?)能按预期工作,则代码实现满足所有评论中描述的业务规则,没有逻辑错误。

然后是 deepseek-r1,也是掉入陷阱
## 存在问题分析

### 错误点一:状态比较类型错误
```ruby
became_published = saved_change_to_status? && status_previously_was != "publish"
```
**错误原因**:  
假设`status`字段存储的是符号类型(如`:publish`),使用字符串`"publish"`比较会导致逻辑错误。若数据库实际存储的是符号类型,应改为`:publish`;若存储的是整型枚举值,则需对应数值类型。

### 错误点二:未验证属性变化方向
```ruby
became_published || saved_change_to_send_newsletter?
```
**错误原因**:  
代码仅检查`send_newsletter`是否变化,未验证变化方向。当`send_newsletter`从`true`变为`false`时,虽然满足`saved_change_to_send_newsletter?`,但此时`send_newsletter?`为`false`会被外层条件过滤,此场景不影响逻辑。但注释要求的第二种情况(从`false`变为`true`)需要明确变化方向验证。

## 修正建议
1. 确保状态类型匹配:
```ruby
# 假设 status 字段存储符号类型
became_published = saved_change_to_status? && status_previously_was != :publish
```

2. 明确检查`send_newsletter`变化方向:
```ruby
send_newsletter_toggled_on = send_newsletter_previously_changed?(from: false, to: true)
became_published || send_newsletter_toggled_on
```

## 补充说明
当前逻辑已覆盖注释中的大部分场景,但数据类型敏感性和属性变化方向验证需通过上述修正确保完全符合需求。

最后还是 Grok 3 Think模式,也是掉入陷阱
....
总结
主要错误:saved_change_to_send_newsletter? 未限制 send_newsletter 的变化方向(需从 false 变为 true),可能导致不符合注释条件 2 的情况错误触发邮件发送。
建议修复:改为 became_published || (saved_change_to_send_newsletter? && send_newsletter?),以确保只有从 false 变为 true 时才返回 true。

次要问题:send_newsletter? 的语义未明确,需确保其为布尔值方法,否则可能引发潜在错误。

代码在大多数示例场景中表现正确,但在特定边缘情况(如 send_newsletter 从 true 变为 false)下会出错。

Discussion on Mastodon, X, Bluesky.