19 июл. 2024 г.·7 мин чтения

Dry-run mode для AI workflow до того, как он коснётся записей

Dry run mode для AI workflow помогает командам тестировать входы, похожие на реальные, ловить плохие обновления и проверять крайние случаи до любых изменений в живых записях.

Dry-run mode для AI workflow до того, как он коснётся записей

Почему живые записи создают проблемы

Живые данные ломаются быстрее, чем ожидает большинство команд. Один неудачный запуск редко создаёт одну плохую запись. Он может изменить сотни за считаные минуты, особенно если AI workflow сортирует тикеты, обновляет поля CRM или сам распределяет работу.

Маленькие ошибки быстро распространяются. Если prompt неправильно обработает крайний случай, workflow может пометить не тех клиентов, создать дубликаты, перевести людей не на тот этап воронки или назначить аккаунт не тому владельцу. Сначала ущерб выглядит небольшим. Потом кто-то открывает дашборд и видит ту же проблему повсюду.

Самая недооценённая часть — это исправление. Тестовый прогон может занять 10 минут. Отмена последствий способна съесть полдня, а то и больше, если другие автоматизации уже отреагировали на плохие данные. Отдел продаж начинает звонить не тем лидам. Сотрудники поддержки отвечают из не той очереди. Руководители перестают доверять отчётам, потому что никто не понимает, какие изменения были настоящими.

Команда поддержки, которая тестирует triage flow на живых тикетах, может столкнуться с этим очень быстро. Модель читает срочные запросы на возврат как низкоприоритетные, добавляет не тот тег и отправляет 240 тикетов в общую очередь. Теперь кому-то нужно найти каждый затронутый тикет, вернуть владельцев, исправить теги и проверить, не сработали ли уже последующие правила. Исправление занимает гораздо больше времени, чем сам тест.

Это становится ещё рискованнее в небольших компаниях, где один workflow часто затрагивает сразу несколько систем. Один запуск может обновить CRM, создать задачи, отправить уведомления и изменить итоговые показатели в отчётах. Поэтому командам нужен безопасный путь до живых записей. Dry run позволяет тестировать на входах, похожих на production-данные, ловить странные случаи и останавливать плохие изменения до того, как они попадут в реальные записи.

Что должен делать dry run

Dry run — это репетиция. Workflow читает вход, похожий на реальный, выполняет ту же логику и показывает, что сделает дальше. Простая разница в том, что ничего на самом деле не меняется.

Система всё ещё может классифицировать тикет, извлекать поля из формы, выбирать ветку и готовить обновления так, будто она работает вживую. Вы по-прежнему видите решения. Можно проверить, имеет ли смысл prompt, правила и вывод модели ещё до любых реальных изменений записей.

Главное — заблокировать побочные эффекты. Настоящий dry run не записывает в базу, не отправляет письма или сообщения в чат, не создаёт задачи и не прокидывает обновления во внешние инструменты. Если workflow обычно вызывает CRM, billing system или help desk, dry run должен остановить финальное обновление и оставить его только как предварительный просмотр.

Это важно, потому что многие сбои очень незаметны. Модель выбирает не ту категорию. Заполняет не то поле. Пытается уведомить не того человека. В live mode одна ошибка быстро расходится дальше. В dry run та же ошибка остаётся видимой и безвредной.

Хороший тест всё ещё должен оставлять доказательства. Команде нужен полный журнал действий, понятное сравнение планируемых изменений, исходный ввод, ответ модели и точный шаг, на котором что-то сломалось. Без таких деталей прогон превращается в гадание. Вы можете знать, что workflow не сработал, но не понимать почему. Хуже того, можно решить, что всё прошло успешно, хотя он тихо пропустил крайний случай.

Лучшие dry run почти скучные. Они ведут себя как production, используют реальные по форме тестовые данные и блокируют любую запись на границе. Это даёт командам чистый способ проверять странные случаи, исправлять логику и запускать тот же вход снова, пока результат не станет правильным.

Где обычно прячутся записи

Обычно команды сначала замечают очевидные записи. Они блокируют вставки в базу, пропускают обновления и выключают загрузку файлов. Это хороший старт, но его редко достаточно.

