Qwen-Audio-3.0-Realtime 发布:如何把低延迟语音、推理与工具调用接入真实业务

2026-07-15 30 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

实时语音模型最难处理的矛盾,不是“能不能听懂”,而是如何在用户仍然期待即时反馈时完成推理、调用工具并自然开口。Qwen-Audio-3.0-Realtime 的发布正面回应了这个问题:公告将升级集中在推理能力、Agent 工具调用、共情对话和双工交互流畅度四条主线上,并提供偏重推理的 Plus 版本与偏重速度的 Flash 版本。

这意味着开发者面对的不再只是一个语音转文字接口,而是一条持续接收音频、判断说话轮次、执行任务并流式返回语音的实时链路。

“实时”是整条链路的预算

端到端响应时间通常由音频采集、网络传输、模型首包、工具执行和音频播放共同决定。模型响应很快,并不代表用户一定觉得快:如果客户端积攒数秒音频才上传,或者工具接口需要两秒才能返回,交互仍会明显停顿。

可以把一次响应拆成以下指标:

指标 含义 建议观察方式
音频上行延迟 采集到服务端收到音频的时间 在音频块中记录客户端时间戳
首事件延迟 发出音频到收到首个模型事件 网关统一打点
首音频延迟 用户停顿到扬声器开始播放 客户端播放回调
工具耗时 模型请求工具到工具返回 按工具名称统计 P50/P95
打断恢复时间 用户插话到旧音频停止 播放器和会话事件联合记录

工程上应优先使用较小的音频块持续上传,并让文本、工具调用和音频结果都采用流式事件。音频块过大会增加等待时间,过小则会提高网络包、序列化和调度开销,需要根据移动网络与桌面网络分别压测。

Plus 和 Flash 不必二选一

公告给出的两个版本有清晰侧重:Plus 强调更强推理,Flash 强调更快速度。实际系统可以按照任务风险和复杂度路由,而不是让全部会话固定使用同一版本。

客服中的欢迎语、意图确认、订单号复述等步骤通常更看重速度;退款判断、课程讲解、多约束行程规划则更需要推理能力。可以这样实践一个简单路由器:

from dataclasses import dataclass

@dataclass
class Turn:
    text: str
    tool_required: bool = False
    high_risk: bool = False


def choose_model(turn: Turn) -> str:
    complex_markers = ("比较", "分析", "为什么", "制定计划", "分步骤")
    needs_reasoning = any(word in turn.text for word in complex_markers)

    if turn.high_risk or turn.tool_required or needs_reasoning:
        return "qwen-audio-3.0-realtime-plus"
    return "qwen-audio-3.0-realtime-flash"


if __name__ == "__main__":
    turns = [
        Turn("帮我查一下订单状态", tool_required=True),
        Turn("你好,请介绍一下课程"),
        Turn("比较两个退款方案的影响", high_risk=True),
    ]
    for turn in turns:
        print(choose_model(turn), turn.text)

这里的模型标识仅用于展示路由结构,应替换成实际服务提供的模型名称。更成熟的路由还应加入用户等级、预算、上下文长度和降级策略,并记录路由原因,避免线上问题无法追踪。

双工交互不是同时收发那么简单

自然的实时对话必须允许用户在模型说话时插话。客户端检测到用户开始说话后,应立即停止当前播放、清空尚未播放的音频,并向服务端发送取消当前响应的事件。否则模型虽然支持双工,用户仍会听到两个声音重叠。

由于公告摘要没有给出正式接口协议,下面是一个可改造的 WebSocket 客户端骨架,事件名和地址均为工程假设,不代表官方 API。运行前安装依赖,并把 REALTIME_URLAPI_KEY、模型名和消息结构替换为正式文档中的定义:

python -m pip install websockets
export REALTIME_URL='wss://your-realtime-endpoint.example/v1/audio'
export API_KEY='replace-me'
python realtime_client.py
# realtime_client.py
import asyncio
import base64
import json
import os
import sys

import websockets

CHUNK_BYTES = 3200  # 示例:按实际采样率和服务端限制调整


async def send_audio(ws, pcm_path: str) -> None:
    with open(pcm_path, "rb") as audio:
        while chunk := audio.read(CHUNK_BYTES):
            await ws.send(json.dumps({
                "type": "input_audio.append",
                "audio": base64.b64encode(chunk).decode("ascii"),
            }))
            await asyncio.sleep(0.1)
    await ws.send(json.dumps({"type": "input_audio.commit"}))


async def receive_events(ws) -> None:
    async for raw in ws:
        event = json.loads(raw)
        event_type = event.get("type")

        if event_type == "response.text.delta":
            print(event.get("text", ""), end="", flush=True)
        elif event_type == "response.audio.delta":
            audio = base64.b64decode(event["audio"])
            sys.stdout.buffer.write(audio)
            sys.stdout.buffer.flush()
        elif event_type == "response.tool_call":
            print("\n工具请求:", event, file=sys.stderr)
        elif event_type == "error":
            raise RuntimeError(event)


async def main() -> None:
    url = os.environ["REALTIME_URL"]
    api_key = os.environ["API_KEY"]

    async with websockets.connect(
        url,
        additional_headers={"Authorization": f"Bearer {api_key}"},
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.update",
            "model": "qwen-audio-3.0-realtime-flash",
            "instructions": "回答简洁;执行外部操作前先复述并确认。",
            "audio_format": "pcm16",
        }))
        await asyncio.gather(
            send_audio(ws, "question.pcm"),
            receive_events(ws),
        )


if __name__ == "__main__":
    asyncio.run(main())

这个骨架刻意把发送和接收放进两个并发协程,以体现全双工连接。生产环境还要补上麦克风采集、扬声器播放队列、断线重连、心跳、会话恢复和响应取消,不能直接把二进制音频写到标准输出当作播放器。

工具调用要把确认、幂等和超时放在模型之外

语音 Agent 可以查订单、预约课程或修改服务,但模型发出工具请求并不等于操作可以无条件执行。涉及付款、退款、删除和身份信息修改时,服务端应校验参数,并要求用户明确确认。

建议为每次工具执行设置:

  • 唯一幂等键,防止重连或重复事件造成二次下单。
  • 明确超时,超时后让模型说明仍在处理中,而不是持续沉默。
  • 参数白名单与权限检查,不能只依赖提示词约束。
  • 可审计日志,保存会话 ID、工具名、参数摘要、确认状态和结果。
  • 语音回读,高风险操作执行前复述金额、对象和影响范围。

共情表达同样存在边界。客服和教育场景可以让模型识别用户的焦虑或困惑,并调整语气,但不应把语言上的共情包装成专业医疗、心理或法律判断。

上线前按场景验收

接入这类模型时,版本选择只是开始。团队应使用真实噪声、口音、弱网和频繁插话构建测试集,分别记录首音频延迟、任务成功率、错误工具调用率和打断成功率。

一个稳妥的采用顺序是:先让 Flash 承担低风险、高频、短回答任务,再把复杂问答路由到 Plus;工具调用从只读查询开始,确认审计链路稳定后再开放写操作。这样既能利用实时交互带来的体验改善,也能把成本、延迟和业务风险控制在可观测范围内。


相关推荐