Комментарии 4
Отдельная метка debug_contact_source - сильное решение: она превращает ошибку распознавания из загадки в наблюдаемый сценарий. Но в этом контуре я бы ещё разделил разбор ответа и выполнение внешних действий.
После раннего 200 OK выполнение n8n может перезапуститься, а исходный webhook - прийти повторно. Тогда один диалог способен дважды отправить сообщение, обновить сделку и поставить задачу менеджеру. Для каждого входящего события и каждого внешнего эффекта полезны стабильный eventId, уникальное ограничение или журнал выполненных операций, причём отдельно для CRM и мессенджера. Иначе идемпотентная обработка webhook ещё не гарантирует идемпотентность всей цепочки.
Как у вас устроены дедупликация повторных webhook и восстановление после падения между обновлением AmoCRM и отправкой ответа клиенту?
Доброго!) Спасибо за Ваш коммент, и глубокий разбор воркфлоу!). Честно-рад что при разборе нашли такую уязвисмость!)
На практике за первые недели плотного продакшена в логах я таких дублей и падений на этом контуре не ловили - система отрабатывает стабильно, а лимитов VPS хватает с запасом. Но чисто архитектурно вы абсолютноправы.
При аварийном падении n8n прямо в момент сетевого запроса к CRM и последующем ретрае цепочки - риск поймать дублирование таски действительно есть. Ваша идея с транзакционными локами под каждый внешний сбой - это топ, подход!) При масштабировании трафика обязательно внедрю такую митигацию. Жму руку за крутую экспертизу и реально полезным фидбэком!)
Интересное решение.
Не уверен что парсинг через регулярные выражения достаточно надёжен. Добавляет достаточно сложную логику. Ответ почему не json понятен. Пробовали ли говорить llm оформлять в html (кастомные теги) и работать через html парсинг в n8n?
Просто, если вы отправляете клиенту только текст без полученных данных, эти строки в ответе llm •Дата рождения: 12.04.1995•Сфера для улучшения: любовь•Телефон/Связь: +375 29 XXX-XX-XX
кажутся избыточными (в таком формате). Учитывая что вы регулрками ищете данные в тексте пользователя напрямую. (Тут уточню что интерес вызывает именно решение задавать формат вывода llm в человеко читаемом виде а потом его парсить сложными регулярками)
Но тут вы уже сделали решение и как понял достаточно надёжное.
По поводу логики. вы нарезайте абзацы по 180 символов а сообщения по 140. То-есть может быть что абзац разделён на 2 сообщения ?
ну и это... в тегах статьи тег "говнокод" всё же лишний )))
Спасибо за детальный разбор кода и отличные конструктивные вопросы по логике!)) Постараюсь вкратце)
1.По поводу HTML-парсинга против регулярок.Идея с кастомными HTML-тегами (вроде <phone.....</phone>) хорошая, я её гонял на этапе проектирования контура. Но в проде вылезли грабли: на fallback-модели (в пики подхватывает Qwen через OpenRouter , сначала была Llama через грок) нейронка при генерации длинных ответов иногда тупо "забывает" закрывать теги, путает их местами или нафиг экранирует знаки < > как HTML-сущности. В итоге штатные HTML/XML парсеры в n8n на таком выводе просто падали по синтаксической ошибке.Текстовые маркеры в верхнем регистре ([VERDICT:...]) и регулярки оказались тупее, это факт...но тупо надежнее!) Флаги gm и универсальный "пылесос-очиститель" /[A-Z_]+:\s*[^\]]*/gi вытаскивают сущности, даже если модель поплыла по форматированию. А сам человекочитаемый блок с анкетой в конце - это внутренний такой "черновик" для самой LLM в рамках сессии LMemory, чтобы она не теряла контекст. Перед отправкай клиенту этот кусок всё равно срезается нодой Format Response, так что юзер его не видит.
2. Про нарезку на 180 и 140 символов.Тут в статье из-за сокращения кода получился визуальный парадокс) Логика там как раз каскадная. Нода Format Response сначала нарезает сырое полотно на абзацы (лимит 180 символов = это чисто визуальный ориентир для предложений, чтобы текст на экране смартфона не выглядел как спам-рассылка - и тут скорее , так сказать "эстетические пожелания заказчика"... А вот финальный контур отправки (сама нода Split AI Response) уже берет этот готовый красивый текст и жестко пилит его по предложениям на чанки строго до 140 символов, чтобы отправлять их в Директ порциями через цикл с Typing Delay. Так что один большой смысловой абзац может улететь двумя короткими сообщениями с имитацией живого набора текста, что визуально считывается клиентом как общение с человеком( и впринципе мало клиентов понимало что они вообще с ии говорят = что пришлось докручивать ,из юридических соображений, на штатное "Здравствуйте, я ИИ - ассистент ателье ХХХХХ" )
3. Про тег "говнокод"..) А вот тут вы попали в самую точку, тег висит не просто так) На самом деле, этот проект вытрепал мне столько мозгов и нервов, сколько ни один другой. И дело было не столько в самой разработке, а в заказчике, точнее скорее непонимании того что он хочет = не так легко и не по щелчку. Перед самым запуском и финальным расчетом мне кидали правки в промпт 6 раз(!). А в первые две недели после внедрения (на период бесплатного теста и гарантии) начался сущий ад — прилетал бесконечный вал скринов и сообщений в духе "твой ИИ говорит не как живой менеджер и не так запятые ставит". Я в какой-то момент вообще перестал семье время уделять (а когда дочери 14 лет, а сыну 1,5 года — это критично), зашился полностью в вылизывании того, что по определению детерминированным быть не может.В итоге просто ткнул пальцем в пункты подписанной оферты и прямо сказал, что сегодня же всё отключаю к чертям до выяснения отношений. Только после этого наступила тишина и ко мне прислушались) Сейчас, к слову, продукт крутится уже 4-й месяц, всё стабилизировалось и работает нормально - на октябрь расширение хотят на ещё мессенджер, к слову меня заказчик не трогает и я их))
опечатка promt в имени поля Redis-ноды, на которую завязана куча логики и которую теперь тупо и не охота трогать - официально считается частью этой так сказать выстраданной архитектуры)) ещё раз спасибо Вам за оценку кейса)

Из генерации — в переписку: доводим ответ ИИ‑агента до клиента и CRM на n8n