从代码使用 Naia 模型的开发者指南。 在按照 4.4 Naia 模型下载运行模型之后,直接使用本地提供的 OpenAI 兼容 API (无网关、无队列)。在任何 OpenAI SDK 或工具中,你只需将 baseURL 指向这个模型。
不局限于 naia-os / 外壳 — 任何会说 OpenAI Realtime/Chat/Audio/Embeddings 的代码都能直接连接, 你还可以在这个模型之上构建并运行新的应用程序。
1. 连接 · 认证
- REST 基址:
http://<host>:8892/v1(同一台 PC 用127.0.0.1) - Realtime(WS):
ws://<host>:8892/v1/realtime(裸的ws://<host>:8892也可用——路径/v1/realtime+ 默认模型自动应用) - 连接:在本地(
127.0.0.1)/ Tailscale 上,无需认证——容器会自我校验其许可证。需要密钥字段的客户端(OpenAI SDK 等)可以传入任意值(naia)。当对外暴露时,请在前面加上 §4.4 的 Tailscale/VPN。
🔑 唯一的密钥 — 订阅密钥
- 订阅密钥 — 你从门户获取的订阅密钥。仅在容器运行时(激活)使用(
-e NAIA_ACCOUNT_TOKEN=<subscription-key>)。它会检查订阅并获取一个带时限的许可证(证书)。 - 没有单独的连接密钥。 一旦激活,容器会用证书在本地自我校验,因此客户端(naia-os、OpenAI SDK)只需通过 URL 连接——同一台 PC 用
127.0.0.1,其他设备用 Tailscale/VPN(§4.4)。它不会在每次连接时调用网关。 - 下面示例中的
api_key是一个占位符(OpenAI SDK 要求该字段)——离线容器不检查它,因此"naia"这样的任意值都可用。
2. 端点(OpenAI 兼容)
| 端点 | 用途 |
|---|---|
GET /health | 就绪状态 {"ready":true,"services":{tts,stt,llm},"vad":true}(无需认证) |
GET /v1/models | 模型列表 |
WS /v1/realtime | 实时语音会话(VAD、打断、情绪) |
POST /v1/chat/completions | 聊天(流式) |
POST /v1/audio/speech | 文本转语音(TTS) |
POST /v1/audio/transcriptions | 语音转文本(STT) |
POST /v1/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)— 只需替换 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)
转录(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. 实时语音 — 连接流程(WS)
与 4.3 实时演示使用的流程相同。(离线立即启动,没有网关队列 / 分配。)
-
连接 — 打开
ws://<host>:8892。 -
第一帧(认证 · 语言) — 浏览器 WebSocket 无法发送 header,因此作为第一条消息发送:
{ "setup": { "apiKey": "naia", "locale": "en" } } -
当服务器发送
session.created时,用session.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)>" } } -
交互
客户端 → 服务器 语音输入 {"type":"input_audio_buffer.append","audio":"<base64 PCM16 24kHz>"}(服务器 VAD 检测语音结束)文本输入 先 conversation.item.create,再response.create打断 response.cancel服务器 → 客户端 response.audio.deltabase64 PCM16 24kHz 音频块 response.audio_transcript.delta/response.text.delta回答文本(流式) conversation.item.input_audio_transcription.completed你语音的转录 emotion.updated情绪 / 韵律标签(§5) response.done一轮结束
4. 语言 — 30 种语言(默认 = 自动 / 全局)
模型支持 30 种语言(阿拉伯语、缅甸语、中文、丹麦语、荷兰语、英语、芬兰语、法语、 德语、希腊语、希伯来语、印地语、印尼语、意大利语、日语、高棉语、韩语、老挝语、马来语、挪威语、 波兰语、葡萄牙语、俄语、西班牙语、斯瓦希里语、瑞典语、他加禄语、泰语、土耳其语、越南语)。
- 默认(未设置)= 全局 / 自动 — 它会检测你所说的语言,并用该语言回复(每轮)。
- 要固定某种特定语言,在
setup.locale或session.update的input_audio_transcription.language中给出 ISO-639-1 代码(例如ko/en/ja)。
5. 输出格式(情绪 · 韵律标签)
输出格式针对语音对话进行了调优——如果客户端了解它,就能表达得更丰富。
- 韵律标签:回答文本中会在情绪变化处混入像
[laughing]、[sigh]、[breath]、[pause]、[hesitation]这样的小写英文方括号标签(用于语音韵律)。模型被 指示不要使用像[웃음]这样的韩语标签、像(smiling)这样的括号舞台提示,或像*smiles*这样的星号。已知词汇:laughing/laugh/laughter/chuckle/giggle · sigh/exhale · breath/inhale · pause · hesitation · gasp/cough/sneeze/yawn/sniff/hum · cry/sob/moan/whisper/shout/cheer(其他标签会原样传递)。 - 对于每个标签,服务器会发送一个 1:1 的
emotion.updated事件(state== 标签名,小写):{ "type": "emotion.updated", "state": "laughing", "tag": "[laughing]", "known": true } - TTS 路径保留这些标签并将其送入合成以实现语音韵律,而 chat 的
text.delta发送去除标签的干净文本。(输出中没有表情符号、markdown 或括号自述。) - 客户端映射(naia-os 参考):将
emotion.updated.state(韵律标签)映射到虚拟形象表情——laughing/chuckle/giggle/cheer → happy、sigh/exhale/cry/sob → sad、gasp → surprised、shout → angry、hesitation → think。像breath·pause这样的非情绪韵律不会改变表情 (保持之前的——这样它不会在每次呼吸时都闪回中性)。 - 建议进行稳健处理:LLM 输出并不总是精确。优先使用
emotion.updated,但如果它 缺失,则在转录文本本身中自动检测标签(大写[HAPPY]/ 小写韵律标签)或 泄漏的舞台提示((smiles)·*sigh*)并反映到表情中;如果没有线索,则保持 当前表情(参见 naia-osshell/src/lib/vrm/expression.ts的extractExpression)。
6. 更换对话模型 · 上传新版本(运维)
这是通过命令行直接更换的详细指南。个人订阅者也可以照样使用(无需密钥),同时也包含用于共享 / 自助终端运维的锁定选项。简易摘要见 4.4 离线。
6.1 更换对话模型(从 0.91 起)
保持容器不动,仅在运行时更换负责对话的模型。语音(说与听)、水印和订阅认证都保持不变。
先了解三件事:
- 默认模型是内置的开源 LLM。换了之后可以随时恢复为默认。
- 要上传的新模型必须是 GGUF 格式。而且语音功能已占用约 10GB 内存,因此对话模型最多大约可达 14GB。更大的模型会被拒绝;即便上传过程中失败,也会自动回退到之前使用的模型(对话不会中断)。
- 个人订阅者无需单独的密钥。 你本机的订阅认证(许可证)即为权限,因此直接用下面的命令更换即可——这和语音不需要密钥是一样的。(只有在多人共用的共享 / 自助终端机上,运维者才能在启动时用
-e NAIA_ADMIN_KEY=你设定的密码加锁,此时请求中要一并发送-H "Authorization: Bearer 你设定的密码"。)
实操 — 先设定好地址:
BASE=http://127.0.0.1:8892 # 在其他设备上请用 §4.4 的 https 地址(例如 ...:8443)
① 查看当前是哪个模型、内存还剩多少:
curl -s $BASE/admin/llm/status
② 更换模型 — 只需替换双引号内的模型部分再粘贴。
直接填入 HuggingFace 模型卡地址(https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF)或其 id(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}'
hf.co/ 前缀和量化等级(quant)会自动补上(默认为 Q4_K_M)。如需指定特定量化等级,可像 Qwen/Qwen2.5-7B-Instruct-GGUF:Q5_K_M 这样在后面写明。首次下载模型时需要数十秒到几分钟。
②-离线 — 不联网,用自己已有的 GGUF 文件更换。
像展会、咨询现场那样没有网络时,不从 HuggingFace 下载,而是注册已有的 GGUF 文件来更换。(区分规则:名称中带有像 组织/仓库 这样的斜杠就走 HuggingFace 在线,不带斜杠的简单名称则为本地模型。)
请逐行复制粘贴。把 mymodel 处换成你想要的名称,mymodel.gguf 处换成实际文件名:
podman cp ./mymodel.gguf naia-omni:/app/models/mymodel.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/mymodel.gguf\n" > /tmp/Modelfile && ollama create mymodel -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"mymodel:latest","pull":false}'
⚠️ 自行转换 / 合并的 GGUF 可能缺少聊天模板,导致回答语无伦次或被截断。这种情况下,请在第 2 步的 Modelfile 中加入对应模型系列的聊天模板(
TEMPLATE)和停止标记(PARAMETER stop)后再注册——开发者详情见 [参考实现 §7]。(HuggingFace 官方 Instruct GGUF 通常已内置,可直接使用。)
③ 恢复为默认模型:
curl -s -X POST $BASE/admin/llm/restore
如果是共享 / 自助终端机(运维者设置了
NAIA_ADMIN_KEY),请在上述每条命令中加上-H "Authorization: Bearer 你设定的密码"。个人订阅者无需如此。
更换之后,naia-os 等应用仍然用同一个地址直接连接即可(无需重新连接)。如果希望重启或更新后也继续以该模型启动,请在启动容器时用 -e NAIA_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct-GGUF 指定默认模型。
6.2 更新到新版本
有新版本时,只更换镜像(版本),订阅与设置保持不变。 首次启动新版本时,容器会通过互联网自动重新认证(沿用现有订阅与设备——无需手动重新输入密钥)。因此更新时必须联网。
podman pull ghcr.io/nextain/naia-0.9-omni-24g:latest # 拉取最新版本
podman stop naia-omni && podman rm naia-omni # 仅清理容器(见下方注意)
# 请原样再次执行首次安装时使用的运行命令——只要挂载同一个许可证卷即可。
⚠️ 更新时请勿点击“设备解除(release)”。 解除仅用于把当前电脑迁移到另一台电脑。若在更新时误做解除,就得从头重新认证。更新时只要保留许可证卷,订阅和设备注册就会保持不变。
之前已认证的用户只需照上面拉取最新版本并重新启动,即可平滑过渡到支持更换模型的新版本(认证保持)。若要精确指定某个版本,可用 :0.91 这样的版本号代替 :latest。
7. 另请参阅
- 参考实现 / 示例代码(开源):naia-os 的语音客户端
shell/src/lib/voice/(Apache 2.0)— 包含与此 API 通信的实际客户端(naia-omni.ts)以及情绪 / 韵律处理 (emotion-tags.ts;表情映射与稳健提取见vrm/expression.ts)。 把它用作测试新模型和构建 Tauri 应用的起点。在 4.3 实时演示中实时试用。 - 阵容与定价:4.1 模型定价
- 云端(计划中):4.6 在线