# API接入协议
# 接口说明
1、构建成功并发布上线的对话流,可以通过API实现人机之间按照设计好的交互逻辑进行持续,双向,自然地沟通。
2、接口调用所需参数chatflow_id
(对话流唯一标识)和apiKey
(接口秘钥)由iFLYOS 对话流平台提供,调用方管理。
3、调用方可以根据需要重置apiKey
,以确保秘钥安全。
注意
- 在iFLYOS 对话流平台创建对话流并构建成功后即可通过此API协议进行接入测试,测试期间每天限制500次调用,如果要获得更多的调用次数,需提交上线发布申请,并由后台管理员审核通过。
# 接口要求
集成对话流API时,需按照以下要求
参数 | 说明 |
---|---|
请求协议 | http[s](为提高安全性,强烈推荐https) |
请求地址 | http[s]: //chatflow.iflyos.cn/app/ |
请求方式 | POST |
接口鉴权 | 详情请参照下方接口鉴权 |
字符编码 | UTF-8 |
响应格式 | 统一采用JSON格式 |
开发语言 | 任意,只要可以向对话流服务发起HTTP请求的均可 |
# 接口鉴权
对话流API根据以下参数进行鉴权验证:
参数 | 说明 |
---|---|
chatflow_id | 对话流唯一标识 |
ts | 当前时间戳,从1970年1月1日0点0分0秒开始到现在的秒数 |
apiKey | 接口密钥,由iFLYOS 对话流平台提供,调用方管理 |
signature | 加密数字签名,计算方法:HmacSHA1(MD5(chatflow_id+ ts),apiKey) |
# signature生成示例
① 获取baseString
baseString由chatflow_id和当前时间戳ts拼接而成;
假如chatflow_id = 202988d20e5d4c7aa7ba1a4a64ab9d8f,ts = 1502607694,则baseString = 202988d20e5d4c7aa7ba1a4a64ab9d8f1502607694
② 对baseString进行MD5
假如baseString为上一步生成的202988d20e5d4c7aa7ba1a4a64ab9d8f1502607694,MD5之后则为 0829d4012497c14a30e7e72aeebe565e
③ 以apiKey为key对MD5之后的baseString进行HmacSHA1加密,然后再对加密后的字符串进行base64编码。
假如apiKey = d9f4aa7ea6d94faca62cd88a28fd5234,MD5之后的baseString为上一步生成的0829d4012497c14a30e7e72aeebe565e,则HmacSHA1加密之后再进行base64编码得到的signature为: IrrzsJeOFk1NGfJHW6SkHUoN9CU=
# 备注:
- signature 有效期:出于安全性考虑,每个 signature 的有效期为 5 分钟(用 ts 计算),同时 ts 要与标准时间同步,否则,时间相差太大,服务端会直接认为 ts 无效;
- BASE64 编码采用 MIME 格式,字符包括大小写字母各26个,加上10个数字,和加号 + ,斜杠 / ,一共64个字符。
# 请求头
Content-Type: application/json; charset=utf-8
# 请求体
请求参数构造成json对象放入请求体,参数说明如下:
参数 | 类型 | 必须 | 说明 | 示例 |
---|---|---|---|---|
chatflow_id | string | 是 | 对话流唯一标识 | 202988d20e5d4c7aa7ba1a4a64ab9d8f |
signature | string | 是 | 加密数字签名(基于HMACSHA1算法,生成方式见【授权认证】) | BFQEcN3SgZNC4eECvq0LFUPVHvI= |
ts | string | 是 | 当前时间戳,从1970年1月1日0点0分0秒开始到现在的秒数 | 1502607694 |
auth_id | string | 是 | 用户唯一ID(32位字符串,包括英文小写字母与数字,开发者需保证该值与终端用户一一对应) | 2049a1b2fdedae553bd03ce6f4820ac4 |
data_type | string | 是 | 数据类型,可选值:text(文本),audio(音频) | text |
data | string | 是 | 数据内容(文本内容或音频二进制字节数组),采用base64编码后的值 | 北京的天气 |
test | boolean | 否 | 是否为测试调用,如果不传则默认为正式调用 | true |
asr | boolean | 否 | 音频请求时是否需要返回语音识别结果,如果不传则默认不返回 | true |
sample_rate | string | 否 | 音频采样率,可选值:16000(16k采样率)、8000(8k采样率),默认为16000 | 16000 |
aue | string | 否 | 音频编码,可选值:raw(未压缩的pcm或wav格式)、speex(speex格式,即sample_rate=8000的speex音频)、speex-wb(宽频speex格式,即sample_rate=16000的speex音频),默认为 raw | raw |
speex_size | string | 否 | speex音频帧大小,speex音频必传。详见speex_size与speex库压缩等级关系表 | 60 |
# 返回值
返回结果为 JSON 对象,字段说明如下:
字段名称 | 类型 | 说明 |
---|---|---|
code | string | 结果码(具体见错误码) |
desc | string | 描述 |
sid | string | 会话ID |
data | array | 结果数据 |
其中 sid 字段主要用于追查问题,如果出现问题,可以提供 sid 给讯飞技术人员帮助确认问题。
# data 字段说明
data为一个对象数组,包含语音识别、语义理解、回复内容等结果,字段说明如下:
字段名称 | 类型 | 说明 |
---|---|---|
type | String | 结果类型 |
content | object | 结果内容 |
语义理解结果说明:
语义理解结果的type值为semantic,content字段说明如下:
字段 | 类型 | 说明 |
---|---|---|
qa | boolean | 是否为qa,保留字段 |
template | string | 语料模板 |
rc | int | 语义理解服务返回码 |
slots | object | 语义理解的槽位信息 |
text | string | 语义理解的输入文本 |
intent | string | 意图 |
version | string | 语义理解的版本 |
sid | string | 语义理解服务返回的sid,用于追踪语义理解服务的调用链路 |
识别结果说明:
语音识别结果的的type值为asr,content字段说明如下:
字段 | 类型 | 说明 |
---|---|---|
sn | number | 第几句 |
ls | boolean | 是否最后一句 |
bg | number | 开始 |
ed | number | 结束 |
ws | array | 词 |
cw | array | 中文分词 |
w | string | 单字 |
sc | number | 分数 |
回复结果说明:
回复结果的的type值为answer,content字段说明如下:
字段 | 类型 | 说明 |
---|---|---|
type | string | 回复类型:文本(text),音频(audio) |
text | string | 回复的文本内容 |
data | string | type为audio时,回复的音频内容经过base64压缩后的结果 |
chatStop | boolean | 此次回复以后,对话流是否结束 |
结果示例
{
"code": "0",
"data": [
{
"content": {
"qa": false,
"template": "{chinacity}的天气",
"rc": 0,
"slots": [
{
"name": "chinacity",
"value": "深圳",
"normValue": "深圳市"
}
],
"text": "深圳的天气",
"intent": "weather",
"version": "2.0",
"sid": "atn003aeb55@dx00071125b3f7a11201"
},
"type": "semantic"
},
{
"content": {
"chatStop": false,
"text": "天气很好",
"type": "text"
},
"type": "answer"
}
],
"desc": "success",
"sid": "072fbe87f4014e0dad0ceb230bc62ada"
}
# 错误码
错误码 | 描述 | 说明 | 处理方式 |
---|---|---|---|
0 | success | 成功 | |
10100 | bad_request | 通用错误请求 | 根据错误描述进行判断处理 |
10105 | illegal_access | 没有权限 | 检查apiKey,ip,checkSum等授权参数是否正确 |
10106 | invalid_parameter | 无效参数或参数值为空 | 上传必要的参数, 检查参数格式以及编码 |
10107 | illegal_arameter | 非法参数值 | 检查参数值是否超过范围或不符合要求 |
10108 | not_published | 对话流未发布 | 申请发布并等待审核通过 |
10109 | illegal_data_length | 数据长度非法 | 检查传递的音频和文本是否超过长度 |
10110 | no_license | 无授权许可 | |
10111 | exceed_free_count | 超过免费调用次数 | |
10112 | chatFlow_not_existed | 对话流不存在 | 检查传入的chatflow_id是否有误 |
10114 | time_out | 超时 | |
10301 | parse_data_error | 解析数据错误 | |
10302 | request_tts_error | 请求tts服务错误 | 提供接口返回值,向服务商反馈 |
10303 | parse_tts_error | 解析tts结果数据错误 | 提供接口返回值,向服务商反馈 |
20001 | flow_json_parse_error | flow配置json解析错误 | |
20002 | load_chatflow_config_error | 加载chatFlow配置信息错误 | |
30001 | no_node_error | 配置节点未发现 | |
30002 | no_enter_node_error | 未发现Enter节点 | |
30005 | execute_script_error | 执行脚本错误 | 检查脚本是否正确 |
30008 | no_match_rule | 未匹配上任何规则 | 检查判断条件判断配置 |
30012 | http_request_error | 调用第三方接口错误 | 检查http节点的配置是否正确 |
40003 | request_nlu_timeout | 请求语义理解服务超时 | |
40004 | request_asr_timeout | 请求识别服务超时 |
# 调用示例
java程序调用示例
说明:需要将代码中的 CHATFLOW_ID、API_KEY参数替换成平台已发布对话流的 chatflow_id 及对应的 apiKey。
package com.iflytek.flow.demo;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;
/**
* iFLYOS 对话流平台 API调用示例
* <p>
* 运行方法:直接运行 main()
* <p>
* 结果: 控制台输出接口返回值信息
*
* @author iflyos_chatflow
*/
public class WebApiDemo {
private static final String URL = "https://chatflow.iflyos.cn/app/";
private static final String CHATFLOW_ID = "";
private static final String API_KEY = "";
private static final String AUTH_ID = "12345";
private static final String QUERY_TEXT = "帮我订下酒店";
public static void main(String[] args) throws SignatureException {
Map<String, String> header = new HashMap<String, String>();
header.put("Content-Type", "application/json; charset=utf-8");
JSONObject body = new JSONObject();
body.put("auth_id", AUTH_ID);
body.put("data_type", "text");
body.put("chatflow_id", CHATFLOW_ID);
body.put("test", true);//测试调用需加此参数,发布后正式调用去掉
String ts = String.valueOf(System.currentTimeMillis() / 1000);
body.put("ts", ts);
body.put("data", Base64.encodeBase64String(QUERY_TEXT.getBytes()));
String signature = HmacSHA1Encrypt(MD5(CHATFLOW_ID + ts), API_KEY);
body.put("signature", signature);
String result = httpPost(URL, header, body.toJSONString());
System.out.println(result);
}
/**
* 加密数字签名(基于HMACSHA1算法)
*
* @param encryptText
* @param encryptKey
* @return
* @throws SignatureException
*/
private static String HmacSHA1Encrypt(String encryptText, String encryptKey) throws SignatureException {
byte[] rawHmac = null;
try {
byte[] data = encryptKey.getBytes("UTF-8");
SecretKeySpec secretKey = new SecretKeySpec(data, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKey);
byte[] text = encryptText.getBytes("UTF-8");
rawHmac = mac.doFinal(text);
} catch (InvalidKeyException e) {
throw new SignatureException("InvalidKeyException:" + e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new SignatureException("NoSuchAlgorithmException:" + e.getMessage());
} catch (UnsupportedEncodingException e) {
throw new SignatureException("UnsupportedEncodingException:" + e.getMessage());
}
String oauth = new String(Base64.encodeBase64(rawHmac));
return oauth;
}
private final static String MD5(String pstr) {
char md5String[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] btInput = pstr.getBytes();
MessageDigest mdInst = MessageDigest.getInstance("MD5");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) { // i = 0
byte byte0 = md[i]; // 95
str[k++] = md5String[byte0 >>> 4 & 0xf]; // 5
str[k++] = md5String[byte0 & 0xf]; // F
}
return new String(str);
} catch (Exception e) {
return null;
}
}
/**
* post请求
*
* @param url
* @param header
* @param body
* @return
*/
private static String httpPost(String url, Map<String, String> header, String body) {
String result = "";
BufferedReader in = null;
OutputStream out = null;
try {
URL realUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
for (String key : header.keySet()) {
connection.setRequestProperty(key, header.get(key));
}
connection.setDoOutput(true);
connection.setDoInput(true);
//connection.setConnectTimeout(20000);
//connection.setReadTimeout(20000);
try {
out = connection.getOutputStream();
out.write(body.getBytes());
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
try {
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}