概览

加载中

控制台概览

账户资源与调用情况

¥0.00
0个
0个
¥0.00/ 分钟

最近用量

服务模型用量费用时间

服务状态

新语音调试

输入 Prompt、选择音色,直接调试千阶 S2S 实时对话

未连接

调试配置

配置只用于本次调试,不会覆盖智能语音的正式配置。

体验保存后,UUID 会绑定此 kg_id;引导知识和 QA 知识按同一实体加载。
开始语音对话

点击按钮并允许使用麦克风

对话中

实时语音

API Key 直接建立 S2S WebSocket,不需要临时 token

浏览器调试

连接地址

GET /developer/realtime
wss://your-domain/developer/realtime?api_key=sk-qj-...

Authorization: Bearer sk-qj-...

启动消息

{"type":"start","voice_id":"你的千阶音色ID","mode":"prompt","prompt":"系统提示词"}

API Key

密钥用于实时语音和免费向量接口

密钥明文
名称前缀状态最近使用创建时间

声音复刻

上传一段清晰人声,或直接录音,生成属于当前租户的专属音色。

创建我的音色

🎵
点击或拖拽音频文件到此处
支持 mp3、wav、m4a 格式
建议 10 秒以上、单人清晰讲话

音色归属

系统音色和当前租户创建的克隆音色会统一出现在实时语音的选择列表中。其他租户的音色不会展示。

复刻完成后即可在"实时语音"中选择使用。

可用音色

名称来源状态语速创建时间

知识与向量

引导知识和 QA 问答知识按现有 kg_entity 隔离,S2S 会话按 kg_id 自动加载

新增 QA 问答

QA 问答知识

问题答案状态创建时间

充值与账单

积分用于 AI 对话/语音/创作;美元与人民币用于 API 调用,两账户独立、不可互转。

0
$0.00
¥0.00
$0.00
¥0.00

积分充值(AI 对话 / 语音 / 创作专用)

= 0 积分

充值汇率 1000 积分 = 1 元;积分不可退款、不可转赠、不可兑换为 API 账户余额。

微信 Native 充值(API 账户 USD / CNY)

选择美元时按下单瞬间的实时汇率换算人民币应付金额;汇率不可用则不会创建订单。

充值订单

订单号币种到账微信应付状态时间

钱包流水

标题金额余额时间

全部用量

服务模型数量单位费用请求 ID时间

千阶实时语音 · 接入手册

S2S (Speech-to-Speech) 双向流式 PCM 实时语音 API 文档

1. 概述

千阶实时语音(S2S,Speech-to-Speech)是基于大模型的双向流式语音对话服务。与传统的 ASR → LLM → TTS 级联方案不同,S2S 在单条 WebSocket 连接上完成语音输入理解、大模型推理和语音合成输出,实现更低延迟和更自然的交互体验。

核心能力

  • 双向流式 PCM:用户边说、AI 边听边回复,无需等待一轮完整说完
  • 实时打断:识别用户有效开口后立即停止当前 TTS 播放并重新进入理解
  • 智能抢话:理解停顿、换气和犹豫,判断用户是否真正结束表达
  • 企业 RAG:会话自动加载租户知识库,无需客户端传临时 context
  • 意图与决策引擎:识别用户意图、拒绝原因、状态变化,输出话术级控制信号
  • 400B+ 模型:默认挂载大尺寸模型,支持复杂理解和多轮推理

适用场景

  • 专业外呼(销售、回访、通知)
  • 智能客服与呼入接待
  • 催收、提醒等业务语音场景
  • 教育辅导、咨询等对话型应用

2. 接入准备

获取 API Key

登录千阶控制台,在「API Key」页面创建访问令牌。API Key 以 sk-qj- 前缀开头,创建后可随时在列表中点击「查看」获取明文。

余额要求

账户需要有足够余额(千阶币或 CNY)才能发起语音会话。新注册用户赠送 1000 千阶币 + ¥3 CNY 体验额度。余额不足时会话将被拒绝。

提示:语音会话按分钟扣费,建议在正式调用前确认余额充足,避免通话中途被中断。

3. 建立 WebSocket 连接

连接 URL

wss://trial.qjchat.com/developer/realtime?api_key=sk-qj-xxxxxxxx

将 trial.qjchat.com 替换为你的实际接入域名。API Key 直接通过 query 参数传递。

通过 Header 鉴权(可选)

如果客户端支持在 WebSocket 握手时设置自定义 Header,也可以使用 Authorization 头:

Authorization: Bearer sk-qj-xxxxxxxx
注意:浏览器原生 WebSocket API 不支持设置自定义 Header。在浏览器环境中请使用 query 参数方式。服务端客户端(Python/Node.js)可以使用 Header 方式。

Token Bootstrap(可选)

API Key 本身即可作为 Token 直接使用,通常不需要额外的 bootstrap 步骤。如需获取临时 Token:

