import gradio as gr from sentence_transformers import SentenceTransformer, util from transformers import pipeline # T5 yerine standart pipeline kullanacağız import torch import os import pickle import re # --- 1. KONFİGÜRASYON --- TEXT_FILE = "icerik.txt" EMBEDDING_CACHE_FILE = "embeddings.pkl" EMBEDDING_MODEL_NAME = "all-MiniLM-L6-v2" # YENİ MODEL: Türkçe için eğitilmiş, "saçmalamayan" Extractive QA modeli QA_MODEL_NAME = "savasy/bert-base-turkish-squad" RETRIEVAL_THRESHOLD = 0.55 # Paragraf bulma benzerlik skoru QA_THRESHOLD = 0.40 # Cevap çıkarma güven skoru # İLETİŞİM BİLGİLERİ (Arayüze Entegre Edilecek) PHONE_MAIN = '+90 531 294 22 34' PHONE_LANDLINE_EXT = '0232 464 41 00 (Dahili 165)' WHATSAPP_LINK = 'http://wa.me/905312942234' APPOINTMENT_LINK = 'https://calendar.app.google/JT9A1oGHVGopNZ9y8' MAP_LINK = 'https://maps.app.goo.gl/PLsBy9afjiRB9WDb6' # --- 2. MODELLERİN YÜKLENMESİ ve VERİ YÜKLEME --- print("Modeller yükleniyor...") # 1. Paragraf Bulucu (Retrieval) embedding_model = SentenceTransformer(EMBEDDING_MODEL_NAME) # 2. Cevap Çıkarıcı (Extraction) qa_pipeline = pipeline("question-answering", model=QA_MODEL_NAME, tokenizer=QA_MODEL_NAME) print("Tüm modeller yüklendi.") def load_documents(): """Dokümanları yükler ve embedding'leri önbellekten çeker.""" try: if os.path.exists(EMBEDDING_CACHE_FILE): with open(EMBEDDING_CACHE_FILE, "rb") as f: data = pickle.load(f) return data["docs"], data["embeddings"] with open(TEXT_FILE, "r", encoding="utf-8") as f: paragraphs = f.read().split("\n\n") docs = [p.strip() for p in paragraphs if len(p.strip()) > 0] if not docs: raise ValueError("icerik.txt dosyası boş.") embeddings = embedding_model.encode(docs, convert_to_tensor=True) with open(EMBEDDING_CACHE_FILE, "wb") as f: pickle.dump({"docs": docs, "embeddings": embeddings}, f) return docs, embeddings except (FileNotFoundError, ValueError) as e: print(f"HATA: {e}") return [], None docs, embeddings = load_documents() # --- 3. POST-PROCESSİNG (Sadece Kaynak Gösterimi İçin) --- def post_process_context(context: str) -> str: paragraphs = context.split('\n\n') cleaned_paragraphs = [] for p in paragraphs: p = p.strip() p = re.sub(r'^[\s]*[\*\-\•\d\.\)]+\s*', '', p, flags=re.MULTILINE).strip() if len(p) > 20: cleaned_paragraphs.append(p) return "
• " + "
• ".join(cleaned_paragraphs) # --- 4. ANA MANTIK (Extractive RAG) --- def answer_question(question: str): if qa_pipeline is None or not docs or embeddings is None: return "

Sistem Hatası:

Lokal bilgi kaynakları veya Yapay Zeka motoru yüklenemedi.

" # 1. Retrieval (Çekme): En alakalı paragrafları bul question_embedding = embedding_model.encode(question, convert_to_tensor=True) scores = util.pytorch_cos_sim(question_embedding, embeddings)[0] top_k = min(3, len(docs)) top_results = torch.topk(scores, k=top_k) selected_context = [] for score, idx in zip(top_results.values, top_results.indices): if score.item() >= RETRIEVAL_THRESHOLD: selected_context.append(docs[idx.item()]) if not selected_context: # Cevap bulunamadı durumu return f"""

Üzgünüz, dahili bilgi bankamızda bu konuyla ilgili doğrudan bir bilgiye ulaşılamadı.

Daha açıklayıcı bir soru yazabilir veya WhatsApp üzerinden bize ulaşabilirsiniz.

""" full_context = "\n\n".join(selected_context) # 2. Extraction (Cevabı Çıkarma) - YENİ KISIM # Model, bu metin içinden sorunun cevabını bulur qa_result = qa_pipeline(question=question, context=full_context) answer = qa_result['answer'] score = qa_result['score'] # Güven skoru düşükse (cevap alakasızsa) veya cevap çok kısaysa if score < QA_THRESHOLD or len(answer) < 10: return f"""

Bilgi bankamızda bu soruya yönelik net bir cevap bulunamadı.

Lütfen ofisimizle iletişime geçin. WhatsApp üzerinden bize ulaşabilirsiniz.

""" # 3. Final Sunum processed_context_html = post_process_context(full_context) answer_html = f"""

🧾 Sorunuz

{question}

✅ Yapay Zeka Yanıtı (Çıkarım)

{answer}

📚 Cevabın Alındığı Kaynak Metinler

{processed_context_html}

📢 Kişisel Değerlendirme ve Görüşme

Detaylı durum tespiti ve kişisel yol haritanız için hemen bizimle iletişime geçin:

📞 WhatsApp Üzerinden Hızlı İletişim
Telefon: {PHONE_MAIN} | Dahili: {PHONE_LANDLINE_EXT}

""" return answer_html # --- 5. GRADIO ARAYÜZÜ --- # (Arayüz kodunda değişiklik yok, tüm iletişim bilgileri yerinde) header_info = """

Şahin Hukuk | Akıllı Asistan

📞 Telefon: {PHONE_MAIN} & {PHONE_LANDLINE_EXT}
💬 WhatsApp: Tıklayın
📅 Randevu: Randevu Al
🗺️ Yol Tarifi: Haritada Gör

""".format( PHONE_MAIN=PHONE_MAIN, PHONE_LANDLINE_EXT=PHONE_LANDLINE_EXT, WHATSAPP_LINK=WHATSAPP_LINK, APPOINTMENT_LINK=APPOINTMENT_LINK, MAP_LINK=MAP_LINK ) interface = gr.Interface( fn=answer_question, inputs=gr.Textbox(label="Sorunuzu buraya yazın", lines=2, placeholder="Örn: Kira tespit davası nedir?"), outputs=gr.HTML(label="Yanıt"), title="Şahin Hukuk | Akıllı Asistan", description=header_info, allow_flagging="never", submit_btn="Gönder", clear_btn="Temizle") interface.launch()