Amazon Nova Sonic 的端到端实时语音对话解决方案有点东西啊

111 阅读9分钟

在当下这个 AI 技术风靡的时代,似乎不把产品“AI 化”就显得有些落伍。可是,如果只是为了“降本增效”而生硬地植入 AI ,反而会让体验大打折扣。

前几天我在某猫超市的在线客服咨询一款路由器。我只想了解一下我选的这款是不是包含两个路由器(我怕选错)。其实客服只要回答“是”或者“不是”就行了。可对方(AI客服)查商品信息硬是查了四五分钟,给我生成了一大坨冗长解释,还死活不肯转人工客服。整个过程不仅慢,还极度不贴心,甚至越聊越生气。

01.png

文字对话客服已经如此糟糕,体验过语音客服的工友恐怕会更崩溃。

AI 都火了好几年,生成音乐、数字人的 AI 应用都层出不穷,搞个靠谱的AI客服出来就这么难吗?我不信!

在网上查了一下,发现亚马逊云科技推出了 Amazon Nova Sonic,我体验了一下感觉它给出的对话语音挺真实的(有真人对话的感觉了),完全不存在传统语音系统的高延迟、低保真和对话不连贯等问题。

Amazon Nova Sonic 和传统智能客服有什么区别?

除了在产品能力上和传统的智能客服拉开好几条街的差距外,我还查了下 Amazon Nova Sonic 的官方资料,发现 Amazon Nova Sonic 在开发对接部署上也有很大优势。

  • 一体化模型:Amazon Nova Sonic 将自动语音识别(ASR)与文本到语音(TTS)合成为同一模型,无需分别调用不同服务,也无需自行编排数据流。

  • 部署平台: 作为 Amazon Bedrock 上的一款基础模型,Amazon Nova Sonic 可在控制台中一键启用,或通过 API 直接集成到应用中。

  • 实时流式 API:采用 HTTP/2 双向流式接口 InvokeModelWithBidirectionalStream,在同一连接里接收用户音频输入并实时返回合成音频输出,实现“你说我接”的自然对话。

上手实操一下

在实操之前我先把官方文档贴出来:docs.aws.amazon.com/nova/latest…

它支持 C++JavaJavaScriptKotlinRubyRustSwitfPython 等编程语言对接。

我用 Python 演示一下如何使用 Amazon Nova Sonic 的双向流式 API(会话初始化、系统提示配置、音频流的发送与接收等核心步骤)。

import os, json, base64, asyncio
from aws_sdk_bedrock_runtime.client import BedrockRuntimeClient, InvokeModelWithBidirectionalStreamOperationInput
from aws_sdk_bedrock_runtime.models import InvokeModelWithBidirectionalStreamInputChunk, BidirectionalInputPayloadPart

# 创建 Bedrock 客户端(需要设置 AWS 凭证环境变量或其它认证方式)
client = BedrockRuntimeClient(region="us-east-1")

