PersonaPlex

Модель полнодуплексного диалога речь-в-речь на базе архитектуры Moshi (Kyutai). PersonaPlex 7B генерирует голосовые ответы непосредственно из голосового входа — без промежуточного текстового пайплайна. Модель поставляется с 18 голосовыми пресетами и доступна в 8-битной (рекомендуется) и 4-битной квантизации. По умолчанию 8-bit — она на 30% быстрее и выдаёт связные ответы, тогда как 4-bit ухудшает качество вывода.

Архитектура

PersonaPlex — это многопоточная авторегрессивная модель с тремя ключевыми компонентами:

КомпонентДетали
Temporal Transformer32 слоя, dim=4096, 32 head, SwiGLU (hidden_scale=4.125), RoPE, 8-bit-квантизация (по умолчанию)
Depformer6 слоёв, dim=1024, 16 head, MultiLinear (weights_per_step=true), dep_q=16
Mimi Codec16 codebook, частота кадров 12.5 Гц, аудио-выход 24 кГц

Модель обрабатывает 17 потоков одновременно: 1 текстовый поток + 8 пользовательских аудиопотоков + 8 аудиопотоков агента. Эта архитектура позволяет полнодуплексный диалог, где модель может одновременно слушать и говорить.

Голосовые пресеты

PersonaPlex включает 18 встроенных голосовых пресетов в натуральном и разнообразных стилях:

КатегорияПресеты
Natural FemaleNATF0, NATF1, NATF2, NATF3
Natural MaleNATM0, NATM1, NATM2, NATM3
Varied FemaleVARF0, VARF1, VARF2, VARF3, VARF4
Varied MaleVARM0, VARM1, VARM2, VARM3, VARM4

Внутренний монолог

PersonaPlex на каждом шаге генерирует два параллельных потока: 8 audio-codebook-токенов для Mimi-кодека и один текстовый токен для внутреннего монолога модели. Текстовый поток — это то, что модель «думает», пока говорит — он может немного расходиться с финальным аудио, но на практике достаточно близко повторяет голосовой ответ, чтобы использовать его как живой транскрипт.

Текстовые токены возвращаются как сырые SentencePiece piece ID. Декодируйте их через SentencePieceDecoder, который поставляется в PersonaPlex:

import PersonaPlex
import AudioCommon

let model = try await PersonaPlexModel.fromPretrained()
let decoder = try model.makeTextDecoder()  // SentencePieceDecoder

let result = model.respondWithTranscript(userAudio: userSamples, voice: .NATM0)
let transcript = decoder.decode(result.textTokens)
print(transcript)        // "Sure, I can help with that..."
playAudio(result.audio)  // 24 кГц моно Float32

В потоковом режиме respondStream выдаёт куски textTokens по мере их появления — декодируйте их инкрементально, чтобы обновлять живые субтитры, пока аудио всё ещё генерируется. Флаг CLI --transcript делает именно это за кулисами.

Почему это важно: SentencePieceDecoder построен на общем AudioCommon.SentencePieceModel-ридере protobuf, так что PersonaPlex, OmnilingualASR и любые будущие модели на SentencePiece декодируют через одну реализацию токенизатора. См. справочник SentencePieceModel.

Системные промпты

Передайте любой пользовательский системный промпт как обычную строку — внешняя токенизация не нужна:

let response = model.respond(
    userAudio: audio,
    voice: .NATM0,
    systemPrompt: "You enjoy having a good conversation."
)

Или используйте встроенный пресет:

Использование CLI

Сгенерировать голосовой ответ из аудио-входа:

# Базовая речь-в-речь
.build/release/audio respond --input question.wav

# Выбрать голосовой пресет
.build/release/audio respond --input question.wav --voice NATM0

# Стримить аудио-выход во время генерации
.build/release/audio respond --input question.wav --stream

# Пользовательский системный промпт
.build/release/audio respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# Использовать пресетный системный промпт
.build/release/audio respond --input question.wav --system-prompt customer-service

# Получить транскрипт вместе с аудио
.build/release/audio respond --input question.wav --transcript

# JSON-вывод с метаданными
.build/release/audio respond --input question.wav --json

Опции

ОпцияОписание
--inputВходной аудиофайл (WAV, обязательно)
--voiceИмя голосового пресета (например, NATM0, VARF2)
--system-promptПресет системного промпта: assistant, focused, customer-service, teacher
--system-prompt-textПользовательский текст системного промпта (переопределяет --system-prompt)
--max-stepsМаксимум шагов генерации
--streamВыдавать аудио-чанки во время генерации
--compileИспользовать compiled-инференс MLX для более быстрой генерации
--transcriptВыводить текстовый транскрипт вместе с аудио
--jsonJSON-вывод с метаданными

Параметры сэмплирования также можно переопределить:

ОпцияПо умолчаниюОписание
--audio-temp0.8Температура сэмплирования audio-токенов
--audio-top-k250Top-k сэмплирование audio-токенов
--text-temp0.7Температура сэмплирования текстовых токенов
--text-top-k25Top-k сэмплирование текстовых токенов

Потоковый режим

Флаг --stream включает вывод аудио в реальном времени. Аудио-чанки выдаются по мере их генерации, так что воспроизведение может начаться до того, как полный ответ будет готов. Это особенно полезно для интерактивных приложений, где важна низкая задержка.

Производительность

МетрикаЗначение
Real-time factor (RTF)~1.4 (8-bit, почти реальное время)
Задержка шага~112 мс/шаг на M2 Max (8-bit)
Размер модели (8-bit)~9.1 ГБ
Пиковая RAM (8-bit)~11 ГБ
Размер модели (4-bit)~4.9 ГБ
Пиковая RAM (4-bit)~7 ГБ
Важно

PersonaPlex 7B (8-bit) требует минимум 24 ГБ RAM. Вариант 4-bit помещается на устройства с 16 ГБ, но выдаёт ухудшенный вывод. На устройствах с 8 ГБ ни один вариант не поместится. Используйте --compile для лучшей производительности на поддерживаемом оборудовании.

Варианты модели

МодельРазмерHuggingFace
PersonaPlex-7B (8-bit) рекомендуется9.1 ГБaufklarer/PersonaPlex-7B-MLX-8bit
PersonaPlex-7B (4-bit)4.9 ГБaufklarer/PersonaPlex-7B-MLX-4bit

Swift API

import PersonaPlex

let model = try await PersonaPlexModel.loadFromHub()
let response = try await model.respond(
    audioFile: "question.wav",
    voice: .NATM0,
    systemPrompt: .assistant
)
try response.audio.write(to: "answer.wav")