Дисклеймер: в статье упоминается Meta — организация, признанная экстремистской и запрещённая на территории РФ.

Все ключи, телефоны и названия в примерах изменены; код, схема, логика и скрины‑ из рабочего проекта заказчика, и строго с его разрешения! Статья не является рекламой/самопиаром и тому подобное.

В прошлой статье я разобрал конвейер между агентом и клиентом: теги, регулярки, абзацы по 180 символов. Осталось два больших пояснения — что происходит со сделкой в amoCRM, когда диалог дошёл до неё. Поэтому сегодня показываю два Salesbot’а, которые замыкают контур: «Раздатчик» (диспетчеризация задач менеджерам) и «Идентификатор» (оцифровка старой базы).

Хочу начать с вопроса, который оставался за кадром всех предыдущих статей. Нода IF: Is New User? в начале ветки проверяет Redis‑ключ связки: пустой — значит клиент новый, запускаем синхронизацию со сделкой. А если клиент старый — заведён в CRM руками за год до внедрения ИИ? Если к примеру, как в данном проекте, у заказчика сделки хранятся до последнего пока, база не дойдёт примерно до 30 000 сделок? У такого клиента связки тоже нет. Формально, для автоматизации он выглядит как новый. Обычная система в этот момент создаёт вторую сделку на первого же старого клиента, который написал ночью. Дубль = Добро пожаловать в слепую зону!

CRM‑ветка: что происходит после Is Dialog Finished?

Напомню, где мы остановились. Нода Is Dialog Finished? (три условия через OR) решила: диалог больше не болтовня, несём данные в CRM. Дальше топология такая:

Is Dialog Finished? -> Redis: Get Union Link -> IF: Has Union Link?
    TRUE  -> Amo: Update Existing Lead (Union) -> Redis: Save Union Link
    FALSE -> Prepare IG Response -> Send direct message -> Clear Cache

FALSE‑ветка скучная и правильная: ответили клиенту, почистили агрегат, разошлись. Ничего более. А TRUE‑ветка — это поход в amoCRM. Перед ним, естественно, ещё одна проверка: связку не просто читаем, а валидируем:

// из Redis достаётся ключ вида {"leadId":"123456"}
IF: JSON.parse($json.amoLeadId).leadId не пустое

И только потом — обновление сделки. Вот его реальный маппинг (ID кастомных полей amoCRM оставил как есть — они вам ничего не говорят, зато честно видно, что поле не одно):

Поле amoCRM

Что пишем из n8n

Вердикт ИИ (text)

verdict — тот самый [VERDICT:ORDER:DATA|HOT] из третьей части

MESSENGER‑ID (textarea)

chatId клиента

MESSENGER‑TYPE (textarea)

источник — объект вебхука (inst)

Ответ ИИ (textarea)

анкета: Имя / Дата рождения / Контакт / Дизайн / Сфера

Тег сделки

task из [TASK:NEED_MANAGER_CALL]; [MANAGER_INTERVENE] ; [CONFIRM_BOOKING] ;

Код обновления, сокращённо до сути:

{
  id: JSON.parse($json.amoLeadId).leadId,
  responsible_user_id: 13533162,   // жёстко прописанный менеджер
  custom_fields_values: [
    { id: 2452289, value: verdict },          // Вердикт ИИ
    { id: 2461565, value: chatId },           // MESSENGER-ID
    { id: 2458673, value: 'Имя: ...\nДата рождения: ...\nКонтакт: ...\nДизайн: ...\nСфера: ...' }
  ],
  _embedded: { tags: [{ id: task }] }
}

Три решения здесь не случайны.

Почему «Ответ ИИ» — отдельное поле, а не «пусть менеджер откроет Direct». Менеджер утром открывает карточку и видит анкету целиком: имя, дата рождения, контакт, сфера, дизайн. Ему не нужно листать мессенджер и собирать данные по сообщениям. Это и есть разница между «данные где‑то в системе» и «данные в карточке».

Почему responsible_user_id прописан жёстко. Помните засаду из второй части — штатная интеграция при обновлении перетягивала «Ответственного» на своего технического пользователя? Вот его лечение в коде: каждый апдейт возвращает сделку нужному менеджеру. Не однажды — а именно каждый).

Кстати, поясню про «апдейт возвращает сделку нужному менеджеру». В этом проекте весь ночной поток замыкается на одного ответственного: старшего менеджера, который утром быстро разбирает сделки и распределяет дальше. Роутинг по исполнителям (этот лид в воронку ХХХ менеджеру 1, этот лид в воронку ХХХ менеджеру 2 и тому подобное) Это внутренняя бизнес‑логика заказчика: она живёт на его стороне и меняется, и зашивать её в код автоматики я не хочу — автоматизация не должна знать о бизнесе больше, чем требуется для её задачи! В идеальном мире здесь динамический ID из конфига; здесь же граница «отдать на единую точку входа, дальше — люди и их правила» оказалась честнее.

