Полгода в одиночку: как глупая идея превратилась в платформу для ролевых игр с ИИ

Весной я сел делать анонимный чат. Частично от скуки, частично меня просто не устраивали существующие опции на рынке. В этой статье я расскажу, как моя идея менялась, с какими трудностями я столкнулся, какие полезные фишки в работе с ИИ и кодом нашел для себя, а так же во что это все вылилось по итогам полугодия.
Полгода спустя от изначальной идеи осталось немного. Проект получил название Katomu и дважды сменил курс: сначала стал площадкой для текстовых ролевых игр с живыми людьми, а потом сделал ставку на ролки с ИИ‑персонажами, а потом... эхэхэ. Я даже боюсь представить,что мне подкинет моя фантазия. Делаю я проект полностью в одиночку (только с рекламой друг помогает), поэтому любые идеи волен реализовывать.


С чего всё началось
Немного предыстории и о том, как меня вообще занесло в анонимки. Я давний постоялец Nekto.me. За многие годы этот сайт, как и многие другие анонимные чаты, во многом меня разочаровал. Сценарий там почти всегда один и тот же: заходишь, тебя случайно соединяют с собеседником, вы болтаете, обсуждаете дела насущные и… скорее всего, расходитесь навсегда. И тут проблема в том, что тебя даже не пытаются соединить с человеком, с которым тебе будет хотя бы в теории интересно. Тебя просто гоняют по случайным чатам, постоянно впаривая рекламу какого‑то слопа.
Поэтому я, как типичный мечтатель, решил, что смогу сделать свою, идеальную версию. Первая называлась AnonChat (как позже окажется, название настолько неоригинальное, что давно уже занято. Да и в тот момент это был шуточный проект. Думал пару недель побалуюсь и брошу. Ага, конечно), и цель у неё была одна: практика в коде. Да, я в тот момент к проект относился исключительно как к манекену для тренировок, не более. Но я так увлекся, что к концу апреля (а разработка началась в феврале) чат уже жил на удаленном сервере. Домен я выбрал такой же оригинальный как и название: ChatsChan.com (отсылка на другие сайты с припиской Чан. Думал будет прикольное, слегка маргинальное местечко, а потом посчитал название кринжовым, поэтому в начале мая появился домен katomu.com. Название, кстати, взято из моего псевдонима: КАфка ТОМУра. Вот так вот:3)
Как всё пошло не туда
И вот когда базовый функционал был готов, проект свернул совсем не туда. При чем так свернул, что я до сих пор не могу вектор движения выбрать достаточно четко. Все началось с банального. Я заметил, что самые интересные (по моему опыту) диалоги, происходят далеко не в разделе общения и... даже не в разделе флирта, а в ролевых играх. Там разговор не заканчивается на делах насущных или интиме. В ролке двое пишут историю, и она перерастает в дружбу и в целую прожитую вместе жизнь. (не уверен что на Хабре интересуются таким, так как я новенький, но тем не менее). А это уже хороший задел под свое комьюнити, которым я всегда грезил, поэтому решил, что делать очередной анонимный чат будет.. тупо, что‑ли. Так что сосредоточился на ролевых играх.
Я и сам часто ролил и хорошо знаю главную проблему всех сайтов: поиск нормального соигрока — это тонна времени. (ряльна многа). И дело не в людях, а в самом поиске. Ты пролистываешь десятки людей с разными интересами, пока не наткнёшься на того самого. Я подумал: а что если наоборот? Повесить объявление о том, кого ты ищешь, и ждать, пока нужный человек найдёт тебя сам. Выставить нужного человека в розыск, так скажем.
Так появилась доска сюжетов (источником вдохновения был ЧатВдвоем), и проблем с ней было много. Непонятно было, как сделать, чтобы объявление сразу связывало тебя с собеседником, а не превращалось в ещё одну анкету. И как удержать людей на сайте, чтобы все не уходили в Telegram.
Сначала мне казалось важным, чтобы люди ролили именно в пределах моей платформы. В итоге я от этой идеи отказался: моя задача — помочь найти собеседника, а где вы будете играть, решать вам. Поэтому к объявлению можно привязать свой Telegram, и тот, кто его увидел, сразу напишет напрямую. А кто хочет остаться на сайте, откликается и сразу попадает к автору в чат. Объявления к тому же уходят в Telegram‑бота, чтобы их видели и те, кто сейчас не на сайте. (Вроде норм компромисс)
Вокруг доски вырос и остальной сайт. Чат разделился на три раздела: просто ролка, ролевая с ИИ и флирт. Разделы с общением и флиртом я просто вырезал, отныне они просто мешали, так как потенциально распределяли аудиторию между режимами, что при маленьком онлайне убивало сайт.
А так как у меня акцент на ролевых играх, пришлось расширять количество механик. Для
ролевиков важно, с кем ты переписываешься: одним нравится короткая переписка в пару строк, другие пишут по абзацу на пост, а иногда даже целые мини истории на несколько лимитов сообщений (в телеге). А это значит, что обычный поиск мне не подойдет. (возвращаемся к проблеме подкидывания случайных собеседников.)
Поэтому появились репутация и мастерство. Репутация растёт, когда нормально общаешься, и падает за грубость и жалобы, отсеивая совсем уж неадекватов от основного пула пользователей, а мастерство считается по качеству постов. Считается разнообразие слов, количество знаков препинания, количество предложений и прочее и прочее. И исходя из всех этих метрик человеку присваивается, так называемый «класс письма»: от «однострочника» до «романиста». Подбор собеседников учитывает как репутацию, так и мастерство, чтобы романист не встречался с однострочником и наоборот.
P. S. Система вышла весьма интересной, но на момент октября 26 года, так себя и не проявила. Так как онлайн пользователей не набрал необходимой критической массы.


