Webhook 接入方式会将语义理解服务生成的数据传递到您的 Web 服务中并从中获取结果。该 Web 服务需要开发者自己搭建,并且需要遵从平台预先定义好的输入输出。

1. 认证文件校验

目前开放平台将对开发者填写的 Webhook 服务地址做真实性校验(检测服务地址的有效性和开发者对服务地址是否有权限)
在配置 Webhook URL 前请先下载技能的认证文件,并将认证文件配置在服务器中供平台检测。
1587868271656-a354821b-385b-452a-9af0-812758710912.png

认证文件的配置方法为:

  • 如果您开发的是基于 Tomcat 等运行的普通 Web 项目,请将下载的认证文件放到要配置的服务地址所属服务器软件上。PS:服务器软件是指 Tomcat / Nginx / Jetty 等,而不是运行服务器所依赖的操作系统。文件存放路径为:服务器软件根目录/aligenie/认证文件。
  • 如果您开发的是 Springboot Web 项目,最后是可执行 jar 包在服务器运行,Springboot 项目内置有 Tomcat 插件,所以认证文件放置在项目中即可,文件存放路径为:resources/static/aligenie/认证文件。

平台的检测方法为:
获取您配置的 Webhook URL,取出URL中域名和端口号(如果有),然后拼接上”aligenie/认证文件名.txt “访问这个路径,以返回的结果(认证文件的内容)作为依据,判断认证是否成功。
例如:

  1. 下载到的认证文件是 13f776873db7e9dfae87121bcec0712a.txt;
  2. 意图内配置的 Webhook URL 为 https://webhook-service.com/**
  3. 那么我们将访问 https://webhook-service.com/aligenie/13f776873db7e9dfae87121bcec0712a.txt;
  4. 将获取到的结果做校验,校验通过 Webhook URL 才能配置成功。

您可以手动访问认证文件的链接,看能否正确获取到认证文件的内容,即可确认认证文件是否配置成功。

若提示无效URL,说明URL格式不正确、无法访问或访问被拒绝。
若提示未正确获取到文件,说明在指定目录下没有认证文件或文件内容不正确。
当意图配置的Webhook URL是https链接时,如果服务器SSL证书无效,也会无法正确获取到文件。

PS: 每个 技能 只会生成一个认证文件,若开发者服务器上已经存放该文件,则无需再次下载认证文件;下载到的认证文件请不要修改文件名称和文件内容,以免认证失败。

2. Webhook服务搭建方式

无论您选择何种语言开发技能的 Webhook 服务,都需要准从平台规定的输入输出格式要求。详情参考【请求响应协议】。

对于使用 Java 语言开发的服务,我们有提供 SDK 帮助开发者转换请求响应对象。java开发SDK点此下载,或引入如下maven坐标下载

  1. <dependency>
  2. <groupId>com.alibaba.da.coin</groupId>
  3. <artifactId>semantic-execute-meta</artifactId>
  4. <version>1.1.18-REALEASE</version>
  5. </dependency>

