kimrang commited on
Commit
f25b56f
·
verified ·
1 Parent(s): 3f4986e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -203
app.py CHANGED
@@ -13,181 +13,113 @@ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
13
  vectorstores = {}
14
  embeddings = None
15
 
16
- def comprehensive_debug():
17
- """완전한 디버깅 함수"""
18
- print("=" * 50)
19
- print("🔍 벡터스토어 디버깅 시작")
20
- print("=" * 50)
21
-
22
- # 1. 현재 디렉토리 정보
23
  current_dir = os.getcwd()
24
- print(f"📍 현재 작업 디렉토리: {current_dir}")
25
 
26
- # 2. 모든 파일과 폴더 나열
27
- try:
28
- all_items = os.listdir(current_dir)
29
- print(f"📂 현재 디렉토리 내 모든 항목들:")
30
- for item in sorted(all_items):
31
- item_path = os.path.join(current_dir, item)
32
- if os.path.isdir(item_path):
33
- print(f" 📁 {item} (폴더)")
34
- else:
35
- print(f" 📄 {item} (파일)")
36
- except Exception as e:
37
- print(f"❌ 디렉토리 조회 실패: {e}")
38
- return []
39
 
40
- # 3. 벡터스토어 폴더들 상세 검사
41
  expected_folders = ['vectorstore1', 'vectorstore2', 'vectorstore3']
42
- working_folders = []
43
 
44
  for folder_name in expected_folders:
45
- print(f"\n🔍 {folder_name} 검사 중...")
46
  folder_path = os.path.join(current_dir, folder_name)
47
-
48
- # 폴더 존재 확인
49
- if not os.path.exists(folder_path):
50
- print(f" 폴더가 존재하지 않음: {folder_path}")
51
- continue
52
-
53
- if not os.path.isdir(folder_path):
54
- print(f" ❌ 폴더가 아님: {folder_path}")
55
- continue
56
-
57
- print(f" ✅ 폴더 존재함: {folder_path}")
58
-
59
- # 폴더 내용 확인
60
- try:
61
- folder_contents = os.listdir(folder_path)
62
- print(f" 📋 폴더 내용: {folder_contents}")
63
-
64
- # 필수 파일들 확인
65
- required_files = ['index.faiss', 'index.pkl']
66
- for req_file in required_files:
67
- file_path = os.path.join(folder_path, req_file)
68
- if os.path.exists(file_path):
69
- file_size = os.path.getsize(file_path)
70
- print(f" ✅ {req_file} 존재 (크기: {file_size} bytes)")
71
  else:
72
- print(f" ❌ {req_file} 없음")
 
73
 
74
- # 모든 파일이 있으면 작업 목록에 추가
75
- if all(os.path.exists(os.path.join(folder_path, f)) for f in required_files):
76
- working_folders.append(folder_name)
77
- print(f" 🎯 {folder_name} 사용 가능!")
78
- else:
79
- print(f" ⚠️ {folder_name} 필수 파일 누락")
80
-
81
- except Exception as e:
82
- print(f" ❌ 폴더 내용 확인 실패: {e}")
83
 
84
- print(f"\n📊 결과 요약:")
85
- print(f" 검사한 폴더: {len(expected_folders)}")
86
- print(f" 사용 가능한 폴더: {len(working_folders)}")
87
- print(f" 사용 가능한 폴더 목록: {working_folders}")
88
-
89
- return working_folders
90
-
91
- def load_single_vectorstore(folder_name):
92
- """단일 벡터스토어 로드 테스트"""
93
- global embeddings
94
-
95
- print(f"\n🚀 {folder_name} 로드 시도...")
96
 
