Xem bản tiếng Anh để biết thông tin mới nhất về lệnh gọi công cụ và hiệu năng VoiceChat.
Mô hình speech-to-speech
Soniqo hỗ trợ hai họ mô hình MLX native cho hội thoại speech-to-speech: PersonaPlex 7B và VoiceChat 11B. Cả hai đều nhận giọng nói và tạo trực tiếp audio của mô hình, nhưng dùng kiến trúc duplex và hợp đồng runtime khác nhau.
Chọn mô hình
| Mô hình | Kiểu hội thoại | Phù hợp nhất cho |
|---|---|---|
| PersonaPlex 7B | Full duplex đồng thời với Moshi, Depformer và Mimi | Nghe/nói chồng lấp và 18 giọng có thể chọn |
| VoiceChat 11B | Duplex liên tục, đồng bộ theo frame với FastConformer, Nemotron-H, EAR-TTS và neural codec | Frame streaming 80 ms, event văn bản/chức năng và giọng native của checkpoint |
Đây là các mô hình speech-to-speech cho hội thoại. Hibiki cũng chuyển trực tiếp giọng nói thành giọng nói, nhưng nhiệm vụ của nó là dịch giọng nói streaming nên được tài liệu hóa riêng.
PersonaPlex 7B
Mô hình hội thoại speech-to-speech song công dựa trên kiến trúc Moshi (Kyutai). PersonaPlex 7B tạo phản hồi nói trực tiếp từ đầu vào nói — không cần pipeline văn bản trung gian. Mô hình đi kèm 18 preset giọng nói và có sẵn ở dạng lượng tử hóa 8-bit (khuyến nghị) và 4-bit. 8-bit là mặc định — nó nhanh hơn 30% và tạo ra phản hồi mạch lạc, trong khi 4-bit làm suy giảm chất lượng đầu ra.
VoiceChat 11B
Tài liệu speech-to-speech duplex: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), được model card VoiceChat upstream của NVIDIA trích dẫn.
M5 Pro, 48 GB, Release, nhịp live 80 ms, INT5 với head được bảo vệ: ba lần chạy có kiểm soát đo RTF toàn pipeline là 0.94, 0.92 và 0.92, với RSS đỉnh khoảng 8.70 GB. Văn bản nói đầu tiên xuất hiện sau 44.3–46.7 ms và âm thanh phát được đầu tiên sau 74.0–76.4 ms. Đây là số liệu tính toán đã warm-up, không phải độ trễ luân phiên lượt nói đã học; hiệu năng INT8 sau tối ưu chưa được đo lại.
VoiceChat là runtime speech-to-speech MLX native còn lại trong speech-swift. Nó kết hợp nhận thức FastConformer nhân quả, kênh văn bản/chức năng duplex Nemotron-H, EAR-TTS độc lập điều kiện theo văn bản và neural codec 22,05 kHz. Session liên tục nhận audio mono 16 kHz và trả về sự kiện văn bản cùng một frame đầu ra 1.764 mẫu mỗi 80 ms.
Tải bằng VoiceChatModel.load(from:), bắt đầu VoiceChatSession và đưa audio qua pushAudio. Bundle đầy đủ phải có encoder/, llm/ và tts/; bundle cũ chỉ có phần hiểu sẽ bị từ chối.
INT5 duy trì thông lượng tổng thể dưới RTF 1 trên M5 Pro đã thử nghiệm. Cả ba lần chạy cũng giữ p95 tổng mỗi frame dưới 80 ms, ở mức 76.2–78.6 ms; vì vậy dư địa deadline vẫn hẹp. Hãy đo riêng thời điểm model mở lượt và độ trễ tính toán phần cứng.
VoiceChat CLI trực tiếp
Trò chuyện với Soniqo bằng bundle INT5 protected-head đầy đủ, dựa trên NVIDIA Nemotron VoiceChat 11B. Lệnh giữ capture và playback trong cùng một AVAudioEngine để dùng Apple AEC, stream phụ đề người dùng RNN-T có sẵn trong bundle và phát audio mô hình 22,05 kHz trong khi vẫn tiếp tục lắng nghe.
Prompt Soniqo mặc định nêu rõ giới hạn khả năng: CLI này có thể trò chuyện và trả lời câu hỏi nhưng không truy cập được lịch, lời nhắc, ứng dụng, tài khoản, thiết bị hoặc dịch vụ bên ngoài. Với các yêu cầu đó, Soniqo nói rõ rằng mình không thể thực hiện, không yêu cầu xác nhận cho việc không thể hoàn tất và gợi ý ngắn gọn điều người dùng có thể tự làm.
speech voice-chat
speech voice-chat --input question.wav --output response.wav
speech voice-chat \\
--mcp-config Examples/VoiceChatMCP/apple-reminders.json
Công cụ MCP có thể cấu hình
Adapter apple-reminders-eventkit đi kèm giờ chỉ công khai cho mô hình list_reminders, create_reminder và update_reminder. Việc khám phá danh sách được giữ riêng bên trong adapter. Khi cập nhật, người dùng nói tên lời nhắc và runtime ánh xạ nó tới ID EventKit riêng, đáng tin cậy; ID này không bao giờ xuất hiện trong kết quả mà mô hình thấy. Việc tạo và thời hạn vẫn được commit bằng một lần lưu EventKit. Lần khởi động server đầu có ít nhất 60 giây, còn tool call thường giữ mặc định 15 giây.
MCP chỉ bật khi có --mcp-config. JSON trung lập nhà cung cấp có thể khởi chạy bất kỳ MCP server stdio nào và chọn rõ tối đa năm công cụ qua enabledTools. Mọi công cụ không nằm trong readOnlyTools đều được xem là ghi. Chính sách mặc định là confirm: lần gọi đầu được giữ lại; lời xác nhận xác định của Soniqo nhắc lại các đối số đã hiểu và nói rõ chưa có gì thay đổi. Chỉ một lượt xác nhận mới của người dùng mới thực thi nó đúng một lần. Không thể chạy cùng một lệnh gọi hai lần nếu chưa có lời nói mới của người dùng, kể cả với allow. Cũng có deny và quyền rõ ràng allow. Kết quả thật trở lại qua function channel của mô hình và thành công đòi hỏi phản hồi ok.
Các đối số ghi được đối chiếu với bản chép lời của người dùng trước khi xác nhận. Ngày tương đối dùng ngày địa phương hiện tại; tên danh sách bịa đặt, ngày lỗi thời, mức ưu tiên và các giá trị chưa được xác minh sẽ không bao giờ được thực thi. Lời nói RNN-T mới rồi đến cuối lượt sẽ xử lý xác nhận ngay cả sau khi bản chép lời được đặt lại. Soniqo chỉ báo thành công khi MCP server thực sự trả về ok: true; nếu không, nó nói rằng không thể xác nhận việc thực thi.
“Tôi có những lời nhắc nào?” thực hiện một lần gọi list_reminders dạng phẳng trên mọi danh sách EventKit. Danh sách, thời hạn, trạng thái hoàn tất và ID ổn định chỉ dành cho runtime được cache, nên câu hỏi chi tiết và cập nhật an toàn không phải đọc lại từng danh sách. Dashboard hiển thị riêng thời gian, số token step và token/s của function decoding; audio RTF vẫn chỉ đo các frame âm thanh 80 ms ở foreground.
Lời xác nhận hỏi người dùng có muốn “tiếp tục” hay không và cố ý tránh mọi cụm từ khẳng định trong allow-list, nên tiếng vọng playback không thể tự cấp quyền cho hành động đang chờ.
Child MCP server chỉ kế thừa PATH, HOME, thư mục tạm và các biến locale. Credentials phải được truyền rõ ràng trong env map của server; các secret không liên quan từ parent process sẽ không được chuyển tiếp.
Function projection 8-bit có kích thước khoảng 518 MB. Trong lời nói thông thường, VoiceChat chỉ đánh giá probe PAD/tool-start 36 KB đã cache. Head đầy đủ 131.072 hàng chỉ chạy khi tool-start trước hết vượt PAD, sau đó xác minh global argmax và tiếp tục hoạt động trong lúc tạo một JSON call đang mở. Khi kết quả MCP đã biết, VoiceChat phát lại bằng các chunk causal-prefill giới hạn 16 token, giữ nguyên function feedback đã huấn luyện cùng trạng thái cache ngôn ngữ và giọng nói mà không chạy một lần decode 11B cho mỗi token kết quả. Nhờ vậy hội thoại MCP vẫn realtime mà không làm yếu quyết định bắt đầu tool.
Mặc định playback buffer ba frame 80 ms trước khi phát, tổng cộng 240 ms. Sau một khoảng gián đoạn, playback chờ vùng đệm khôi phục tám frame. Sau callback 88 ms đầu tiên hoặc ba frame xếp hàng, tinh chỉnh giảm từ tám xuống hai bước; ở 120 ms, sáu frame hoặc khi đồng bộ lại đầu vào, nó giảm thẳng xuống một bước. Nếu hàng đợi vẫn đạt tám frame (640 ms), hệ thống chỉ bỏ lượng audio cũ tối thiểu cần thiết để nhận đầu vào mới. Quá tải kéo dài được xem là một đợt khôi phục duy nhất, và lượt người dùng đã được RNN-T xác nhận vẫn hoạt động thay vì bị xóa bởi các lần reset lặp lại. Từ bị bỏ qua vẫn được đánh dấu và có thể cần nói lại.
Các số đo mô tả trải nghiệm: behind 0.3 s là audio microphone đang chờ, RTF 0.93× là thời gian tính chia cho thời gian audio (dưới 1 theo kịp, trên 1 bị chậm), và last 74 ms for 80 ms audio so thời gian tính gần nhất với 80 ms audio của một frame mô hình. Trung bình trượt dùng 120 callback microphone gần nhất; sự kiện replay không được tính là thời gian audio bổ sung.
Chế độ microphone mặc định dùng điều khiển lượt RNN-T theo NVIDIA. Quyết định BOS/EOS đã học của mô hình vẫn là đường dẫn độ trễ thấp thông thường. Dự đoán đầu tiên của mỗi frame 80 ms được xem là blank hoặc non-blank; sau khi xác nhận giọng nói người dùng, 40 frame blank liên tiếp (3,2 giây) chỉ là fallback an toàn để bắt đầu phản hồi, còn 40 frame non-blank mới cung cấp fallback tương ứng để ngắt lời. Bộ đếm giọng nói được đặt lại khi Soniqo bắt đầu, nên hoạt động từ lượt người dùng vừa kết thúc không thể cắt ngay phản hồi. Mọi frame âm thanh vẫn liên tục được đưa vào mô hình duplex; dùng --no-rnnt-turn-taking để tắt các fallback này và giao hoàn toàn quyết định BOS/EOS cho language head đã học. Dashboard báo barge-in RNN-T tách biệt với khoảng trống playback.
Ở chế độ bình thường, BOS gốc của mô hình bị chặn cho đến khi xác nhận giọng nói người dùng, nên Soniqo không nói trước. --greet cho phép rõ ràng lời chào mở đầu. Sau nội dung phản hồi nghe được, blank PAD vẫn nằm trong lượt mở ít nhất 16 frame hoặc ba frame cho mỗi token nội dung, lấy giá trị dài hơn. Blank PAD đầu tiên vượt ngân sách sẽ đóng lượt logic. Đuôi tỉ lệ theo nội dung này giữ lại các từ sinh muộn mà ngưỡng cố định 1,28 giây có thể làm mất tiếng.
Chế độ tương tác dùng bảng điều khiển cố định trong màn hình thay thế của terminal, nên trạng thái được vẽ lại tại chỗ thay vì lấp đầy lịch sử cuộn. Dùng --plain cho đầu ra nối tiếp.
Để chẩn đoán cục bộ, --debug-timeline gắn thời gian cho từng câu của người dùng và Soniqo, đồng thời chèn vào cùng dòng thời gian các tham số lệnh gọi gốc đã phân tích, thời điểm MCP bắt đầu/hoàn tất và đồng bộ cache kết quả. Chế độ này cũng đánh dấu pronunciation ended tại cửa sổ PCM tạo sinh cuối cùng nghe được; đây là thời gian đầu ra mô hình, không phải lúc thiết bị phát xong. Demo chủ ý bỏ các khối thời gian lịch sử; số liệu chi tiết về giải mã gốc, nhà cung cấp, cache và áp lực micro nằm trong JSON của voicechat-bench. Chế độ này có thể lộ tham số công cụ, vì vậy không dùng trong log chia sẻ. Bộ đếm giai đoạn được đặt lại giữa giải mã gốc và chờ nhà cung cấp.
Phiên trực tiếp giữ prompt người nói cố định 37 frame cùng 20 giây lịch sử EAR-TTS gần nhất; Nemotron-H giữ riêng ngữ cảnh hội thoại ngữ nghĩa. PAD trong đuôi âm thanh tỉ lệ theo nội dung vẫn được sinh bình thường; chỉ PAD im lặng phía sau mới được tiến hành theo nhân quả thành từng lô tám frame. Nhờ vậy phản hồi nói không bị cắt, còn attention TTS và công việc khi nhàn rỗi vẫn có giới hạn. --live-speech-context-seconds 0 khôi phục toàn bộ lịch sử TTS và chế độ file vẫn dùng lịch sử đầy đủ, chính xác theo mặc định.
Trong profile 63,6 giây trên M5 Pro với đuôi âm thanh an toàn, đường chạy trực tiếp đo được RTF tổng 0,87, RTF cửa sổ cuối 0,71, tổng hợp cửa sổ cuối 4,1 ms/frame, peak RSS 8,72 GB và peak physical footprint 23,67 GB. Các cửa sổ đang phát lời với tám bước đạt RTF 1,05 và 1,12, vì vậy CLI tương tác vẫn giảm có thể đảo ngược xuống hai bước và dùng một bước trong chế độ khẩn cấp khi đầu vào xếp hàng. Độ tương đồng cosine tối thiểu giữa trạng thái cache nhàn rỗi tuần tự và theo lô là 0,9999999.
Bộ phát hiện tiếng bật đầu câu của voicechat-bench kiểm tra phần dẫn và 50 ms đầu của lời nói liên tục. Nó chỉ đánh dấu bước nhảy vượt biên độ 0,05 và sáu lần đường cơ sở p99 của lời nói ổn định; kịch bản có thể đặt maximum_suspect_onset_transients bằng không.
Ba lần chạy Release trên M5 Pro 48 GB đều đo RTF 0,92, total-frame p95 74,8–75,8 ms, audio đầu tiên có thể phát sau 74,2–74,9 ms và peak RSS khoảng 8,74 GB. Transcript và câu trả lời giống nhau trong cả ba lần.
Kiến trúc và cách dùng PersonaPlex
PersonaPlex là một mô hình tự hồi quy đa luồng với ba thành phần cốt lõi:
| Thành phần | Chi tiết |
|---|---|
| Temporal Transformer | 32 lớp, dim=4096, 32 head, SwiGLU (hidden_scale=4.125), RoPE, lượng tử hóa 8-bit (mặc định) |
| Depformer | 6 lớp, dim=1024, 16 head, MultiLinear (weights_per_step=true), dep_q=16 |
| Mimi Codec | 16 codebook, tần số khung 12,5 Hz, đầu ra âm thanh 24 kHz |
Mô hình xử lý đồng thời 17 luồng: 1 luồng văn bản + 8 luồng âm thanh người dùng + 8 luồng âm thanh tác nhân. Kiến trúc này cho phép hội thoại song công, mô hình có thể nghe và nói cùng lúc.
Preset giọng nói
PersonaPlex bao gồm 18 preset giọng tích hợp sẵn với các phong cách tự nhiên và đa dạng:
| Danh mục | Preset |
|---|---|
| Giọng nữ tự nhiên | NATF0, NATF1, NATF2, NATF3 |
| Giọng nam tự nhiên | NATM0, NATM1, NATM2, NATM3 |
| Giọng nữ đa dạng | VARF0, VARF1, VARF2, VARF3, VARF4 |
| Giọng nam đa dạng | VARM0, VARM1, VARM2, VARM3, VARM4 |
Độc thoại nội tâm
PersonaPlex tạo hai luồng song song ở mỗi bước: 8 token audio codebook cho codec Mimi và một token văn bản cho độc thoại nội tâm của mô hình. Luồng văn bản là điều mô hình đang “suy nghĩ” khi nói — nó có thể lệch nhẹ so với âm thanh cuối cùng, nhưng trên thực tế nó phản chiếu phản hồi nói đủ sát để dùng như một transcript trực tiếp.
Các token văn bản quay về dưới dạng ID piece SentencePiece thô. Giải mã chúng bằng SentencePieceDecoder mà PersonaPlex cung cấp:
import PersonaPlex
import AudioCommon
let model = try await PersonaPlexModel.fromPretrained()
let decoder = model.tokenizer // SentencePieceDecoder?
let result = model.respond(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
Ở chế độ streaming, respondStream phát ra các chunk textTokens khi chúng được tạo ra — giải mã chúng tăng dần để điều khiển một view phụ đề trực tiếp trong khi âm thanh vẫn đang được tạo. Cờ CLI --transcript thực hiện chính xác điều này phía sau hậu trường.
Tại sao điều đó quan trọng: SentencePieceDecoder được xây dựng trên bộ đọc protobuf chia sẻ AudioCommon.SentencePieceModel, vì vậy PersonaPlex, OmnilingualASR và mọi mô hình dựa trên SentencePiece trong tương lai đều giải mã qua cùng một triển khai tokenizer. Xem tham chiếu SentencePieceModel.
System prompt
Truyền bất kỳ system prompt tùy chỉnh nào dưới dạng chuỗi thuần — không cần tokenize bên ngoài:
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
Hoặc dùng preset có sẵn:
assistant— Trợ lý hữu ích mục đích chung (mặc định)focused— Phản hồi ngắn gọn, trực tiếpcustomer-service— Tác nhân hỗ trợ lịch sự, hướng giải phápteacher— Phong cách giảng dạy kiên nhẫn, giải thích
Sử dụng CLI
Tạo phản hồi nói từ đầu vào âm thanh:
# Basic speech-to-speech
.build/release/speech respond --input question.wav
# Choose a voice preset
.build/release/speech respond --input question.wav --voice NATM0
# Stream audio output during generation
.build/release/speech respond --input question.wav --stream
# Custom system prompt text
.build/release/speech respond --input question.wav --system-prompt-text "You enjoy having a good conversation."
# Use a preset system prompt
.build/release/speech respond --input question.wav --system-prompt customer-service
# Get transcript alongside audio
.build/release/speech respond --input question.wav --transcript
# JSON output with metadata
.build/release/speech respond --input question.wav --json
Tùy chọn
| Tùy chọn | Mô tả |
|---|---|
--input | Tệp âm thanh đầu vào (WAV, bắt buộc) |
--voice | Tên preset giọng (ví dụ NATM0, VARF2) |
--system-prompt | Preset system prompt: assistant, focused, customer-service, teacher |
--system-prompt-text | Văn bản system prompt tùy chỉnh (ghi đè --system-prompt) |
--max-steps | Số bước sinh tối đa |
--stream | Phát các chunk âm thanh trong khi sinh |
--compile | Dùng suy luận compile của MLX để sinh nhanh hơn |
--transcript | Đầu ra transcript văn bản kèm âm thanh |
--json | Đầu ra JSON với metadata |
Các tham số lấy mẫu cũng có thể được ghi đè:
| Tùy chọn | Mặc định | Mô tả |
|---|---|---|
--audio-temp | 0.8 | Nhiệt độ lấy mẫu token âm thanh |
--audio-top-k | 250 | Lấy mẫu top-k token âm thanh |
--text-temp | 0.7 | Nhiệt độ lấy mẫu token văn bản |
--text-top-k | 25 | Lấy mẫu top-k token văn bản |
Streaming
Cờ --stream bật đầu ra âm thanh thời gian thực. Các chunk âm thanh được phát ra khi chúng được tạo, vì vậy việc phát lại có thể bắt đầu trước khi phản hồi đầy đủ hoàn tất. Điều này đặc biệt hữu ích cho các ứng dụng tương tác mà độ trễ thấp là quan trọng.
Hiệu năng
| Chỉ số | Giá trị |
|---|---|
| Hệ số thời gian thực (RTF) | ~1,4 (8-bit, gần thời gian thực) |
| Độ trễ mỗi bước | ~112 ms/bước trên M2 Max (8-bit) |
| Kích thước mô hình (8-bit) | ~9,1 GB |
| RAM đỉnh (8-bit) | ~11 GB |
| Kích thước mô hình (4-bit) | ~4,9 GB |
| RAM đỉnh (4-bit) | ~7 GB |
PersonaPlex 7B (8-bit) yêu cầu tối thiểu 24 GB RAM. Biến thể 4-bit chạy được trên thiết bị 16 GB nhưng tạo ra đầu ra suy giảm. Trên thiết bị 8 GB, không biến thể nào chạy được. Dùng --compile để có hiệu năng tốt nhất trên phần cứng hỗ trợ.
Biến thể mô hình
| Mô hình | Kích thước | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8-bit) khuyến nghị | 9,1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4-bit) | 4,9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
API Swift
import PersonaPlex
import AudioCommon
let model = try await PersonaPlexModel.fromPretrained()
let response = model.respond(
userAudio: userSamples,
voice: .NATM0,
systemPrompt: "You are a helpful assistant."
)
try WAVWriter.write(samples: response.audio, sampleRate: 24000, to: URL(filePath: "answer.wav"))