Dry run ломается, когда хотя бы один побочный эффект всё же проходит. Workflow может не трогать основную таблицу и при этом создавать шум где-то ещё. Изменения в базе — это только часть картины. Записи могут появляться также в файловом хранилище, кэше, который запускает последующие задачи, поисковых индексах, инструментах аналитики и audit logs, которые питают другие системы.

Скрытые записи создают больше всего проблем, потому что находятся вне основного пути кода. Модель завершает задачу, а потом вспомогательный сервис срабатывает через webhook. Worker повторной попытки видит временную ошибку и отправляет запрос ещё раз. Задача из очереди просыпается через минуту и создаёт запись, хотя исходный прогон был только тестом.

Это постоянно происходит в системах сообщений и клиентских системах. Dry run всё ещё может отправить письмо, опубликовать сообщение в Slack, открыть тикет в support, создать контакт в CRM, добавить заметку к сделке или отправить событие в аналитику. С первого взгляда это не похоже на запись в базу, но такие действия всё равно меняют реальные системы.

Один пропущенный побочный эффект может испортить весь тест. Представьте flow для triage поддержки, который правильно блокирует обновления тикетов в вашем приложении, но всё равно отправляет webhook в help desk. Теперь агенты видят фальшивые обращения, клиенты получают ответы, которых не должны были видеть, а команда перестаёт доверять тестовой среде.

Ретраи делают ситуацию хуже. Если код блокирует первую запись, но оставляет активным путь повторной попытки, система может попытаться выполнить то же действие ещё раз через другого worker'а или service account. То, что выглядело как чистый тест, превращается в запутанную охоту за багом.

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

Как это построить

Начинайте с точки входа, а не середины. Если workflow может запускаться через API-вызов, очередь или запланированную задачу, каждый путь должен установить один и тот же флаг dry_run до того, как произойдёт что-либо ещё. Этот выбор удерживает весь прогон в честных рамках.

Dry run mode чаще всего ломается, когда флаг остаётся только на первом уровне. Модель видит тестовые данные, но вызов инструмента, вспомогательный скрипт или retry job всё равно пишет в базу. Одной пропущенной передачи достаточно, чтобы появились реальные записи.

Практичная реализация обычно проходит пять шагов:

  1. Добавьте один флаг dry run в точке старта workflow.
  2. Передавайте этот флаг в prompts, вызовы инструментов и async jobs.
  3. Пропустите все записи через один слой адаптера.
  4. Замените реальные методы записи на заглушки или preview-версии, когда флаг включён.
  5. Сохраняйте один отчёт, который показывает, что workflow пытался сделать.

Этот слой адаптера важнее, чем многие ожидают. Если код пишет прямо в базу в пяти разных местах, dry run mode превращается в угадайку. Вынесите создание записей, обновления, удаления, отправку сообщений и публикацию webhook'ов в понятные функции, а потом переключайте их по флагу.

Сделайте отчёт полезным

Dry run должен оставлять доказательства, а не побочные эффекты. Хорошие отчёты показывают запланированные действия, различия в полях, ошибки валидации, выводы инструментов и любую ошибку, которая остановила бы живой запуск.

Сделайте отчёт лёгким для просмотра. Рецензент должен за минуту-две понять, что workflow обновил бы Customer A, пропустил Customer B и сломался на Customer C, потому что не хватало обязательного поля. Если отчёт выглядит небрежно, люди перестают внимательно его читать.

Фоновые задачи требуют той же аккуратности. Если первый шаг безопасен, но он планирует более позднюю задачу без флага, эта вторая задача может всё равно записать данные через несколько часов. Передавайте флаг через payload задач, сообщения событий и ретраи каждый раз.

Сначала прогоните небольшой пакет, прежде чем масштабировать тест. Десять или двадцать реалистичных случаев обычно уже многое показывают: странные имена, пустые поля, дублирующиеся записи и конфликтующие состояния. Потом разберите результат вместе с людьми, которые лучше всех знают workflow.

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

Выбирайте тестовые данные, похожие на реальные

Проверьте AI workflow
Найдите места, где ваш workflow всё ещё отправляет письма, обновляет или создаёт записи по ошибке.

