AI 助手
扣子 AI 帮助与支持
你好,我是 扣子 文档问答助手 🎉 你在阅读当前文档的过程中,无论对文档概念的解释,还是文档内容方面的疑问,都可以随时向我提问,我会全力为你解答
推荐问题
扣子 3.0 都有什么新特性?
扣子和扣子编程有什么区别?
扣子如何收费?
文档反馈
低代码项目
动态
快速开始
智能体
工作流
应用
资源
发布
模型
多人协作
推广与变现

基于 WebSocket OpenAPI 实现音频通话

除了基于 WebRTC 的实时通信方案外,扣子编程智能语音还推出了 WebSocket OpenAPI 方案,用于实现用户与智能体之间的实时语音通话。该方案通过 WebSocket 协议提供高效、灵活的语音交互能力,适用于多种应用场景。

注意事项

WebSocket OpenAPI 支持的音频编码格式如下:

  • 上行:输入音频支持 PCM、G711A、G711U、OPUS,上传语音文件支持 WAV 或 OGG 格式,默认格式为 WAV。
  • 下行:输出音频支持 PCM、G711A、G711U、OPUS格式,默认为采样率 24000 的 PCM 片段。

准备工作

在开始集成 WebSocket OpenAPI 之前,你需要先完成以下准备工作。

操作

说明

发布智能体

已成功搭建并发布智能体为 API 服务。搭建步骤可参考搭建低延时语音助手,发布步骤请参见发布为 API 服务

获取访问密钥

获取访问密钥,用于身份认证与鉴权。

  • 体验或调试场景:建议生成短期的个人访问令牌(PAT),以快速完成 语音通话的整体流程。个人访问令牌的获取方法请参见添加个人访问令牌
  • 线上环境:在线上环境中,应使用服务访问令牌(SAT)或 OAuth 鉴权方案。
    • SAT 鉴权:操作更简单,能够有效简化授权流程,适合需要长期稳定访问且无需进行会话隔离的场景。但请注意,仅企业版(企业标准版、企业旗舰版)支持使用 SAT 鉴权。详细说明可参考添加服务访问令牌
    • OAuth 鉴权:​如果需要支持渠道用户访问智能体,或实现会话隔离,即智能体不同账号的消息内容需要互相隔离时,你需要使用 OAuth JWT 鉴权方式。OAuth 鉴权方案的详细说明请参见OAuth 应用管理

说明

扣子编程 SDK 封装了多种鉴权方式,能够有效简化鉴权流程,你可以参考鉴权示例代码实现不同方式的 OAuth 认证,以获取和管理访问扣子编程 API 所需的令牌。

示例项目源码

你可以通过扣子编程 Python SDK 示例代码快速了解调用智能语音 WebSocket OpenAPI 实现语音通话的整体流程,包括语音输入、语音输出以及聊天对话等。
此外,参考 websockets_chat_realtime_gui.py 文件,你可以快速实现包含图形化界面的智能语音应用。通过 WebSocket 与扣子编程 API 通信,快速体验智能语音的 Demo 效果。

实现语音通话

步骤一 :建立 WebSocket 连接

发起 HTTP 请求时,在请求头(Header)中添加Authorization信息。Authorization的取值固定为Bearer $Access_Token,用于扣子编程 OpenAPI 鉴权的访问密钥。将您在准备工作中获取的访问密钥替换掉 $Access_Token 后再发起请求。

说明

浏览器不支持在 WebSocket 连接中设置 Header。如果需要通过浏览器发起 WebSocket 建连请求,可以将Authorization 参数放到 URL 的查询参数中。例如,与语音识别 API 建立连接的 URL 格式如下:

wss://ws.coze.cn/v1/audio/transcriptions?authorization=Bearer xxx

以下是建立 WebSocket 连接的示例代码:
本文以双向流式语音对话为例,语音识别和语音合成的 WebSocket 连接建立方式相同,只需要将示例代码中的 url 路径替换为对应的 API 路径即可,语音识别和语音合成的示例代码请参见双向流式语音识别双向流式语音合成

import WebSocket from 'ws';

# 双向流式语音对话
const url = `wss://ws.coze.cn/v1/chat?bot_id=73791654286875***&authorization=Bearer pat_OYDacMzM3WyOWV3Dtj2bHRMymzxP****`;

# 如果是语音识别,需要将 url替换为如下地址 
# url = `wss://ws.coze.cn/v1/audio/transcriptions?authorization=Bearer pat_OYDacMzM3WyOWV3Dtj2bHRMymzxP****`

const ws = new WebSocket(url);

ws.on('open', function open() {
  console.log('Connected to server.');
});

ws.on('message', function incoming(message) {
  console.log(JSON.parse(message.toString()));
});

# example requires websocket-client library:
# pip install websocket-client

import os
import json
import websocket

ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN")
# 流式语音对话
url = "wss://ws.coze.cn/v1/chat?bot_id=73791654286875***"
# 如果是语音识别,需要将 url替换为如下地址 
# url = "wss://ws.coze.cn/v1/audio/transcriptions"
headers = [
    "Authorization: Bearer " + ACCESS_TOKEN
]

