
Привет, Хабр! Меня зовут Игорь Козлов и я — ведущий разработчик в команде «Membrana Голос» от МТС. Наш продукт — это сервис, который принимает звонки, когда вы заняты, и общается со звонящими почти как живой человек. Он основан на полноценном диалоговом фреймворке, десятках моделей машинного обучения и тщательно продуманной логике обработки интентов. Мы взяли за основу open source-фреймворк Rasa, но сильно его доработали, чтобы превратить в гибкую платформу для управления голосовыми диалогами.
В этом материале я покажу, как мы переписали Action Server и обучили DIETClassifier, а также расскажу, как нам удалось обойти ограничения Rasa и сделать умного, настраиваемого и устойчивого к ошибкам телефонного ассистента.
Зачем нам понадобился Rasa
Membrana — это управляемый тариф, в котором кибербезопасность вынесена на уровень оборудования оператора. В его основе — отдельное приложение, которое помогает пользователю управлять входящими звонками и защищать личные данные в сети.
Одна из фич — встроенный ИИ-секретарь, который берёт на себя рутину общения. Он умеет определять приоритетность звонков: забирает на себя незнакомые вызовы, а звонки от добавленных контактов напрямую отправляет пользователю.
Телефонный секретарь Membrana основан на open source-версии фреймворка Rasa. По сути, это связующее ядро, которое объединяет разные инструменты обработки естественного языка в единый пайплайн — от предобработки текста до генерации ответа. Это и есть главное преимущество Rasa: она позволяет собрать в одном месте всё, что нужно для диалога — модели, фичавайзеры, экстракторы и кастомную логику.
Как мы дорабатывали фреймворк Rasa для Membrana
В основе работы фреймворка Rasa лежат две ключевые составляющие:
Core — набор ML-компонентов, которые отвечают за обработку пользовательских сообщений. Core анализирует входной текст, извлекает из него entity (сущности) и определяет intent (намерение пользователя). У этой части есть собственный endpoint, через который проходит вся цепочка обработки: от токенизации и фичирования до классификации. Результаты работы Core — распознанные интенты и сущности — передаются дальше в Action Server.
Action Server — компонент, который управляет поведением ассистента. Он получает структурированные данные от Core и выбирает, какой ответ или действие нужно выполнить. Логика этой части описана на Python и основана на наборе правил и сценариев.
Главная особенность Rasa — end-to-end-подход: вся цепочка обработки, от текста до ответа, объединена в единую модель. Это и преимущество, и ограничение. Из коробки Rasa следует строгой архитектурной логике, описанной в документации, и не позволяет легко от неё отступать.
Однако в Membrana мы решили эту проблему. Мы перенастроили взаимодействие стандартных компонентов Rasa так, чтобы они работали с нашей собственной логикой принятия решений. Теперь фреймворк использует кастомные сценарии, которые проще дорабатывать, тестировать и масштабировать без переобучения модели.
«Из коробки» Rasa не решает всех задач. Чтобы добиться качественных ответов, мы пересобрали архитектуру и разделили ответственность за выбор реплики на две части:
ML-компоненты — сюда входят модели, экстракторы сущностей, фичарайзеры и прочие инструменты машинного обучения.
Python-логика — код, который управляет выбором ответов и сценариев на основе заданных правил и эвристик.
Это разделение дало нам два независимых направления для развития секретаря:
Улучшить ML-часть. Мы можем точнее определять интенты — за счёт дополнительного обучения, использования предобученных или более сложных моделей и подбора гиперпараметров. Это путь повышения метрик и точности распознавания.
Усилить логику правил. Даже если интент определён неверно, с помощью эвристик и дополнительных проверок мы можем «переориентировать» пользователя в нужную ветку сценария. Так система остаётся устойчивой к ошибкам и ведёт диалог более естественно.
В результате, Membrana не просто реагирует на команды, а адаптируется к поведению пользователя, сохраняя контекст и корректируя собственные решения на лету.
Расскажу подробнее о внесённых в Rasa изменениях.
Доработка Core
Основное ядро распознавания в Rasa — это компонент DIETClassifier. Он отвечает за определение интента (намерения пользователя) и entity (сущностей в тексте). Однако его ключевое ограничение заключается в том, что модель не является предобученной: она обучается с нуля во время сборки общей модели секретаря — на внутреннем датасете, который, по очевидным причинам, не может охватить всё разнообразие реальных фраз.
Из-за этого DIETClassifier иногда испытывает трудности с пониманием фраз, которые не представлены в обучающем наборе. Чтобы повысить качество распознавания, мы внедрили в пайплайн custom components — собственные модули, подключающие предобученные модели:
SpaCy с моделью ru_core_news_sm;
RuBERT Tiny;
Natasha.
Эти инструменты извлекают дополнительные векторные признаки (features) для DIETClassifier и помогают корректнее выделять сущности, с которыми базовой модели работать сложно.
Помимо этого, в процессе тестирования мы столкнулись с неожиданной проблемой: модель периодически ошибочно определяла нецензурные выражения там, где их не было. Это происходило из-за схожести некоторых слов с матерными по написанию. Чтобы снизить количество ложных срабатываний, мы добавили компонент Sentiment Analysis, основанный на RuBERT Tiny. Он позволяет оценивать эмоциональную окраску фразы и определять, действительно ли в сообщении есть негатив — или это просто ложная тревога.
Отдельное улучшение коснулось извлечения имён. Мы добавили модель Natasha специально для выделения entity типа “имя”. Предобученные модели существенно повышают качество в этой области: без них закрыть весь спектр имён в русском языке практически невозможно.
Доработка Action сервера
Изначально Rasa следует декларативной парадигме: всё поведение ассистента заранее описывается в виде набора правил, кода и конфигураций, после чего модель обучается и формирует строго определённую реакцию на каждое событие.
Такой подход удобен, когда сценарии стабильны, но плохо работает в системах, где логика должна динамически меняться. Любое обновление — будь то новый ответ или корректировка сценария — требовало переобучения всей модели с подбором гиперпараметров. Чтобы избавиться от этой проблемы, мы кардинально переработали архитектуру Action Server и сделали три ключевых изменения:
Вынесли в отдельный компонент тексты, которые возвращала модель. Ранее они были жёстко “зашиты” в её структуру. Теперь они могут редактироваться независимо от обучения. Это позволяет оперативно менять формулировки и сценарии без пересборки модели.
Вынесли логику принятия решений из тела модели в самостоятельную компоненту. Теперь сценарии можно дополнять и изменять на лету, просто обновляя описание правил — без необходимости переобучения.
Создали новую систему определения сценариев. В классической Rasa переход между состояниями зависел от простых условий: распознанного интента и найденного entity. Теперь система может учитывать: несколько интентов и entity одновременно, их историю, текст запроса, комбинированные условия (AND, OR) и приоритеты срабатывания.
Для управления этой логикой мы написали собственный диспетчер, который анализирует все доступные признаки и выбирает наиболее подходящий сценарий. Структура сценариев описана в YAML-файлах, и теперь их можно редактировать или добавлять новые без обучения модели.
В результате мы получили гибкий и масштабируемый Action Server, который сохраняет преимущества Rasa, но при этом позволяет развивать логику ассистента как полноценный программный модуль — независимо от ML-части.
Схема диалога
Мы используем Rasa с полностью измененной логикой как Action-сервера, так и Core. Получился совершенно новый фреймворк, использующий все преимущества Rasa и лишенный его недостатков. Покажу, как это работает в кейсах с диалогами.

