← В блог
    Кейс · AI-продукт в проде23 июня 20268 минут чтения

    «Ближе»: бот, который я довёл до прода и продолжаю вести

    В 1997 психолог Артур Арон усадил двух незнакомцев напротив друг друга и дал список из 36 вопросов. Они шли по нарастающей: от «с кем бы ты хотел поужинать» до «когда ты последний раз плакал при другом человеке». В конце — четыре минуты молча смотреть в глаза. Часть пар после эксперимента сошлась. Одна — поженилась.

    Вопросы сближают. Но только когда отвечают двое и слышат друг друга, а не подсматривают и не подстраиваются. Я взял этот принцип и собрал на нём продукт. Собрал нейронками — я ИИ-консультант и строю тем же инструментом, который продаю. Но архитектуру, развилки и прод-реальность тащу я, а не нейронка. Спроектировал, довёл до прода, продолжаю вести. Бот живёт под именем «Ближе», работает каждый день и делает ровно то, ради чего сделан: двое узнают друг друга глубже.

    Кейс не про «сделал бота». Кейс про то, как я довожу AI-продукт до прода нейронками — и что начинается ПОСЛЕ запуска. Потому что «после запуска» как раз и отделяет работающий продукт от папки на гитхабе.

    Слепое взаимное раскрытиеэто механика, при которой оба партнёра отвечают на один вопрос порознь, и ответ другого открывается только когда ответили оба. Никто не видит чужой ответ заранее и потому не может под него подстроиться — каждый отвечает честно. Это ядро продукта и главный инвариант, вокруг которого построено всё остальное.

    Зачем нужен бот и почему он работает?

    Цель простая: чтобы пара после бота знала друг о друге больше, чем до. Не «поиграли и забыли», а «о, не знал, что ты так думаешь».

    Этот момент не случайность. Он заложен в механику. Ядро — слепое взаимное раскрытие: оба отвечают на один вопрос порознь, и ответ партнёра открывается только когда ответили оба. Никто не видит чужой ответ заранее, а значит, не может под него подстроиться. Каждый отвечает честно — и потому раскрытие бьёт.

    Цель достигается не уговорами, а архитектурой. В этом и кейс.

    Что у бота внутри?

    Коротко: связка в пару, сессии вопросов с нарастанием близости, вопрос дня, игра «угадай ответ» и стрики. По пунктам:

    • связка в пару по коду или по пересылаемой ссылке: диплинк → один клик → вы в паре
    • сессии по 5 вопросов с нарастанием близости, 4 настроения на 3 уровнях, 128 вопросов
    • раздел «Страсть» открывается только по обоюдному согласию
    • вопрос дня каждый день, с умным напоминанием тому, кто молчит
    • игра «угадай ответ»: сначала отвечаешь сам, потом угадываешь партнёра
    • стрики, история раскрытых ответов, админка со статистикой и рассылкой

    Как устроена архитектура?

    Главное решение — развести чистую логику и обвязку Telegram. Вся суть продукта живёт в модулях без единого импорта телеграма, и потому полностью под тестами. Телеграм — тонкий слой сверху.

    app/
      чистая логика (без Telegram, под тестами)
        pairing.py   связка пары, код, разбор приглашения
        reveal.py    правило раскрытия: видно только когда ответили оба
        sessions.py  подбор вопросов по нарастанию близости
        streaks.py   стрики и их пересчёт
        daily.py     расписание и выбор свежего вопроса дня
      данные
        db.py        SQLite-слой, схема, миграции
      обвязка Telegram (тонкая)
        handlers/    старт, сессия, вопрос дня, админка, роутер
        runner.py    сборка, регистрация, фоновый планировщик

    Почему так, а не «всё в одном файле»? Потому что продукт про правила, а правила надо проверять. Когда логика отделена от телеграма, её гоняют тесты — и при любой правке раскрытие, стрики и подбор вопросов не разъезжаются.

    Какие решения и сделали продукт?

    Кейс не в том, что «сделали бота». Кейс в решениях.

    Слепое раскрытие как инвариант

    Ответ партнёра не виден, пока не ответили оба. Одно правило в одной функции, весь продукт построен вокруг него.

    Одна активная сессия на пару

    Чтобы пара не открыла три разговора разом и не запуталась. Простой инвариант, который снимает целый класс багов.

    База под реальные условия

    Сначала данные жили во внешней облачной базе. На сервере она дышала через раз: запрос то 0.3 секунды, то 14, то таймаут. Перенёс на локальный SQLite рядом с ботом — стало 0.0003 секунды. Урок: считаться с реальной инфраструктурой, а не с идеальной.

    Отказ от лишнего

    Был соблазн сделать режим «отвечать с одного телефона по очереди». Убил: он размывает ядро, слепое раскрытие работает только когда у каждого свой экран. Вырезал на уровне замысла, а не доделывал ненужное.

    Чем сборка от архитектуры отличается от сборки от функций?

    Сборка от функций гонится за количеством возможностей и надеется, что одна выстрелит. Сборка от архитектуры выбирает одну механику-ядро и подчиняет ей всё остальное — «Ближе» собран так.

    От функцийОт архитектуры (этот проект)
    Откуда берётся результатБольше функций, авось одна выстрелитОдна механика-ядро, всё работает на неё
    Как решается объёмДелать всё, что проситсяРезать то, что не служит ядру
    Логика ядраСплетена с платформойОтделена, под тестами
    Поведение на краяхЛатается баг за багомДержится инвариантами
    Роль автораКодер запросовТот, кто ведёт систему

    Продукт сдан. Тут работа и начинается

    «Бот готов» — сказал бы тут другой и закрыл проект. Живой тест прошёл, ответ партнёра открывался мгновенно. Я выдохнул. Рано.

    Через пару недель я полез в живую базу — просто посмотреть, как идут реальные диалоги. И увидел: один ответ лёг не под тот вопрос. Не «теоретически может», а вот он, в данных. Картинка собралась быстро: человеку приходил новый вопрос дня, а он отвечал на старый, который висел выше в чате — и ответ уезжал не туда.

    Чинится это не заплаткой. Чинится двумя решениями:

    • Ответ привязан к тому вопросу, который человеку реально показали — а не к «текущему вопросу пары». Telegram не говорит, на что именно отвечают, поэтому привязку держу я.
    • Полу-готовый вопрос больше не подменяется. Если один ответил, а второй молчит — там живёт запертый ответ, который ждёт второго. Раньше планировщик мог его затереть новым. Теперь не трогает.

    А раз уж залез — докрутил то, ради чего продукт и живёт, удержание двух людей:

    • стрик прощает один пропущенный день: одна случайная пропажа не обнуляет накопленное
    • замолчавшую пару бот мягко возвращает лесенкой, а не нудит в пустоту
    • как только один ответил — второму сразу летит «партнёр ответил, открой и увидишь»
    • догадку можно пропустить, раунд раскроется и без неё
    • совпала догадка с ответом — отдельный маленький праздник в раскрытии

    Было 62 теста. Стало 108.

    Вот это и есть разница между «сдал и забыл» и «веду». Баг нашёл я — читая живую базу руками. А не клиент, у которого бот «почему-то путает вопросы».

    И тут важное. Код написала нейронка, быстро. Но нейронка не пойдёт сама читать живую базу и ловить, что один ответ лёг не под тот вопрос. Не примет решение, чем привязку держать. Не выкатит и не проверит на живых данных. Код теперь дешёвый — это умеет каждый. Дорого другое: довести его до того, чтобы он реально работал на людях. Вот это и есть моя работа.

    Это реально работает?

    Да. Всё в проде. Развёрнуто в Docker, база локальная, работает каждый день. 108 автотестов зелёные. Код модульный, с разделением слоёв. Найденный в бою баг закрыт, удержание докручено, изменения выкачены и проверены на живых данных.

    Что это демонстрирует?

    Это витрина того, как я работаю. Не «накидать функций», а:

    • развести чистую логику и обвязку, чтобы суть продукта была под тестами
    • держать инварианты вместо латания багов поодиночке
    • считаться с реальной инфраструктурой, а не с идеальной
    • довести до прода — и не бросить там, а вести по живым данным

    И связать всё это с целью. Бот существует не ради фич, а ради того, чтобы двум людям стало теплее. Архитектура тут не самоцель, она работает на близость.

    Что я предлагаю?

    Беру вашу идею и довожу до работающего AI-продукта в проде. А потом веду его — потому что продукт, который не ведут, тихо умирает.

    Кому это

    • Бизнесу с аудиториейкоучам, школам, комьюнити, сервисам. Бот, который вовлекает и удерживает людей, а не «ещё один чат, который полистали и забыли».
    • ФаундерамMVP, который не стыдно показать инвестору и пользователю. С архитектурой, тестами и в проде, а не «работает на моей машине».

    Как устроено

    • Сборка под ключ нейронкамиот идеи до прода, быстро. Я строю тем же инструментом, что продаю. Но направляю его, ловлю то, что он проморгал, и довожу до работающего: чистая логика отделена от обвязки, покрыта тестами, развёрнута.
    • Саппорт на MRRя не исчезаю после запуска. Слежу за живыми данными, нахожу дыры раньше пользователей, докручиваю то, что держит людей в продукте.

    Почему именно так. Половина «готовых» продуктов умирает не от плохого кода, а от того, что их сдали и бросили. Баг находит пользователь. Удержание никто не считает. Лента замолкает, и все делают вид, что так и было. Я закрываю ровно этот разрыв: довожу до прода и остаюсь рядом, пока продукт живёт.

    «Ближе» — не демо «на коленке». Это работающий продукт, на котором видно весь мой подход: от слепого раскрытия как инварианта до бага, пойманного в живой базе руками.

    Есть идея, которую пора довести до прода?

    Или продукт, который запустили и забросили? Напишите. Гляну, скажу честно: что реально сделать, а что звездёж.

    Вопросы и ответы

    Роман Денисов

    Об авторе

    Роман Денисов

    ИИ-консультант

    MBA (МИРБИС), 17 лет в маркетинге и продажах B2B. Проектирую ИИ-системы и нейроагентов, которые автоматизируют продажи, контент и поддержку — и растят выручку.

    Подробнее о Романе