Лазурит

Парсер сказал 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. Из десяти тысяч — семьдесят процентов.

Триста девятнадцать платили — первая очередь. Каждому — два варианта сообщения, ссылка на переписку, поля для отслеживания работы.

Отправил Вадиму таблицу. Он открыл, пролистал, нажал на первую ссылку — открылась переписка с клиенткой, которая платила полгода назад. Рядом — готовый текст. «Ну что, начинаю?»

Начинай.

Сорок пять сообщений за два дня. И одна строчка в заметках, которая изменила всё.

← 1.2 1.4 →