Это запись для тех, кто любит заглядывать под капот. Я постараюсь писать так, чтобы не потерять тех, кто не читает код, но без технических деталей рассказ не получится. Каждое число ниже я измерял сам, а то, чего не знаю, так и помечено.
Задача
В мире должны жить сотни персонажей, которые выглядят как игроки: качаются, торгуют, болтают, собираются в пати. Всё это должно помещаться на один ноутбук (12 ГБ памяти под систему разработки плюс 4 ГБ под виртуальную машину с сервером) и стоить недорого. Бюджет на языковые модели для всего флота я ставлю себе порядка десяти долларов в месяц. Это цель, а не достигнутый результат.
Клиент, а не скрипт внутри сервера
Каждый ИИ-игрок это обычный клиент игры без картинки: программа на Python (asyncio), которая говорит с сервером тем же сетевым протоколом, что и настоящий клиент. Для сервера это игрок с логином и персонажем, у которого нет никаких привилегий. Протокол я собирал из трёх источников: описания сообщества и открытые проекты, собственные перехваты трафика и статический разбор серверных программ. Что-то в нём я мог понять неправильно, поэтому боты проверяются на реальном сервере, а не на моих предположениях.
Мозг в три слоя
Идея в том, чтобы дорогие решения принимал как можно реже.
- Рефлексы на каждом тике: выпить зелье, отступить, сменить цель. Это обычный код, он ничего не стоит.
- Очередь дел. У каждого бота есть очередь дел с приоритетами по «полосам необходимости»: выжить, восстановиться, выполнить обязательства (просьба игрока, роль в группе), обслужить себя (сумки, ремонт, зелья), заниматься прогрессом (культивация, квесты, подземелья, снаряжение) и только потом общаться и отдыхать. Оценщики смотрят на состояние бота и добавляют или убирают дела, наверху очереди работает самое важное. Это тоже код.
- Языковая модель подключается там, где нужен разговор или неоднозначный выбор: имена и характеры, реплики в чате, редкие «жизненные» решения. Для самых близких персонажей задуман полноценный «ум» с памятью, который просыпается по событиям, но это пока дизайн, а не реальность в мире.
Сегодня почти всё в мире делает код. Языковая модель заранее пишет имена, истории и банки реплик, причём в нескольких голосах (сдержанный, шутник, ворчун и так далее). Вторым вызовом дешёвая модель оценивает написанное, а детерминированный фильтр отсекает мусор и слова, которые выдают бота. Живых вызовов модели во время игры мало, и каждый вызов записывается и считается.
Один хост на всех ботов
Сначала каждый запуск бота тянул за собой процесс Python с загруженными данными игры: около 0,6 ГБ до появления первого бота. Сотни таких процессов не поместились бы в память, поэтому я сделал единый «хост ботов»: долгоживущий процесс и несколько рабочих, в каждом из которых живёт до 40 ботов на общих данных. Бот внутри рабочего процесса стоит примерно 20 МБ и 0,011 ядра процессора, а когда я сам запускаю тяжёлую игру на этом же ноутбуке, примерно 0,027. Если рабочий процесс начинает отставать, хост сам отключает часть ботов.
Ещё хост решает вторую задачу: боты работают только на выпущенных версиях кода. Версия фиксируется командой выкладки, и рабочие переезжают на неё. То, что я только что поправил в рабочей копии, до живого мира не доезжает, пока я этого не решу.
Часовая телеметрия и A/B
Каждый бот каждые две секунды пишет, что он делает: где стоит, на каком шаге, куда идёт. Раз в час отдельная проверка складывает это в отчёт: здоровье сервера и ошибки, темп прокачки по классам, сколько ботов застряло, как выглядит чат, что происходит в экономике. Отдельный наблюдатель заходит в стартовые зоны как новичок и ставит оценку от 0 до 100 тому, насколько мир обжит. Эта оценка выведена на странице «Живой мир».
Новые возможности я включаю не на всех. В прошлой репетиции модуль походов в подземелья работал в пяти шардах из десяти, и по отчёту видно, что он даёт. Если польза не видна, модуль не остаётся.
Что пока не работает
- Пати по ролям. Бот может позвать недостающую роль, жрец из другой части системы откликается, но группа, которая стабильно убивает боссов, у меня пока не получилась. Это главный долг.
- Рынок. Лавки открывать умеют, но живой экономики с настоящими ценами пока нет.
- Чат. Реплики лучше, чем были: 88 % шаблонных строк в прошлой популяции меня не устраивали. Но повторы и однообразие никуда не делись, и новичка встречают не всегда.
- Память. Рабочие процессы разрослись, и потолок в 400 сейчас не держится, пока я это чиню.
- Живая смена эпох. Собрана и проверена на файлах, но на работающем мире не применялась.
- Английский. Пока ИИ-игроки говорят по-русски.
Как я проверяю, что не сломал мир
Автотесты (около 470 файлов, порядка 8 000 проверок) гоняются перед выпуском версии. Выпуск версии — отдельный шаг, а не побочный эффект. Дальше работает часовая телеметрия и сравнение A/B, а решение «пускать в мир» принимаю я. Проверки на каждый коммит (CI) пока нет, я её сейчас настраиваю.
Если что-то из этого вам хочется обсудить, я в сообществе.