async def run_nova_sonic_session():
    # 启动双向流会话,指定模型ID
    stream = await client.invoke_model_with_bidirectional_stream(
        InvokeModelWithBidirectionalStreamOperationInput(model_id='amazon.nova-sonic-v1:0')
    )
    is_active = True

    # 1) 发送会话开始事件(可配置推理参数,例如 maxTokens、topP、temperature 等)
    session_start = {
        "event": {
            "sessionStart": {
                "inferenceConfiguration": {
                    "maxTokens": 1024,
                    "topP": 0.9,
                    "temperature": 0.7
                }
            }
        }
    }
    await stream.send(json.dumps(session_start).encode('utf-8'))

    # 2) 发送提示开始事件(配置语音输出格式,如采样率、声道、音色等)
    prompt_start = {
        "event": {
            "promptStart": {
                "promptName": "chatPrompt1",
                "audioOutputConfiguration": {
                    "mediaType": "audio/lpcm",
                    "sampleRateHertz": 24000,
                    "sampleSizeBits": 16,
                    "channelCount": 1,
                    "voiceId": "matthew",  # 选择语音模型:例如 "matthew"
                    "encoding": "base64",
                    "audioType": "SPEECH"
                }
            }
        }
    }
    await stream.send(json.dumps(prompt_start).encode('utf-8'))

    # 3) 发送系统提示事件(定义对话角色和初始指令)
    content_start = {
        "event": {
            "contentStart": {
                "promptName": "chatPrompt1",
                "contentName": "content1",
                "type": "TEXT",
                "interactive": True,
                "role": "SYSTEM",
                "textInputConfiguration": {"mediaType": "text/plain"}
            }
        }
    }
    await stream.send(json.dumps(content_start).encode('utf-8'))

    # 发送系统提示内容,例如设定助手语气或规则
    system_prompt = "你是一个友好的助手,和用户进行实时的语音对话。请以简短的句子回复。"
    text_input_event = {
        "event": {
            "textInput": {
                "promptName": "chatPrompt1",
                "contentName": "content1",
                "content": system_prompt
            }
        }
    }
    await stream.send(json.dumps(text_input_event).encode('utf-8'))

    # 结束系统提示内容事件
    content_end = {
        "event": {
            "contentEnd": {
                "promptName": "chatPrompt1",
                "contentName": "content1"
            }
        }
    }
    await stream.send(json.dumps(content_end).encode('utf-8'))

    # 4) 开始异步处理模型返回的结果
    async def process_responses():
        nonlocal is_active
        while is_active:
            output = await stream.await_output()
            result = await output[1].receive()
            if result.value and result.value.bytes_:
                event_json = json.loads(result.value.bytes_.decode('utf-8')).get("event", {})
                
                # 处理文本输出事件
                if "textOutput" in event_json:
                    text = event_json["textOutput"]["content"]
                    role = event_json.get("role", "")
                    print(f"{role}: {text}")
                
                # 处理音频输出事件
                if "audioOutput" in event_json:
                    audio_content = event_json["audioOutput"]["content"]
                    audio_bytes = base64.b64decode(audio_content)
                    # 将 audio_bytes 放到播放队列或直接播放
                    # play_audio(audio_bytes)  # 用户可调用播放接口
    # 开启异步任务处理响应
    asyncio.create_task(process_responses())

    # 5) 模拟音频输入循环(此处仅示例发送一段已有音频)
    # 在真实场景中,可以从麦克风读取音频并分块发送
    dummy_audio_chunk = b'\x00\x01...'  # 示例原始PCM数据
    audio_event = {
        "event": {
            "audioInput": {
                "promptName": "chatPrompt1",
                "contentName": "content1",
                "audioChunk": {
                    "audioContent": base64.b64encode(dummy_audio_chunk).decode('utf-8')
                }
            }
        }
    }
    await stream.send(json.dumps(audio_event).encode('utf-8'))

    # (发送更多音频块...)

    # 6) 结束音频输入流
    audio_end = {
        "event": {
            "audioInputEnded": {
                "promptName": "chatPrompt1",
                "contentName": "content1"
            }
        }
    }
    await stream.send(json.dumps(audio_end).encode('utf-8'))

    # 停止会话
    is_active = False
    await stream.await_close()

# 运行以上会话函数
asyncio.run(run_nova_sonic_session())

上面的代码演示了使用 Amazon Nova Sonic 的基本流程:创建 Bedrock 客户端、启动双向流式会话、发送 sessionStart、promptStart 和系统提示 (SYSTEM) 等事件。然后通过音频事件向模型发送用户语音输入,并通过响应处理实时接收模型生成的文字和音频输出。

你也可根据需要自定义模型参数、声音类型及系统提示内容来控制对话风格。

Amazon Nova Sonic 是怎么设计的?

初步了解完 Amazon Nova Sonic 的使用方法,接下来聊聊 Amazon Nova Sonic 是怎么设计的。

要实现双向流 API 功能;支持实时、低延迟的多轮对话;说话自然,还能随时打断它并且不会丢失上下文。要做到这些,Amazon Nova Sonic 给出了这个方案。

先看看这个流程图。

02.png

整个交互过程围绕“JSON 事件”展开。如果上图看上去有点复杂,那我整个表格讲解一下(从上往下看)。

