ChatGPT на вашем ноутбуке: бесплатно, анонимно, навсегда
ChatGPT на вашем ноутбуке: бесплатно, анонимно, навсегда

Полная версия

Настройки чтения
Размер шрифта
Высота строк
Поля
На страницу:
9 из 9

· Воспроизведение: sounddevice берёт массив float32 и проигрывает его с указанной частотой. sd.wait() блокирует выполнение до окончания звука, чтобы наша программа не завершилась раньше времени.

Тестирование

python

# test_tts.py

from speech_synthesis import TextToSpeech

tts = TextToSpeech(speaker="xenia")

tts.speak("Привет! Я локальный голосовой помощник. Чем могу помочь?")

Запустите — вы должны услышать приятный женский голос.

Интеграция в проект

В будущем GUI мы будем вызывать tts.speak(response) сразу после получения ответа от модели. Асинхронное озвучивание (не ждать окончания, чтобы можно было задать следующий вопрос) можно реализовать с помощью потоков, но пока для простоты оставим синхронный вызов.

Выбор голоса под настроение

Функция set_speaker позволит переключать голос на лету:

python

def set_speaker(self, speaker: str):

"""Сменить голос."""

if speaker in self.model.speakers: # некоторые версии silero хранят список спикеров

self.speaker = speaker

else:

print(f"Голос {speaker} не найден, используется {self.speaker}")

Но в рамках нашей книги мы оставим один голос по умолчанию.

Возможные проблемы с синтезомречи

Ошибка «ModuleNotFoundError: No module named 'torch'». Библиотека Silero работает поверх PyTorch, который не был установлен. Выполните pip install torch — для синтеза речи достаточно CPU-версии, она легче и устанавливается быстрее. Если позже понадобится GPU-ускорение, можно будет доустановить CUDA-версию PyTorch отдельно.

Ошибка загрузки модели: «HTTPError: Not Found». При первом создании объекта TextToSpeech Silero пытается скачать модель из интернета. Проверьте соединение — модель скачивается один раз и кэшируется. Если интернет недоступен, скачайте файл ru_v3.pt вручную с сайта models.silero.ai и поместите его в папку кэша PyTorch Hub. После этого модель будет загружаться локально.

Звук воспроизводится с искажениями или задержкой. Скорее всего, не совпадает частота дискретизации. Silero по умолчанию использует 22050 Гц — проверьте, что sounddevice.play() вызывается именно с этим параметром. Если звук всё равно искажён, попробуйте явно указать samplerate=22050 в вызове sd.play(). Иногда проблема в драйверах звуковой карты — обновите их.

Русский текст не озвучивается — модель молчит. Silero обучена на кириллическом русском тексте и не понимает транслитерацию. Убедитесь, что текст содержит настоящие русские буквы, а не латинские замены вроде «privet». Также проверьте, что выбран русский голос — например, xenia или aidar. Голоса для других языков не смогут произнести кириллицу.

Ошибка «PortAudioError» при воспроизведении. Звуковое устройство занято другим приложением или отсутствует. Проверьте, что колонки или наушники подключены и не используются эксклюзивно другой программой. На Linux может помочь запуск звукового сервера командой pulseaudio --start. Если ошибка повторяется, попробуйте перезагрузить компьютер — иногда звуковой драйвер «зависает» и его нужно переинициализировать.

Альтернативный путь: piper-tts (если нужно максимально легковесно)

Если вы по каким-то причинам не хотите тянуть PyTorch, вот минимальный пример с piper-tts:

bash

# Установка piper-tts

pip install piper-tts

# Скачать модель ru_RU-ruslan-medium с GitHub в папку models/

python

from piper import PiperVoice

import sounddevice as sd

import wave

voice = PiperVoice.load("models/ru_RU-ruslan-medium.onnx")

audio = voice.synthesize("Привет, мир!")

# audio — это bytes с WAV, нужно декодировать и воспроизвести

Но мы в книге придерживаемся Silero: для нашего проекта качество речи важнее лишних 200 МБ зависимостей, которые всё равно перекрываются объёмом языковой модели.

Что дальше

Теперь у нас есть и «уши» (распознавание речи), и «голос» (синтез). В следующей главе 5.3 «Интеграция в голосового ассистента» мы объединим эти два компонента с нашим LocalModel и создадим полноценный диалоговый интерфейс, где можно нажать кнопку, задать вопрос голосом и услышать ответ — и всё это офлайн.

Глава 5.3. Интеграцияв голосового ассистента.

Мы добрались до момента, когда наш ИИ-помощник обретает способность к полноценному разговору. В главе 5.1 мы дали ему «уши» — локальное распознавание речи на основе Whisper. В главе 5.2 — «голос» с помощью Silero TTS. Теперь мы свяжем эти два компонента с нашим классом LocalModel и создадим голосового ассистента, который слушает вопрос, думает и отвечает вслух. И всё это — без интернета, по нажатию одной кнопки (или по ключевому слову).

Архитектура голосового ассистента

