위키백과 QA API

위키백과 QA API 란?

자연어로 기술된 질문의 의미를 분석하여, 위키백과 문서에서 정답과 신뢰도 및 검색 단락을 추론하여 제공하는 API 입니다.

위키백과 QA는 질문에서 물어보고자 하는 대상의 정의와 속성에 대해 답을 제공할 수 있을 뿐만 아니라 인물, 장소, 작품 등 다양한 단답형 정답을 묻는 질문에도 답을 할 수 있습니다. 보다 정확한 답을 제공하기 위해 정보검색을 기반으로 언어분석된 결과로부터 정답후보를 찾는 NLP 기반 질의응답 시스템과 기계독해 기반의 딥러닝 기반 질의응답 시스템, 지식베이스 기반의 질의응답 시스템을 통합하여 위키백과 QA 시스템을 구성하였습니다. 정답을 찾는 데이터로는 한국어 위키피디아(2018년 10월 버전, 43만건 문서로 구성) 및 우리말샘 사전을 사용하였습니다.

위키백과QA API는HTTP 기반의 REST API 인터페이스로 JSON 포맷 기반의 입력 및 출력을 지원하며 ETRI에서 제공하는 API Key 인증을 통해 사용할 수 있는 Open API 입니다.

API 호출 1일 허용량
기술명 API명 1일 허용량
위키백과 QA API 사용하기

위키백과 QA API는 REST API이며, 질문과 QA 타입 데이터를 HTTP 통신으로 ETRI Open API 서버에 전달하면 됩니다. QA 타입은 검색과 기계독해 기반의 질의응답 시스템을 통합한 질의응답 방식(irqa)과 지식베이스 기반의 질의응답 방식(kbqa), 그리고 두 가지를 통합한 질의응답 방식(hybridqa) 중 하나를 반드시 지정해야 합니다. 서버가 제공하는 REST API의 URI는 다음과 같으며 POST 방식으로 호출해야 합니다.

http://aiopen.etri.re.kr:8000/WikiQA

HTTP 요청으로 위키백과 QA를 요청할 때 사전 준비 사항에서 발급받은 Access key정보를 요청 본문에 포함시켜야 합니다. 다음은 HTTP 요청 메시지 예입니다.

[HTTP Request Body]
{
    "request_id": "reserved field",
    "access_key": “YOUR_ACCESS_KEY”,
    "argument": {
    	"type": “ENGINE_TYPE”,
    	"question": “YOUR_QUESTION”	
    }
}
								

위와 같은 HTTP 요청을 ETRI Open API 서버로 전달하면 ETRI Open API 서버는 JSON 형태의 Text 데이터를 HTTP 응답 메시지로 반환합니다. 다음은 HTTP 응답 예입니다.

[HTTP Respone Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:0
Content-Type:application/json; charset=UTF-8

[HTTP Respone Body]
{
    "request_id": "reserved field",
    "result": 0,
    "return_type": "com.google.gson.internal.LinkedTreeMap",
    "return_object": {위키백과 QA 결과 JSON}
}
								
구현 예제
Java

JSON parsing을 위해 Gson 라이브러리를 사용하여 제공하고 있습니다. Gson 라이브러리에 대한 자세한 설명은 https://github.com/google/gson 에서 확인 하실 수 있습니다.

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;

public class Example {

    static public void main ( String[] args ) {
    	String openApiURL = "http://aiopen.etri.re.kr:8000/WikiQA";
    	String accessKey = "YOUR_ACCESS_KEY";    // 발급받은 API Key
    	String type = "ENGINE_TYPE";            // 분석할 문단 데이터
    	String question = "YOUR_QUESTION";          // 질문 데이터
    	Gson gson = new Gson();
    
    	Map<String, Object> request = new HashMap<>();
    	Map<String, String> argument = new HashMap<>();
    
    	argument.put("question", question);
    	argument.put("type", type);
    
    	request.put("access_key", accessKey);
    	request.put("argument", argument);
    
    
    	URL url;
    	Integer responseCode = null;
    	String responBody = null;
    	try {
    			url = new URL(openApiURL);
    			HttpURLConnection con = (HttpURLConnection)url.openConnection();
    			con.setRequestMethod("POST");
    			con.setDoOutput(true);
    
    			DataOutputStream wr = new DataOutputStream(con.getOutputStream());
    			wr.write(gson.toJson(request).getBytes("UTF-8"));
    			wr.flush();
    			wr.close();
    
    			responseCode = con.getResponseCode();
    			InputStream is = con.getInputStream();
    			byte[] buffer = new byte[is.available()];
    			int byteRead = is.read(buffer);
    			responBody = new String(buffer);
    
    			System.out.println("[responseCode] " + responseCode);
    			System.out.println("[responBody]");
    			System.out.println(responBody);
    
    	} catch (MalformedURLException e) {
    			e.printStackTrace();
    	} catch (IOException e) {
    			e.printStackTrace();
    	}
    }
}
											
PHP
    <?php
    $openApiURL = "http://aiopen.etri.re.kr:8000/WikiQA";
    $accessKey = "YOUR_ACCESS_KEY";
    $question = "YOUR_QUESTION";
    $type = "ENGINE_TYPE";
    
    $request = array(
        "access_key" => $accessKey,
        "argument" => array (
        	"question" => $question,
        	"type" => $type,
        )
    );
    
    try {
        $server_output = "";
        $ch = curl_init();
        $header = array(
        	"Content-Type:application/json; charset=UTF-8",
        );
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_URL, $openApiURL);
        curl_setopt($ch, CURLOPT_VERBOSE, true);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode ( $request) );
        
        $server_output = curl_exec ($ch);
        if($server_output === false) {
        	echo "Error Number:".curl_errno($ch)."\n";
        	echo "Error String:".curl_error($ch)."\n";
        }
        
        curl_close ($ch);
    } catch ( Exception $e ) {
    	echo $e->getMessage ();
    }
    
    echo "result = " . var_dump($server_output);