def on_open(ws):
    print("Connected to server.")

def on_message(ws, message):
    data = json.loads(message)
    print("Received event:", json.dumps(data, indent=2))

ws = websocket.WebSocketApp(
    url,
    header=headers,
    on_open=on_open,
    on_message=on_message,
)

ws.run_forever()

# 双向流式语音对话
const url = `wss://ws.coze.cn/v1/chat?bot_id=73791654286875***&authorization=Bearer pat_OYDacMzM3WyOWV3Dtj2bHRMymzxP****`;
# 如果是语音识别,需要将 url 替换为如下地址 
# url = `wss://ws.coze.cn/v1/audio/transcriptions?authorization=Bearer pat_OYDacMzM3WyOWV3Dtj2bHRMymzxP****`;
const ws = new WebSocket(url);

ws.addEventListener('open', function () {
  console.log('Connected to server.');
});

ws.addEventListener('message', function (message) {
  console.log(JSON.parse(message.data.toString()));
});

步骤二:发送和接收事件

通过 WebSocket 与扣子编程智能体进行实时语音交互时,需要通过 WebSocket 接口发送和接收消息。成功连接后,客户端可以发送和接收代表文本、音频、配置更新等的事件。客户端可以发送的事件消息以及从服务器接收的事件消息列表请参见双向流式语音识别事件双向流式语音合成事件双向流式对话上行事件
发送和接收事件的示例代码如下:

# To send a client event, serialize a dictionary to JSON
# of the proper event type
def on_open(ws):
    print("Connected to server.")
    
    data = {
        "role": "user",
        "content_type": "text",
        "content": "你好呀"
    }
    event = {
        "event_type": "conversation.message.create",
        "data":  data
    }
    ws.send(json.dumps(event))

# Receiving messages will require parsing message payloads
# from JSON
def on_message(ws, message):
    data = json.loads(message)
    print("Received event:", json.dumps(data, indent=2))

核心功能

双向流式语音识别

扣子编程提供流式语音识别 WebSocket OpenAPI,可以将指定的音频判断转为文字,支持识别中英文双语种。详细的接口信息请参见双向流式语音识别,相关的事件详细信息请参见双向流式语音识别事件

双向流式语音合成

扣子编程提供流式语音合成 WebSocket OpenAPI,可以将文字信息转为指定音色的语音片段。详细的接口信息请参见双向流式语音合成,相关事件详细信息请参见双向流式语音合成事件

双向流式语音对话

扣子编程提供流式语音对话 WebSocket OpenAPI,向指定的智能体发起语音对话。详细的接口信息请参见双向流式语音对话,相关事件详细信息请参见双向流式对话上行事件

WebSocket 事件概述

使用扣子编程提供的 WebSocket OpenAPI,你可以实现双向流式语音识别、双向流式语音合成和双向流式语音对话。每个场景中的配置均通过上行事件完成,配置成功后服务端会返回下行事件通知配置结果。通过 WebSocket 事件,你可以使用 WebSocket 协议实现智能语音对话的各种典型场景。

事件类型

智能语音 WebSocket 事件包括上行事件和下行事件。每个事件有 ID 和 EventType,通过 EventType 可以区分具体的事件类型,每个事件类型对应的 Payload 在 Data 中,开发者可以按需去提取需要的内容。

  • 上行事件:设备端上报给服务端的事件。应用程序需要根据扣子编程提供的事件结构,在触发事件时填充字段内容并上报事件。
  • 下行事件:服务端下发给设备端的事件,应用程序需要解析下行事件,并根据业务需求进行下一步操作。

注意事项

  • 每个上行事件 ID 建议不要重复,故障排查场景下便于定位问题。
  • 每个事件有 ID 和 EventType,通过 EventType 可以区分具体的事件类型,每个事件类型对应的 Payload 在 Data 中,开发者可以按需去提取需要的内容。

公共参数

智能语音 WebSocket 事件的公共参数如下:

参数名称

类型

描述

id

String

事件 ID,也就是事件的唯一标识。由客户端或服务端生成,在故障排查场景下用于定位具体的事件,便于排查问题。

event_type

String

事件的类型。

data

JSON

事件的详细信息,其中包含具体事件的业务字段。

常见问题

SSL/TLS 失败