事件类型作用
sessionStart初始化会话,传入推理配置(诸如 maxTokenstemperature
promptStart设置提示名称和音频输出格式(采样率、声道、编码、voiceId
contentStart/textInput/contentEnd发送系统或用户文本提示,与后续音频输入对应
audioInput分片发送用户的 PCM/Opus 音频(Base64 编码)
audioInputEnded标记音频输入流结束
textOutput模型生成的文字响应
audioOutput模型合成的音频分片(Base64 编码)
toolUse模型发起的函数调用或知识检索指令

简单来说,客户端与 Nova Sonic 模型通过 HTTP/2 建立一个持久的、双向的流式连接,不再是传统的“请求—响应”模式,而是在同一连接上同时传输输入和输出数据。在该连接中,客户端可以连续不断地推送用户音频分片,模型也可以并发地返回文本、工具调用指令和合成音频,真正做到“边说边听、边听边说”。

其中,核心阶段有3个。

  1. 会话初始化(Session Initialization) 客户端打开双向流连接后,首先发送 sessionStartpromptStart 等配置事件,通知模型如何进行推理以及如何输出音频。
  2. 音频流上传(Audio Streaming) 当用户开始说话时,前端将采集到的音频分片编码为 Base64,并封装在 audioInput 事件中,通过流式连接不断发送给模型。模型端会实时对这些音频进行语音识别和语义理解。
  3. 响应流下发(Response Streaming) 随着音频输入的到达,模型会即时触发三类输出事件:
    • textOutput:将用户的话转写为文字,方便前端展示或进一步处理;
    • audioOutput:以分片形式返回合成的 PCM 音频数据,前端可立即播放;
    • toolUse:如果触发了函数调用或知识检索,会下发相应指令,前端或后端可根据指令调用外部 API,再将结果回传模型继续对话。

畅想未来

AI语音助手有哪些应用场景,最容易想到的肯定是智能语音客服和销售,将产品相关资料准备好,打造成一个企业级的知识库,加上 Amazon Nova Sonic 的加持,做出一个靠谱的智能语音客服就是顺水推舟的事了。

除了智能语音客服外,教育行业应该也能革新。先不说教学内容,我觉得光正确发音这事都已经不是人人都能做到。我是在粤语地区看着 TVB 长大的,记得小时候父亲和我说,要讲好粤语就多看TVB,尤其是看 TVB 的新闻。其实小时候并不懂 TVB 里播出的粤语和其他讲粤语的人有什么区别,感觉就口音上面有点不一样吧。上学后接触的人多了,发现除了口音之外,还有一些人是有“懒音”的,就是那种发音发一半,把后半段音节吞掉了,听着很难受。后来才知道,要讲好一门语言,首先就要听清这门语言的发音。不管是粤语、普通话还是其他语言都是如此。在这点上,我觉得 Amazon Nova Sonic 出来的效果挺满意的。

从我自身的角度出发,除了写文章之外,其实我还挺想做音频内容的。但正如前面所说,我是在讲粤语的环境中长大的,导致我讲的普通话是“港普”,虽然不至于讲话讲不清,但对比起正常的播客,我的普通话会显得很奇怪。而 Amazon Nova Sonic 对于像我这种情况的人来说,算得上量身订造了。除了能解决我“港普”问题外,还解决了我想做对话类型的播客这个痛点(因为找不到搭子和我一起做😭)。

其他场景比如小说“真人朗读”、智能助手等方面就不展开聊了,这些都属于语音生成领域,对于 Amazon Nova Sonic 来说都是撒撒水的事。

Amazon Nova Sonic 通过统一的端到端模型、实时双向流式 API、上下文长记忆与自适应语调等创新能力,彻底简化了语音对话系统的架构复杂度,同时大幅提升了交互的自然性和响应速度。无论是教育、客服、内容创作,还是智能助手与有声读物,Amazon Nova Sonic 都为开发者提供了一个通用、高效且可扩展的语音交互基石。有兴趣的工友可以在 Amazon Bedrock 中启用 Nova Sonic 功能,去体验一下它的能力。