Момент, когда проект перестал помещаться в моей голове
Сайт я делаю с помощью ИИ. Скрывать это смысла нет, так как опытные юзеры сразу поймут это из дизайна. Несмотря на то, что идея зародилась еще в начале февраля, а активная разработка началась в марте, я только в июне перенёс проект под нормальный контроль версий, и уже тогда это было больше 150 тысяч строк. С тех пор набралось около полутора тысяч коммитов и почти миллион строк кода, из которых половину это тесты. О чем позже. (с момента начала разработки до текущего момента)

Примерно тогда же пришлось взрослеть по инфраструктуре. Сначала всё жило на SQLite, но я быстро переехал на PostgreSQL, так как старая база данных была.. медленной. (у меня нездоровое стремление все оптимизировать под десятки тысяч пользователей, хотя по факту их у меня едва ли наберется даже тысяча.)
Ещё появилась резервная статичная версия сайта на случай, если основной сервер лежит. (А он лежал часто).
Несмотря на то, что актива на сайте особо не было (так как тогда в тг сидело меньше сотни человек, не особо заинтересованных в проекте. Что в целом неудивительно, так как я их набирал в анонимном чате, буквально уговаривая присоединиться в тг. Кровью и потом я собрал тогда 100 человек и остановился), я решил запариться на счет оптимизации сайта. Зачем? Да черт его знает. Мне показалось, что сайт должен иметь возможность выдержать нагрузку в тысячи человек, в то время, как на него заходили единицы.
Я всерьёз испугался, что сайт не выдержит нагрузки, которой не было. (И до сих пор нет). Из‑за чего несколько недель потратил на кластеризацию: вместо одной копии приложения теперь работают две, и при обновлении сайт не падает. (к тому же я могу таких копий запустить сколько угодно, лишь бы ядер на сервере хватало)
Звучит просто, но для чата это боль. Вот что пришлось решать:
Собеседники на разных копиях. Балансировщик закрепляет каждого пользователя за одной копией, но два человека из одного диалога вполне могут попасть на разные. Сообщения между копиями ходят через Redis: Socket.IO отправляет событие в общую «комнату», и его получают обе стороны, где бы они ни сидели.
Состояние в памяти. Очередь поиска, комнаты, лимиты на спам раньше спокойно лежали в памяти процесса. На двух копиях каждая видела бы только свою половину, поэтому всё это переехало в Redis и базу.
Гонки. Две копии могли одновременно взять из очереди одного и того же человека и свести его сразу с двумя собеседниками. Пришлось делать выбор из очереди атомарным: человека забирает тот, кто успел первым, второй получает отказ. Кто первый взял, того и тапки.
Фоновые задачи. Чистка старых чатов и рассылки не должны выполняться дважды. Задачу берёт та копия, которая первой получила «аренду» в Redis, а сама задача написана так, чтобы повторный запуск ничего не ломал.
А если упадёт сам Redis? Это был самый неприятный вопрос. Обычные функции переходят на запасной режим через базу и работают медленнее, но работают. А всё, что связано с безопасностью и деньгами, наоборот, честно отказывает и пишет об ошибке: лучше не дать сделать действие, чем сделать его дважды.
Нагрузки, ради которой всё это делалось, пока, честно говоря, нет. Но когда она придёт (а я надеюсь, что придёт), переписывать ничего не придётся.
Вообще, как вы заметили, я довольно оптимистично отношусь к своему детищу, хотя за полгода в телеграм‑канал пришло всего около 250 человек. Но, как говорится, оптимизм — это недостаток информации. Так оно и оказалось: таких проектов, как у меня, оказалось много, а заначит продвигать ее будет очень сложно. Да идея сайта только с живыми людьми быстро себя исчерпала. Я сделал всё, что хотел, но этого оказалось мало, чтобы сайт стал по‑настоящему удобным местом для времяпрепровождения в виртуальных мирах.
Ролки с ИИ
К тому же я иду в ногу со временем, так что приоритеты пришлось пересмотреть ещё раз. К июню я сам плотно сидел в ботах для ролевых игр и понял, что они меня тоже не устраивают. (Да, если уж делать, то сразу во всех направлениях, которые меня не устраивают. А меня не устраивает почти все.)
В этот период я явно осознал в чем проблема живых (игроков). У них есть своя беда: их не всегда найдёшь в два часа ночи, а история обрывается, как только человек решает, что сон важнее выдуманных историй. Поэтому летом я добавил то, ради чего, честно говоря, сейчас всё и делается: ролевые игры с ИИ‑персонажами. Создаёшь мир и персонажей с характером, предысторией и манерой речи, а дальше играешь, сколько хочешь.
Быстро выяснилось, что подключить нейросеть можно минут за двадцать, а сделать так, чтобы с ней было интересно играть, — это месяцы. Вот на что ушло больше всего времени.
Память. Модель видит только последние сообщения, поэтому всё важное из прошлого нужно хранить отдельно и подкладывать ей в нужный момент. Тут меня ждал неприятный сюрприз: бот начал зацикливаться и по кругу пересказывать одно и то же. Оказалось, что он сам себя кормил: из его же ответа извлекался факт, по этому же ответу строился поиск, находился этот же факт, и модель повторяла его снова. Разорвать эту петлю удалось только несколькими правками сразу. Главный вывод: в «тупости» ИИ чаще всего виновата не модель, а то, что ей подают. (Хватит хейтить DeepSeak за тупость. Это не он тупой, а, вероятнее всего, неправильный промпт‑инжиниринг)
Время. В обычных ботах ночь может длиться десять ходов. У моих же миров есть часы: время идёт от действий, а в подробных режимах персонажи устают, хотят есть и спать. (хотя и стоит признать то, что пока это работает криво) Часы дают и кое‑что ещё: по ним можно подкидывать события через определённые промежутки и точнее считать состояние персонажей. А чем точнее считается состояние, тем детальнее можно симулировать характер. Правда функционала для таких событий я пока не добавил. В процессе.
Характер. Если прописать персонажу манеру речи, через пару ходов он обычно начинает звучать как все. Пришлось разбираться, как модель распределяет внимание по длинному тексту, и перестраивать подачу.

