PersonaPlex

Moshi 아키텍처(Kyutai) 기반의 전이중 음성-음성 대화 모델입니다. PersonaPlex 7B는 음성 입력에서 직접 음성 응답을 생성하며, 중간 텍스트 파이프라인이 필요하지 않습니다. 모델은 18개의 음색 프리셋과 함께 제공되며 8비트(권장)와 4비트 양자화로 이용할 수 있습니다. 8비트가 기본입니다 — 30% 더 빠르고 일관된 응답을 생성하는 반면 4비트는 출력 품질이 저하됩니다.

아키텍처

PersonaPlex는 세 가지 핵심 구성 요소를 가진 멀티 스트림 자기회귀 모델입니다:

구성 요소세부 내용
Temporal Transformer32 레이어, dim=4096, 32 헤드, SwiGLU (hidden_scale=4.125), RoPE, 8비트 양자화 (기본)
Depformer6 레이어, dim=1024, 16 헤드, MultiLinear (weights_per_step=true), dep_q=16
Mimi Codec16 코드북, 12.5 Hz 프레임 속도, 24 kHz 오디오 출력

모델은 동시에 17개 스트림을 처리합니다: 텍스트 스트림 1개 + 사용자 오디오 스트림 8개 + agent 오디오 스트림 8개. 이 아키텍처는 모델이 듣고 말하는 것을 동시에 할 수 있는 전이중 대화를 가능하게 합니다.

음색 프리셋

PersonaPlex는 자연스러운 스타일과 다양한 스타일에 걸쳐 18개의 내장 음색 프리셋을 포함합니다:

카테고리프리셋
자연스러운 여성NATF0, NATF1, NATF2, NATF3
자연스러운 남성NATM0, NATM1, NATM2, NATM3
다양한 여성VARF0, VARF1, VARF2, VARF3, VARF4
다양한 남성VARM0, VARM1, VARM2, VARM3, VARM4

내부 독백

PersonaPlex는 매 스텝마다 두 개의 병렬 스트림을 생성합니다: Mimi 코덱용 8개 오디오 코드북 토큰과 모델의 내부 독백을 위한 텍스트 토큰 1개. 텍스트 스트림은 모델이 말하면서 “생각”하는 것입니다 — 최종 오디오와 약간 다를 수 있지만, 실제로는 음성 응답과 충분히 가까워서 라이브 전사로 사용할 수 있습니다.

텍스트 토큰은 raw SentencePiece piece ID로 돌아옵니다. PersonaPlex가 제공하는 SentencePieceDecoder로 디코딩하세요:

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 kHz 모노 Float32

스트리밍 모드에서 respondStream은 생성되는 대로 textTokens 청크를 방출합니다 — 오디오가 아직 생성 중인 동안 이를 점진적으로 디코딩하여 라이브 캡션 뷰를 구동합니다. --transcript CLI 플래그는 내부적으로 이 동작을 수행합니다.

왜 중요한가: 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더 빠른 생성을 위해 MLX 컴파일된 추론 사용
--transcript오디오와 함께 텍스트 전사 출력
--json메타데이터를 포함한 JSON 출력

샘플링 파라미터도 재정의할 수 있습니다:

옵션기본값설명
--audio-temp0.8오디오 토큰 샘플링 temperature
--audio-top-k250오디오 토큰 top-k 샘플링
--text-temp0.7텍스트 토큰 샘플링 temperature
--text-top-k25텍스트 토큰 top-k 샘플링

스트리밍

--stream 플래그는 실시간 오디오 출력을 활성화합니다. 오디오 청크가 생성되는 대로 방출되므로 전체 응답이 완성되기 전에 재생을 시작할 수 있습니다. 이는 지연이 중요한 인터랙티브 애플리케이션에 특히 유용합니다.

성능

지표
실시간 계수 (RTF)약 1.4 (8비트, 실시간에 근접)
스텝 지연M2 Max에서 약 112 ms/step (8비트)
모델 크기 (8비트)약 9.1 GB
피크 RAM (8비트)약 11 GB
모델 크기 (4비트)약 4.9 GB
피크 RAM (4비트)약 7 GB
중요

PersonaPlex 7B (8비트)는 최소 24 GB의 RAM이 필요합니다. 4비트 변형은 16 GB 기기에 맞지만 저하된 출력을 생성합니다. 8 GB 기기에서는 두 변형 모두 맞지 않습니다. 지원되는 하드웨어에서 최상의 성능을 위해 --compile을 사용하세요.

모델 변형

모델크기HuggingFace
PersonaPlex-7B (8비트) 권장9.1 GBaufklarer/PersonaPlex-7B-MLX-8bit
PersonaPlex-7B (4비트)4.9 GBaufklarer/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")