Hướng dẫn cho nhà phát triển về việc sử dụng mô hình Naia từ mã nguồn. Sau khi chạy mô hình qua 4.4 Tải mô hình Naia, hãy dùng API tương thích OpenAI được phục vụ cục bộ (không gateway, không hàng đợi) như hiện có. Với bất kỳ OpenAI SDK hay công cụ nào, bạn chỉ cần trỏ baseURL tới mô hình này.
Không chỉ dành cho naia-os/vỏ giao diện — bất kỳ mã nguồn nào nói được OpenAI Realtime/Chat/Audio/Embeddings đều kết nối được như hiện có, và bạn có thể xây dựng và chạy các ứng dụng mới trên nền mô hình này.
1. Kết nối · xác thực
- REST base:
http://<host>:8892/v1(127.0.0.1trên cùng một PC) - Realtime (WS):
ws://<host>:8892/v1/realtime(dạng trầnws://<host>:8892cũng hoạt động — đường dẫn/v1/realtime+ mô hình mặc định được áp dụng tự động) - Kết nối: trên local (
127.0.0.1) / Tailscale, không cần xác thực — container tự xác minh giấy phép của nó. Các client cần một trường khóa (OpenAI SDK, v.v.) có thể truyền bất kỳ giá trị nào (naia). Khi phơi bày ra ngoài từ xa, hãy đặt Tailscale/VPN của §4.4 ở phía trước.
🔑 Một khóa duy nhất — khóa đăng ký
- Khóa đăng ký — khóa đăng ký bạn nhận được từ cổng thông tin. Chỉ dùng vào lúc chạy container (kích hoạt) (
-e NAIA_ACCOUNT_TOKEN=<subscription-key>). Nó kiểm tra đăng ký và lấy một giấy phép có giới hạn thời gian (chứng chỉ). - Không có khóa kết nối riêng. Sau khi đã kích hoạt, container tự xác minh cục bộ bằng chứng chỉ, nên các client (naia-os, OpenAI SDK) chỉ cần kết nối bằng URL —
127.0.0.1trên cùng một PC, hoặc Tailscale/VPN (§4.4) từ thiết bị khác. Nó không gọi gateway theo từng kết nối. api_keytrong các ví dụ bên dưới là một giá trị giữ chỗ (OpenAI SDK yêu cầu trường này) — container ngoại tuyến không kiểm tra nó, nên bất kỳ giá trị nào như"naia"đều hoạt động.
2. Endpoint (tương thích OpenAI)
| Endpoint | Công dụng |
|---|---|
GET /health | Trạng thái sẵn sàng {"ready":true,"services":{tts,stt,llm},"vad":true} (không cần xác thực) |
GET /v1/models | Danh sách mô hình |
WS /v1/realtime | Phiên giọng nói thời gian thực (VAD, ngắt lời, cảm xúc) |
POST /v1/chat/completions | Trò chuyện (streaming) |
POST /v1/audio/speech | Chuyển văn bản thành giọng nói (TTS) |
POST /v1/audio/transcriptions | Chuyển giọng nói thành văn bản (STT) |
POST /v1/embeddings | Embeddings |
Chat (curl):
curl -s http://127.0.0.1:8892/v1/chat/completions \
-H "Authorization: Bearer naia" -H "Content-Type: application/json" \
-d '{"model":"naia-0.9-omni-24g","messages":[{"role":"user","content":"hi"}],"stream":false}'
OpenAI SDK (Python) — chỉ cần đổi baseURL:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8892/v1", api_key="naia")
print(client.chat.completions.create(
model="naia-0.9-omni-24g",
messages=[{"role": "user", "content": "hi"}],
).choices[0].message.content)
Phiên âm (STT):
curl -s http://127.0.0.1:8892/v1/audio/transcriptions \
-H "Authorization: Bearer naia" \
-F file=@sample.wav -F model=naia-0.9-omni-24g
3. Giọng nói thời gian thực — luồng kết nối (WS)
Cùng luồng mà 4.3 bản demo trực tiếp sử dụng. (Ngoại tuyến khởi động ngay lập tức, không có hàng đợi/phân bổ của gateway.)
-
Kết nối — mở
ws://<host>:8892. -
Khung đầu tiên (xác thực · ngôn ngữ) — WebSocket của trình duyệt không thể gửi header, nên hãy gửi dưới dạng tin nhắn đầu tiên:
{ "setup": { "apiKey": "naia", "locale": "en" } } -
Khi máy chủ gửi
session.created, hãy cấu hình phiên bằngsession.update:{ "type": "session.update", "session": { "modalities": ["text", "audio"], "input_audio_format": "pcm16", "output_audio_format": "pcm16", "instructions": "<persona instructions>", "turn_detection": { "type": "server_vad" }, "input_audio_transcription": { "language": "en" }, "ref_audio_url": "<URL of a voice sample to mimic (optional)>" } } -
Trao đổi
Client → Máy chủ Đầu vào giọng nói {"type":"input_audio_buffer.append","audio":"<base64 PCM16 24kHz>"}(server VAD phát hiện kết thúc lời nói)Đầu vào văn bản conversation.item.createrồiresponse.createNgắt lời response.cancelMáy chủ → Client response.audio.deltakhối âm thanh base64 PCM16 24kHz response.audio_transcript.delta/response.text.deltavăn bản trả lời (streaming) conversation.item.input_audio_transcription.completedbản phiên âm lời nói của bạn emotion.updatedthẻ cảm xúc / ngữ điệu (§5) response.donekết thúc một lượt
4. Ngôn ngữ — 30 ngôn ngữ (mặc định = tự động/toàn cục)
Mô hình hỗ trợ 30 ngôn ngữ (Ả Rập, Miến Điện, Trung, Đan Mạch, Hà Lan, Anh, Phần Lan, Pháp, Đức, Hy Lạp, Hebrew, Hindi, Indonesia, Ý, Nhật, Khmer, Hàn, Lào, Mã Lai, Na Uy, Ba Lan, Bồ Đào Nha, Nga, Tây Ban Nha, Swahili, Thụy Điển, Tagalog, Thái, Thổ Nhĩ Kỳ, Việt).
- Mặc định (không đặt) = toàn cục/tự động — nó phát hiện ngôn ngữ bạn nói và trả lời bằng ngôn ngữ đó (theo từng lượt).
- Để ghim một ngôn ngữ cụ thể, hãy đưa vào mã ISO-639-1 (ví dụ
ko/en/ja) trongsetup.localehoặc tronginput_audio_transcription.languagecủasession.update.
5. Định dạng đầu ra (thẻ cảm xúc · ngữ điệu)
Định dạng đầu ra được tinh chỉnh cho trò chuyện bằng giọng nói — nếu client hiểu nó, nó có thể biểu đạt phong phú hơn.
- Thẻ ngữ điệu: văn bản trả lời chứa các thẻ trong ngoặc vuông bằng tiếng Anh viết thường như
[laughing],[sigh],[breath],[pause],[hesitation]xen vào những chỗ có chuyển biến cảm xúc (cho ngữ điệu giọng nói). Mô hình được hướng dẫn không dùng các thẻ tiếng Hàn như[웃음], các chỉ dẫn sân khấu trong ngoặc đơn như(smiling), hay dấu hoa thị như*smiles*. Từ vựng đã biết:laughing/laugh/laughter/chuckle/giggle · sigh/exhale · breath/inhale · pause · hesitation · gasp/cough/sneeze/yawn/sniff/hum · cry/sob/moan/whisper/shout/cheer(các thẻ khác được truyền qua nguyên vẹn). - Với mỗi thẻ, máy chủ gửi một sự kiện
emotion.updated1:1 (state== tên thẻ, viết thường):{ "type": "emotion.updated", "state": "laughing", "tag": "[laughing]", "known": true } - Đường TTS giữ lại các thẻ và đưa chúng vào tổng hợp để tạo ngữ điệu giọng nói, trong khi chat
text.deltagửi văn bản sạch đã bị loại bỏ thẻ. (Không có emoji, markdown, hay tự thuật trong ngoặc đơn ở đầu ra.) - Ánh xạ phía client (tham chiếu naia-os): ánh xạ
emotion.updated.state(thẻ ngữ điệu) tới biểu cảm avatar —laughing/chuckle/giggle/cheer → happy,sigh/exhale/cry/sob → sad,gasp → surprised,shout → angry,hesitation → think. Ngữ điệu phi cảm xúc nhưbreath·pausekhông làm thay đổi biểu cảm (giữ biểu cảm trước đó — để nó không nhấp về trạng thái trung tính sau mỗi hơi thở). - Khuyến nghị xử lý bền vững: đầu ra của LLM không phải lúc nào cũng chính xác. Ưu tiên
emotion.updated, nhưng nếu nó thiếu, hãy tự động phát hiện các thẻ ngay trong chính bản phiên âm (thẻ viết hoa[HAPPY]/ thẻ ngữ điệu viết thường) hoặc các chỉ dẫn sân khấu bị lọt ra ((smiles)·*sigh*) và phản ánh chúng vào biểu cảm; nếu không có manh mối, hãy giữ biểu cảm hiện tại (xem naia-osshell/src/lib/vrm/expression.tsextractExpression).
6. Đổi mô hình trò chuyện · cập nhật phiên bản mới (vận hành)
Đây là hướng dẫn chi tiết để thay đổi trực tiếp từ dòng lệnh. Người dùng đăng ký cá nhân cũng có thể dùng nguyên như vậy (không cần khóa), và nó bao gồm cả tùy chọn khóa cho vận hành chia sẻ/ki-ốt. Tóm tắt dễ hiểu có ở 4.4 Ngoại tuyến.
6.1 Đổi mô hình trò chuyện (từ 0.91)
Giữ nguyên container, chỉ thay đúng mô hình đảm nhận việc trò chuyện trong lúc đang chạy. Giọng nói (nói · nghe), watermark và xác thực đăng ký vẫn được giữ nguyên.
Ba điều cần biết trước:
- Mô hình mặc định là một LLM mở tích hợp sẵn. Bạn có thể đổi rồi quay về mặc định bất cứ lúc nào.
- Mô hình mới phải ở định dạng GGUF. Vì tính năng giọng nói đang dùng khoảng 10GB bộ nhớ, mô hình trò chuyện có thể lên tới khoảng 14GB. Mô hình lớn hơn sẽ bị từ chối, và nếu lỡ tải lên thất bại thì hệ thống tự quay về mô hình đang dùng (cuộc trò chuyện không bị gián đoạn).
- Người dùng đăng ký cá nhân không cần khóa riêng. Xác thực đăng ký (giấy phép) trên chính máy của bạn đã là quyền truy cập, nên cứ đổi bằng các lệnh bên dưới — giống như việc giọng nói không cần khóa. (Chỉ trên các máy chia sẻ/ki-ốt nhiều người dùng chung, người vận hành mới có thể đặt khóa bằng
-e NAIA_ADMIN_KEY=mật_khẩu_đã_đặtkhi chạy, và khi đó phải gửi kèm-H "Authorization: Bearer mật_khẩu_đã_đặt"trong yêu cầu.)
Thực hành — chỉ cần đặt sẵn địa chỉ:
BASE=http://127.0.0.1:8892 # nếu làm từ thiết bị khác, dùng địa chỉ https của §4.4 (ví dụ ...:8443)
① Xem hiện đang là mô hình nào và còn lại bao nhiêu bộ nhớ:
curl -s $BASE/admin/llm/status
② Đổi mô hình — chỉ thay phần mô hình trong dấu ngoặc kép rồi dán vào.
Bạn có thể đưa nguyên địa chỉ thẻ mô hình HuggingFace (https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF) hoặc id của nó (Qwen/Qwen2.5-7B-Instruct-GGUF):
curl -s -X POST $BASE/admin/llm/swap \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'
Tiền tố hf.co/ và mức nén (quant) được thêm tự động (mặc định là Q4_K_M). Nếu muốn mức nén cụ thể, hãy ghi thêm vào sau như Qwen/Qwen2.5-7B-Instruct-GGUF:Q5_K_M. Lần đầu tải một mô hình sẽ mất từ vài chục giây đến vài phút.
②-ngoại tuyến — đổi bằng tệp GGUF bạn đang có, không cần internet.
Khi không có internet như ở triển lãm · tư vấn, thay vì tải từ HuggingFace, hãy đăng ký tệp GGUF bạn đã có để đổi. (Quy tắc phân biệt: nếu tên có dấu gạch chéo kiểu tổ_chức/kho thì là HuggingFace trực tuyến, còn tên đơn giản không có dấu gạch chéo thì là mô hình cục bộ.)
Hãy sao chép và dán từng dòng. Điền tên bạn muốn vào chỗ mohinhcua_toi, và tên tệp thực tế vào chỗ mohinhcua_toi.gguf:
podman cp ./mohinhcua_toi.gguf naia-omni:/app/models/mohinhcua_toi.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/mohinhcua_toi.gguf\n" > /tmp/Modelfile && ollama create mohinhcua_toi -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"mohinhcua_toi:latest","pull":false}'
⚠️ GGUF do bạn tự chuyển đổi · gộp có thể thiếu mẫu trò chuyện (chat template), khiến phản hồi lan man/bị cắt. Trong trường hợp đó, hãy thêm mẫu trò chuyện của dòng mô hình (
TEMPLATE) và token dừng (PARAMETER stop) vào Modelfile ở bước 2 rồi đăng ký — chi tiết cho nhà phát triển ở [triển khai tham chiếu §7]. (GGUF Instruct chính thức của HuggingFace thường đã tích hợp sẵn nên dùng được luôn.)
③ Quay về mô hình mặc định:
curl -s -X POST $BASE/admin/llm/restore
Nếu là máy chia sẻ/ki-ốt (người vận hành đã đặt
NAIA_ADMIN_KEY), hãy thêm-H "Authorization: Bearer mật_khẩu_đã_đặt"vào mỗi lệnh trên. Người dùng đăng ký cá nhân không cần.
Sau khi đổi, các ứng dụng như naia-os vẫn kết nối bằng cùng địa chỉ như cũ (không cần kết nối lại). Nếu muốn nó tiếp tục khởi động với mô hình đó kể cả sau khi khởi động lại hay cập nhật, hãy chỉ định mô hình mặc định bằng -e NAIA_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct-GGUF khi chạy container.
6.2 Cập nhật lên phiên bản mới
Khi có phiên bản mới, chỉ đổi ảnh (phiên bản) và giữ nguyên đăng ký · cấu hình. Lần đầu bật phiên bản mới, container tự xác thực lại qua internet (giữ nguyên đăng ký · thiết bị cũ — không cần nhập lại khóa bằng tay). Vì vậy khi cập nhật phải có kết nối internet.
podman pull ghcr.io/nextain/naia-0.9-omni-24g:latest # tải phiên bản mới nhất
podman stop naia-omni && podman rm naia-omni # chỉ dọn dẹp container (xem lưu ý bên dưới)
# Hãy chạy lại đúng lệnh chạy mà bạn đã dùng khi cài đặt lần đầu — chỉ cần gắn lại đúng volume giấy phép như cũ là xong.
⚠️ Khi cập nhật, đừng nhấn "giải phóng thiết bị (release)". Giải phóng chỉ dùng khi chuyển sang một máy tính khác. Nếu giải phóng trong lúc định cập nhật thì bạn sẽ phải xác thực lại từ đầu. Cập nhật chỉ cần giữ nguyên volume giấy phép là đăng ký và đăng ký thiết bị vẫn được duy trì.
Người dùng đã xác thực trước đó chỉ cần tải phiên bản mới nhất và bật lại như trên là chuyển ngay sang phiên bản mới có thể đổi mô hình (vẫn giữ xác thực). Nếu muốn lấy đúng một phiên bản cụ thể, hãy dùng số phiên bản như :0.91 thay cho :latest.
7. Xem thêm
- Triển khai tham chiếu / mã mẫu (mã nguồn mở): client giọng nói của naia-os
shell/src/lib/voice/(Apache 2.0) — chứa client thực tế giao tiếp với API này (naia-omni.ts) và phần xử lý cảm xúc/ngữ điệu (emotion-tags.ts; ánh xạ biểu cảm & trích xuất bền vững trongvrm/expression.ts). Hãy dùng nó làm điểm khởi đầu để thử nghiệm các mô hình mới và xây dựng ứng dụng Tauri. Thử trực tiếp tại 4.3 bản demo trực tiếp. - Danh mục & giá: 4.1 Giá mô hình
- Đám mây (đã lên kế hoạch): 4.6 Trực tuyến