Конструктор ИИ-девушки: как настройки превращаются в персонажа
Разработчик ИИ-приложений, работает с языковыми моделями с 2022 года. Создал несколько чат-ботов с памятью и характером.
Конструктор персонажа — это форма, где вы отмечаете чипы и нажимаете «создать». Внутри всё намного проще, чем кажется по интерфейсу, и намного честнее, чем обычно бывает в таких интерфейсах. Почти всё, что вы выбрали, превращается в три текстовых поля, которые потом уходят в системный промпт чата и в промпт генератора картинок.
Статья написана разработчиком, который и писал этот код, поэтому здесь нет ни одного обещания, которое нельзя проверить по исходникам. Там, где пайплайн чего-то не умеет, я так и скажу: не умеет.
Что конструктор умеет настраивать, а что — нет
Набор настроек фиксированный и заметно уже, чем кажется на первый взгляд. Всё сводится к восьми группам полей:
- Кто это — пол и имя до 32 символов. Имя дополнительно санитизируется: вырезаются угловые и фигурные скобки, квадратные, обратная и прямая косые черты и часть служебных символов, пробелы схлопываются, длина обрезается до 32. Причина простая: имя рендерится на сайте и одновременно уходит в промпт, а значит является потенциальным вектором для инъекции служебных меток.
- Стиль — реалистичный или аниме. Это не косметика: стиль выбирает image-модель, набор негативных тегов, арт-дирекшен и весь способ удержания лица.
- Внешность — этничность (7 вариантов), цвет волос (7), длина волос (3), цвет глаз (5), телосложение (6 для женского персонажа, 5 для мужского), рост (3) и, только для женского персонажа, размер груди (3), плюс свободный текст до 600 символов.
- Характер — 12 черт-пресетов (заботливая, игривая, доминантная, покорная, застенчивая, уверенная, интеллектуальная, загадочная, романтичная, ревнивая, весёлая, опекающая) плюс свободный текст до 600 символов.
- Первая реплика — приветствие до 400 символов. Поле необязательное, но с практическим последствием: если его не написать, персонаж откроет пустой чат молчанием.
- Стиль близости — слайдер откровенности от 0 до 100 (по умолчанию 34) и 8 чипов-предпочтений (романтика, доминация, подчинение, ролевые игры, пошлые разговоры, дразнилки, собственничество, похвала), плюс свободный текст.
- Приватность — личный или доступный по ссылке.
- Превью-картинка — техническое поле, о котором отдельно.
Теперь то, чего в конструкторе нет, — и это важнее, чем список того, что есть:
- Возраста. Поля нет вообще. Возраст — не параметр внешности, а юридическое требование: все персонажи сервиса взрослые, и это задано на уровне модерации и негативных промптов, а не пользовательской настройкой.
- Одежды. Чипа одежды нет, и это сделано осознанно. Одежда описывается в промпте сцены на каждом шаге — модель сама выбирает уровень раздетости по контексту.
- Голоса. Поле voice_id в API ещё принимается, но принудительно сбрасывается в None — выбор голоса временно убран из интерфейса.
- Био и описания. Поле description в модели есть, но конструктор его не заполняет: описание в каталоге собирается из personality_prompt, а пользовательские персонажи в общий каталог не попадают вообще.
- Редактирования после создания. Эндпоинта изменения нет. Есть создать, посмотреть список, удалить. Опечатку в характере придётся исправлять удалением и пересозданием — вместе со всей историей переписки.
Как настройки превращаются в промпт
Вот механика целиком, без маркетинговой обёртки. На бэкенде есть функция сборки визуального профиля. Первым идёт подлежащее — «красивая женщина» или «красивый мужчина», и это тоже выбирается по полу. Дальше в фиксированном порядке подставляются теги: этничность, цвет волос, длина волос, цвет глаз, телосложение, рост и, если персонаж женский, грудь. Каждому чипу соответствует короткая английская фраза: «brunette hair», «medium bust», «Slavic features».
Порядок неслучаен и зафиксирован в коде. Дальше идут черты с фото, если они есть, потом ваш свободный текст; всё склеивается точками в один текст. Если не выбрано вообще ничего, подставляется нейтральная заглушка «attractive adult character», чтобы промпт никогда не оказался пустым.
Характер собирается отдельно и иначе. Двенадцать пресетов — это не декоративные подписи, а готовые фразы с родом: «заботливая, внимательная к чувствам собеседника» и «заботливый, внимательный к чувствам собеседника» — это разные строки под одним ключом. Если вы ничего не выбрали, подставляется «живая, эмоциональная личность». Ваш свободный текст добавляется после пресетов, а не вместо них: пресеты задают грамматический каркас, свободный текст его дополняет. Если бы он заменял пресеты, пустое поле оставило бы personality_prompt пустым, и модель получила бы персонажа без характера вообще.
Стиль близости собирается из двух частей. Слайдер от 0 до 100 переводится в одну из четырёх фраз-якорей по порогам: 0 — «романтичная и сдержанная…», 34 — «раскрывается постепенно, комфортно флиртует…», 67 — «открыто флиртует и уверена в своей сексуальности…», 85 — «очень раскрепощённая и откровенная…». Четыре ступени, а не сто: сглаженная фраза звучит в разговоре заметно хуже, чем четыре осмысленных состояния, между которыми модель переключается по контексту. Поверх якоря накладываются выбранные чипы и ваш свободный текст.
И последний штрих механики. Три собранных поля попадают в системный промпт, который пересобирается на языке пользователя. personality_prompt и intimacy_profile остаются на русском, каким и были собраны: системный промпт и так строится из русских фраз, а переводить их на каждом сообщении — лишний вызов модели. А вот visual_profile переводится на английский один раз, при создании персонажа, потому что image-модели обучены на английских тегах. В базе у пользовательского персонажа внешность лежит уже на английском.
Как задаётся внешность: seed, превью и загруженное фото
Первый — seed. У каждого персонажа есть поле reference_seed, и при первом же запросе фото оно проставляется по формуле (id * 7919 + 104729) mod 2^31. Это не случайное число и не сгенерированный UUID — детерминированная функция от идентификатора персонажа в базе. Смысл в том, чтобы у одного и того же персонажа стартовая точка генератора всегда была одна и та же: при неизменном промпте вы получаете не тот же кадр, но очень близкий по композиции и цвету. Второй важный момент: в момент создания это поле равно None. Seed назначается лениво, при первой генерации.
Второй — превью. Кнопка пробной генерации создаёт временный, несуществующий в базе объект персонажа и просит у image-модели один кадр в полный рост с нейтральным сценарным промптом. Seed этого объекта считается от id пользователя, а не персонажа, — то есть в пределах одной учётной записи повторные превью стабильны. Дальше развилка: если вы нажали «создать» и фронт отправил ту же картинку в поле превью, она сохраняется как мастер-портрет и сразу становится аватаром.
Третий — загрузка фото. Загруженное фото не сохраняется: байты уходят в один vision-вызов и выходят из области видимости. Модель возвращает не лицо, а список общих черт — цвет и длина волос, цвет глаз, примерное телосложение и рост, тон кожи, заметные стилевые детали вроде очков или макияжа. Ограничение — 40 слов. Системный промпт этого вызова прямо требует описывать «другой, вымышленный персонаж, вдохновлённый стилем» и запрещает называть или подразумевать, кто это. Отдельная проверка отклоняет фотографии несовершеннолетних, узнаваемых публичных людей и снимки без одного явного лица человека. Фича специально спроектирована как «вдохновиться», а не как «клонировать».
Теперь честная часть про identity-ноду. Узлы, которые переносят черты лица с референса в результат, в проекте есть — они используются в резервном пайплайне на RunningHub, куда мастер-портрет загружается и подставляется в ноду изображения воркфлоу. Но основной фото-пайплайн сейчас идёт через Venice и это чистый text-to-image: ни мастер, ни identity-нода в нём не участвуют, всё держится на якорных тегах плюс seed. Причина — замеренная: полноценная генерация аниме-кадра через edit-пайплайн занимала около 30 секунд против примерно 5 у text-to-image при выигрыше в стабильности лица, который на практике покрывался тегами.
Чего конструктор не делает и почему
Главное, чего не делает конструктор, — он не позволяет «просто загрузить фото и получить такого же человека». Не из этических соображений и не из лени, а по трём техническим причинам.
- Vision-вызов не умеет возвращать параметрический вектор лица. Чтобы сгенерировать «тот же» человек, нужен либо эмбеддинг лица, либо референсное изображение в ноде. Ни то, ни другое мы не получаем — вместо этого получаем 40 слов тегов, и по ним строится похожий вымышленный персонаж.
- Копия реального лица — это идентификация человека. Собственный код это запрещает: промпт vision-вызова требует описать другого персонажа. Такой запрет легко обойти в другом продукте, но здесь он зашит в ту же строку, которая формирует описание.
- Предсказуемость не равна узнаваемости. Даже с идеальной identity-нодой результат — не фотография того же человека, а перерисовка по нему. Обещать «загрузи фото — получишь его же» было бы обещанием, которое пайплайн не может выполнить.
Что происходит после сохранения
Здесь конструктор устроен иначе, чем можно ожидать от «публикации в каталоге». Пользовательские персонажи не попадают в общий каталог вообще. Это не побочный эффект, а осознанное решение: общий каталог собирается из системных персонажей плюс одобренные публичные пользовательские, но конструктор принимает только два значения приватности — «личный» и «доступный по ссылке», а любое другое значение, включая «публичный», приводится к «личному». Ничего из созданного вами не может попасть в общую витрину.
Модерации «на входе» для персонажа тоже нет — он сразу создаётся в статусе «одобрен». Но проверки есть: имя, итоговая внешность (уже на английском) и интимный профиль проходят через стоп-лист, и если что-то срабатывает, персонаж не создаётся вовсе.
Ограничение по количеству — 30 персонажей на аккаунт, при превышении приходит ошибка 429. Доступ ровно такой: личный — только вы; доступный по ссылке — любой, у кого есть ссылка. Ссылка не перебирается, потому что это случайный токен, а не последовательный идентификатор, и страница за ней закрыта от индексации.
Сайт и Telegram
На сайте кнопка открывает тот же виджет чата, что и системные персонажи, только с вашим идентификатором и токеном ссылки — виджет сам передаёт их в API, и доступ проверяется как «системный персонаж разрешённого стиля, владелец или предъявленная ссылка». Ваш персонаж проходит по второй ветке.
С Telegram всё тоньше, и это отдельное решение, а не недосмотр. После создания выдаются две ссылки, и они не взаимозаменяемы. Первая — личная, с токеном привязки: открыв её, вы связываете свой Telegram с веб-аккаунтом и получаете доступ к персонажу. Её нельзя пересылать, потому что чужой Telegram, открывший её, привязался бы к вашему аккаунту. Срок жизни токена — 30 дней. Вторая — по токену ссылки: её можно отправлять кому угодно, она работает и для личного персонажа, и ничего не привязывает.
Токен личной ссылки пишется сразу в три базы Redis — русского, английского и португальского ботов, потому что у каждого своя база и каждый читает токен только из своей. Это мелочь в коде и очень неочевидная причина, по которой английская версия ссылки работала бы «сама», если бы разработчик не подумал об этом заранее. Сам бот при этом один на язык: русская, английская и португальская версии раздельны намеренно, потому что язык влияет на системный промпт персонажа, на регистр и на способ обращения, а переключение языка посреди разговора ломает персонажа.
Сколько это стоит и сколько занимает
Само создание персонажа бесплатно. Платный здесь ровно один шаг — пробная генерация в превью: она тарифицируется как обычное фото, то есть 14 кристаллов.
Загрузка фото для вдохновения — единственная функция конструктора, которая требует именно платной подписки, любой из трёх. На бесплатном тарифе сервер отвечает кодом 402, и фронтенд показывает предложение обновиться.
По времени: отправка формы — это сборка трёх строк, предварительный фильтр и запись в базу, то есть доли секунды. Генерация приветствия, если вы его не написали, — один вызов языковой модели, и он синхронный: персонаж не создастся мгновенно.