import gradio as gr import PyPDF2 from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import FAISS from langchain.llms import HuggingFacePipeline from transformers import pipeline, AutoTokenizer, GPT2LMHeadModel, BitsAndBytesConfig import os import torch # --- 0. Global Değişkenler ve Ayarlar --- PDF_PATH = "mevzuat.pdf" CHUNK_SIZE = 400 CHUNK_OVERLAP = 100 EMBEDDING_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" LLM_MODEL_NAME = "ytu-ce-cosmos/turkish-gpt2-medium" DEVICE = "cuda" if torch.cuda.is_available() else "cpu" # GPT-2'nin maksimum token uzunluğu (genellikle 1024) MODEL_MAX_LENGTH = 1024 # Üretilecek maksimum yeni token sayısı MAX_NEW_TOKENS = 200 # Global olarak tanımlandı # --- 1. PDF'i İşle ve Vektör Veritabanı Oluştur --- def create_vector_db_from_pdf(pdf_path): print(f"PDF okunuyor: {pdf_path}") text = "" try: with open(pdf_path, "rb") as file: reader = PyPDF2.PdfReader(file) for page_num, page in enumerate(reader.pages): page_text = page.extract_text() if page_text: text += f"Sayfa {page_num + 1}:\n" + page_text + "\n\n" except Exception as e: print(f"PDF okunurken hata oluştu: {e}") return None if not text: print("PDF'ten metin çıkarılamadı.") return None print(f"Metin toplam {len(text)} karakter.") text_splitter = RecursiveCharacterTextSplitter( chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP, length_function=len, ) chunks = text_splitter.split_text(text) print(f"Metin {len(chunks)} parçaya ayrıldı.") print(f"Embedding modeli yükleniyor: {EMBEDDING_MODEL_NAME}") embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME) print("FAISS vektör veritabanı oluşturuluyor...") db = FAISS.from_texts(chunks, embeddings) print("Vektör veritabanı başarıyla oluşturuldu.") return db, embeddings vector_db, embeddings_model = None, None if os.path.exists(PDF_PATH): vector_db, embeddings_model = create_vector_db_from_pdf(PDF_PATH) else: print(f"Hata: {PDF_PATH} bulunamadı. Lütfen PDF dosyasını projenizin kök dizinine yükleyin.") print(f"LLM modeli yükleniyor: {LLM_MODEL_NAME} - Cihaz: {DEVICE}") tokenizer = None llm = None try: tokenizer = AutoTokenizer.from_pretrained(LLM_MODEL_NAME) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token if DEVICE == "cuda": quantization_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True, ) model = GPT2LMHeadModel.from_pretrained(LLM_MODEL_NAME, quantization_config=quantization_config, device_map="auto") else: model = GPT2LMHeadModel.from_pretrained(LLM_MODEL_NAME).to(DEVICE) text_generation_pipeline = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=MAX_NEW_TOKENS, # Global MAX_NEW_TOKENS kullanıldı temperature=0.7, do_sample=True, top_k=50, num_return_sequences=1, device=0 if DEVICE == "cuda" else -1, pad_token_id=tokenizer.pad_token_id, return_full_text=False ) llm = HuggingFacePipeline(pipeline=text_generation_pipeline) print("LLM modeli başarıyla yüklendi.") except Exception as e: print(f"LLM modeli yüklenirken hata oluştu: {e}") llm = None # --- 2. Gradio Arayüzü İçin Soru Cevaplama Fonksiyonu --- def answer_question(question, chat_history): global tokenizer if vector_db is None or llm is None or tokenizer is None: return "", chat_history + [[question, "Üzgünüm, PDF, LLM veya Tokenizer yüklenemediği için şu an sorularınızı cevaplayamıyorum. Lütfen logları kontrol edin."]] print(f"Gelen soru: {question}") retrieved_docs = vector_db.similarity_search(question, k=2) context = "\n\n".join([doc.page_content for doc in retrieved_docs]) print(f"Bağlam için {len(retrieved_docs)} belge bulundu. İlk 200 karakter:\n{context[:200]}...") prompt = f"""Aşağıdaki bağlamı kullanarak soruyu cevaplayın. Bağlamda yoksa uydurma yapmayın. Cevabı madde madde yazın ve en sonda kullandığınız kaynakları (varsa 'Sayfa X' gibi belirtin) listeleyin. Bağlam: {context} Soru: {question} Cevap: """ print("LLM'e gönderilen prompt'ın başlangıcı:", prompt[:500]) # --- Prompt uzunluğunu MODEL_MAX_LENGTH - MAX_NEW_TOKENS ile sınırlayalım --- # Modelin içine girecek token sayısı (MODEL_MAX_LENGTH - MAX_NEW_TOKENS) olmalı max_input_tokens = MODEL_MAX_LENGTH - MAX_NEW_TOKENS # Prompt'u token'lara ayır ve uzunluğunu kontrol et prompt_tokens = tokenizer.encode(prompt, return_tensors='pt')[0] tokenized_prompt_length = prompt_tokens.shape[0] print(f"Oluşturulan prompt'un token uzunluğu: {tokenized_prompt_length}") if tokenized_prompt_length > max_input_tokens: print(f"UYARI: Prompt'un token uzunluğu ({tokenized_prompt_length}) modelin kabul edebileceği maksimum girdi uzunluğunu ({max_input_tokens}) aşıyor! Kısaltılıyor...") prompt_tokens = prompt_tokens[:max_input_tokens] prompt = tokenizer.decode(prompt_tokens, skip_special_tokens=True) print(f"Prompt kısaltıldı. Yeni token uzunluğu: {len(tokenizer.encode(prompt))}") print("Kısaltılmış prompt'ın başlangıcı:", prompt[:500]) try: response_data = llm(prompt) if isinstance(response_data, list) and len(response_data) > 0 and 'generated_text' in response_data[0]: final_answer = response_data[0]['generated_text'].strip() else: final_answer = str(response_data).strip() if not final_answer: final_answer = "Sorunuzla ilgili bağlamda yeterli bilgi bulunamadı." if final_answer.lower().startswith(prompt.lower()): final_answer = final_answer[len(prompt):].strip() if "Cevap:" in final_answer: final_answer = final_answer.split("Cevap:", 1)[-1].strip() if final_answer.lower().startswith("aşağıdaki bağlamı kullanarak"): final_answer = final_answer.split("Cevap:", 1)[-1].strip() if "Cevap:" in final_answer else final_answer.strip() final_answer = "..." + final_answer except Exception as e: final_answer = f"Cevap üretilirken bir hata oluştu: {e}" print(final_answer) chat_history.append((question, final_answer)) return "", chat_history # --- 3. Gradio Arayüz Tanımı --- with gr.Blocks() as demo: gr.Markdown( """ # Fırat Üniversitesi Mini RAG Botu Fırat Üniversitesi mevzuat.firat.edu.tr adresinden alınan PDF dosyası üzerinde soru-cevap yapın. Sorularınıza belgedeki bilgilere dayanarak cevaplar verilecektir. Bağlamda bilgi yoksa, bot uydurma yapmayacaktır. """ ) chatbot = gr.Chatbot(height=400, label="Sohbet") msg = gr.Textbox(label="Sorunuzu buraya yazın:", placeholder="Örn: 'Belgede tez jürisi kuralı ne?'") with gr.Row(): submit_btn = gr.Button("Gönder") clear_btn = gr.Button("Sohbeti Temizle") msg.submit(answer_question, [msg, chatbot], [msg, chatbot]) submit_btn.click(answer_question, [msg, chatbot], [msg, chatbot]) clear_btn.click(lambda: (None, []), outputs=[msg, chatbot]) if __name__ == "__main__": demo.launch()