97
- try:
98
- # 임베딩 모델 초기화
99
- if embeddings is None:
100
- print(" 📥 임베딩 모델 로드 중...")
101
- embeddings = HuggingFaceEmbeddings(
102
- model_name="sentence-transformers/all-MiniLM-L6-v2"
103
- )
104
- print(" ✅ 임베딩 모델 로드 완료")
105
-
106
- # 벡터스토어 로드
107
- folder_path = f"./{folder_name}"
108
- print(f" 📂 벡터스토어 로드 시도: {folder_path}")
109
-
110
- vectorstore = FAISS.load_local(
111
- folder_path,
112
- embeddings,
113
- allow_dangerous_deserialization=True
114
- )
115
-
116
- print(f" ✅ {folder_name} 로드 성공!")
117
-
118
- # 간단한 테스트
119
- try:
120
- # 벡터스토어 정보 확인
121
- index_size = vectorstore.index.ntotal if hasattr(vectorstore, 'index') else "알 수 없음"
122
- print(f" 📊 벡터 개수: {index_size}")
123
-
124
- # 간단한 검색 테스트
125
- test_results = vectorstore.similarity_search("테스트", k=1)
126
- print(f" 🔍 검색 테스트 결과: {len(test_results)}개 문서 반환")
127
-
128
- except Exception as test_e:
129
- print(f" ⚠️ 벡터스토어 테스트 실패: {test_e}")
130
-
131
- return vectorstore
132
-
133
- except Exception as e:
134
- print(f" ❌ {folder_name} 로드 실패: {e}")
135
- print(f" ❌ 에러 타입: {type(e).__name__}")
136
-
137
- # 상세 에러 정보
138
- import traceback
139
- error_details = traceback.format_exc()
140
- print(f" 📋 상세 에러:\n{error_details}")
141
-
142
- return None
143
 
144
  def load_all_vectorstores():
145
- """모든 벡터스토어 로드"""
146
- global vectorstores
147
-
148
- print("\n" + "=" * 50)
149
- print("🚀 모든 벡터스토어 로드 시작")
150
- print("=" * 50)
151
 
152
- # 1. 디버깅으로 사용 가능한 폴더 찾기
153
- available_folders = comprehensive_debug()
154
-
155
- if not available_folders:
156
- print("\n❌ 사용 가능한 벡터스토어 폴더가 없습니다!")
157
- return []
158
 
159
- # 2. 폴더별로 로드 시도
160
- successful_loads = []
161
- for folder_name in available_folders:
162
- vectorstore = load_single_vectorstore(folder_name)
163
- if vectorstore is not None:
164
- vectorstores[folder_name] = vectorstore
165
- successful_loads.append(folder_name)
166
 
167
- print(f"\n📊 최종 결과:")
168
- print(f" 성공적으로 로드된 벡터스토어: {len(successful_loads)}")
169
- print(f" 로드된 벡터스토어 목록: {successful_loads}")
 
 
 
 
 
 
 
 
 
170
 
171
- return successful_loads
172
 
173
- # 질문 리스트 (간소화)
174
  suggested_questions = [
175
  '교원 신규 임용은 어떻게 하나요?',
 
 
176
  '교직원의 평일 근무시간은 어떻게 되나요?',
 
 
 
 
 
 
177
  '등록금 납부 방법은 무엇인가요?',
 
 
 
 
178
  '장학금 관리 기관은 어디인가요?',
179
- '학생 단체는 어떻게 등록하나요?'
 
 
 
180
  ]
181
 
182
- # 프롬프트 템플릿
183
  prompt_template = """당신은 한남대학교 규정집 도우미입니다.
 
184
  주어진 문서 내용을 바탕으로 질문에 대해 정확하고 친절하게 한국어로 답변해주세요.
185
-
186
  참고 문서:
187
  {context}
188
-
189
  질문: {question}
190
-
 
 
 
 
 
191
  한국어 답변:"""
192
 
