ESPN DeportesBarcelona aumenta la venta en casaThe Jerusalem PostMilan's Jewish woman describes what it's like to be Jewish amid rising antisemitism - interviewESPNFollow live: USA locked in tight battle with Spain in FIBA semifinalוואלהשריפה פרצה סמוך ל"ביג פאשן" בירכאBBC NewsCricket: Today at the TestCollider7 Years Later, Stephen King’s Forgotten Horror Anthology Series Deserves a RevivalHabertürkİHA saldırılarına kınamaIl Fatto QuotidianoI vincitori di Venezia 83 – Il Leone d’oro va Woman Unknown. Malkovich e Arcel migliori attori. All’israeliano Naza il premio speciale della Giuriaynet בידורהסרט שמציג עדויות על "הרג מכוון" בעזה זכה בפרס יוקרתי בפסטיבל ונציהANSAVenezia, Il Premio Speciale della Giuria a Naza, Coppa Volpi a Mathilde Arcel e John MalkovichNumeramaBlizzCon 2026 en direct : retour de StarCraft, sortie de Diablo V, WoW Forever, série Netflix x Diablo, The Last Titan…ZDF heute"Bombastisch": Para-Schwimmer beenden EM mit 27 Medaillen
The Daily Newsstand · Free, Always
Saturday, September 12, 2026

Как я автоматизировал почти всю работу в CRM

Translate

Я долго ходил по конференциям, у меня хороший нетворк, и все это длительное время я наполнял 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 меняется, имена совпадают, телефон присутствует не везде.

Поэтому объединение идёт в несколько ступеней:

  1. Точное совпадение идентификатора.

  2. Совпадение по username.

  3. Имя плюс компания или другой дополнительный признак.

  4. Совпадения проверяются, не объединяются автоматически.

В карточке сохраняется происхождение каждого факта. Если два источника противоречат друг другу, система должна показать мне это.

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.

Я бы построил систему в таком порядке:
1. Неизменяемый архив событий.
2. Устойчивые идентификаторы людей.
3. Провенанс каждого факта.
4. Вычисляемый скоринг с датой пересчёта.
5. Очередь следующих действий.
6. Отдельный безопасный транспорт.
7. И только потом интерфейс.

Большая часть ценности - в правильном переходе от события к следующему действию.

Вывод

Я наполнил CRM данными, а автоматизация началась позже

Теперь:
- архив хранит факты;
- база связывает их с людьми;
- обычный код считает;
- агент принимает содержательные решения;
- транспорт выполняет действия;
- журнал доказывает, что они действительно выполнены.

Что я хочу сделать еще? Проанализировать результаты автоматизации и результаты по конверсиям по всей моей базе лидов. Мои агенты будут считать не только все, что и кому они написали, а будут делать мне живые графики по тому, какие категории лидов приносят больше денег, какие этапы коммуникаций стоит доработать, какие формулировки и варианты ответов лидам стоит совершенствовать, чтобы в результате росло количество продаж моих сервисов.

Старая CRM продолжает выполнять механическую работу, которую уже умеет делать.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.