?>
											
C++

JSON parsing을 위해 jsoncpp 라이브러리를 사용하여 제공하고 있습니다. jsoncpp 라이브러리에 대한 자세한 설명은 https://github.com/open-source-parsers/jsoncpp 에서 확인 하실 수 있습니다.

HTTP 통신을 위해 curl 라이브러리를 사용하여 제공하고 있습니다. curl 라이브러리에 대한 자세한 설명은 https://curl.haxx.se/libcurl 에서 확인 하실 수 있습니다.

컴파일을 위해서는 아래와 같이 추가된 LIB에 대한 옵션을 추가해야 합니다.
g++ (c++파일명) (JSONCPP)/json/json.h (JSONCPP)/json/json-forwards.h (JSONCPP)/jsoncpp.cpp ?I(CURL)/include -lcurl

#include <curl/curl.h>
#include <json/json.h>
#include <iostream>
#include <string>

using namespace std;

size_t writeDataFunc(void *ptr, size_t size, size_t nmemb, std::string stream);

int main() {
char* openApiURL = (char*)"http://aiopen.etri.re.kr:8000/WikiQA";
string accessKey = "YOUR_ACCESS_KEY";
string question = "YOUR_QUESTION";
string type = "ENGINE_TYPE";

Json::Value request;
Json::Value argument;

argument["type"] = type;
argument["question"] = question;

request["access_key"] = accessKey;
request["argument"] = argument;

CURL *curl;
curl_slist* responseHeaders = NULL;
curl = curl_easy_init();

if( curl == NULL ) {
	cout << "Unable to initialize cURL interface" << endl ;
} else {
	responseHeaders = curl_slist_append( responseHeaders , "Content-Type: application/json; charset=UTF-8" ) ;
	string requestJson = request.toStyledString();
	long statusCode;
	string response;

	curl_easy_setopt(curl, CURLOPT_URL, openApiURL);
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, responseHeaders ) ;
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestJson.c_str());
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeDataFunc);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

	curl_easy_perform(curl);

	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
	curl_easy_cleanup(curl);

	cout << "[responseCode] " << statusCode << endl;
	cout << "[responBody]" << endl;
	cout << response << endl;
}

return 0;
}

size_t writeDataFunc(void *ptr, size_t size, size_t nmemb, std::string stream) {
size_t realsize = size * nmemb;
std::string temp(static_cast<const char*>(ptr), realsize);
stream.append(temp);
return realsize;
}
											
Python

python 3.0을 기준으로 작성되었습니다.

HTTP 통신을 위해 urllib3 라이브러리를 사용하여 제공하고 있습니다. Python 3.0 이하의 버전에서 예제를 실행하기 위해서는 별도로 urllib3의 설치가 필요합니다. 설치에 대한 설명은 https://pypi.python.org/pypi/urllib3 를 참고하시기 바랍니다. urllib3 라이브러리에 대한 자세한 설명은 https://urllib3.readthedocs.io/en/latest/ 에서 확인 하실 수 있습니다.

#-*- coding:utf-8 -*-
import urllib3
import json

openApiURL = "http://aiopen.etri.re.kr:8000/WikiQA"
accessKey = "YOUR_ACCESS_KEY"
question = "YOUR_QUESTION"
type = "ENGINE_TYPE"

requestJson = {
"access_key": accessKey,
"argument": {
	"question": question,
	"type": type
}
}

