Abstract

ИИбан DJ — ИИ-диджей для Маргинал радио. Личность советского диктора — торжественный, размеренный, с сарказмом. Не просто объявляет треки, а комментирует музыку, рассказывает контекст, кураторский подход. Работает на локальной vLLM (Gemma 4 12B), голос через Piper TTS.


Персоны

Две персоны с разным характером:

ПерсонаСтильЧастотаДлина скриптаГолос
ИИбанРазмеренный эфир, советский дикторmoderatestorytellerru_RU-ruslan-medium
TOVARISHГлубокий голос, лаконично и вескоaggressiveextendedru_RU-ruslan-medium

Активная персона — ИИбан (p_default0). DJ-режим включён — ИИбан сам выбирает треки и комментирует.

Промпты на английском

Кириллица в промптах — 2–5x больше токенов на слово. Поэтому soul (характер персоны) на английском с инструкцией WRITE SCRIPTS IN RUSSIAN для выхода.


Навыки (skills)

7 навыков — ИИбан не просто объявляет треки, а ведёт эфир:

НавыкЧто делает
album-anniversaryГодовщины альбомов — «Ровно 10 лет назад вышел этот альбом…»
curiosityИнтересные факты о треке/артисте — контекст, истории
library-deep-cutРедкие треки из библиотеки — «А вот вам глубокий крой…»
newsКраткие новости — ИИбан как диктор новостей
now-playing-digРазбор текущего трека — артист, альбом, контекст
weatherПогода в Перми — советская метеосводка
web-searchВеб-поиск через DuckDuckGo — актуальный контекст

Архитектура

Navidrome (671 трек, 241 артист)
    ↓
Маргинал Controller (DJ agent — 7 skills, tool calling)
    ↓
vLLM (Gemma 4 12B abliterated, 16K context, numCtx 8128)
    ↓
Piper TTS (ru_RU-ruslan-medium, 0.2s синтез)
    ↓
Liquidsoap (crossfade 10s, loudness -14 LUFS)
    ↓
Icecast → эфир (mp3 320kbps + flac + aac)

vLLM

vLLM — локальная LLM на GPU. OpenAI-совместимый API. Модель Gemma 4 12B (abliterated, FP8).

Ключевые настройки:

  • reasoning: true — модель думает перед ответом, более развёрнутые скрипты
  • maxOutputTokens: 1500 — баланс между длинными скриптами и чистым JSON
  • toolChoice: required — DJ agent использует tool calling для навыков
  • pickerAgent: true — LLM сама выбирает треки (не случайный pool)
  • agentTimeoutMs: 300000 — 5 минут на pick, потом fallback на pool
  • noRepeatWindow: 100 — не повторяет треки в течение 100 треков
  • pauseWhenEmpty: true — DJ засыпает когда нет слушателей, играет fallback playlist

Abliterated модель и JSON

Модель с убранной safety-tuning иногда выдаёт битый JSON. 5-15% fallback rate — норма для abliterated Gemma 4. Маргинал падает на pool picker, эфир не прерывается. Health report показывает 2-5 schema-validation failures из 20 вызовов.

Piper TTS

Голос ИИбана — Piper (built-in, Russian voices). Перешли с XTTS v2 на Piper — освободили 3.2GB RAM + 820MB swap. Синтез ~0.2s вместо 3-15s на CPU.

4 русских голоса: dmitri (medium), ruslan (medium, deeper), denis (medium), irina (female). ИИбан использует ruslan — глубокий мужской голос.

Эксперимент с Silero TTS

15 июля тестировали Silero TTS как замену Piper — 6 русских голосов, Docker sidecar, CPU. Синтез 0.4-0.9s, RTF 0.15x. Работало, но разница в качестве минимальна, а +530MB RAM и 3.2GB образ не оправданы. Вернулись на Piper.

ИИбан ищет в вебе через DuckDuckGo (без VPN, без токена). Навык web-search — актуальные новости, факты об артистах, контекст треков.

Эфир

4 кодировщика: MP3 (320kbps, основной), FLAC, AAC, hourly archive. Load avg 0.5-0.8 на 12 потоков — кодировщики не напрягают.


Скробблинг

Эфир скробблится в ListenBrainz — нативная интеграция Маргинал:

  • playing_now — когда трек начинается
  • single listen — когда трек доиграл (минимум 30 секунд)

История эфира, статистика, рекомендации — всё на listenbrainz.org/user/mrgnl044.


Библиотека

671 трек, 241 артист. Mood-тегами покрыто 100% библиотеки — ИИбан использует теги для подбора по настроению (calm, energetic, reflective…).

Авто-тегирование

Библиотека тегируется через Beets + кастомные скрипты:

  • Обложки — iTunes Search API, 600x600, встраиваются в MP3
  • Жанры — iTunes primaryGenreName (Hip-Hop, Alternative, Rock)
  • Тексты — lrclib (synced) → Genius (scraping) — встраиваются в MP3
  • Очистка теговclean-and-refetch.py (347 строк): чистит title от “Prod. by”, “Remix”, “feat.”, извлекает artist/title из имени файла для нетегированных треков

Cron

ВремяЗадача
03:00Spotify sync + Beets (метаданные, обложки, тексты)
04:00Navidrome scan (переиндексация библиотеки)

Spotify API 429

Spotify API иногда блокирует приложение на 24 часа (HTTP 429, retry-after 86400). В это время метаданные из Spotify недоступны. spotdl берёт аудио с YouTube Music, метаданные из Spotify API — если API заблокирован, теги не обновляются.

ИИбан использует теги для навыков: now-playing-dig (разбор трека), album-anniversary (годовщины), library-deep-cut (редкие треки). Чем богаче теги — тем содержательнее комментарии.


Сеть

vLLM и Маргинал в разных Docker сетях. Решение — подключить vLLM к сети Маргинал:

docker network connect app_default vllm-server

После этого Controller стучится на http://vllm-server:8000/v1.

Команду надо повторять после пересоздания контейнера

Docker не сохраняет ad-hoc подключения к сетям.


Связанные статьи

  • ИИбан — продуктовое описание ИИбана (без технических деталей)
  • Маргинал — радио, которое ведёт ИИбан
  • ListenBrainz — скробблинг эфира
  • Beets — авто-тегирование треков
  • vLLM — LLM бэкенд
  • Silero-TTS — эксперимент с заменой Piper
  • ИИбан-бот — мультиагентный бот (Telegram + Matrix)
  • Docker — контейнеры и сети