帮你快速理解、总结文档立即下载
文档中心>媒体处理>其他说明文档>Websocket STS (智能实时对话)

Websocket STS (智能实时对话)

最近更新时间:2026-07-27 10:53:42

我的收藏
本协议描述客户端与语音对话服务 (Speech-to-Speech,下文简称 STS) 之间的实时通信规范。
LLM 模式 (默认,mode=llm):上行流式音频 → 实时语音识别 (ASR) → 大模型多轮对话 (LLM) → 语音合成 (TTS) → 下行流式音频
Simul 同传模式 (mode=simul):上行流式音频 → 实时语音识别 (ASR) → 文本翻译服务 (Translate) → 语音合成 (TTS) → 下行流式音频。
测试工具

1. 连接与鉴权

1.1 连接地址

URL 格式如下:
wss://mps.cloud.tencent.com/sts/v1/<appid>?{请求参数}
其中<appid>是腾讯云用户账号的唯一标识(UInt64),可以从控制台账号中心 > 账号信息 页面获得:

请求参数格式如下:
key1=value1&key2=value2...(key 和 value 都需要进行 urlencode)

1.2 URL 请求参数

鉴权参数(必填)

参数
类型
说明
secretId
string
云 API 访问密钥 ID。
signature
string
请求签名,算法见 1.3
timeStamp
int64
请求发起时的 UNIX 时间戳(秒)。
expired
int64
签名到期 UNIX 时间戳(秒),必须 > timeStamp> 当前时间
nonce
string
一次性随机串,防重放。

通用业务参数

下列参数在 mode=llm 与 mode=simul 下均生效,语义与默认值完全一致。
参数
类型
必填
默认值
说明
mode
string
llm
工作模式选择,可选 llm / simul;不传或非法值均按 llm 处理。
voice
string
音色 ID (两种模式都用它做 TTS)。
inputFormat
string
pcm
上行音频编码,当前仅支持 pcm。
inputSampleRate
int
16000
上行采样率,允许区间 [8000, 48000]。
outputFormat
string
pcm
下行音频编码,可选 pcm / mp3 / wav。
outputSampleRate
int
16000
下行采样率(Hz)。
lang
string
zh
源语言标识,例如 zh / en ;ASR 使用;LLM 模式下 TTS 语言与其保持一致。
timeoutSec
int
30
会话空闲超时(秒),取值区间 [1, 120]。
maxSpeakMs
int
15000
单句最长时长(毫秒), 0 或超过服务端上限时使用上限 (60000)。
resId
string
资源 ID, 用于区分计费。
说明:
voice 合成音色,可以使用 系统音色音色复刻

LLM 模式专属参数(仅在 mode=llm 下生效)

下列参数仅在 LLM 模式下解析并生效,在同传模式下即使传入也会被服务端忽略。
参数
类型
必填
默认值
说明
llmApiKey
string
自定义 LLM 服务的鉴权 API Key。留空则使用自定义大模型接入的配置。
maxHistoryRounds
int
50
单会话最多保留的对话轮数(一问一答 = 1 轮),上限100,超过将被截断。

同传模式专属参数(仅在 mode=simul 下生效)

下列参数仅在同传模式下解析并生效,在 LLM 模式下即使传入也会被服务端忽略。
参数
类型
必填
默认值
说明
dstLang
string
同传目标语言(BCP47 / ISO 639-1 短码),例如 en / zh / ja;缺失时握手直接失败并返回 4001。
同传模式下,源语言由通用参数 lang 指定 (默认 zh );TTS 语言/音色由通用参数 voice 决定,请自行选择与 dstLang 匹配的音色。
说明:
未列出的参数以服务端默认为准。

1.3 签名算法

签名沿用腾讯云 CAM 签名标准。客户端按下列规则生成 signature
1. CanonicalQueryString:取 URL 上signature 之外的所有 query 参数,按 key 字典序升序,对每个 value 做 URL 编码,以 & 拼接。
2. CanonicalRequest
HTTPRequestMethod:post
CanonicalURI:/sts/v1/{appid}
CanonicalQueryString:第 1 步结果
CanonicalHeaders:content-type:application/json; charset=utf-8\\nhost:mps.cloud.tencent.com\\n
SignedHeaders:content-type;host
HashedRequestPayload:sha256("")
3. 流程参考:签名生成
4. 把参与签名的 query 参数signature 一并附在 URL Query 上。
警告:
SecretKey 严禁出现在客户端 / 浏览器侧的明文环境。生产环境签名必须由接入方后端代签,再下发给前端用于连接。

