Нормализация чисел: как 155 строк кода решают проблему «роботизированного» голоса

Habr AI · оригинал

Материал подготовлен автоматизированной редакционной системой. Факты можно сверить по указанному первоисточнику.

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

Качество голосового интерфейса критически зависит от естественности произношения чисел. Описанный подход позволяет стандартизировать вывод TTS-систем, делая его независимым от конкретного движка синтеза и повышая доверие пользователей к автоматическим звонкам.

В современном голосовом интерфейсе качество синтеза речи уже не является главным барьером для восприятия. На телефонных линиях с ограниченной полосой пропускания разница между «хорошим» и «отличным» TTS-движком часто стирается. Однако есть маркер, который мгновенно выдаёт собеседника как программу: способ чтения чисел. Дата, прочитанная как дробь, или телефонный номер, озвученный как единое десятизначное число, заставляют человека вспомнить о взаимодействии с ИИ. Автор статьи предлагает решение этой проблемы через слой нормализации текста, который преобразует все числовые данные в слова до передачи их в синтезатор.

Архитектурное решение основано на двух принципах: детерминизме и переносимости. Разные TTS-движки по-разному интерпретируют форматы вроде «1 490 ₽» или дат. Перенося логику чтения чисел в отдельный слой, автор гарантирует, что любой движок получит уже готовый текстовый вариант на словах. Это позволяет легко менять провайдера синтеза (например, с закрытого API на открытую модель), не переписывая бизнес-логику. Единственная внешняя зависимость библиотеки — пакет num2words, который переводит числа в слова.

Первая техническая сложность связана с грамматикой русского языка. Библиотека num2words генерирует порядковые числительные в мужском роде («пятнадцатый»), тогда как для дат требуется средний род («пятнадцатое»). Автор реализует функцию замены окончаний, но сталкивается с исключением для числа «третий», которое требует ручной обработки из-за мягкой основы. Ошибка здесь приводит к неестественному звучанию вроде «третьье», что легко улавливается на слух.

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

Особое внимание уделяется телефонным номерам. Человек диктует их группами, а не по одной цифре или как одно большое число. Критичным моментом являются группы с ведущим нулём (например, «005»). Если прочитать их как число, получится «пять», что приведёт к ошибке при записи номера. Алгоритм определяет ведущий ноль и читает такие группы по цифрам, а остальные — числами.

Также библиотека решает проблему склонения существительных после числительных («один рубль», «два рубля», «пять рублей»), включая исключения для чисел от 11 до 14. Автор честно указывает на ограничения: система не анализирует контекст предлогов, поэтому фраза «до 03.03.2026» может быть прочитана в именительном падеже вместо требуемого родительного. Это компромисс между сложностью кода и качеством результата, приемлемый для большинства сценариев голосовых агентов.

В результате получается компактный инструмент из 155 строк кода, который можно легко перенести в любой проект. Он не заменяет полноценную inverse text normalization, но закрывает базовые потребности в естественном звучании речи для русскоязычных пользователей. Код открыт под лицензией MIT и включает набор тестов, проверяющих корректность склонений и обработки специальных случаев.