Я долго ходил по конференциям, у меня хороший нетворк, и все это длительное время я наполнял CRM контактами, результатами звонков, переписками, договорённостями и тд.
В какой‑то момент в ней оказалось почти 10К человек и вся моя история отношений с ними.
Но накопить данные конечно легче чем применять их так, чтобы была максимальная польза. Чтобы CRM приносила эту пользу, я должен помнить, кого и как там искать, какие фильтры включить, что делать дальше и как масштабировать это.
Я хотел автоматизировать почти всё.
Что было в первой версии моей CRM
Источники данных существовали независимо друг от друга:
Контакты, которые у меня появились после личных знакомств на конференциях и телефонных митингов
Отдельно информация о встречах и звонках за 2022–2026 годы;
архивы Telegram с десятками тысяч диалогов;
карточки людей, заметки, статусы и теги;
отдельная старая CRM, которая умела отправлять сообщения.
После объединения и дедупликации получалось 9 718 сущностей‑контактов.
Но один человек мог присутствовать сразу в нескольких источниках: например, сначала написать в Telegram, потом прийти на звонок, а через год появиться в заметках под каким‑то другим именем.
В общем, определить, какие записи относятся к одному человеку, сохранить происхождение каждого факта и не потерять историю — не всегда просто
Почему я не стал переписывать CRM с нуля
Сначала я хотел взять эту ранее написанную старую CRM, составить список функций и написать вместо нее новую версию, которую смогу сделать идеальнее.
Я от этого отказался.
У старой CRM уже были рабочие механизмы авторизации, отправки сообщений, управления аккаунтами и соблюдения ограничений Telegram. Переписывание всего сразу означало бы повторить несколько лет накопленных исключений и ошибок, часть которых никто уже не помнил.
Я разделил систему на две части.
Старая CRM выполняет механическую работу:
читает и записывает данные;
получает события;
отправляет сообщения;
соблюдает лимиты;
повторяет запрос после временной ошибки;
сохраняет доказательство отправки.
Все решения переходят к агенту. Он решает:
кого сейчас стоит поднять из базы;
почему этот человек важен;
что изменилось после последнего общения;
какое действие будет следующим;
нужен ли вообще контакт сейчас.
Получилась не новая CRM в привычном смысле. Скорее, старая CRM стала руками, а агент — как бы слоем принятия решений.
Как система устроена сейчас
В ней пять основных слоёв:

Немного про каждый слой отдельно.
1. Архив событий
У меня 51 000 Telegram‑диалогов по четырём аккаунтам. Выгружать полтора миллиона сообщений через LLM дорого и медленно. С этой работой лучше справляются обычные скрипты и SQLite:
SELECT dialog_id, MAX(message_date) AS last_contact, COUNT(*) AS message_count FROM messages GROUP BY dialog_id;
Скрипт просто и бесплатно определяет дату последнего общения, количество сообщений и наличие ответа. Модель нужна, когда потребуется понять смысл разговора.
2. Единая карточка
Следующий слой объединяет события вокруг человека.
Контакт может быть записан как:
имя в заметках после звонка;
Telegram username;
числовой Telegram ID;
телефон из WhatsApp;
имя и компания из визитки;
участник группового чата.
Надёжного универсального идентификатора нет. Username меняется, имена совпадают, телефон присутствует не везде.
Поэтому объединение идёт в несколько ступеней:
Точное совпадение идентификатора.
Совпадение по username.
Имя плюс компания или другой дополнительный признак.
Совпадения проверяются, не объединяются автоматически.
В карточке сохраняется происхождение каждого факта. Если два источника противоречат друг другу, система должна показать мне это.
3. Высчитываю все параметры
Я проставил критерий по тому, какие у меня отношения с этим лидом:
— Hot;
— Warm;
— Lukewarm;
— Cold;
— Archived.
Дополнительно каждый контакт получил приоритет реактивации от 0 до 100.
Упрощённо формула такая:
priority = 0.35 × recency + 0.20 × frequency + 0.25 × depth + 0.20 × resurgence − penalty
Recency ‑давность последнего содержательного касания; frequency — количество звонков и разговоров; depth — глубина отношений; resurgence — новый входящий сигнал; penalty — оставшиеся без ответа сообщения.
score = 2 ** (-days_since_event / half_life)
Для разных типов событий используются разные сроки пересмотра.
Например я сейчас делаю обновления такого перерасчета и задачи по моим контактам (моим лидам) такие:
16 — написать сейчас;
77 — надо обработать на этой неделе;
601 — рассмотреть в течение месяца;
остальные — оставить в покое до появления новых данных.
4. Автоматизация
Передавать агенту всю историю CRM при каждом запросе нельзя. Даже если забыть о цене, данных для обработки слишком много.
Сначала работает детерминированный поиск:
SELECT * FROM people WHERE reactivation_priority >= 75 ORDER BY reactivation_priority DESC LIMIT 20;
Для каждого лида собирается информация:
кто это;
откуда мы знакомы;
дата последнего контакта;
последние содержательные сообщения;
результаты звонков;
незакрытые договорённости;
подтверждённые общие интересы;
причина, по которой контакт попал в очередь.
Только после этого подключается языковая модель.
Её задача — не искать человека среди тысяч карточек. Она должна решить локальную задачу: что разумно сделать с конкретным контактом, имея подтверждённые факты.
Если данных недостаточно, правильным результатом считается решение needs_review.
5. Автоматизация коммуникации с лидами
Агент не отправляет сразу сам сообщения.
Сначала он подготавливает действие. Затем это действие получает отдельный транспортный слой, который ничего не знает о стратегии, зато знает ограничения канала.
Он проверяет:
с какого аккаунта можно писать этому человеку;
не превышен ли лимит;
не было ли это сообщение уже отправлено;
не пересекаются ли параллельные кампании;
есть ли идентификатор доставленного сообщения;
нужно ли остановиться после ответа.
Между отправками добавляется случайная задержка. При FloodWait транспорт ждёт и повторяет операцию.
Все аккаунты используют общий журнал бюджета, поэтому два параллельных процесса не могут независимо решить, что лимит ещё свободен.
if not budget.can_send(account): return "LIMIT_REACHED" if already_contacted(person, campaign): return "DUPLICATE" result = send_with_jitter(message) if result.message_id: record_delivery(result.message_id) else: record_attempt()
ATTEMPT означает, что система попыталась отправить сообщение. SENT означает, что получено доказательство доставки. Смешивать эти два состояния конечно нельзя.
Пример:
Представим человека, с которым три года назад было несколько звонков и активная переписка.
В старой CRM у него до сих пор висит статус new: его поставили при заведении карточки и с тех пор никто не менял, потому что статус ни с чем не связан.
Ночной сборщик получает свежие сообщения и видит новый входящий сигнал. Детерминированный расчёт обновляет дату последнего контакта и поднимает приоритет реактивации.
Агент получает только карточку лида с последними сообщениями и с информацией об обещаниях, которые надо выполнять.
После подтверждения сообщение уходит через транспорт. CRM сохраняет идентификатор доставки, дату и причину касания. Если человек отвечает, запланированная цепочка автоматически останавливается.
В этом месте CRM становится моей системой продолжения отношений с людьми
Не получилось
Не удалась попытка заставить LLM делать всё
Очень хотелось давать агенту открытые запросы по типу «найди лучшего лида» для определенной цели. На практике это дорогой и плохо проверяемый путь. Модель тратит контекст на сортировку, которую SQL выполняет быстрее и точнее.
Я делаю разделение: код считает, база фильтрует, модель оценивает смысл, транспорт отправляет.
Доверие к статусам
Статусы быстро устаревают, сразу после последнего обновления. Вычисляемая температура тоже может ошибаться, но она хотя бы содержит формулу, дату расчёта и исходные сигналы.
Автоматизация только исходящих
Изначально почти все функции отвечали на вопрос «кому написать»: кампании, напоминания, холодные сообщения, анализ участников групп. Я делал большое количество исходящих рассылок
Но я поменял акценты, мне важнее сейчас тот лид, кто пришёл сам. Поэтому входящие обращения получили отдельную очередь, владельца и срок реакции.
Что в итоге автоматизировано
Сейчас без ручного просмотра тысяч карточек выполняются:
загрузка диалогов и сообщений;
объединение данных из разных источников;
обновление карточек;
определение давности и глубины отношений;
вычисление температуры;
построение очереди контактов;
обнаружение новых входящих сигналов;
подготовка контекста для агента;
предложение следующего действия;
контроль лимитов и защита от повторной отправки;
доставка и сохранение подтверждения;
остановка цепочки после ответа;
написание ответа лиду с продолжением диалога;
сигнал человеку в спорных случаях.
Ручная проверка с моей стороны осталась там, где она действительно нужна: выбрать цель, проверить рискованное действие и принять содержательный результат.
Сколько токенов это экономит
Без участия LLM выполняются обработка примерно 1,7 млн сообщений, агрегация десятков тысяч диалогов, дедупликация, расчёт давности, фильтрация, проверка лимитов и журналирование отправок.
Модель видит только те несколько карточек, которые уже отобраны обычным кодом. Поэтому стоимость работы агента зависит не от размера всей CRM, а от размера текущей очереди решений.
Большая память агента не должна целиком помещаться в контекст. Она должна уметь доставать маленький, проверяемый фрагмент в нужный момент.
Что бы я сделал иначе
Если бы пришлось начинать заново, я бы не начинал с интерфейса CRM.
Я бы построил систему в таком порядке:
Неизменяемый архив событий.
Устойчивые идентификаторы людей.
Провенанс каждого факта.
Вычисляемый скоринг с датой пересчёта.
Очередь следующих действий.
Отдельный безопасный транспорт.
И только потом интерфейс.
Большая часть ценности — в правильном переходе от события к следующему действию.
Вывод
Я наполнил CRM данными, а автоматизация началась позже
Теперь:
архив хранит факты;
база связывает их с людьми;
обычный код считает;
агент принимает содержательные решения;
транспорт выполняет действия;
журнал доказывает, что они действительно выполнены.
Что я хочу сделать еще? Проанализировать результаты автоматизации и результаты по конверсиям по всей моей базе лидов. Мои агенты будут считать не только все, что и кому они написали, а будут делать мне живые графики по тому, какие категории лидов приносят больше денег, какие этапы коммуникаций стоит доработать, какие формулировки и варианты ответов лидам стоит совершенствовать, чтобы в результате росло количество продаж моих сервисов.
Старая CRM продолжает выполнять механическую работу, которую уже умеет делать.
