์ฐ์ ์ฑ๋ด์ ํผ์ ๋ง๋ค์ด๋ณด๋ ค๊ณ ๋ง๋ ์๋ฃ๋ค๊ณผ ๋ ผ๋ฌธ์ ์ฝ์ด๋ณด์๋ค.
ํ์ง๋ง ์ปดํจํ ์์์ด ๋ถ์กฑํ๊ณ ์ผ์ผํ ๋ํ์๋ฃ๋ฅผ ๋ง๋ค์ด๋ด๊ธฐ๋ ์ด๋ ค์ ๋ค.
๊ทธ๋์ ์ฐพ์๋ณด์๋๋ฐ ๋น์ทํ ์๋ฃ๊ฐ ์์ด์ ์ด๊ฑธ ์ฑ๋ด์ผ๋ก ๊ตฌํํด๋ณด์๋ค.
๋ณด์๊ธฐ ์ ์ ๋ฏธ๋ฆฌ ๋งํ์ง๋ง ์ฑ๋ด์ด๋ผ๊ณ ํ๊ธฐ์๋ ๋ค์ ๋ฌธ์ ๊ฐ ์๊ธด ํ์ง๋ง,
๊ฐ๋จํ๊ฒ ์ฌ๋ฏธ๋ฅผ ๋ถ์ฌ๋ณผ ์๋ ์๋ค.
์ฐ์ ๋ค ํ ์๋ฃ๋ฅผ ๋จผ์ ๋ณด๊ณ ๋์ ๋๊น์ง ๋ณผ ์ฌ๋์ ๋ด๋ ๋๋ค.
์ด์ ํ์ค ์งํ์ ํด์ผํด์ ์กฐ๊ธ๋ ๊ณ ๋ํํ์ง๋ ๋ชปํ๋ค.
ํ ํฐํ, ๋ฅ๋ฌ๋, ๋์ฌ, ๋ช ์ฌ, ์กฐ์ฌ, ์ด๋ฐ๊ฑฐ ๋ค ํ์์๋ค.
ํ์ง๋ง ์กฐ๊ธ๋ ๋ํ๋ฅผ ๋งค๋๋ฝ๊ฒ ํ๊ธฐ์ํด์๋ ๊ทธ์ ๋๋ ํด์ค๋ ๋๋ค.
๋จผ์ ์ด๋ ต๊ฒ ์ฐพ์ API์ด๋ค.
์ด๊ฑด ๋ฌด๋ฃ์ด๊ณ ์ฑ๋ฅ๋ ๊ด์ฐฎ๊ณ , ํ๊ตญ์์ ๋ฐ์ด๋ ์,๋ฐ์ฌ๋๋ค์ด ๋ง๋ ๊ฒ์ด๋ค.
https://aiopen.etri.re.kr/guide/MRCServlet
์ฌ๊ธฐ๋ฅผ ๊ฐ์ APIํค๋ฅผ ๋ฐ๊ธ๋ฐ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ํค๋ฅผ ์ธ ์ ์๊ณ , ๋๋ ๊ธฐ๊ณ๋ ํด API๋ฅผ ์ด์ฉํด์ ์ฑ๋ด์ ๋ง๋๋ ค๊ณ ํ๋ค.
#-*- coding:utf-8 -*-
import urllib3
import json
openApiURL = "http://aiopen.etri.re.kr:8000/MRCServlet"
accessKey = "๋ณธ์ธ์ ํค"
passage = ''.join(open('info.txt', 'r').readlines()) # ํด๋ต์ง
# ์์) passage = "์๋
ํ์ธ์: ๋ฐ๊ฐ์ต๋๋ค."
http = urllib3.PoolManager()
question = "์ง๋ฌธ๋ด์ฉ"
requestJson = {"argument": { "question": question, "passage": passage }}
response = http.request(
"POST",
openApiURL,
headers={"Content-Type": "application/json; charset=UTF-8","Authorization": accessKey},
body=json.dumps(requestJson)
)
r = json.loads(str(response.data,"utf-8"))
answer = r['return_object']['MRCInfo']['answer']
print(answer)
๋จผ์ ๊ธฐ๋ณธ์ ์ธ ํ์ด๋ค. accessKey์ ๋ณธ์ธ์ด ๋ฐ์ ํค๊ฐ์ ๋ฃ์ผ๋ฉด ๋๋ค.
passage์ ์ง๋ฌธ์ ๋๋ตํ ๋ด์ฉ๋ค์ ๊ธฐ์ ํ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ question์ ์ง๋ฌธ์ ์ ์ด์ ๋ณด๋ด๊ธฐ๋ง ํ๋ฉด ๋์ด๋ค.
์ฌ๊ธฐ๊น์ง๋ง ํด์ ์ฝ๋๋ฅผ API๋ก ๋ง๋ค์ด์ ํต์ ํ๋ฉด ๊ฐ๋จํ ์ฑ๋ด์ด ๋๋ค.
๋๋ ๊ทธ๋ฅ ๊ฐ๋จํ๊ฒ ํ๋ผ์คํฌ๋ก ๊ตฌํํ์ฌ AWS์์ ๋๋ ค๋ณด์๋ค.
๋จผ์ ๋ด๊ฐ ๋ง๋ textํ์ผ์ด๋ค.
ํด๋ํฐ์ ์ธ์ฆํ๋ ๋ฐฉ๋ฒ์ ๋จผ์ ์ด์ฉ๋์๋ฅผ ํ์๊ณ , ํด๋ํฐ๋ฒํธ๋ฅผ ์ ๋ ฅํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ ๋ ฅํ ํด๋ํฐ๋ฒํธ์ ์ธ์ฆ๋ฒํธ๊ฐ ๋ฌธ์๋ฉ์์ง๋ก ์ค๋ฉด ๊ทธ ์ธ์ฆ๋ฒํธ๋ฅผ ์ ๋ ฅํ์๋ฉด ํด๋ํฐ ์ธ์ฆ์ด ๋ฉ๋๋ค. [END] |
๋ชจ์์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ชจ์์ ์ด๋ฆ์ ์ ๋ ฅํ๊ณ , ๊ด์ฌ์ฌ๋ฅผ ์ค์ ํ๊ณ ๊ฐ์คํ๋ฉด ๋ฉ๋๋ค. [END] |
๋ชจ์์ ์ญ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ชจ์ ์ค์ ์ ๋ค์ด๊ฐ์ ๋ชจ์ํด์ฒดํ๊ธฐ๋ฅผ ๋๋ฅด์๋ฉด ๋ฉ๋๋ค. [END] |
๋ชจ์์ด๋ ์คํ๋ผ์ธ์์ ์ฌ๋๋ค๋ผ๋ฆฌ ๋ง๋์ ์ทจ๋ฏธํ๋์ ํ๋ ๊ฒ์ ๋๋ค. ํ๋ฒ ๋ชจ์์ ํด๋ณด์๋๊ฑด ์ด๋จ๊น์? [END] |
์ดํ๋ด์์ ์ฑ๋ด์ ๋ง๋ค์ด๋ณด๋ฉด ์ข๊ฒ ๋ค๋ ์ทจ์ง์์ ์์ํ์ผ๋ฏ๋ก
์ดํ์ฌ์ฉ์ ๊ดํ ๊ฐ๋จํ ๋ด์ฉ์ ํ ์คํธํ์ผ๋ก ๋ง๋ค์๋ค.
[END]๋ฅผ ๋ถ์ธ ์ด์ ๋ question์ ๋ด์ฉ์ด passage์์ ์ ๋๋ก ๊ฒ์์ด ์๋ ๋
์ ์ฒด ๋ด์ฉ์ ๊ธ์ด์ค๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ฐ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด END๋ผ๋ ๋ต๋ณ์ ๋์ ์ค์ ํด์ฃผ์๋ค.
ํน์ ๋๋ฅผ ๋๊น์ง ๋ฐ๋ผํด๋ณด๋ ค๋ ์ฌ๋์ด ์์๊ฒ ๊ฐ์์ ํด๋๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑํ๋ค.
chatbot.py์์ ์๊น์ ๊ฐ์ ์ฝ๋๋ก ๊ตฌ์ฑํ๊ณ , main.py๋ก flask๋ก ๊ฐ๋จํ๊ฒ ๊ตฌํํ๋ค.
๊ทธ๋ฆฌ๊ณ html์์ ์ฒ์ ํ๋ฉด๊ณผ ๊ฐ์ด ๊พธ๋ฉฐ์ฃผ์๋ค.
main.py
# main.py
#-*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect, url_for
import chatbot
app = Flask(__name__)
board = []
@app.route('/')
def index():
return render_template('main.html', rows=board, qnas=chatbot.src)
@app.route('/question',methods=["POST"])
def question():
if request.method == "POST":
board.append([request.form["context"], chatbot.action(request.form["context"])])
return redirect(url_for("index"))
else:
return render_template("main.html", rows=board, qnas=chatbot.src)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=8888)
๋จผ์ main.py์ด๋ค.
๊ฐ๋จํ ์๋ธํ๋ก์ ํธ์ฌ์ django๋ ๊ณต๋ถํ๊ณ ์๋ fastapi๋ ์ฌ์ฉํ์ง ์๊ณ flask๋ก ๊ตฌํํ์๋ค.
flask๋ api, html์ ๋ํด์๋ ๋ฐ๋ก ์ค๋ช ํ์ง๋ ์๊ฒ ๋ค.
์ฒ์์ ํ๋ฉด์ ๋ค์ด๊ฐ๋ฉด main.html์ ๋ถ๋ฌ์จ๋ค.
import urllib3, json
from konlpy.tag import Okt
openApiURL = "http://aiopen.etri.re.kr:8000/MRCServlet"
accessKey = "KEY"
src = open('info.txt', 'r', encoding='utf-8').readlines()
passage = ''.join(src)
http = urllib3.PoolManager()
okt = Okt()
# def action(q):
# requestJson = {"argument": { "question": q, "passage": passage }}
# response = http.request("POST", openApiURL, headers={"Content-Type": "application/json; charset=UTF-8","Authorization": accessKey}, body=json.dumps(requestJson))
# response = json.loads(str(response.data,"utf-8"))
# print(response)
# answer = response['return_object']['MRCInfo']['answer']
# print('๋ณ๊ฒฝ์ :', answer)
# if '[END]' in answer:
# answer = answer[:answer.index('[END]')]
# answer = passage[passage.index(answer): passage.index('[END]', passage.index(answer))]
# print('๋ณ๊ฒฝํ:', answer)
# return answer
def action(q):
q = okt.normalize(q)
if len(okt.nouns(q)):
# print(okt.pos(q))
requestJson = {"argument": { "question": q, "passage": passage }}
response = http.request("POST", openApiURL, headers={"Content-Type": "application/json; charset=UTF-8","Authorization": accessKey}, body=json.dumps(requestJson))
response = json.loads(str(response.data,"utf-8"))
# print(response)
if float(response['return_object']['MRCInfo']['confidence']) > 0.10:
answer = response['return_object']['MRCInfo']['answer']
print('๋ณ๊ฒฝ์ :', answer)
if '[END]' in answer:
answer = answer[:answer.index('[END]')]
answer = passage[passage.index(answer): passage.index('[END]', passage.index(answer))]
print('๋ณ๊ฒฝํ:', answer)
return answer
# print(q)
# end_word = [word for word in q if '๊ฐ'<=word<='ํฃ'][-1]
# josa = '์ด' if (ord(end_word)-ord("๊ฐ")) % 28 > 0 else ''
josa = ''
return '"' + q + '"' + josa + '๋ผ๋ ๋ฌธ์ฅ์ ์์ง ์ ๋๋ก ์ดํดํ์ง ๋ชปํ์ต๋๋ค. ์ด ์ฌํญ์ ์ฑ๋ด ์๋ต์ ์ถ๊ฐ๋ ์์ ์
๋๋ค.'
chatbot.py ํ์ผ์ด๋ค.
์๊น ์์ ์์ค์์ ์ฝ๊ฐ๋ง ๋ณํํ๋ค.
์๊ฐ๋ง ๋ ์์์ผ๋ฉด ๋๋ ํํ์๋ฅผ ๋ถ์ํ์ฌ ์กฐ๊ธ๋ ์ง๋ฌธ์ ์์์ ์ผ์ ํ ์ํค๊ณ ,
ํ์ง์ ์ฌ๋ฆฌ๋ ค ํ์ง๋ง ์๊ฐ์ด ๋ง์ง ์์๋ค.
๊ทธ๋์ ๊ทธ๋ฅ ๊ฐ๋จํ๊ฒ ์ผ์ ์ ํ๋๊ฐ ๋์ผ๋ฉด answer์ ์ง์ด ๊ฐ์น๊ฐ ์๋ค๊ณ ํ๋จํ์ฌ
๊ฐ๋จํ ์ ์ ํ์ html์ ๋ฟ๋ ค์ค๋ค.
๋๋ ํ ์คํธ๋ฅผ ํด๋ณด๋ passage์ ๋ด์ฉ๊ณผ ๋๊ฐ์ ์ง๋ฌธ์ ํ ๋๋ ํผ์ผํธ๊ฐ ๋์์ง๋ง ๋ป์ด ๋น์ทํ ๋ฌธ์ฅ์ผ๋ก ์ง๋ฌธํ์์๋๋
13~20%์ ํ๋ฅ ์ด ๋์๊ธฐ ๋๋ฌธ์ ์ฐ์ 10%๋ง ๋์ด๋ ๋ตํ ๊ฐ์น๊ฐ ์๋ค๊ณ ํ๋จํ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ 10~20%์ ์ ํ๋์ผ๋๋ ๋ชจ๋ธ์์ ์ฐพ์ ๋ต์ ์ฒ์๋ถํฐ,
text๊ฐ ๋๋๋ ๋ชจ๋ ๋ด์ฉ์ ๋ด์์ ์ ๋ต์ด๋ผ๊ณ ๋งํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด
[END]๊ธฐํธ๋ก ์ ๋ต์ ๋๋ถ๋ถ์ ์ง์ ํด ์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ 10%๋ฏธ๋ง์ผ๋๋ passage์ ์๋ ๋ด์ฉ์ด ์๋๋ผ๊ณ ์๊ฐํ๊ณ ๋ตํ ์ ์๋ค๋ ๋ฌธ์ฅ์ ๋ด์์ฃผ์๋ค.
<!-- ./template/main.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<script src="https://kit.fontawesome.com/77ad8525ff.js" crossorigin="anonymous"></script>
<title>OE CHATBOT</title>
<style type="text/css">
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.top{
border-radius: 10px;
display: block;
margin: auto;
padding: 40px;
width: 1000px;
overflow: auto;
background-color: #e2db72;
margin-bottom: 20px;
margin-top: 20px;
}
.top .title{
text-align: center;
margin-bottom: 5px;
}
.top .sub-title{
text-align: center;
margin-bottom: 5px;
}
a {
text-decoration: none;
}
div::-webkit-scrollbar {
display: none;
}
.bottom{
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
display: block;
margin: auto;
padding: 40px 0;
width: 500px;
background-color: #A8C0D6;;
}
.screen{
border-top-left-radius: 10px;
border-top-right-radius: 10px;
display: block;
margin: auto;
padding: 40px 0;
width: 500px;
height: 500px;
overflow: auto;
background-color: #A8C0D6;
}
.screen .chat {
display: flex;
align-items: flex-start;
padding: 20px;
}
.screen .chat .icon {
position: relative;
overflow: hidden;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #eee;
}
.screen .chat .icon i {
position: absolute;
top: 10px;
left: 50%;
font-size: 2.5rem;
color: #aaa;
transform: translateX(-50%);
}
.screen .chat .textbox {
position: relative;
display: inline-block;
max-width: calc(100% - 70px);
padding: 10px;
margin-top: 7px;
font-size: 13px;
border-radius: 10px;
}
.screen .chat .textbox::before {
position: absolute;
display: block;
top: 0;
font-size: 1.5rem;
}
.screen .ch1 .textbox {
margin-left: 20px;
background-color: #ffffff;
}
.screen .ch1 .textbox::before {
left: -15px;
content: "โ";
color: #ffffff;
}
.screen .ch2 {
flex-direction: row-reverse;
}
.screen .ch2 .textbox {
margin-right: 20px;
background-color: #F9EB54;
}
.screen .ch2 .textbox::before {
right: -15px;
content: "โถ";
color: #F9EB54;
}
.input-box {
border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
font-size: 16px;
width: 400px;
margin-left: 11px;
}
.submit-button {
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
.submit-button:hover {
background-color: #3e8e41;
}
</style>
</head>
<body>
<div class="top">
<h3 class="title">์ฑ๋ด ํ
์คํธ</h3>
<h4 class="sub-title">์ฑ๋ด๋ต๋ณ๋ฆฌ์คํธ</h4>
{% for qna in qnas %}
<a>{{ loop.index }}. {{ qna }}</a><br>
{% endfor %}
</div>
<div class="screen" id="screen">
{% for row in rows %}
<div class="chat ch2">
<div class="icon"><i class="fa-solid fa-user"></i></div>
<div class="textbox">{{ row[0] }}</div>
</div>
<div class="chat ch1">
<div class="icon"><img src="{{ url_for('static', filename='image/beomcoder.jpg')}}" alt="TEST"></div>
<div class="textbox">{{ row[1] }}</div>
</div>
{% endfor %}
</div>
<div class="bottom">
<form action="/question" method="POST">
<input type="text" class="input-box" name="context">
<input type="submit" class="submit-button"value="์ ์ก" /><br>
</form>
</div>
<script>
var div = document.getElementById("screen");
div.scrollTop = div.scrollHeight;
</script>
</body>
</html>
main.html์ด๋ค. ๊ฐ๋จํ ํ ์คํธ์ฉ์ด๋ผ ์ํ์ด์ง๋ก ๊ตฌ์ฑํ์๋ค.
๋ด๊ฐ html์ ์ฐ๋จน์ ๋๋ง ํ ์ค ์์์ ๋ฐ๋ก cssํ์ผ์ ๋ง๋ค๊ธฐ์๋ ๊ท์ฐฎ์๋ค.
flask๋ฅผ ์ธ๋์๋ templates์ html๋ค์ด ์์นํด์ผํ๋ค. ์ด๊ฑด๋ง ์งํค๋ฉด ๋ค๋ฅธ ํ์ผ์ ์ด๋ฆ์ ๋ณ๊ฒฝํด๋ ๋๋ค.
https://rgy0409.tistory.com/4854
์ ์ฌ์ดํธ์์ ๊ฐ๋จํ๊ฒ ์นด์นด์คํก์ค๋ฌ์ด ์ด๋ฏธ์ง๋ฅผ ๊ตฌํํ๊ณ ์์ด ์ ์์ค๋ฅผ ์ฐธ์กฐํ์ฌ ๋ง๋ค์๋ค.
์ ์์ค์์ ๋งํ์ ๊ณผ ํ๋กํ์ด๋ฏธ์ง๋ง ๊ฐ์ ธ์ค๊ณ ๋๋จธ์ง๋ ์ง์ ์ ์ํ์๋ค.
์ ๋ ฅ์ฐฝ, ํด๋ํฐ์ฒ๋ผ ๋ณด์ด๊ฒํ๊ณ , ์คํฌ๋กคํ์๋ฅผ ์์ ๊ณ ํ์์ผ๋ ์์์ ๋ ๋ง๋ค๊ณ ์ถ์ ์ฌ๋์ด ์์ผ๋ฉด๋ ๋ง๋ค์ด๋ ์๊ด์๋ค.
์๊ฐ์ด ๋๋ค๋ฉด ํํ์ ๋ถ์๋ ํ๊ณ , ๊ฐ๋จํ qna๋ฐ์ดํฐ๋ฅผ ํ์ต์์ผ์ sub model๋ก ๋๊ณ
api์ ํจ๊ป ์ฌ์ฉํ์ฌ ์์ง์ chatbot์ ๋ง๋ค๊ณ ์ถ์๋ค.
์ ์ ๋ฉ์ถฐ๋๊ณ ๋ค์์ ๋ค์ ๋ง๋ค์ด๋ณด์์ผ๊ฒ ๋ค.
๋๊ธ