Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- .gitattributes +1 -0
- README.md +17 -6
- app.py +185 -59
- requirements.txt +8 -1
- vectorstore/index.faiss +3 -0
- vectorstore/index.pkl +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
vectorstore/index.faiss filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
@@ -1,14 +1,25 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
colorTo: purple
|
6 |
sdk: gradio
|
7 |
-
sdk_version:
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: apache-2.0
|
11 |
-
short_description: hnu aq chat-bot
|
12 |
---
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: 한남대학교 Q&A
|
3 |
+
emoji: 🏫
|
4 |
+
colorFrom: blue
|
5 |
colorTo: purple
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 4.0.2
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: apache-2.0
|
|
|
11 |
---
|
12 |
|
13 |
+
# 한남대학교 규정집 Q&A
|
14 |
+
|
15 |
+
한남대학교 규정집에 대한 질문-답변 시스템입니다.
|
16 |
+
|
17 |
+
## 사용 방법
|
18 |
+
1. 드롭다운에서 자주 묻는 질문을 선택하거나
|
19 |
+
2. 직접 질문을 입력하세요
|
20 |
+
3. AI 모델을 선택하고 "답변 받기" 버튼을 클릭하세요
|
21 |
+
|
22 |
+
## 기능
|
23 |
+
- 한남대학교 규정집 기반 질문 답변
|
24 |
+
- 자주 묻는 질문 드롭다운
|
25 |
+
- 다양한 AI 모델 선택 가능
|
app.py
CHANGED
@@ -1,64 +1,190 @@
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
-
from
|
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 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
)
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
|
|
63 |
if __name__ == "__main__":
|
64 |
-
|
|
|
1 |
+
|
2 |
+
import os
|
3 |
import gradio as gr
|
4 |
+
from langchain_groq import ChatGroq
|
5 |
+
from langchain.chains import RetrievalQA
|
6 |
+
from langchain.prompts import PromptTemplate
|
7 |
+
from langchain.vectorstores import FAISS
|
8 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
9 |
+
|
10 |
+
# Groq API 키 (Hugging Face Secrets에서 가져옴)
|
11 |
+
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
12 |
+
|
13 |
+
# 전역 변수들
|
14 |
+
vectorstore = None
|
15 |
+
embeddings = None
|
16 |
+
|
17 |
+
def load_vectorstore():
|
18 |
+
"""벡터스토어를 로드하는 함수"""
|
19 |
+
global vectorstore, embeddings
|
20 |
+
|
21 |
+
if vectorstore is None:
|
22 |
+
try:
|
23 |
+
embeddings = HuggingFaceEmbeddings(
|
24 |
+
model_name="sentence-transformers/all-MiniLM-L6-v2"
|
25 |
+
)
|
26 |
+
vectorstore = FAISS.load_local(
|
27 |
+
"vectorstore",
|
28 |
+
embeddings,
|
29 |
+
allow_dangerous_deserialization=True
|
30 |
+
)
|
31 |
+
print("✅ 벡터스토어 로드 완료")
|
32 |
+
except Exception as e:
|
33 |
+
print(f"❌ 벡터스토어 로드 실패: {e}")
|
34 |
+
vectorstore = None
|
35 |
+
|
36 |
+
return vectorstore
|
37 |
+
|
38 |
+
# 질문 리스트
|
39 |
+
suggested_questions = [
|
40 |
+
'교원 신규 임용은 어떻게 하나요?',
|
41 |
+
'교원 연구년 기간은 어떻게 되나요?',
|
42 |
+
'조교 신규 임용 기준은 무엇인가요?',
|
43 |
+
'교직원의 평일 근무시간은 어떻게 되나요?',
|
44 |
+
'직원 신규 임용 원칙은 무엇인가요?',
|
45 |
+
'직원 임용시 가산점이 있나요?',
|
46 |
+
'교원 업적의 심사 내용은 무엇인가요?',
|
47 |
+
'외국인 교원의 임기는 어떻게 되나요?',
|
48 |
+
'외국인 교원의 면직 기준은 무엇인가요?',
|
49 |
+
'기간제 계약직의 임기는 얼마정도인가요?',
|
50 |
+
'등록금 납부 방법은 무엇인가요?',
|
51 |
+
'교직 이수는 언제 신청이 가능한가요?',
|
52 |
+
'해외교류유학 지원자격은 어떻게 되나요?',
|
53 |
+
'만족도 조사 실행 대상은 누구인가요?',
|
54 |
+
'마이크로디그리의 유형은 무엇이 있나요?',
|
55 |
+
'장학금 관리 기관은 어디인가요?',
|
56 |
+
'학생 단체는 어떻게 등록하나요?',
|
57 |
+
'학생 설치물 중 금지된 설치물이 있나요?',
|
58 |
+
'비교과 교육과정의 종류는 무엇이 있나요?',
|
59 |
+
'안전사고예방계획은 어디에 제출해야 하나요?'
|
60 |
+
]
|
61 |
+
|
62 |
+
# 프롬프트 템플릿
|
63 |
+
prompt_template = """당신은 한남대학교 규정집 도우미입니다.
|
64 |
+
반드시 한국어로만 답변해주세요. 영어나 다른 언어는 절대 사용하지 마세요.
|
65 |
+
|
66 |
+
주어진 문서 내용을 바탕으로 질문에 대해 정확하고 친절하게 한국어로 답변해주세요.
|
67 |
+
|
68 |
+
참고 문서:
|
69 |
+
{context}
|
70 |
+
|
71 |
+
질문: {question}
|
72 |
+
|
73 |
+
답변 지침:
|
74 |
+
- 이용자를 반기는 인사로 시작하세요
|
75 |
+
- 반드시 한국어로만 답변하세요
|
76 |
+
- 정중하고 친근한 말투를 사용하세요
|
77 |
+
- 구체적이고 도움이 되는 정보를 제공하세요
|
78 |
+
- 문서에서 답을 찾을 수 없으면 "죄송하지만 해당 정보를 규정집에서 찾을 수 없습니다"라고 답변하세요
|
79 |
+
|
80 |
+
한국어 답변:"""
|
81 |
+
|
82 |
+
prompt = PromptTemplate(
|
83 |
+
template=prompt_template,
|
84 |
+
input_variables=["context", "question"]
|
85 |
)
|
86 |
|
87 |
+
def respond_with_groq(question, selected_q, model):
|
88 |
+
"""질문에 대한 답변을 생성하는 함수"""
|
89 |
+
|
90 |
+
# 선택된 질문이 있으면 그것을 사용
|
91 |
+
if selected_q != "직접 입력":
|
92 |
+
question = selected_q
|
93 |
+
|
94 |
+
if not question.strip():
|
95 |
+
return "질문을 입력해주세요."
|
96 |
+
|
97 |
+
if not GROQ_API_KEY:
|
98 |
+
return "❌ API 키가 설정되지 않았습니다. 관리자에게 문의하세요."
|
99 |
+
|
100 |
+
try:
|
101 |
+
# 벡터스토어 로드
|
102 |
+
current_vectorstore = load_vectorstore()
|
103 |
+
if not current_vectorstore:
|
104 |
+
return "❌ 벡터스토어를 로드할 수 없습니다."
|
105 |
+
|
106 |
+
# LLM 설정
|
107 |
+
llm = ChatGroq(
|
108 |
+
groq_api_key=GROQ_API_KEY,
|
109 |
+
model_name=model,
|
110 |
+
temperature=0.1,
|
111 |
+
max_tokens=1000
|
112 |
+
)
|
113 |
+
|
114 |
+
# QA 체인 생성
|
115 |
+
qa_chain = RetrievalQA.from_chain_type(
|
116 |
+
llm=llm,
|
117 |
+
chain_type="stuff",
|
118 |
+
retriever=current_vectorstore.as_retriever(search_kwargs={"k": 3}),
|
119 |
+
chain_type_kwargs={"prompt": prompt},
|
120 |
+
return_source_documents=True
|
121 |
+
)
|
122 |
+
|
123 |
+
# 답변 생성
|
124 |
+
result = qa_chain({"query": question})
|
125 |
+
return result['result']
|
126 |
+
|
127 |
+
except Exception as e:
|
128 |
+
return f"❌ 오류가 발생했습니다: {str(e)}"
|
129 |
+
|
130 |
+
def update_question(selected):
|
131 |
+
"""드롭다운 선택 시 질문을 업데이트하는 함수"""
|
132 |
+
if selected != "직접 입력":
|
133 |
+
return selected
|
134 |
+
return ""
|
135 |
+
|
136 |
+
# Gradio 인터페이스 생성
|
137 |
+
with gr.Blocks(title="한남대학교 Q&A") as interface:
|
138 |
+
gr.HTML("""
|
139 |
+
<div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
|
140 |
+
<h1>🏫 한남대학교 규정집 Q&A</h1>
|
141 |
+
<p>한남대학교 규정집에 대한 질문에 AI가 답변해드립니다.</p>
|
142 |
+
</div>
|
143 |
+
""")
|
144 |
+
|
145 |
+
with gr.Row():
|
146 |
+
with gr.Column(scale=1):
|
147 |
+
question_dropdown = gr.Dropdown(
|
148 |
+
choices=["직접 입력"] + suggested_questions,
|
149 |
+
label="💡 자주 묻는 질문",
|
150 |
+
value="직접 입력"
|
151 |
+
)
|
152 |
+
|
153 |
+
question_input = gr.Textbox(
|
154 |
+
label="❓ 질문을 입력하세요",
|
155 |
+
placeholder="예: 졸업 요건은 무엇인가요?",
|
156 |
+
lines=3
|
157 |
+
)
|
158 |
+
|
159 |
+
submit_btn = gr.Button("답변 받기", variant="primary", size="lg")
|
160 |
+
|
161 |
+
model_choice = gr.Radio(
|
162 |
+
choices=["llama3-70b-8192", "llama3-8b-8192"],
|
163 |
+
label="🤖 AI 모델 선택",
|
164 |
+
value="llama3-70b-8192"
|
165 |
+
)
|
166 |
+
|
167 |
+
with gr.Column(scale=2):
|
168 |
+
output = gr.Textbox(
|
169 |
+
label="💬 답변",
|
170 |
+
lines=15,
|
171 |
+
max_lines=20,
|
172 |
+
show_copy_button=True
|
173 |
+
)
|
174 |
+
|
175 |
+
# 이벤트 연결
|
176 |
+
submit_btn.click(
|
177 |
+
fn=respond_with_groq,
|
178 |
+
inputs=[question_input, question_dropdown, model_choice],
|
179 |
+
outputs=output
|
180 |
+
)
|
181 |
+
|
182 |
+
question_dropdown.change(
|
183 |
+
fn=update_question,
|
184 |
+
inputs=question_dropdown,
|
185 |
+
outputs=question_input
|
186 |
+
)
|
187 |
|
188 |
+
# 앱 실행
|
189 |
if __name__ == "__main__":
|
190 |
+
interface.launch()
|
requirements.txt
CHANGED
@@ -1 +1,8 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio==4.0.2
|
2 |
+
langchain==0.1.0
|
3 |
+
langchain-groq==0.1.0
|
4 |
+
faiss-cpu==1.7.4
|
5 |
+
sentence-transformers==2.2.2
|
6 |
+
python-dotenv==1.0.0
|
7 |
+
pypdf==3.17.0
|
8 |
+
tiktoken==0.5.1
|
vectorstore/index.faiss
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8d595d3dab8036920c1428ccb10fa7ceca4ac06872905aee99b7fa0de7a459b5
|
3 |
+
size 854061
|
vectorstore/index.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:827cc3ca44a0a416f90c9ba15e40fff7bf94e639287d17b317aaf8c96d046ebf
|
3 |
+
size 460200
|