2. 会话时序


说明:
下行音频,是否有空包,来标记语句结束,依赖音色背后的 tts 引擎,有些引擎不支持。

3. 上行消息

3.1 音频帧(ws.Binary

编码:16-bit 有符号 PCM,小端序 (Little Endian)。
声道:单声道。
采样率:与 inputSampleRate 一致。
分片大小:无固定要求,建议每 20–100 ms 发送一次以获得较低的端到端延迟。

3.2 控制信令(ws.Text,JSON)

{ "Command": "Finish" }
字段
类型
必填
说明
Command
string
目前仅支持 Finish
Finish:客户端主动通知本轮说话结束,服务端立即触发端点检测并提交最后一句 ASR。发送 Finish 后:
客户端应停止再发送任何 PCM 音频帧。
服务端会尽快跑完最后一句 ASR / LLM / TTS,随后下发 ProcessEof 关闭连接。
若下游长时间无响应,服务端将强制结束会话。

4. 下行消息

下行分两类通道:
通道
载体
用途
ws.Text
JSON
控制信令 / 识别文本 / 模型文本 / 结束通知。
ws.Binary
二进制
TTS 音频帧、句末边界标记。

4.1 通用外层结构

所有 Text 类通知均为如下 JSON:
{
"NotificationType": "<类型>",
"TaskId": "<会话 ID>",
"<对应字段>": { ... }
}
NotificationType
说明
对应字段
Handshake
握手结果。
HandshakeResult
AsrResult
语音识别结果。
AsrResult
LLMResult
模型回复文本。
LLMResult
ProcessEof
会话结束 / 错误。
ProcessEofInfo

4.2 Handshake

客户端连接建立后,服务端返回的第一条消息即为 Handshake只有 Code=0 才表示可继续发音频,否则连接将被服务端关闭。
{
"NotificationType": "Handshake",
"TaskId": "app123-xxxxxxxx",
"HandshakeResult": {
"Code": 0,
"Message": "success",
"Format": "pcm",
"SampleRate": 16000,
"Channels": 1,
"BitDepth": 16,
"InputFormat": "pcm",
"InputSampleRate": 16000
}
}
字段
说明
Code
错误码,0 为成功,详见 错误码
Message
错误描述。
Format / SampleRate / Channels / BitDepth
协商后的下行音频参数,客户端按此解析 Binary 帧。
InputFormat / InputSampleRate
服务端期望的上行音频参数。

4.3 AsrResult

同一句话可能多次下发,SentenceId 相同:
SteadyState=false:临时结果,Text 会不断变化,可用作实时字幕。
SteadyState=true:稳态最终文本,此后同一 SentenceId 不再更新。
{
"NotificationType": "AsrResult",
"TaskId": "app123-xxxxxxxx",
"AsrResult": {
"Text": "你好,现在天气怎么样",
"SteadyState": true,
"SentenceId": 3,
"StartPtsTime": 12.36,
"EndPtsTime": 14.02
}
}
字段
类型
说明
Text
string
识别文本;临时结果时为累计中的内容。
SteadyState
bool
是否为稳态。
SentenceId
uint64
句子递增序号,同一句所有下发共享同一 ID。
StartPtsTime
float
句子在会话时间轴上的起点(秒),可能省略。
EndPtsTime
float
句子在会话时间轴上的终点(秒),可能省略。

4.4 LLMResult

模型输出以增量方式下发:
Finished=false:一片增量文本,需要客户端累加。
Finished=true:本轮回答已结束,Text 可能为空,仅作结束标记。
{
"NotificationType": "LLMResult",
"TaskId": "app123-xxxxxxxx",
"LLMResult": {
"Text": "现在是",
"Finished": false,
"SentenceId": 3
}
}
SentenceId 与产生该回答的 AsrResult.SentenceId 一致,客户端可据此把问答对齐。

4.5 TTS 音频帧(ws.Binary

编码:由 HandshakeResult.Format 决定,默认为 16-bit PCM,小端。
声道:单声道。
采样率:由 HandshakeResult.SampleRate 决定。
句末边界标记一帧长度为0的 Binary 包 (依赖音色,有些 tts 引擎不支持)表示当前 TTS 句子已合成完毕,与紧邻的上一批音频帧属于同一句。客户端通常无需将其送入解码器/播放器,仅用于句末计数、UI 状态切换等。

4.6 ProcessEof

会话结束通知(正常或异常),下发后服务端会立即关闭 WebSocket:
{
"NotificationType": "ProcessEof",
"TaskId": "app123-xxxxxxxx",
"ProcessEofInfo": {
"Code": 0,
"Message": "finish"
}
}
Code
含义
0
正常结束(通常是客户端发 Finish 后收尾完成)。
其他
错误码

5. 音频格式

5.1 上行

编码
16-bit PCM,小端。
声道
单声道。
采样率
8000 – 48000 Hz(由 inputSampleRate 指定)。
分片
建议 20–100 ms/帧。

5.2 下行

编码
pcm / mp3 / wav / flac / opus / ulaw / alaw(由 outputFormat 指定,握手结果确认)。
声道
单声道。
采样率
outputSampleRate 指定,握手结果确认。
分片
每帧长度不固定,客户端应按流处理。

6. 自定义大模型接入

为满足业务侧对模型选型、私域知识注入、多模态编排等需求,服务支持将 STS 全链路中的 LLM 环节替换为您自建或第三方托管的大模型服务。您只需在自己的模型服务上实现一套与 OpenAI Chat Completions 兼容的 HTTP 接口,即可无缝接入本服务,无需改动客户端 SDK。

6.1 整体架构


STS Service:本服务,负责 ASR / LLM 调度 / TTS 全链路;LLM 环节以 HTTP 客户端身份调用您的后台。
Customer Backend:由您部署与维护,对外只需实现 OpenAI 兼容的 /chat/completions 接口;内部可自由承载 Prompt 管理、上下文管理、RAG 检索、Function Call 编排等能力。
Upstream LLM:可任意选择 OpenAI / hunyuan / gemini / moonshot / minimax / deepseek 等厂商,甚至自研模型;STS 侧完全不感知具体厂商。

6.2 接入原理


说明:
STS 服务作为 客户端 主动向您的模型服务发起 HTTP 请求;您的服务只需要按下方约定返回 SSE 流式响应即可。

6.3 启用方式

1. 部署您的模型服务,暴露一个可通过公网访问的 HTTPS baseURL
2. 提交工单开通"自定义 LLM"能力,并在工单中告知:
baseURL:您的服务根地址(要求为完整的 v1 版本前缀,例如 https://llm.example.com/v1)。
model:默认模型名(对应 OpenAI 请求体中的 model 字段)。
systemPrompt:可选,默认的 system 提示词。
temperature:可选,默认采样温度。
apiKey:可选,作为服务端下发的默认 API Key。
3. 会话时通过 URL 参数 llmApiKey 传入本次会话使用的 API Key;未传时使用工单中提交的默认 apiKey
说明:
通过 URL 参数 llmApiKey 可以做到 每会话/每用户级 的密钥隔离,工单里提交的 apiKey 仅作为兜底默认值。

6.4 接口约定

您的模型服务需要在 POST {baseURL}/chat/completions 上实现一个与 OpenAI 兼容的 Chat Completions 接口。
请求方法
POST {baseURL}/chat/completions?taskId={taskId}
Authorization: Bearer {apiKey}
Content-Type: application/json
Accept: text/event-stream
STS 服务会在 query 中额外附带 taskId(即本次 WebSocket 会话 ID),方便您做请求追踪与日志排查。
Authorization 头固定为 Bearer {apiKey},取值为工单中的默认 apiKey 或 URL 参数 llmApiKey
请求体(OpenAI 兼容)
{
"model": "your-model-name",
"stream": true,
"temperature": 0.7,
"messages": [
{ "role": "system", "content": "你是一位乐于助人的语音助手。" },
{ "role": "user", "content": "你好" },
{ "role": "assistant", "content": "你好呀,有什么我可以帮你的吗?" },
{ "role": "user", "content": "上海今天天气怎么样?" }
]
}
字段
类型
说明
model
string
目标模型名,由工单中 model 决定。
stream
bool
恒为 true,服务端必须以 SSE 方式流式返回。
temperature
float
采样温度,可选;由工单默认配置。
messages
array
完整对话上下文,包含可选的 system、历史 user/assistant 交替消息,最后一条固定为本轮 user 输入。
messages 数组的顺序:[system?] + 历史 (user/assistant 交替) + 本轮 user。历史轮数受 URL 参数 maxHistoryRounds 控制(一问一答 = 1 轮),最多保留最近100轮。

6.5 响应格式(SSE 流式)

服务端必须以 Content-Type: text/event-stream 返回 SSE 流,每条事件形如:
data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}

data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"好"},"finish_reason":null}]}

