文档中心 > AliGenie开发者平台

业务逻辑配置

更新时间:2019/03/22 访问次数:78540

技能执行逻辑配置帮助文档

查看此页文档前,请确认您已经阅读并理解了【新手教程】》【5步创建自定义技能】

自然语言理解(NLU)完成后产出意图、参数、上下文,后续就需要拿到这些信息做具体的业务逻辑处理
image

交互逻辑语义接口(webhook方式)实现说明文档

1、接入方式

Webhook集成允许您将语义理解服务生成的信息传递到Web服务中并从中获取结果。该web服务需要开发者自己搭建,并且需要遵从平台预先定义好的输入输出。

目前开放平台将对开发者填写的Webhook服务地址做真实性校验(服务地址的有效性和开发者对服务地址是否有权限)。
对于已在线的技能不会校验,若技能新建或变更则会校验。
开发者配置webhook前请先下载认证文件(下载地址位于Webhook配置版块中)。
将下载的认证文件放到要配置的服务地址所属服务器上(PS:服务器是指tomcat/nginx/Jetty等,而不是运行服务器所依赖的操作系统),文件存放路径为:服务器根目录/aligenie/文件(即:服务器域名/aligenie/文件),假如下载到的认证文件是file.txt,配置的Webhook地址为https://open.bot.tmall.com/xxx ,那么我们将通过https://open.bot.tmall.com/aligenie/file.txt 获取认证文件内容作校验,校验通过才能配置成功。
开发者需要配置服务器的根目录,在根目录下创建aligenie目录,下载的认证文件放到该目录下即可。
若提示无效url,说明url格式不正确、无法访问或访问被拒绝;若提示未正确获取到文件,说明在指定目录下没有认证文件或文件内容不正确。
PS:每个技能都会生成一个认证文件,若开发者服务器上已经存放该文件,则无需再次下载认证文件;下载到的认证文件也请不要改名,以免认证失败。

1.1 webhook服务搭建方式

a、执行逻辑服务提供者搭建自己的服务,并提供http post的方式供平台进行调用。用户在使用webhook方式时,将自己服务的URL地址填进页面中,并且可以自定义header字段。
b、用户在对应意图下配置之后,开放平台意图逻辑引擎会将语义理解之后的结果以http post的方式调用用户配置的服务,用户服务拿到语义理解的结果之后就可以进行相应的处理,并按照意图逻辑规定的方式返回处理后的结果。

1.2 执行逻辑服务实现方式

a) java形式的,我们提供SDK供服务实现人员将输入转成请求对象。

java开发SDK点此下载

PS:当前版本的SDK,如果在使用过程中出现 java.lang.NoClassDefFoundError ,开发者可以在项目中引入以下2个jar包来解决,版本号可以自行调节

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>
MetaFormat.parseToQuery(String requestMetaData);//requestMetaData是框架调用执行逻辑服务url时传输过去的请求数据post body

  ResultModel<TaskResult>  result //执行结果对象

 /**
  *以天气查询为例的服务提供者的demo示例
  */
 //请求接收者,将最终结果返回
@Controller   
public class RequestController {

    private static final Logger logger = LoggerFactory.getLogger(RequestController.class);

    @Autowired
    private WeatherHandle       weatherHandle;

    /**
     * skill开发者提供的技能执行路径地址,请求方式为POST请求
     * 
     * @param taskQuery
     * @return
     */
    @RequestMapping(value = "/skill/weather", method = RequestMethod.POST)
    public @ResponseBody ResultModel<TaskResult> getResponse(@RequestBody String taskQuery) {

        /**
         * 将开发者平台识别到的语义理解的结果(json字符串格式)转换成TaskQuery
         */
        logger.info("TaskQuery:{}", taskQuery.toString());
        TaskQuery query = MetaFormat.parseToQuery(taskQuery);

        /**
         * 构建服务返回结果
         */
        ResultModel<TaskResult> resultModel = new ResultModel<TaskResult>();
        try {
            /**
             * 调用天气服务执行并构建回复内容
             */
            TaskResult result = weatherHandle.execute(query);
            resultModel.setReturnCode("0");
            resultModel.setReturnValue(result);
        } catch (Exception e) {
            resultModel.setReturnCode("-1");
            resultModel.setReturnErrorSolution(e.getMessage());
        }

        /**
         * 直接返回ResultModel<TaskResult>对象就ok
         */

        return resultModel;
    }

}

 // 天气服务执行,根据NLU理解的结果做相应处理并返回回复语句
@Component
public class WeatherHandleImpl implements WeatherHandle {

