Понедельник, утренний чат. Руководитель поддержки спрашивает, почему вчера вечером система отказала клиенту в компенсации. Клиент недоволен и уже написал второй раз.

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

В чате становится тихо. Отвечать клиенту нечего.

Это самый частый разговор, ради которого меня зовут уже после внедрения. Вопрос «нужен ли нам агент» команда решила правильно, вход у них и правда живой. Ошибка случилась на следующем шаге. Агенту отдали не кусок процесса, а весь процесс целиком.

Нужен ли вероятностный компонент вообще, я разбирал отдельно, в статье про то, не хватит ли обычной программы. Здесь считаем, что ответ уже «да», и решаем другое. Где агент заканчивается и начинается обычный код.

Зрелая архитектура выглядит скучнее, чем слайд подрядчика

На слайдах бывает два варианта. «Агент делает всё» и «агента нет». Оба простые, и оба проигрывают третьему.

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

Эта схема действительно скучная. Зато на вопрос «почему система так поступила» у неё есть ответ, и ответ помещается в одно предложение, понятное клиенту, бухгалтеру и проверяющему.

Дальше про то, как провести границу между двумя частями.

Разрежьте процесс на шаги и подпишите каждый

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

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

Теперь поставьте напротив каждого шага одну из четырёх меток.

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

Дальше по каждому шагу задаётся один вопрос. Есть ли здесь настоящая неопределённость?

Настоящая неопределённость это когда вход каждый раз новый и его надо сначала понять. Ненастоящая это когда правило известно, просто его пока никто не записал.

Шаги с настоящей неопределённостью отдаются модели. Все остальные остаются обычным кодом.

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

Кому принадлежит каждый шаг

Вот та же разметка на примере обращения клиента в поддержку.

Шаг процесса Кому он принадлежит
Понять, о чём письмо Модель
Достать номер заказа из текста Модель, а сверка по базе за кодом
Решить, положена ли компенсация Правило в коде
Изменить сумму к возврату Только код
Проверить права сотрудника Только код
Составить черновик ответа Модель
Отправить клиенту Код, после подтверждения
Записать, что произошло Только код

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

Деньги, права и сроки остаются программой всегда

Три темы не отдаются модели ни при каких обстоятельствах.

  • Деньги. Сумма списания, размер компенсации, скидка, начисление бонуса.
  • Права доступа. Кто что видит и кто что может изменить.
  • Юридически значимые статусы и сроки. Договор заключён, заявление принято, срок рассмотрения истёк.

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

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

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

Правило формулируется коротко.

Если за ошибку в шаге отвечает не программист, шаг остаётся детерминированным.

Понять и сделать это два разных решения

Самая дорогая ошибка в разметке это слипание классификации и исполнения.

Выглядит она безобидно. Агент читает обращение, понимает, что это жалоба на задержку, и сразу оформляет возврат. Один шаг, одна стрелка на схеме, всё быстро.

Беда в том, что решений здесь два. Первое, чем является это обращение. Второе, что полагается делать с обращением такого типа.

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

Второе решение не вероятностное вовсе. Что полагается делать с жалобой на задержку, написано в регламенте компании. Там сказано, при какой задержке положена компенсация, в каком размере и с какими исключениями. Этот текст вообще не нуждается в модели. Он нуждается в переводе в код.

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

Практический признак разделения такой. Между «модель сказала» и «система сделала» есть место, куда можно поставить условие, запись в журнал и тест.

Как гибрид выглядит в обычный вторник

Сцена целиком, от письма до записи в базе.

11:40. Приходит письмо. «Добрый день. Заказ 3312 обещали в четверг, сегодня вторник, ничего нет. Мне это было нужно к выходным, я уже купила в другом месте. Верните деньги».

Модель читает его и выдаёт разметку. Тип обращения - возврат из-за задержки. Номер заказа - 3312. Тон - клиент раздражён. Дословная просьба - вернуть деньги.

Дальше модель молчит и больше ничего не решает.

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

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

Код проверяет лимит. Сумма ниже порога, при котором нужна ручная авторизация. Возврат проводится.

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

Оператор читает черновик две секунды и нажимает отправить.

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

Через месяц клиент спорит. Поднимается запись, в ней видно правило, по которому сработала система, и видно, что именно модель прочитала в письме. Разговор занимает пять минут вместо двух дней.

Сравните с началом статьи. Там был тот же процесс, только без границы внутри.

Что даёт разделение и во сколько обходится его отсутствие

Выгоды практические, их четыре.

  • Тестируемость. Правило возвратов покрывается обычными тестами. Задержка два дня, три дня, четыре. Оплата прошла, не прошла. Пятнадцать случаев, пятнадцать тестов, красный или зелёный. Разметку модели проверяют отдельно, на наборе настоящих писем, и это уже другая задача с другими инструментами.
  • Повторяемость. Одно и то же обращение даёт один и тот же результат, потому что решение принимает правило. Модель может по-разному сформулировать черновик ответа, и это никому не мешает.
  • Дешевле в эксплуатации. Модель вызывается два раза за обращение вместо десяти. На потоке в тысячу писем в день разница в счёте видна невооружённым глазом.
  • Объяснимость. И клиенту, и проверяющему вы показываете текст правила. Не журнал событий, не схему архитектуры, а предложение на русском языке, которое существовало до этого случая.

Признак, что граница проведена не там

Он один, и проверяется за полчаса.

Возьмите решение, которое система приняла вчера. Попросите команду воспроизвести его сегодня. Тот же вход, тот же результат и объяснение, почему результат именно такой.

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

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

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

Один процесс, один лист, полтора часа

Один процесс, один лист бумаги, полтора часа с командой.

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

Напротив каждого поставьте метку. Понимание, решение, действие, проверка.

Напротив каждого ответьте, есть ли там настоящая неопределённость, то есть надо ли сначала понять живой человеческий текст.

Обведите шаги, где ответ «да». Это территория модели, и обычно она оказывается заметно меньше, чем все ожидали.

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

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

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