이렇게 코드를 작성하면, 먼저 텍스트를 로드하고, 사용자의 질문과 가장 관련이 있는 문단을 찾은 다음, 그 관련 문단을 기반으로 Bard에 질문을 합니다. 만약 Bard가 응답을 제공하지 못하면, 적절한 오류 메시지를 반환합니다. 이거 해보니 엄청 답변도 느리고 헛소리를 함. 아마 유사한 문단 취합할때 오류가 생기는듯
from flask import Flask, render_template, request, jsonify
import os
import bardapi
from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# Bard API key 설정
os.environ['_BARD_API_KEY'] = "XwgyrM8N1vkwESsSa5RQIK-bUDSJkbNfpT2GD25LaWTydkW8zCJ-WQPpoo9_sWf_6XBYEw."
bard = bardapi.core.Bard()
app = Flask(__name__)
def load_text(filename):
with open(filename, 'r', encoding='utf-8') as file:
text = file.read()
return text
def find_relevant_paragraphs(question, text, model, max_length=8000):
# 텍스트를 문단으로 분리
paragraphs = text.split("\\n\\n")
# 각 문단의 임베딩을 계산
paragraph_embeddings = model.encode(paragraphs, convert_to_tensor=True)
# 질문의 임베딩을 계산
question_embedding = model.encode(question, convert_to_tensor=True)
# 각 문단과 질문 간의 코사인 유사성을 계산
similarities = cosine_similarity(
question_embedding.reshape(1, -1),
paragraph_embeddings)[0]
# 유사성 점수를 기준으로 문단을 정렬하고, 최고 점수를 받은 문단을 선택
sorted_indices = np.argsort(similarities)[::-1]
selected_paragraphs = []
total_length = 0
for idx in sorted_indices:
paragraph = paragraphs[idx]
if total_length + len(paragraph) > max_length:
break
selected_paragraphs.append(paragraph)
total_length += len(paragraph)
return " ".join(selected_paragraphs)
# BERT 모델을 사용하여 임베딩을 계산
model = SentenceTransformer('bert-base-nli-mean-tokens')
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
data = request.get_json()
user_question = data['question']
text = load_text('bard.txt')
relevant_text = find_relevant_paragraphs(user_question, text, model)
combined_text = "질문:"+ user_question + "이 질문을 아래 텍스트를 기반으로 답변해줘. 텍스트에 없는 내용을 질문하면 모른다고해. 텍스트:" + relevant_text
response = bard.get_answer(combined_text)
if 'choices' in response and len(response['choices']) > 0:
answer = response['choices'][0]['content']
else:
answer = "The key 'choices' is not in the response or there are no choices."
return jsonify({'answer': answer})
return render_template('index.html')
if __name__ == "__main__":
app.run(port=5000, debug=True)
아래는 검증된 앱 코드 이걸 이용할거야
from flask import Flask, render_template, request, jsonify
import os
import bardapi
# Bard API key 설정
os.environ['_BARD_API_KEY'] = "XwgyrM8N1vkwESsSa5RQIK-bUDSJkbNfpT2GD25LaWTydkW8zCJ-WQPpoo9_sWf_6XBYEw."
bard = bardapi.core.Bard()
app = Flask(__name__)
def load_text(filename):
with open(filename, 'r', encoding='utf-8') as file:
text = file.read()
return text # 한글로 13페이지 넘어가면 오류나옴? 글자수의 문제인듯
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
data = request.get_json()
user_question = data['question']
text = load_text('bard.txt')
combined_text = "질문:"+ user_question + "이 질문을 아래 텍스트를 기반으로 답변해줘. 텍스트에 없는 내용을 질문하면 모른다고해. 텍스트:" + text
response = bard.get_answer(combined_text)
# 'choices' 키가 존재하고 그 안에 내용이 있는지 확인
if 'choices' in response and len(response['choices']) > 0:
answer = response['choices'][0]['content']
else:
answer = "The key 'choices' is not in the response or there are no choices."
return jsonify({'answer': answer})
return render_template('index.html')
if __name__ == "__main__":
app.run(port=5000, debug=True)
combined_text = "질문: 사립유치원과 관련하여"+user_question +"위 질문에 대한 답변을 생성하되 아래 텍스트를 기준점으로 삼아서 답변해줘. 애매한 답변은 이 텍스트를 참조하면 되고 스스로 생성한 답변이 아래 텍스트와 상충하는 경우 텍스트를 우선으로 답변해줘 헌법처럼"+ text
이렇게 프롬프트를 짜면 바드의 원래 능력으로 답변을 잘하고 답변할때 텍스트를 우선적으로 고려한다. 너무 좋다.
아래는 프론트엔드 완성본
<!DOCTYPE html>
<html>
<head>
<style>
body {
position: relative;
height: 100vh;
margin: 0;
background-color: #f2f2f2;
font-family: Arial, sans-serif;
}
#chat-icon {
position: fixed;
bottom: 20px;
right: 20px;
cursor: pointer;
padding: 10px 20px;
background-color: #0084ff;
color: white;
font-weight: bold;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 10;
}
#chat-box {
position: fixed;
bottom: 70px;
right: 20px;
width: 400px;
height: 600px;
background-color: white;
border: 1px solid gray;
padding: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
flex-direction: column;
justify-content: space-between;
display: none;
}
#messages {
height: 80%;
overflow-y: auto;
}
.user, .bard {
margin: 10px 0;
padding: 10px;
border-radius: 10px;
clear: both;
}
.user {
background-color: #0084ff;
color: white;
float: right;
}
.bard {
background-color: #f0f0f0;
float: left;
}
#chat-form {
display: flex;
justify-content: space-between;
border-top: 1px solid #ccc;
padding: 10px;
}
#chat-form input[type="text"] {
width: 80%;
padding: 5px;
border: none;
border-radius: 10px;
}
#chat-form input[type="submit"] {
background-color: #0084ff;
color: white;
border: none;
border-radius: 10px;
padding: 5px 10px;
}
</style>
</head>
<body>
<div id="chat-icon" onclick="toggleChatBox()">
챗봇
</div>
<div id="chat-box">
<div id="messages"></div>
<form id="chat-form" onsubmit="event.preventDefault(); submitMessage()">
<input type="text" id="input-message" placeholder="질문을 입력하세요" required>
<input type="submit" value="입력">
</form>
</div>
<script>
var chatBoxVisible = false;
function toggleChatBox() {
var chatBox = document.getElementById("chat-box");
if (!chatBoxVisible) {
chatBox.style.display = "flex";
chatBoxVisible = true;
} else {
chatBox.style.display = "none";
chatBoxVisible = false;
}
}
// 페이지 로드 시 채팅창 초기 상태를 숨김으로 설정합니다.
window.onload = function() {
document.getElementById("chat-box").style.display = "none";
}
function submitMessage() {
var input = document.getElementById("input-message");
var message = input.value;
input.value = "";
var userMessage = document.createElement("div");
userMessage.textContent = message;
userMessage.className = "user";
document.getElementById("messages").appendChild(userMessage);
fetch("/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ question: message })
}).then(response => response.json())
.then(data => {
var bardMessage = document.createElement("div");
bardMessage.textContent = data.answer;
bardMessage.className = "bard";
document.getElementById("messages").appendChild(bardMessage);
});
}
</script>
</body>
</html>