Наш голосовой ассистент — это Python-класс VoiceAssistant, который объединяет три компонента:

1. SpeechRecognizer — распознаёт речь с микрофона и возвращает текст.

2. LocalModel — получает текст, генерирует ответ (может использовать режим RAG, если передан VectorStore).

3. TextToSpeech — принимает текст ответа и озвучивает его.

Все три компонента уже написаны и протестированы по отдельности. Нам осталось лишь написать «дирижёра», который будет координировать их работу.

Код класса VoiceAssistant

Создайте файл voice_assistant.py:

python

# voice_assistant.py

import sys

from speech_recognition import SpeechRecognizer

from speech_synthesis import TextToSpeech

from local_model import LocalModel

from vector_store import VectorStore

from embedder import Embedder

class VoiceAssistant:

"""

Голосовой ассистент, объединяющий распознавание речи,

языковую модель и синтез речи.

"""

def __init__(

self,

model: LocalModel,

whisper_size: str = "tiny",

tts_speaker: str = "xenia",

store: VectorStore = None,

embedder: Embedder = None,

use_documents: bool = False

):

"""

:param model: экземпляр LocalModel

:param whisper_size: размер модели Whisper ("tiny" / "base")

:param tts_speaker: голос Silero TTS

:param store: VectorStore (если используется RAG)

:param embedder: Embedder (если используется RAG)

:param use_documents: режим "по документам" по умолчанию

"""

self.model = model

self.store = store

self.embedder = embedder

self.use_documents = use_documents

print("Инициализация распознавания речи...")

self.recognizer = SpeechRecognizer(model_size=whisper_size)

print("Инициализация синтеза речи...")

self.tts = TextToSpeech(speaker=tts_speaker)

print("Голосовой ассистент готов.\n")

def listen(self) -> str:

"""Слушает микрофон и возвращает распознанный текст."""

text = self.recognizer.recognize_speech()

if text:

print(f"Вы: {text}")

return text

def respond(self, text: str):

"""Генерирует ответ и озвучивает его."""

print("Думаю...", end="", flush=True)

# Определяем, какой метод модели использовать

if self.use_documents and self.store and self.embedder:

response = self.model.ask_documents(

question=text,

store=self.store,

embedder=self.embedder

)

else:

response = self.model.chat(

messages=[{"role": "user", "content": text}],

temperature=0.7

)

print(f"\rАссистент: {response}")

# Озвучиваем ответ

self.tts.speak(response)

def run(self, wake_word: str = None):

"""

Основной цикл. Если wake_word задан, ассистент ждёт ключевое слово.

Иначе — слушает сразу после нажатия Enter.

"""

print("=" * 50)

print("Голосовой ассистент запущен.")

print("Нажмите Ctrl+C для выхода.\n")

if wake_word:

print(f"Ассистент ждёт ключевое слово: \"{wake_word}\"...")

else:

print("Нажмите Enter, чтобы начать говорить...")

try:

while True:

if wake_word:

# Ждём ключевое слово в цикле

text = self.listen()

if text and wake_word.lower() in text.lower():

print(f"Ключевое слово обнаружено!")

# Убираем ключевое слово из запроса

query = text.lower().replace(wake_word.lower(), "").strip()

if query:

self.respond(query)

else:

# Если только ключевое слово — спросить, что нужно

self.tts.speak("Я слушаю.")

else:

# Ручной режим: ждём Enter

input()

text = self.listen()

if text:

# Простые голосовые команды управления

if text.lower() in ["выход", "пока", "завершить"]:

self.tts.speak("До свидания!")

break

elif text.lower() in ["режим документы"]:

self.use_documents = not self.use_documents

status = "включён" if self.use_documents else "выключен"

self.tts.speak(f"Режим документы {status}")

continue

else:

self.respond(text)

except KeyboardInterrupt:

print("\nЗавершение работы...")

self.tts.speak("Работа завершена.")

Пояснения к коду:

· Конструктор принимает все необходимые компоненты. Параметры store и embedder необязательны: если вы не планируете использовать RAG, просто не передавайте их. Флаг use_documents позволяет переключать режим на лету.

· Метод listen() — обёртка над SpeechRecognizer.recognize_speech(), добавляющая вывод распознанного текста.

· Метод respond() — в зависимости от флага выбирает либо обычный чат, либо ask_documents(). Затем выводит ответ на экран и озвучивает его. Обратите внимание на трюк с \r и end="": сначала печатается «Думаю...», а когда ответ готов, курсор возвращается в начало строки и заменяет это слово на «Ассистент:».

· Метод run() — основной цикл. Поддерживает два режима:

o Ручной (по Enter): для отладки и использования за компьютером, когда вы готовы нажать клавишу перед тем, как говорить.

o С ключевым словом: ассистент постоянно слушает микрофон и реагирует только когда услышит заданное слово (например, «ассистент»). Это приближает нас к поведению «умной колонки». Однако постоянное распознавание речи требует больше ресурсов, поэтому по умолчанию режим ручной.

