В личной CRM может накопиться почти 10 000 контактов и несколько лет переписки. Переписывать такую систему ради AI-агента не обязательно: в кейсе она разделена на две части, где старая CRM делает механическую работу, а LLM-агент решает, с кем и зачем связаться.
Принцип распределения ролей короткий: код считает, база фильтрует, модель оценивает смысл, транспорт выполняет действия. Модель не ищет людей среди тысяч карточек, поэтому 10 000 контактов не превращаются в неподъёмный промпт.
Ниже разобраны пять слоёв этой архитектуры, порядок их работы и условия, при которых подход оправдан. Материал пригодится тем, у кого уже есть CRM или база контактов на SQLite и кто хочет добавить AI-агента, не переписывая её целиком.
Почему старую CRM не нужно переписывать: суть подхода
Обычно вопрос ставится так: выбросить текущую базу и собрать новую на современном стеке или надстроить агента над тем, что уже работает. Кейс отвечает в пользу второго варианта. Переписывание означает перенос истории переписок за несколько лет, повторную настройку отправки сообщений и новые ошибки там, где годами всё было стабильно.
Разделение ответственности выглядит так. Старая CRM читает и пишет данные, отправляет сообщения и следит за соблюдением лимитов Telegram. LLM-агент принимает решения: с кем связываться, зачем и что написать. Ни одной части не приходится делать чужую работу.
Ключевой принцип звучит жёстко: LLM не должна искать людей среди тысяч карточек. Код считает, база фильтрует, модель оценивает смысл, транспорт выполняет действия. Из этого правила вырастают все пять слоёв.
Контекст кейса: личная CRM почти с 10 000 контактов, история переписок за несколько лет, SQLite как хранилище и Telegram как канал связи. Это важная деталь, потому что для личной базы требования к надёжности, нагрузке и разграничению прав отличаются от корпоративной системы на несколько отделов.
Если вы как раз выбираете между текущей CRM и переездом на другую, пригодится пошаговая методика сравнения альтернатив в NotebookLM со сбором данных по критериям и готовыми шаблонами запросов.
Архитектура из пяти слоёв: от событий до безопасной отправки
Слои идут последовательно: события, карточка контакта, скоринг, поиск кандидатов, транспорт. Каждый следующий берёт результат предыдущего и не пытается делать работу соседа. Разберём, за что отвечает каждый.
Неизменяемый архив событий: зачем хранить всё как есть
Архив хранит каждое действие отдельной записью: входящее сообщение, исходящее, заметку, звонок. Записи не перезаписываются и не удаляются. Контакт поменял номер телефона - в архиве останутся оба номера, с датой и источником появления каждого.
Практическая выгода в том, что любой производный слой можно пересобрать заново. Изменили формулу скоринга или правила дедупликации - карточки строятся повторно из тех же событий. Аудит тоже становится простым: на вопрос «откуда взялся этот факт» отвечает сама структура хранения, а не память разработчика.
Единая карточка контакта с провенансом каждого факта
Карточка собирает данные о человеке в одном месте, и у каждого факта есть провенанс: источник и время появления. Имя, телефон, компания, интересы, часовой пояс, часовой пояс последнего обращения - всё с пометкой, откуда пришло значение.
Провенанс снимает проблему расхождений. Если из Telegram пришло новое имя, а в CRM лежит старое, видно, какое значение свежее и какому каналу доверять. LLM-агент получает не плоский набор полей, а поля с историей, и может обратиться к человеку так, как он сам себя назвал, вместо варианта пятилетней давности.
Скоринг приоритета реактивации: как выбрать, с кем связаться
Скоринг - вычисляемый показатель, который расставляет контакты по приоритету реактивации. Конкретных формул в кейсе не приводится, но логика факторов читается по структуре данных: давность последнего контакта, частота взаимодействий, текущий статус, источник появления записи. Человек, с которым плотно общались месяц назад, и человек из выгрузки пятилетней давности не могут стоять на одной позиции.
Считает скоринг код, а не LLM. Модели не нужно держать в голове арифметику по 10 000 записей: это дорого, медленно и невоспроизводимо. Число, полученное SQL-запросом или скриптом, одинаково при каждом запуске, его можно объяснить и пересчитать одной командой.
Детерминированный поиск кандидатов через SQL
Кандидатов отбирают SQL-запросы. Условия задаются явно: скоринг выше порога, последний контакт больше N дней назад, нет отметки «не беспокоить», нет открытого диалога. Пример структуры запроса на SQLite:
SELECT id, reactivation_score
FROM contacts
WHERE reactivation_score >= 60
AND last_touch_at < datetime('now', '-180 days')
AND do_not_contact = 0
AND has_open_dialog = 0
ORDER BY reactivation_score DESC
LIMIT 20;
Запрос отрабатывает по индексу за миллисекунды и возвращает короткий список. Отбор предсказуем: одинаковые данные и условия дают одинаковый результат. Модель на этом шаге не участвует вовсе.
Транспортный слой: безопасная отправка сообщений в Telegram
Транспорт изолирует отправку от логики решений. Агент передаёт готовый текст и идентификатор контакта, а слой отвечает за то, чтобы сообщение ушло один раз, в допустимое время и без превышения лимитов Telegram. В кейсе эту работу продолжает старая CRM, которая уже умеет отправлять сообщения и соблюдать ограничения.
Изоляция транспорта даёт три вещи: защиту от дублей при повторных запусках, единую точку контроля лимитов и возможность отключить отправку одной кнопкой, не трогая агента. Похожий набор правил для связки агента с внешней системой через API разобран в материале про безопасное подключение ИИ-агентов к рабочей системе: минимальные права, подтверждение мутаций, защита токенов и независимый readback после каждой записи.
| Слой | Задача | Чего от него не ждут |
|---|---|---|
| Архив событий | Хранить все события без изменения | Перезаписи истории |
| Карточка контакта | Собирать факты с провенансом | Потери источника значения |
| Скоринг реактивации | Считать приоритет кодом | Оценки приоритета через LLM |
| Поиск кандидатов | Фильтровать базу через SQL | Передачи модели всей базы |
| Транспорт | Отправлять с учётом лимитов | Решения, кому писать |
Как LLM-агент принимает решения, не перебирая все контакты
На вход модели приходит короткий список, например 20 контактов с наивысшим скорингом. Дальше цикл повторяется:
- SQL-запрос выбирает кандидатов по порогу скоринга и условиям отбора.
- Для каждого кандидата собирается компактный контекст: карточка, последние события переписки, статус, прошлые попытки реактивации.
- LLM оценивает смысл: уместно ли писать сейчас, что предложить, как сформулировать сообщение.
- Готовый текст проходит детерминированные проверки и уходит в транспорт.
- Результат отправки фиксируется как новое событие в архиве и влияет на скоринг при следующем запуске.
Работа модели ограничена смысловой задачей. Она не считает дни с последнего контакта и не сверяется со стоп-листами: это делает код до и после неё. Такой порядок снижает нагрузку на LLM в разы, потому что вместо 10 000 карточек модель видит два десятка.
В промпт обычно попадают четыре блока: данные контакта с провенансом, краткая выжимка истории, цель обращения и жёсткие правила. В правилах перечисляют запреты (не обещать скидок, не упоминать чужие данные, не давить на срочность) и требуемый формат ответа, например JSON с текстом сообщения, обоснованием и оценкой уверенности. Порог уверенности отсекает случаи, которые лучше показать человеку.
Проверять, что агент не разошёлся с исходными требованиями, можно без вызовов LLM. Как устроены такие проверки и почему длинного контекста для них не хватает, разобрано в статье про intent continuity и верификацию требований: три проверки на чистом Python, без эмбеддингов и обращений к модели.
Что понадобится для внедрения: стек и данные
Минимальный набор компонентов: существующая база контактов, SQLite как хранилище, Telegram как канал связи, LLM для оценки смысла и скрипт или сервис для механики. Конкретные модели, версии SQLite и библиотеки для Telegram в кейсе не указаны, так что выбор остаётся за вами.
SQLite подходит для такого объёма по нескольким причинам. База живёт одним файлом, не требует отдельного сервера, поддерживает полноценный SQL с индексами и транзакциями, а 10 000 контактов вместе с историей переписок укладываются в гигабайты. Ограничение тоже стоит знать: у SQLite один писатель в момент времени, и при параллельной записи из нескольких процессов появятся блокировки. Для личной CRM это редко становится проблемой, для многопользовательской нагрузки лучше сразу смотреть в сторону серверной СУБД.
Выбор LLM зависит от того, сколько кандидатов вы пропускаете через модель в сутки и нужна ли локальная обработка персональных данных. После фильтрации через SQL объём запросов небольшой, поэтому на первый план выходят стоимость и качество формулировок, а не размер модели. Как сравнивать варианты под свою задачу, а не по общему лидерборду, разобрано в материале про прикладной бенчмарк для выбора LLM.
Python здесь удобен тем, что связывает три части: доступ к SQLite, вызов LLM и отправку в Telegram. Слой доступа к данным стоит вынести сразу, чтобы SQL-запросы не расползались по коду агента. Если собираете систему сами, полезен разбор архитектуры AI-агента с нуля: оркестрация, память, инструменты, обработка ошибок и метрики latency, cost и reliability.
Ограничения и подводные камни подхода
Честный список того, что стоит учитывать до старта.
- В описании кейса названы пять слоёв и их назначение, но деталей реализации нет. Схему базы, формат карточки и правила скоринга придётся проектировать самостоятельно.
- Метрик эффективности реактивации, точности скоринга и объёмов обработки в кейсе не приводится. Результат придётся оценивать на своей базе.
- Провенанс требует дисциплины. Как только факты начинают попадать в карточку без источника, преимущество перед плоской таблицей исчезает.
- Скоринг может ошибаться: контакт с давней историей иногда важнее свежего, а формальные признаки этого не покажут. Порог отбора стоит периодически пересматривать.
- Отправка в Telegram ограничена лимитами, и их нарушение ведёт к ограничениям аккаунта. Транспортный слой и старая CRM снижают риск, но не отменяют его.
- Кейс описывает личную CRM. В корпоративной системе добавятся права доступа, несколько пользователей, журнал согласий и требования к хранению персональных данных.
- Без понимания SQL и основ работы LLM проект быстро упирается в отладку: непонятно, ошибается запрос, скоринг или промпт.
Отдельный риск - стоимость. Если передавать модели всю базу, расход токенов растёт линейно вместе с числом контактов. Фильтрация через SQL убирает эту зависимость, но только пока она выполняется до вызова LLM.
Пошаговый план: как адаптировать кейс под свою CRM
- Проведите аудит базы: сколько контактов, какие поля, сколько дублей, есть ли история переписок и в каком виде.
- Спроектируйте схему SQLite: таблица событий, таблица карточек и таблица фактов со ссылкой на событие-источник. Начинайте с событий, карточку собирайте из них.
- Посчитайте скоринг как SQL-запрос или скрипт с явными коэффициентами. Формулу держите в коде, чтобы её можно было пересчитать и объяснить.
- Настройте запросы отбора кандидатов с параметрами: порог скоринга, окно давности, стоп-листы. Ограничьте выдачу небольшим числом строк.
- Подключите LLM к уже отфильтрованному списку: соберите контекст из карточки и последних событий, задайте правила и формат ответа.
- Сделайте транспорт отдельным модулем с логированием: идентификатор контакта, текст, время отправки, результат. Логи пишите в архив событий.
Начать можно с одного слоя. Соберите архив событий и карточки, а отправку первое время оставьте ручной: агент готовит тексты, вы нажимаете «отправить». Так видно качество решений до того, как система получит доступ к каналу связи.
Итоги: кому подходит и что запомнить
Подход работает там, где уже есть CRM или база контактов с историей и нет желания её переписывать. Он же подойдёт тем, кто ведёт базу на SQLite и хочет получить работающий реактивационный сценарий без покупки новой платформы. При этом понадобятся навыки SQL, понимание работы LLM и готовность поддерживать порядок в данных.
Главное правило стоит держать в голове при любых доработках: LLM не ищет людей среди тысяч карточек. Код считает скоринг, база фильтрует кандидатов, модель оценивает смысл, транспорт выполняет отправку. Как только модель просят «посмотреть все контакты и выбрать лучших», система теряет предсказуемость и дешевизну.
Первый практический шаг: выгрузите события из текущей базы в отдельную таблицу SQLite и посчитайте скоринг простой формулой по давности и частоте контактов. Если список кандидатов выглядит осмысленно, подключайте LLM к первым двадцати строкам и проверяйте формулировки вручную.