data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

关键约定:
每个 data: 行是一个独立 JSON 对象,STS 服务只读取 choices[0].delta.content 作为增量文本。
首片可以只带角色(delta.role="assistant")而不含 content,STS 会忽略。
生成结束时必须发送 finish_reason 非空的 chunk(stop / length / content_filter 等),并额外发送 data: [DONE] 一行作为终止标记。
单条 SSE 事件之间以空行(\\n\\n)分隔。
出错时应返回 HTTP 非 2xx 状态码,或在 SSE 流中提前中断连接。

6.6 鉴权与安全

鉴权方式:Bearer Token(Authorization: Bearer {apiKey})。
建议为每个租户/子账号签发独立的 apiKey,并具备回收/轮换能力。
建议启用 HTTPS 并校验 taskId 是否合法、是否重复。
建议对模型服务本身做 QPS/并发限流,避免恶意占用。

6.7 错误处理

您的服务返回非 2xx 或 SSE 中途异常断开时,STS 会:
1. 立即结束本轮 LLM,向客户端下发 LLMResult (Finished=true, Text="")
2. 向客户端下发 ProcessEof (Code=4201) 并关闭 WebSocket。
建议错误响应示例(HTTP 4xx/5xx,非 SSE,application/json):
{
"error": {
"code": "invalid_api_key",
"message": "invalid api key",
"type": "authentication_error"
}
}