Dry run показывает правду только тогда, когда вход похож на те данные, с которыми команда реально работает. Если workflow ожидает customer_email, ticket_body и order_id, ваши тестовые записи должны использовать те же имена полей и ту же структуру. Заглушки создают слабые тесты, потому что пропускают мелкие несоответствия, которые ломают настоящие запуски.

Очистите личные данные, но оставьте хаос. Замените имена, email, номера телефонов и номера счетов безопасными значениями. Сохраните странные пробелы, сломанную капитализацию, пустые поля, вставленные подписи и смешанные форматы дат. Вот где тестирование AI workflow становится действительно полезным.

Короткого, аккуратного набора обычно недостаточно. Реальные системы получают длинные сообщения, наполовину заполненные формы, дублирующиеся заметки и записи, которые противоречат друг другу. Одно поле говорит «возврат одобрен», а другое — «не возвращать». Именно такие противоречия нужно ловить до того, как workflow сможет что-то записать обратно.

Хороший стартовый набор обычно включает нормальные записи, записи с пропущенными полями, очень длинный текст, противоречивые значения в разных полях и неровное форматирование, скопированное из писем, чатов или старых систем.

Держите эти входы в небольшой сохранённой библиотеке. Когда какой-то случай ломает workflow, не просто исправляйте prompt и двигайтесь дальше. Сохраните эту запись, отметьте, что именно сломалось, и запускайте её снова после каждого изменения. Со временем это станет вашей защитной подборкой.

Один практичный принцип сильно помогает: сначала тестируйте структуру, потом содержимое. Структура означает, что запись имеет ту же форму, что и production-данные. Содержимое означает, что в ней есть странные случаи, которые люди создают по ошибке каждый день. Нужны оба.

Если вы работаете со стартапами или внутренними операциями, попросите пять–десять недавних записей и обезличьте их. Обычно это даёт лучшее покрытие, чем придумывать пятьдесят фальшивых примеров. Реальные входы несут скрытые особенности: старые названия полей, якобы необязательные значения, которые на деле не такие уж необязательные, и текст, который выглядит безобидно, пока модель не прочитает его неправильно.

Если тестовый набор кажется чуть неопрятным, он, скорее всего, ближе к правде.

Пример с triage поддержки

Хороший тестовый случай — это одно грязное письмо в поддержку, а не аккуратный тикет. Представьте, что клиент пишет: «Сегодня утром я обновился, ваша система списала деньги с карты дважды, я всё ещё не могу войти в новую учётную запись, а кнопка экспорта падает, когда я пытаюсь скачать данные за прошлый месяц».

Одно это сообщение смешивает три проблемы, которые часто попадают в разные очереди. Здесь есть проблема с аккаунтом, проблема с биллингом и баг продукта. Именно тут dry run приносит пользу. Команда может подать реалистичный вход в triage flow и проверить каждое решение, пока записи остаются заблокированными.

Пример результата

AI читает сообщение, классифицирует его и черновиком готовит следующий шаг. В dry run система может показать результат, который отправила бы в help desk или CRM:

  • Теги: billing, account access, export bug
  • Владелец: product support
  • Приоритет: medium
  • Черновик ответа: «Спасибо, что сообщили об этом. Пожалуйста, попробуйте выйти из аккаунта и войти снова. Мы проверяем проблему с экспортом.»

Ничего не создаётся. Ни тикет, ни заметка к аккаунту, ни изменение статуса, ни ответ клиенту.

Что выявляет тест

На первый взгляд такой результат кажется нормальным, а через две секунды проблема становится очевидной. Владелец выбран неверно. При двойном списании нужен специалист по биллингу как можно быстрее, а приоритет должен быть выше, потому что клиент уже заплатил и всё ещё не может войти.

Поскольку записи заблокированы, команда может исправить логику до того, как она коснётся CRM. Например, можно добавить правило: любое сообщение с фразами «списали дважды» и «не могу войти» сначала идёт в billing, а для product support создаётся внутренняя заметка о проблеме с экспортом.

После этого следующий dry run должен выглядеть иначе. Теги могут остаться теми же, но владелец меняется на billing operations, приоритет становится высоким, а черновик ответа подтверждает проблему с оплатой, просит номер счёта и сообщает клиенту, что команда уже зафиксировала проблему с экспортом для проверки.

