TL;DR
Исследователи из PayPal построили агента, который сам расширяет набор своих вычислительных операций и не пускает в него ни одной ошибочной. Агент не считает числа «в голове». Он пишет выражение на маленьком языке команд (DSL, domain-specific language), и выражение исполняет проверенный интерпретатор. Новая операция допускается по двум условиям: модель написала её дважды независимо, и обе версии дали одинаковый результат на всех записях. Плюс результат обязан подчиняться закону сохранения: например, суммы по группам складываются в общую сумму.
Главная находка: привычные сигналы «это хорошо» не отличают правильный код от неправильного. «Код запускается без ошибок», «модель сама с собой согласна» и «библиотека стала короче» пропускают почти все баги. Из 138 намеренно сломанных операций такие проверки пропустили 101–129. Независимая вторая реализация не пропустила ни одной, и это на дефектах, которые модель нашла бы не сразу. Просьба к модели оценить, верен ли код, тоже не работает: она ошибочно забраковала часть правильных операций и не дала ничего проверяемого.
Суть метода в двух проверках. Первая: второй раз пишем решение заново, по тексту вопроса, не показывая первое, и сверяем результаты на каждой записи. Вторая: проверяем инварианты, которые обязаны выполняться при любом правильном решении. Это сумма по группам = общая сумма, один результат на один уникальный ключ, пустой вход даёт пустой выход, порядок записей не влияет на итог. Ошибка должна пройти обе проверки сразу.
Схема метода
ВНУТРЕННИЙ ЦИКЛ (расширяем охват без новых операций)
ШАГ 1: Модель придумывает новые вопросы к данным → вопрос + выражение
ШАГ 2: Выражение исполняется на каждой записи → одно число на запись
ШАГ 3: Модель отвечает на ТЕКСТ вопроса заново, отдельно → второе выражение
ШАГ 4: Принимаем вопрос, только если оба выражения совпали на ВСЕХ записях
и величина новая
ВНЕШНИЙ ЦИКЛ (добавляем новую операцию)
ШАГ 1: Считаем, какой операции модели чаще всего не хватает → рейтинг пробелов
ШАГ 2: Модель пишет операцию (топ-3 пробела по очереди) → код операции
ШАГ 3: ВОРОТА:
а) вторая независимая реализация даёт те же значения (допуск 10⁻⁶)
б) выполняются законы сохранения на уже проверенных операциях
в) все открытые операцией вопросы проходят проверку из внутреннего цикла
ШАГ 4: Прошла всё → в библиотеку, внутренний цикл запускается снова
Каждый шаг выполняется отдельным обращением к модели. Вторая реализация пишется в отдельном вызове, без доступа к первой.
Пример применения
Задача: Вы продавец на Ozon, у вас выгрузка заказов за квартал: около 40 000 строк, колонки покупатель_id, сумма, город, дата. Вы хотите знать, на какого покупателя пришлось больше всего выручки и сколько приносит каждый город. Эти цифры пойдут в презентацию инвесторам, ошибиться нельзя. Нужен чат с запуском кода (ChatGPT с анализом данных, Claude с анализом файлов или любой агент с песочницей).
Промпт, чат №1:
Вот файл orders.csv. Колонки: покупатель_id, сумма, город, дата.
Вопрос: «Какой покупатель принёс больше всего выручки и какая это сумма?»
Напиши код на Python (pandas), запусти и верни результат.
Покажи сам код.
Промпт, чат №2 (новый чат, первое решение НЕ показываем):
Вот файл orders.csv. Колонки: покупатель_id, сумма, город, дата.
Вопрос: «Какой покупатель принёс больше всего выручки и какая это сумма?»
Реши задачу без pandas.groupby: используй чистый Python
(csv, словари, циклы). Запусти и верни результат.
Сначала напиши, как ты понимаешь вопрос, потом код.
Промпт, чат №3 (сверка и закон сохранения):
Вот два независимых решения одного вопроса и их результаты.
[решение 1 + результат]
[решение 2 + результат]
1. Сравни результаты. Если они расходятся — найди причину
(разное понимание вопроса, обработка пустых значений, дубли).
2. Проверь инварианты запуском кода на файле orders.csv:
- сумма выручки по всем покупателям = сумма всей колонки «сумма»;
- число групп = число уникальных покупатель_id;
- сумма выручки по городам = общая выручка.
3. Выведи таблицу: проверка | ожидалось | получилось | прошла/нет.
Результат: В чате №3 модель сравнит два ответа и назовёт расхождение, если оно есть. Затем она запустит проверки и выдаст таблицу «проверка / ожидалось / получилось / статус». Если оба решения верны, числа совпадут и все инварианты пройдут. Если одно решение теряет строки с пустым городом или неверно обрабатывает дубли, расхождение вскроется до презентации.
Почему это работает
Слабость. Если просто спросить у модели «а это правильно?», она оценит правдоподобие, а не верность. Неверный код тоже запускается, тоже выглядит аккуратно и тоже убеждает «судью». Сама модель на собственную ошибку смотрит тем же взглядом, которым её сделала: если зациклиться на одном решении, ошибка не видна.
Сильная сторона. Модель хорошо пишет код по текстовому описанию. Два решения, написанные независимо, с разной реализацией, редко ошибаются одинаково. Законы сохранения тоже проверяются легко и не требуют эталонного ответа: сумма частей должна равняться целому, и это известно заранее.
Как метод использует это. Вместо «пойми, правильно ли» мы просим «сделай заново и сравни числа». Это превращает субъективное суждение в механическую проверку. Закон сохранения ловит то, что пропускает пара решений: если обе копии содержат один и тот же баг, суммы по группам всё равно не сойдутся с общей суммой. В эксперименте двойное решение само пропустило такие общие баги почти полностью (129 из 138), а законы сохранения сократили пропуск до 10 из 138. Слои дополняют друг друга.
Рычаги управления: - Стиль второй реализации (другая библиотека, чистый Python вместо pandas) → меньше шансов повторить одну и ту же ошибку. - Вторая реализация в другой модели → ещё сильнее независимость. В исследовании независимая реализация другой семейства модели тоже не пропустила ни одного бага. - Набор инвариантов → подбирайте под задачу: «сумма по группам = итог», «число результатов = число уникальных ключей», «пустой вход → пустой выход», «порядок не важен». - Допуск сравнения → для денег подойдёт точное совпадение или копейки, для дробных вычислений — относительная погрешность. - Крайние случаи → добавьте проверки на пустые значения, дубли и один элемент. В исследовании именно крайние случаи ловят хитрые баги.
Шаблон промпта
Метод можно переносить в чат. Ниже два промпта: один для независимого решателя, второй для судьи-сверки. В авторской системе это отдельные вызовы одной замороженной модели.
Файл: {файл_или_данные}
Колонки/поля: {описание_полей}
Вопрос: {вопрос_по_данным}
РЕШАТЕЛЬ_А: реши задачу, покажи код и результат.
РЕШАТЕЛЬ_Б (независимо): новый чат, НЕ показывай решение А.
Сначала своими словами опиши, как понимаешь вопрос.
Затем напиши решение в другом стиле: {другой_стиль}.
1. differential: сравни результаты А и Б на каждой записи
(допуск {допуск}). Расхождение → найди причину, не усредняй.
2. conservation (проверка по закону сохранения), запусти код:
- sum(результат_по_группам) == sum({поле_суммы})
- число групп == число уникальных значений {поле_группировки}
- пустой вход → пустой результат
- результат не зависит от порядка строк
- {свой_инвариант}
3. Каждая проверка должна опираться на реальные вычисления,
а не на утверждение «это очевидно верно».
Что подставлять:
- {файл_или_данные}, {описание_полей}, {вопрос_по_данным} — ваша задача.
- {другой_стиль} — например «чистый Python без pandas», «SQL вместо pandas», «через циклы вместо groupby».
- {допуск} — «точное совпадение» для целых и денег, «1e-6 относительная» для дробных.
- {поле_суммы}, {поле_группировки} — колонки, по которым верен закон сохранения.
- {свой_инвариант} — любое известное вам правило, например «доли в сумме дают 100%».
🚀 Быстрый старт — вставь в чат:
Вот шаблон «двойной расчёт + закон сохранения». Адаптируй под мою задачу: [твоя задача по данным].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит про структуру данных, группировки и какие суммы или счётчики должны сойтись, потому что для работы метода нужны конкретные инварианты — законы сохранения, которые выполняются заранее известным образом. Она возьмёт паттерн из шаблона и адаптирует под вашу таблицу.
Ограничения
⚠️ Нужен запуск кода: Вся надёжность держится на том, что число считает интерпретатор, а не модель. В чате без выполнения кода метод превращается в ещё одну проверку «на глаз» и теряет смысл.
⚠️ Полный метод — инженерный: Авторская система (песочница, ограниченный язык, автоматический цикл, рейтинг пробелов) требует кода. В чате воспроизводится только ядро: двойное решение и проверка инвариантов, без самораспространения библиотеки операций.
⚠️ Это проверка тестами, а не доказательство: Допуск проверяется на конечном наборе записей. Законы сохранения формулирует сама модель. Метод показывает, что операция прошла целевые тесты, а не что она верна на всех входах.
⚠️ Общий баг в обеих копиях: Если обе копии содержат одну и ту же ошибку, двойное решение слепо. В авторском эксперименте такой случай закрывали законы сохранения, но не полностью: пропускалась небольшая доля. Намеренно хитрые баги, построенные с учётом проверок, тоже проходили.
⚠️ Нужен проверяемый инвариант: Там, где закона сохранения нет, метод слабее. Например, «самое частое значение» — нечисловой максимум без закона сохранения. Авторы честно оставили такие случаи вне метода.
⚠️ Неоднозначные вопросы: Две реализации могут по-разному понимать вопрос. Авторы столкнулись с этим в «самом загруженном дне»: разные правила разрешения ничьей давали разный ответ. Расхождение — не всегда баг, иногда это сигнал, что вопрос нужно уточнить.
⚠️ Правильное иногда отвергается: Из 14 корректных операций одна была отклонена из-за неверно сформулированного закона, а не из-за неверного значения. Нужен человек, который разберёт отказ.
⚠️ Проверено на открытых моделях среднего размера: Qwen, Mistral, Gemma, Llama. Слабые модели не всегда выдают пригодные операции. На флагманах авторы не тестировали.
Как исследовали
Идея была простой: если нет эталонных ответов и судьи, можно ли всё равно безопасно расширять возможности агента? Команда взяла одну замороженную модель (Qwen3.6-35B-A3B) и маленький язык вычислений над таблицей транзакций. На нём агент придумывал вопросы и писал выражения. Данные были полностью синтетическими, 63 тестовых «мерчанта».
Самый показательный опыт — мутационный тест. Исследователи взяли 14 принятых операций и сломали каждую всеми возможными единичными правками. Осталось 138 действительно неверных вариантов. Затем прогнали их через разные проверки. Простой запуск пропустил 129 из 138, самосогласованность тоже 129, правило «сжатия библиотеки» — 101. Независимая вторая реализация не пропустила ни одной. Когда баг намеренно записали в обе копии, двойное решение пропустило 129, а законы сохранения сократили это до 10. Вывод: ловят разные классы ошибок, поэтому нужны оба слоя.
Затем проверили масштаб и перенос. Внутренний цикл за один проход расширял набор вопросов примерно с 10 до 36, а за несколько проходов — до 60 с лишним, и это без новых операций. Внешний цикл на трёх независимых стартовых наборах сам придумал недостающие операции, например «сумма по группам». Контрольный запуск без индукции не дал ни одного нового вопроса такого типа. Совпадение с независимым «золотым» кодом на 3000 тестовых мерчантах было полным для всех вычисляемых типов. Без правок кода, только с заменой парсера, метод вырастил наборы на бухгалтерских данных (BookSQL) и медицинских записях (EHRSQL): рост в десять и почти в двадцать раз. На других моделях (Qwen3 от 4B до 32B, Mistral, Gemma, Llama) ворота оставались надёжными везде; менялась лишь результативность, то есть сколько операций модель смогла придумать.
Неожиданным оказалось то, что надёжность проверки не зависела от силы модели. Слабая модель не вносила неверных операций, она просто не писала ничего допустимого. Для практики из этого следует: безопасность строится на механической проверке, а не на качестве модели.
Адаптации и экстраполяции
🔧 Техника: вторая реализация в другой модели → меньше общих ошибок
В исследовании независимая реализация другого семейства (Gemma вместо Qwen) тоже не пропустила ни одной ошибки. В чате решайте задачу в ChatGPT, а «вторую копию» заказывайте у Claude или Gemini:
Вот только вопрос и описание колонок (код первого решения НЕ показываю).
Реши другим способом: {другой_стиль}. Запусти, верни число.
🔧 Техника: потребуй отчёт о проверках → видны пропущенные случаи
Добавьте в : «Перечисли крайние случаи, которые ты проверил (пустые значения, дубли, одна строка), и результат каждого». Так вы увидите, не пропустила ли модель то, что исследователи ловили «фаззингом».
Экстраполяция: правило «двойной расчёт» в файле инструкций аналитического агента
Идея авторов (любое число принимается только после независимой проверки и закона сохранения) естественно ложится в CLAUDE.md или системный промпт агента-аналитика. В статье такого промпта нет, это перенос принципа:
## Правила для чисел в отчёте
- Любое число, которое пойдёт в отчёт, считай ДВАЖДЫ независимыми способами
(другая библиотека или другой стиль кода). Второй способ пиши, не глядя
на первый.
- Числа принимай, только если оба совпали на всех строках.
- Затем проверь инварианты: сумма по группам = итог; число групп = число
уникальных ключей; пустые значения обработаны явно.
- Если проверка не прошла — не округляй и не усредняй, сообщи о расхождении
и опиши причину.
- В отчёте укажи, какие проверки пройдены.
Ресурсы
- Название работы: A Self-Improving Agent that Grows its Own DSL: Oracle-Free Operator Induction for Exact Question Answering (препринт).
- Авторы: Yun-Shiuan Chuang, Yirou Ge, Yixi Li, Yiming Bian, Zijie Pan, Jenna L. Ballard, Uma Kona, Linsey Pang, Prakhar Mehrotra — PayPal AI.
- Отсылки: дифференциальное тестирование (McKeeman, 1998; Yang et al., 2011); метаморфическое тестирование (Chen et al., 1998; Segura et al., 2016); Program-of-Thoughts (Chen et al., 2023); библиотеки навыков (Ellis et al., 2021; Grand et al., 2024; Wang et al., 2024b). Наборы данных BookSQL и EHRSQL (на MIMIC-III).