POST /developer/realtime/token
Authorization: Bearer sk-qj-xxxxxxxx

# 响应
{"token": "sk-qj-xxxxxxxx", "expires_in": 3600}

4. 会话指令

连接建立后,客户端需要发送 JSON 文本帧来控制会话。

start 指令

发送 start 指令开启一次语音会话:

{
  "type": "start",
  "model": "s2s-realtime",
  "voice_id": "你的音色ID",
  "mode": "prompt",
  "prompt": "你是专业客服,请友好回答用户问题"
}
字段类型必填说明
typestring是固定 "start"
modelstring否默认 s2s-realtime
voice_idstring是音色 ID,在「声音管理」页获取
modestring否prompt 或 script,默认 prompt
promptstring否Prompt 模式下的系统提示词
script_idstring否脚本模式下使用,在「知识管理」页创建脚本后获取 ID

stop 指令

发送 stop 指令结束会话:

{"type": "stop"}

也可以直接关闭 WebSocket 连接来结束会话,服务端会自动清理资源。

5. 音频格式

音频通过 WebSocket 二进制帧传输,格式为原始 PCM(无文件头、无编码)。

方向采样率位深声道传输方式
用户音频(客户端 → 服务端)16000 Hz16-bit单声道WebSocket 二进制帧
AI 音频(服务端 → 客户端)24000 Hz16-bit单声道WebSocket 二进制帧
重要:用户音频必须是 16kHz / 16bit / 单声道 PCM。如果采样率不匹配(如 48kHz),需要先在客户端进行重采样,否则 AI 将无法正确识别。

帧大小建议

  • 每帧建议 20–40ms 音频(320–640 samples,即 640–1280 bytes)
  • 帧过大增加延迟,帧过小增加协议开销
  • 连续发送,不要等待前一帧确认

字节序

PCM 数据使用小端序(Little-Endian),与 WAV 文件的 PCM data chunk 格式一致。

6. 事件流

一次完整的语音会话事件流如下:

客户端                          服务端
  │                               │
  │── WebSocket 连接 ────────────▶│  (鉴权通过)
  │                               │
  │── start 指令 (JSON 文本帧) ──▶│  (加载音色/模型/知识)
  │                               │
  │── 用户音频 (二进制帧) ───────▶│  (持续发送)
  │◀── AI 音频 (二进制帧) ────────│  (持续接收)
  │◀── AI 音频 (二进制帧) ────────│
  │── 用户音频 (二进制帧) ───────▶│
  │      ... 双向传输 ...          │
  │                               │
  │── stop 指令 / 关闭连接 ──────▶│  (清理资源)
  │                               │

录音

通话过程中服务端自动录音,生成以下文件:

  • caller.wav — 用户侧音频(16kHz 单声道)
  • bot.wav — AI 侧音频(24kHz 单声道)
  • merged.wav — 合并后的 24kHz 立体声 WAV(左声道=用户,右声道=AI)
  • meta.json — 通话元数据与对话记录

录音可在管理端回放,S2S 录音的 source 字段标记为 "s2s"。

7. 计费

计费方式

实时语音按会话时长(分钟)计费,精确到秒,不足 1 秒按 1 秒计算。

项目价格说明
成本价¥0.09 / 分钟上游语音服务成本
零售价由加价倍率推导成本 × 倍率,倍率可在管理端配置
千阶币扣费按 voiceCoinsPerSecond 计算从千阶币余额扣除

扣费公式

扣币数 = 成本Cny × 加价倍率 × 汇率系数 × 1000
其中:
  成本Cny = 秒数 / 60 × 0.09
  汇率系数 = 1.5 (1000币 = ¥1.5,可配)
  加价倍率 = 1.4 (默认,可配)

扣费时机

  • 千阶币账户:会话结束时按实际秒数一次性扣费
  • API CNY/USD 账户:API 调用时扣费,CNY 优先、USD 兜底
  • 余额不足时会话将被拒绝或中途中断
示例:一通 18 秒的通话,成本 = 18/60 × 0.09 = ¥0.027,扣币 = 0.027 × 1.4 × 1.5 × 1000 ≈ 57 币。

8. 代码示例

JavaScript(浏览器)

const ws = new WebSocket(
  "wss://trial.qjchat.com/developer/realtime?api_key=sk-qj-xxxx"
);

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "start",
    model: "s2s-realtime",
    voice_id: "你的音色ID",
    mode: "prompt",
    prompt: "你是专业客服"
  }));
  startMicCapture();
};

ws.onmessage = async (event) => {
  if (typeof event.data === "string") {
    const msg = JSON.parse(event.data);
    console.log("收到事件:", msg);
  } else {
    const pcm = await event.data.arrayBuffer();
    playAudio(pcm, 24000);
  }
};