Этот пример небольшой, но он показывает ошибку, которая сильнее всего бьёт в живой поддержке. AI звучит полезно, но отправляет обращение не туда. Хорошее тестирование должно ловить такую тихую ошибку заранее, прежде чем клиент будет ждать шесть часов, а команде придётся распутывать следы записи.

Ошибки, которые ломают тест

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

Dry run проваливается, когда выглядит безопасным, но всё же что-то меняет. Команды часто блокируют записи в основном приложении и забывают про боковые пути вокруг него. Worker, retry job, webhook или scheduled task всё ещё могут обновить записи спустя долгое время после завершения первого запроса.

Такая ошибка распространена, потому что записи редко живут в одном месте. Модель возвращает метку, но другая задача сохраняет её, отправляет письмо, обновляет поле CRM или открывает тикет. Если хотя бы один из этих путей остаётся живым, тест оставляет после себя беспорядок.

Ещё одна частая проблема — слишком чистые фальшивые данные. Реальные пользователи вставляют длинные сообщения, кривые номера телефонов, странные форматы дат, текст капсом, скопированные подписи и недозаполненные формы. Аккуратные примеры скрывают именно те проблемы форматирования, которые ломают prompts, парсеры и правила маршрутизации.

Логи важны не меньше. Без них никто не сможет сравнить ожидаемый результат с фактическим. Вам нужна понятная запись входа, версии prompt, вывода модели, заблокированного действия и финального решения, которое система бы приняла. Иначе люди начинают спорить по памяти, а память плохо помнит крайние случаи.

Один удачный прогон почти ничего не доказывает. Workflow может пройти один раз и всё равно сломаться на ретраях, дублирующихся событиях, пустых полях или длинных входах. Запускайте одни и те же случаи больше одного раза. Потом специально прогоняйте грязные кейсы.

Сторонние сервисы — ещё одна ловушка. Вы можете заблокировать записи в своей базе и всё равно по ошибке вызвать живые системы. Это может отправить клиентские письма, создать тикеты поддержки, сжечь бюджет API или запустить последующие автоматизации вне вашего контроля.

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

Проверки перед live mode

Улучшите подсказки и инструменты
Приведите в порядок подсказки, вызовы инструментов и адаптеры записи с практичной поддержкой CTO.

Поставьте переключатель режима в одном месте. Один флаг должен решать всё: live writes включены или работает только dry run. Если одна задача читает DRY_RUN, а другая проверяет другой параметр, тест перестаёт быть надёжным.

Этот один переключатель должен доходить до каждого действия, которое может что-то изменить вне workflow. Вставки в базу — самая очевидная часть, но далеко не единственная. Отправка писем, обновления тикетов, вызовы webhook, изменения в CRM, загрузка файлов и фоновые задачи — всё это тоже должно читать один и тот же режим перед выполнением.

Правило должно оставаться простым. В dry run system может читать, классифицировать, оценивать, черновиковать и логировать, но не может создавать, обновлять, отправлять или удалять. Команды чаще всего пропускают один боковой путь, например retry worker или fallback-скрипт. Именно там и начинается беспорядок.

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

Используйте свежие крайние случаи, а не лёгкие примеры, с которых вы начали. Ещё раз прогоните странные кейсы из последних недель: пропущенные поля, дублирующиеся записи, злые сообщения клиентов, частичные загрузки и противоречивые инструкции. Если workflow чисто проходит их в dry run, у вас появляется гораздо больше причин доверять ему в live mode.

Сделайте шаг утверждения скучным и понятным. За финальное «да» должен отвечать один человек. В маленькой компании это может быть CTO, founder или инженер, который понимает весь workflow целиком. Смысл не в статусе. Смысл в том, что один человек проверяет доказательства и принимает риск.

Хорошо работает простой финальный чек: один ли флаг управлял запуском, подчинялись ли ему все внешние действия, были ли логи понятными, прошли ли недавние крайние случаи и одобрил ли live mode назначенный ответственный? Если хотя бы на один вопрос ответ «нет», подождите день и исправьте это.

Начните с одного workflow и расширяйте постепенно

