РУКОВОДСТВО

Озвучка текста для Ollama: пусть локальная модель говорит, полностью офлайн

Ollama запускает языковые модели на вашем компьютере, и её чат-API отвечает текстом. Чтобы слышать ответы, нужен шаг озвучки, и если вы выбрали Ollama ради приватности, этот шаг тоже должен работать локально.

В этом руководстве мы добавим его с бесплатным движком SuperClonica: короткий скрипт на Python отправляет вопрос в Ollama, читает ответ по мере генерации и озвучивает каждое предложение, как только оно готово, поэтому голос звучит задолго до того, как модель допишет ответ. Ничего не уходит с вашего компьютера.

Установка

Установите Ollama, скачайте модель и запустите её, затем поставьте два пакета Python:

ollama pull llama3.2

pip install requests superclonica-cpu

При первом запуске движок скачивает модель Supertonic 3, около 400 МБ; дальше он работает офлайн на обычном процессоре, а видеокарта NVIDIA лишь ускоряет его. В Linux скрипт играет звук через aplay из пакета alsa-utils.

Скрипт

Сохраните его как ollama_voice.py и запустите. Напишите вопрос: ответ печатается и звучит одновременно, а разговор сохраняет историю:

import json, os, re, subprocess, sys, tempfile
import requests
from superclonica import Superclonica

OLLAMA = "http://localhost:11434/api/chat"
MODEL = "llama3.2"  # any model you pulled with ollama pull
VOICE = "F1"  # M1..M5, F1..F5 or a voice file from the catalog
SENTENCE_END = re.compile(r"[.!?]\s+")
tts = Superclonica()

def play(path):
    if sys.platform == "win32":
        import winsound
        winsound.PlaySound(path, winsound.SND_FILENAME)
    elif sys.platform == "darwin":
        subprocess.run(["afplay", path], check=True)
    else:
        subprocess.run(["aplay", "-q", path], check=True)

def say(sentence):
    sentence = re.sub(r"[*#`_]", "", sentence).strip()
    if sentence:
        audio, _ = tts.synthesize(sentence, voice=VOICE, lang="en")
        path = os.path.join(tempfile.gettempdir(), "ollama-say.wav")
        tts.save(path, audio)
        play(path)

history = []
while True:
    history.append({"role": "user", "content": input("You: ")})
    answer = buffer = ""
    body = {"model": MODEL, "messages": history, "stream": True}
    with requests.post(OLLAMA, json=body, stream=True, timeout=300) as response:
        response.raise_for_status()
        for line in response.iter_lines():
            if not line:
                continue
            piece = json.loads(line).get("message", {}).get("content", "")
            print(piece, end="", flush=True)
            answer += piece
            buffer += piece
            while match := SENTENCE_END.search(buffer):
                say(buffer[:match.end()])
                buffer = buffer[match.end():]
    say(buffer)
    print()
    history.append({"role": "assistant", "content": answer})

Скрипт режет поток в конце каждого предложения и озвучивает его, пока Ollama пишет дальше. Он убирает символы Markdown вроде звёздочек: модели любят их ставить, а вслух они звучат странно.

Чтобы звучало правильно

Просите модель говорить, а не писать: начните историю с системного сообщения вроде «Отвечай короткими разговорными предложениями, без списков, таблиц и форматирования». В say укажите lang — язык разговора, например "ru"; движок говорит на 31 языке, но пока не по-китайски. VOICE — один из десяти встроенных голосов, M1–M5 и F1–F5, или путь к файлу голоса из каталога SuperClonica, например голос ассистента в духе киношного ИИ.

Разговор голосом

Чтобы говорить вместо того, чтобы печатать, поставьте перед скриптом локальное распознавание речи, например Whisper через пакет faster-whisper, и передавайте его текст туда, где скрипт вызывает input. Тогда весь ассистент — уши, мозг и голос — работает на вашей машине.