http = urllib3.PoolManager()
response = http.request(
"POST",
openApiURL,
headers={"Content-Type": "application/json; charset=UTF-8"},
body=json.dumps(requestJson)
)

print("[responseCode] " + str(response.status))
print("[responBody]")
print(str(response.data,"utf-8"))
											
Node.js
    var openApiURL = 'http://aiopen.etri.re.kr:8000/WikiQA';
    var access_key = 'YOUR_ACCESS_KEY';
    var question = 'YOUR_QUESTION';
    var type = 'ENGINE_TYPE';
    
    var requestJson = {
        'access_key': access_key,
        'argument': {
        	'question': question,
        	'type': type
        }
    };
    
    var request = require('request');
    var options = {
        url: openApiURL,
        body: JSON.stringify(requestJson),
        headers: {'Content-Type':'application/json; charset=UTF-8'}
    };
    request.post(options, function (error, response, body) {
        console.log('responseCode = ' + response.statusCode);
        console.log('responseBody = ' + body);
    });
											
위키백과 QA API 레퍼런스
요청 파라미터

위키백과 QA API에 필요한 요청 본문에 다음과 같은 파라미터를 작성해야 합니다.

[HTTP Request Body]
{
    “access_key”: “YOUR_ACCESS_KEY”,
    “argument”: {
    	“question”: “YOUR_QUESTION”,
    	“type”: “ENGINE_TYPE”
    }
}
											

다음은 파라미터에 대한 설명입니다.

Field 명 타입 필수 여부 설명
access_key String API 사용을 위해 ETRI에서 발급한 사용자 API Key
argument Object API 사용 요청 시 분석을 위해 전달할 내용
question String 질문하고자 하는 Text 로서 UTF-8 인코딩된 텍스트만 지원
type String 질문 응답 엔진의 종류 로서 UTF-8 인코딩된 텍스트만 지원
  • irqa: 언어분석 기반과 기계독해 기반의 질의응답을 통합한 질의응답 방식
  • kbqa: 지식베이스 기반의 질의응답 방식
  • hybridqa: irqa와 kbqa를 통합한 질의응답 방식
응답

위키백과 QA API는 요청된 질문의 분석된 결과를 JSON 형태의 Text 데이터로 반환합니다.

다음은 정상적인 요청 처리에 대한 HTTP 응답 예입니다.

[HTTP Respone Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:50783
Content-Type:application/json; charset=UTF-8

[HTTP Respone Body]
{
    "request_id": "reserved field",
    "result": 0,
    "return_type": "com.google.gson.internal.LinkedTreeMap",
    "return_object": {위키백과 QA 결과 JSON}
}
											

다음은 오류가 발생한 요청 처리에 대한 HTTP 응답 예입니다.

[HTTP Respone Header]
Access-Control-Allow-Origin:*
Connection:close
Content-Length:0
Content-Type:application/json; charset=UTF-8

[HTTP Respone Body]
{
    "request_id": "reserved field",
    "result": -1,
    "reason": {오류 메시지}
}
											

분석된 결과는 다음과 같은 내용이 포함되어 있습니다.

구분 JSON Key 이름 설명
위키백과 QA
기본정보
(WikiInfo)
IRInfo 검색 정보 Json Object Array
wiki_title 검색 결과의 위키백과 타이틀
sent 검색 단락
AnswerInfo 정답 정보 Json Object Array
rank 정답 순위
answer 정답
confidence 정답의 신뢰도
오류 코드

위키백과 QA API의 오류 코드 목록은 다음과 같습니다.

http status code result reason 설명
400 -1 Required arguments is empty 필수 파라미터의 값이 없는 경우
400 -1 One or more arguments are not valid 파라미터의 값이 유효하지 않는 경우
413 -1 Request Entity Too Large 요청 문장 또는 어휘의 크기가 서버가 처리 할 수 있는 것보다 큰 경우
429 -1 Too Many Requests 사용자가 주어진 시간 내에 서버에 너무 많은 요청을 하는 경우
404 -1 Unknown Handler 등록되지 않는 서비스를 요청한 경우
408 -1 Handler Timeout 서버의 요청 대기가 시간을 초과한 경우
500 -1 ETRI service server connection refused ETRI 분석 서버에서 요청을 받지 못하는 경우
500 -1 ETRI service server is not exists 수행 가능한 ETRI 분석 서버가 없는 경우
500 -1 Recipient Failure ETRI 분석 서버에서 요청을 처리하지 못하는 경우
500 -1 Unknown ReplyFailure API 요청에 알 수 없는 내부 오류가 발생한 경우
500 -1 Unknown Exception 알 수 없는 내부 오류가 발생한 경우