Почему после апдейта снова Save Union Link. TTL связки продлевается при каждом касании: клиент, который пишет каждую неделю, никогда не выпадет из контекста.

Salesbot “Раздатчик”: почему задача, а не уведомление

ИИ агент отработал ночь, данные в карточках. Казалось бы — победа. Но представьте утро менеджера: в воронке пятнадцать сделок, обновлённых за ночь, и среди них три горячих лида с телефонами. Без диспетчера он открывает карточки по очереди, в порядке, в котором они отображаются — а не в порядке приоритета. Горячий лид с телефоном ждёт рядом с «посмотрел каталог».

Это и есть слепая зона между «данные зафиксированы» и «данные отработаны». Закрывает её Salesbot "Раздатчик" — встроенный механизм amoCRM, который живёт уже не в n8n, а внутри CRM и триггерится на события воронки.

Алгоритм из четырёх шагов:

  1. Сканирование маркеров. Salesbot проверяет сделку на системные теги (к примеруNEED_MANAGER_CALL из [TASK:]) и валидность поля «Вердикт ИИ».

  2. Оценка критичности. Поле «Вердикт ИИ» заполнено значит, ночь закончилась не просто перепиской: есть имя, контакт и суть запроса. Сделка признаётся приоритетной.

  3. Постановка задачи. На ответственного менеджера мгновенно вешается задача с типом «Связаться с клиентом (Лид от ИИ)» и жёстким дедлайном.

  4. Контроль внимания. Менеджер приходит на смену и видит не «какую‑то обновлённую карточку», а активную задачу со счётчиком времени в разделе «Мои задачи».

Почему Salesbot, а не n8n напрямую. Резонный вопрос: задача создаётся в amoCRM — почему бы не слать её из воркфлоу через API? Технически можно. Но у этой операции нюанс,если можно сказать, «юрисдикции». Момент "сделка горячая = ставь задачу" определяет не мой воркфлоу, а состояние воронки: тег появился, поле «Вердикт ИИ» заполнилось, ответственный назначен. Чтобы отследить это из n8n, пришлось бы строить вторую подсистему наблюдения: поллинг состояний сделки, собственные расписания, обработку смены ответственного. Salesbot — это штатный механизм amoCRM: он живёт внутри событий воронки и выполняет в данном проекте ровно одну функцию — как только в сделке появился тег NEED_MANAGER_CALL и валидный «Вердикт ИИ», железобетонно выставить задачу ответственному, с дедлайном.

Разделение ролей получается жёсткое. Интеллект = в n8n: сформировать правильный тег, посчитать вердикт, закинуть его в сделку = это работа инженера, и вся она описана выше. Диспетчерская доставка = Salesbot'у: нативный механизм, которому эта функция заложена изначально. Принцип: мы не отдаём Salesbot'у мышление — мы отдаём ему доставку.

И это не «тупо просто решение», а осознанная граница инструментов. Возражение возможны против такого подхода: в community нодах amoCRM для n8n есть ноды по задачам — Get list of tasks, Create new tasks, Update tasks. Технически создать задачу из воркфлоу можно.

Те самые ноды работы с задачами из пакета n8n-nodes-amocrm
Те самые ноды работы с задачами из пакета n8n‑nodes‑amocrm

Salesbot живёт по другую сторону в данном проекте — внутри amoCRM. Его триггер не мой вызов API, а само состояние сделки: в карточке появился тег NEED_MANAGER_CALL и валидный «Вердикт ИИ» = задача выставлена ответственному, с дедлайном. Неважно, кто поставил тег: воркфлоу ночью или менеджер руками днём, механизм отработает одинаково. Кросс‑системных запросов между «тег появился» и «задача в Моих задачах» равен нулю. Ломаться нечему здесь нечему в принципе.

1 - тег поставлен; 2 - заполнены поля Вердикт ИИ/ Ответ ИИ; 3 - заполнены поля  MESSENGER-ID/ MESSENGER-TYPE
1 — тег поставлен; 2 — заполнены поля Вердикт ИИ/ Ответ ИИ; 3 — заполнены поля MESSENGER‑ID/ MESSENGER‑TYPE