    @Override
    public TaskResult execute(TaskQuery taskQuery) {
        logger.info("WeatherHandleImpl execute...");

        //从请求中获取意图参数以及参数值
        Map<String, String> paramMap = taskQuery
                .getSlotEntities()
                .stream()
                .collect(
                        Collectors.toMap(slotItem -> slotItem.getIntentParameterName(),
                                slotItem -> slotItem.getStandardValue()));
        logger.info("paramMap :" + paramMap.toString());
        //如果意图是询问空气质量,则执行空气质量逻辑
        if (taskQuery.getIntentName().equals("空气质量")) {
            return aqiQuery(taskQuery, paramMap);
            //如果意图是询问天气情况,则执行天气查询逻辑
        } else if (taskQuery.getIntentName().equals("天气查询")) {
            return baseQuery(taskQuery, paramMap);
        } else {
            return null;
        }
    }

    private TaskResult baseQuery(TaskQuery taskQuery, Map<String, String> paramMap) {
        TaskResult result = new TaskResult();
        try {
            //请求服务并填充回复语句
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("areaName", paramMap.get("city")));
            params.add(new BasicNameValuePair("date", DateUtil.getStartDate(paramMap.get("time"))));
            String executeBody = httpGet(params);
            String weather = getWeather(executeBody);
            Map<String, String> properties = new HashMap<String, String>();

            properties.put("city", paramMap.get("city"));
            properties.put("time", paramMap.get("time"));
            properties.put("weather", weather);
            properties.put("temp_low", getTempLow(executeBody));
            properties.put("temp_high", getTempHigh(executeBody));
            properties.put("wind_direct", getWindDirect(executeBody));
            properties.put("power", getPower(executeBody));
            if (weather == null) {
                result.setReply("对不起,我现在只支持查询最近4天的天气");
            } else {
                result.setReply(TemplateFillUtil
                        .fillTemplate(
                                "@{city} @{time}天气 @{weather},温度@{temp_low}到@{temp_high}度,@{wind_direct}@{power}",
                                properties));
            }

            result.setExecuteCode(ExecuteCode.SUCCESS);
            result.setResultType(ResultType.RESULT);
        } catch (Exception e) {
            logger.info("query exception", e);
            result.setExecuteCode(ExecuteCode.EXECUTE_ERROR);
        }

        return result;
    }
}

b) 其他形式的服务实现者需要参考我们下面提供的服务请求时的json格式进行相应处理,返回的格式也必须遵从后面提供的服务返回结构

2、请求字段说明

字段名 type 描述 是否必要
sessionId String 会话ID,session内的对话此ID相同
botId Long 应用ID,来自于创建的应用或者技能
utterance String 用户输入语句
skillId Long 技能ID
skillName String 技能名称
intentName String 意图名称
token String 技能鉴权中配置oauth2.0授权后可以得到此token,详细请查看oauth2.0配置文档
requestData Map<String,String> 业务请求附带参数,来自于设备调用语义理解服务额外携带的信息,只做透传
slotEntities List<SlotEntity> 从用户语句中抽取出的slot参数信息
domainId Long 领域ID
intentId Long 意图ID

requestData 中包含的主要信息字段

字段名 type 描述
city String 放置设备所处的城市信息

slotEntities 中每一个数组对象SlotEntity的具体字段:

字段名 type 描述 是否必要
intentParameterId Long 意图参数ID
intentParameterName String 意图参数名
originalValue String 原始句子中抽取出来的未做处理的slot值
standardValue String 原始slot归一化后的值
liveTime Integer 该slot已生存时间(会话轮数)
createTimeStamp Long 该slot产生的时间点

3、webhook 服务请求结构样例

POST http://my-service.com/action

 Headers:
 //默认已填充
 Content-type: application/json
 //用户自定义headers


POST body:
{
    "sessionId": "397bea5e-fc13-491e-8939-de37ba8d9c2c",
    "botId": 10090,
    "utterance": "给我来个冷笑话",
    "skillId": 10048,
    "skillName": "逗乐笑话",
    "intentName": "get_joke",
    "requestData": {},
    "slotEntities": [
        {
            "intentParameterId": 2695,
            "intentParameterName": "tag",
            "originalValue": "冷",
            "standardValue": "冷",
            "liveTime": 0,
            "createTimeStamp": 1491889193835,
            "slotName": "tag:tags",
            "slotValue": "冷"
        }
    ]
}

4、升级安全策略,选择RSA加密的请求数据传输

4.1 RSA加密使用说明

a、选择加密后,则会将前文提到的请求对象先转换成json串,然后进行加密传输
a、用户上传RSA的公钥串到开放平台,公私钥可以使用SDK提供的工具类生成,如果用户需要自己生成,秘钥位数必须设置为1024位,并需要将公钥以base64编码成格式友好的公钥串,如果用户是使用openssl等工具生成的公钥,则已经是编码后的了,可以直接上传使用
b、用户自己保存公钥,阿里开发者平台请求用户服务时,会将上述提到的TaskQuery对象进行加密,用户得到加密后的字符串后使用自己的私钥进行解密之后使用