Выберите один workflow, где плохая запись наносит ущерб, но масштаб всё ещё достаточно мал, чтобы за ним можно было внимательно следить. Это даст команде чистое место для обучения. Если попытаться сразу завернуть весь стек в dry run mode, люди пропустят скрытые записи, проигнорируют крайние случаи и перестанут доверять тесту.

Хорошая первая цель обычно имеет понятный вход, видимый результат и реальный шанс причинить вред, если что-то пойдёт не так. Маршрутизация обращений, одобрение возврата, оценка лидов и тегирование тикетов хорошо подходят. Изменения в зарплате и обновления контрактов рискованнее, и им обычно нужны более жёсткие меры контроля, прежде чем их можно безопасно тестировать таким способом.

Прежде чем добавлять ретраи, фоновые задачи или дополнительную автоматизацию вокруг этого workflow, сначала добавьте dry run mode. Вам нужен один простой путь, который принимает production-подобный вход, выполняет ту же логику и останавливается до любых изменений записей. Если базовый путь размыт, дополнительная автоматизация только скрывает проблему.

Для первого шага обычно хорошо подходят три признака: люди уже проверяют результат вручную, workflow работает с важными записями, и командa может тестировать его много раз за неделю. Это ускоряет обратную связь и облегчает поиск ошибок.

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

Потом расширяйтесь по порядку. Сначала докажите, что workflow может безопасно работать без записей. После этого добавьте логи и оповещения. И только потом добавляйте окружающую автоматизацию. Порядок внедрения скучный — и именно поэтому он работает.

Если вашей команде нужен аккуратный план запуска, Oleg Sotnikov на oleg.is может помочь проверить границы записи, пути отказа и порядок релиза в рамках своей Fractional CTO advisory работы. Такой внешний взгляд часто полезен, когда несколько систем затрагивают одни и те же записи.

Часто задаваемые вопросы

Что такое dry run в AI workflow?

Dry run позволяет workflow прочитать входные данные, похожие на реальные, выполнить ту же логику и показать, что он сделал бы, но без изменения настоящих записей. Вы можете проверить теги, владельцев, обновления полей и черновики ответов до того, как что-то попадёт в CRM, help desk или базу данных.

Почему бы не тестировать на живых данных небольшими партиями?

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

Что должен блокировать dry run?

Заблокируйте все побочные эффекты, а не только записи в базу. Остановите письма, сообщения в чат, вебхуки, создание задач, обновления CRM, загрузку файлов, ретраи и любые фоновые задачи, которые позже могут изменить другую систему.

Где обычно прячутся скрытые записи?

Скрытые записи чаще всего просачиваются через вспомогательные сервисы, worker'ы ретраев, queue jobs, fallback-скрипты и сторонние интеграции. Если какой-то шаг может отправить данные наружу или разбудить другую систему, считайте его записью и проводите через проверку dry run.

Что нужно логировать во время dry run?

Логируйте исходный ввод, версию prompt, ответ модели, вызовы инструментов, заблокированные действия, различия в полях, а также ошибки валидации или выполнения. Так у команды будет достаточно деталей, чтобы понять, что workflow пытался сделать и где логика свернула не туда.

Как добавить dry run, не переписывая всю систему?

Начните с точки входа workflow и задайте там один флаг dry_run. Затем передайте его во все вызовы инструментов и задачи, а все записи пустите через один слой адаптеров, чтобы можно было заменить реальные действия на preview-действия.

Какие тестовые данные стоит использовать?

Используйте обезличенные записи, но с теми же названиями полей, структурой и неровным содержимым, которое команда видит в продакшне. Сохраняйте странные пробелы, длинный текст, пустые значения, дублирующиеся заметки и противоречивые поля, потому что именно такие случаи чаще всего ломают prompts и правила маршрутизации.

Сколько записей нужно протестировать сначала?

Начните с десяти–двадцати реалистичных записей. Обычно этого хватает, чтобы поймать неверные предположения, а потом можно исправить логику и прогнать более крупную партию с большим количеством сложных случаев.

Как понять, что workflow готов к live mode?

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

С какого workflow лучше начать?

Выберите один workflow, который работает с реальными записями и уже проходит ручную проверку, например маршрутизацию обращений, одобрение возврата, оценку лидов или тегирование тикетов. Начните с него, докажите, что dry run ловит ошибки, и только потом добавляйте вокруг него больше автоматизации.