API文档

产品定价

Hi,您好,欢迎使用有道智云英文作文批改(图像识别)API 接口服务。
本文档主要针对需要集成 HTTP API 的技术开发工程师,详细描述英文作文批改(图像识别)能力相关的技术内容。
如果您有与我们商务合作的需求,可以通过以下方式联系我们:
商务邮箱: AIcloud_Business@corp.youdao.com
如果您对文档内容有任何疑问,可以通过以下几种方式联系我们:
客服 QQ:1906538062
智云作文批改技术交流 QQ 1 群: 1092639188
联系邮箱: zhiyun@corp.youdao.com
温馨提示:
有道 AI 团队自研的基于深度学习网络技术的自动作文批改技术。覆盖学生英语学习全周期,提供高参考价值的智能批改功能,用户只需要输入一篇英文作文,选择对应的英文级别,智能批改服务即可一键给出文章得分、整体评价、拼写错误反馈、语法错误反馈、主题相关度反馈、以及高级词汇和推荐词组等一系列结果,从而向用户提供针对性的作文指导意见。
英文作文批改(图像识别) API HTTPS 地址:
https://openapi.youdao.com/correct_writing_image
调用方在集成文本翻译 API 时,请遵循以下规则。
规则 | 描述 |
---|---|
传输方式 | HTTPS |
请求方式 | POST |
字符编码 | 统一使用 UTF-8 编码 |
请求格式 | 表单 |
响应格式 | JSON |
调用 API 需要向接口发送以下字段来访问服务。
字段名 | 类型 | 含义 | 必填 | 备注 |
---|---|---|---|---|
q | text | 图片的 base64。大小不超过 5MB | True | 图片的 base64 |
langType | text | 语言,目前仅支持英文 | False | en |
appKey | text | 应用标识(应用 ID) | True | 可在 应用管理 查看 |
salt | text | 随机字符串 | True | hfa12lak56ja9gjl |
sign | text | 签名信息:sha256(appKey+input+salt+密钥) | True | xxxxx |
grade | text | 作文等级。支持列表见下面 grade 支持列表,默认不管等级,只评价句子好坏 | false | default |
title | text | 作文标题 | false | 0 |
modelContent | text | 用户提供的范文内容 | false | 0 |
goodExpression | text | 用户提供的好的表达 | false | 0 |
needTypo | text | 是否需要 typo,(true/false),默认为 true | false | true |
signType | text | 签名类型 | true | v3 |
limitedWords | text | 作文字数限制 | false | 1000 |
最好传输 limitedWords,这样评分更精确。
签名生成算法如下:
sha256 签名计算方法为:sha256(应用 ID+input+salt+当前 UTC 时间戳+密钥)。
其中,input 的计算方式为:input
=多个q拼接后前10个字符
+ 多个q拼接长度
+ 多个q拼接后十个字符
(当多个 q 拼接后长度大于 20)或 input
=多个q拼接的字符串
(当多个 q 拼接后长度小于等于 20)。
不同语言获取时间戳,请参看此此链接
如果对签名有疑问,可以参看各语言 demo。
级别 | 代码 |
---|---|
不考虑级别,单纯评价句子好坏 | default |
小学 | elementary |
初中 | junior |
高中 | high |
四级 | cet4 |
六级 | cet6 |
考研 | graduate |
托福 | toefl |
GRE | gre |
雅思 | ielts |
错误码 | 含义 |
---|---|
101 | 缺少必填的参数,首先确保必填参数齐全,然后,确认参数书写是否正确。 |
102 | 不支持的语言类型 |
103 | 翻译文本过长 |
104 | 不支持的 API 类型 |
105 | 不支持的签名类型 |
106 | 不支持的响应类型 |
107 | 不支持的传输加密类型 |
108 | appKey 无效,注册账号, 登录后台创建应用和实例并完成绑定, 可获得应用 ID 和密钥等信息,其中应用 ID 就是 appKey( 注意不是应用密钥) |
109 | batchLog 格式不正确 |
110 | 无相关服务的有效实例,应用没有绑定服务实例,可以新建服务实例,绑定服务实例。注:某些服务的翻译结果发音需要 tts 实例,需要在控制台创建语音合成实例绑定应用后方能使用。 |
111 | 开发者账号异常 |
201 | 解密失败,可能为 DES,BASE64,URLDecode 的错误 |
202 | 签名检验失败,如果确认应用 ID 和应用密钥的正确性,仍返回 202,一般是编码问题。请确保翻译文本 q 为 UTF-8 编码. |
203 | 访问 IP 地址不在可访问 IP 列表 |
205 | 请求的接口与应用的平台类型不一致,确保接入方式(Android SDK、IOS SDK、API)与创建的应用平台类型一致。 |
206 | 因为时间戳无效导致签名校验失败 |
207 | 重放请求 |
301 | 辞典查询失败 |
302 | 小语种查询失败 |
303 | 服务端的其它异常 |
401 | 账户已经欠费停 |
411 | 访问频率受限,请稍后访问 |
412 | 大图片请求过于频繁,请稍后访问 |
29001 | inputType 类型不对 |
29002 | needTypo 类型不对 |
29003 | Q 超过限制长度 |
29004 | grade 类型不对 |
29005 | 文件为空 |
29006 | 图片过大 |
29007 | 作文批改的文本内容为空 |
29301 | 作文批改请求失败 |
{
"errorCode": "错误码",
"Result": {
"uniqueKey": "每个请求独一无二的字符串标识",
"essayLangName": "语言信息",
"rawEssay": "请求原文",
"refEssay": "参考范文",
"stLevel": "作文级别",
"reqSource": "请求来源",
"extraParams": "额外请求参数(扩展用参数)",
"wordNum": "文章总词数"
"conjWordNum": "文章连接词数",
"AllFeatureAdvice": { # 作文各特征的建议
"WordNum": "词数建议,如文章字数疑似超出该考试字数要求",
"Spelling": "拼写错误建议",
"WordDiversity": "词汇丰富度建议,如词汇量积累非常少,只能给出一些零散的简单词汇,建议多积累词汇",
"Structure": "文章结构建议",
"AdvanceVocab": [
"xx", "xx", "xx" # 文中使用的高级词汇
],
"AdvanceVocabLeval": [0, 1, 2], # 对应高级词汇的级别
"lexicalSubs": [ # 词汇替换(注意:candidates中词汇可能为空,表示没有推荐替换的近义词,但word使用频率超过3次)
{"candidates": ["xx", "xx"], "count": 频率, "word": xx 对应词}, ...
]
"Conjunction": [{
"used": ["xx", "xx", "xx"] # 已使用连词
"advice": ["xx", "xx", "xx"] # 推荐词
}],
"Topic": "主题相关性建议",
"Grammar": "语法相关建议",
"GoodExpression": ["xx", "xx", "xx"] # 好的表达
},
"AllFeatureScore": { # 对应上面AllFeatureAdvice各特征得分,除NeuralScore是没有Advice的,它代表神经网络作文打分结果,不是最终打分结果!
"NeuralScore": 68.64, # 范围:[0,100]
"WordNum": 10, # 范围:[0, 10] ---> 指的是词数得分
"Spelling": 10, # 范围:[0, 10]
"WordDiversity": 0, # 范围:[0, 10]
"Structure": 8, # 范围:[0, 10]
"AdvanceVocab": 7.61, # 范围:[0, 10]
"Conjunction": 6.94, # 范围:[0, 10]
"Topic": 6.03, # 范围:[0, 10]
"Grammar": 2.5 # 范围:[0, 10]
"SentComplex": 10 # 范围: [0, 10]
},
"majorScore": { # 是AllFeatureScore中score整合结果
"WordScore": 10, # 词汇得分:包括词数、丰富度、高级词汇等得分
"GrammarScore": 10, # 语法得分:包括拼写、语法、句子复杂度得分等
"StructureScore": 10, # 逻辑得分:包括段落和连接词得分
"topicScore": 10, # 内容(主题相关性)得分,如果没有参考范文,该部分得分会从语法和复杂度上考虑
},
"essayFeedback":{
"sentsFeedback": [
{
"sentId": "句子在全文的编号,从0开始",
"paraId": "该句所在的段落号,从0开始",
"rawSent": "原句",
"segSent": "原句分词后的结果",
"correctedSent": "原句修正后的结果",
"sentStartPos": "该句子在全文中相对于文章初始位置的偏移量",
"errorPosInfos": [
{
"type": "错误类型(包括`grammar`,`typo`,`refactor`)",
"startPos": "错误起始位置相对rawSent起始位置的偏移量",
"endPos": "错误结束位置相对rawSent起始位置的偏移量",
"orgChunk": "错误块的具体内容",
"correctChunk": "错误块修正后的具体内容",
"error_type": "(弃用) 错误的具体类别(0表示拼写错误,1表示冠词错误,2表示动词时态或者第三人称单复数错误,3表示名词单复数错误,4表示格错误,5表示介词错误,6表示其他语法错误,7表示文本格式错误,8表示正确)",
"new_error_type": "错误类别(0表示完全正确,
1表示书写格式不规范,2表示拼写错误,
3表示标点错误,4表示冠词错误,5表示动词错误,
6表示名词单复数错误,7表示代词错误,8表示介词错误,
9表示形容词错误,10表示副词错误,11表示连词错误,
20表示其他错误,21表示代指所有语法错误(兼容))"
"new_sub_error_type": "细分错误类别(0表示正确,1表示未知错误,2表示词汇缺失,3表示词汇冗余,
4表示冠词误用,5表示介词误用,6表示动词主谓一致错误,7表示动词时态错误,8表示情态动词后应接动词原形错误,
9表示被动语态错误,10表示动词不定式错误,11表示动词错误,12表示形容词比较级错误,
13表示形容词最高级错误,14表示副词比较级错误,15表示副词最高级错误,16表示名词单复数错误,
17表示名词错误,18表示人称代词主宾格混淆,19表示人称代词和物主代词混淆,20表示形容词性和名词性代词混淆,
21表示人称代词和反身代词混淆,22表示疑问/关系/连接代词混淆,23表示指示代词混淆,24表示不定代词混淆,
25表示代词错误,26表示标点符号误用,27表示拼写错误,28表示不规范错误)"
"举例说明": 如果new_error_type=5, new_sub_error_type=2,说明是动词缺失
"reason": "错误的具体原因",
"isValidLangChunk": "类似下面的isValidSent,判断是否为合法片段(该片段如果语言检测结果与期望不一致,则认为不合法)"
"analysis": "错误的原因的具体辨析(保留接口,暂时应该没用)"
}, ..., {}
],
"isValidLangSent": "是否为合法句子(合法与否取决于语言检测对该句的语言信息识别结果与期望结果是否一致)"
"sentFeedback": "错误原因反馈,基于errorPosInfos中所有reason字段拼接而成",
"isContainTypoError": "返回是否含有typo错误",
"isContainGrammarError": "返回是否含有语法错误",
"sentScore": "句子得分(暂时没有用,即将实现)"
}
]
}
"totalScore": "文章最终得分"
"fullScore": "对应级别满分"
"essayAdvice": "文章最终评价"
"paraNum": "文章段落数"
"sentNum": "文章句子数"
}
}
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class ImagecorrectwritingV3Demo {
private static Logger logger = LoggerFactory.getLogger(ImagecorrectwritingV3Demo.class);
private static final String YOUDAO_URL = "https://openapi.youdao.com/correct_writing_image";
private static final String APP_KEY = "您的应用ID";
private static final String APP_SECRET = "您的应用密钥";
public static void main(String[] args) throws IOException {
Map<String,String> params = new HashMap<String,String>();
String q = loadAsBase64("图片的路径");
String salt = String.valueOf(System.currentTimeMillis());
String grade = "default";
params.put("q", q);
params.put("signType", "v3");
params.put("grade", grade);
String curtime = String.valueOf(System.currentTimeMillis() / 1000);
params.put("curtime", curtime);
String signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET;
String sign = getDigest(signStr);
params.put("appKey", APP_KEY);
params.put("salt", salt);
params.put("sign", sign);
params.put("signType", "v3");
String result = requestForHttp(YOUDAO_URL,params);
/** 处理结果 */
System.out.println(result);
}
public static String requestForHttp(String url,Map<String,String> params) throws IOException {
String result = "";
/** 创建HttpClient */
CloseableHttpClient httpClient = HttpClients.createDefault();
/** httpPost */
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
Iterator<Map.Entry<String,String>> it = params.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String,String> en = it.next();
String key = en.getKey();
String value = en.getValue();
paramsList.add(new BasicNameValuePair(key,value));
}
httpPost.setEntity(new UrlEncodedFormEntity(paramsList,"UTF-8"));
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
try{
HttpEntity httpEntity = httpResponse.getEntity();
result = EntityUtils.toString(httpEntity,"UTF-8");
EntityUtils.consume(httpEntity);
}finally {
try{
if(httpResponse!=null){
httpResponse.close();
}
}catch(IOException e){
logger.info("## release resouce error ##" + e);
}
}
return result;
}
/**
* 生成加密字段
*/
public static String getDigest(String string) {
if (string == null) {
return null;
}
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
byte[] btInput = string.getBytes();
try {
MessageDigest mdInst = MessageDigest.getInstance("SHA-256");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public static String loadAsBase64(String imgFile)
{//将图片文件转化为字节数组字符串,并对其进行Base64编码处理
File file = new File(imgFile);
if(!file.exists()){
logger.error("文件不存在");
return null;
}
InputStream in = null;
byte[] data = null;
//读取图片字节数组
try
{
in = new FileInputStream(imgFile);
data = new byte[in.available()];
in.read(data);
in.close();
}
catch (IOException e)
{
e.printStackTrace();
}
//对字节数组Base64编码
return Base64.getEncoder().encodeToString(data);//返回Base64编码过的字节数组字符串
}
public static String truncate(String q) {
if (q == null) {
return null;
}
int len = q.length();
String result;
return len <= 20 ? q : (q.substring(0, 10) + len + q.substring(len - 10, len));
}
}
# -*- coding: utf-8 -*-
import sys
import uuid
import requests
import base64
import hashlib
import time
reload(sys)
sys.setdefaultencoding('utf-8')
YOUDAO_URL = 'https://openapi.youdao.com/correct_writing_image'
APP_KEY = '您的应用ID'
APP_SECRET = '您的应用密钥'
def truncate(q):
if q is None:
return None
size = len(q)
return q if size <= 20 else q[0:10] + str(size) + q[size - 10:size]
def encrypt(signStr):
hash_algorithm = hashlib.sha256()
hash_algorithm.update(signStr.encode('utf-8'))
return hash_algorithm.hexdigest()
def do_request(data):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
return requests.post(YOUDAO_URL, data=data, headers=headers)
def connect():
f = open(r'图片的路径', 'rb') # 二进制方式打开图文件
q = base64.b64encode(f.read()) # 读取文件内容,转换为base64编码
f.close()
data = {}
grade = "您的年级"
curtime = str(int(time.time()))
data['curtime'] = curtime
salt = str(uuid.uuid1())
signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
sign = encrypt(signStr)
data['appKey'] = APP_KEY
data['salt'] = salt
data['q'] = q
data['sign'] = sign
data['grade'] = grade
data['signType'] = 'v3'
response = do_request(data)
print response.content
if __name__ == '__main__':
connect()
using System;
using System.IO;
using System.Web;
using System.Net;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Cryptography;
namespace zhiyun_csharp_demo
{
class ImagecorrectwritingV3Demo
{
public static void Main()
{
Dictionary<String, String> dic = new Dictionary<String, String>();
string url = "https://openapi.youdao.com/correct_writing_image";
string q = LoadAsBase64("图片的路径");
string appKey = "您的应用ID";
string appSecret = "您的应用密钥";
string grade = "待输入年级";
string salt = DateTime.Now.Millisecond.ToString();
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc));
long millis = (long) ts.TotalMilliseconds;
string curtime = Convert.ToString(millis / 1000);
dic.Add("curtime", curtime);
dic.Add("q", q);
string signStr = appKey + Truncate(q) + salt + curtime + appSecret;;
string sign = ComputeHash(signStr, new SHA256CryptoServiceProvider());
dic.Add("appKey", appKey);
dic.Add("salt", salt);
dic.Add("sign", sign);
dic.Add("signType", "v3");
dic.Add("grade", grade);
string result = Post(url, dic);
Console.WriteLine(result);
}
protected static string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes).Replace("-", "");
}
protected static string Truncate(string q)
{
if (q == null)
{
return null;
}
int len = q.Length;
return len <= 20 ? q : (q.Substring(0, 10) + len + q.Substring(len - 10, 10));
}
protected static string LoadAsBase64(string filename)
{
try
{
FileStream filestream = new FileStream(filename, FileMode.Open);
byte[] arr = new byte[filestream.Length];
filestream.Position = 0;
filestream.Read(arr, 0, (int)filestream.Length);
filestream.Close();
return Convert.ToBase64String(arr);
}
catch (Exception ex)
{
return null;
}
}
protected static string Post(string url, Dictionary<String, String> dic)
{
string result = "";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
StringBuilder builder = new StringBuilder();
int i = 0;
foreach (var item in dic)
{
if (i > 0)
builder.Append("&");
builder.AppendFormat("{0}={1}", item.Key, item.Value);
i++;
}
byte[] data = Encoding.UTF8.GetBytes(builder.ToString());
req.ContentLength = data.Length;
using (Stream reqStream = req.GetRequestStream())
{
reqStream.Write(data, 0, data.Length);
reqStream.Close();
}
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
return result;
}
}
}
<?php
define("CURL_TIMEOUT", 2000);
define("URL", "https://openapi.youdao.com/correct_writing_image");
define("APP_KEY", "您的应用ID"); // 替换为您的应用ID
define("SEC_KEY", "您的应用密钥"); // 替换为您的密钥
function do_request($q)
{
$salt = create_guid();
$args = array(
'q' => $q,
'appKey' => APP_KEY,
'salt' => $salt,
);
$curtime = strtotime("now");
$args['curtime'] = $curtime;
$grade = "default";
$args['grade'] = $grade;
$args['signType'] = "v3";
$signStr = APP_KEY . truncate($q) . $salt . $curtime . SEC_KEY;
$args['sign'] = hash("sha256", $signStr);
$ret = call(URL, $args);
print_r($ret);
$ret = json_decode($ret, true);
return $ret;
}
// 发起网络请求
function call($url, $args=null, $method="post", $testflag = 0, $timeout = CURL_TIMEOUT, $headers=array())
{
$ret = false;
$i = 0;
while($ret === false)
{
if($i > 1)
break;
if($i > 0)
{
sleep(1);
}
$ret = callOnce($url, $args, $method, false, $timeout, $headers);
$i++;
}
return $ret;
}
function callOnce($url, $args=null, $method="post", $withCookie = false, $timeout = CURL_TIMEOUT, $headers=array())
{
$ch = curl_init();
if($method == "post")
{
$data = convert($args);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POST, 1);
}
else
{
$data = convert($args);
if($data)
{
if(stripos($url, "?") > 0)
{
$url .= "&$data";
}
else
{
$url .= "?$data";
}
}
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(!empty($headers))
{
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
if($withCookie)
{
curl_setopt($ch, CURLOPT_COOKIEJAR, $_COOKIE);
}
$r = curl_exec($ch);
curl_close($ch);
return $r;
}
function convert(&$args)
{
$data = '';
if (is_array($args))
{
foreach ($args as $key=>$val)
{
if (is_array($val))
{
foreach ($val as $k=>$v)
{
$data .= $key.'['.$k.']='.rawurlencode($v).'&';
}
}
else
{
$data .="$key=".rawurlencode($val)."&";
}
}
return trim($data, "&");
}
return $args;
}
// uuid generator
function create_guid(){
$microTime = microtime();
list($a_dec, $a_sec) = explode(" ", $microTime);
$dec_hex = dechex($a_dec* 1000000);
$sec_hex = dechex($a_sec);
ensure_length($dec_hex, 5);
ensure_length($sec_hex, 6);
$guid = "";
$guid .= $dec_hex;
$guid .= create_guid_section(3);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= $sec_hex;
$guid .= create_guid_section(6);
return $guid;
}
function truncate($q) {
$len = strlen($q);
return $len <= 20 ? $q : (substr($q, 0, 10) . $len . substr($q, $len - 10, $len));
}
function ensure_length(&$string, $length){
$strlen = strlen($string);
if($strlen < $length)
{
$string = str_pad($string, $length, "0");
}
else if($strlen > $length)
{
$string = substr($string, 0, $length);
}
}
function create_guid_section($characters){
$return = "";
for($i = 0; $i < $characters; $i++)
{
$return .= dechex(mt_rand(0,15));
}
return $return;
}
// 输入
$file = "图片的路径";
$fp = fopen($file, "r") or die("Can't open file");
// base64编码
$q = base64_encode(fread($fp, filesize($file)));
fclose($fp);
do_request($q);
?>