Как я работаю один
Без помощников такой объём один человек не вытянет, поэтому значительную часть кода я пишу вместе с ИИ‑агентами. Ну точнее, чаще всего, они пишут, а я им только идеи реализации накидываю. А то вроде кодекс уже чуть ли не на сверх интеллект претендует, а решить логично задачу — не всегда. А еще и сломать умудрится все по пути.
Поэтому главное, во что я вкладываюсь, — тесты. И тут я, пожалуй, перегнул палку: на них уже больше 400 тысяч строк кода. По сути, большую часть времени ИИ пишет не новые функции, а страховку, чтобы не сломать уже сделанное. Логично ли это? Не уверен. Друг говорит, что я страдаю херней. Я же гордо продолжаю: сделал кусок проекта — залил его бетоном из тестов, чтобы быть уверенным, что никакой дурак его случайно не сломает. (обычно в роли дурака выступаю я, давая максимально неразумной модели супер сложную задачу. В итоге он все ломает. Удивительно, да?)
Но и тут не всё гладко: у такого подхода есть свои минусы. Бывало, что функцию уже починили, а тесты всё ещё ждали старое поведение. Пару раз модель из‑за этого откатывала большие изменения, лишь бы тесты снова стали зелёными. Спасал Git, да и было это на старых моделях, кажется, на GPT-5.5. На новых такого пока не замечал.
Зато урок вышел ценный. Как‑то я починил баг: тест зелёный, всё отлично. А в проде не работало ничего. Оказалось, тест проверял функцию напрямую, а настоящий код до неё даже не доходил. Тест был зелёным, а код за ним — мёртвым.
Так я открыл для себя мутационное тестирование. Идея простая: прежде чем верить тесту, нарочно ломаешь в коде именно то, что он должен проверять, и запускаешь его. Упал — значит, тест работает. Не упал — он ничего не проверяет, и его надо переписать. Это медленнее, зато тесты наконец говорят правду.
Что получилось за полгода
Соцсеть я так и не сделал. Зато Katomu стал полностью ролевым чатом: от анонимной болтовни остались только ролки, с живыми людьми и с ИИ. Соигрока сайт подбирает по репутации и по тому, как человек пишет, а если ждать подбора не хочется, можно повесить сюжет на доску, и он улетит ещё и в Telegram. Появились друзья, личные чаты и приложение для Android. Ролки с ИИ помнят, что было раньше, живут по часам мира и держат характер.
Ближайшее, что выйдет, — персонажи с собственными целями. Они будут не только отвечать игроку, а сами чего‑то добиваться, пробовать разные способы и не сдаваться после первой неудачи. Как это устроено, расскажу отдельно, когда механика выйдет.
Но ролки — это только начало. Я хочу сделать самостоятельную площадку для творчества: место, где пишут фанфики и собственные истории, ролят с людьми и с ИИ, а сыгранная история не теряется в переписке и может стать рассказом. Раздел фанфиков уже ждёт своего часа, просто до него пока не дошли руки. И главное, я хочу собрать вокруг этого своё комьюнити: людей, которым интересно сочинять вместе.
Забавно, но в каком‑то смысле я вернулся к тому, с чего начинал. Я хотел, чтобы знакомство в анонимном чате не заканчивалось на «пока». Похоже, для этого нужна была не лента с лайками, а общая история.
Если хотите посмотреть, что из этого выйдет, я пишу о проекте в телеграм‑канале: t.me/KatomuChat. А в комментариях буду рад вопросам и про разработку в одиночку, и про то, как заставить ИИ не нести чушь.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.