4.2 RSA加密的请求对象结构

字段名 type 描述 是否必要
queryMetaInfo QueryMetaInfo 加密请求元数据
securityQuery String 将前文提到的TaskQuery对象进行加密后的字符串,该对象解密后就是前文提到的语义请求对象

QueryMetaInfo对象字段说明

字段名 type 描述 是否必要
source String 请求来源标示,值默认为
queryVersion String 加密策略版本
securityMode String 加密方式 当前为RSA

4.3 java方式的处理范例

@RequestMapping(value = "/skill/weather", method = RequestMethod.POST)
    public @ResponseBody ResultModel<TaskResult> getResponse(@RequestBody String securityWrapperTaskQuery,
                                                             @RequestHeader HttpHeaders headers) {
        try {

            //需要先得到加密后传输的对象
            SecurityWrapperTaskQuery wrapperTaskQuery = MetaFormat
                    .parseToWrapperQuery(securityWrapperTaskQuery);
            //然后利用RSA私钥解密语义理解对象,解密后为JSON格式的字符串
            String taskQuery = RSAUtil.decryptByPrivateKey(wrapperTaskQuery.getSecurityQuery(),
                    privateKey);
            /**
             * 将开发者平台识别到的语义理解的结果(json字符串格式)转换成TaskQuery
             */
            TaskQuery query = MetaFormat.parseToQuery(taskQuery);
            //进行处理执行

            /**
             * 构建服务返回结果
             */
            ResultModel<TaskResult> resultModel = new ResultModel<TaskResult>();
            return resultModel;
    }

4.4 加密请求串样例

POST http://my-service.com/action

 Headers:
 //默认已填充
 Content-type: application/json
 //用户自定义headers

POST body:
{
    "queryMetaInfo": {
        "queryVersion": "V1", 
        "securityMode": "RSA",
        "source": "ali-genie"
    },
 "securityQuery":"WrMHm5wsw1g8UIsNX2TQ/D2Dsk6g6WYdg205dmMtsAumm+evdfjH80q+Dh1ptZOviPpzuYC8VkI2Md9quXfte6QHu5/cwgjgmceBsk8oIQAt56241/XgPrIKLkwwgD7vmX+CEOLH+o4webbZUUpwsCmzO0CqOBXUljSMna+qc5AjwJmhpVofb0y3lhlfH+l+pzjtCULQugYCIfNO+5Pwa9JKoX3ZP93+FJErO2ar1Iedh3i7SVTF50PBlvVyR/RJtbobespYhW6Oza4A=="
}

5、返回字段说明

返回结果对象:

字段名 type 描述 是否必要
returnCode String “0”默认表示成功,其他不成功的字段自己可以确定
returnErrorSolution String 出错时解决办法的描述信息
returnMessage String 返回执行成功的描述信息
returnValue 意图理解后的执行结果

returnValue 字段内容:

字段名 type 描述 是否必要
reply String 回复播报语句
resultType String 回复时的状态标识(ASK_INF:信息获取,例如“请问从哪个城市出发”,在此状态下,用户说的下一句话优先进入本意图进行有效信息抽取 RESULT:正常完成交互的阶段并给出回复 CONFIRM:期待确认)
properties Map<String, String> 生成回复语句时携带的额外信息
askedInfos List<AskedInfoMsg> 在ASK_INF状态下,必须设置本次追问的具体参数名(开发者平台意图参数下配置的参数信息) 在ASK_INF状态下必须
actions List<Action> 播控类信息,目前只支持播放音频
executeCode String “SUCCESS”代表执行成功;“PARAMS_ERROR”代表接收到的请求参数出错;“EXECUTE_ERROR”代表自身代码有异常;“REPLY_ERROR”代表回复结果生成出错

AskedInfoMsg字段内容:

字段名 type 描述 是否必要
parameterName String 询问的参数名(非实体名)
intentId Long 意图ID,从请求参数中可以获得

特别说明一下Action结构
允许用户将音频文件上传至开放平台,获取到音频ID。播放某一音频ID对应的音频

字段名 type 描述 是否必要
name String Action名,该名字必须设置为“audioPlayGenieSource”
properties Map Action中的信息字段,“audioGenieId”的key必须设置,标示播放的开放平台存储的音频ID

6、返回结果json样例:

{
    "returnCode": "0",
    "returnErrorSolution": "",
    "returnMessage": "",
    "returnValue": {
        "reply": "哈哈哈",
        "resultType": "RESULT",
        "actions": [
            {
                "name": "audioPlayGenieSource",
                "properties": {
                    "audioGenieId": "123"
                }
            }
        ],
        "properties": {},
        "executeCode": "SUCCESS",
        "msgInfo": ""
    }
}

FAQ

上下文

测试显示IDE_REPLY_CREATE_ERROR

返回
顶部