Бонус, который оценить должен в принципе любой заказчик: задача с дедлайном является самым видимым и проверяемым следом!. Менеджер пропустил горячий лид? Это видно по задаче, а не по жалобе клиента через два дня. Снял тег вручную, чтобы ИИ агент не мешал? Действие осталось в истории карточки = саботаж автоматизации наблюдаем, а не растворяется в тишине. Технические сбои падают в executions,а человеческие остаются в карточке: система не прячет ни тех, ни других. Может звучит как то жёстко, наверное и не к месту, но вполне рабочая инструкция по оценке работы каждого.

Практический эффект постановки задачи - реакция на горячий лид в первые минуты смены, а не "когда дошли руки".
Практический эффект постановки задачи — реакция на горячий лид в первые минуты смены, а не «когда дошли руки».

Возникает вопрос про дубли: Salesbot триггерится на тег, а что если тег сняли и он появился снова? Задача встанет повторно. И это не баг, а цикл сделки: тег = рабочая метка людей, а не флаг машины. Ночью агент ставит тег, когда появились свежие данные; днём менеджер, взяв карточку, снимает его и двигает сделку дальше по своим правилам. Тег вернулся = значит, ночью были новые данные или новый повод, и вторая задача оправдана. Пока тег живёт в карточке = задача одна; тег снят = контур молчит.

Так что граница простая: сложную логику = извлечение вердикта, защиту от race condition, буферы = реализуем в n8n своими мозгами. Нативную доставку задачи внутри CRM = отдаём платформе: у неё для этого штатный механизм, который срабатывает по событию, а не по вызову.

Отдельно про «почему задача, а не уведомление». Ответ: это принципиальный выбор. Уведомление можно смахнуть и забыть; оно живёт секунды. Задача с дедлайном является неким элементом учёта: она висит в списке, у неё есть срок, её невыполнение видно. Когда я пишу «исключить человеческий фактор», я не имею в виду, что менеджеры плохие. Я имею в виду, что «посмотрю карточки после кофе» обычное, нормальное человеческое поведение, и система должна быть рассчитана на него, а не на идеального исполнителя.

Salesbot “Идентификатор”: слепая зона старой базы

Теперь обещанная разгадка из интро данной статьи. Схема из второй части работает идеально, пока связка «чат = сделка» строится для новых клиентов: клик по рекламе = новая сделка от штатной интеграции = синхронизация = связка в Redis на 30 дней. Замкнутый цикл.

Но у любого бизнеса, внедряющего автоматизацию, за плечами уже есть база. Клиенты, заведённые менеджерами вручную, со сделками, историей, покупками. Что происходит, когда такой клиент вспоминает о вас в час ночи и пишет в Direct?

Разбираем по схеме: связки в Redis нет (её некто не создавал — сделку тогда создавал человек), поле MESSENGER-ID в карточке пустое. IF: Is New User? видит пустой ключ и делает единственно возможный логичный вывод: новый клиент. Дальше штатная интеграция создаёт вторую сделку на человека, который уже купил у вас браслет. Он замечает, что его «заново завели» — и это уже не техническая проблема, а репутационная.

Salesbot “Идентификатор” закрывает эту дыру на стороне amoCRM. Его настройки (реальные):

  • источник: НЕЛЬЗЯgram‑сообщения;

  • триггер: первое входящее сообщение от контакта;

  • режим: ежедневно с 20:00 до 00:00 и с 00:00 до 10:00 — ровно окно работы ИИ;

  • этапы воронки: все, кроме «Неразобранное».

Алгоритм работы:

  1. Перехват. Старый контакт пишет в Direct в нерабочее время. Salesbot, в отличие от меня, всегда смотрит в базу: этот chatId у нас уже есть. Он инициализирует технический POST‑запрос в n8n.

  2. Runtime‑идентификация. n8n параллельно держит два потока: вебхук от amoCRM (через Salesbot) и входящий поток от НЕЛЬЗЯgram Graph API. Они приходят почти одновременно.

  3. Генерация связей. n8n прописывает MESSENGER-ID в существующую карточку и создаёт в Redis ключ связки app:union:ig:{chatId} с обычным TTL 30 дней.

  4. Результат. Слепая зона закрывается на лету, сессия не прерывается: к моменту, когда ветка агента дойдёт до Is New User?, связка уже существует, и старый клиент опознаётся как старый. ИИ видит контекст прошлой покупки, а не спрашивает «чем могу помочь?» у человека, который у вас уже покупал.

