kimrang's picture
Update app.py
3f4986e verified
raw
history blame
12.5 kB
import os
import gradio as gr
from langchain_groq import ChatGroq
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
# Groq API 키 (Hugging Face Secrets에서 가져옴)
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
# 전역 변수들
vectorstores = {}
embeddings = None
def comprehensive_debug():
"""완전한 디버깅 함수"""
print("=" * 50)
print("🔍 벡터스토어 디버깅 시작")
print("=" * 50)
# 1. 현재 디렉토리 정보
current_dir = os.getcwd()
print(f"📍 현재 작업 디렉토리: {current_dir}")
# 2. 모든 파일과 폴더 나열
try:
all_items = os.listdir(current_dir)
print(f"📂 현재 디렉토리 내 모든 항목들:")
for item in sorted(all_items):
item_path = os.path.join(current_dir, item)
if os.path.isdir(item_path):
print(f" 📁 {item} (폴더)")
else:
print(f" 📄 {item} (파일)")
except Exception as e:
print(f"❌ 디렉토리 조회 실패: {e}")
return []
# 3. 벡터스토어 폴더들 상세 검사
expected_folders = ['vectorstore1', 'vectorstore2', 'vectorstore3']
working_folders = []
for folder_name in expected_folders:
print(f"\n🔍 {folder_name} 검사 중...")
folder_path = os.path.join(current_dir, folder_name)
# 폴더 존재 확인
if not os.path.exists(folder_path):
print(f" ❌ 폴더가 존재하지 않음: {folder_path}")
continue
if not os.path.isdir(folder_path):
print(f" ❌ 폴더가 아님: {folder_path}")
continue
print(f" ✅ 폴더 존재함: {folder_path}")
# 폴더 내용 확인
try:
folder_contents = os.listdir(folder_path)
print(f" 📋 폴더 내용: {folder_contents}")
# 필수 파일들 확인
required_files = ['index.faiss', 'index.pkl']
for req_file in required_files:
file_path = os.path.join(folder_path, req_file)
if os.path.exists(file_path):
file_size = os.path.getsize(file_path)
print(f" ✅ {req_file} 존재 (크기: {file_size} bytes)")
else:
print(f" ❌ {req_file} 없음")
# 모든 파일이 있으면 작업 목록에 추가
if all(os.path.exists(os.path.join(folder_path, f)) for f in required_files):
working_folders.append(folder_name)
print(f" 🎯 {folder_name} 사용 가능!")
else:
print(f" ⚠️ {folder_name} 필수 파일 누락")
except Exception as e:
print(f" ❌ 폴더 내용 확인 실패: {e}")
print(f"\n📊 결과 요약:")
print(f" 총 검사한 폴더: {len(expected_folders)}")
print(f" 사용 가능한 폴더: {len(working_folders)}")
print(f" 사용 가능한 폴더 목록: {working_folders}")
return working_folders
def load_single_vectorstore(folder_name):
"""단일 벡터스토어 로드 테스트"""
global embeddings
print(f"\n🚀 {folder_name} 로드 시도...")
try:
# 임베딩 모델 초기화
if embeddings is None:
print(" 📥 임베딩 모델 로드 중...")
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
print(" ✅ 임베딩 모델 로드 완료")
# 벡터스토어 로드
folder_path = f"./{folder_name}"
print(f" 📂 벡터스토어 로드 시도: {folder_path}")
vectorstore = FAISS.load_local(
folder_path,
embeddings,
allow_dangerous_deserialization=True
)
print(f" ✅ {folder_name} 로드 성공!")
# 간단한 테스트
try:
# 벡터스토어 정보 확인
index_size = vectorstore.index.ntotal if hasattr(vectorstore, 'index') else "알 수 없음"
print(f" 📊 벡터 개수: {index_size}")
# 간단한 검색 테스트
test_results = vectorstore.similarity_search("테스트", k=1)
print(f" 🔍 검색 테스트 결과: {len(test_results)}개 문서 반환")
except Exception as test_e:
print(f" ⚠️ 벡터스토어 테스트 실패: {test_e}")
return vectorstore
except Exception as e:
print(f" ❌ {folder_name} 로드 실패: {e}")
print(f" ❌ 에러 타입: {type(e).__name__}")
# 상세 에러 정보
import traceback
error_details = traceback.format_exc()
print(f" 📋 상세 에러:\n{error_details}")
return None
def load_all_vectorstores():
"""모든 벡터스토어 로드"""
global vectorstores
print("\n" + "=" * 50)
print("🚀 모든 벡터스토어 로드 시작")
print("=" * 50)
# 1. 디버깅으로 사용 가능한 폴더 찾기
available_folders = comprehensive_debug()
if not available_folders:
print("\n❌ 사용 가능한 벡터스토어 폴더가 없습니다!")
return []
# 2. 각 폴더별로 로드 시도
successful_loads = []
for folder_name in available_folders:
vectorstore = load_single_vectorstore(folder_name)
if vectorstore is not None:
vectorstores[folder_name] = vectorstore
successful_loads.append(folder_name)
print(f"\n📊 최종 결과:")
print(f" 성공적으로 로드된 벡터스토어: {len(successful_loads)}")
print(f" 로드된 벡터스토어 목록: {successful_loads}")
return successful_loads
# 질문 리스트 (간소화)
suggested_questions = [
'교원 신규 임용은 어떻게 하나요?',
'교직원의 평일 근무시간은 어떻게 되나요?',
'등록금 납부 방법은 무엇인가요?',
'장학금 관리 기관은 어디인가요?',
'학생 단체는 어떻게 등록하나요?'
]
# 프롬프트 템플릿
prompt_template = """당신은 한남대학교 규정집 도우미입니다.
주어진 문서 내용을 바탕으로 질문에 대해 정확하고 친절하게 한국어로 답변해주세요.
참고 문서:
{context}
질문: {question}
한국어 답변:"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
def respond_with_groq(question, selected_q, model, selected_vectorstore):
"""질문에 대한 답변을 생성하는 함수 (디버깅 버전)"""
print(f"\n🎯 답변 생성 시작")
print(f" 질문: {question}")
print(f" 선택된 벡터스토어: {selected_vectorstore}")
# 선택된 질문이 있으면 그것을 사용
if selected_q != "직접 입력":
question = selected_q
print(f" 실제 사용할 질문: {question}")
if not question.strip():
return "질문을 입력해주세요."
if not GROQ_API_KEY:
return "❌ API 키가 설정되지 않았습니다."
# 벡터스토어 상태 확인
print(f" 현재 로드된 벡터스토어들: {list(vectorstores.keys())}")
if not vectorstores:
return "❌ 로드된 벡터스토어가 없습니다. 페이지를 새로고침해보세요."
if selected_vectorstore not in vectorstores:
available_stores = list(vectorstores.keys())
return f"❌ 선택된 벡터스토어({selected_vectorstore})를 찾을 수 없습니다.\n사용 가능: {available_stores}"
try:
current_vectorstore = vectorstores[selected_vectorstore]
print(f" ✅ 벡터스토어 선택 완료: {selected_vectorstore}")
# LLM 설정
llm = ChatGroq(
groq_api_key=GROQ_API_KEY,
model_name=model,
temperature=0.1,
max_tokens=1000
)
print(f" ✅ LLM 설정 완료: {model}")
# QA 체인 생성
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=current_vectorstore.as_retriever(search_kwargs={"k": 3}),
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
print(f" ✅ QA 체인 생성 완료")
# 답변 생성
print(f" 🤖 답변 생성 중...")
result = qa_chain({"query": question})
print(f" ✅ 답변 생성 완료")
return result['result']
except Exception as e:
print(f" ❌ 답변 생성 실패: {e}")
import traceback
error_details = traceback.format_exc()
print(f" 📋 상세 에러:\n{error_details}")
return f"❌ 오류가 발생했습니다: {str(e)}"
def update_question(selected):
if selected != "직접 입력":
return selected
return ""
# 앱 시작시 벡터스토어들 로드
print("🚀 애플리케이션 시작...")
available_vectorstores = load_all_vectorstores()
# Gradio 인터페이스 생성
with gr.Blocks(title="한남대학교 Q&A") as interface:
gr.HTML("""
<div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
<h1>🏫 한남대학교 규정집 Q&A (디버깅 버전)</h1>
<p>벡터스토어 로딩 상태를 확인할 수 있습니다.</p>
</div>
""")
# 상태 표시
if available_vectorstores:
status_html = f"✅ <b>{len(available_vectorstores)}개의 벡터스토어가 성공적으로 로드되었습니다:</b> {', '.join(available_vectorstores)}"
else:
status_html = "❌ <b>벡터스토어를 로드할 수 없습니다. 로그를 확인해주세요.</b>"
gr.HTML(f'<div style="padding: 10px; background-color: #f0f0f0; border-radius: 5px; margin-bottom: 20px;">{status_html}</div>')
with gr.Row():
with gr.Column(scale=1):
if available_vectorstores:
vectorstore_dropdown = gr.Dropdown(
choices=available_vectorstores,
label="📚 벡터스토어 선택",
value=available_vectorstores[0]
)
else:
vectorstore_dropdown = gr.Dropdown(
choices=["사용 불가"],
label="📚 벡터스토어 선택",
value="사용 불가"
)
question_dropdown = gr.Dropdown(
choices=["직접 입력"] + suggested_questions,
label="💡 자주 묻는 질문",
value="직접 입력"
)
question_input = gr.Textbox(
label="❓ 질문을 입력하세요",
placeholder="예: 졸업 요건은 무엇인가요?",
lines=3
)
submit_btn = gr.Button(
"답변 받기",
variant="primary",
size="lg",
interactive=bool(available_vectorstores)
)
model_choice = gr.Radio(
choices=["llama3-70b-8192", "llama3-8b-8192"],
label="🤖 AI 모델 선택",
value="llama3-70b-8192"
)
with gr.Column(scale=2):
output = gr.Textbox(
label="💬 답변",
lines=15,
max_lines=20,
show_copy_button=True
)
# 이벤트 연결
if available_vectorstores:
submit_btn.click(
fn=respond_with_groq,
inputs=[question_input, question_dropdown, model_choice, vectorstore_dropdown],
outputs=output
)
question_dropdown.change(
fn=update_question,
inputs=question_dropdown,
outputs=question_input
)
# 앱 실행
if __name__ == "__main__":
interface.launch()