准备工作#
| 项 | 要求 |
|---|---|
| 密钥 | 开放平台签发的 appKey 与 secretKey。联调期间用沙箱密钥,每把每天 200 次,不扣套餐与余额,申请方式见沙箱环境 |
| 音频 | 一段 1 秒以上的 WAV 录音,16 kHz,16 位,单声道。可以直接用示例音频,朗读的参考文本为 今天天气很好 |
| 运行环境 | Java 11,安卓 5.0,iOS 13,Chrome 73,Node.js 20,微信基础库 2.20.1。Node.js 18 需要传入 crypto 选项,各端完整要求见版本兼容 |
secretKey 只放在接入方服务端或可信环境。网页,小程序与移动端联调时可以用签名,例如小程序写 auth: { appKey, secretKey },正式上线改用 token,做法见鉴权。
接入步骤#
五分钟快速开始#
分三步执行。需要 JDK 11 或更高版本与 Maven 3.6 或更高版本,一段 3 秒左右的 16 kHz,16 位,单声道 WAV 录音,以及开放平台签发的 appKey 与 secretKey,联调期间用沙箱密钥,申请方式见 沙箱环境。
- 新建工程目录
yugu-quickstart,在其中新建pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>yugu-quickstart</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>11</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>shengzhiai</id>
<url>https://open.shengzhiai.com/maven/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.shengzhiai.yugu</groupId>
<artifactId>yugu-java-sdk</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<mainClass>QuickStart</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>- 新建
src/main/java/QuickStart.java:
import com.shengzhiai.yugu.YuguClient;
import com.shengzhiai.yugu.errors.YuguException;
import com.shengzhiai.yugu.model.EvalResult;
import com.shengzhiai.yugu.model.EvaluateConfig;
import java.nio.file.Path;
public class QuickStart {
public static void main(String[] args) {
try (YuguClient client = YuguClient.builder()
.apiKey(System.getenv("YUGU_APP_KEY"), System.getenv("YUGU_SECRET_KEY"))
.build()) {
EvalResult r = client.evaluate(Path.of("audio.wav"),
new EvaluateConfig(EvaluateConfig.CORE_SENTENCE, "今天天气很好", "zh-CN"));
System.out.println("总分 " + r.getOverall());
System.out.println("发音 " + r.getDims().getPronunciation());
System.out.println("流利度 " + r.getDims().getFluency());
System.out.println("完整度 " + r.getDims().getIntegrity());
r.getWords().forEach(w -> System.out.println(w.getWord() + " " + w.getScores().getOverall()));
} catch (YuguException e) {
System.err.println(e.getCategory() + " " + e.getCode() + " " + e.getRawMessage());
}
}
}- 把录音放到工程目录,命名为
audio.wav,设置密钥后运行,控制台输出总分,分项与逐字分数:
YUGU_APP_KEY=沙箱appKey YUGU_SECRET_KEY=沙箱secretKey mvn -q compile exec:javademos/java-cli 是同样流程的完整工程,带命令行参数,可直接运行。
安装#
在 settings.gradle 里加入优谷雅言 Maven 仓库:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url "https://open.shengzhiai.com/maven/" }
}
}在模块的 build.gradle 里声明依赖:
dependencies {
implementation "com.shengzhiai.yugu:yugu-android-sdk:2.1.0"
}Kotlin DSL 写法:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://open.shengzhiai.com/maven/") }
}
}
// app/build.gradle.kts
dependencies {
implementation("com.shengzhiai.yugu:yugu-android-sdk:2.1.0")
}工程仍在顶层 build.gradle 的 allprojects 里声明仓库时,把同一行 maven { url "https://open.shengzhiai.com/maven/" } 加进那里。
仓库里的每个版本发布后不再改动,依赖写成具体版本号即可锁定,需要回退时改回历史版本号。每个文件都附带 .md5,.sha1,.sha256,.sha512 校验和,另有源码包 -sources.jar 与接口文档包 -javadoc.jar。1.x 版本没有发布到 Maven 仓库,源码包仍可在 https://open.shengzhiai.com/sdk/ 下载,已停止维护。
SDK 自带 R8 混淆保留规则,接入方不需要额外配置。
权限#
SDK 的清单文件已声明 INTERNET,构建时自动合并。使用录音器时,宿主应用需要声明录音权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />Android 6.0 起录音权限需要在运行时申请:
if (Build.VERSION.SDK_INT >= 23 &&
checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 1001)
}把 Context 传给 Recorder 后,SDK 在开始录音前检查权限,没有权限时抛 PermissionException,错误码 90201。
五分钟快速开始#
第一步,创建客户端。整个应用共用一个实例即可。
val client = YuguClient.builder()
.auth(Auth.appKey("YOUR_APP_KEY", "YOUR_SECRET_KEY"))
.build()第二步,准备一段 16 kHz,16 位,单声道的 WAV 音频,调用整段评测。REST 方法是阻塞调用,放在后台线程执行:
thread {
val result = client.evaluate(
EvaluateConfig(CoreType.SENTENCE, "今天天气很好", Language.ZH_CN),
File(filesDir, "sample.wav"),
)
Log.i("yugu", "总分 ${result.overall},发音 ${result.dimensions.pronunciation}")
}也可以使用异步方法,回调默认在主线程执行,可以直接更新界面:
client.evaluateAsync(
EvaluateConfig(CoreType.SENTENCE, "今天天气很好", Language.ZH_CN),
AudioInput.fromFile(File(filesDir, "sample.wav")),
object : YuguCallback<EvalResult> {
override fun onSuccess(result: EvalResult) {
scoreView.text = "总分 ${result.overall}"
}
override fun onFailure(error: YuguException) {
scoreView.text = "评测失败 ${error.code} ${error.message}"
}
},
)第三步,应用退出或不再评测时关闭客户端:
client.close()完整的可运行工程见 demos/android-demo,包含录音评测,示例音频评测与实时评测。
安装#
Xcode 添加依赖#
- 在 Xcode 菜单 File 中选择 Add Package Dependencies。
- 在搜索框输入
https://open.shengzhiai.com/git/yugu-ios-sdk.git。 - Dependency Rule 选择 Up to Next Major Version,版本填
2.1.0。 - 产品勾选
YuguSDK,点击 Add Package。
Package.swift 声明#
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [.iOS(.v13)],
dependencies: [
.package(url: "https://open.shengzhiai.com/git/yugu-ios-sdk.git", from: "2.1.0"),
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "YuguSDK", package: "yugu-ios-sdk"),
]),
]
)包标识取仓库地址的最后一段,所以 package: 参数写 yugu-ios-sdk。
麦克风权限#
使用录音器的 App 必须在 Info.plist 中声明麦克风用途,缺少这一项时 iOS 在首次录音时终止 App。
<key>NSMicrophoneUsageDescription</key>
<string>录制朗读音频,用于语音评测</string>macOS App 开启了沙盒时,另需在 entitlements 中打开 com.apple.security.device.audio-input。只评测已有音频时不需要麦克风权限。
五分钟上手#
import YuguSDK
// 创建客户端,App 内复用同一个实例
let client = YuguClient(options: YuguClientOptions(
auth: .appKey("<appKey>", secretKey: "<secretKey>")))
// 评测一段 16 kHz 单声道 WAV
let config = EvaluateConfig(coreType: .sentence, referenceText: "今天天气很好", language: "zh-CN")
let result = try await client.evaluate(audio: .file(wavURL), config: config)
// 读取分数
print("总分", result.overall ?? 0)
print("流利度", result.dimensions.fluency ?? 0, "完整度", result.dimensions.integrity ?? 0)
for word in result.words {
print(word.word ?? "", word.overall ?? 0)
}
// 不再使用时释放
client.close()不使用 async 时用完成回调写法,回调在 callbackQueue 上执行,缺省为主队列:
client.evaluate(audio: .file(wavURL), config: config) { outcome in
switch outcome {
case .success(let result): print(result.overall ?? 0)
case .failure(let error): print(error.category, error.code, error.message)
}
}用麦克风录音再评测:
let recorder = YuguRecorder()
YuguRecorder.requestPermission { granted in
guard granted else { return }
try? recorder.start()
}
// 用户读完后
if let recording = recorder.stop() {
client.evaluate(audio: recording.audioInput, config: config) { outcome in
// 处理结果
}
}
recorder.release()安装#
npm 仓库安装#
SDK 发布在优谷雅言自建 npm 仓库 https://open.shengzhiai.com/npm/。仓库公开读取,安装不需要账号或令牌。在项目根目录的 .npmrc 写入一行:
@shengzhiai:registry=https://open.shengzhiai.com/npm/然后安装指定版本:
npm install @shengzhiai/yugu-web-sdk@2.1.0pnpm 与 Yarn 1 读取同一个 .npmrc,Yarn 2 及以上版本在 .yarnrc.yml 的 npmScopes 里配置同一个地址。查看全部可用版本用 npm view @shengzhiai/yugu-web-sdk versions,回退到历史版本时把 @2.1.0 换成目标版本号。
script 标签引入#
安装后的 dist 目录带有 UMD 构建,不需要打包工具。把 node_modules/@shengzhiai/yugu-web-sdk/dist/yugu-sdk.umd.min.js 复制到站点静态目录后引用,全局对象为 YuguSDK:
<script src="/static/yugu-sdk.umd.min.js"></script>
<script>
const client = new YuguSDK.YuguClient({ token: '由接入方后端签发的 JWT' });
</script>五分钟快速开始#
-
准备环境:Node.js 20 或更高版本,一段 3 秒左右的 16 kHz 单声道 WAV 录音,沙箱环境的 appKey 与 secretKey。沙箱密钥的申请方式与每日额度见 沙箱环境,Node.js 18 的做法见 Node.js 用法。
-
新建工程,安装 SDK:
mkdir yugu-quickstart && cd yugu-quickstart
npm init -y
echo "@shengzhiai:registry=https://open.shengzhiai.com/npm/" > .npmrc
npm install @shengzhiai/yugu-web-sdk@2.1.0- 新建
quickstart.mjs:
import { readFile } from 'node:fs/promises';
import { YuguClient } from '@shengzhiai/yugu-web-sdk';
const client = new YuguClient({
appKey: process.env.YUGU_APP_KEY,
secretKey: process.env.YUGU_SECRET_KEY,
});
const audio = await readFile(process.argv[2] || 'hello.wav');
const result = await client.evaluate(audio, {
coreType: 'sentence',
referenceText: '今天天气很好',
language: 'zh-CN',
});
console.log('总分', result.overall, '各维度', result.dims);
await client.close();- 运行脚本,查看分数:
YUGU_APP_KEY=沙箱appKey YUGU_SECRET_KEY=沙箱secretKey node quickstart.mjs hello.wav- 浏览器里试用时,在同一目录新建
index.html,用任意静态服务器打开,例如python3 -m http.server 8080后访问http://localhost:8080/:
<!doctype html>
<meta charset="utf-8" />
<input type="file" id="f" accept=".wav,.mp3" />
<pre id="out"></pre>
<script src="node_modules/@shengzhiai/yugu-web-sdk/dist/yugu-sdk.umd.min.js"></script>
<script>
// 仅限沙箱试用。正式环境的浏览器页面改用后端签发的 token。
const client = new YuguSDK.YuguClient({ appKey: '沙箱appKey', secretKey: '沙箱secretKey' });
document.getElementById('f').onchange = async (e) => {
const r = await client.evaluate(e.target.files[0], { coreType: 'sentence', referenceText: '今天天气很好', language: 'zh-CN' });
document.getElementById('out').textContent = '总分 ' + r.overall;
};
</script>包含录音,实时评测与结果展示的完整页面见仓库 demos/web-demo 目录。
安装#
在小程序工程的根目录,即 project.config.json 所在目录,执行:
echo "@shengzhiai:registry=https://open.shengzhiai.com/npm/" >> .npmrc
npm install @shengzhiai/yugu-miniprogram-sdk@2.1.0工程里还没有 package.json 时先执行 npm init -y。npm 源不需要账号,已发布的版本不会被覆盖,退回历史版本时把版本号换成目标版本重新安装即可。
安装后在微信开发者工具中构建 npm:
- 打开工程,点击菜单“工具”,选择“构建 npm”。
- 构建完成后工程里出现
miniprogram_npm/@shengzhiai/yugu-miniprogram-sdk目录。 - 代码中按包名引用:
const { YuguClient } = require('@shengzhiai/yugu-miniprogram-sdk');project.config.json 设置了 miniprogramRoot 时,package.json 放在该目录下,或者在 setting.packNpmRelationList 里登记 package.json 与 miniprogram_npm 的位置。升级 SDK 后要重新构建 npm。
合法域名#
SDK 只通过 wx.request 与 wx.connectSocket 访问平台,上线前在小程序管理后台的“开发管理”,“开发设置”,“服务器域名”中配置:
| 类型 | 域名 |
|---|---|
| request 合法域名 | https://open.shengzhiai.com |
| socket 合法域名 | wss://open.shengzhiai.com |
SDK 2.0 不再使用 wx.uploadFile,不需要配置 uploadFile 合法域名。开发阶段可以在开发者工具的“详情”,“本地设置”里勾选“不校验合法域名,web-view,TLS 版本以及 HTTPS 证书”,真机预览与发布前要改回。
录音需要麦克风权限。在小程序管理后台的“用户隐私保护指引”中声明使用麦克风,首次录音时微信弹出授权框,用户拒绝后录音器报 PermissionException,错误码 90201。
五分钟上手#
整段评测:录一段音,上传评测,打印总分。
const { YuguClient } = require('@shengzhiai/yugu-miniprogram-sdk');
const client = new YuguClient({
auth: { token: '<由自有服务端下发的短期 token>' },
});
const recorder = client.createRecorder();
Page({
onRecordStart() {
recorder.start();
},
async onRecordStop() {
const recording = await recorder.stop();
const result = await client.evaluate({
coreType: 'sentence',
referenceText: '今天天气很好',
language: 'zh-CN',
audio: recording,
});
console.log('总分', result.overall, '发音', result.dims.pronunciation, '流利', result.dims.fluency);
},
onUnload() {
client.close();
},
});实时评测:录音帧直接推入会话,停止录音时自动结束会话。
const session = client.streamEvaluate(
{ coreType: 'sentence', referenceText: '今天天气很好', language: 'zh-CN' },
{
onResult(result) { console.log('总分', result.overall); },
onError(err) { console.log('评测失败', err.code, err.message); },
},
);
recorder.pipeTo(session);
recorder.start();
// 用户读完后
recorder.stop();没有 SDK 的语言直接调用 REST 接口。签名用 HMAC-SHA256,openssl 一行即可算出,被签名参数与拼接规则见签名与鉴权。
export YUGU_APP_KEY=沙箱appKey YUGU_SECRET_KEY=沙箱secretKey
curl -sSO https://open.shengzhiai.com/sdk/v2/spec/fixtures/audio/zh_short.wav && mv zh_short.wav audio.wav在 audio.wav 所在目录执行:
# 原生整段评测。被签名参数只有 config 段的 JSON 原文
CONFIG='{"coreType":"sentence","referenceText":"今天天气很好","language":"zh-CN"}'
TS=$(date +%s)
NONCE=$(openssl rand -hex 16)
SIGN=$(printf 'config=%s' "$CONFIG" | openssl dgst -sha256 -hmac "$YUGU_SECRET_KEY" -binary | base64)
curl -sS https://open.shengzhiai.com/api/v1/evaluate \
-H "X-App-Key: $YUGU_APP_KEY" \
-H "X-Timestamp: $TS" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIGN" \
-H "Idempotency-Key: $(openssl rand -hex 16)" \
-F "audio=@audio.wav" \
-F "config=$CONFIG;type=application/json"返回结果#
整段评测成功时直接返回结果对象,不包信封。常用字段如下,各评测模式的完整字段见评测结果字段。
| 字段 | 含义 |
|---|---|
recordId |
评测记录号,报告查询与问题排查使用 |
result.overall |
总分,百分制。连读题型的总分在 result.connected_overall |
result.pronunciation,result.fluency,result.integrity |
发音,流利度,完整度 |
result.tone,result.rear_tone |
中文声调分与句末语调 |
result.words |
逐字或逐词分数,含拼音,音素与起止时间,时间单位 10 毫秒 |
warnings |
音频质量警告码,例如 1002 音量过低,评分照常返回 |
{
"recordId": "eval_a31712f40cbf",
"eof": 1,
"result": {
"paragraphs": [],
"kernel_version": "1.0.0",
"resource_version": "1.0.0",
"overall": 88.9,
"pronunciation": 100,
"tone": 83,
"fluency": 96,
"rhythm": 78,
"integrity": 100,
"speed": 225,
"rear_tone": "fall",
"duration": "1.920",
"numeric_duration": 1.92,
"warning": [],
"words": [
{
"word": "今",
"pinyin": "jin",
"rawpinyin": "jin1",
"symbolpinyin": "jīn",
"charType": 0,
"readType": 0,
"tone": "tone1",
"scores": {
"overall": 78,
"pronunciation": 78,
"tone": 100,
"overall_pron": 78,
"prominence": 0
},
"span": {
"start": 15,
"end": 31
},
"pause": {
"type": 0,
"duration": 0
},
"phonemes": [
{
"phoneme": "J",
"phone": "j",
"category": 0,
"pronunciation": 78,
"tone_index": "0",
"span": {
"start": 15,
"end": 23
}
},
{
"phoneme": "IN",
"phone": "in",
"category": 1,
"pronunciation": 78,
"tone_index": "1",
"span": {
"start": 23,
"end": 31
}
}
],
"normalized_syllables": [
{
"syllables": "今",
"pinyin": "jin",
"tone": "tone1",
"tone_sandhi": "tone1",
"phoneme_span": [
0,
1
]
}
],
"word_parts": [
{
"part": "今",
"charType": 0,
"beginIndex": 0,
"endIndex": 0
}
],
"phonics": [],
"linkable": false,
"syllables": [],
"evidence": {
"scored": false,
"rms_db": -32.7,
"rel_db": -0.5,
"dur_ms": 160,
"dur_ratio": 0.8,
"sfgop": 0.625
},
"readStatus": "correct",
"read_status": "correct"
},
{
"word": "天",
"pinyin": "tian",
"rawpinyin": "tian1",
"symbolpinyin": "tiān",
"charType": 0,
"readType": 0,
"tone": "tone1",
"scores": {
"overall": 95,
"pronunciation": 95,
"tone": 100,
"overall_pron": 95,
"prominence": 0
},
"span": {
"start": 31,
"end": 57
},
"pause": {
"type": 0,
"duration": 0
},
"phonemes": [
{
"phoneme": "T",
"phone": "t",
"category": 0,
"pronunciation": 95,
"tone_index": "0",
"span": {
"start": 31,
"end": 43
}
},
{
"phoneme": "IAN",
"phone": "ian",
"category": 1,
"pronunciation": 95,
"tone_index": "1",
"span": {
"start": 43,
"end": 55
}
}
],
"normalized_syllables": [
{
"syllables": "天",
"pinyin": "tian",
"tone": "tone1",
"tone_sandhi": "tone1",
"phoneme_span": [
0,
1
]
}
],
"word_parts": [
{
"part": "天",
"charType": 0,
"beginIndex": 0,
"endIndex": 0
}
],
"phonics": [],
"linkable": false,
"syllables": [],
"evidence": {
"scored": false,
"rms_db": -31.8,
"rel_db": 0.5,
"dur_ms": 240,
"dur_ratio": 1.2,
"sfgop": 0.917
},
"readStatus": "correct",
"read_status": "correct"
}
],
"sentences": [
{
"sentence": "今天天气很好",
"index": 0,
"scores": {
"overall": 89,
"pronunciation": 100,
"fluency": 96,
"integrity": 100
},
"details": [
{
"word": "今",
"pinyin": "jin",
"rawpinyin": "jin1",
"symbolpinyin": "jīn",
"charType": 0,
"readType": 0,
"tone": "tone1",
"scores": {
"overall": 78,
"pronunciation": 78,
"tone": 100,
"overall_pron": 78,
"prominence": 0
},
"span": {
"start": 15,
"end": 31
},
"pause": {
"type": 0,
"duration": 0
},
"phonemes": [
{
"phoneme": "J",
"phone": "j",
"category": 0,
"pronunciation": 78,
"tone_index": "0",
"span": {
"start": 15,
"end": 23
}
},
{
"phoneme": "IN",
"phone": "in",
"category": 1,
"pronunciation": 78,
"tone_index": "1",
"span": {
"start": 23,
"end": 31
}
}
],
"normalized_syllables": [
{
"syllables": "今",
"pinyin": "jin",
"tone": "tone1",
"tone_sandhi": "tone1",
"phoneme_span": [
0,
1
]
}
],
"word_parts": [
{
"part": "今",
"charType": 0,
"beginIndex": 0,
"endIndex": 0
}
],
"phonics": [],
"linkable": false,
"syllables": []
},
{
"word": "天",
"pinyin": "tian",
"rawpinyin": "tian1",
"symbolpinyin": "tiān",
"charType": 0,
"readType": 0,
"tone": "tone1",
"scores": {
"overall": 95,
"pronunciation": 95,
"tone": 100,
"overall_pron": 95,
"prominence": 0
},
"span": {
"start": 31,
"end": 57
},
"pause": {
"type": 0,
"duration": 0
},
"phonemes": [
{
"phoneme": "T",
"phone": "t",
"category": 0,
"pronunciation": 95,
"tone_index": "0",
"span": {
"start": 31,
"end": 43
}
},
{
"phoneme": "IAN",
"phone": "ian",
"category": 1,
"pronunciation": 95,
"tone_index": "1",
"span": {
"start": 43,
"end": 55
}
}
],
"normalized_syllables": [
{
"syllables": "天",
"pinyin": "tian",
"tone": "tone1",
"tone_sandhi": "tone1",
"phoneme_span": [
0,
1
]
}
],
"word_parts": [
{
"part": "天",
"charType": 0,
"beginIndex": 0,
"endIndex": 0
}
],
"phonics": [],
"linkable": false,
"syllables": []
}
]
}
],
"compositeReport": {
"compositeScore": 88.9,
"integrityScore": 100,
"accuracyScore": 100,
"toneVoiceScore": 83,
"nasalsScore": 98,
"pronunciationScore": 100,
"phonemeScore": 100,
"fluencyScore": 96,
"fluentScore": 96,
"readSpeedScore": 85,
"readSpeed": 225,
"skillScore": 78,
"stopConnScore": 85,
"stressScore": 32,
"intonationScore": 100,
"emotionScore": 76,
"literalTotal": 89,
"toneScore": 100,
"completenessScore": 100,
"skillSuggest": "重音变化不够明显,关键词应当适度强调(音高提升 / 时长延长 / 加强气流)。",
"emotionSuggest": "第三声(上声)调值偏低。练习时走 214 曲线,先降再升。推荐练习『你好/美好/小狗』等词。",
"initialScore": 83.5,
"finalScore": 83.5
},
"yuguScores": {
"overall": 88.9,
"integrity": 100,
"accuracy": {
"overall": 100,
"tone": 100,
"nasal": 98,
"phoneme": 100,
"initial": 83.5,
"final": 83.5
},
"fluency": {
"overall": 96,
"speed_score": 85,
"naturalness": 100
},
"reading_skill": {
"overall": 78,
"pause": 85,
"stress": 32,
"intonation": 100
},
"prosody": 78,
"emotion": 76,
"tone": 83,
"rear_tone": "fall",
"speed": 225,
"duration_s": 1.92
},
"initialScore": 83.5,
"finalScore": 83.5,
"nasalsScore": 98,
"nasals": 98,
"accuracy": 100,
"reading_skill": 78,
"emotion": 76
},
"report": {
"dimensionSuggestions": {
"integrity": [],
"accuracy": [],
"fluency": [],
"reading_skill": [
"重音变化不够明显,关键词应当适度强调(音高提升 / 时长延长 / 加强气流)。",
"先标出停连位置、句子重音和语调走向,再按标记朗读并回听节奏是否清楚。"
],
"emotion": [
"第三声(上声)调值偏低。练习时走 214 曲线,先降再升。推荐练习『你好/美好/小狗』等词。",
"先判断语句的情绪基调,选定符合语义的表达方式朗读;录音回听情感是否与内容一致。"
]
},
"dimensionScores": {
"accuracy": 100,
"fluency": 96,
"integrity": 100,
"reading_skill": 78,
"affect": 76,
"speechRate": 85,
"readSpeedRaw": 225
},
"dimensionEvidence": {
"accuracy": [
{
"word": "很",
"type": "pitchLow",
"detail": "声调 1 分(低于 60)"
}
],
"fluency": [],
"integrity": [],
"affect": [],
"speechRate": []
},
"rubricVersion": "v20260707b",
"scores": {
"compositeScore": 88.9
},
"dimensions": {
"accuracy": "声调:整句调型判定 100 分;其中“很”(字级调型 1 分)的调型与标准读音有偏差,需对照标准音重点纠正,其余字词调型整体稳定。",
"reading_skill": "停连、重音、语调和节奏基本可控,细节仍可加强。",
"emotion": "情感表达基本到位,语气起伏与文本内容大体匹配,变化幅度还可以更明显。"
}
},
"standardAudio": {},
"asrText": {},
"warnings": []
}