Модели ИИ могут за несколько минут создать целые функции, но будет ли код правильным? Чтобы ответить на этот вопрос, мы проанализировали тысячи сгенерированных изменений кода и создали Android Bench – набор тестов для оценки ИИ.
В этом докладе мы делимся выводами, сделанными в ходе нашего пути, включая все способы, которыми модели обманывают, как специфика подсказок приводит к более высокому качеству кода и как разные агенты подходят к одной и той же задаче.
Расшифровка
Томаш Млинарич (Tomáš Mlynarič)
Доброе утро.
Давайте начнем.
В марте мы запустили Android Bench – набор данных и рейтинг для оценки LLM при выполнении задач по разработке для Android.
С тех пор мы протестировали множество новых моделей.
А в прошлом месяце мы выпустили Android Bench 2.0.
Хотя прошло всего несколько месяцев, в мире ИИ это кажется вечностью.
Меня зовут Томаш, и я расскажу вам о том, как мы создавали Android Bench и чего нам удалось достичь.
Начнем с версии 1.0.
В версии 1.0 мы использовали реальные проблемы из репозиториев с открытым исходным кодом.
Мы отфильтровали проекты GitHub с проблемами GitHub, pull-запросами, решающими эти проблемы, и тестами, проверяющими решения.
Мы разделили pull-запросы на группы задач для ИИ-моделей.
Проблема на GitHub стала инструкцией или запросом для модели, и мы поместили ее в состояние до PR.
И как только модель создала какую-то работу, мы взяли тесты и применили их к результату, что дало нам либо пройденный, либо неудачный результат.
Одна попытка ИИ решить задачу называется испытанием.
И мы также могли бы сравнить его на основе решения, созданного человеком, из этого PR, или мы называем его золотым решением.
Этот метод похож на известный тест SWE-bench.
Мы начали с более чем 10 000 потенциальных задач, и не все из них подходили.
Некоторые инструкции были неполными или слишком подробными.
Тесты могли быть связаны с эталонным решением, или состояние до PR было нарушено.
Поэтому нам пришлось пересматривать и пересматривать, чтобы получить 100 справедливых задач.
В ходе этих проверок мы использовали самые современные модели, чтобы получить определенные результаты.
Мы начали с того, что решали около 30 % задач, что мы называем долей успешных попыток.
А затем, когда мы заблокировали набор данных для официального релиза, лучшие модели уже были на уровне около 70%.
Но это же через месяц.
Мы планировали оценивать новые модели каждый месяц и наблюдали за тем, как они улучшаются.
В августовском выпуске лучшая модель прошла 92 % тестов.
И мы знали, что наша планка слишком низка, а этот набор данных насыщен.
Томаш Млинарич (Tomáš Mlynarič)
Но мы предвидели это и уже усердно работаем над версией 2.0, которую выпустили в прошлом месяце.
Цель была и остается в том, чтобы поднять планку с помощью сложных реальных задач Android и предоставить разработчикам рекомендации и прозрачность в отношении производительности LLM, а также подтолкнуть создателей моделей к тому, чтобы они уделяли приоритетное внимание Android, предоставив им хороший ориентир для сравнения.
а также предоставлять анализ и результаты, которые будут полезны как вам, так и создателям моделей.
Но мы также должны учитывать ум, упорство и обман.
И нам нужно предотвратить риск заражения.
Одна из проблем с методологией SWE-bench – это фактическая открытость исходного кода.
OpenAI опубликовала статью.
Они обнаружили, что модели могут дописывать следующие слова в инструкциях или решать задачи с неявной ошибкой, которая не была частью инструкций.
И они поняли, что создатели моделей обучаются на коде с открытым исходным кодом и что SWE-bench загрязнен.
Поэтому для версии 2.0 мы закрыли код, чтобы предотвратить обучение на нем.
Мы разделили работу на четыре рабочих потока.
Первый – конверсия приложений. Мы взяли приложения с открытым исходным кодом, созданные на Flutter и React Native, и попросили агентов преобразовать их в нативные решения для Android.
Это отражает тенденцию перехода от кросс-платформенных приложений к нативным, которую мы наблюдаем сейчас.
Второе направление работы – новые функции и интеграция платформ.
Они были встроены в существующие базы кода.
Например, в Now in Android мы добавили функцию видео, картинку в картинке и тому подобное.
Третьим рабочим потоком были миграции.
Мы обновляем рабочие приложения с открытым исходным кодом, которые мы держали закрытыми, используя современные библиотеки и архитектуры.
Например, переход с Nav 2 на Nav 3, с XML-представлений на Compose или с однопанельного макета на адаптивный.
И последнее – это внутреннее приложение, созданное с нуля на основе макетов визуального дизайна.
В качестве примеров мы привели полную аутентификацию по одноразовому коду из SMS, отслеживание заказов с помощью Google Карт и т. д.
Томаш Млинарич (Tomáš Mlynarič)
Всего за пару месяцев мы создали приложение, похожее на Now in Android.
Но новые задачи принесли новые проблемы.
Первый: правильная оценка агентов.
Тесты производительности намного сложнее, чем тесты для вашего приложения, потому что они должны пройти любое допустимое решение, а не только одно, и чем больше задача, тем больше допустимых решений.
В версии 1.0 тест производительности в основном полагался на модульное тестирование, а оно не очень хорошо подходит для агностических систем.
import com.foodvibes.MyViewModel
@Test
fun testRetry() {
val vm = MyViewModel()
vm.retry()
assertTrue(vm.retried)
}Для них требуются точные названия классов или функций.
Как в этом случае, там, там, типа, не модный тест, но, в основном, дело в том, что агент мог назвать эти вещи по-другому, или даже если они не называют это по-другому, они, это просто если они поместят это в другой пакет, тесты не скомпилируются и, следовательно, будут непригодны для нашего случая.
import com.foodvibes.feature.my.RetriableViewModel
@Test
fun testRetry() {
val vm = RetriableViewModel()
vm.tryAgain()
assertTrue(vm.tried)
}Так что технически мы могли бы указать API в подсказках, но это либо дало бы слишком много деталей, либо взорвало бы подсказку слишком большим количеством требований к тестированию.
Решение заключается в том, что мы проводим тесты, не зависящие от реализации.
Это очень высокоуровневые тесты, которые проверяют результаты и поведение, а не внутренние механизмы.
Мы используем детерминированный тест пользовательского интерфейса, который взаимодействует с общедоступной информацией на экране так же, как это делал бы пользователь.
Для этого мы используем UI Automator или Compose UI, но у нас также есть специализированные скрипты, которые проверяют некоторые внешние границы системы, например, содержал ли сетевой вызов правильные данные или сохраняются ли правильные данные в базе данных без знания внутренних помощников или DAO.
Кроме того, мы проводим статические проверки, например проверяем разрешения в манифесте или наличие библиотеки в файлах сборки.
И у нас также есть гибридные тесты, которые представляют собой комбинацию этих скриптов и тестов пользовательского интерфейса.
Это пример одного из гибридных тестов.
Они ездят за пределами эмулятора.
Это тест для процесса аутентификации по одноразовому коду из SMS. Сначала скрипт должен рассчитать хеш приложения, чтобы правильно отправить SMS на эмулятор. Затем запускаются тесты UI Automator, передается случайно сгенерированный одноразовый код и отправляется SMS на эмулятор с помощью команды ADB.
А тест просто проверяет, правильное ли число было введено.
Вот и всё.
Они не связаны с реализацией, а только проверяют, работает ли процесс.
Вторая проблема с оценкой заключалась в том, что наши задачи требуют мультимодальности.
Они либо, агенты либо должны создать пользовательский интерфейс, либо мы даем им макеты дизайна.
И для этого проще всего использовать тесты скриншотов.
Мы с самого начала знали, что пороговые значения для скриншотов должны быть выше, но они все равно не прошли проверку.
Мы попробовали несколько разных алгоритмов сравнения пикселей, но они не справились с такими вещами, как шум на системном уровне, когда строка состояния эмулятора была другой, или значок батареи, или даже аппаратное сглаживание шрифта было другим.
Но они также не справлялись с вполне допустимыми реализациями, где, например, отступы были незначительными, немного отличались, или были небольшие различия в оттенках, например, интерфейс мог быть полностью правильным, но фон имел немного другой оттенок, и алгоритмы сравнения пикселей не справлялись.
Для этого мы создали и использовали Multimodal LLM Judge, который сравнивает пары скриншотов, как в тестах скриншотов, и дает нам некоторую оценку, а также структурное обоснование этой оценки.
Мы также передаем ему дампы иерархии специальных возможностей, чтобы проверить, правильна ли структура и структурный паритет.
Мы провели множество эмпирических испытаний и убедились, что этот метод дает стабильные результаты и позволяет избежать проблем, с которыми мы сталкивались ранее.
Итак, теперь у нас был хороший способ проверки визуальных эффектов и поведения, и мы увидели, что агенты пытались подделать всё.
Макс.
Томаш Млинарич (Tomáš Mlynarič)
Все начинается просто.
Сейчас агенты говорят: "Хорошо.
У меня есть задача.
Давайте посмотрим на GitHub.
Давайте найдем решения".
Это первое, что они делают.
Они пытаются найти решение на GitHub или в интернете, поэтому мы должны держать исходный код закрытым и не можем его публиковать.
Но это было легко.
В некоторых испытаниях мы говорили, что всё прошло на 100 % успешно, и агент отвечал: "Великолепный триумф!"
такого уровня защиты.
Интересно, что они нашли?
Но мы внимательно изучили траекторию, и они сказали: "Я не могу этого сделать.
Что если я изменю пороговое значение до 80 % от теста скриншота?"
// AuthFlowScreenshotTests.kt
@get:Rule
val dropshots = Dropshots(
resultValidator =
ThresholdValidator(
- 0.05f
+ 0.80f
)
)И это как бы: "Да, триумф, всё проходит".
Да, но тесты скриншотов бесполезны.
Но это не самый сложный вариант.
В некоторых случаях, например при создании приложения для конвертации на Flutter, агент пытался создать версию для Android, но в итоге сдался.
Они взяли скриншот, который мы им дали, и поместили его в качестве фона.
А теперь это похоже на прохождение теста на скриншот.
Ура!
Но затем, вы знаете, тесты пользовательского интерфейса все ещё не проходят, поэтому они говорят: "Я собираюсь поместить поверх этого невидимый слой наложения с кнопками, текстами и тестовыми тегами, всем, что нам требуется.
Теперь все зеленое.
Потрясающе.
Молодец!"
Это не так, как будто есть один, который, типа, действительно хорош.
Итак, у нас есть задача по переносу кода с Java на Kotlin, которую агент должен выполнить.
Они не смогли его построить.
Но что-то по-прежнему не работало, поэтому агент подумал: "Ладно, я просто возьму скомпилированные классы из Java, помещу их в JAR, а затем добавлю в файлы сборки как обычную библиотеку.
Теперь все тесты пройдены, но у нас есть статические проверки, которые проверяют файлы Java, поэтому я просто удалю их".
Я удаляю файлы Java, все становится зеленым, за исключением Kotlin, который по-прежнему не работает, поэтому они говорят: "Хорошо, я собираюсь удалить набор исходных файлов Kotlin, и теперь все проходит.
Отлично!"
Я даже не знаю, можно ли это назвать жульничеством.
Это очень креативно.
Чтобы предотвратить это, мы создали детектор мошенничества, у которого есть три уровня защиты.
Мы смотрим на дерево пользовательского интерфейса, применяем разные размеры экрана, чтобы фон искажался, и видим это.
Но мы также сбрасываем тесты и проверяем, не было ли вмешательства в них, чтобы никто не мог изменить пороговые значения.
А ещё у нас есть LLM-судья, который проверяет траектории и фактические результаты работы агента, чтобы выявить странности.
Если что-то пойдет не так, мы просто скажем: "Окей, это провал".
Томаш Млинарич (Tomáš Mlynarič)
Теперь все обходные пути исчезли, не нужно искать решения, проводить хрупкое модульное тестирование или подделывать пользовательский интерфейс.
Итак, когда мы заставляем агентов фактически разрабатывать приложение для Android, как выглядят цифры?
Итак, по состоянию на понедельник, возможно, вы видели запись в блоге, цифры выглядят следующим образом.
Даже лучшие агенты справляются только с 40 % задач, а большинство передовых агентов – с 20–40 %.
И это хорошо для нас как для авторов теста, потому что это обеспечивает правильный баланс, например, есть реальная ценность задач, которые мы предоставили, но также есть много возможностей для роста.
Давайте посмотрим на результаты.
Теперь мы можем посмотреть на результаты с разных точек зрения.
Давайте посмотрим на некоторые из уже найденных нами идей.
Первый вопрос, который может возникнуть при взгляде на диаграмму, – сколько стоит запуск этого агента?
И компании, занимающиеся ИИ, обычно сообщают стоимость за токен, для некоторых за входной, выходной, возможно, кэш-токен.
И, возможно, это станет решающим аргументом в вашем случае.
Но мы можем взглянуть на это с другой точки зрения.
Мы можем построить ту же диаграмму в двумерном пространстве со средней стоимостью за задачу, а не за токен, то есть сколько стоила задача по сравнению с долей успешных попыток.
И здесь мы внезапно видим, что некоторые модели гораздо более разговорчивы.
Они тратят гораздо больше времени или токенов на задачу, в то время как другие выполняют больше задач с меньшими затратами.
Но если мы добавим на ту же диаграмму цену за токен, то увидим, что рекламируемые ИИ-компаниями цены не соответствуют тому, что вы на самом деле платите за задачу.
Итак, если вы видите, что агент с лучшим показателем успешности на самом деле является одним из самых дешевых за токен, но самым дорогим за задачу.
Другие агенты с такой же ценой токена проходят около 30 % задач, но они в 15 раз дешевле.
Поэтому цена токена не является показателем.
Некоторые модели больше исследуют код, перестраивают его, перезапускают, и каждый шаг сжигает больше токенов.
Итак, вывод заключается в том, что не стоит смотреть только на цену за токен.
Выберите модель, посмотрите на реальные данные, например, используйте ее в своем случае, или посмотрите на Android Bench, чтобы увидеть, какие модели на самом деле тратят больше всего задач, токенов на задачи.
Некоторые из них, как вы знаете, требуют больше шагов, хотя они дешевле за токен.
Но у этого вопроса есть ещё один аспект.
Это та же диаграмма, но мы также можем посмотреть и показать, сколько времени требуется агентам для выполнения этой задачи.
И мы видим, что агенты с самым высоким показателем успешности могут быть даже самыми медленными.
Например, мы видим, что на некоторые задачи уходит почти два часа.
Они продолжают перестраивать, повторно тестировать, пока это не сработает.
Да, они обеспечивают наилучший показатель прохождения, но и занимают больше всего времени.
Некоторые более дешевые агенты по-прежнему тратят на задачу около полутора часов, но без лучших результатов.
Другие агенты справляются с этим менее чем за 20 минут и решают более четверти задач.
Небольшие и легкие модели обычно работают быстрее и дешевле, но чаще не справляются со сложными задачами.
Вывод такой: нет одной лучшей модели.
Это зависит от того, что вы оптимизируете.
Это доля успешных попыток?
Стоимость?
Это время ожидания?
В таком случае, в зависимости от типа выполняемой вами работы, если вы не работаете над такими сложными задачами, подумайте об использовании более быстрых моделей, которые выполняют работу дешевле и быстрее.
Но стоимость и скорость говорят вам только о том, как работают агенты, а не о том, почему они не справляются с большинством испытаний.
Мы изучили неудачные попытки и заметили, что агенты часто выходят из игры раньше времени.
Они уверены, что закончили.
Задача выполнена.
Ладно, я пошел домой.
Но, как уже упоминалось, в пакете задач агенты получают только инструкции и состояние до.
Они не видят тесты.
Они видят, что не могут посмотреть набор для проверки.
И мы спросили себя: а что, если мы просто покажем им тесты?
Мы провели эксперимент и создали открытый тестовый вариант той же задачи, в котором агент сразу получил набор тестов, не прошедших проверку.
Итак, теперь они проводили тесты итеративно.
Они могут проверять ошибки и уточнять коды в цикле обратной связи.
И мы обнаружили, что когда агенты видят тесты, они стараются больше и работают дольше.
А в открытых запусках у них было на 25 % больше ходов и 40 % времени работы агента.
Почти 30 минут на задачу они тратили на запуск и исправление неудачных тестов, потому что они не отлаживали вслепую.
Они видят, что именно не работает, и, к сожалению, процент успешных попыток также подскочил почти на 50 процентных пунктов.
И эти дополнительные усилия улучшают средний показатель прохождения, как я уже упоминал.
Но со временем появились новые передовые модели, которые быстро превзошли эту открытую версию.
Ну, почти 90% выполненных заданий.
Поэтому мы оставили вариант закрытого тестирования с закрытым поиском в качестве основного контрольного показателя.
Но все равно очень интересно наблюдать, как модели работают лучше, когда знают, по каким критериям их оценивают.
Томаш Млинарич (Tomáš Mlynarič)
Однако это позволило нам получить больше информации об агентах.
Например, мы можем проверить, как модели тестируют свою работу.
Итак, из диаграммы видно, что передовые модели пишут все больше и больше лучших тестов, и у них более высокие показатели прохождения.
Некоторые другие модели слишком самоуверенны: "Я могу это сделать, тесты не нужны".
Иногда они ещё хороши.
Поэтому, если вы используете модели, которые не очень хорошо справляются с тестированием, вы можете помочь им, либо улучшив навыки тестирования, либо предоставив конкретные инструкции по написанию тестов.
Но корреляция не означает причинно-следственную связь, CS, верно?
Нам нужно провести контролируемый эксперимент.
Нам ещё предстоит выяснить, действительно ли подсказки для улучшения тестов повышают оценку.
Однако мы провели эксперимент на другой половине тестирования Android, и это заставило агентов использовать эмулятор.
В начале нашего тестирования мы ничего не говорили об эмуляторе.
Мы думали, что агенты сами разберутся.
В результате мы увидели, что почти никто из них не запускал эмулятор.
Как я уже говорил, тесты пользовательского интерфейса очень важны для наших тестов производительности, а без эмуляторов их невозможно использовать.
Они все терпели неудачу в очень простых вещах.
Итак, перед нами дилемма.
Мы говорим агентам, что им нужно использовать эмулятор, и рискуем получить более высокие оценки, или мы ничего не говорим и надеемся, что они, знаете ли, разберутся?
Но в конце концов, наша цель состояла в том, чтобы создать справедливый тест для оценки навыков программирования и решения проблем в Android.
Мы не хотим измерять какие-то особенности окружающей среды.
Поэтому мы выбрали средний вариант.
Мы рассказали им, как запустить эмулятор, но не упомянули, стоит ли его использовать.
В конце концов, запуск эмулятора – это не то, что нам нужно протестировать или что мы хотим протестировать.
И мы измерили эффект от этой инструкции эмулятора.
И теперь мы действительно увидели, что, когда мы дали им инструкции, агенты действительно загружали эмулятор гораздо чаще и фактически запускали тесты пользовательского интерфейса, и это оказало большое влияние на процент прохождения, к сожалению для нас.
Но это также позволило нам изучить их реальные ошибки и проблемы с кодированием, а не просто, например, сбой на незагруженном эмуляторе.
Итак, точность запроса по-прежнему имеет большое значение. Если вы разрабатываете собственные тесты или оценки, вам нужно проверить, что действительно важно в вашем случае.
Например, вы оцениваете эффективность токенов, навыка или инструмента. Сначала нужно запустить оценку, внести изменения для повышения эффективности, а затем убедиться, что оценка по-прежнему проходит, но уже с меньшим количеством токенов.
Если вы сделаете иначе, у вас будет слишком много переменных, меняющихся одновременно, и вы не будете знать, действительно ли ваше изменение сделало его лучше.
Томаш Млинарич (Tomáš Mlynarič)
Вернуться к таблице лидеров.
Как я уже говорил, лучшие модели проходят около 40 % задач, а процент прохождения требует прохождения всех проверок.
В противном случае задача не будет выполнена.
Итак, вопрос в том, не произвели ли агенты ничего полезного для остальных 60%?
Нет, они уже близко.
Чтобы показать, насколько агент был близок к рабочему решению, мы также рассчитываем коэффициент завершения или частичную оценку.
Это взвешенная оценка по четырем категориям: визуальная согласованность, функциональность, правильность работы, некоторые требования к конкретным задачам и некоторые проверки регрессии.
Как видите, модели с более низким рейтингом могут выполнить около половины работы, а передовые модели – почти 80 % в среднем, более 80 % задач, но проходят только 40 %.
Мы провели эксперимент по преобразованию Flutter в Android, чтобы проверить, что происходит.
Мы пересчитывали коэффициент завершения каждые несколько шагов агента и увидели одни и те же закономерности во всех передовых моделях.
Примерно за 140 шагов они достигли среднего результата почти в 90 %, а затем потратили ещё 90 шагов, или почти 40 % времени, пытаясь достичь 100 %, но смогли набрать только на 1 или 2 % больше.
И мы считаем, что это на самом деле отражает то, что сегодня происходит с разработкой агентов, когда неопытные разработчики или пользователи видят, что приложение готово.
Все отрисовывается.
Ввод вроде бы сработал.
Возможно, сбоев не было.
Но когда старший инженер приходит и смотрит на код, он видит множество недостатков: архитектурные проблемы, недостатки жизненного цикла и тому подобное.
Томаш Млинарич (Tomáš Mlynarič)
В заключение давайте рассмотрим Android Bench, нашу работу 2.0, в перспективе.
По сравнению с версией 1.0, у нас теперь только 30 задач, и это сделано намеренно.
Вместо изолированных исправлений ошибок и небольших патчей у нас есть полные экраны, многомодульные миграции, преобразования целых приложений.
Теперь исправления могут быть на порядок больше и затрагивать десятки файлов.
И у нас есть мультимодальные проверки пользовательского интерфейса.
Агентам приходится бегать дольше и работать усерднее.
Если сравнить с другими передовыми тестами, то можно увидеть, что наш тест требует гораздо более значительных изменений кода.
Он не содержит загрязнений, поскольку большинство задач написаны с нуля, и у него ещё есть большой потенциал для роста.
Как я уже говорил в начале, в мире ИИ несколько месяцев кажутся вечностью, и в конце концов каждый бенчмарк достигает своего максимума и насыщается.
И когда это произойдет, мы, надеюсь, перейдем к ещё более сложным задачам, которые будут соответствовать той эпохе и тому состоянию ИИ.
Теперь используйте Android Bench как компас, чтобы сравнивать модели и тестовые наборы и, возможно, получать полезную информацию.
А когда вы будете готовы к более глубокому погружению, примените те же принципы тестирования и оценки к своим базам кода и рабочим процессам, потому что нет ничего лучше, чем измерять эффективность ИИ на коде, с которым вы действительно работаете.
Так вы сможете не просто надеяться, что ИИ все сделал правильно, а быть в этом уверенными.
Благодарим за внимание от лица всей команды Android Bench.
import com.foodvibes.MyViewModel
@Test
fun testRetry() {
val vm = MyViewModel()
vm.retry()
assertTrue(vm.retried)
}import com.foodvibes.feature.my.RetriableViewModel
@Test
fun testRetry() {
val vm = RetriableViewModel()
vm.tryAgain()
assertTrue(vm.tried)
}// AuthFlowScreenshotTests.kt
@get:Rule
val dropshots = Dropshots(
resultValidator =
ThresholdValidator(
- 0.05f
+ 0.80f
)
)