请求说明
Release Notes
版本 | 日期 | 说明 |
---|---|---|
0.1.0 | 2017.08.22 | 第一版! |
0.2.0 | 2017.10.23 | 修复服务器连接不稳定的问题 |
0.3.0 | 2018.06.19 | 引入session机制,增加语速、识别时间结果返回 |
0.3.1 | 2018.10.26 | 新增通话结束确认机制 |
接入准备
- 参考“流程说明”—>“免费服务申请”完成权限申请;
- 点击智能呼叫中心-实时语音识别Java SDK完成Java SDK下载;
- 开发环境Java版本升至1.8及以上;
- 为节省公网传输带宽,SDK内置语音压缩算法(压缩比例1/8),若启用压缩功能,请在linux x64操作系统下集成,非压缩支持Linux,Windows,Mac;
- 开发机对SDK内conf/sdk.properties配置中“service.http.server.url”(服务访问)、“token.http.server.url”(鉴权)地址开放公网访问权限;
- 编译并运行SDK自带demo程序,熟悉SDK工作过程。
语音流改造
要求项 | 取值要求 |
---|---|
音频格式 | PCM |
采样率 | 8KHz |
采样精度 | 16bits |
声道 | 单声道 |
实时语音流发送间隔 | 20ms~160ms内任意10的倍数,建议最佳发送间隔160ms |
压缩选项 | 压缩为原始大小的1/8,节省带宽,但是音频质量会有损失 |
分轨 | 为保证识别效果,进行话者分离,拆分通话双侧音频流为两通路 |
输出
key | 值说明 |
---|---|
格式 | Json字符串,详见下文识别结果”说明 |
语音识别 | utf8编码,实时逐字识别文字,由静音检测识别出的完整句,完整句文字对应开始&结束时间,语速 |
输出方式 | 回调,实时返回 |
语速 = 完整句字数 / 秒。其中,单个单词整体算作1个字,不会按实际字母数统计。
SDK 目录
- conf 目录下有默认的配置文件sdk.properites
- libs 库文件目录。需要复制该目录到您自己项目里集成
- src
|--- main
|---java 目录下有demo项目的java文件
|---resources 目录下默认的日志配置文件logback.xml
|--- test
- resources 测试音频文件目录
以下文件和目录可以忽略:
gradle* 为gradle命令相关的文件
.idea IntelliJ IDEA的 IDE项目文件
Demo 运行
-
修改conf/sdk.properties 文件 参考“流程说明”—>“免费账号和服务申请”流程,进入应用“概览”页面,点击左侧导航“应用列表”,查询“API Key”和“Secret Key”。
app.appKey = API Key app.appSecret = Secret Key app.scope = brain_bicc
-
编译及运行
Windows:
gradlew run --no-daemon //如果安装过cygwin, 可以运行 sh run-test.sh
Linux Mac
sh gradlew run --no-daemon //或者 sh run-test.sh
任意平台IntelliJ IDEA 软件
File->Open... => 选中项目目录打开
调用接口及参数说明
配置文件(sdk.properties)参数说明
参数名 | 客户必填 | 含义 | 说明 |
---|---|---|---|
app.appKey | 必填 | api key | 从网页上申请应用的API Key |
app.appSecret | 必填 | secret key | 从网页上申请应用的Secret Key |
app.scope | 必填 | 服务类型 | 实时语音识别 值为“brain_bicc” |
app.machineName | 必填 | SDK名称 | 您自定义的sdk名称,注意一份配置文件用一个sdk名称,方便区分 |
upload.threadNum | 非必填,可用默认 | 上传线程数 | 根据网络和您的实际通话数,调整该值。 |
upload.maxThreadNum | 非必填,可用默认 | 上传线程数最大值 | 如果发现您的网络不佳,会自动增加上传线程数到该值 |
send.blockingQueue.size | 非必填,可用默认 | Sdk内部队列最大允许的缓冲包的个数 | 如果网速较慢,则发送的包会卡在sdk, 如果卡住的包数量超过该值,则会抛出异常 |
http.upload.requestDurationInSeconds | 非必填,可用默认 | 单个上传请求的保持时间 | 建议1800s,正常情况不需要修改 |
upload.http.chunkSize | 非必填,可用默认 | 最大chunk尺寸 | 建议6144字节,且比每个音频包略大,每个音频包<=100ms,正常情况不需修改此参数 |
service.http.server.url | 使用自带配置 | 百度智能云服务访问地址 | 遇见https错误可改为http |
token.http.server.url | 使用自带配置 | 百度鉴权访问地址 | 遇见https错误可改为http |
全局接口
1、启动阶段初始化controller 仅调用一次,多次调用会报错,主要工作为发起鉴权请求,初始化线程等,可能比较耗时。
Controller controller = new Controller(new LogBeforeUploadListener(), new PrintAfterDownloadListener());
// LogBeforeUploadListener 为IBeforeUploadListener的实现类
// PrintAfterDownloadListener 为 IAfterDownloadListener的实现类,打印收到的数据
2、新建session的Config,设置识别参数 Session.Config 类参数说明
参数名 | 类型 | 是否常用 | 默认值 | 说明 |
---|---|---|---|---|
roleId | RoleId | 常用 | 必填 | AGENT(0), CUSTOMER(1), BOTH(2)三选一, AGENT单路坐席音频, CUSTOMER 单路用户音频, BOTH, 本次通话有上述2个音频 |
compress | bool | 常用 | 必填 | 是否需要压缩, 开启压缩时,每个包的音频必须160字节的整数倍(10ms的音频),如果不足160字节,sdk内部会自动补0后压缩。 |
agentDn | int | 不常用 | 选填,默认0 | 坐席号,会回传 |
params | Map | 不常用 | 切句模式等定制化参数,具体设置见下表,注意这里是Map, SDK内部会转为json字符串 |
定制化参数params 请务必检查设置的字符串格式是否正确。 结果返回模式
"sentenceHandler": {
"client": "STANDARD_COMPLETED",
"agent": "STANDARD_COMPLETED"
}
上述示例对应的java代码为
// Session.Config config
Map<String, Object> sentenceHandlerParams = new LinkedHashMap<>();
sentenceHandlerParams.put("agent", "STANDARD_COMPLETED");
sentenceHandlerParams.put("client", "STANDARD_COMPLETED");
Map<String, Object> params = new LinkedHashMap<>();
params.put("sentenceHandler", sentenceHandlerParams);
client 和 agent表示对应用户与坐席的返回类型设置,支持的结果返回模式包括
值 | 含义 | 备注 |
---|---|---|
STANDARD_COMPLETED | 完整句切句 | 屏蔽掉所有过程句子,只下发完整句。 |
NONE | 实时翻译结果 | 默认值,逐字识别结果及完整句 |
接口调用
Session.Config config = Session.createConfig(Session.Config.RoleId.AGENT, false);
// AGENT 表示坐席的单路电话识别,
// false 表示不启动传输压缩, 传输压缩只能在linux上启用
3、服务停止结束controller 仅调用一次,多次调用会报错,主要工作为释放资源。
controller.stop();
单通电话调用
每通电话分为三种状态:电话开始、电话过程中、电话结束。服务过程中不断循环这三个状态的调用。
1、电话开始 初始化session并发送开始包
Session session = controller.startSession(config);
如果发送正常的话,会有如下日志
{"roleId":0,"callId":"s1-ASR-26356-1-1528802449609","name":"speech_start","sendTs":1528802449609,"beforeCompressTs":0,"afterCompressTs":0,"beforeUploadTs":1528802449634,"compress":0,"sdkId":"s1-java0.3-26356-0eff1144","connectionId":"uploadT-2","ts":1528802449637,"seqId":0,"agentDn":"123","rate":8000,"params":"","heartBeat":false,"logid":"uploadT-2-Event:speech_start[s1-ASR-26356-1-1528802449609--0-P]-0"}
2、电话过程中
语音流发送接口
语音流发送间隔20ms(160字节)~160ms(2560字节)内任意160字节的倍数.
推荐发送间隔为160ms,语音流长度2560字节。
如果一通电话(callId)有两路通话的话,坐席一侧语音流发送调用sendFirstRoleSpeech,用户的那一路数据调sendSecondRoleSpeech。
如果只有一路通话,请不要调用sendSecondRoleSpeech。
session.sendFirstRoleSpeech(bytes); // 一通电话只有坐席或者用户一路电话。 如果roleId=2, 这里表示坐席的那一路电话。
session.sendSecondRoleSpeech(bytes); // 如果roleId=2 会有第二路通话。这里表示用户的那一路电话。
若启用压缩,两方法均有一定耗时。
调用语音流发送接口,SDK通过IBeforeUploadListener 回调接口返回处理状态。其中,重点关注void onSendLocalException(Exception exception, int level);
public interface IBeforeUploadListener extends ILevel {
/**
* 上行时遇到异常回调
*
* level = 8 sdk遇见异常,但是通常会自行处理
* level = 16 表示SDK已经不能处理该异常,此时用户需要关注, 建议立刻排查问题
*
* @param exception
* @param level
*/
void onSendLocalException(Exception exception, int level);
/**
* 排查性能时使用:该包的即将上传
* @param info
*/
void onBeforeUpload(SendInfo info);
/**
* 排查性能时使用:该包上传完毕
* @param info
*/
void onAfterUpload(SendInfo info);
/**
* 留空,忽略该接口即可
* @param info
*/
void onBeforeCompress(SendInfo info);
/**
* 留空,忽略该接口即可
* @param info
*/
void onAfterCompress(SendInfo info);
}
识别结果回调接口 SDK使用回调的形式告知用户报错消息和返回结果
/**
* 下行监听
* <p>
*/
public interface IAfterDownloadListener {
/**
* 服务器正常返回的内容,里面有识别结果和其它有用的业务信息
*
* @param json
*/
void onReceive(String json);
/**
* 返回json里有未知name值, 通常代表出错
*
* @param json
*/
void onReceiveError(String json);
/**
* 下行时SDK报错
*
* @param exception
* @param level level = 16 表示非常严重的错误,不能忽视
*/
void onRecieveLocalException(Exception exception, int level);
}
3、电话结束
发送结束数据包
sendEndSpeech 发送通话结束包,如果不发送的话,当前CallId会占用您的配额。
destroy请在sendEndSpeech 后调用,调用后Session内部释放资源,再次调用send*方法会报错。
session.sendEndSpeech(); // 必须等坐席用户全部发送完成,才能end。
session.destroy(); // 释放压缩传输数据的资源
服务端需要额外的处理来释放这通电话的资源,当SDK收到name=SPEECH_END_ACK(speech_end_ack)的事件时,表示服务端已经完整释放了这通电话的资源,并释放了您的并发配额。
识别结果
Json字符串形式,字段说明如下:
key | 说明 | 必选 | 内容 |
---|---|---|---|
appId | 标识所属app | 否 | |
callId | 知识所属call | 是 | |
roleCategory | 知识所属于角色 | 否 | AGENT,座席;CLIENT,客户 |
category | 知识类型 | 是 | TXT-文本; INTENT-意图; KNOWLEDGE-知识; SPEECH-合成语音(url地址); EVENT-事件 |
content | 知识内容 | 是 | 返回的知识的内容 |
triggerTxt | 触发知识的文本 | 否 | string |
triggerTime | 触发知识的时间 | 否 | timestamp |
extJson | 扩展字段 | 否 | 扩展Json对象 |
logId | 本次返回标记 | 是 | String |
extJson字段说明:
key | 说明 | 必选 | 内容 |
---|---|---|---|
completed | 识别文本状态 | 是 | 取值1和3。 1,过程中逐字识别结果; 3,vad切分产生的完整句 |
snStartTime | 文本开始时间 | 是 | 毫秒级,样例:00:00.100 |
snStopTime | 文本结束时间 | 是 | 毫秒级,样例:00:01.100 |
speed | 完整句语速 | 否 | 完整句字数 / 秒。其中,单个单词整体算作1个字,不会按实际字母数统计。 |
示例
{
"callId" : "xxxxx.21532-32024.1519268928131-5812",
"category" : "TXT",
"content" : "医学考试时间",
"logId" : "f579116d-7741-4bf4-b94b-caf2cdd650cf_-1",
"roleCategory" : "AGENT",
"triggerTime" : 1519268932820,
"extJson":{
"snStopTime":"00:00.900",
"speed":"1.25",
"snStartTime":"00:00.100",
"completed":3
}
}
jvm 启动参数
-Daip.talker.conf.filename=conf/sdk.properties //sdk.properties路径
-Dlogback.configurationFile=/path/to/logback.xml //logback.xml路径
日志配置
日志使用logback框架, 配置文件为src/main/resources/logback.xml。 详细配置请访问logback配置
报错信息及原因
demo除了HttpClient库报错外,共有以下三大类异常:
- InitException 初始化报错,通常在初始化过程中,配置或者调用的次序不正确;
- SendException 发送出错, 通常在发送语音数据的时候,未按照指定的要求;
- LevelException SDK运行过程中,如与服务端交互时,网络异常等因素。其中level等于8,表面有异常情况发生。16表示sdk遇见严重错误。
报错内容 | Exception | 说明 |
---|---|---|
Conf file not existed | InitException | 没有配置文件,demo中为conf/sdk.properties |
Conf file load failed | InitException | 读取配置文件遇见IOException |
the specified key is not set in conf file | ConfigurationException | 配置文件中没有xx参数, 请确保demo中的conf/sdk.properties没有额外的修改 |
callId is not correct. pattern is | InitException | 如果是自定义callId, 需要按照报错中pattern的正则表达式, 推荐机器名+uuid |
jni libConverter.so load failed | InitException | 没有找到libConverter.so , 确保您在linux x64 环境下运行,并且 libConverter.so在您的库目录下。 参见demo设置的-Djava.library.path参数 |
token init failed, maybe appKey appSecret error | InitException | 配置文件中的app.appKey 或者 app.appSecret填错 |
audio data length is not correct, length | SendException | 发送数据包时,数据为0字节或者超过5120字节 |
Session has been destoried | SendException | 调用sendFirstRoleSpeech时, session已经调用过destory |
roleId is not BOTH or Session has been destroyed | SendException | session初始化时指定的不是2路通话, 但是用户调用了sendSecondRoleSpeech。 或者session已经调用过destory |
Extra Upload commands too many, num= | LevelException | 与服务端通讯时,响应速度过慢,SDK会自动新增上传线程。 如果线程数超过upload.maxThreadNum,会有此错误, 此时数据包被过分积压或者与服务端传输数据时耗时严重,不能实时识别 |
token check failed | LevelException | token没有实时语音识别或者智能电销的权限 |
Event check fail to recieve ack, times:3 | LevelException | 重试3次后,在规定的时间均未得到服务端的确认, 请检查是否有包积压或者网络通讯问题 |
分布式运行
识别服务支持分布式。
启用压缩:由于压缩算法具有一定的效果优化策略,需确保一通电话单侧(坐席|客户)语音流由同一个SDK实例上传。
非压缩:可由同一SDK实例上传,也可采用一通电话的音频数据从不同的SDK实例(不同进程、不同机器)上传。