Парсер сказал 92% горячих. Он врал
Два архива. Три с половиной гигабайта. Ноль читаемого текста. Вадим говорил — семь с половиной тысяч диалогов. Instagram выгрузил 10 119. На 35% больше, чем ожидали. Разница объяснилась быстро. Вадим считал тех, с кем реально общались. А Instagram хранит всех — включая тех, кто написал «здравствуйте» и пропал навсегда. Одно сообщение — уже отдельный файл в архиве. Десять тысяч диалогов. Где-то внутри — люди, которые платили и приходили. Остальные — те, кто мог бы прийти. Задача — отличить одних от других.
Архив, который не читается
Открыл первый файл. Вместо текста — мусор. Там, где должно быть «Привет, хочу записаться» — набор непонятных символов. Десять тысяч диалогов — и ни один нельзя прочитать.
Instagram хранит русский текст в особой кодировке. Буквы при выгрузке ломаются — каждая превращается в два-три бессмысленных символа. Проблема известная, Instagram не чинит её годами. Починил сам — написал функцию, которая берёт сломанный текст и восстанавливает оригинал. Двадцать минут — и десять тысяч диалогов стали читаемыми.
Техническая деталь: кодировка Instagram
Instagram при выгрузке архива ломает кириллицу — это известная проблема, которую не чинят годами. Суть: русский текст проходит двойную перекодировку (UTF-8 → Latin-1), и каждая буква превращается в набор бессмысленных символов. Решается обратным преобразованием — буквально три строки кода. Если работаешь с Instagram-архивом на русском — скажи агенту: «двойная перекодировка Latin-1/UTF-8, нужно обратное преобразование для всех текстовых полей». Без этого — ни один диалог не прочитать.
Девяносто два процента горячих
Текст читается. Запускаю парсер — программу, которая должна разложить десять тысяч диалогов по категориям. Логика простая: если в переписке есть слова «оплата», «запись», «карта» — значит клиент горячий.
Результат: 9 287 из 10 108 — горячие. Девяносто два процента базы.
Красивая цифра. И абсолютно невозможная.
Открываю «горячие» диалоги один за другим. Картина одна и та же: салон отправил шаблон со словом «оплата», клиент не ответил ни разу. Парсер увидел ключевое слово и записал его в горячие. А человек по ту сторону экрана даже не открыл сообщение.
Ошибка не в коде. Ошибка в логике. Парсер искал слова во всём диалоге — включая сообщения самого салона. Шаблонная рассылка со словом «оплата» улетала каждому — и каждый автоматически становился «горячим».
Три попытки — один метод
Починил. Теперь парсер ищет ключевые слова только в диалогах, где клиент хоть раз ответил. Результат: 2 963. Показываю Вадиму — он не верит. Открываем конкретные диалоги. Клиент написал «здравствуйте, подмышки», а слово «оплата» — по-прежнему из шаблона салона. Ответил — да. Обсуждал оплату — нет.
Третья попытка. Ищу слова оплаты только в сообщениях самого клиента. Результат: 42. Половина — рекламные аккаунты типа «ЛУЧШАЯ ОПЛАТА КАРТОЙ».
9 287 → 2 963 → 42. Три прогона, один и тот же метод — ключевые слова. Каждый раз казалось: вот теперь правильно. Каждый раз — нет. Клиенты не пишут слово «оплата» — они говорят «куда скинуть?» или просто молча переводят деньги.
Ключевые слова не работают. Слова врут. Нужен другой подход — проверяемые факты.
Звонок Вадиму
Показываю промежуточные цифры. Категория «были на процедуре» — 89 человек из десяти тысяч. Вадим: «Побольше 89, это точно.»
И дальше — фраза, которая изменила весь подход: «Любая переписка, где есть ссылка yookassa.ru — это 95%, что купили и пришли.»
YooKassa — платёжный сервис салона. Если в диалоге есть ссылка на оплату — человек не просто интересовался. Он достал карту. Это факт, который нельзя трактовать двояко: ссылка либо есть, либо нет.
Сканирую архив. 1 216 диалогов с yookassa. Ещё 141 с dolyame (рассрочка). Ни один из них не попал в категорию «были на процедуре» при первом прогоне — потому что парсер искал слова, а факт оплаты — это ссылка, не слово.
89 превратились в 408. Точность выросла в четыре с половиной раза. Благодаря одному вопросу человеку, который знает свой бизнес.
Никакой алгоритм не знает, что конкретная ссылка означает оплату. Это знает только владелец. Данные без контекста — просто текст.
Что нашёл deep research
Отправил задачу в глубокое исследование — сформулировал все вопросы, описал три провала с ключевыми словами и текущую гипотезу. Вернулось семь разделов. Три вещи перевернули подход.
В архиве Instagram есть не только текст. Есть поля с фотографиями и голосовыми сообщениями. Мы их не проверяли — думали, текст и текст. Сканирую архив: 407 диалогов с фото от клиента. 28 — с голосовыми. Когда человек фотографирует зону для эпиляции и отправляет салону — он не просто интересуется. Он ждёт консультации. Это сильнее любого ключевого слова.
Молчуны — 67% базы. Конверсия 1–3%. При пятидесяти сообщениях в день — 135 рабочих дней на один процент результата. Это меняет стратегию: тратить месяцы на тех, кто ни разу не ответил — не стоит.
И главное: начинать нужно с тех, кто платил. Самая тёплая аудитория, самая быстрая победа.
Остальные находки deep research
Порог «4 сообщения» для определения активности — пустышка. Ни одна платформа не использует простой подсчёт сообщений.
Среди ответов на промо-рассылку салона только 40–50% положительные. Остальные — «стоп», «не интересно», просто эмодзи. Нельзя считать горячим каждого, кто ответил.
Закон 38-ФЗ — каждое сообщение должно быть персональным напоминанием, не промо.
Четыре категории
Исследование, звонки с Вадимом, три провала парсера — всё это привело к простой раскладке. Платил. Писал. Молчал. Не клиент. Четыре категории на основе проверяемых фактов. Всё остальное — фото, голосовые, вопросы о цене — не категории, а контекст. Генератор их видит и использует для текста.
Вадиму нужно открыть таблицу и за секунду понять: этот платил, этот писал, этот молчал. Точка.
Когда, а не кто
Но в «платили» есть Дарья, которая заплатила три недели назад, и Анна, которая заплатила два года назад. Одна помнит салон, другая забыла что записывалась. Одна категория — два разных человека.
Для бьюти-салона время решает больше чем статус. Волосы отрастают, эффект LPG проходит, кожа возвращается. Биология задаёт ритм. Не вернулась через два месяца — уже уходит. Через полгода — ушла. Через год — начинать с нуля.
Перевернули логику. Давность — первичный фильтр, категория — вторичный. Сверху таблицы — те, кто пропал недавно. Внутри — сначала платили, потом писали. Чем ниже — тем холоднее.
Молчали больше года — отсечка. Три тысячи контактов, которые не ответили ни разу за двенадцать месяцев. Рабочая база: 6 947.
Генератор
Таблица с категориями готова. Теперь каждому из почти семи тысяч нужно написать персональное сообщение. Нейросеть берёт одного человека, видит его историю, категорию и давность — пишет текст. Следующий. И так тысячи раз.
Первая проблема: для вызова нейросети нужен API-ключ — отдельный продукт, отдельная оплата. У меня подписка, которая покрывает работу с ИИ через приложение, но не через программу. Два продукта, один бренд, ноль пересечений.
Решение нашлось в том же инструменте, в котором мы работали. Подписка покрывает вызов нейросети через командную строку. Переписал генератор за пятнадцать минут — убрал зависимость от ключа. Заработало.
Техническая деталь: Claude CLI вместо API
Подписка на Claude (Max) не включает доступ к API — это отдельный продукт с отдельной оплатой. Но та же подписка покрывает вызов модели через командную строку в print-режиме. По сути — тот же запрос к нейросети, только без API-ключа. Если у тебя подписка и нужно гонять тысячи запросов из скрипта — скажи агенту использовать CLI вместо API. Ноль дополнительных затрат.
Протестировал на семи категориях. Результат: каждое сообщение — уникальное, тон соответствует категории. Для постоянной клиентки Дарьи — тёплое, по имени, с вопросом. Для молчуна — 42 символа: «Привет! Лазерная эпиляция ещё актуальна?» Без давления, без деталей. Просто проверка — жив ли контакт.
Галлюцинация
Поднял модель на самую мощную из доступных. Первый тест. Дарья Фадеева, категория «платила». Модель написала: «Как ваши впечатления после последнего визита?»
Красиво. Тепло. Лично. И — ложь. Мы знаем одно: в переписке есть ссылка на оплату. Дарья заплатила. Но пришла ли — не знаем. В данных этого нет. А модель уже спрашивает про впечатления. Правдоподобная догадка опаснее очевидного бреда — её невозможно отличить от правды.
В инструкции было написано: «не предполагай, оплата не равно визит». Абстрактное правило. Модель его прочитала — и проигнорировала. Добавил список запрещённых фраз — дословно. «После визита», «после процедуры», «как результат». Не правило — конкретный список. Модель не умеет обойти конкретный запрет — она умеет обойти абстрактный.
Техническая деталь: антигаллюцинационный промпт
Мета-промпт: скармливаешь модели свой системный промпт и просишь найти все места, где генератор может галлюцинировать. Нашёл восемь проблем: нигде не написано «не выдумывай», модель не знает какие поля получит на вход, «факт посещения» — размытое понятие.
Решение: блок входных данных (модель знает ровно семь фактов и ничего больше), семь явных запретов, чеклист самопроверки, список запрещённых фраз дословно. Правила — для людей. Списки — для LLM.
Битые ссылки
Триста девятнадцать сообщений для тех, кто платил — готовы. Ноль ошибок. Собрал таблицу с фильтрами, статусами и кликабельными ссылками на переписку.
Вадим открыл таблицу. Нажал на первую ссылку. Открылся профиль блогерши с тридцатью тысячами подписчиков из Парижа. Это не его клиентка. Вторая ссылка — парень из Казани. Третья — закрытый аккаунт.
Все триста девятнадцать ссылок — мимо.
Instagram экспортирует диалоги в папки, названные по имени собеседника, не по логину. «Катя» — это имя на экране, а за ним может быть любой аккаунт. В архиве настоящего логина нет — Instagram его не выгружает.
Но в имени папки есть длинное число — идентификатор переписки. И у Instagram есть ссылка, о которой мало кто знает: она открывает не профиль, а саму переписку. Нажимаешь — и видишь историю диалога с этим клиентом. Все сообщения, фото, голосовые. Не нужен логин. Прямая ссылка в чат.
И это даже лучше чем профиль. Профиль — «кто этот человек». Переписка — «о чём мы с ним говорили». Вадиму нужно второе.
Техническая деталь: thread ID вместо username
Instagram не включает логины пользователей в архив — только отображаемые имена. Но в имени каждой папки с диалогом есть длинное число — идентификатор переписки (thread ID). Ссылка вида
instagram.com/direct/t/{это число}открывает саму переписку, а не профиль. Если работаешь с архивом — скажи агенту: «строй ссылки через thread ID из имени папки, не через display name». Учти: если имя собеседника состоит из эмодзи, папка будет называться просто числом без подчёркивания.
Тысяча сто в корзину
Пока разбирались со ссылками, генератор работал над второй категорией — «писали». Почти три тысячи записей. Час работы — тысяча сто сообщений готовы.
Но ссылки в них — старые, битые. Те самые, которые ведут к чужим людям.
Генератор сохранял результат только в конце — когда последняя строка обработана. Промежуточных файлов не было. Остановить и сохранить нельзя. Либо ждать до конца и потом чинить ссылки, либо убить процесс и потерять всё.
Убил. Починил парсер, починил генератор, перезапустил с нуля. Тысяча сто сообщений — час работы — в корзину.
Техническая деталь: промежуточные сохранения
Если скрипт обрабатывает тысячи записей и сохраняет результат только в конце — любой сбой уничтожает всю работу. Принцип: сохранять промежуточный результат каждые сто записей. При повторном запуске — проверять, есть ли сохранённый прогресс, и продолжать с последней точки, а не с нуля. Скажи агенту: «добавь checkpoint каждые N записей и resume при перезапуске». Мы потеряли 1 100 готовых сообщений из-за отсутствия этой логики.
Урок: инструмент, который не умеет останавливаться и продолжать — это инструмент, который заставит тебя начинать сначала.
Таблица готова
Парсер переписан трижды. Генератор перестроен. Модель поймана на галлюцинации и обучена заново. Ссылки починены. Промежуточные сохранения добавлены.
Финальные цифры:
- Платили: 319
- Писали: 2 984
- Молчали (рабочие, до 12 месяцев): 3 644
- Не клиенты: 101
- Отсечка (молчали больше года): 3 071
Рабочая база: 6 947. Из десяти тысяч — семьдесят процентов.
Триста девятнадцать платили — первая очередь. Каждому — два варианта сообщения, ссылка на переписку, поля для отслеживания работы.
Отправил Вадиму таблицу. Он открыл, пролистал, нажал на первую ссылку — открылась переписка с клиенткой, которая платила полгода назад. Рядом — готовый текст. «Ну что, начинаю?»
Начинай.
Сорок пять сообщений за два дня. И одна строчка в заметках, которая изменила всё.