Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Мы тут автоматизируем автобусы, недавно вот с нашей помощью все билеты в России стали электронными. Рынок только-только хоть как-то приходит в ИТ, и там всё ещё очень много всего делается в амбарных книгах.
Расскажу про один простой эпизод автоматизации, который уже десятки лет назад пройден в авиации и на железной дороге, но только-только начался у нас. Итак, ситуация: есть примерно сотня разных информационных систем, которые присылают нам данные про автобусные рейсы. Это набор самописных автоматизаций разных перевозчиков и конкурирующих коммерческих продуктов. Каждая система имеет свой формат записи о том, как делается возврат билета на автобус. Чаще всего — человекочитаемая запись на русском языке, написанная для операторов и кассиров, но около 20% систем вообще не присылают данные о возврате в принципе.
Часть правил пересекаются, причём может быть несколько уровней вложенности: «Все билеты невозвратные, но на этом направлении туда возвращаем по 259-ФЗ, обратно — вот по этим условиям».
Нам нужно показывать пассажиру условия возврата билета (возвратный, невозвратный, 100% возврат или нет, когда можно возвращать), использовать эти параметры для поиска, сравнения и, собственно, автоматизации возвратов.
Ну а мне нужно было понять, как несколько тысяч текстов на русском языке превратить в параметры билета, где хранить и как этим всем управлять.
Как выглядят ответы источников данных?
Вот примеры:
Первое, что приходит в голову — это NLP-разбор. Чтобы научить нейросетевой движок NLP разбирать всё это, нужен набор правил, который сформирует корпус для обучения. Чтобы получить набор правил, нужно распарсить всё вручную и свести к неким наборам правил в едином формате.
Решение оказалось простым, как бревно. Правила возврата почти у всех не меняются годами, и в месяц приходит всего несколько новых строк. У нас есть отдел контента, который занимается тем, что собирает данные из разных источников — например, обзванивает автовокзалы, собирает данные об остановках и так далее. Что-то из этого автоматизируется, что-то нет. То, что автоматизируется — покрывается скриптом и тестами и идёт в прод. То, что делается вручную, может быть упрощено тем, что в контент будут приходить уже подготовленные данные, то есть мы будем вызывать человека-оператора через некое API, содержащее типовую форму запроса.
Распарсить всё вручную один раз и поддерживать изменения вручную оказалось дешевле, чем прикручивать и поддерживать автоматизацию, а потом следить за её правильностью. В итоге мы использовали сложные нейросети — непосредственно мозг операторов. И они показали очень высокую производительность.
Потом мы добавили хеширование правила по MD5 после очистки от нефункциональных пробелов и приведения к одному регистру — чтобы понимать, что оно поменялось. Если оно поменялось — автоматизация ставит задачу отделу контента, а отдел контента заносит новое правило в нашу систему.
Опять же, правильно использовать для хранения множества правил решения класса BRMS. Но у нас всё оказалось проще, всё множество правил свелось к вот таким матрицам:
В этой итерации решили на модификаторы забить. Во-первых, непонятно какие они бывают. Во-вторых — похоже, что они мало где используются. По крайней мере, до сих пор особой необходимости в них не возникало.
Превращается это вот в такой текст единого формата:
Поэтому мы их храним прямо в своей системе, управляющей параметрами билетов. То есть, по сути, просто добавляем в базу данных в каждый билет ссылку на правила его возврата от данной компании.
Вот так это стало выглядеть:
GDS — это источники, дальше идёт «схлопывание» рейсов (один и тот же рейс может прийти из разных источников с некоторыми изменениями, больше про этот ад есть вот здесь, например).
Вот так работает уровень матчера правил. Из каждого рейса достаётся правило возврата, по его хешу ищется соответствующее наше правило (разобранное в нужный нам вид), и если всё получилось, оно применяется:
Часто GDS не присылает для какого-то рейса правила возврата. В таком случае мы можем завести свои, «ручные», правила возврата. Например, можем применить стандартные, прописанные в федеральном законе. Кстати, что интересно, по идее, это должны быть минимальные условия для всех, но на практике они часто либо улучшаются, либо ухудшаются перевозчиками.
У перевозчиков могут быть локальные правила, как я приводил пример — «для всех рейсов так, но на рейсах Москва — Петербург — вот так». Специально для этого мы для «ручных» правил сделали параметр «приоритет». В итоге такое «ручное» правило возврата состоит из трёх частей: параметры, по которым мы понимаем, что это правило подходит (город отправления/прибытия, перевозчик, GDS), приоритет и результат (собственно, те самые интервалы с процентами удержания). Когда GDS отдаёт рейс без правил возврата, мы идём в базу с «ручными» правилами, выбираем все подходящие и берём то, у которого наибольший приоритет. Дальше рейс декорируется этими полученными правилами.
Конечно, мы можем что-то не покрыть такими «ручными» правилами. Для этого мы сделали отчёт, в который попадают направления, не покрытые правилами. Его в ручном режиме разбирают сотрудники отдела контента.
Вот так. Как я говорил, всё довольно просто, но таких ситуаций на рынке ещё море, потому что автобусный рынок только-только открывает электронные продажи, и там огромный зоопарк самописных решений, либо же вообще часто никакой автоматизации нет.
Ну а мы теперь создали единую базу правил возврата билетов для каждого известного нам официального автобусного рейса в России.