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

Update app.py

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