Spaces:
Sleeping
Sleeping
| import os | |
| import fitz # PyMuPDF | |
| import gradio as gr | |
| import faiss | |
| import numpy as np | |
| from sentence_transformers import SentenceTransformer | |
| from groq import Groq | |
| # ✅ 1. Groq API 키 설정 | |
| os.environ["GROQ_API_KEY"] = "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" | |
| client = Groq(api_key=os.environ["GROQ_API_KEY"]) | |
| # ✅ 2. PDF 파일 경로 리스트 | |
| pdf_paths = [ | |
| "pdfs/의왕단오축제 _ 의왕문화원.pdf", | |
| "pdfs/성인 문화학교 _ 의왕문화원.pdf", | |
| "pdfs/횡성문화원_문화학교.pdf", | |
| "pdfs/발달장애인 교육사업 _ 꿈꾸는 마을.pdf", | |
| "pdfs/횡성의지역축제 - 횡성축제소개.pdf", | |
| "pdfs/어린이 문화학교 _ 의왕문화원.pdf" | |
| ] | |
| # ✅ 3. 텍스트 추출 | |
| def extract_texts_from_pdfs(pdf_paths): | |
| chunks = [] | |
| for path in pdf_paths: | |
| try: | |
| with fitz.open(path) as doc: | |
| for page in doc: | |
| text = page.get_text().strip() | |
| if len(text) > 30: | |
| chunks.append({ | |
| "text": text, | |
| "source": os.path.basename(path) | |
| }) | |
| print(f"✅ 텍스트 추출 완료: {path}") | |
| except Exception as e: | |
| print(f"❌ 오류 발생: {path} -> {e}") | |
| return chunks | |
| docs = extract_texts_from_pdfs(pdf_paths) | |
| # ✅ 4. 임베딩 및 FAISS 색인 | |
| embed_model = SentenceTransformer("jhgan/ko-sroberta-multitask") | |
| texts = [doc["text"] for doc in docs] | |
| sources = [doc["source"] for doc in docs] | |
| embeddings = embed_model.encode(texts, convert_to_numpy=True, show_progress_bar=True) | |
| index = faiss.IndexFlatL2(embeddings.shape[1]) | |
| index.add(np.array(embeddings)) | |
| # ✅ 5. 유사 문서 검색 | |
| def search_similar_docs(query, top_k=3): | |
| query_emb = embed_model.encode([query])[0] | |
| scores, indices = index.search(np.array([query_emb]), top_k) | |
| results = [] | |
| for idx in indices[0]: | |
| results.append(docs[idx]["text"]) | |
| return "\n\n".join(results) | |
| # ✅ 6. Groq 응답 | |
| def ask_with_groq(question, context): | |
| response = client.chat.completions.create( | |
| model="llama3-8b-8192", | |
| messages=[ | |
| {"role": "system", "content": "너는 문화 관련 문서를 기반으로 질문에 한국어로 답하는 도우미야."}, | |
| {"role": "user", "content": f"{question}\n\n[문서 내용]\n{context[:3000]}"} | |
| ] | |
| ) | |
| return response.choices[0].message.content | |
| # ✅ 7. 챗봇 인터페이스 | |
| def chatbot_interface(question): | |
| context = search_similar_docs(question) | |
| answer = ask_with_groq(question, context) | |
| return answer | |
| # ✅ 8. 추천 질문 리스트 | |
| suggested_questions = [ | |
| "의왕단오축제는 언제 열리나요?", | |
| "의왕단오축제 주요 프로그램은 무엇인가요?", | |
| "의왕문화원의 성인 문화학교 교육 대상은 누구인가요?", | |
| "성인 문화학교에서 제공하는 수업 종류는 무엇인가요?", | |
| "횡성문화원의 문화학교는 어떤 교육을 진행하나요?", | |
| "꿈꾸는 마을 프로그램 참가 자격은 어떻게 되나요?", | |
| "횡성지역축제의 대표 축제는 무엇인가요?", | |
| "횡성축제의 개최 시기와 일정은 어떻게 되나요?", | |
| "의왕문화원의 어린이 문화학교는 몇 세부터 참여 가능한가요?", | |
| "어린이 문화학교에서 진행하는 주요 프로그램은 무엇인가요?", | |
| "의왕문화원 문화 프로그램은 어디에서 진행되나요?", | |
| "각 프로그램의 참가 신청 방법과 일정은 어떻게 되나요?", | |
| "발달장애인 교육사업에서 제공하는 지원 서비스는 무엇인가요?", | |
| "의왕단오축제 참가비는 얼마인가요?", | |
| "횡성문화원 문화학교는 몇 개의 과정을 운영하나요?", | |
| "어린이 문화학교 행사 일정은 어떻게 되나요?", | |
| "문화 프로그램이 지역사회에 미치는 긍정적 영향은 무엇인가요?", | |
| "프로그램 안전관리 및 코로나 방역 대책은 어떻게 되어 있나요?", | |
| "문화 프로그램 참여 후 제공되는 피드백 절차가 있나요?", | |
| ] | |
| # ✅ 9. Gradio UI | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 📘 오아시스 문서 기반 문화 프로그램 챗봇 🤖 '뮤지스(Musesis)'") | |
| gr.Markdown("질문을 입력하면 유사 문서를 검색해 답변해줍니다.") | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| question_box = gr.Textbox(placeholder="질문을 입력하세요", label="질문", lines=1) | |
| with gr.Row(): | |
| submit_btn = gr.Button("질문하기") | |
| clear_btn = gr.Button("기록 지우기") | |
| answer_box = gr.Textbox(label="답변", lines=10) | |
| with gr.Column(scale=1): | |
| with gr.Accordion("💡 추천 질문 보기 (펼치기/접기)", open=False): | |
| for q in suggested_questions: | |
| btn = gr.Button(q, scale=1) | |
| btn.click(fn=chatbot_interface, inputs=gr.Textbox(value=q, visible=False), outputs=answer_box) | |
| # ✅ 버튼 클릭 및 Enter로 질문 실행 | |
| submit_btn.click(fn=chatbot_interface, inputs=question_box, outputs=answer_box) | |
| question_box.submit(fn=chatbot_interface, inputs=question_box, outputs=answer_box) | |
| # ✅ 기록 지우기 | |
| clear_btn.click(lambda: ("", ""), inputs=[], outputs=[question_box, answer_box]) | |
| # ✅ 10. 실행 | |
| if __name__ == "__main__": | |
| demo.launch() | |