![Волшебное дерево принятия решений Волшебное дерево принятия решений](https://habrastorage.org/getpro/habr/upload_files/534/db1/daf/534db1dafeefff6c1c10d00529e1dea8.jpg)
Введение
Сегодня мы покажем инструмент, который помог избавиться от хардкода в одной из legacy систем. Он здорово облегчил жизнь разработчикам и команде сопровождения. Возможно, кто-то найдет в нашем тексте решение и своей похожей проблемы.
Инструмент постараемся описать простым языком, без использования математических формул и заумных формулировок, ведь мы хотим продемонстрировать его практическое применение. Речь пойдет о аналитической подсистеме в CRM (далее по тексту просто «система»), которая отвечает за создание стратегии взаимодействия с потенциальным клиентом.
Немного предыстории
Опишем ситуацию, в которую мы попали около 5 лет назад. Возможно, кто-то узнает в ней себя, потому что история типовая.
На сопровождение и развитие нам передали CRM-систему. Если с частью системы, которая отвечает за работу операторов – звонки, сбор и накопление данных – все было понятно, то аналитическая часть, которая выстраивала стратегию взаимодействия с потенциальными клиентами, оказалась черным ящиком, да еще и со своими «скелетами в шкафу».
Проблемы, с которыми мы столкнулись:
Практически любое изменение бизнес-правил приводило к корректировке кода. Условно говоря, добавлялся новый "IF". С ростом бизнеса скорость и объем вывода новых изменений постоянно увеличивались. Изменения нужно было применять, что называется, на лету, однако текущий подход не позволял этого делать. Система превратилась в «кладбище с привидениями».
В случае возникновения вопросов отсутствовала возможность без привлечения разработчиков понять, почему был сделан тот или иной выбор. Разбор кейсов занимал длительное время.
Рефакторинг. Новые требования к системе
Всем стало ясно, что для дальнейшего развития системы нужны кардинальные изменения.
После анализа мы сформулировали ряд требований для рефакторинга. Вот они:
Вынести все бизнес-правила из кода приложения в настройки.
Заложить потенциал к подключению UI для визуализации бизнес правил.
Добавить возможность повторить расчет с учетом параметров субъекта на момент принятия решения.
Заложить возможность перенастройки основного объема бизнес-правил «на горячую» - без простоя системы. Сложность состояла в том, что надо было все запланированные требования выполнить без изменения бизнес-логики существующих процессов. А еще переход надо было совершить плавно, чтобы конечные пользователи не заметили никаких изменений.
Наше решение
В заголовке статьи мы намекнули, что ставку сделали на «дерево принятия решения» (далее просто дерево) и так называемые аналитические признаки.
Сначала раскроем суть некоторых терминов.
Дерево — структура данных, состоящая из узлов, которые соединены направленными дугами. В каждый узел ведет только одна дуга (кроме корневого, у которого вообще нет входящих дуг – см. рис.1).
Корневой узел — это начальный узел, не имеющий предков
Лист — это конечный узел, из которого не выходит ни одной дуги.
![Рисунок 1 Рисунок 1](https://habrastorage.org/getpro/habr/upload_files/c72/14a/6e5/c7214a6e5f1bf067b890a49645f14226.png)
Структуру можно считать деревом, если выполняются следующие условия:
Есть только один корневой узел. Из него стартует обход узлов дерева.
Каждый некорневой узел имеет только одного предка. Аналитический признак предлагаем рассматривать как некий параметр, значение которого анализируется при принятии решения. Это может быть:
Простой параметр – статический, например, продукт из интернет-заявки, возраст субъекта, пол и т.п.
Более сложный – динамический, вычисляемый в моменте и зависящий от ряда фактов.
Факты могут быть самыми изощренными из разряда «наличие интернет-заявок за последние 30 дней, поданных субъектом с определенным продуктом после дождичка в четверг, когда семь планет выстроились в ряд» и т.п.
Таким образом, аналитические признаки дают нам механизм, который позволяет упаковать экзотические требования заказчика и далее, жонглируя ими, настраивать бизнес-правила в деревьях.
Структуры в базе данных
Упростим структуры данных до минимума, который позволит продемонстрировать рассматриваемое решение. Даже замахнемся на святое - нормализацию ?.
Справочник аналитических признаков T_ANALYSIS_UNITS
Наименование | Тип | Комментарий |
ANALYSIS_NAME | Строка | Уникальное наименование |
ANALYSIS_DESCR | Строка | Описание |
DATA_TYPE | Строка | Тип аналитического признака (строка, число, дата) |
Таблица дерева T_TREE
Наименование | Тип | Комментарий |
ID | Число | Ид. Первичный ключ |
ID_PARENT | Число | Ид записи родителя |
ANALYSIS_NAME | Строка | Аналитический признак |
PRIORITY | Число | Приоритет обхода дерева на уровне |
ANALYSIS_COMPARE_VALUE | Строка | Значение аналитического признака для сравнения |
OPERATOR_TYPE | Строка | Оператор сравнения (=, >, <, >=, <=, !=, in) |
TARGET_VALUE | Строка | Целевое значение |
Графическую перечисленные структуры мы отобразили на рис 2.
![Рисунок 2 Рисунок 2](https://habrastorage.org/getpro/habr/upload_files/587/bf0/941/587bf0941deafdeba3c3612e8ed1da93.png)
Алгоритм принятия решения
Целью работы алгоритма является определение целевого значения. Этим значением для разных типов деревьев может быть: дата вывода на обзвон, дата отправки информационного сообщения, выбор финансового консультанта, выбор причины исключения из обзвона и т.п.
В демонстрационном примере будем выбирать абстрактную стратегию взаимодействия с клиентом.
Ниже представлены записи в таблицах T_ANALYSIS_UNITS и T_TREE, которые задают структуру дерева на рис 2.
T_ANALYSIS_UNITS | ||
ANALYSIS_NAME | ANALYSIS_DESCR | DATA_TYPE |
А1 | Тип события | Строка |
А2 | Город проживания | Строка |
А3 | Продукт | Число |
А4 | Сумма кредита | Строка |
А5 | Время поступления интернет-заявки. После 21:00 принимает значение ‘Y’, иначе ‘N’. | Строка |
T_TREE | ||||||
ID | ID_PARENT | ID_ANALYSIS_UNIT | PRIORITY | ANALYSIS_COMPARE_VALUE | OPERATOR_TYPE | TARGET_VALUE |
1 | А1 | 100 | EventInternetApp | = | ||
2 | 1 | А2 | 100 | Казань | = | |
3 | 1 | А2 | 100 | Саратов | = | |
4 | 2 | А3 | 100 | Продукт_01 | = | Стратегия_01 |
5 | 2 | А3 | 100 | Продукт_02 | = | Стратегия_02 |
6 | 3 | А5 | 120 | Y | != | Стратегия_03 |
7 | 3 | А3 | 100 | Продукт_03 | = | Стратегия_04 |
8 | 3 | А3 | 100 | Продукт_04 | = | |
9 | 3 | А3 | 100 | Продукт_05 | = | Стратегия_05 |
10 | 8 | А4 | 100 | 100000 | <= | Стратегия_06 |
11 | 8 | А4 | 100 | 100000 | > | Стратегия_07 |
Как все это работает?
Предварительно рассчитываем значения всех аналитических признаков. Получаем их в виде коллекции. Начинаем пробегать дерево. Обход стартует с корня. Движемся сверху вниз.
Порядок обхода узлов дерева, находящихся на одном уровне, определяется их приоритетом. Приоритет нужен в случае, когда на одном уровне встречаются узлы с разными аналитическими признаками.
Чтобы провалиться в узел, мы должны выполнить его условия. Для этого:
Из узла берем аналитический признак.
Подтягиваем заранее посчитанное для него значение.
Сравниваем с эталонным значением, применяя оператор сравнения, указанный в узле.
Если результат операции сравнения true, то проваливаемся в узел.
Если false, то путь закрыт – переходим к следующему узлу.
Обход завершается при наступлении одного из событий:
мы достигли любого листа дерева (получили целевое значение),
обошли все дерево, но так и не удалось добраться до листа (ничего не выбрали).
Примеры
На вход дерева подадим коллекцию значений аналитических признаков.
Пример №1:
А1 = EventInternetApp, А2= Казань, А3= Продукт_02, А4= 80000, А5= Y
![Рисунок 3 Рисунок 3](https://habrastorage.org/getpro/habr/upload_files/502/86f/6d9/50286f6d92235addf1cd07da1cea56ae.png)
Результат выбора: Стратегия_0
Пример №2:
А1 = EventInternetApp, А2= Саратов, А3= Продукт_03, А4= 80000, А5= N
![Рисунок 4 Рисунок 4](https://habrastorage.org/getpro/habr/upload_files/247/455/d1d/247455d1dab920d2124d850c40df9deb.png)
Результат выбора: Стратегия_03
История расчета
Все посчитанные значения аналитических признаков сохраняются в базу данных. Это позволяет при возникновении вопросов или спорных ситуаций повторить расчет по дереву и провести анализ. Объем данных большой, поэтому период хранения ограничен: в нашем случае 6 месяцев.
Подведем итоги
На этом, пожалуй, все. Материал пытались изложить максимально кратко, не перегружая подробностями реализации. Цель – поделиться опытом и продемонстрировать удачное решение в действии. Как бывает в жизни: есть алгоритмы или математические модели, но как их применить на практике – не всегда очевидно.
Классическое решение в виде «дерева выбора» обладает двумя ключевыми свойствами:
Гибкость настройки (добавил/переместил узел дерева – получил желаемую логику).
Простота визуализации (для отображения прикрутить UI не составит особого труда).
Последнее свойство дарит еще такую плюшку как «самодокументирование».
Функционал дерева можно постоянно расширять под свои задачи. Например, на начальных этапах у нас были простые операторы сравнения в узлах, позже мы добавили операторы работы со множествами. Область применения ограничивается только вашей фантазией ? Спасибо за внимание! Надеемся, что материал был для вас полезен.