- PVSM.RU - https://www.pvsm.ru -

Мы живём в Мельбурне. Дочь растёт в русскоязычной семье, и мне хочется, чтобы русский был для неё не только «языком, на котором разговаривают дома», но и языком, на котором происходит что-то прикольное.
Я купил Яндекс Алису Макс и Алису Про. Для русского языка альтернатив толком нет: Amazon Echo, Apple HomePod, Google Nest, всё это про русский знает в лучшем случае на троечку. Яндекс другое дело: русская речь, русская музыка, русские команды, всё работает из коробки.
Но если зайти в Яндекс Диалоги и посмотреть, что там за навыки, становится грустно. Математические задачки, ролевые игры, сказки. Мёртвый продукт из эпохи до ChatGPT. Я смотрел на это и думал: ну ребята, можно же сделать что-то живое.
Так появилась идея навыка генерации музыки. Говоришь «создай песню про море», ждёшь минуту, и Алиса её играет.
На всё ушло два вечера. У меня уже был готовый шаблон инфраструктуры и пара похожих pet-проектов, так что я не начинал с нуля.
«Алиса, запусти навык генерация музыки»
Алиса: «Привет! Скажи: создай песню про что-нибудь и в каком стиле»
«Создай песню про серого пса»
Алиса: «Создаю песню, это займёт минуту. Спросите: готово?»
«Готово?»
Алиса: «Работаю. Осталось секунд тридцать. Спросите: готово?»
«Готово?»
Песня играет прямо на станции
Всё. Никаких приложений, никаких QR-кодов, никакого «зайдите на сайт».
Первая версия была наивной до неприличия. Пользователь просит песню, Lambda дёргает AI API, ждёт ответа, возвращает результат. Задеплоил, попробовал. Тишина.
Оказалось, у Яндекс Диалогов есть жёсткий таймаут: 4.5 секунды. Не уложился, сессия закрылась, до свидания.
📖 Документация про таймаут [2]
А генерация музыки занимает от 30 секунд до двух минут. Ну то есть совсем не близко.
Пришлось переделать всё на асинхронный лад:
Пользователь просит песню
Навык сразу отвечает «создаю, спросите готово?» и в фоне запускает генерацию
Пользователь спрашивает «готово?», навык лезет в DynamoDB смотреть статус
Когда готово, играет
Чтобы человек не сидел в неведении, я сделал ответы зависящими от времени ожидания. И ещё подсмотрел один приятный трюк: в TTS есть тег sil <[2000]>, который делает паузу. Алиса как бы «думает» перед ответом, и это сильно меняет ощущение от общения:
До 40 секунд: «Только начала... [пауза] Это займёт около минуты.»
40–80 секунд: «Работаю... [пауза] Осталось секунд тридцать.»
Больше 80: «Уже должно быть готово... [пауза] Спросите ещё раз.»
Мелочь, а живёт.
Я переделал на асинхрон, но первая итерация была всё ещё кривая. Фоновая Lambda дёргала AI API синхронно и ждала ответа. То есть пользователю мы отвечали сразу, но Lambda внутри жила полторы минуты и кушала ресурсы.
А я выбрал Lambda именно потому, что не хочу платить за простой. Никто не пользуется, ничего не тикает. Lambda, которая две минуты сидит в await, это уже не serverless, это виртуалка на минималках.
У Replicate есть штука получше, чем replicate.run() (который блокирует до результата). Можно использовать predictions.create() и попросить дёрнуть webhook, когда всё будет готово:
const prediction = await client.predictions.create({
model: 'google/lyria-3-pro',
input: { images: [], prompt },
webhook: `https://my-domain.com/alice/music/webhook?userId=${userId}`,
webhook_events_filter: ['completed'],
});
Lambda запускает генерацию и тут же умирает. Когда Replicate закончит, постучится сам. Я плачу секунды вместо минут.
Вот это был самый неожиданный сюрприз.
Логично же: положу MP3 в S3, дам Алисе URL, она сыграет. Документация описывает директиву audio_player:
{
"directives": {
"audio_player": {
"action": "Play",
"item": {
"stream": {
"url": "https://my-bucket.s3.amazonaws.com/songs/...",
"token": "track_uuid",
"offset_ms": 0
}
}
}
}
}
Алиса молчит. Я начал копать и нашёл целый букет граблей:
end_session обязан быть true (а у меня был false)
token не больше 128 символов (а у меня UUID + метаданные)
В тестовом чате консоли разработчика audio_player вообще не работает, только на реальных устройствах. То есть я отлаживал в пустоту.
Но главное: Алиса не умеет играть произвольный URL. Файл должен лежать в её собственном хранилище. S3 идёт лесом.
1. Получить OAuth-токен
Идёте на oauth.yandex.ru [4], создаёте приложение с правами на Диалоги, получаете токен вида:
OAuth AQAAAAAB5vpbAAQ7o9abBlrUn0nshvcHZE4Irhw
Кладёте в переменные окружения Lambda как YANDEX_OAUTH_TOKEN.
2. Загрузить MP3
POST https://dialogs.yandex.net/api/v1/skills/{skill_id}/sounds
Authorization: OAuth <токен>
Content-Type: multipart/form-data
В ответе будет sound.id.
3. Подождать, пока Яндекс переварит файл
Он конвертирует в OPUS асинхронно. Опрашиваете:
GET https://dialogs.yandex.net/api/v1/skills/{skill_id}/sounds/{sound_id}
Когда придёт isProcessed: true, можно играть.
4. Играть
{
"response": {
"text": "Играю: название песни",
"tts": "<speaker audio='dialogs-upload/{skill_id}/{sound_id}.opus'>",
"end_session": false
}
}
И вот это уже работает на всех устройствах без танцев с бубном.
Первой попробовал minimax/music-2.5. У неё есть отдельные поля для текста и для стиля, выглядит удобно. Но русский язык она поёт так себе: интонации мимо, произношение ломается, слушать неприятно.
Перешёл на google/lyria-3-pro через Replicate. Тут один общий промпт, в него идёт и текст, и стиль. Зато по-русски поёт заметно лучше.
Замена модели в коде это одна строка:
const LYRIA_MODEL = 'google/lyria-3-pro';
За это люблю Replicate: единый интерфейс поверх разных моделей, переключение тривиальное.
Suno делает треки сильно лучше, особенно русскоязычные. Но публичного API у них нет, и пока не предвидится. Lyria сейчас лучшее из того, к чему можно достучаться по нормальному API. Думаю, до конца года появятся модели поинтереснее.
У Lyria нервная модерация, и работает она хитро. Модель сначала генерирует текст песни, а потом сама же его и заворачивает за «чувствительный контент». То есть банит собственную работу.
Самое забавное, что слово-триггер вообще не обязано быть в моём промпте. Я пишу «шансон». Дальше Lyria по ассоциации сочиняет текст про блатную романтику и сама себя банит. То же самое с death metal: попроси жанр, и она напишет что-то про смерть, после чего откажется отдавать. Жанр тянет ассоциации, ассоциации тянут лирику, лирика ловит модератор. Я в этой цепочке не участвую.
Решил добавить автоматический retry. Не прошло, webhook сразу запускает вторую попытку с тем же промптом, модель генерирует другой текст, обычно проскакивает. Промпт беру прямо из тела webhook-запроса (input.prompt), хранить отдельно смысла нет.
Яндекс Диалоги дают 1 ГБ под аудиофайлы навыка. Считается размер уже после конвертации в OPUS, и треки автоматически режутся до 2 минут. Получается примерно 1000 песен, потом лимит. Для публичного навыка пришлось бы делать ротацию старых файлов.
Генерация одной песни в Lyria стоит $0.08. Для публичного навыка без монетизации это дорого, особенно если кто-то решит наспамить. Поэтому навык я оставил приватным, только для семьи. Но исходники планирую выложить, чтобы каждый мог поднять у себя.
Создаёт песни по голосовой команде на любую тему
Играет последнюю песню: «играй»
Ищет по названию: «играй про море»
Переделывает: «измени», «перепиши»
Рассказывает про прогресс генерации
Стек: AWS Lambda + DynamoDB, Replicate (Google Lyria 3 Pro), Yandex Dialogs API, TypeScript.
Дочка довольна, песни про серых псов и про море крутятся по кругу. Цель достигнута.
🔗 GitHub [5]
📖 Про таймаут 4.5 секунды [2]
📖 Загрузка аудио в навык [3]
Автор: SadeDV
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/generatsiya-muzy-ki/452199
Ссылки в тексте:
[1] dialogs.yandex.ru/developer: https://dialogs.yandex.ru/developer
[2] Документация про таймаут: https://yandex.ru/dev/dialogs/alice/doc/ru/wait-response
[3] Документация по загрузке аудио: https://yandex.ru/dev/dialogs/alice/doc/ru/resource-sounds-upload
[4] oauth.yandex.ru: http://oauth.yandex.ru
[5] GitHub: https://github.com/DmitrySadovnikov/alice-music-generator
[6] Google Lyria 3 Pro на Replicate: https://replicate.com/google/lyria-3-pro
[7] Источник: https://habr.com/ru/articles/1038948/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1038948
Нажмите здесь для печати.