Внутри цикла реализованы простые голосовые команды:

· «выход», «пока», «завершить» — завершают работу.

· «режим документы» — переключает между обычным чатом и режимом RAG.

Тестирование голосового ассистента

Создайте скрипт test_voice_assistant.py:

python

# test_voice_assistant.py

from model_loader import download_model

from local_model import LocalModel

from voice_assistant import VoiceAssistant

from vector_store import VectorStore

from embedder import Embedder

# Загружаем языковую модель

print("Загрузка языковой модели...")

model_path = download_model()

llm = LocalModel(model_path=model_path, n_threads=4)

# Если у вас проиндексированы документы, подключим их

store = VectorStore(collection_name="my_docs")

emb = Embedder()

use_docs = True # поставьте False, если документов нет

# Создаём ассистента

assistant = VoiceAssistant(

model=llm,

whisper_size="tiny",

tts_speaker="xenia",

store=store,

embedder=emb,

use_documents=use_docs

)

# Запускаем

assistant.run() # ручной режим (по Enter)

Запустите:

bash

python test_voice_assistant.py

Пример сеанса (ручной режим):

text

Инициализация распознавания речи...

Модель готова.

Инициализация синтеза речи...

Модель готова.

Голосовой ассистент готов.

==================================================

Голосовой ассистент запущен.

Нажмите Ctrl+C для выхода.

Нажмите Enter, чтобы начать говорить...

[Enter]

Слушаю... (говорите)

Тишина, завершаю запись.

Вы: какой сегодня день недели

Думаю...

Ассистент: К сожалению, я не имею доступа к текущей дате.

(озвучивается голосом)

[Enter]

Слушаю... (говорите)

Вы: расскажи анекдот про программистов

Ассистент: Почему программисты путают Хэллоуин и Рождество? Потому что 31 OCT = 25 DEC!

(озвучивается)

Режим с ключевым словом

Если вы хотите, чтобы ассистент слушал постоянно и откликался на ключевое слово, замените вызов:

python

assistant.run(wake_word="ассистент")

Теперь ассистент будет непрерывно слушать микрофон и реагировать, когда вы скажете «ассистент, какой сегодня день?». Учтите, что постоянный мониторинг увеличивает нагрузку на процессор. Для реального использования можно добавить «засыпание» после периода бездействия, но для нашего проекта ручной режим более практичен.

Потоковое озвучивание (опционально)

Сейчас ассистент ждёт полного ответа модели, прежде чем начать озвучивание. Это может создавать заметную паузу, особенно если модель генерирует длинный ответ. Чтобы ответ начал озвучиваться по мере генерации, можно объединить потоковый вывод модели с озвучиванием. Для этого потребуется:

· Разбивать ответ на предложения (по точке, вопросительному или восклицательному знаку).

· Озвучивать каждое предложение, как только оно сформировано.

Это более сложная техника, и мы оставим её для самостоятельного изучения. В рамках книги синхронный respond() полностью выполняет задачу.

Возможные проблемы с голосовымассистентом

Микрофон не реагирует или слышен постоянный шум. Порог чувствительности, заданный в SILENCE_THRESHOLD, не подходит для вашего помещения. Если микрофон вообще не реагирует на голос — уменьшите порог до 0.005 или даже 0.003. Если, наоборот, запись никогда не останавливается из-за фонового шума — увеличьте порог до 0.02. Универсальное решение — использовать гарнитуру: она даёт чистый сигнал, и настройки по умолчанию работают хорошо.

Whisper не распознаёт тихую речь. Проверьте уровень громкости микрофона в системных настройках — возможно, он убавлен до минимума. Поднимите усиление микрофона (Mic Boost), если такая опция есть. Если речь всё равно распознаётся плохо, замените модель tiny на base — она чувствительнее к тихим звукам и лучше понимает нечёткую дикцию, хотя работает чуть медленнее.

Ответ модели слишком медленный для озвучки. Вы задали вопрос, проходит несколько секунд, и только потом ассистент начинает говорить — это разрушает естественность диалога. Сократите max_tokens в методе respond() до 150–200 — короткие ответы генерируются быстрее. Используйте GPU с параметром n_gpu_layers=-1, чтобы модель работала на видеокарте. Выберите более быстрое квантование — Q4_K_S работает шустрее, чем Q4_K_M, почти без потери качества.

Silero произносит английские слова с сильным акцентом. Модель Silero оптимизирована для чистого русского языка. Если ответ содержит английские термины или имена, они будут звучать с русским акцентом. Это ожидаемое поведение. Для смешанных текстов можно написать функцию, которая временно заменяет английские слова русской транскрипцией перед озвучкой. В большинстве случаев проблема решается сама собой — ассистент отвечает на том же языке, на котором его спросили.

Конец ознакомительного фрагмента.

Текст предоставлен ООО «Литрес».

Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.

Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.

Конец ознакомительного фрагмента
Купить и скачать всю книгу
На страницу:
9 из 9