问题排查
借助抓包工具来分析通信过程中的数据包。以下是详细的排查步骤:

  1. 检查 SSL 握手过程中客户端是否发送了 Client Hello 包。
    • 如果未发送 Client Hello 包,需要检查客户端是否存在配置错误、网络问题导致数据包丢失,或是客户端应用程序逻辑缺陷未能正确触发 SSL 握手过程。
    • 如果已发送 Client Hello 包,请执行下一步,检查是否收到了扣子编程返回的 Server Hello 的包。
  2. 检查是否收到了扣子编程返回的 Server Hello 包。
    • 如果未收到 Server Hello 包,从以下几个方面检查 Client Hello 包是否有问题:

      排查项

      说明

      检查 Cipher Suites

      检查 Client Hello 包中的 Cipher Suites 是否全部为扣子编程不支持的加密套件。扣子编程支持 TLS1.2 和 TLS1.3 版本,但出于安全考虑,加密套件不支持以下几种:

      • tls.TLS_RSA_WITH_AES_128_CBC_SHA,
      • tls.TLS_RSA_WITH_AES_256_CBC_SHA,
      • tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
      • tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
      • tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
      • tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

      Image

      检查 Random 中的时间戳

      时间戳相差过大可能会影响握手过程。客户端需确保其系统时间准确,与服务器时间偏差在合理范围内。若客户端系统时间设置错误,如相差数小时甚至更久,可能导致服务器认为客户端发送的 Client Hello 包无效,从而不返回 Server Hello 包。建议客户端定期同步系统时间,可使用网络时间协议(NTP)等工具来校准时间。
      Image

      检查 Extension 中的 server_name(SNI)

      Client Hello 包的 Extension 中必须带有 server_name(SNI)。若缺失 SNI 扩展,服务器可能无法正确识别客户端请求的目标虚拟主机,进而拒绝握手。客户端需检查其 SSL/TLS 配置,确保在发起握手时正确设置了 SNI 扩展,明确指定目标服务器的域名。
      Image

      检查 Extension 中的 signature_algorithms

      signature_algorithms 扩展是 SSL/TLS 握手过程中用于协商签名算法的关键部分。若该扩展缺失或不正确,可能导致服务器无法与客户端协商出合适的签名算法,从而使握手失败。客户端需确保其支持的签名算法与扣子编程要求一致,并正确在 Client Hello 包中携带 signature_algorithms 扩展。

      检查 TLS1.3 相关扩展

      若使用的是 TLS1.3,Client Hello 包的 Extension 中还需带有 supported_groups 和 key_share 扩展。supported_groups 扩展用于协商支持的椭圆曲线群组,key_share 扩展则用于携带客户端的密钥共享信息。若缺少这些扩展,TLS1.3 握手将无法正常进行。

    • 如果已收到 Server Hello 包,说明 SSL/TLS 握手已初步成功。请执行下一步检查 HTTP 包。

  3. 检查 HTTP 包。
    1. 检查 HTTP 格式是否符合规范。包括正确的请求行(如 GET / HTTP/1.1)、完整的请求头以及必要的请求体(对于 POST 请求等)。格式错误的 HTTP 请求可能导致服务器无法正确解析,从而引发通信失败。
    2. 检查 HTTP 请求头中是否正确携带了 Authorization: {Access_Token} 字段。若缺失该字段或字段值错误,服务器可能因无法验证客户端身份而拒绝处理请求。正确示例:authorization=Bearer pat_OYDacMzM3WyOWV3Dtj2bHRMymzxP****
  4. 联系扣子技术支持协助排查。
    若经过上述排查仍未定位到问题,建议及时联系扣子编程团队协助进一步排查。联系扣子编程技术支持时,建议提供详细的排查过程记录、抓包文件(如有)、客户端相关配置信息以及出现问题时的上下文环境描述等,以便扣子编程团队更高效地定位问题并提供针对性的解决方案。

WebSocket 连接关闭

可能原因
在 WebSocket 通信过程中,若出现连接关闭的情况,可能是由以下原因导致的:

  • 心跳机制超时
    扣子编程每 15 秒会下发一个 ping 帧给客户端,若客户端连续四次未响应该 ping 帧,即未返回 pong 帧,扣子编程会认为客户端已下线,从而关闭 WebSocket 连接。
  • 账号欠费或资源不足
    在连接过程中,若建立连接使用的 Token 所属账号欠费,扣子编程会先下发一个 error 事件,data 中的 code 为 4027(账户欠费)或 4028(积分不足),随后关闭 WebSocket 连接。客户端需及时处理该 error 事件。
  • 接收数据失败
    扣子编程从 WebSocket 中接收数据失败也会导致连接关闭。例如,保留位读到了非 0 的数据、读取数据超时,或读到了 WebSocket: close 100x(err msg)等错误情况。这些情况可能表明客户端在数据接收过程中存在解析错误或网络传输问题。客户端需仔细检查 WebSocket 数据接收逻辑,确保正确处理各种帧类型(如文本帧、二进制帧等),同时合理设置数据读取超时时间,避免因超时导致连接异常关闭。

问题排查

  1. 检查心跳机制。
    检查客户端是否正确响应了扣子编程发送的 ping 帧。确保客户端能够及时接收并返回 pong 帧,以维持连接的活跃状态。
  2. 检查客户端对 error 事件的处理逻辑。
    客户端在接收到 error 事件后不能立即关闭连接。当收到 error 事件时,客户端需要查看 error 事件中具体的错误描述,例如:若建立连接时 AccessToken 无效,扣子编程会返回authentication is invalid错误。根据错误描述,客户端应采取相应的措施,如提示用户、记录日志或重新尝试连接等。
  3. 检查网络情况。
    检查客户端与扣子编程之间的网络连接是否稳定。客户端可以设置定期发送 ping 帧给扣子编程的心跳包,例如每 15 秒发送一次 ping 帧,以确保连接的持续性和稳定性。