Anthropic 用 Claude 与属性测试在 Python 生态中找 bug
Finding bugs across the Python ecosystem with Claude and property-based testing
Anthropic 研究团队开发了一个基于 Claude Code 自定义命令的智能体,通过阅读类型注解、docstring 和注释推断代码应满足的属性,再用 Hypothesis 编写属性测试,在 NumPy、SciPy、Pandas 等 100 多个热门 Python 包中发现数百个潜在 bug。
Anthropic 用 Claude 智能体在 NumPy、SciPy、Pandas 等包中自动写属性测试找 bug,并给出人工复核后的有效率与已合并修复
Muhammad Maaz1,2, Liam DeVoe3, Zac Hatfield-Dodds2, Nicholas Carlini2
1MATS, 2Anthropic, 3Northeastern University
我们开发了一个能够高效识别大型软件项目中的错误的代理。为此,我们的代理推断出代码中应该为真的通用属性,然后通过应用基于属性的测试——一种类似于模糊测试的技术——我们能够发现顶级Python包如NumPy、SciPy和Pandas中的错误。经过广泛的 manual validation,我们正在将发现的这些错误报告给开发者,其中一些错误已经被修复。
更多信息,请阅读完整的 论文,查看 GitHub仓库,或浏览我们在 网站 上发现的错误。
引言
确保程序没有错误是软件工程中最具挑战性的方面之一。尽管开发者尽了最大努力,但错误仍然经常存在。测试代码最常见的方法是基于 示例的测试:开发者编写一个特定的用例示例,然后验证实际输出是否与预期输出匹配。例如,开发者可能会验证对列表 [2, 10, 5, 4] 调用排序函数会返回输出 [2, 4, 5, 10]。然而,用这种方法彻底覆盖程序是具有挑战性的:错误经常存在于开发者未测试的边缘情况。毕竟,如果开发者没有考虑测试一个边缘情况,那么开发者很可能在实现中也没有考虑该情况!
相比之下,基于属性的测试 是一种旨在测试代码的通用属性是否对所有(或大多数)输入都成立的软件测试范式。开发者指定关于程序的属性或不变量——例如,JSON反序列化是序列化的逆操作——以及属性接受的输入类型描述(例如,任何可序列化的JSON对象)。基于属性的测试框架然后通过使用类似于模糊测试的技术自动生成有效输入作为测试用例来搜索该属性的反例。由于开发者指定的是通用输入域,而不是单个测试用例,因此基于属性的测试使开发者从考虑每个边缘情况中解放出来,并允许他们在更高的抽象层次上工作。
在我们的论文中,我们在2025年NeurIPS 深度学习用于代码 研讨会上展示了一个AI代理,该代理是 MATS 项目的成果,能够自主地为现有代码编写基于属性的测试。我们指导代理通过读取类型注解、docstrings、函数名和注释来发现属性。然后代理使用Hypothesis编写相应的基于属性的测试。
对于这项工作,我们专注于识别错误的问题,而不仅仅是安全漏洞。许多导致安全漏洞的逻辑错误类别都适合基于属性的测试。例如,在最近的 博客文章 中,我们专注于识别 智能合约 中的错误;几乎所有这些漏洞都是逻辑错误的结果。未来,我们设想可能能够应用我们这里描述的技术,在部署前主动识别错误。
我们使用我们的代理发现了流行开源 Python 仓库(如 NumPy、SciPy 和 Pandas)中数百个潜在错误。为了负责任地披露这些错误并确保我们不无谓地给维护者带来负担,我们仔细审查了每个错误。
我们使用的审查过程比我们通常对自身代码审查实施的要费力得多,但我们强烈倾向于减少误报的数量。我们的流程如下:首先,我们只选择最高优先级的错误进行审查。我们将这些潜在错误发送给三位专家进行审查(平均每个错误一小时)。然后,我们丢弃了任何三位人工审查员对其有效性不确定的错误。最后,我们(这篇博客的作者)手动审查了每个候选错误。只有当我们也对它的正确性有信心时,我们才会手动向仓库维护者提交问题。我们已经提交了其中一些错误报告,并且正在处理提交更多错误。
我们已经公开了所有我们的数据,包括尚未验证的错误,并且为了完整性,甚至我们确定为无效的错误,以便维护者可以在这个 网站 上查看它们。在未来几周内,我们打算提交其中许多剩余的错误(在进一步验证后),以及将我们的项目扩展到其他 PyPI 项目。
测试代码的两种方法。基于示例的单元测试验证特定的手动指定输入。相比之下,基于属性的测试指定一个通用属性(例如,排序列表的定义),并依赖于框架自动构建可能失败该属性的输入。
我们的基于属性的测试代理
我们的基于属性的测试代理作为一个自定义的 Claude 代码命令构建。该代理接受一个参数,指向特定的目标,无论是单个 Python 文件(例如,normalizers.py)、一个模块(例如,numpy、scipy.signal)还是一个函数(例如,requests.get、.loads)。然后该代理遵循以下流程来识别潜在错误:
- 读取和理解目标,通过阅读代码、拉取相关文档以及探索它与代码库其他部分的关系。
- 根据这些发现提出属性。
- 在 Hypothesis 中编写相应的基于属性的测试。
- 运行测试并反思:如果它失败了,测试是否真正发现了错误,或者测试需要调整?如果它成功了,测试是否测试了任何有价值的东西,或者它是否只是琐碎的?
- 如果代理确信它已经发现了一个真正的错误,它会输出一个格式化的错误报告。