6.8 端到端示例

STS → 您的模型服务
POST https://llm.example.com/v1/chat/completions?taskId=1258344699-wsss2s-xxxx HTTP/1.1
Authorization: Bearer sk-your-key
Content-Type: application/json
Accept: text/event-stream

{
"model": "your-model-name",
"stream": true,
"messages": [
{ "role": "system", "content": "你是一位乐于助人的语音助手。" },
{ "role": "user", "content": "上海今天天气怎么样?" }
]
}
您的模型服务 → STS(SSE 流)
HTTP/1.1 200 OK
Content-Type: text/event-stream

data: {"choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":"上海"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":"今天多云,"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":"最高 28℃。"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

客户端最终收到(经 STS 转发)
{"NotificationType":"LLMResult","LLMResult":{"Text":"上海","Finished":false,"SentenceId":1}}
{"NotificationType":"LLMResult","LLMResult":{"Text":"今天多云,","Finished":false,"SentenceId":1}}
{"NotificationType":"LLMResult","LLMResult":{"Text":"最高 28℃。","Finished":false,"SentenceId":1}}
{"NotificationType":"LLMResult","LLMResult":{"Text":"","Finished":true,"SentenceId":1}}
随后 STS 会把上述文本送入 TTS 引擎并将合成音频以 Binary 帧下发给客户端。

6.9 常见问题

可以不返回 SSE,而是直接返回一个完整 JSON 吗?

不可以。STS 依赖流式增量文本驱动 TTS 边生成边合成,非流式响应会导致端到端延迟显著增加,服务端会当作错误处理。

messages 里的 system 提示词能否由客户端覆盖?

暂不支持客户端级覆盖,system 提示词以工单中的 systemPrompt 为准。

temperature 能否会话级指定?

暂不支持通过 URL 参数指定,统一使用工单默认值。

如何在自己的服务里做业务鉴权?

STS 会透传 Authorization: Bearer {llmApiKey}?taskId=;您可以基于 llmApiKey 做单次会话的鉴权、基于 taskId 来做追踪记录。

7. 错误码

错误码
语义
0
成功。
4001
请求参数不合法。
4002
空闲超时(长时间未收到上行音频)。
4003
上行音频数据格式不合法。
4004
并发连接数超限,默认是2。
4005
账户状态不可用(欠费 / 冻结等)。
4100
鉴权失败:签名无效。
4101
未授权访问该接口。
4102
未授权访问该资源。
4104
secretId 不存在。
4105
会话 ID 错误。
4106
MFA 校验失败。
4110
其它鉴权失败。
4111
AppID 不合法。
4200
语音识别上游错误。
4201
大模型上游错误。
4202
语音合成上游错误。
4203
上行控制信令 JSON 非法。
4500
重放攻击。
5000
服务内部错误。
4xxx 均为客户端可自查/可重试类错误。
5xxx 表示服务侧临时故障,客户端可延迟后重试。
说明:
如果需要提升并发请 联系我们

8. 断开与超时

空闲超时timeoutSec 秒内未收到上行音频,服务端下发 ProcessEof (Code=4002) 并关闭连接。
业务错误:任一环节出错,服务端下发 ProcessEof (Code=4xxx/5xxx) 并关闭连接。
主动结束:客户端发送 {"Command":"Finish"} 后,服务端跑完最后一句再下发 ProcessEof (Code=0) 并关闭连接。
客户端关闭:客户端可以随时直接 close WebSocket,服务端将释放本会话所有资源。

9. 完整示例

以下为一次典型会话的消息流:
▶ Client → Server (WS Upgrade)
wss://mps.cloud.tencent.com/sts/v1/1258344699/
?inputFormat=pcm&inputSampleRate=16000
&outputFormat=pcm&outputSampleRate=16000
&lang=zh&voice=xxx
&timeoutSec=30
&secretId=AKIDxxx&nonce=1234567890
&timeStamp=1783603200&expired=1783606800
&signature=<hex>

◀ Server → Client (Text)
{ "NotificationType":"Handshake", "TaskId":"...",
"HandshakeResult":{"Code":0,"Message":"success",
"Format":"pcm","SampleRate":16000,"Channels":1,"BitDepth":16,
"InputFormat":"pcm","InputSampleRate":16000} }

▶ Client → Server (Binary × N) ← 16kHz/16bit/mono PCM

◀ Server → Client (Text)
{ "NotificationType":"AsrResult", "TaskId":"...",
"AsrResult":{"Text":"你好","SteadyState":false,"SentenceId":1} }

◀ Server → Client (Text)
{ "NotificationType":"AsrResult", "TaskId":"...",
"AsrResult":{"Text":"你好呀","SteadyState":true,"SentenceId":1,
"StartPtsTime":1.20,"EndPtsTime":2.05} }

◀ Server → Client (Text)
{ "NotificationType":"LLMResult", "TaskId":"...",
"LLMResult":{"Text":"你好,","Finished":false,"SentenceId":1} }

◀ Server → Client (Text)
{ "NotificationType":"LLMResult", "TaskId":"...",
"LLMResult":{"Text":"很高兴见到你。","Finished":false,"SentenceId":1} }

◀ Server → Client (Text)
{ "NotificationType":"LLMResult", "TaskId":"...",
"LLMResult":{"Text":"","Finished":true,"SentenceId":1} }

◀ Server → Client (Binary × M) ← TTS 音频帧

◀ Server → Client (Binary, length=0) ← SentenceEnd 边界

▶ Client → Server (Text)
{ "Command":"Finish" }

◀ Server → Client (Text)
{ "NotificationType":"ProcessEof", "TaskId":"...",
"ProcessEofInfo":{"Code":0,"Message":"finish"} }

✖ Server closes WebSocket