写点什么

AI 辅助编程工具引入了全新的错误类型

  • 2025-01-22
    北京
  • 本文字数:2543 字

    阅读完需:约 8 分钟

大小:1014.02K时长:05:46
AI 辅助编程工具引入了全新的错误类型

与代码一样,机器产生的错误往往不同于人类犯下的错误。

 

当所有人都在谈论“AI”如何帮助人们解决错误时,我来分享一下 LLM 辅助编程工具是怎样给我造了一个 2024 年我找起来最费劲儿的错误吧。

 

我不会带你一起经历我“激动人心”的调试之旅,没那么多废话,咱们直奔主题。这是我在处理一些导入语句时微软 Copilot 给我引入的错误:

 

from django.test import TestCase as TransactionTestCase
复制代码

Python 的“import as”

 

这里具体是什么意思呢?先给不熟悉 Python 的读者讲一下背景,import 中的 as 关键字允许你为导入的实体赋予不同的名称。它可用于避免命名冲突,或者让代码看起来更简洁。

 

以下是 as 关键字的一些合理的用法:

# 为简洁/习惯用法:import numpy as np

# 为避免命名冲突/引入清晰度:from django.test import TestCase as DjangoTestCasefrom unittest import TestCase as RegularTestCase
复制代码

但是,上面的错误不属于这些合理的用法。事实上,这是 as 最邪恶的用法。

 

问题出在哪儿?django.test 包含多个不同的测试类,包括 TestCase 和 TransactionTestCase,它们的语义略有不同。上面那行代码导入了其中一个,但用的是另一个的名字。

 

错误解析

 

在这个例子中,两个 TestCase(正如其中一个的名称所暗示的那样)在数据库事务方面有着略微不同的语义。

 

  • TestCase 类将每个测试包装在一个事务中,并在每次测试后回滚该事务,从而提供测试隔离。

  • TransactionTestCase 类(有点令人惊讶,取决于你如何阅读这个名称)没有隐式事务管理,这使其成为依赖于应用程序的 DB 事务管理或测试其部分内容的理想测试选项。

 

那么,这里的错误在于,如果你依赖 TransactionTestCase 的语义,但实际上正在运行 Django 的默认 TestCase(因为这个奇怪的导入),那么你最终会遇到突然失败的测试。这就是发生在我身上的情况。

 

我生命中的两个小时

 

我不会让你再经历一遍我在这两个小时的调试中所遇到的一系列故障了,或者那些让我失败的测试,或者再具体讲一遍我为了避免再次陷入这个陷阱而采取的所有步骤。

 

简单总结下:在确定我的测试失败是因为数据库事务没有按应有的方式运行后,我首先在自己的代码中寻找问题,然后怀疑 Django 中存在错误,最后才发现了如上所述的这个问题。

 

我为什么开始怀疑 Django?嗯……因为我确信自己使用的是 TransactionTestCase,但从测试的行为来看,很明显 TransactionTestCase 的行为与文档中承诺的不一样。这让我怀疑 Django 中存在某种微妙的错误,然后一遍又一遍检查 Django 的源代码来排查。

 

为什么这个错误这么难发现?

 

你可能认为这个问题很容易发现,那是因为我已经在本文的第一行中给出了答案。相信我,真要自己动手去找就是另一回事了。我们来看看为什么会这样。

 

首先,请弄清楚一件事,虽然我在提交之前确实运行了测试,但我并没有在 Copilot 引入这一行后立即运行它们。所以当我终于拿到一个失败的测试时,我大约需要对比两整屏幕的文本的差异。

 

然后,我们来看看别名的使用位置。请注意,这里只是读取了 TransactionTestCase,并且精心编写的注释现在会进一步误导你,让你相信这就是你正在查看的内容。

 

class IngestViewTestCase(TransactionTestCase):# 我们使用 TransactionTestCase 的原因如下:# >Django 的 TestCase 类将每个测试包装在一个事务中,并在每次测试后回滚该事务,以提供测试隔离。这意味着程序实际上从未提交过任何事务,因此你的 on_commit() 回调将永远不会运行。# >[..]# >克服限制的另一种方法是使用 TransactionTestCase# >而不是 TestCase。这意味着你的事务已提交,并且回调将正常运行。但是[..]速度明显变慢了[..]
复制代码

 

别名误导了我,让我以为 TransactionTestCase 的用法是正确的。再加上解释 TransactionTestCase 用法的详细注释,让我浪费了很多时间去深入研究 Django 内部,而不是怀疑导入本身。

 

一个非人为错误

 

不过,让这个错误找起来这么费劲的最重要因素是,错误实在太奇怪了。

 

请注意,尽管问题是新引入的,但我花了大约两个小时来调试它。(因为我还没有提交,并且已经确定之前的提交没有问题,所以我可以运行 git diff 来查看发生了什么变化)。

 

事实上,我确实多次运行了 git diff 和 git diff --staged。但是谁会想到查看导入语句呢?导入语句是你觉得最不可能出现错误的地方。在这里你只会发现一堆最无聊、最无趣和最难变化的代码。

 

调试的前提是建立某种理解,任何理解都基于假设。一个合理的假设(LLM 出现之前)是,上述代码不可能存在,因为谁会写这样的东西?

 

