От скрипта к трансформеру: как behavior cloning привёл команду Сбера к серебру Kaggriculture

Летом 2026 года на Kaggle появилось соревнование Kaggriculture, где два агента управляли соседними фермами. За 30 игровых дней им нужно было выращивать урожай, содержать животных, покупать землю и продавать продукцию на общем рынке. Команда трайба «Цифровой ассистент В2С» за два месяца проверила шесть подходов — от ручного скрипта до трансформера на 20,2 млн параметров. В результате она заняла 436-е место среди 10 148 команд и получила серебро.
Игра, в которой случайность меняет экономику
Поле каждого игрока имело размер 10 × 10 клеток и делилось на четыре квартала: один был доступен с начала, остальные требовалось покупать. На ферме выращивали овощи и содержали коров, овец и гусей. Урожай нужно было вовремя собирать, а животных — кормить. Партия продолжалась 720 тактов: 30 игровых дней по 24 часа.
Рабочих нанимали на один день, причём стоимость последующих наймов росла по ряду Фибоначчи: десять рабочих стоили 143 доллара, а двенадцать — уже 376. Вся продукция поступала на общий рынок с динамической ценой. Равновесным считался запас в 10 000 единиц: дефицит повышал стоимость, а избыток резко её снижал. Особенно чувствительными были шерсть, клубника и молоко — для падения цены до одного доллара достаточно было сравнительно небольшого переполнения.
На цену влияли не только объёмы, но и порядок заявок: продажи двух игроков обрабатывались поштучно, а каждая следующая единица меняла состояние рынка. Поэтому фермы могли одновременно терять деньги из-за переполнения одного товара и недополучать выручку из-за нехватки другого. Дополнительную неопределённость создавали городские лавки: с третьего по 24-й день каждые трое суток открывалась новая лавка случайного типа. В 3 000 партий зеркального бота первая лавка меняла суммарный доход игроков примерно в полтора раза — от 134 517 долларов в случае кафе для питомцев до 207 630 долларов для смузи-бара.
Почему версии сравнивали на одних и тех же партиях
Анализ 2 266 результатов от 34 команд показал: на саму игровую партию приходилось 88,8% разброса итоговой кассы. Вклад собственной команды составлял 1,9%, соперника — 2,3%. В разнице между кассами влияние силы команд было заметнее, но всё равно составляло лишь 16,4% дисперсии. Это означало, что несколько случайных запусков не позволяли надёжно понять, стал ли агент лучше.
Для контролируемых экспериментов команда реализовала среду на JAX, обрабатывавшую батчи партий на GPU, и сверила её с официальным движком версии kaggle-environments 1.32.7. Совпали все 2 157 проверенных переходов. Затем разработчики использовали «стенд-призрак»: запускали партию с тем же seed, оставляли кандидату место своей версии, а соперник воспроизводил записанные действия.
Главной метрикой стали перевороты. Запись «+23/−12» означала, что новая версия выиграла 23 партии, проигранные базовой, и уступила в 12 партиях, которые выигрывала исходная. Для оценки применяли знаковый тест. Такой метод не заменял полноценные живые игры, поскольку записанный соперник не реагировал на новые действия, но позволял отделить улучшение агента от удачного случайного набора лавок.
От правил к копированию сильного игрока
Первый бот был построен на шаблоне, извлечённом из 260 реплеев лидеров. Он почти полностью тратил стартовую кассу на рабочих, семена и животных, а к 12-му дню стремился получить около 60 грядок, 13–14 животных и три дополнительных квартала. Продажи рассчитывались отдельным симулятором цен, совпавшим с игровым движком на 1,68 млн значений, а динамическое программирование выбирало подходящие моменты для сбыта.
Правила дали live-рейтинг 2 056, но быстро обнаружили свои ограничения. В 16,7% заявленного объёма товары продавались не дороже пяти долларов и приносили только 0,33% выручки. Бот мог продолжать выставлять шерсть по доллару даже при запасе рынка около 10 060 единиц. Кроме того, он следовал усреднённому расписанию и почти не учитывал действия соперника.
Следующим этапом стал k-NN-агент без обучения весов. Команда собрала 782 «ленты» одного сильного игрока: последовательности действий, цен, состояния фермы, открытых лавок и итоговой кассы. На каждом такте агент искал запись с похожей раскладкой, рынком и недавней динамикой цен, после чего продолжал выбранную последовательность. При отсутствии точного совпадения использовались более мягкие критерии, а за управление отдельными юнитами отвечал собственный исполнитель.
Такой подход хорошо воспроизводил дебют, но терял сходство в поздней игре. В дни с нулевого по пятый совпадение действий с оригиналом было полным, на шестой–четырнадцатый дни составляло 85%, а после 15-го дня падало до 2,5%. На 391 переигровке финальная версия выиграла 339 партий против записанного соперника, тогда как последний скриптовый кандидат — 140. Однако в отдельной проверке на 462 партиях копия уступала исходному боту в среднем 6,9 тыс. долларов, причём почти весь разрыв возникал после 17-го дня.
Почему не сработали гибриды и планировщик
Команда пробовала дополнять найденные траектории реактивной логикой и локальными вмешательствами. Прогноз кассы и склада часто ошибался: дефицит предсказывался примерно в трети тактов. Окно корректировок в 12 тактов помогало, а расширение до 24 уже ухудшало результат. Полная замена управления юнитами привела к доходу 33 тыс. долларов против 113 тыс. у опорной ленты: агент слишком много тратил на обслуживание животных и не успевал накопить на второй квартал.
Ещё одним экспериментом стал сезонный планировщик на CP-SAT из OR-Tools с отдельной задачей линейного программирования для продаж. Модель включала посадки, животных, землю, рабочих, корм и склад — 2 170 переменных и около 11 тыс. ограничений. Она находила план с расчётным доходом 141 тыс. долларов за 10 секунд и 184 тыс. за 20 секунд, но эти значения не были заработком в реальной партии. План быстро устаревал, а пересчёт каждый такт оказался слишком дорогим.
Behavior cloning и трансформер
В итоге разработчики перешли к нейросети, обученной повторять решения сильного игрока методом behavior cloning. За основу взяли архитектурный подход из другого симуляционного соревнования Kaggle: каждому объекту среды соответствовал отдельный токен, а состояние обрабатывалось трансформером с памятью.
Состояние фермы представлялось 197 токенами. В них входили глобальные признаки, сведения об игроках, клетки поля, свои и чужие юниты, товары, рыночные слоты и память. Восемь блоков EntityBlock шириной 384 давали около 20,2 млн параметров. Архитектура использовала pre-LN, восьмиголовое attention, QK-нормировку и FFN с SiLU-gate. Между ходами состояние сохранялось через GRU: память записывала пары «наблюдение — действие», а на следующем такте их читали четыре специальных токена.
Для действий юнитов модель сначала выбирала клетку через attention к полю, затем — операцию, предмет и количество. Маски исключали недопустимые действия. Отдельная рыночная голова определяла число заявок от нуля до десяти, а затем выбирала товар, операцию и объём каждой заявки. В последней версии появились макродействия: политика выбирала пару «клетка — операция», после чего исполнитель сам прокладывал маршрут и выполнял работу на месте. Это избавило модель от необходимости заново планировать каждый шаг перемещения.
Итоги
Лучшей стала именно модель, обученная через behavior cloning: она точнее ручных правил и лучше переносила стратегию сильного игрока, чем простое продолжение записанной партии. Позднее настроенный PPO дал небольшой дополнительный прирост, хотя на ранних запусках обучение могло ухудшать уже освоенную торговлю. Главный практический вывод команды оказался шире результата одного соревнования: в средах с высокой случайностью сначала нужно научиться честно сравнивать версии на одинаковых сценариях, а затем сочетать имитацию сильных решений с моделью, способной адаптироваться к состоянию игры.


