VEU.WathBody/1-docs/4-modules/m11-voice-input/api_specs/01-voice-command-parser.md

109 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Спецификация 01: VoiceCommandParser — грамматика Ф-1.2
Источники: Ф-1.2 (грамматика), Ф-1.3 (отклонение), О-2-Б («с
половиной» → +0,5 кг), tech-req 5 (постобработка чисел). Единица 1.
Чистая функция JVM — без Android-зависимостей.
## 1. Интерфейс
```kotlin
sealed interface VoiceCommand {
data class Approach(val weightKg: Double, val reps: Int) : VoiceCommand
data class Weight(val weightKg: Double) : VoiceCommand
data class Rest(val seconds: Int) : VoiceCommand
data class Rpe(val rpe: Int) : VoiceCommand
data class Rejected(val reason: String, val attempted: JsonObject?) : VoiceCommand
}
object VoiceCommandParser {
fun parse(alternatives: List<String>): VoiceCommand?
// null — ни одна гипотеза не содержит распознаваемой команды
fun semantics(cmd: VoiceCommand): JsonObject // для журнала Ф-1.5
}
```
`parse` получает список финальных гипотез Vosk (спец 02, до 3;
режим одного варианта — список из одного) и возвращает первую
успешно разобранную команду; если все гипотезы мусор — `Rejected`
из первой гипотезы. Отклонённая команда — тоже результат (журнал
Ф-1.3/1.5), «не было команды вообще» (тишина/чужая речь) — `null`,
в журнал не пишется.
## 2. Предобработка текста
- нижний регистр, ё→е, удаление знаков препинания, сжатие пробелов;
- нормализация аббревиатуры RPE: «эр пэ э», «эрпэ» → «рпэ»
(свободное распознавание слышит буквы по отдельности);
- токены — слова и цифровые группы.
## 3. Числа (`NumberRu`, 0–999)
- **словами**: [сотни] [десятки] [единицы] → сумма. Словари:
`ноль`; `один/одна/одно`, `два/две`, `три…девятнадцать`;
десятки `двадцать…девяносто`; сотни `сто, двести, триста,
четыреста, пятьсот…девятьсот`. Составное число — подряд стоящие
разряды («сто двадцать три» → 123); постобработка «сто двадцать»
→ 120 (tech-req 5);
- **цифрами**: `120`, дробное `120,5` / `120.5` (= только 5 после
разделителя — шаг 0,5);
- **«с половиной»** после числа → +0,5 (О-2-Б); допустимо ТОЛЬКО
в позиции веса `W` (команды Approach/Weight); «с половиной»
в позиции `R`/`S`/`N` — грамматическая ошибка (отклонение);
- порядок разрядов нарушен («двадцать сто») — грамматическая
ошибка.
## 4. Грамматика команд (Ф-1.2)
| Команда | Шаблон | Диапазоны |
|---|---|---|
| Approach | `подход` `W` [`с половиной`] `на` `R` | W 0–500 (шаг 0,5), R 1–100 |
| Weight | `вес` `W` [`с половиной`] | W 0–500 (шаг 0,5) |
| Rest | `отдых` `S` [`секунд` / `секунды`] | S 5–600 |
| Rpe | `рпэ` `N` | N 1–10 |
- ключевые слова обязательны и однозначны; `на`, `секунд` —
по шаблону (секунды склоняются: «девяносто секунд» /
«тридцать секунд» — Vosk может выдать и «секунды» — принимать
`секунд|секунды`);
- лишние слова до/после шаблона — грамматическая ошибка
(узкая грамматика, Ф-1.3);
- число разобрано, но вне диапазона → `Rejected(reason=
"out_of_range", attempted={…значения…})`;
- шаблон не совпал → `Rejected(reason="out_of_grammar",
attempted=null)`.
## 5. Семантика для журнала (Ф-1.5, контракт m09 spec 04)
```json
{"cmd":"set","weight_kg":120.0,"reps":8}
{"cmd":"weight","weight_kg":62.5}
{"cmd":"rest","sec":90}
{"cmd":"rpe","rpe":7}
{"cmd":"rejected","reason":"out_of_range","weight_kg":600.0,"reps":8}
```
- `semantics` — объект (как в PayloadFactory.commandLog m09);
`rejected` сохраняет распознанные значения — разбор ошибок
при анализе журнала;
- исходный текст гипотезы пишется в `recognized_text` исполнителем
(спец 03).
## 6. Тесты (unit, pure JVM)
1. Четыре команды — числа словами: «подход сто двадцать на
восемь» → Approach(120,8); «вес шестьдесят два с половиной» →
Weight(62.5); «отдых девяносто секунд» → Rest(90); «рпэ семь» /
«эр пэ э семь» → Rpe(7).
2. Цифрами: «подход 120 на 8», «вес 62,5», «отдых 90», «120.5».
3. Границы: W=0 и W=500 приняты; W=500,5 / R=0 / R=101 / S=4 /
S=601 / N=0 / N=11 → out_of_range (attempted заполнен).
4. Отклонения: «погнали» → out_of_grammar; «подход сто двадцать»
(нет «на») → out_of_grammar; «отдых тридцать с половиной» →
out_of_grammar («с половиной» не у веса); «подход двадцать сто
на пять» → out_of_grammar.
5. NumberRu: крайние числа — ноль, сто, девятьсот девяносто
девять, «двести двадцать два»; регистр и пунктуация («Подход
120, на 8!»); формы «два»/«две».
6. Alternatives: первая гипотеза мусор, вторая валидна → команда
из второй; все мусор → Rejected из первой.