Когда пользователь взаимодействует с ИИ-секретарём, весь процесс обработки запроса проходит через несколько этапов:
Получение данных. Запрос переводится в текстовый формат и вместе с метаинформацией передается в компонент Core.
-
Извлечение признаков (feature extraction). Внутри пайплайна выполняется несколько шагов:
SpaCyNLP токенизирует текст и извлекает базовые лингвистические признаки.
RuBERT Tiny добавляет контекстные векторные представления (эмбеддинги) слов.
CountVectorFeaturizer строит частотные представления слов и фраз.
RegexFeaturizer выделяет паттерны на основе регулярных выражений.
Полученные фичи объединяются и передаются в DIETClassifier, который определяет интент (в нашем примере — greet, приветствие).
-
Извлечение сущностей (entity extraction). На этом этапе работают несколько специализированных компонентов:
RegexEntityExtractor — ищет сущности по регулярным шаблонам.
EntitySynonymMapper — сопоставляет найденные значения с заранее определёнными синонимами.
SentimentAnalysis — определяет эмоциональный тон сообщения (например, mood = neutral).
Natasha — извлекает именованные сущности, такие как имена людей (в нашем случае — Игорь).
Передача данных в Action Server. Вся собранная информация (интенты, сущности, настроение и контекст) отправляется в Action Server. Сначала базовое правило Rasa перенаправляет данные в наш кастомный диспетчер, который оценивает вероятность каждой ветки сценария.
Выбор сценария и генерация ответа. Диспетчер рассчитывает «очки» для каждой ветки на основе интентов, сущностей и текста. В приведённом выше примере максимальное значение получает сценарий state_name. Далее запрос попадает в соответствующий обработчик, где Python-код анализирует контекст (например, знаком ли абонент, звонил ли он сегодня) и формирует финальный ответ, который возвращается пользователю.
Итоги
За год работы с Rasa я глубоко погрузился в его логику и могу сказать, что это очень мощный инструмент, незаслуженно обделенный вниманием в России. У него много интеграций с разными площадками (telegram, slack и другие), а также есть подробные гайды почти на все случаи жизни. Если перед вами стоит задача бысто создать чат-бота, рекомендую обратить внимание именно на Rasa.
Для работы с Rasa важно понимать специфику собственных задач. Большинству скриптовых сценариев достаточно базовой логики — она достаточно хорошо реализована в этом фреймворке. В этом материале я описал моменты, на решения которых может уйти достаточно много времени. Надеюсь, это поможет с вашими проектами.
По итогам работы мы c одной стороны ушли от стандартных сценариев использования фреймворка и полностью переписали свой action-cервер, но с другой — сохранили логику поведения модуля. Это позволит нам в будущем легко обновляться или использовать pro-версию без поддержки собственного форка. Так мы закрыли свои потребности без использования ресурсоемких LLM.
Дальше мы планируем развивать модель в сторону персонализации диалогов, самообучения на данных и повышения устойчивости к шумам и ошибкам речи. Цель — сделать цифрового секретаря, который не ищет готовый ответ, а анализирует запрос и может на него ответить даже при недостатке данных.