Признайся: ты тоже покупаешь книги в PDF, а потом месяцами не открываешь? Или скачиваешь мануалы по дронам, чтоб однажды их прочитать, но в итоге — пыль в папке «Будущая мудрость». Лучший способ переварить контент — аудиоформат. Но платные сервисы вроде ElevenLabs давят на кошелёк и шлют данные на сервера. А если ты параноик (или просто инженер), то тебе нужен полностью локальный пайплайн. Под капотом: Kokoro 82M — самая быстрая TTS-модель, Qwen — мозг для чистки текста, и llama.cpp — скелет, на котором всё держится.
Проблема: PDF — это не книга, а свалка байтов
Ты открываешь PDF, выделяешь текст — и получаешь кашу из переносов строк, таблиц, сносок и «мусорных» символов. Обычное копирование не спасает: колонтитулы, номера страниц, сломанные юникод-символы. Дальше — TTS спотыкается. Дебил «читает» «на с. 42» как «на эс сорок два», а сноски «¹» превращает в иероглиф. Итог: слушать невозможно.
Решение — класть PDF в LLM, которая умеет выделять чистый текст, а уже потом кормить TTS. Но если ты возьмёшь GPT-4 за доллар за книгу — ты проиграл. Мы идём локально.
Почему именно Kokoro 82M + Qwen + llama.cpp?
На рынке сотни TTS-моделей. Coqui TTS — устарел, Piper — хорош, но не для длинных текстов, WhisperSpeech — слишком тяжел. Kokoro 82M (на самом деле 82 миллиона параметров — кроха) выдает голос на уровне коммерческих решений за секунды, даже на CPU. Запускается через ONNX Runtime — никаких GPU-монстров. На русском звучит как живой диктор, а не робот из 90-х.
Qwen (последняя версия на 2026 — Qwen3.5-7B-Instruct) через llama.cpp даёт контекст 32K токенов — хватает на главу. Мы не гоним PDF целиком, мы режем на абзацы, чистим промптами и склеиваем обратно. Llama.cpp — зверь для инференса: quantized до 4-bit, работает на любой видеокарте или даже на чистом CPU.
Уже чувствуешь запах свободы? Вот рецепт.
Пошаговый план: от PDF до mp3
1 Подготовка окружения: ставим адский движок
Забудь про Docker с миллионом слоёв. Ставим всё через pip и apt.
# Python 3.11+, venv обязателен
python3 -m venv audiobook-env
source audiobook-env/bin/activate
# Утилиты
sudo apt install espeak-ng libsndfile1-dev pipx
pipx install llama.cpp # ставим последнюю на 29.04.2026
# Kokoro TTS — стабильная версия 1.2.0 с ONNX
pip install kokoro onnxruntime soundfile
# Qwen через llama.cpp: скачай model Qwen3.5-7B-Instruct-Q4_K_M.gguf
get https://huggingface.co/Qwen/Qwen3.5-7B-Instruct-GGUF/resolve/main/qwen3.5-7b-instruct-q4_k_m.gguf -O ./models/qwen.gguf
Не пугайся размера — 4.2 ГБ. Это меньше, чем Fortnite.
2 Извлечение сырого текста из PDF
Здесь многие бегут за Tesseract OCR — зря. Для машинописных PDF (а не сканов) используем PyMuPDF (fitz). Быстро и без костылей.
import fitz
doc = fitz.open("book.pdf")
text = ""
for page in doc:
text += page.get_text()
Проблема: в text летят колонтитулы, номера страниц, странные пробелы. Для теста — нормально, для чистого TTS — нет. Поэтому дальше — Qwen.
3 Очистка текста через Qwen + llama.cpp
Заводим сервер llama.cpp REST API:
llama-server -m ./models/qwen.gguf -c 32768 -ngl 33 --host 127.0.0.1 --port 8082
Теперь общаемся с Qwen через простой промпт. Режем текст на чанки по 2000 токенов (чтобы влезало в контекст с запасом).
import requests, json, re
LLM_URL = "http://127.0.0.1:8082/v1/chat/completions"
def clean_chunk(chunk):
prompt = f"""Удали из текста всё лишнее: номера страниц, колонтитулы, сноски-цифры, символы формата {page 10}.
Сохрани смысл, абзацы, переносы строк. Не добавляй ничего. Текст:
{chunk}"""
resp = requests.post(LLM_URL, json={
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.0,
"max_tokens": 4000
})
return resp.json()["choices"][0]["message"]["content"]
Важно: temperature = 0.0, чтобы модель не фантазировала. И ни в коем случае не проси «перефразируй» — Qwen начнёт писать свой текст.
Склеиваем чистые чанки обратно в один текст. Можно ещё попросить Qwen разбить на логические главы (но это опционально).
4 Синтез речи через Kokoro 82M
Kokoro 82M — это модель + голосовые эмбеддинги. Мы используем дефолтный голос AM_NEW (американский нейтральный, но он отлично читает и по-русски).
from kokoro import KPipeline
import soundfile as sf
pipeline = KPipeline(lang_code='a') # 'a' — multilingual (русский, английский)
# Разбиваем очищенный текст на предложения (pipeline сам умеет пунктуацию)
gen = pipeline(text_chunkule, voice='af_bella', speed=1.0) # voice можно взять из списка
segments = []
for i, (gs, ps, audio) in enumerate(gen):
segments.append(audio)
print(f'Голосовой фрагмент {i} — {len(audio) / 24000:.2f} сек')
# Склеиваем все фрагменты
full_audio = np.concatenate(segments)
sf.write('chapter_01.wav', full_audio, 24000)
Kokoro выдаёт WAV. Хочешь mp3? Конвертируй ffmpeg.
ffmpeg -i chapter_01.wav -codec:a libmp3lame -b:a 64k chapter_01.mp3
64 kbps — норм для речи, 128 — перебор.
5 Автоматизация: скрипт одной кнопкой
Собираем всё в пайплайн. Можно даже Makefile написать, но я предпочитаю Python-скрипт с аргументами.
python3 pdf_to_audiobook.py --input book.pdf --output book.mp3 --voice af_bella
Внутри: извлечение, очистка через Qwen (если сервер запущен), синтез, конвертация. Повеси на cron — пусть ночью генерит.
Осторожно: Kokoro 82M не поддерживает кириллические эмодзи и диакритику. Если в PDF есть `«ё» и «ü»` — всё ок, а вот `emoji 😊` — пропустит. Лучше предварительно отфильтровать через regex.
Частые ошибки (и как их не допустить)
- Qwen начинает дописывать текст. Если после очистки модель добавляет «В данном отрывке говорится...» — значит, промпт плохой. Явно запрети любые дополнения: «Только исходные слова, без пояснений».
- Kokoro на русском звучит акцентом. Выбери голос
af_bellaилиam_adam— они multilingual. Не бериen_*— они для английского. - Слишком длинный текст пайплайна. Kokoro не умеет обрабатывать больше 10 минут за раз. Режь на главы или куски по 15-20 предложений.
- llama.cpp зависает на первом запросе. Скорее всего, не хватает оперативки. Включи параметр
--mlockили уменьши контекст до 8192.
Сравнение с альтернативами
| Инструмент | Качество голоса (рус.) | Скорость (CPU) | Нужен GPU? | Цена |
|---|---|---|---|---|
| Kokoro 82M | 8/10 | ~5 мин на 1 час аудио | Нет | Бесплатно |
| Qwen3 TTS (облачный) | 9/10 | Мгновенно | Нет | Платно за API |
| Edge TTS (через Playwright) | 7/10 | ~3 мин на 1 час | Нет | Бесплатно, но не локально |
Апгрейд пайплайна: голосовой клон и перевод
Если тебе мало дефолтных голосов, можно клонировать свой — для этого читай конвертер аудиокниг на Qwen3 TTS. Там показано, как с помощью OpenVoice или CosyVoice сделать копию голоса и прикрутить сюда.
Ещё один хак: если PDF на английском, а ты хочешь аудиокнигу на русском — используй Qwen для перевода (например, промптом «Переведи на русский литературно»). Пример такого перевода для манги есть в статье про перевод манги — принцип тот же.
А что если PDF — это сканы?
Тогда PyMuPDF не поможет. Придётся ставить OCR. Я бы взял LiteParse от LlamaIndex — он умеет локально парсить даже сканы, используя Surya OCR. Но это тянет GPU. Если нет — Tesseract + Qwen для исправления ошибок.
Почему не стоит использовать «голый» TTS без LLM?
Потому что TTS, обученный на «сырой» PDF, будет запинаться на числах, датах и сносках. Qwen превращает «На стр. 12 (см. рис. 3)» в «На странице двенадцать, смотрите рисунок три». Разница — как между учителем литературы и роботом-пылесосом.
Последний штрих: тюнинг под свой голос
У Kokoro есть возможность дообучения (fine-tuning) на 10 минут твоей речи. Инструмент для этого описан в Fine-tuning из PDF за 5 минут — да, даже PDF (!) можно использовать как источник для датасета. Но это уже тема отдельной статьи.
Почему я не доверяю облачным API для аудиокниг?
Я не хочу, чтобы моя коллекция технической литературы стала тренировочным датасетом для OpenAI. И мне не нравится ждать, пока сервер обработает 300 страниц, когда локально это занимает 15 минут. Плюс никаких лимитов — хоть «Войну и мир» генерируй.
А главное — ты контролируешь каждую запятую. Если Kokoro неверно прочитал акроним — ты правишь промпт Qwen, а не молишься на закрытое API.
Итоговый тест-драйв: книга за 10 минут
Я прогнал через пайплайн PDF «Марсианин» Энди Уира (английский текст). Результат: голос Кокоро читает с интонациями, паузы в нужных местах, шутки звучат как шутки. Единственная проблема — Qwen иногда выкидывал длинные числа, пришлось дописать в промпт «Сохраняй все числа как есть, не округляй».
Слушать можно в машине, в метро, на пробежке. И никаких утечек.
Собери свой пайплайн уже сегодня. Через неделю забудешь, что такое «читать» — будешь слушать. А если наткнёшься на баг — правь код сам, ты же DevOps.