Проблема: Почему облачные ассистенты нас не устраивают?
Сегодня каждый может попросить ChatGPT о чём угодно через браузер или мобильное приложение. Но что, если вы хотите поговорить с ИИ, как с настоящим собеседником — голосом, без задержек, и чтобы ваши разговоры оставались приватными? Облачные решения вроде Alexa или Google Assistant отправляют ваши запросы на серверы компаний, где они анализируются и хранятся.
Ключевые проблемы облачных ассистентов: задержки из-за сети, зависимость от интернета, вопросы приватности данных, невозможность тонкой настройки под свои нужды и, что немаловажно — ежемесячные платежи за API-вызовы к мощным моделям.
Решение — собрать своего ассистента локально. И видеокарта RTX 3090 с её 24 ГБ памяти — идеальный кандидат для этой задачи. Она позволяет запустить всю цепочку: от распознавания вашей речи до генерации осмысленного ответа и его озвучивания — полностью на вашем компьютере.
Решение: Архитектура локального голосового ассистента
Наша система будет состоять из трёх ключевых компонентов, работающих последовательно:
- STT (Speech-to-Text): Модель распознавания речи, которая преобразует ваш голос в текст. Мы будем использовать Parakeet от NVIDIA — одну из лучших open-source моделей.
- LLM (Large Language Model): Языковая модель, которая понимает запрос и генерирует текстовый ответ. Выбор зависит от ваших задач: от быстрых 7B-моделей до более умных 20B вариантов.
- TTS (Text-to-Speech): Модель синтеза речи, которая превращает текстовый ответ ИИ в естественную речь. Kokoro — современная модель с отличным качеством и поддержкой русского языка.
| Компонент | Модель | Примерный объём VRAM | Особенности |
|---|---|---|---|
| STT | Parakeet RNNT 1.1B | 2-3 ГБ | Высокая точность, низкая задержка |
| LLM | Qwen2.5-7B-Instruct (Q4_K_M) | 5-6 ГБ | Хороший баланс скорости и качества |
| TTS | Kokoro 82M | 1-2 ГБ | Естественное звучание, мультиязычность |
| Итого | — | 8-11 ГБ | Остаётся запас для буферов |
Как видите, даже с запасом мы укладываемся в 24 ГБ памяти RTX 3090. Это даёт нам пространство для манёвра: можно выбрать более крупную LLM или оставить память для параллельных задач. Если же вы хотите пойти дальше и использовать две карты, изучите наш гайд по настройке Dual RTX 3090 с NVLink.
Пошаговый план сборки
1 Подготовка системы и установка базовых компонентов
Перед началом убедитесь, что у вас установлены свежие драйверы NVIDIA (версия 535 или выше) и CUDA Toolkit 12.1+. Мы будем использовать Python 3.10+ и виртуальное окружение для изоляции зависимостей.
# Создаём и активируем виртуальное окружение
python -m venv voice_assistant_env
source voice_assistant_env/bin/activate # Для Windows: voice_assistant_env\Scripts\activate
# Устанавливаем базовые библиотеки
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install transformers>=4.36.0 accelerate>=0.25.0
pip install sounddevice pyaudio wave # Для работы с аудио
pip install llama-cpp-python --force-reinstall --upgrade --no-cache-dir \
--verbose \
-f https://abetlen.github.io/llama-cpp-python/whl/cu121/
llama-cpp-python с поддержкой CUDA критически важно для производительности. Эта сборка позволяет загружать GGUF-модели прямо в VRAM видеокарты, что значительно ускоряет генерацию ответов по сравнению с CPU.2 Установка и настройка STT (Parakeet)
Parakeet — семейство моделей распознавания речи от NVIDIA, обученных на огромных датасетах. Мы будем использовать относительно компактную версию, которая отлично работает на GPU.
# Установка дополнительных зависимостей для STT
pip install nemo_toolkit['asr']
# Пример кода для распознавания речи с микрофона
import sounddevice as sd
import numpy as np
import torch
import nemo.collections.asr as nemo_asr
# Загружаем модель Parakeet
parakeet_model = nemo_asr.models.ASRModel.from_pretrained("nvidia/parakeet-rnnt-1.1b")
parakeet_model = parakeet_model.to('cuda')
parakeet_model.eval()
def record_audio(duration=5, sample_rate=16000):
"""Запись аудио с микрофона"""
print("Говорите сейчас...")
audio = sd.rec(int(duration * sample_rate),
samplerate=sample_rate,
channels=1,
dtype='float32')
sd.wait()
return audio.flatten()
def speech_to_text(audio_array, sample_rate=16000):
"""Преобразование аудио в текст"""
# Подготовка аудио для модели
audio_tensor = torch.tensor(audio_array).unsqueeze(0).to('cuda')
# Распознавание
with torch.no_grad():
transcription = parakeet_model.transcribe(
audio_tensor,
batch_size=1,
return_hypotheses=False
)[0]
return transcription
# Использование
# audio = record_audio(duration=7)
# text = speech_to_text(audio)
# print(f"Вы сказали: {text}")
3 Выбор и загрузка LLM модели
Это сердце нашего ассистента. Выбор модели зависит от ваших потребностей: скорость ответа vs. качество рассуждений. Для RTX 3090 я рекомендую квантованные GGUF-модели размером 7B-20B параметров.
| Модель | Размер (GGUF) | Качество диалога | Скорость (токенов/с) | VRAM |
|---|---|---|---|---|
| Qwen2.5-7B-Instruct Q4_K_M | 4.5 GB | Отличное | 25-40 | ~5 GB |
| Llama-3.2-3B-Instruct Q4_K_M | 2.1 GB | Хорошее | 60-100 | ~3 GB |
| Qwen2.5-14B-Instruct Q4_K_M | 8.2 GB | Превосходное | 15-25 | ~9 GB |
# Загрузка LLM через llama.cpp
from llama_cpp import Llama
# Инициализация модели (скачайте GGUF файл заранее)
llm = Llama(
model_path="./models/qwen2.5-7b-instruct-q4_k_m.gguf",
n_gpu_layers=-1, # Загрузить все слои на GPU
n_ctx=4096, # Контекстное окно
n_batch=512, # Размер батча для GPU
verbose=False
)
def generate_response(prompt, max_tokens=256):
"""Генерация ответа на промпт"""
# Форматируем промпт в соответствии с шаблоном модели
formatted_prompt = f"<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n"
# Генерация
output = llm(
formatted_prompt,
max_tokens=max_tokens,
temperature=0.7,
top_p=0.9,
stop=["<|im_end|>"]
)
return output['choices'][0]['text'].strip()
# Пример использования
# response = generate_response("Объясни, что такое квантовая запутанность простыми словами")
Если вы новичок в теме квантования и форматов GGUF, рекомендую нашу статью «Что такое квантизация GGUF?», где мы подробно разбираем форматы и их влияние на качество.
4 Настройка TTS с Kokoro
Kokoro — современная модель синтеза речи от Facebook, которая поддерживает множество языков (включая русский) и позволяет управлять интонацией. Модель относительно компактна и хорошо работает на GPU.
# Установка TTS библиотеки
pip install TTS
import torch
from TTS.api import TTS
# Инициализация модели Kokoro
# Модель автоматически скачается при первом запуске
tts = TTS(model_name="tts_models/multilingual/multi-dataset/kokoro",
progress_bar=False,
gpu=True)
def text_to_speech(text, output_path="output.wav", language="ru"):
"""Преобразование текста в речь"""
# Генерация аудио
tts.tts_to_file(
text=text,
file_path=output_path,
speaker=tts.speakers[0], # Первый доступный голос
language=language
)
print(f"Аудио сохранено в {output_path}")
# Воспроизведение аудио (дополнительно)
import simpleaudio as sa
def play_audio(file_path):
wave_obj = sa.WaveObject.from_wave_file(file_path)
play_obj = wave_obj.play()
play_obj.wait_done()
# Пример использования
# text_to_speech("Привет! Я ваш локальный голосовой помощник.", "greeting.wav")
# play_audio("greeting.wav")
5 Сборка всего пайплайна в единое приложение
Теперь объединим все компоненты в единый цикл «слушай-думай-отвечай». Мы создадим простой скрипт, который будет работать в бесконечном цикле, ожидая голосовые команды.
import time
import threading
from queue import Queue
class VoiceAssistant:
def __init__(self):
print("Инициализация компонентов...")
# Инициализация STT
self.stt_model = self.load_stt_model()
# Инициализация LLM
self.llm = self.load_llm()
# Инициализация TTS
self.tts = self.load_tts()
self.is_listening = False
self.audio_queue = Queue()
print("Ассистент готов к работе!")
def load_stt_model(self):
"""Загрузка модели STT"""
import nemo.collections.asr as nemo_asr
model = nemo_asr.models.ASRModel.from_pretrained("nvidia/parakeet-rnnt-1.1b")
model = model.to('cuda')
model.eval()
return model
def load_llm(self):
"""Загрузка LLM модели"""
from llama_cpp import Llama
return Llama(
model_path="./models/qwen2.5-7b-instruct-q4_k_m.gguf",
n_gpu_layers=-1,
n_ctx=4096,
n_batch=512,
verbose=False
)
def load_tts(self):
"""Загрузка TTS модели"""
from TTS.api import TTS
return TTS(model_name="tts_models/multilingual/multi-dataset/kokoro",
gpu=True,
progress_bar=False)
def listen_loop(self):
"""Фоновая задача для прослушивания микрофона"""
import sounddevice as sd
import numpy as np
sample_rate = 16000
silence_threshold = 0.01
silence_duration = 1.0 # секунды тишины для конца фразы
audio_buffer = []
silent_frames = 0
def audio_callback(indata, frames, time, status):
nonlocal silent_frames, audio_buffer
volume_norm = np.linalg.norm(indata) * 10
if volume_norm > silence_threshold:
# Есть речь
audio_buffer.append(indata.copy())
silent_frames = 0
else:
# Тишина
silent_frames += frames
if silent_frames > sample_rate * silence_duration and len(audio_buffer) > 0:
# Завершили фразу
full_audio = np.concatenate(audio_buffer, axis=0)
self.audio_queue.put(full_audio.flatten())
audio_buffer = []
silent_frames = 0
# Начинаем прослушивание
with sd.InputStream(callback=audio_callback,
channels=1,
samplerate=sample_rate,
blocksize=1024):
print("Слушаю... Произнесите команду.")
while self.is_listening:
sd.sleep(100)
def process_audio(self, audio_array):
"""Обработка аудио: STT -> LLM -> TTS"""
# 1. Распознавание речи
print("Распознаю речь...")
audio_tensor = torch.tensor(audio_array).unsqueeze(0).to('cuda')
with torch.no_grad():
user_text = self.stt_model.transcribe(audio_tensor)[0]
print(f"Вы: {user_text}")
# 2. Генерация ответа
print("Думаю над ответом...")
prompt = f"Ты полезный голосовой помощник. Отвечай кратко и по делу. Вопрос: {user_text}"
response = self.generate_response(prompt)
print(f"Ассистент: {response}")
# 3. Синтез речи
print("Озвучиваю ответ...")
temp_file = f"response_{int(time.time())}.wav"
self.tts.tts_to_file(text=response,
file_path=temp_file,
language="ru")
# 4. Воспроизведение
self.play_audio(temp_file)
return response
def generate_response(self, prompt):
"""Генерация ответа через LLM"""
formatted_prompt = f"<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n"
output = self.llm(formatted_prompt,
max_tokens=150,
temperature=0.7,
stop=["<|im_end|>"])
return output['choices'][0]['text'].strip()
def play_audio(self, file_path):
"""Воспроизведение аудио файла"""
import simpleaudio as sa
wave_obj = sa.WaveObject.from_wave_file(file_path)
play_obj = wave_obj.play()
play_obj.wait_done()
def run(self):
"""Запуск основного цикла ассистента"""
self.is_listening = True
# Запускаем поток прослушивания
listen_thread = threading.Thread(target=self.listen_loop)
listen_thread.daemon = True
listen_thread.start()
try:
while True:
# Обрабатываем аудио из очереди
if not self.audio_queue.empty():
audio_data = self.audio_queue.get()
self.process_audio(audio_data)
time.sleep(0.1)
except KeyboardInterrupt:
print("\nЗавершение работы...")
self.is_listening = False
listen_thread.join()
# Запуск ассистента
if __name__ == "__main__":
assistant = VoiceAssistant()
assistant.run()
Нюансы реализации и частые ошибки
Даже с готовым кодом вы можете столкнуться с проблемами. Вот самые распространённые из них и способы их решения.
Ошибка 1: Нехватка видеопамяти при одновременной загрузке всех моделей
Решение: Используйте контекстные менеджеры для загрузки моделей только когда они нужны, или реализуйте механизм выгрузки/загрузки моделей по требованию. Для более крупных LLM рассмотрите стратегии масштабирования, включая распределение по нескольким GPU.
Ошибка 2: Большая задержка между вопросом и ответом (более 5-10 секунд)
Решение:
- Используйте более легковесную LLM (например, 3B вместо 7B)
- Уменьшите параметр
max_tokensдля генерации более коротких ответов - Проверьте, что все модели загружены на GPU (используйте
nvidia-smiдля мониторинга) - Рассмотрите использование специализированных NPU для определённых задач, если они есть в вашей системе
Ошибка 3: Плохое качество распознавания речи в шумной обстановке
Решение:
- Используйте направленный микрофон
- Добавьте простой шумоподавитель перед передачей аудио в STT модель
- Обучите или дообучите STT модель на своих аудиоданных (потребует времени и данных)
Ошибка 4: ИИ "галлюцинирует" или даёт неточные ответы
Решение:
- Используйте RAG (Retrieval-Augmented Generation) для предоставления модели актуальных данных
- Реализуйте систему вызова функций (function calling) для точных операций (погода, калькулятор и т.д.)
- Понизьте температуру (
temperature) до 0.3-0.5 для более детерминированных ответов - Изучите практический гайд по избеганию ошибок при локальном запуске LLM
Дальнейшее развитие проекта
Ваш базовый голосовой ассистент готов. Но на этом возможности не заканчиваются. Вот что можно добавить для создания по-настоящему мощного помощника:
- Вызов функций (Function Calling): Научите ассистента выполнять конкретные действия — включать музыку, искать в интернете, управлять умным домом.
- Контекстная память: Реализуйте хранение истории диалога, чтобы ассистент помнил, о чём вы говорили 10 минут назад.
- Мультимодальность: Добавьте возможность обрабатывать не только голос, но и изображения с веб-камеры. Для вдохновения изучите подходы к мультимодальному RAG.
- Голосовая идентификация: Настройте распознавание разных пользователей по голосу для персонализированных ответов.
- Офлайн-знания: Интегрируйте локальную базу знаний (например, с помощью ChromaDB) для быстрого доступа к вашим документам.
Заключение
Собрать полноценного голосового ассистента на одной видеокарте RTX 3090 не только возможно, но и практично. Вы получаете приватного помощника, который работает без интернета, не отправляет ваши данные на сторонние серверы и может быть настроен под ваши конкретные нужды.
Ключевое преимущество такого подхода — полный контроль над системой. Вы можете менять модели, настраивать параметры, добавлять функциональность без ограничений со стороны облачных провайдеров. И хотя начальная настройка требует времени, в долгосрочной перспективе вы получаете гибкий инструмент, который будет развиваться вместе с вашими потребностями.
Начните с базовой версии из этого гайда, а затем экспериментируйте — пробуйте разные LLM, улучшайте качество распознавания, добавляйте новые возможности. Мир локального ИИ стремительно развивается, и ваш RTX 3090 — отличный полигон для экспериментов.