193
  prompt = PromptTemplate(
@@ -196,36 +128,30 @@ prompt = PromptTemplate(
196
  )
197
 
198
  def respond_with_groq(question, selected_q, model, selected_vectorstore):
199
- """질문에 대한 답변을 생성하는 함수 (디버깅 버전)"""
200
-
201
- print(f"\n🎯 답변 생성 시작")
202
- print(f" 질문: {question}")
203
- print(f" 선택된 벡터스토어: {selected_vectorstore}")
204
 
205
  # 선택된 질문이 있으면 그것을 사용
206
  if selected_q != "직접 입력":
207
  question = selected_q
208
- print(f" 실제 사용할 질문: {question}")
209
 
210
  if not question.strip():
211
  return "질문을 입력해주세요."
212
 
213
  if not GROQ_API_KEY:
214
- return "❌ API 키가 설정되지 않았습니다."
215
 
216
- # 벡터스토어 상태 확인
217
- print(f" 현재 로드된 벡터스토어들: {list(vectorstores.keys())}")
218
-
219
  if not vectorstores:
220
- return "❌ 로드된 벡터스토어가 없습니다. 페이지를 새로고침해보세요."
221
-
222
- if selected_vectorstore not in vectorstores:
223
- available_stores = list(vectorstores.keys())
224
- return f"❌ 선택된 벡터스토어({selected_vectorstore})를 찾을 수 없습니다.\n사용 가능: {available_stores}"
225
 
226
  try:
 
 
 
 
 
227
  current_vectorstore = vectorstores[selected_vectorstore]
228
- print(f" 벡터스토어 선택 완료: {selected_vectorstore}")
229
 
230
  # LLM 설정
231
  llm = ChatGroq(
@@ -234,7 +160,6 @@ def respond_with_groq(question, selected_q, model, selected_vectorstore):
234
  temperature=0.1,
235
  max_tokens=1000
236
  )
237
- print(f" ✅ LLM 설정 완료: {model}")
238
 
239
  # QA 체인 생성
240
  qa_chain = RetrievalQA.from_chain_type(
@@ -244,61 +169,57 @@ def respond_with_groq(question, selected_q, model, selected_vectorstore):
244
  chain_type_kwargs={"prompt": prompt},
245
  return_source_documents=True
246
  )
247
- print(f" ✅ QA 체인 생성 완료")
248
 
249
  # 답변 생성
250
- print(f" 🤖 답변 생성 중...")
251
  result = qa_chain({"query": question})
252
- print(f" ✅ 답변 생성 완료")
253
-
254
  return result['result']
255
 
256
  except Exception as e:
257
- print(f" ❌ 답변 생성 실패: {e}")
258
  import traceback
259
  error_details = traceback.format_exc()
260
- print(f" 📋 상세 에러:\n{error_details}")
261
  return f"❌ 오류가 발생했습니다: {str(e)}"
262
 
263
  def update_question(selected):
 
264
  if selected != "직접 입력":
265
  return selected
266
  return ""
267
 
268
  # 앱 시작시 벡터스토어들 로드
269
- print("🚀 애플리케이션 시작...")
270
  available_vectorstores = load_all_vectorstores()
271
 
272
  # Gradio 인터페이스 생성
273
  with gr.Blocks(title="한남대학교 Q&A") as interface:
274
  gr.HTML("""
275
  <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
276
- <h1>🏫 한남대학교 규정집 Q&A (디버깅 버전)</h1>
277
- <p>벡터스토어 로딩 상태를 확인할 있습니다.</p>
278
  </div>
279
  """)
280
 
281
- # 상태 표시
282
- if available_vectorstores:
283
- status_html = f"✅ <b>{len(available_vectorstores)}개의 벡터스토어가 성공적으로 로드되었습니다:</b> {', '.join(available_vectorstores)}"
284
- else:
285
- status_html = "❌ <b>벡터스토어를 로드할 수 없습니다. 로그를 확인해주세요.</b>"
286
-
287
- gr.HTML(f'<div style="padding: 10px; background-color: #f0f0f0; border-radius: 5px; margin-bottom: 20px;">{status_html}</div>')
288
-
289
  with gr.Row():
290
  with gr.Column(scale=1):
 
291
  if available_vectorstores:
 
 
 
 
 
 
 
 
292
  vectorstore_dropdown = gr.Dropdown(
293
- choices=available_vectorstores,
294
- label="📚 벡터스토어 선택",
295
- value=available_vectorstores[0]
296
  )
297
  else:
298
  vectorstore_dropdown = gr.Dropdown(
299
- choices=["사용 불가"],
300
- label="📚 벡터스토어 선택",
301
- value="사용 불가"
302
  )
303
 
304
  question_dropdown = gr.Dropdown(
@@ -313,12 +234,7 @@ with gr.Blocks(title="한남대학교 Q&A") as interface:
313
  lines=3
314
  )
315
 
316
- submit_btn = gr.Button(
317
- "답변 받기",
318
- variant="primary",
319
- size="lg",
320
- interactive=bool(available_vectorstores)
321
- )
322
 
323
  model_choice = gr.Radio(
324
  choices=["llama3-70b-8192", "llama3-8b-8192"],
@@ -335,18 +251,17 @@ with gr.Blocks(title="한남대학교 Q&A") as interface:
335
  )
336
 
337
  # 이벤트 연결
338
- if available_vectorstores:
339
- submit_btn.click(
340
- fn=respond_with_groq,
341
- inputs=[question_input, question_dropdown, model_choice, vectorstore_dropdown],
342
- outputs=output
343
- )
344
-
345
- question_dropdown.change(
346
- fn=update_question,
347
- inputs=question_dropdown,
348
- outputs=question_input
349
- )
350
 
351
  # 앱 실행
352
  if __name__ == "__main__":
 
13
  vectorstores = {}
14
  embeddings = None
15
 
16
+ def find_vectorstore_folders():
17
+ """현재 디렉토리에서 벡터스토어 폴더들을 찾는 함수"""
 
 
 
 
 
18
  current_dir = os.getcwd()
19
+ print(f"현재 디렉토리: {current_dir}")
20
 
21
+ # 모든 파일과 폴더 확인
22
+ all_items = os.listdir(current_dir)
23
+ print(f"현재 디렉토리 내 모든 항목들: {all_items}")
 
 
 
 
 
 
 
 
 
 
24
 
25
+ # 예상되는 벡터스토어 폴더들
26
  expected_folders = ['vectorstore1', 'vectorstore2', 'vectorstore3']
27
+ vectorstore_folders = []
28
 
29
  for folder_name in expected_folders:
 
30
  folder_path = os.path.join(current_dir, folder_name)
31
+ if os.path.exists(folder_path) and os.path.isdir(folder_path):
32
+ try:
33
+ folder_contents = os.listdir(folder_path)
34
+ print(f"📁 {folder_name} 폴더 내용: {folder_contents}")
35
+
36
+ # 필수 파일들 확인
37
+ required_files = ['index.faiss', 'index.pkl']
38
+ has_all_files = all(file in folder_contents for file in required_files)
39
+
40
+ if has_all_files:
41
+ vectorstore_folders.append(folder_name)
42
+ print(f"✅ {folder_name} - 모든 필수 파일 존재")
 
 
 
 
 
 
 
 
 
 
 
 
43
  else:
44
+ missing_files = [f for f in required_files if f not in folder_contents]
45
+ print(f"❌ {folder_name} - 누락된 파일: {missing_files}")
46
 
47
+ except Exception as e:
48
+ print(f"❌ {folder_name} 폴더 확인 중 오류: {e}")
49
+ else:
50
+ print(f" {folder_name} 폴더가 존재하지 않음")
 
 
 
 
 
51
 
52
+ if not vectorstore_folders:
53
+ print(" 사용 가능한 벡터스토어 폴더를 찾을 수 없습니다")
54
+ else:
55
+ print(f" {len(vectorstore_folders)}개의 벡터스토어 폴더를 찾았습니다: {vectorstore_folders}")
 
 
 
 
 
 
 
 
56
 
57
+ return vectorstore_folders
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  def load_all_vectorstores():
60
+ """모든 벡터스토어를 로드하는 함수"""
61
+ global vectorstores, embeddings
 
 
 
 
62
 
63
+ if not embeddings:
64
+ embeddings = HuggingFaceEmbeddings(
65
+ model_name="sentence-transformers/all-MiniLM-L6-v2"
66
+ )
 
 
67
 
68
+ # 벡터스토어 폴더들 찾기
69
+ folders = find_vectorstore_folders()
 
 
 
 
 
70
 
71
+ for folder_name in folders:
72
+ if folder_name not in vectorstores:
73
+ try:
74
+ vectorstore = FAISS.load_local(
75
+ f"./{folder_name}",
76
+ embeddings,
77
+ allow_dangerous_deserialization=True
78
+ )
79
+ vectorstores[folder_name] = vectorstore
80
+ print(f"✅ {folder_name} 로드 완료")
81
+ except Exception as e:
82
+ print(f"❌ {folder_name} 로드 실패: {e}")
83
 
84
+ return list(vectorstores.keys())
85
 
86
+ # 질문 리스트 (기존과 동일)
87
  suggested_questions = [
88
  '교원 신규 임용은 어떻게 하나요?',
89
+ '교원 연구년 기간은 어떻게 되나요?',
90
+ '조교 신규 임용 기준은 무엇인가요?',
91
  '교직원의 평일 근무시간은 어떻게 되나요?',
92
+ '직원 신규 임용 원칙은 무엇인가요?',
93
+ '직원 임용시 가산점이 있나요?',
94
+ '교원 업적의 심사 내용은 무엇인가요?',
95
+ '외국인 교원의 임기는 어떻게 되나요?',
96
+ '외국인 교원의 면직 기준은 무엇인가요?',
97
+ '기간제 계약직의 임기는 얼마정도인가요?',
98
  '등록금 납부 방법은 무엇인가요?',
99
+ '교직 이수는 언제 신청이 가능한가요?',
100
+ '해외교류유학 지원자격은 어떻게 되나요?',
101
+ '만족도 조사 실행 대상은 누구인가요?',
102
+ '마이크로디그리의 유형은 무엇이 있나요?',
103
  '장학금 관리 기관은 어디인가요?',
104
+ '학생 단체는 어떻게 등록하나요?',
105
+ '학생 설치물 중 금지된 설치물이 있나요?',
106
+ '비교과 교육과정의 종류는 무엇이 있나요?',
107
+ '안전사고예방계획은 어디에 제출해야 하나요?'
108
  ]
109
 
110
+ # 프롬프트 템플릿 (기존과 동일)
111
  prompt_template = """당신은 한남대학교 규정집 도우미입니다.
112
+ 반드시 한국어로만 답변해주세요. 영어나 다른 언어는 절대 사용하지 마세요.
113
  주어진 문서 내용을 바탕으로 질문에 대해 정확하고 친절하게 한국어로 답변해주세요.
 
114
  참고 문서:
115
  {context}
 
116
  질문: {question}
117
+ 답변 지침:
118
+ - 이용자를 반기는 인사로 시작하세요
119
+ - 반드시 한국어로만 답변하세요
120
+ - 정중하고 친근한 말투를 사용하세요
121
+ - 구체적이고 도움이 되는 정보를 제공하세요
122
+ - 문서에서 답을 찾을 수 없으면 "죄송하지만 해당 정보를 규정집에서 찾을 수 없습니다"라고 답변하세요
123
  한국어 답변:"""
124
 
125
  prompt = PromptTemplate(
 
128
  )
129
 
130
  def respond_with_groq(question, selected_q, model, selected_vectorstore):
131
+ """질문에 대한 답변을 생성하는 함수"""
 
 
 
 
132
 
133
  # 선택된 질문이 있으면 그것을 사용
134
  if selected_q != "직접 입력":
135
  question = selected_q
 
136
 
137
  if not question.strip():
138
  return "질문을 입력해주세요."
139
 
140
  if not GROQ_API_KEY:
141
+ return "❌ API 키가 설정되지 않았습니다. 관리자에게 문의하세요."
142
 
143
+ # 벡터스토어가 로드되지 않은 경우
 
 
144
  if not vectorstores:
145
+ return "❌ 사용 가능한 벡터스토어가 없습니다. 관리자에게 문의하세요."
 
 
 
 
146
 
147
  try:
148
+ # 선택된 벡터스토어 확인
149
+ if selected_vectorstore not in vectorstores:
150
+ available_stores = list(vectorstores.keys())
151
+ return f"❌ 선택된 벡터스토어({selected_vectorstore})를 찾을 수 없습니다. 사용 가능한 스토어: {available_stores}"
152
+
153
  current_vectorstore = vectorstores[selected_vectorstore]
154
+ print(f"✅ 사용 중인 벡터스토어: {selected_vectorstore}")
155
 
156
  # LLM 설정
157
  llm = ChatGroq(
 
160
  temperature=0.1,
161
  max_tokens=1000
162
  )
 
163
 
164
  # QA 체인 생성
165
  qa_chain = RetrievalQA.from_chain_type(
 
169
  chain_type_kwargs={"prompt": prompt},
170
  return_source_documents=True
171
  )
 
172
 
173
  # 답변 생성
 
174
  result = qa_chain({"query": question})
 
 
175
  return result['result']
176
 
177
  except Exception as e:
 
178
  import traceback
179
  error_details = traceback.format_exc()
180
+ print(f" 상세 오류 정보:\n{error_details}")
181
  return f"❌ 오류가 발생했습니다: {str(e)}"
182
 
183
  def update_question(selected):
184
+ """드롭다운 선택 시 질문을 업데이트하는 함수"""
185
  if selected != "직접 입력":
186
  return selected
187
  return ""
188
 
189
  # 앱 시작시 벡터스토어들 로드
 
190
  available_vectorstores = load_all_vectorstores()
191
 
192
  # Gradio 인터페이스 생성
193
  with gr.Blocks(title="한남대학교 Q&A") as interface:
194
  gr.HTML("""
195
  <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
196
+ <h1>🏫 한남대학교 규정집 Q&A</h1>
197
+ <p>한남대학교 규정집에 대한 질문에 AI가 답변해드립니다.</p>
198
  </div>
199
  """)
200
 
 
 
 
 
 
 
 
 
201
  with gr.Row():
202
  with gr.Column(scale=1):
203
+ # 벡터스토어 선택 드롭다운 추가
204
  if available_vectorstores:
205
+ # 사용자 친화적인 이름으로 매핑
206
+ vectorstore_display_names = {
207
+ 'vectorstore1': '📚 데이터베이스 1',
208
+ 'vectorstore2': '📚 데이터베이스 2',
209
+ 'vectorstore3': '📚 데이터베이스 3'
210
+ }
211
+ display_choices = [vectorstore_display_names.get(vs, vs) for vs in available_vectorstores]
212
+
213
  vectorstore_dropdown = gr.Dropdown(
214
+ choices=list(zip(display_choices, available_vectorstores)),
215
+ label="📚 데이터베이스 선택",
216
+ value=available_vectorstores[0] if available_vectorstores else None
217
  )
218
  else:
219
  vectorstore_dropdown = gr.Dropdown(
220
+ choices=[("벡터스토어 없음", "none")],
221
+ label="📚 데이터베이스 선택",
222
+ value="none"
223
  )
224
 
225
  question_dropdown = gr.Dropdown(
 
234
  lines=3
235
  )
236
 
237
+ submit_btn = gr.Button("답변 받기", variant="primary", size="lg")
 
 
 
 
 
238
 
239
  model_choice = gr.Radio(
240
  choices=["llama3-70b-8192", "llama3-8b-8192"],
 
251
  )
252
 
253
  # 이벤트 연결
254
+ submit_btn.click(
255
+ fn=respond_with_groq,
256
+ inputs=[question_input, question_dropdown, model_choice, vectorstore_dropdown],
257
+ outputs=output
258
+ )
259
+
260
+ question_dropdown.change(
261
+ fn=update_question,
262
+ inputs=question_dropdown,
263
+ outputs=question_input
264
+ )
 
265
 
266
  # 앱 실행
267
  if __name__ == "__main__":