Для локального STT у нас настроены faster-whisper, модель base, русский язык и подсказка из рабочих терминов. Это подтверждает [секция stt: в config.yaml][CFG]. Реальное использование видно в agent.log.1: журнал содержит загрузку base, обработку голосовых файлов и успешное завершение с lang=ru. Распознавание завершилось, но текст мог содержать ошибки. Остальной диалог тоже не обязательно обрабатывается локально.
Артефактов достаточно, чтобы разобрать конфигурацию, путь выполнения и несколько временных интервалов. Их недостаточно для оценки точности, выигрыша от лексикона и потребления памяти самим STT. Настройки, события журнала и инженерные предположения различаем. Текущий исходный код объясняет реализацию, но мы не установили, совпадает ли он с версией исторического запуска.
Зачем локально
В [transcription_tools.py][SELECT] явно выбранный провайдер local направляет распознавание в локальную реализацию. Если она недоступна, код проверяет локальную команду и возможность установки зависимости, затем возвращает отсутствие провайдера. Эта ветка не переключает голос на облачный STT. Секция openai с whisper-1, присутствующая в [конфигурации][CFG], сама по себе не означает использование API: активный выбор — provider: local.
Чтобы преобразовать аудио в текст, запись не нужно отправлять отдельному сервису распознавания. Это и есть выигрыш приватности. В [журнале шлюза][GWCACHE] источник записей — Telegram. После первого успешного STT agent.log.1 показывает создание клиента облачного агента. Поэтому граница локальности проходит вокруг распознавания. Артефакты не подтверждают, что доставка голоса, хранение переписки и дальнейшая обработка текста происходят только на малине. Отдельного сетевого аудита тоже нет.
Локальный путь убирает из распознавания ожидание удалённого API и зависимость от его доступности. Это преимущество устройства системы, а не измеренный выигрыш скорости относительно облака: сопоставимого облачного прогона нет. При первом использовании [загрузчик][LOAD] получает модель. Для работы без сети нужны заранее установленные зависимости и сохранённые веса; наличие этой логики не доказывает проверенную работу стенда при отключённой сети.
Локальная транскрипция обходится без платного вызова STT API. В выбранной ветке платы за минуты нет. Вычисления занимают собственный процессор и память. Электричество, оборудование и обслуживание остаются расходами. Их денежная оценка в найденных STT-артефактах не зафиксирована.
Модель: что стоит и почему маленькая
В [config.yaml][CFG] указано именно base. В [transcription_tools.py][LOAD] модель создаётся через WhisperModel из faster_whisper; в [pyproject.toml][DEP] для голосовой зависимости закреплён faster-whisper==1.2.1. Проект объявляет эту версию. Отчёта о версиях пакетов исторического процесса нет.
Язык задан общей настройкой language: ru. [Функция разрешения языка][LANG] сначала проверяет язык конкретного провайдера, затем общий язык, затем переменную окружения. В показанной локальной секции отдельного языка нет, поэтому штатный путь получает ru. Журнал завершённых распознаваний также указывает русский. На этом пути выбор языка не оставлен автоматическому определению.
Почему выбрана base, мы не знаем: нет ни решения автора настройки, ни сравнительного испытания с large. Небольшая модель позволяет ограничить нагрузку на машину, которая одновременно обслуживает шлюз и другие задачи. В коде base — локальная модель по умолчанию, а комментарии к кэшированию прямо обсуждают занятую между голосовыми сообщениями память. Это объясняет компромисс, но не доказывает, что выбранная модель оптимальна для нашей речи.
Размер загрузки base в [комментарии исходника][RUN] оценён примерно в 150 МБ. Это оценка автора кода для скачивания, не замер файла на стенде и не расход RAM. Подменять ею потребление памяти нельзя: в памяти находятся также рантайм, рабочие данные и остальной процесс шлюза.
В локальной секции не заданы устройство и формат вычислений. [Вызов загрузчика][RUN] передаёт значения auto; при определённых ошибках библиотек CUDA предусмотрен переход на CPU с int8. Из этой запасной ветки нельзя заключить, что исторический запуск на малине точно использовал int8. Фактический тип вычислений, число рабочих потоков и характеристики платы в найденных записях распознавания не указаны.
Лексикон: подсказка вместо переобучения
В [config.yaml][LEX] записана такая строка:
Кодло, byte//run, Верстак, Оснастка, Сбруя, KEDR, тикет, лейн,
гейт, Hermes, Gitea, GitHub, CanvasTTY, Jev, промпт, малина.
Это названия, латинские написания и разговорные рабочие термины. [build_local_transcribe_kwargs][KWARGS] берёт строку из stt.local.initial_prompt и передаёт её в model.transcribe. Словарь поступает на вход распознавания. В этом механизме нет обучения весов, таблицы произношений или обязательного правила «такой звук всегда заменять таким словом».
Назначение подсказки обозначено в [исходнике][PROMPT] как помощь со словарём и контекстом. Модель получает желательные написания до декодирования. Для имён собственных одного звучания недостаточно: оно не определяет выбор кириллицы, латиницы или необычной пунктуации. Но список не устанавливает жёсткое ограничение на выходной текст.
Подтверждённого изменения качества у нас нет. Не найдена пара результатов на одной записи с включённым и выключенным initial_prompt, нет эталонного текста и перечня исправленных ошибок. Утверждение «после добавления KEDR имя стало распознаваться правильно» ничем не подтверждено. Мы установили, что подсказка передаётся. Улучшение распознавания конкретных названий ещё нужно проверить.
Лексикон нужно менять по наблюдаемым ошибкам. Для этого сохраняем исходную запись и вручную записываем ожидаемое название. Затем сравниваем варианты подсказки при одинаковых остальных настройках. Для byte//run надо отдельно оценивать узнавание самого имени и точность символов в написании. Такой эксперимент пока не проведён; наличие слова в конфигурации не заменяет его результат.
Стоимость: время видно, память не выделена
В agent.log.1 и его следующем фрагменте есть длительности аудио и отметки обработки. Интервалы ниже рассчитаны вычитанием временных отметок того же файла. Это наблюдения рабочего шлюза, не серия контролируемых замеров.
| Запись | Длительность аудио | Интервал по журналу | Границы и источник |
|---|---|---|---|
audio_bfb0f7a06099.ogg |
56,194 с | 27,191 с | Начало загрузки модели → завершение STT; agent.log.1, строки 1454 и 1464 |
| Та же запись | 56,194 с | 23,287 с | Processing audio → завершение; agent.log.1, строки 1455 и 1464 |
audio_79c8cb2b6810.ogg |
19,074 с | 8,886 с | Processing audio → завершение; agent.log.1, строки 1510 и 1512 |
Для первого запуска от сообщения о загрузке модели до Processing audio прошло 3,904 с: расчёт по agent.log.1, строкам 1454–1455. Этот промежуток нельзя назвать чистым временем скачивания или чтения весов. Во втором фрагменте повторной загрузки нет, что согласуется с [кэшем модели в исходнике][RUN]. В обоих случаях измеренный интервал STT короче исходной записи, но это не гарантия такого соотношения для другого звука или нагрузки.
Пользователь может ждать гораздо дольше, чем длится распознавание. Для второй записи между кэшированием файла и началом обработки прошло 140,073 с, до завершения — 148,959 с: расчёт по agent.log.1, строкам 1461, 1510 и 1512. В это время журнал содержит работу агента; точная причина ожидания не установлена. Весь этот интервал не показывает скорость Whisper. Время одного STT тоже не определяет срок ответа пользователю: остаётся очередь.
В том же журнале встречаются снимки RSS при обслуживании памяти шлюза. Они относятся ко всему процессу, который выполняет и другие операции. Прибавка памяти от STT и пик во время распознавания отдельно не измерены. Расход RAM распознавателем не зафиксирован. Нет и отдельных замеров загрузки CPU, энергопотребления и влияния температуры.
Между запросами модель тоже занимает память. [Код выгрузки][IDLE] допускает освобождение модели после простоя, но без настройки тайм-аута значение по умолчанию — 0, то есть автоматическая выгрузка отключена. В показанной [секции конфигурации][CFG] тайм-аут отсутствует. Сохранение модели сокращает повторную загрузку ценой занятой памяти; величина этой цены на стенде неизвестна.
Где ломается: что известно об отказах
В указанных местах записей для проверки ошибок не осталось: voice-memos/ отсутствует, .hermes/audio_cache/ пуст при осмотре. Найденный через журнал каталог .hermes/cache/audio/ тоже пуст. [gateway.log][CLEAN] фиксирует удаление устаревших аудиофайлов. При поиске по именам записей в каталоге сессий связанные транскрипты не найдены. Это ограничивает проверку, но не доказывает отсутствие копий во всей системе.
Имена, латиница и смешанная речь пока требуют испытаний. Ошибки распознавания в этих категориях у нас не установлены. Лексикон показывает, какие написания мы хотим сохранить; конфигурация показывает русский язык. Как фиксированный ru влияет на английские вставки, аббревиатуры и переключение языка, на этих артефактах оценить нельзя.
Для шума и тишины защитные механизмы видны непосредственно в [исходнике][KWARGS]: включён VAD, отключено использование текста предыдущего окна, затем отбрасываются сегменты, которые одновременно похожи на отсутствие речи и имеют низкую уверенность. В agent.log.1 VAD удалил 0,784 с из первой записи, а в следующем фрагменте — 0,208 с из второй. Это подтверждает работу фильтра, но не точность отбора речи.
[Тесты фильтрации][TEST] используют искусственные сегменты и подставную модель. Они описывают ожидаемую логику, включая сохранение тихой уверенно распознанной речи; ошибки на нашем микрофоне они не измеряют. Если фильтр удалил слова или шум превратился в текст, установить это можно лишь по сохранённому аудио и ручному эталону.
Успешный ответ STT с пустым текстом и ошибку распознавания [шлюз][GW] обрабатывает отдельно. Система предусматривает случай, когда слов не получено. Насколько часто оно возникает в эксплуатации, журнал этой выборки не сообщает.
Что дальше: большая модель и предел малины
Переход на модель побольше нужно проверить экспериментом. Он должен показать, оправдывает ли уменьшение ошибок на терминах, смешанной речи и трудных записях дополнительное ожидание и память. Сравнения base с large в найденных артефактах нет, поэтому обещать улучшение или утверждать невозможность запуска large на малине нельзя.
Сначала нужен набор одинаковых сохранённых записей и отдельное сравнение лексикона. Затем можно менять модель, сохраняя язык, подсказку и условия обработки. Иначе улучшение нельзя будет приписать размеру модели. Критерий пригодности — правильный текст при допустимом ожидании, без нарушения работы остальных задач шлюза.
Предел этой малины пока численно не определён. Для него нужны характеристики платы, свободная память, поведение при длительной нагрузке и одновременной работе сервисов. Наблюдаемые успешные запуски base подтверждают работоспособность выбранного пути в конкретных случаях. Они не задают запас ресурсов для более тяжёлой модели.
Что проверить скептику
— Прочитать stt: командой grep -n -A12 "^stt:" ~/.hermes/config.yaml и сверить провайдер, модель, язык и строку лексикона.
— Сопоставить конфигурацию с _get_provider, разрешением языка и передачей initial_prompt в [transcription_tools.py][KWARGS].
— Найти загрузку и завершения в agent.log.1, повторить расчёт интервалов и отделить их от ожидания в шлюзе.
— Проверить границу приватности: Telegram доставляет голос, локальный STT создаёт текст, дальнейший агент использует облачный клиент. Эти стадии видны в журнале.
— Потребовать аудио и эталон для любого утверждения об улучшении имён. Здесь таких доказательств нет.
Что нужно замерить, но не замерено
— Пик RSS/PSS и прибавку памяти от модели относительно того же процесса без STT; память после простоя и выгрузки. — Полное время вызова STT, отдельно загрузку и обработку, с повторениями при свободном и занятом шлюзе; задержку от получения сообщения до выдачи транскрипта. — Фактическое устройство, тип вычислений, версии рантайма и число потоков; характеристики платы, температуру, троттлинг и питание во время нагрузки. — Ошибки слов и названий по ручному эталону: с лексиконом и без него, на латинице, смешанной речи, тихих и шумных записях. — Сравнение моделей на одинаковом наборе аудио и проверку распознавания без сети после подготовки весов. Сетевую передачу текста последующему агенту нужно проверять отдельно.
[CFG]: ~/.hermes/config.yaml: 156 [LEX]: ~/.hermes/config.yaml: 161 [DEP]: ~/.hermes/hermes-agent/pyproject.toml: 213 [SELECT]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1015 [LANG]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 181 [LOAD]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1761 [RUN]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1931 [KWARGS]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1823 [PROMPT]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1988 [IDLE]: ~/.hermes/hermes-agent/tools/transcription_tools.py: 1677 [GW]: ~/.hermes/hermes-agent/gateway/run.py: 25927 [TEST]: ~/.hermes/hermes-agent/tests/tools/test_stt_silence_hallucinations.py: 1
[GWCACHE]: ~/.hermes/logs/gateway.log: 14117 [CLEAN]: ~/.hermes/logs/gateway.log: 14455
Написано по артефактам студии; ссылки на файлы — имена наших исходников, они живут в репозитории, а не в вебе.