你确定是 Copilot 吗?

 

是的……

 

不幸的是,我没有视频证据或对 copilot 的 MITM 请求日志来证明这一点。但 8 个月后,我依旧可以根据某些条件重现这个情况:

from django.test import Te... # copilot autocomplete finishes this as:from django.test import TestCase as TransactionTestCase
复制代码

因为我知道这个导入语句下方的代码包含 TransactionTestCase 的一些用途,但没有 TestCase 的用途,所以我可以明白一台经过填空训练的机器是怎样输出这么一行代码的。也就是说,对于某些合理的定义,这是合理的。

 

但人类没有合理的理由来写出这样的一行代码。它不是惯用的,它不是一种常见的模式,也不是一个好主意。这就让 copilot 成为了唯一合理的嫌疑人。

 

Copilot 引发的“坠机事故”

 

AI 辅助编程工具引入了全新的错误类型。

 

经验丰富的开发人员了解自己的故障模式,以及其他人的故障模式(如初级开发人员)。但 AI 为这种组合增加了一种新的故障。它自信地制造了我们从未预料到的错误,比如上面的 import 语句。

 

当我们依赖 AI 辅助编程时,我们遇到的错误并不总是我们自然而然就能预料到的。相反,它们反映了 AI 的某些怪癖,为我们的工作流程引入了新的不可预测性。对我个人而言,工具总体来说还是利大于弊,但重点在于要注意 AI 可能引入的新类型的错误。

 

那么标题中的“Copilot 引发的坠机事故”是什么意思呢?好吧,这有点像个玩笑。这个错误是由 Copilot 引入的,但这里程序并没有真的崩溃(我从未提交过这段代码)。但考虑到“Copilot”这个词的意思就是“副驾驶”,所以继续使用飞机失事的比喻实在太诱人了。

 

原文链接:

 

https://www.bugsink.com/blog/copilot-induced-crash/

2025-01-22 10:186846

评论

发布
暂无评论

使用gitflow时如何合并hotfix

Geek_pwdeic

动态路由协议(一)

我叫于豆豆吖.

11月月更

东方通Tongweb中间件Linux环境部署

@下一站

技术 中间件 linux 文件权限控制 Java core 11月月更

2022下半年《软考-系统架构设计师》备考经验分享

劼哥stone

软考 系统架构师

uni-app 性能优化实战之逻辑层条件编译的生产环境

恒山其若陋兮

11月月更 uni

JAVA concurrency -- CyclicBarrier 与 CountDownLatch 源码详解

骑牛上青山

Java 源码

JAVA concurrency -- ThreadLocal 源码详解

骑牛上青山

Java 源码

动态路由协议(二)

我叫于豆豆吖.

11月月更

算法题学习---单链表的排序

桑榆

算法题 11月月更

Redis LRU 内存淘汰算法大有玄机

码哥字节

LRU Redis 6.0

Flowable 外置的 HTML 表单怎么玩?

江南一点雨

Java spring flowable JavaEE

MUI框架的上拉加载的深入探索和实战运用

恒山其若陋兮

mui 11月月更

一次zuul版本升级产生的问题排查记录

骑牛上青山

Java spring 源码 Zuul 生产环境

分布式系统中的哈希算法

骑牛上青山

数据结构 分布式 算法 哈希

L1、L2范数理解--Ridge以及Lasso回归

Studying_swz

深度学习 11月月更

链路状态路由协议 OSPF (一)

我叫于豆豆吖.

11月月更

融云「百幄」之数字人,升级交互体验的「新同事」

融云 RongCloud

AI 通信 数字化

HIFIVE音加加:多场景音乐版权解决方案,让「用音乐」更便捷

曲多多(嗨翻屋)版权音乐

版权保护 视频后期 数字版权保护

助力车路云一体化,EMQ在车路协同领域的应用实践

EMQ映云科技

物联网 IoT emq 11月月更 车路协同

爬虫基本原理介绍、实现以及问题解决

石臻臻的杂货铺

爬虫

通过阅读源码解决项目难题:GToken替换JWT实现SSO单点登录

王中阳Go

Go golang 高效工作 学习方法 11月月更

简单时序逻辑电路

芯动大师

Verilog 11月月更 锁存器

2022昇腾AI创新大赛圆满收官,看这届评委怎么说?

极客天地

OSPF路由协议一

初学者

11月月更

流程表单初体验

江南一点雨

Java spring springboot flowable

为什么要做用户留存分析

穿过生命散发芬芳

用户留存 11月月更

一场算力集结令,国产芯片如何开启冲刺跑?

脑极体

JAVA concurrency -- ReentrantLock 源码详解

骑牛上青山

Java 源码

JAVA concurrency -- ArrayBlockingQueue源码详解

骑牛上青山

Java 源码

jvm(二)内存管理与虚拟机执行子系统

想要飞的猪

java对象内存布局 jvm加载子系统

对于Ajax在MUI框架中的用运以及单 webview 模式中的下拉刷新功能探究

恒山其若陋兮

mui 11月月更

AI 辅助编程工具引入了全新的错误类型_AI&大模型_Klaas van Schelven_InfoQ精选文章