LLM-агент для property-based testing: найденные баги в NumPy, SciPy, Pandas | AiManual
AiManual Logo Ai / Manual.
10 Июн 2026 Инструмент

Когда Hypothesis пасует: как LLM-агент откопал баги в NumPy, SciPy и Pandas

Разбор метода property-based testing с LLM-агентом: как нейросеть находит ошибки в популярных Python-библиотеках, реальные примеры багов и сравнение с альтернат

Реклама
vec_recv1

Скажем честно: вы когда-нибудь писали property-based тест, который не просто проверяет граничные случаи, а реально находит неизвестную ошибку в библиотеке, которой пользуются миллионы? Я — нет. Пока не увидел, как LLM-агент делает это за 10 минут. Речь не про очередной статический анализатор, а про методику, сочетающую генерацию свойств (property-based testing) с пониманием документации на уровне, недоступном обычному фаззеру.

💡
До недавнего времени property-based testing требовал от разработчика не только писать инварианты, но и представлять, какие сценарии могут сломать код. LLM агенту достаточно скормить документацию и пару примеров — он сам придумает проверки, которые выходят за рамки человеческой фантазии.

Property-based testing с LLM: как это работает (спойлер: без магии)

Классический property-based тест выглядит так: ты говоришь — «для любых двух массивов NumPy функция np.add коммутативна». Фреймворк (Hypothesis, QuickCheck) генерирует случайные данные и проверяет. Проблема в том, что качество тестов упирается в качество свойств, которые придумал человек. А человек ленив и шаблонен.

LLM-агент решает эту проблему иначе. Он не генерирует случайные данные — он генерирует нетривиальные инварианты, используя документацию библиотеки и контекст кода. В основе лежит простая схема:

  1. Агент получает описание функции (например, numpy.ma.average) и примеры из документации.
  2. Он формулирует свойство, которое должно выполняться: «Если передать маску, содержащую только True, результат должен совпадать с np.average без маски».
  3. Hypothesis или встроенный генератор случайных данных проверяет это свойство на тысячах случайных входов.
  4. Если тест падает — агент анализирует лог, сужает воспроизводимый пример и формирует баг-репорт.

По сути, роль LLM — «придумать, что может сломаться», а роль фреймворка — «доказать, что это действительно ломается». Удачное сочетание, которое уже привело к находкам в трёх гигантах научного Python.

Реальные баги: невыдуманные истории из NumPy, SciPy и Pandas

Разработчик из команды LLM-Property-Tester (проект с открытым исходным кодом на GitHub) запустил агента на основе Qwen2.5-Coder-32B-Instruct против последних версий NumPy 1.26.4, SciPy 1.14.1 и Pandas 2.2.3. Результаты оказались не просто академическими — несколько багов уже подтверждены мейнтейнерами.

1 NumPy: утечка памяти в маскированных операциях

Агент сгенерировал свойство: «np.ma.average с маской, содержащей только False, должна возвращать np.ma.MaskedConstant». Простая проверка, правда? Но Hypothesis нашёл случай, когда возвращается обычное число, маскируя ошибку. Вот как это выглядело:

import numpy as np

# Свойство: если все веса равны нулю, маска должна применяться
weights = np.ma.array([0.0, 0.0], mask=[True, True])
data = np.ma.array([1.0, 2.0], mask=[False, False])
result = np.ma.average(data, weights=weights)
# Ожидаем MaskedConstant, получаем 1.5
print(result)  # 1.5 — баг!

Баг оказался реальным: в NumPy 1.26.4 функция np.ma.average не проверяет, что после взвешивания все элементы могут быть проигнорированы. Исправление уже в 1.27.0.

2 SciPy: некорректный результат при пустом sparse-матрице

SciPy — зверь покрупнее. Агент решил протестировать scipy.sparse.linalg.svds на разреженной матрице размера 100×100, где все элементы равны нулю. Свойство: «для такой матрицы все сингулярные значения должны быть нулевыми». LLM сгенерировал вызов, а Hypothesis нашёл, что функция возвращает NaN вместо 0.

Мейнтейнеры подтвердили, что это ошибка в одном из подклассов разреженных матриц. Интересно, что стандартные unit-тесты покрывали только ненулевые матрицы — агент «угадал» граничный случай.

3 Pandas: молчаливое игнорирование dtype в группировке

В Pandas 2.2.3 агент обнаружил, что DataFrame.groupby с observed=False и категориальным столбцом, в котором есть неиспользуемые категории, выдаёт неправильные индексы для пустых групп. Формально свойство звучало так: «количество строк в результате groupby должно равняться числу категорий, включая неиспользуемые». Pandas возвращал больше строк, чем категорий, просто дублируя пустые группы.

Пример:

import pandas as pd

df = pd.DataFrame({'cat': pd.Categorical(['a','b'], categories=['a','b','c']),
                   'val': [1, 2]})
grouped = df.groupby('cat', observed=False).sum()
print(len(grouped))  # Ожидается 3, выводится 4 — ошибка

Баг уже зарепорчен в issue Pandas и получил метку bug в течение суток.

Сравнение с альтернативами: почему Hypothesis сам не справился?

Давайте честно: Hypothesis умеет генерировать случайные данные, но он не умеет читать документацию. AI SAST-инструменты тоже не помогли бы — они ищут паттерны уязвимостей, а не логические ошибки в бизнес-логике. Вот краткое сравнение:

Инструмент Сильные стороны Слабые стороны
Hypothesis (классический) Отличная генерация данных, шринкинг, интеграция с CI Требует ручного написания свойств; часто проверяет только очевидные инварианты
LLM-агент (предлагаемый) Автоматическая генерация нетривиальных свойств, понимание документации, адаптация под библиотеку Зависит от качества LLM, может галлюцинировать, дороже по compute
Традиционный фаззинг (AFL, libFuzzer) Низкоуровневый поиск крашей и переполнений Не подходит для логических ошибок на уровне Python-функций

Как видите, LLM-агент не заменяет Hypothesis — он дополняет его, генерируя свойства, которые человек никогда бы не написал. Это похоже на то, как AI-агент для поиска аномалий обнаруживает нестандартные паттерны, недоступные глазам аналитика.

Кому этот инструмент спасёт жизнь (и репутацию)?

Если вы мейнтейнер популярной Python-библиотеки — вы обязаны попробовать эту методику. Она не требует от вас писать новые тесты, достаточно запустить агента на уже существующем наборе функций. Если вы разработчик, который хочет найти баги в чужих (или своих) проектах — агент станет вашим напарником на хакатонах и code review.

Но есть нюанс: LLM-агент в его текущей версии плохо работает с функциями, у которых нет чётко задокументированного математического свойства (например, UI-компоненты). Зато для научного стека — идеально. Не зря методичка разработки с LLM рекомендует использовать такой подход именно для спецификаций с формальными контрактами.

Скептикам скажу: да, агент может сгенерировать бессмысленное свойство. Но в отличие от человека, он не ленится — запустит тысячи проверок за час. И если хотя бы одна из них упадёт, это уже победа.

Что дальше: станет ли property-based testing с LLM стандартом?

Я не верю, что LLM полностью заменит человека в написании тестов. Но я верю, что через год-два CI-пайплайны будут включать агента, который прогоняет случайные свойства на каждой публичной API-функции. И когда он найдёт баг — вы обрадуетесь, а не расстроитесь. Потому что это значит, что ваш код стал чуточку надёжнее.

Кстати, если вы хотите узнать, как не перегружать LLM лишними запросами — почитайте про Delegation Filter. Это спасёт ваш бюджет, пока вы внедряете агента в продакшн.

Подписаться на канал