# 动态实体
静态实体
的适用范围很大,但当开发者遇到以下几种用例时可能不能满足,为此我们推出了动态实体
,满足开发者各终端的实体需要差异化设置时的需求。
# 动态实体的定义和维度
一个动态实体的定义可以包含多个资源Resource的定义,资源定义中包含了资源名称,以及从客户端上传数据抽取说法的字段名以及生效的维度。 生效的维度目前有用户级(uid)、应用级(appid)、自定义级,在客户端上传对应的资源数据时,维度信息需要和定义时保持一致。
# 应用级
举例
以开发两款百科 App 为例,分别是《水浒传百科》和《西游记百科》,开发者期望用户的交互方式为:
我想听李逵的故事
介绍一下孙悟空
我们可以把以上两句话抽象为一个自定义技能
,其中语料如下:
我想听{name}的故事
介绍一下{name}
《水浒传百科》和《西游记百科》两个 App 均添加了这个技能,但是这两个 App 的实体并不相同。{name}
内容分别如下:
水浒传 name 实体 | 西游记 name 实体 |
---|---|
宋江 | 唐僧 |
鲁智深 | 孙悟空 |
林冲 | 猪八戒 |
武松 | 沙僧 |
李逵 | 太上老君 |
…… | …… |
为了实现这个目标,我们提供了动态实体(应用级)
,动态实体(应用级)
可以在实体名相同的情况下,为每个 appid 设置不同的实体副本。
动态实体(应用级)
作用域为 appid,生效时间为永久。
# 用户级
举例
以开发一款电话 App 为例, 开发者期望用户的提问方式为:
打电话给张三
呼叫李四
我们可以把以上两句话抽象为一个自定义技能
,其中语料如下:
打电话给{contacts}
呼叫{contacts}
由于每个用户的联系人名称都不一样,因此无法使用静态实体。为此 AIUI 的 SDK 中,为开发者提供了相应的 API 接口,开发者可以针对每个用户维护一个实体副本。动态实体(用户级)
只对特定用户生效,生效时间为永久。
# 自定义级
举例
以开发一个全国连锁餐厅点餐 App 为例, 开发者期待用户的交互方式为:
我想吃杭椒牛柳
点一份宫保鸡丁
我们将其抽象为一个自定义技能
,其中语料如下:
我想吃{dish}
点一份{dish}
但是由于该连锁餐厅全国不同省份的菜单不一样,每个省份有若干台点餐终端,但是实体不能通用。
为了解决这个问题,我们引入了动态实体(自定义级)
。
我们为这款点餐 App 新建了一个动态实体
命名为" dish",同时在这个实体下创建两个资源,分别是"dish_general" 和"dish_specail",其中 "dish_general" 的维度为应用级
," dish_specail" 的维度为自定义
,我们把这个自定义的维度命名为"province"。在控制台的设置如下:
如图所示,开发者可以为一个动态实体,设置两个维度的资源。
其中 dish_general 中,开发者可以通过 API上传全国通用的菜单。
针对 dish_special, 开发者可以通过维度名 province 上传各个省份的菜单,例如 Anhui,Beijing,Fujian 等,具体操作请参考 SDK 文档。
使用动态实体(自定义级)
开发者可以自定义分组,实体设置成功后,只对属于特定分组的终端生效。
# 动态实体的上传
动态实体的上传有两种方式:通过动态实体上传的 WebAPI 接口,和通过 AIUI SDK 对应接口。
# WebAPI上传
本接口为你提供了通过 HTTP 的方式上传动态实体资源,你可以通过本接口上传资源。
动态实体的使用分两步,上传资源,资源生效。本接口只提供上传资源,Android、iOS、Windows、Linux 请通过CMD_SET_PARAMS
设置pers_param
使之生效,具体方法请参阅SDK示例,WebSocket API 应用请传pers_param
参数。
说明
- 授权认证,调用接口需要将nameSpace,nonce,curtime和checkSum信息放在HTTP请求头中。
- 所有接口统一为UTF-8编码。
- 所有接口支持http和https。
# 调用示例
# 授权认证
说明
AccountKey 为账户级Key,每个AIUI 账户具有一个AccountKey,在「技能工作室-我的实体-动态实体密钥」中即可查看。
在调用所有业务接口时,都需要在Http Request Header中加入以下参数作为授权验证
参数名 | 说明 | 是否必须 |
---|---|---|
X-NameSpace | aiui开放平台的命名空间,在「技能工作室-我的实体-动态实体密钥」中查看 | 是 |
X-Nonce | 随机数(最大长度128个字符) | 是 |
X-CurTime | 当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数(String) | 是 |
X-CheckSum | MD5(accountKey + Nonce + CurTime),三个参数拼接的字符串,进行MD5哈希计算 | 是 |
注意
- CheckSum有效期:出于安全性考虑,每个CheckSum的有效期为5分钟(用curTime计算),同时CurTime要与标准时间同步,否则,时间相差太大,服务端会直接认为CurTime无效。
- checkSum生成示例,例如:
accountKey是abcd1234, Nonce是12, CurTime是1502607694。
那么CheckSum为MD5(abcd1234121502607694)
最终MD5为32位小写 bf5aa1f53bd173cf7413bf370ad4bddc
# IP 白名单
IP 白名单具备打开和关闭两种状态。
当 IP 白名单打开时,用户在调用所有业务接口时,在授权认证通过后,检查调用方ip是否在aiui开放平台配置的ip白名单中。若在,则向用户提供服务,否则拒绝提供服务。
当 IP 白名单关闭时,任意终端均可访问 AIUI 服务器,开发者需要自行保证 nameSpace 和 key 值安全。
注意
拒绝提供服务返回值:
{
"code":"20004",
"desc":"ip非法",
"data":null,
"sid":"rwabb52e660@dx6c9b0e56f81d3ef000"
}
# 上传资源接口
本接口提供动态实体上传资源功能,用于动态更新实体资源。
接口地址
POST http[s]://openapi.xfyun.cn/v2/aiui/entity/upload-resource HTTP/1.1
Content-Type:application/x-www-form-urlencoded; charset=utf-8
# 参数说明
参数 | 类型 | 必须 | 说明 | 示例 |
---|---|---|---|---|
appid | string | 是 | 应用id | 5adde3cf |
res_name | String | 是 | 资源名,XXX为用户的命名空间 | XXX.music |
pers_param | String | 是 | 个性化参数(json) | {"appid":"xxxxxx"} |
data | String | 是 | Base64编码的资源 | 示例1 |
其中,pers_param为个性化参数。示例如下:
维度 | 示例 | 说明 |
---|---|---|
应用级 | {"appid":"xxxxxx"} | |
用户级 | {"auth_id": "d3b6d50a9f8194b623b5e2d4e298c9d6"} | auth_id为用户唯一ID(32位字符串,包括英文小写字母与数字,开发者需保证该值与终端用户一一对应) |
自定义级 | {"xxxxxx":"xxxxxx"} |
data为web页面定义的主子段、从字段给的json格式对应的base64。例如,主字段为song、从字段singer,上传资源的格式为:
{"song":"给我一首歌的时间","singer":"周杰伦"}
{"song":"忘情水","singer":"刘德华"}
{"song":"暗香","singer":"刘德华"}
{"song":"逆光","singer":"梁静茹"}
注:每条数据之间用换行符隔开。
Base64编码为
eyJzb25nIjoi57uZ5oiR5LiA6aaW5q2M55qE5pe26Ze0Iiwic2luZ2VyIjoi5ZGo5p2w5LymIn0NCnsic29uZyI6IuW/mOaDheawtCIsInNpbmdlciI6IuWImOW+t+WNjiJ9DQp7InNvbmciOiLmmpfpppkiLCJzaW5nZXIiOiLliJjlvrfljY4ifQ0KeyJzb25nIjoi6YCG5YWJIiwic2luZ2VyIjoi5qKB6Z2Z6Iy5In0=
# 返回说明
参数名 | 说明 | 是否必须 |
---|---|---|
code | 结果码 | 是 |
data | 返回结果,见data字段说明 | 是 |
desc | 描述 | 是 |
sid | 本次webapi服务唯一标识 | 是 |
data字段说明
参数 | 类型 | 必须 | 说明 | 示例 |
---|---|---|---|---|
sid | String | 是 | 本次上传sid,可用于查看上传资源是否成功 | psn003478f3@ch00070e3a78e06f2601 |
csid | String | 是 | 本次服务唯一标识 | rwa84b7a73b@ch372d0e3a78e0116200 |
# 查看上传资源是否成功接口
本接口提供检查动态实体上传资源是否成功。
注意
上传资源数据后至少间隔10秒后再进行查看上传资源是否成功
接口地址
POST http[s]://openapi.xfyun.cn/v2/aiui/entity/check-resource HTTP/1.1
Content-Type:application/x-www-form-urlencoded; charset=utf-8
# 参数说明
参数 | 类型 | 必须 | 说明 | 示例 |
---|---|---|---|---|
sid | string | 是 | sid | psn开头的sid |
# 返回说明
参数名 | 说明 | 是否必须 |
---|---|---|
code | 结果码 | 是 |
data | 返回结果,见data字段说明 | 是 |
desc | 描述 | 是 |
sid | 本次webapi服务唯一标识 | 是 |
data字段说明
参数 | 类型 | 必须 | 说明 |
---|---|---|---|
sid | String | 是 | 上传sid |
csid | String | 是 | 上传sid |
reply | String | 是 | 查看上传资源是否成功描述 |
error | int | 是 | 查看上传资源是否成功错误码 |
# 资源生效
资源上传成功后5min生效。你可以通过webapi 请求,传pers_param
参数验证是否已生效。查看详情 (opens new window)
# AIUI SDK上传
除WebAPI接口外,动态实体的上传也可以配合客户端 SDK 进行,完整文档建议参阅 AIUI SDK 接入文档 (opens new window)。
# 动态上传资源数据
通过CMD_SYNC
上传同步资源数据,arg1表示同步的数据类型,动态实体对应SYNC_DATA_SCHEMA
(常量对应值为3)。 data为同步JSON内容的utf-8二进制数据。 JSON内容示例如下:
{
"param": {
"id_name": "uid", // 维度
"id_value": "", // 维度具体值,当维度取uid或appid时,该值可取空,AIUI会自动补全
"res_name": "XXX.user_applist" // 资源名称
},
"data": "xxxxxx"// 与schema名称对应的数据内容base64编码
}
注意:同步数据前需确保设备已经连接到AIUI服务器,可通过接收到EVENT_CONNECTED_TO_SERVER
事件来判断,当收到EVENT_SERVER_DISCONNECTED
事件后,将不能进行同步个性化数据等操作。
XXX为用户的命名空间(namespace),在技能工作室任意一个技能的基本信息中可以查看。
id_name
与动态实体定义资源时指定的维度对应,如果定义时是用户级,那此处id_name
就对应uid
。id_value
是维度具体值,如id_name
为uid
,id_value
就需要是该资源针对生效的用户的具体UID,AIUI会使用 当前用户UID进行补全,appid同理。自定义维度因为是由开发者自定义,所以id_name
、id_value
都需要设置具体值。
通过指定id_name
、id_value
,上传的动态实体资源数据到服务端就有了唯一的从属,在生效使用时就可以指定此处的id_name
、id_value
的值就能生效使用当前上传的动态实体资源。
res_name
对应的是平台自定义动态实体资源名,格式:“命名空间.资源名”。
data中是原始资源数据的base64编码内容。原始资源数据是包含多条json记录的文本,通过动态实体定义时的抽取字段名可以从每条 json记录中抽取出定义支持的说法。
如定义资源时数据抽取的主列名为appName,别名为alias,那就要确保每条json记录中都要包含如上的字段(可以包含冗余字段,如下面的extra字段), 示例如下:
{"appName": "微信", "alias": "wechat", "extra": "xxx"}
{"appName": "新浪微博", "alias": "微博", "extra": "yyy"}
{"appName": "Telegram", "alias": "电报", "extra": "zzz"}
{"appName": "讯飞输入法", "alias": "", "extra": "uuu"}
注:每条数据之间用换行符隔开。
data的实际内容是将如上数据进行base64编码后的结果。
同步上传的代码示例如下:
JSONObject syncSchemaJson = new JSONObject();
JSONObject paramJson = new JSONObject();
paramJson.put("id_name", "uid");
paramJson.put("res_name", "XXX.user_applist");
syncSchemaJson.put("param", paramJson);
syncSchemaJson.put("data", Base64.encodeToString(./fileUtil.readAssetsFile(context, "file_path"), Base64.DEFAULT | Base64.NO_WRAP));
// 传入的数据一定要为utf-8编码
byte[] syncData = syncSchemaJson.toString().getBytes("utf-8");
AIUIMessage syncAthenaMessage = new AIUIMessage(AIUIConstant.CMD_SYNC,AIUIConstant.SYNC_DATA_SCHEMA, 0, "", syncData);
mAIUIAgent.sendMessage(syncAthenaMessage);
CMD_SYNC
完成后会有EVENT_CMD_RETURN
事件回调,可以获取该操作对应的sid,便于后面查询使用:
private void processCmdReturnEvent(AIUIEvent event) {
switch (event.arg1) {
case AIUIConstant.CMD_SYNC: {
int dtype = event.data.getInt("sync_dtype");
//arg2表示结果
if (0 == event.arg2) { // 同步成功
if (AIUIConstant.SYNC_DATA_SCHEMA == dtype) {
mSyncSid = event.data.getString("sid");
showTip("schema数据同步成功,sid=" + mSyncSid);
}
} else {
if (AIUIConstant.SYNC_DATA_SCHEMA == dtype) {
mSyncSid = event.data.getString("sid");
showTip("schema数据同步出错:" + event.arg2 + ",sid=" + mSyncSid);
}
}
} break;
}
}
若用户需要同时上传多条数据,但在上传结果回调里无法将上传数据与结果一一对应时,可在上传资源数据时加上sync_tag标签,数据上传后,在结果回调里也会将此标签带出。用户可通过此标签,将结果与上传数据对应。
上传使用示例如下:
String dataTag = "data_tag_1";
JsonObject params = new JsonObject();
params.put("tag", dataTag);
AIUIMessage syncAthenaMessage = new AIUIMessage(AIUIConstant.CMD_SYNC,AIUIConstant.SYNC_DATA_SCHEMA, 0, params.toString(), syncData);
mAIUIAgent.sendMessage(syncAthenaMessage);
结果回调示例如下:
private void processCmdReturnEvent(AIUIEvent event) {
switch (event.arg1) {
case AIUIConstant.CMD_SYNC: {
int dtype = event.data.getInt("sync_dtype");
String sync_tag = event.data.getString("tag");
} break;
}
}
# 查询打包状态
通过CMD_SYNC
上传同步动态实体的资源数据后,AIUI服务端会进行处理然后生效,处理的过程是异步的,可以通过CMD_QUERY_SYNC_STATUS
查询上传的资源数据是否处理成功。
arg1表示状态查询的类型,动态实体对应SYNC_DATA_SCHEMA
(常量对应值为3),params为json,包含需要对应同步上传操作的sid,示例如下:
JSONObject paramsJson = new JSONObject();
paramsJson.put("sid", mSyncSid);
AIUIMessage querySyncMsg = new AIUIMessage(AIUIConstant.CMD_QUERY_SYNC_STATUS,AIUIConstant.SYNC_DATA_SCHEMA, 0,paramsJson.toString(), null);
mAIUIAgent.sendMessage(querySyncMsg);
CMD_QUERY_SYNC_STATUS
执行完成后会有EVENT_CMD_RETURN
事件回调,表示查询结果,解析示例如下:
private void processCmdReturnEvent(AIUIEvent event) {
switch (event.arg1) {
//schema数据打包结果查询结果
case AIUIConstant.CMD_QUERY_SYNC_STATUS: {
int syncType = event.data.getInt("sync_dtype");
if (AIUIConstant.SYNC_DATA_QUERY == syncType) {
String result = event.data.getString("result");
if (0 == event.arg2) {
showTip("查询结果:" + result);
} else {
showTip("schema数据状态查询出错:" + event.arg2 + ", result:" + result);
}
}
} break;
}
}
注1:请上传资源数据后至少间隔10秒后再进行查询打包状态操作。
注2:具体代码示例工程可参考AIUIChatDemo (opens new window)中AIUIRepository类的queryDynamicSyncStatus方法实现。
注3:动态实体上传后5min生效。。
# 生效使用
动态实体上传后5min生效,届时可以通过CMD_SET_PARAMS
设置pers_param
即可使用已设置的动态实体(CMD_SET_PARAMS
具体用法参见动态配置
生效应用级动态实体:
{
"audioparams": {
"pers_param":"{\"appid\":\"\"}"
}
}
生效用户级动态实体:
{
"audioparams": {
"pers_param":"{\"uid\":\"\"}"
}
}
生效自定义级动态实体:
{
"audioparams": {
"pers_param":"{\"custom_key\":\"custom_val\"}"
}
}
如果需要在本机器上生效当前应用对应的所有应用级的动态实体,在pers_param
加入\"appid\":\"\"
(值留空, AIUI中会自动补全appid和uid的值),同理用户级动态实体生效需要加入\"uid\":\"\"
。
对于自定义维度需要用后台定义实体时的自定义维度名作为key,使用动态上传指定的自定义维度作为值。如 后台定义的自定义维度名为vendor,那在动态上传时就需要构造如下数据进行上传:
{
"param": {
"appid": "xxxxxx",// appid
"id_name": "vendor",// 自定义维度名
"id_value": "spec_vendor", // 自定义维度value
"res_name": "user_applist" // 资源名称
},
"data": "xxxxxx"// 与schema名称对应的数据内容base64编码
}
那对应需要在交互时使用该自定义维度对应的动态实体就需要加入\"vendor\":\"spec_vendor\"
。
除了通过CMD_SET_PARAMS
设置pers_param
:
注意: set audioParams这种方式只会对语音交互生效,文本语义参考下面数据写入带pers_param的方式
JSONObject params = new JSONObject();
JSONObject audioParams = new JSONObject();
audioParams.put("pers_param", "{\"appid\":\"\"}");
params.put("audioparams", audioParams);
AIUIMessage setMsg = new AIUIMessage(CMD_SET_PARAMS, 0 , 0, params.toString(), "");
mAgent.sendMessage(setMsg);
也可以在音频写入时设置该参数:
//写入音频
byte[] audio = xxx; //初始化
String params = "data_type=audio,sample_rate=16000,pers_param={\"appid\":\"\"}";
AIUIMessage msg = new AIUIMessage(AIUIConstant.CMD_WRITE, 0, 0, params, audio);
mAIUIAgent.sendMessage(msg);
如需同时生效使用多种级别的动态实体,可直接在pers_param
里添加对应的级别的参数即可,示例如下:
{
"audioparams": {
"pers_param":"{\"appid\":\"\", \"uid\":\"\"}"
}
}
或
audioParams.put("pers_param", "{\"appid\":\"\", \"uid\":\"\"}");
注:具体代码示例工程可参考AIUIChatDemo (opens new window)中AIUIRepository类的queryDynamicSyncStatus方法实现。动态实体需要5min才能生效。
# 动态实体QA
Q:上传资源数据返回的状态与查询打包状态有什么区别?
**A:**上传资源数据时返回的状态代表数据是否上传成功至服务端,主要是用来反馈上传的数据格式是否正确;查询打包状态代表数据上传至后台后是否将这些资源数据处理成功,主要是用来反馈数据能否被正常生效使用。
Q:动态实体上传失败的原因一般有哪些?
**A:**①res_name未加上命名空间;②上传资源字段与后台不对应
Q:动态实体不能正常生效使用的原因一般有哪些?
**A:**①未设置pers_param参数;②上传资源数据时如果是在文本文件里读取的内容,请注意不要将文件的BOM头带入数据中或者直接将其转为无BOM头的文件;③aiui.cfg文件里未指定appid。