全部產品
Search
文件中心

Alibaba Cloud Model Studio:接入實時語音對話模型

更新時間:Nov 04, 2026

讓 Agent 工具自動建立並執行語音客戶端,接入 Token Plan 的實時語音對話模型。

支援的模型

模型能力
qwen-audio-3.0-realtime-plus實時語音對話

更多模型以 Token Plan 支援的實時語音對話模型 為準。

前提

  • 已訂閱 Token Plan。個人版在 我的訂閱(個人版) 獲取以 sk-sp- 開頭的 API Key;團隊版在 成員管理 分配席位後獲取 API Key。
  • 本地已安裝 Python 3.9 及以上版本,並有可用的麥克風和揚聲器。

步驟一:將接入指令發給 Agent 工具

展開下面的接入指令,複製後發給目前使用的 Agent 工具。它會讀取已配置的 Token Plan Key、安裝依賴、建立 qwen_realtime_voice.py 並執行,不需要手動配置 Key 或撰寫 Python。

接入指令(含語音客戶端指令碼)

請在當前專案接入 Token Plan 實時語音對話模型 qwen-audio-3.0-realtime-plus:

1. 使用當前 Agent 工具已配置的 Token Plan API Key(優先讀取 TOKEN_PLAN_API_KEY,其次讀取 ANTHROPIC_AUTH_TOKEN)。如果兩者都沒有,提示我先在 Agent 工具中配置 Token Plan Key。不要向我顯示 Key,也不要寫入檔案或日誌。
2. 安裝依賴:pip install "websockets>=15" sounddevice certifi
3. 建立 qwen_realtime_voice.py,內容如下:

```python
#!/usr/bin/env python3
import asyncio
import base64
import json
import os
import queue
import ssl
import threading

import certifi
import sounddevice as sd
import websockets

API_KEY = os.environ.get("TOKEN_PLAN_API_KEY") or os.environ.get("ANTHROPIC_AUTH_TOKEN")
if not API_KEY:
    raise SystemExit("Token Plan API Key not found. Configure it in the Agent tool first.")
WS_URL = "wss://token-plan.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime?model=qwen-audio-3.0-realtime-plus"

play_queue = queue.Queue()


def play_audio():
    with sd.RawOutputStream(samplerate=24000, channels=1, dtype="int16") as stream:
        while True:
            chunk = play_queue.get()
            if chunk is None:
                break
            stream.write(chunk)


async def receive(ws):
    transcript = []
    async for message in ws:
        event = json.loads(message)
        event_type = event["type"]
        if event_type == "conversation.item.input_audio_transcription.completed":
            print(f"\n[你說] {event.get('transcript', '')}", flush=True)
        elif event_type == "response.audio.delta":
            play_queue.put(base64.b64decode(event["delta"]))
        elif event_type == "response.audio_transcript.delta":
            transcript.append(event.get("delta", ""))
        elif event_type == "response.audio_transcript.done":
            print(f"[AI] {''.join(transcript)}", flush=True)
            transcript.clear()
        elif event_type == "error":
            print(f"[錯誤] {event}", flush=True)


async def send_mic(ws):
    loop = asyncio.get_running_loop()
    audio_queue = asyncio.Queue()

    def callback(indata, frames, time_info, status):
        loop.call_soon_threadsafe(audio_queue.put_nowait, bytes(indata))

    with sd.RawInputStream(
        samplerate=16000,
        channels=1,
        dtype="int16",
        blocksize=1600,
        callback=callback,
    ):
        print("開始說話,按 Ctrl+C 結束。", flush=True)
        while True:
            chunk = await audio_queue.get()
            await ws.send(
                json.dumps(
                    {
                        "type": "input_audio_buffer.append",
                        "audio": base64.b64encode(chunk).decode(),
                    }
                )
            )


async def main():
    context = ssl.create_default_context(cafile=certifi.where())
    async with websockets.connect(
        WS_URL,
        ssl=context,
        additional_headers={"Authorization": f"Bearer {API_KEY}"},
        max_size=None,
    ) as ws:
        await ws.recv()
        await ws.send(
            json.dumps(
                {
                    "type": "session.update",
                    "session": {
                        "modalities": ["audio", "text"],
                        "voice": "longanqian",
                        "instructions": "你是語音助手,回答簡潔自然。",
                        "input_audio_format": "pcm",
                        "output_audio_format": "pcm",
                        "turn_detection": {
                            "type": "server_vad",
                            "threshold": 0.5,
                            "silence_duration_ms": 600,
                        },
                    },
                }
            )
        )
        threading.Thread(target=play_audio, daemon=True).start()
        await asyncio.gather(receive(ws), send_mic(ws))


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
    finally:
        play_queue.put(None)
```

4. 使用 Bash 工具執行 python3 qwen_realtime_voice.py,timeout 設為 600000 毫秒。
5. 啟動後提示「開始說話,按 Ctrl+C 結束」。

說明首次執行時,需要在系統設定中允許執行 Agent 工具的應用程式(如 Cursor、Terminal)存取麥克風,否則指令碼收不到聲音。建議使用耳機,避免揚聲器播放的語音被麥克風擷取,觸發模型打斷。

步驟二:開始語音對話

Agent 工具執行指令碼後,看到「開始說話」提示即可說話。終端顯示識別結果和模型回覆,揚聲器播放模型語音。按 Ctrl+C 結束會話。

說明Agent 工具負責安裝依賴、建立指令碼和啟動會話。語音資料在本地客戶端和 Token Plan 之間傳輸,不經過 Agent 工具的文字模型。

儲存為工具擴充

需要再次使用時,讓 Agent 工具將上面的指令儲存為目前工具的 Skill、Slash Command 或 Agent 配置,之後透過該工具的觸發方式啟動。常見工具的擴充機制和路徑如下:

工具擴充機制配置檔案路徑
Claude CodeSlash Command.claude/commands/voice-chat.md
CodexSkill~/.codex/skills/token-plan-voice/SKILL.md
Qwen CodeSkill~/.qwen/skills/voice-chat/SKILL.md
OpenCodeAgent.opencode/agents/voice-chat.md
OpenClawSkill~/.openclaw/workspace/skills/token-plan-voice/SKILL.md
Hermes AgentSkill~/.hermes/skills/media/voice-chat/SKILL.md
QoderSkill~/.qoder/skills/voice-chat/SKILL.md

限制

  • 外放時,模型語音可能被麥克風再次擷取並打斷,建議使用耳機。
  • Agent 工具的 Bash 呼叫有逾時限制。長時間對話建議將指令碼作為獨立程序執行,或封裝為 MCP server。
  • 當前方案只處理語音輸入和輸出。