- BrainTools - https://www.braintools.ru -
Шахматы — почти идеальная игра для голосового интерфейса. Состояние компактное, ходов конечное число, правила формальны. Казалось бы, бери и делай.
На практике голосовой навык ломается не о шахматы, а о расшифровку речи. Игрок говорит «пешка е два е четыре», распознавание может вернуть «пешка е два и четыре». Для человека смысл очевиден; навык должен восстановить e2–e4 и при этом не угадывать, если позиция или качество разбора оставляют несколько вариантов.
Ниже — про то, как устроен навык «Шахматы с Юрой» для Яндекс Алисы и какие решения пришлось принять, чтобы игра оставалась играбельной на слух [1].
Навык делался в первую очередь для незрячих и слабовидящих игроков. Из этого следует правило, которое пришлось соблюдать буквально везде: любое действие, нужное для партии, должно работать без экрана, и любой ответ должен звучать целиком.
Экранная карточка с доской есть, но она лишь дополняет ответ: ни одно обязательное действие не требует экрана, а позицию, историю и результат можно получить голосом. Это не про доступность как галочку в чек-листе — иначе продукт просто не работает для той аудитории, для которой он сделан.
Побочный эффект, которого я не планировал: тот же режим оказался удобен зрячим игрокам, тренирующим игру вслепую. Раньше это упиралось в живого партнёра, который ведёт доску и называет ходы. Терпеливого партнёра, готового к тридцатой партии подряд, найти сложно.
Распознавание речи обучено на обычном языке, а шахматная запись состоит из коротких похожих слогов. Названия вертикалей легко спутать; парсер учитывает, например, варианты «бэ», «пэ», «дэ» и склеенные координаты вроде «едва». Игроки также опускают предлог: «конь эф три» вместо «конь на эф три». Один и тот же ход можно правильно назвать несколькими способами.
Первая версия пыталась нормализовать фразу регулярками и надеяться на лучшее. Замеров я тогда не вёл, поэтому цифры не назову — но спор с ассистентом посреди партии случался достаточно часто, чтобы переписать этот слой целиком.
Рабочим оказался другой подход: текущая позиция служит словарём. Для каждого допустимого хода строятся возможные голосовые формы, а нормализованная фраза сопоставляется с ними. Один вариант с достаточной уверенностью — применяем ход. Несколько вариантов или низкая уверенность — переспрашиваем и не меняем доску. Если допустимого варианта нет, но фигуру и поля удалось восстановить, отдельно объясняем нарушенное правило; если намерение осталось неясным, просим уточнить.
Последний случай важнее, чем кажется. «Так нельзя» — бесполезный ответ, когда игрок не видит доску. Например, навык называет поле первой фигуры, закрывающей путь: «Путь закрыт: на поле e3 стоит пешка».
Соблазн очевидный: Stockfish и так считает позицию, пусть он и скажет, законен ли ход. Так делать нельзя, и в проекте это записано отдельным инвариантом.
Stockfish — оптимизирующий поиск, а не арбитр правил. Он работает со своим представлением позиции, его вывод зависит от параметров поиска, и он вправе молчать при нехватке времени. Правила проверяет python-chess, детерминированно и без сети. Движок выбирает ответ соперника и используется в аналитических функциях: по запросу игрока, при разборе партии и для предупреждений тренера.
Разделение дало неожиданный бонус: тесты на правила не требуют движка вообще. Полный прогон шахматной логики идёт без единого процесса Stockfish.
Алиса обрывает запрос через пять секунд. Не «показывает спиннер» — обрывает. Значит, весь путь «распознали → разобрали фразу → проверили правила → нашли ход → собрали речь» должен укладываться в бюджет с запасом.
Как это устроено:
общий бюджет ответа — 4,5 с; ожидание свободного процесса Stockfish вместе с поиском получает не более 3 с;
Stockfish живёт постоянно запущенным пулом процессов (по умолчанию два), каждый со своей блокировкой: запуск процесса на каждый ход не влезал в бюджет;
пул ограничен, и при исчерпании навык отвечает осмысленной фразой, а не встаёт в очередь до обрыва;
поиск уходит с event loop, чтобы не блокировать обработку.
Отдельная история — идемпотентность. Алиса повторяет запрос при сетевых сбоях, и повтор не должен приводить ко второму ходу. Каждый запрос несёт идентификатор, по которому распознаётся повтор, и повторный запрос получает тот же ответ, а не новую партию.
Первая версия команды «какая позиция» читала всю доску одним ответом. Технически корректно. На слух — бесполезно: к четвёртой горизонтали первая уже забыта.
Сейчас доска читается по две горизонтали за раз, «дальше» продолжает чтение. Но чаще работает не полное чтение, а точечные вопросы: «что на е четыре», «где белые слоны», «чей ход», «есть ли шах», «какие фигуры съедены», «какой был последний ход», «что делали чёрные четыре хода назад».
Та же логика [2] в справке: единый длинный список команд ухом не воспринимается, поэтому справка разбита на именованные разделы и читается страницами по три строки, с навигацией «дальше», «назад», «сначала».
Общий вывод, применимый к любому голосовому интерфейсу: ограничение — не время ответа, а объём, который удерживается в памяти [3] без возможности перечитать.
Голосовая сессия обрывается легко: колонку выключили, ушли из комнаты, Алиса переключилась на другой навык. Партия при этом не должна теряться.
Полное состояние партии хранится на сервере: каноничен список ходов UCI, FEN — производный снимок. В состоянии Алисы остаются идентификатор партии, ревизия и ограниченные данные текущего диалога — например, незавершённое уточнение и позиция в справке. Самой доски и полной истории там нет. Команда «продолжить последнюю партию» восстанавливает позицию с сервера и напоминает последние ходы.
Исходный Alice ID в базу не попадает: до сохранения из него вычисляется псевдоним через HMAC-SHA256 с серверным секретом. Сырые webhook-запросы и аудио не сохраняются. Для улучшения распознавания навык до 30 дней хранит нормализованный текст команды и результат маршрутизации, связанные только с псевдонимом. Ключи запросов и сессий в служебной статистике хешируются, а публичная страница показывает лишь агрегированные счётчики и динамику по дням — без идентификаторов и текстов команд.
Открытая статистика проекта, который просит доверия у аудитории с повышенной чувствительностью к приватности, стоит дороже любого обещания в описании.
Полная партия против Stockfish, уровень сложности от 0 до 20, сохранение и продолжение.
Объяснение недопустимого хода конкретной причиной, когда понятен его замысел.
Голосовое описание позиции, истории, фактов о партии, дебюта и стадии.
Режим тренера: оценка позиции словами и числом, ходы-кандидаты, угрозы, подсказки от общего намёка до конкретного хода, разбор варианта без его исполнения.
Разбор сыгранной партии: перелом, главная ошибка [4], PGN, переигрывание позиции.
Голосовые задачи: мат в один и два хода, вилка, связка, сквозной удар.
Настройки речи, которые запоминаются между партиями.
Стек: Python 3.12, FastAPI, SQLAlchemy, MariaDB, python-chess, Stockfish. Модульный монолит, исходники открыты.
Попробовать: «Алиса, запусти навык Шахматы с Юрой». Сайт проекта: yurachess.ru [5]. Про игру без экрана — отдельная страница [6], про тренировку вслепую — здесь [7]. Код: github.com/Blaryxoff/yura-chess [8].
Что я сознательно не привожу: цифр точности распознавания. Корпус реальных фраз собирается с июля, но исторический результат показывает поведение [9] той версии, которая работала в момент запроса. Чтобы честно назвать точность текущей версии, нужно повторно прогнать корпус через актуальный маршрутизатор и отдельно проверить выборку вручную. До такого замера процент был бы выдумкой.
Замечания и разборы кейсов, где навык говорит непонятно, — то, ради чего эта статья и написана.
Автор: blaryx
Источник [10]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/35977
URLs in this post:
[1] слух: http://www.braintools.ru/article/6251
[2] логика: http://www.braintools.ru/article/7640
[3] памяти: http://www.braintools.ru/article/4140
[4] ошибка: http://www.braintools.ru/article/4192
[5] yurachess.ru: https://yurachess.ru/?source=habr_1
[6] отдельная страница: https://yurachess.ru/accessibility?source=habr_1
[7] здесь: https://yurachess.ru/blindfold?source=habr_1
[8] github.com/Blaryxoff/yura-chess: https://github.com/Blaryxoff/yura-chess
[9] поведение: http://www.braintools.ru/article/9372
[10] Источник: https://habr.com/ru/articles/1086234/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1086234
Нажмите здесь для печати.