Возникает вопрос: почему параллельные вебхуки не конфликтуют? Ответ реализован в два этажа защиты — вкратце поясню оба. Первым является идемпотентная запись: ключ детерминирован chatId, значение —leadId той самой карточки; кто бы ни пришёл первым, Salesbot‑вебхук или поток НЕЛЬЗЯgram, в Redis ляжет одно и то же. Однако могу сказать что как раз запись только полдела. Вторая защита — двухэтапное решение. Ранняя защита нодой IF: Is New User? — предварительный этап: он лишь решает, ставить ли ключ синхронизации, и ничего не создаёт. Финальный вердикт выносится позже, в CRM‑ветке, на свежих данных: перед обновлением сделки стоит повторная проверка — ноды Redis: Get Union Link → IF: Has Union Link? (видно на топологии в начале статьи). От старта потока до этой перепроверки проходят десятки секунд = Salesbot и штатная интеграция успевают отработать, связка на месте, и ветка воркфлоу уходит в обновление существующей карточки, а не в создание дубля. В общем одной фразой можно пояснить так: Идемпотентная запись закрывает гонку записи, перепроверка = гонку решения.

Самое приятное в этом модуле — он оцифровывает базу сам. Каждый старый клиент, написавший ночью, навсегда получает MESSENGER-ID в карточке и связку в Redis. Через пару месяцев работы вся активная база оказывается «подключённой» к ИИ‑контуру без единого ручного действия.

Настройка Salesbot'а "Идентификатор"
Настройка Salesbot'а «Идентификатор»
Заполнение тех.полей: вид в воркфлоу
Заполнение тех.полей: вид в воркфлоу

Стоп‑кран: как человек перехватывает диалог

Последний элемент, который отличает боевую систему от демо: что происходит, когда менеджер хочет вмешаться в диалог, который ведёт бот.

Требование формулируется просто: любое событие «человек вступил в диалог» должно глушить бота — иначе клиент получит два ответа подряд от разных сущностей. В моей схеме это решено на двух уровнях.

Проверка перед отправкой. В ветке отправки стоит предохранитель IF: Go or STOP?, который смотрит на связку со сделкой до того, как воркфлоу пойдёт дальше. Отбойная ветка (Clear Cache STOP) подчищает агрегат сообщений, чтобы накопленный буфер не протёк в следующую сессию. Гарантия банальная, но критичная: не отправляем то, что отправлять уже не нужно.

Управление по состоянию сделки. Идея, которую я вынашиваю в следующую итерацию из обсуждений с заказчиком — явный флаг: скрытое поле‑чекбокс «Остановить ИИ» в карточке. Перед отправкой ответа n8n проверяет галочку; менеджер, взяв диалог, ставит её = бот молчит. Вариант ещё проще, который уже работает в amoCRM из коробки: смена этапа сделки на «взят в работу» = как сигнал. Здесь честно скажу: текущая версия закрывает основной сценарий, чекбокс — следующий шаг, и я сознательно не выпускаю его, пока не проверю все ветки (ну и обсуждение условий и одобрения заказчика).

Важно, что стоп‑кран не ломает сессию: Redis‑контекст и связка остаются на месте. Когда менеджер закончит и клиент снова напишет ночью — бот продолжит с того места, где был контекст, а не с «Здравствуйте! Чем можем помочь?».

Что в итоге в цифрах

Методология прежняя: amoCRM + Google Sheets, не контролируемый эксперимент. Что могу сказать честно:

  • сделок с дублями после внедрения «Идентификатора» = 0;

  • задача «Связаться с клиентом (Лид от ИИ)» ставится на каждую сделку с непустым «Вердиктом ИИ» — здесь механика даёт 100% покрытие по построению;

  • среднее время реакции менеджера на задачу: около 15 минут (в пики нагрузки конечно может и до получаса доходить);

  • снижение ручной работы команды — около 40% (оценка по списку задач до/после, как и во второй части).

Что дальше

На бумаге всё выглядит красиво: вот мы элегантно закрыли проблему вебхуков, вот дисциплинированные Salesbot'ы ставят задачи, вот стоп‑кран для человека. Но у каждой схемы есть обратная сторона, которую не рисуют на архитектурных картинках: собственная архитектура ломает всё сама (сложный промпт — это тоже риск), есть ограничения платформ, и есть баги, которые всплывают не в день запуска, а спустя время — когда трафик идёт как снежный ком.

В следующей части — разбор самого странного бага: рассуждающая модель тратила токены, отчитывалась finish_reason: "stop", а клиенту уезжала пустая строка. Reasoning Lock: почему LLM сама решает молчать, почему это не ловится «автотестами» — и четвёртый контур предохранителей, на этот раз от самой модели.

Всем добра, и надеюсь, что понравился разбор, а статья будет полезна!:‑)

Комментарии (0)