以天气查询为例的服务提供者的demo示例:

  1. @Controller
  2. public class RequestController {
  3. private static final Logger logger = LoggerFactory.getLogger(RequestController.class);
  4. @Autowired
  5. private WeatherHandle weatherHandle;
  6. /**
  7. * skill开发者提供的技能执行路径地址,请求方式为POST请求
  8. *
  9. * @param taskQuery
  10. * @return
  11. */
  12. @RequestMapping(value = "/skill/weather", method = RequestMethod.POST)
  13. public @ResponseBody ResultModel<TaskResult> getResponse(@RequestBody String taskQuery) {
  14. /**
  15. * 将开发者平台识别到的语义理解的结果(json字符串格式)转换成TaskQuery
  16. */
  17. logger.info("TaskQuery:{}", taskQuery.toString());
  18. TaskQuery query = MetaFormat.parseToQuery(taskQuery);
  19. /**
  20. * 构建服务返回结果
  21. */
  22. ResultModel<TaskResult> resultModel = new ResultModel<TaskResult>();
  23. try {
  24. /**
  25. * 调用天气服务执行并构建回复内容
  26. */
  27. TaskResult result = weatherHandle.execute(query);
  28. resultModel.setReturnCode("0");
  29. resultModel.setReturnValue(result);
  30. } catch (Exception e) {
  31. resultModel.setReturnCode("-1");
  32. resultModel.setReturnErrorSolution(e.getMessage());
  33. }
  34. /**
  35. * 直接返回ResultModel<TaskResult>对象就ok
  36. */
  37. return resultModel;
  38. }
  39. }
  40. // 天气服务执行,根据NLU理解的结果做相应处理并返回回复语句
  41. @Component
  42. public class WeatherHandleImpl implements WeatherHandle {
  43. @Override
  44. public TaskResult execute(TaskQuery taskQuery) {
  45. logger.info("WeatherHandleImpl execute...");
  46. //从请求中获取意图参数以及参数值
  47. Map<String, String> paramMap = taskQuery
  48. .getSlotEntities()
  49. .stream()
  50. .collect(
  51. Collectors.toMap(slotItem -> slotItem.getIntentParameterName(),
  52. slotItem -> slotItem.getStandardValue()));
  53. logger.info("paramMap :" + paramMap.toString());
  54. //如果意图是询问空气质量,则执行空气质量逻辑
  55. if (taskQuery.getIntentName().equals("空气质量")) {
  56. return aqiQuery(taskQuery, paramMap);
  57. //如果意图是询问天气情况,则执行天气查询逻辑
  58. } else if (taskQuery.getIntentName().equals("天气查询")) {
  59. return baseQuery(taskQuery, paramMap);
  60. } else {
  61. return null;
  62. }
  63. }
  64. private TaskResult baseQuery(TaskQuery taskQuery, Map<String, String> paramMap) {
  65. TaskResult result = new TaskResult();
  66. try {
  67. //请求服务并填充回复语句
  68. List<NameValuePair> params = new ArrayList<NameValuePair>();
  69. params.add(new BasicNameValuePair("areaName", paramMap.get("city")));
  70. params.add(new BasicNameValuePair("date", DateUtil.getStartDate(paramMap.get("time"))));
  71. String executeBody = httpGet(params);
  72. String weather = getWeather(executeBody);
  73. Map<String, String> properties = new HashMap<String, String>();
  74. properties.put("city", paramMap.get("city"));
  75. properties.put("time", paramMap.get("time"));
  76. properties.put("weather", weather);
  77. properties.put("temp_low", getTempLow(executeBody));
  78. properties.put("temp_high", getTempHigh(executeBody));
  79. properties.put("wind_direct", getWindDirect(executeBody));
  80. properties.put("power", getPower(executeBody));
  81. if (weather == null) {
  82. result.setReply("对不起,我现在只支持查询最近4天的天气");
  83. } else {
  84. result.setReply(TemplateFillUtil
  85. .fillTemplate(
  86. "@{city} @{time}天气 @{weather},温度@{temp_low}到@{temp_high}度,@{wind_direct}@{power}",
  87. properties));
  88. }
  89. result.setExecuteCode(ExecuteCode.SUCCESS);
  90. result.setResultType(ResultType.RESULT);
  91. } catch (Exception e) {
  92. logger.info("query exception", e);
  93. result.setExecuteCode(ExecuteCode.EXECUTE_ERROR);
  94. }
  95. return result;
  96. }
  97. }
  1. 在您开发好开发 Webhook 服务后,将此 Web 服务的 URL 地址填入意图的 回复逻辑 中。
  2. 当测试技能时开放平台意图逻辑引擎会将语义理解之后的结果以 http(s) post 的方式调用此服务。
  3. 您也可以自定义header字段,这样在发送 post 请求时,请求头中会携带您定义的信息。
  4. 服务拿到语义理解的结果之后就可以进行相应的处理,并按照意图逻辑规定的方式返回处理后的结果。