В прошлом посте я рассказывал, как делал приложение для листов сохранения «Осквернённого Грааля». Там AI записывал чужую игру. Правила есть, страницы есть, его задача — не соврать.
Параллельно у меня шёл второй проект, и с ним всё сложнее. Мне мало коробок на полке, хочется своей истории в этом мире. Так появилась «Ненаписанная легенда» — одиночная браузерная RPG в тёмном фэнтези. Обычный человек попадает в историю погибшего королевства и собирает новый Круг. Память и легенды меняют реальность, колода героя отражает его поступки.
Проект неофициальный, фанатский. Сюжет и мифология здесь свои; в канон серии они не входят. И вот это «собственные» оказалось главной инженерной задачей.
Сейчас работает первая часть целиком: шесть героев, пролог, семь глав, регионы, которые можно проходить в любом порядке, общий игровой календарь, карточный бой, личные линии, пять финалов. Около 1,8 МБ сюжетного контента в JSON. Пишет его агент. А кто автор?
Агент знает Грааль лучше, чем нужно
С листом сохранения знание модели об игре было опасно тем, что оно неточное. Здесь опасно, что оно есть вообще.
Попросите написать сцену у древнего камня в мире по мотивам Грааля, и в тексте появится менгир. Опишите, как мир портится, — получите искаженье. Герой устал — значит, у него шкала энергии. Модель не ворует специально. Она просто достраивает мир, который видела, самым вероятным способом. А самый вероятный способ — чужой.
Если любишь оригинал, это особенно коварно: чужие слова звучат правильно. Читаешь и киваешь, потому что именно так и должно быть в Граале. Только у меня не Грааль.
Поэтому в проекте есть документ, который я считаю самым важным во всей папке с каноном. В docs/narrative/REFERENCES.md раздел называется «Термины изданий: не вводить как наши».
искаженье → Изнанка
«Наследие» → завет
воспоминание → узнавание
достижение → сюжетный флаг
слава как ресурс → у истории есть источник,
версии, носители и затухание
Список длинный: менгиры, стражи, путеводные камни, шкалы энергии, здоровья и ужаса, строфы, летописец, лист свершений. Эти слова нельзя использовать как наши сущности, ключи флагов и заголовки интерфейса. В обычной речи документов можно, если за ними не стоит наша механика.
После ввода этого правила агент прогнал поиск по всем документам. Совпадений не нашлось, кроме двух ложных: «катастрофа» и «апостроф». В обоих есть сочетание «строф», за которое зацепился поиск. Меня это рассмешило, но по сути это лучший признак нормальной проверки: она настолько дотошная, что ловит слово внутри слова, и сама честно пишет, что это ложные срабатывания.
Брать функцию, а не форму
Запретить слова — половина дела. Вторая половина — как опираться на любимый оригинал, не копируя его мир.
Правило получилось такое: из референса переносится функция, не форма. Хороший пример — магия. В настолке она работает как отложенное действие и как валюта, а не способность: платишь маркером внутри карточной встречи. Мне понравилась сама идея отсрочки. У меня отсрочка живёт в мировом календаре, а платишь долгом перед конкретными людьми. Механика про то же, но собрана из других деталей.
AI в этом неплохой партнёр, если правильно поставить вопрос. «Напиши магию как в Граале» даёт копию. «Что эта механика даёт игроку и чем ещё можно дать то же самое» даёт разговор. Разбор настольных изданий у меня лежит отдельным документом, по первичным источникам, и из каждого наблюдения в нём записано, какую функцию мы берём.
Основа, рабочий канон и гипотеза
Вторая беда AI-сюжета тише первой. Агент пишет сцену, и ему нужен факт о мире. Факта нет. Он его придумывает, вполне удачно, и тот оседает в тексте. Через три главы другая сцена опирается уже на него, и никто не помнит, что это была импровизация.
В обычном коде такое ловит компилятор или тест. В нарративе галлюцинация выглядит как лор.
Поэтому у каждого решения в проекте есть статус. Основа — положения исходного задания. Рабочий канон — новые авторские решения: их можно менять, но проверив зависимые истории. Гипотеза — открытый вопрос, который нельзя использовать как факт. И отдельное правило в AGENTS.md: не превращать гипотезу в факт незаметно.
Отдельно лежит STORY_SECRETS.md — закрытая правда для авторов, тайны S01–S22. Как на самом деле пал Камелот. Кто такой Белый Король. Почему у путника два имени. Игрок узнаёт это по кусочкам, агент знает всё сразу. Без такого файла каждая новая сцена по-своему объясняла бы одно и то же, и к пятой главе у мира было бы три истории падения.
Кстати, в самих тайнах до сих пор честно написано «пока неизвестно»: кто отказался и кто настоял, когда рвался договор. Это не недописанность. Это граница, которую агенту переходить нельзя, пока её не передвину я.
Тёмное фэнтези, написанное языком ТЗ
Сюжет от AI с первого раза выходит связным. Логика есть, причины и следствия на месте. А читать его невозможно: он звучит как протокол.
Это видно по истории репозитория. Среди последних коммитов два — не фичи, а вычитка. Один называется «живые надписи на кнопках, без канцелярита и слов разработчика». 63 файла, 2190 строк заменено на 2190 строк. Механика та же, изменился только язык.
− Причина двух имён пока не установлена.
+ Почему у него два имени, пока неизвестно.
− Поминовение превратило это в рассказ
о добровольной жертве.
+ На поминках об этом стали рассказывать
как о добровольной жертве.
− Ты просыпаешься отдохнувшим телом
и с напряжёнными руками.
+ Тело отдохнуло, но руки просыпаются сведёнными.
− Следующее отступление не теряет личный паёк
+ При следующем отступлении личный паёк не теряется
«Не установлено» — так пишет следователь. «Приказ требовал захвата, не убийства» — так пишет юрист. В мире, где люди помнят прошлое по-разному и спорят из-за этого, так не разговаривают.
Интересно, что вычитку тоже делал агент. Но только после того, как я сформулировал, что мне режет слух, и показал на примерах. Сам он такой текст считал нормальным: он точный. Но даже точная по смыслу фраза может звучать мёртво. Интонацию приходится ставить отдельно, и мерило для неё — мои уши, а не тест.
Сюжет, который можно прогнать тестами
При этом есть место, где AI-сюжет даже удобнее написанного руками. Моя история хранится как данные: у каждой сцены есть условия, варианты выбора и последствия.
Сцены, варианты выбора и последствия лежат версионируемым контентом. Условия и эффекты используют закрытые типизированные реестры: никакого eval, PHP или SQL из контента. Игровое время детерминировано, реальное ожидание не двигает календарь. Отдельная команда content:part1 проверяет, что все ключи сходятся и узлы связаны.
А браузерные тесты проходят игру. Шестью героями. Через опоздания по календарю, когда поздние регионы уже изменились. До финала без присяг и до финала с собранным Кругом из одиннадцати мест, который открывает новый договор.
Роман так не проверишь. А тут, если агент в седьмой главе сослался на флаг, который нигде не ставится, я узнаю об этом от красного теста, а не от игрока, застрявшего перед закрытой дверью. Это та часть авторства, которую я с удовольствием отдал машине: следить, чтобы история не противоречила сама себе на уровне механики.