优谷·雅言SDK 文档2.0.0

声通兼容实时评测

路径与参数帧沿用声通命名,可开启朗读进度。

查看 Markdown
WSSwss://open.shengzhiai.com/{coreType}

帧序列,沙箱实录

兼容实时评测帧序列
  1. 0 ms客户端GET /sent.eval.cn?appKey=…&nonce=…&timestamp=…&signature=…
  2. 27 ms服务端{"event":"connected","coreType":"sent.eval.cn"}
  3. 27 ms客户端{"refText":"今天天气很好","language":"zh-CN","realtime_feedback":true,"idempotencyKey":"49db5f38…"}
  4. 477 ms服务端{"event":"started","coreType":"sent.eval.cn"}
  5. 477 ms客户端{"cmd":"ping"}
  6. 477 ms客户端二进制帧 96 个,每个 640 字节,共 61440 字节
  7. 915 ms服务端{"event":"pong","ts":1791483761096}
  8. 1429 ms服务端{"eof":0,"result":{"bytes":16000}}
  9. 1933 ms服务端{"eof":0,"result":{"bytes":32000}}
  10. 2472 ms服务端{"eof":0,"result":{"bytes":48000}}
  11. 2509 ms客户端{"cmd":"end"}
  12. 6268 ms服务端{"recordId":"eval_a07810a40779","result":{"overall":94.6,"pronunciation":100,"fluency":96,"integrity":100,"tone":83,"rhythm":78,"speed":225,"duration":"1.920","warning":[],"words":[{"word":"今","scores":{"overall":78}},{"word":"天","scores":{"overall":95}},{"word":"天","scores":{"overall":85}},{"word":"气","scores":{"overall":76}},{"word":"很","scores":{"overall":94}},{"word":"好","scores":{"overall":73}}]},"eof":1}

示例代码

直连协议
// 声通兼容实时评测,直连协议。Node.js 18 及以上,依赖 npm install ws
import crypto from 'node:crypto';
import { readFile } from 'node:fs/promises';
import WebSocket from 'ws';

const idempotencyKey = crypto.randomBytes(16).toString('hex');
const query = {
  appKey: process.env.YUGU_APP_KEY,
  nonce: crypto.randomBytes(16).toString('hex'),
  timestamp: String(Math.floor(Date.now() / 1000)),
};
const payload = Object.keys(query).sort().map((k) => `${k}=${query[k]}`).join('&');
const signature = crypto.createHmac('sha256', process.env.YUGU_SECRET_KEY).update(payload, 'utf8').digest('base64');
const ws = new WebSocket('wss://open.shengzhiai.com/sent.eval.cn?' + new URLSearchParams({ ...query, signature }));

const wav = await readFile('audio.wav');
const pcm = wav.subarray(wav.indexOf('data') + 8);

ws.on('message', (data, isBinary) => {
  if (isBinary) return;
  const msg = JSON.parse(data.toString());
  if (msg.event === 'connected') {
    // 参数帧一轮只发一次,幂等键放在参数帧里
    ws.send(JSON.stringify({ refText: '今天天气很好', language: 'zh-CN', realtime_feedback: true, idempotencyKey }));
  } else if (msg.event === 'started') {
    for (let i = 0; i < pcm.length; i += 640) ws.send(pcm.subarray(i, i + 640));
    ws.send(JSON.stringify({ cmd: 'end' }));
  } else if (msg.eof === 0) {
    console.log('进度', msg.result.bytes);
  } else if (msg.eof === 1) {
    console.log('recordId', msg.recordId, '总分', msg.result.overall);
    ws.close();
  } else if (msg.event === 'error') {
    console.error('错误', msg.code, msg.message);
    ws.close();
  }
});

握手参数#

query 参数与原生实时评测相同,路径中的 coreType 取值同声通兼容整段评测。

会话流程#

wss://open.shengzhiai.com/{coreType},coreType 取值同声通兼容整段评测。

  1. 服务端发 {"event": "connected", "coreType": "sent.eval.cn"}。
  2. 客户端发参数帧 {"refText": "北京你好", "language": "zh-CN", "realtime_feedback": true, "idempotencyKey": "..."},也接受 text 代替 refText。不要朗读进度时不带 realtime_feedback。服务端回 {"event": "started", "coreType": "sent.eval.cn"}。参数帧会清空这一轮已收的音频,一轮只发一次。
  3. 客户端发音频,规则同原生实时评测。realtime_feedback 为 true 时服务端在收音过程中下发朗读进度帧,见朗读进度。
  4. 客户端发 {"cmd": "end"} 或 {"end": true},服务端回 {"recordId": ..., "eof": 1, "result": {...}},重放结果带 "replayed": true。

参数帧带 "timing": 1 时终评帧另有 timing 对象,见声通兼容整段评测。握手可协商 permessage-deflate,服务端下发的帧随之压缩,段落题终评帧可小到原来的十分之一左右。

客户端消息#

参数帧文本帧必填

{"refText": ..., "language": ..., "realtime_feedback": true, "idempotencyKey": ...},也接受 text 代替 refText,一轮只发一次

二进制帧音频必填

规则同原生实时评测

{"cmd":"end"}文本帧必填

结束帧,也接受 {"end": true}

{"cmd":"ping"}文本帧可选

应用层心跳

服务端消息#

{"event":"connected"}文本帧必填

连接建立,带 coreType

{"event":"started"}文本帧必填

参数帧已受理

{"eof":0}文本帧可选

进度帧,realtime_feedback 为 true 时下发,result.bytes 为已收字节数,另带逐字的朗读进度,见朗读进度,不含评分

{"eof":1}文本帧必填

终评,带 recordId 与 result,重放时带 "replayed": true

{"event":"error"}文本帧可选

错误,带 message,原因是服务端业务错误时另带 code

{"event":"pong"}文本帧可选

心跳回复

更新于 2026-10-13