async function startMicCapture() {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: { sampleRate: 16000, channelCount: 1 }
  });
  const ctx = new AudioContext({ sampleRate: 16000 });
  const source = ctx.createMediaStreamSource(stream);
  const processor = ctx.createScriptProcessor(512, 1, 1);
  source.connect(processor);
  processor.connect(ctx.destination);
  processor.onaudioprocess = (e) => {
    const float32 = e.inputBuffer.getChannelData(0);
    const int16 = float32ToInt16(float32);
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(int16.buffer);
    }
  };
}

function float32ToInt16(float32) {
  const int16 = new Int16Array(float32.length);
  for (let i = 0; i < float32.length; i++) {
    const s = Math.max(-1, Math.min(1, float32[i]));
    int16[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
  }
  return int16;
}

function playAudio(pcmBuffer, sampleRate) {
  const ctx = new AudioContext({ sampleRate });
  const int16 = new Int16Array(pcmBuffer);
  const float32 = new Float32Array(int16.length);
  for (let i = 0; i < int16.length; i++) {
    float32[i] = int16[i] / 0x8000;
  }
  const buffer = ctx.createBuffer(1, float32.length, sampleRate);
  buffer.getChannelData(0).set(float32);
  const src = ctx.createBufferSource();
  src.buffer = buffer;
  src.connect(ctx.destination);
  src.start();
}

Python

import asyncio
import json
import websockets
import pyaudio

API_KEY = "sk-qj-xxxx"
WS_URL = f"wss://trial.qjchat.com/developer/realtime?api_key={API_KEY}"
USER_SAMPLE_RATE = 16000
CHUNK = 1024

async def voice_session():
    async with websockets.connect(WS_URL) as ws:
        await ws.send(json.dumps({
            "type": "start",
            "model": "s2s-realtime",
            "voice_id": "你的音色ID",
            "mode": "prompt",
            "prompt": "你是专业客服"
        }))
        pa = pyaudio.PyAudio()
        stream_in = pa.open(format=pyaudio.paInt16, channels=1,
            rate=USER_SAMPLE_RATE, input=True, frames_per_buffer=CHUNK)
        stream_out = pa.open(format=pyaudio.paInt16, channels=1,
            rate=24000, output=True, frames_per_buffer=CHUNK)

        async def send_audio():
            while True:
                data = stream_in.read(CHUNK, exception_on_overflow=False)
                await ws.send(data)

        async def recv_audio():
            while True:
                msg = await ws.recv()
                if isinstance(msg, bytes):
                    stream_out.write(msg)
                else:
                    print("事件:", json.loads(msg))

        await asyncio.gather(send_audio(), recv_audio())

asyncio.run(voice_session())

curl — Token Bootstrap

curl -X POST https://trial.qjchat.com/developer/realtime/token \
  -H "Authorization: Bearer sk-qj-xxxx" \
  -H "Content-Type: application/json"

9. 声音复刻

声音复刻(Voice Clone)允许你上传一段参考音频,训练生成自定义音色,用于 S2S 会话或其他 TTS 场景。

克隆流程

  • 1. 上传参考音频:准备 10–30 秒清晰人声录音(WAV/MP3,16kHz 以上)
  • 2. 调用克隆接口:提交参考音频,服务端异步训练
  • 3. 获取 voice_id:训练完成后返回 voice_id
  • 4. 用于 S2S:在 start 指令中使用该 voice_id
注意:声音复刻按业务单独开通,不在通用计费内。如需使用请联系管理员。

默认提供 100+ 真人音色,可在「声音管理」页查看和选择。

10. 常见问题

连接不上 WebSocket?

  • 检查 API Key 是否正确(sk-qj- 前缀)
  • 检查账户余额是否充足
  • 确认 URL 协议是 wss://(不是 ws://)
  • 检查域名是否正确,生产环境使用 trial.qjchat.com

没有声音输出?

  • 检查 AI 音频帧是否为二进制帧(ArrayBuffer),而非文本帧
  • 确认播放器采样率设置为 24000 Hz
  • 确认 PCM 数据为 16-bit 小端序
  • 浏览器需要用户交互后才能播放音频(autoplay 策略)

AI 听不到我说?

  • 检查麦克风音频是否为 16kHz / 16bit / 单声道 PCM
  • 确认发送的是二进制帧(ws.send(arrayBuffer)),而非 Base64 编码
  • 检查麦克风权限是否已授权
  • 确认 Float32 到 Int16 转换正确

延迟高?

  • 检查网络延迟(建议 RTT < 100ms)
  • 减小音频帧大小(20–40ms / 帧)
  • 确认网络带宽足够(16kHz PCM ≈ 32KB/s 上行)
  • 检查客户端是否在发送前等待了不必要的确认

会话被中断?

  • 检查账户余额是否在通话过程中耗尽
  • 确认 WebSocket 连接未被客户端意外关闭
  • 网络异常断开后需重新建立连接并发送 start