PersonaPlex

基于 Moshi 架构(Kyutai)的全双工语音到语音对话模型。PersonaPlex 7B 直接从语音输入生成语音响应——不需要中间文本流水线。模型自带 18 种预设音色,提供 8 位(推荐)和 4 位量化版本。8 位是默认选项——它快 30% 并能产生连贯的回答,而 4 位会降低输出质量。

架构

PersonaPlex 是一个多流自回归模型,由三个核心组件构成:

组件细节
Temporal Transformer32 层,dim=4096,32 heads,SwiGLU (hidden_scale=4.125),RoPE,默认 8 位量化
Depformer6 层,dim=1024,16 heads,MultiLinear (weights_per_step=true),dep_q=16
Mimi Codec16 个 codebook,12.5 Hz 帧率,24 kHz 音频输出

模型同时处理 17 个流:1 个文本流 + 8 个用户音频流 + 8 个 agent 音频流。这种架构实现了模型可以同时听和说的全双工对话。

预设音色

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 个用于 Mimi codec 的音频 codebook token 和一个模型内部独白的文本 token。文本流是模型在说话时"正在想"的内容——它与最终音频可能略有偏差,但实际上足够贴近口头回答,可以作为实时转写使用。

文本 token 以原始 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 mono Float32

在流式模式下,respondStream 会在生成过程中持续发出 textTokens 块——增量解码这些块即可驱动实时字幕视图,同时音频仍在生成。CLI 的 --transcript 标志正是在背后做同样的事。

为什么重要:SentencePieceDecoder 构建在共享的 AudioCommon.SentencePieceModel protobuf reader 之上,因此 PersonaPlex、OmnilingualASR 以及任何未来基于 SentencePiece 的模型都通过同一个 tokenizer 实现进行解码。参见 SentencePieceModel 参考

System Prompt

可以将任意自定义 system prompt 作为普通字符串传入——不需要外部 tokenization:

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

# 自定义 system prompt 文本
.build/release/audio respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# 使用预设的 system prompt
.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音色预设名(如 NATM0VARF2
--system-promptsystem prompt 预设:assistant、focused、customer-service、teacher
--system-prompt-text自定义 system prompt 文本(会覆盖 --system-prompt
--max-steps最大生成步数
--stream在生成过程中持续输出音频 chunk
--compile使用 MLX 编译推理以更快生成
--transcript在音频之外输出文本转写
--json带元数据的 JSON 输出

采样参数也可覆盖:

选项默认值说明
--audio-temp0.8音频 token 采样温度
--audio-top-k250音频 token top-k 采样
--text-temp0.7文本 token 采样温度
--text-top-k25文本 token top-k 采样

流式

--stream 标志启用实时音频输出。音频 chunk 在生成时即被输出,因此在完整响应完成之前就可以开始播放。这对低延迟非常重要的交互式应用特别有用。

性能

指标
实时因子 (RTF)~1.4(8 位,接近实时)
单步延迟M2 Max 上约 112 ms/step(8 位)
模型大小(8 位)~9.1 GB
峰值内存(8 位)~11 GB
模型大小(4 位)~4.9 GB
峰值内存(4 位)~7 GB
重要

PersonaPlex 7B(8 位)至少需要 24 GB 内存。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")