代理的工作流程。
为了帮助代理执行长程多步推理,我们指导它使用待办事项列表来跟踪其进度。
在设计代理时,优先考虑的是减少误报的数量——根据我们的个人经验,有用的开发者工具会最大限度地减少向开发者呈现的不正确报告。自我反思循环有助于减少误报,将任何属性都基于目标的明确使用和文档也可以帮助减少误报。例如,在代理运行之一中,代理编写了一个首先通过的属性。但是,在自我反思后,代理意识到它将整个测试包裹在一个 try-catch 块中。删除后,测试失败了,代理发现了一个错误。我们还观察到与 Sonnet 4 相比,在 Opus 4.1 和 Sonnet 4.5 中自我反思有显著改进。
示例运行
为了展示代理如何通过测试目标来工作,我们展示了一个改写后的转录文本,其中 Claude 识别了 numpy.random.wald 实现中的一个错误。代理首先调查该函数,它的签名,它的文档字符串,甚至现有的测试:
请注意,修复不正确;当我们修复这个错误时,我们追踪了错误的来源到一个数值不稳定的计算;参见我们合并的修复 这里。
在真实的 PyPI 软件包中寻找错误
为了测试我们的代理在现实世界中的能力,我们策划了一个超过 100 个流行 Python 软件包的多样化集合。这些库涵盖了从数值计算到解析到数据库的各种领域。我们对每个这些软件包调用了我们的代理,并收集了所有生成的错误报告。
评估代理
在我们的评估的第一阶段,我们运行代理与 Claude Opus 4.1 在每个软件包上,并收集了所有生成的错误报告。为了评估这些错误报告,我们确定了两个标准:第一,"这是一个有效的错误吗?",第二,"这是一个有效的错误,并且是我们合理地向库维护者报告的吗?" 第二个标准比第一个更严格,因为,例如,一个错误可能是有效的,但太小而不值得提交报告。
在 984 个错误报告中,我们手动选择了 50 个进行审查。我们发现其中 56% 的报告是有效的错误,32% 是我们也会报告的有效错误。
根据这个手动审查,我们开发了一个评分标准,将错误排名为 15 分,目的是向开发者展示最有可能有效且值得修复的错误。我们使用 Opus 4.1 根据这个评分标准对所有错误报告进行评分。我们发现这个排名步骤非常有效:在得分最高的错误报告中,86% 是有效的,81% 是既有效又值得报告的。
在评估的第二阶段,我们在 10 个重要软件包的子集上使用 Sonnet 4.5 运行代理多次。我们还开发了一个评估代理,使用 Sonnet 4.5 读取代码和错误报告来检查错误的正确性和严重性,这比第一阶段使用的评分标准更复杂。最后,我们支付了 3 位专家人类审查员来评估高严重性的错误。
要阅读我们的代理找到的所有错误报告,请参见 https://mmaaz-git.github.io/agentic-pbt-site/。
维护者验证
评估任何在代码中发现错误的工具的有效性都很困难。虽然我们尽力验证错误报告的正确性,但软件包维护者仍然是最终的事实仲裁者。为了验证我们的代理找到的错误是维护者认为有效且值得修复的,我们选择了五个特别有趣的错误,并手动向它们各自的 GitHub 报告了这些错误,并附带了一个建议的补丁。在接下来的几周里,我们打算继续报告更多的错误,以验证。
numpy
numpy.random.wald 有时会返回负数,这是一个错误,因为 Wald 分布的样本应该只返回正数。这是我们上面示例运行中演示的错误。Claude 知道这是 Wald 分布的一个属性,并编写了一个简单的 PBT 来查看所有生成的样本是否都是正数。我们追踪错误到一个在代码中发生的灾难性取消,并在我们提交拉取请求时开发了一个更数值稳定的公式。如 NumPy 维护者在拉取请求中所示,我们的重新公式比以前的算法的相对误差低了近十个数量级。
补丁合并: https://github.com/numpy/numpy/pull/29609
aws-lambda-powertools
slice_dictionary() 重复返回第一个块,因为未递增迭代器。我们的代理通过识别切片然后重建字典应该返回原始字典来捕获这个问题。
补丁合并: https://github.com/aws-powertools/powertools-lambda-python/pull/7246
cloudformation-cli-java-plugin
item_hash() 对于所有列表都产生相同的 hash(None) 值,因为使用了原地 .sort() 方法,该方法返回 None。代理通过测试不同输入的哈希值应该不同来捕获这个问题。
补丁提交: https://github.com/aws-cloudformation/cloudformation-cli/pull/1106
tokenizers
EncodingVisualizer.calculate_label_colors() 缺少一个闭括号,返回无效的 HSL CSS。我们的代理通过测试输出应该匹配 HSL 颜色代码的正则表达式来识别这个问题。
补丁合并: https://github.com/huggingface/tokenizers/pull/1853
python-dateutil
easter() 在使用儒略历时,对于某些年份返回一个非星期日的日期。维护者将这种行为识别为预期行为,因为日历系统不同,并承认语义很微妙。
问题无效: https://github.com/dateutil/dateutil/issues/1437
向 python-dateutil 的报告显示了代理的一个重要局限性:从具有微妙或复杂语义的代码中推导属性仍然很困难。如果代码做出了隐含的假设,只有库维护者才能决定要测试的正确属性是什么。
结论
随着语言模型的不断改进,我们认为基于属性的代理测试可能会成为人类编写的测试越来越有价值的补充。基于属性的测试的高级语义保证使它们在开发过程中是一个自然的搭配。我们发现大型语言模型特别擅长从上下文(函数的名称、文档字符串、其他函数如何调用它等)中识别关于给定代码块应该为真的属性。这使得大型语言模型能够有效地编写高质量的基于属性的测试。
向前看,我们相信将大型语言模型应用于测试和错误查找是一个重要的研究方向。特别是随着大型语言模型在利用漏洞的过程中不断改进,有必要领先于使用大型语言模型进行利用的攻击者。
虽然我们在这项工作中不专注于补丁的自动生成,但这无疑是未来工作的一个明确方向。如果有可能(几乎)完全指定代码块的正确性属性,那么纠正错误将变得显著容易,我们相信在不久的将来,LLM 将能够有效地提出值得维护者考虑的高质量补丁。
来源: Anthropic Research · anthropic.com