aysemutluay commited on
Commit
d8d3708
·
verified ·
1 Parent(s): a989689

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -72
app.py CHANGED
@@ -8,82 +8,112 @@ from transformers import pipeline, AutoTokenizer, GPT2LMHeadModel, BitsAndBytesC
8
  import os
9
  import torch
10
  import re
 
11
 
12
  # --- 0. Global Değişkenler ve Ayarlar ---
13
  PDF_PATH = "mevzuat.pdf"
14
- CHUNK_SIZE = 600
15
- CHUNK_OVERLAP = 120
 
16
  EMBEDDING_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
17
  LLM_MODEL_NAME = "ytu-ce-cosmos/turkish-gpt2-medium"
18
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
19
 
20
  MODEL_MAX_LENGTH = 1024
21
- MAX_NEW_TOKENS = 300
22
-
23
- # --- Yardımcı Metin Temizleme Fonksiyonu (Geliştirildi) ---
24
- def clean_and_normalize_text(text):
25
- text = re.sub(r'\s+', ' ', text) # Birden fazla boşluğu tek boşluğa çevir
26
- text = text.strip() # Baştaki ve sondaki boşlukları kaldır
27
- text = text.lower() # TÜM METNİ KÜÇÜK HARFE ÇEVİR
28
- # Opsiyonel: Türkçe karakterleri normalize etme (örneğin ı->i, ğ->g vb. ancak bu bazen anlamı bozabilir, dikkatli kullanılmalı)
29
- # text = text.replace('ş', 's').replace('ç', 'c').replace('ö', 'o').replace('ü', 'u').replace('ğ', 'g').replace('ı', 'i')
 
 
 
 
 
 
 
 
 
 
30
  return text
31
 
32
  # --- 1. PDF'i İşle ve Vektör Veritabanı Oluştur ---
33
  def create_vector_db_from_pdf(pdf_path):
34
- print(f"PDF okunuyor: {pdf_path}")
35
 
36
  chunks_with_metadata = []
37
 
38
  try:
39
  with open(pdf_path, "rb") as file:
40
  reader = PyPDF2.PdfReader(file)
 
 
 
41
  for page_num, page in enumerate(reader.pages):
42
- page_text = page.extract_text()
43
- if page_text:
44
- # Metin temizliği ve normalizasyonunu uygula
45
- cleaned_page_text = clean_and_normalize_text(page_text)
46
-
47
- text_splitter = RecursiveCharacterTextSplitter(
48
- chunk_size=CHUNK_SIZE,
49
- chunk_overlap=CHUNK_OVERLAP,
50
- length_function=len,
51
- )
52
- page_chunks = text_splitter.split_text(cleaned_page_text)
53
-
54
- for chunk in page_chunks:
55
- chunks_with_metadata.append({
56
- "content": chunk,
57
- "source_page": page_num + 1
58
- })
 
 
 
 
 
59
  except Exception as e:
60
- print(f"PDF okunurken hata oluştu: {e}")
61
  return None
62
 
63
  if not chunks_with_metadata:
64
- print("PDF'ten metin çıkarılamadı veya parçalanamadı.")
65
  return None
66
 
67
- print(f"Metin {len(chunks_with_metadata)} parçaya ayrıldı (metadata dahil).")
68
 
69
- print(f"Embedding modeli yükleniyor: {EMBEDDING_MODEL_NAME}")
70
- embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)
 
 
 
 
71
 
72
  texts = [item["content"] for item in chunks_with_metadata]
73
  metadatas = [{"source_page": item["source_page"]} for item in chunks_with_metadata]
74
 
75
- print("FAISS vektör veritabanı oluşturuluyor...")
76
- db = FAISS.from_texts(texts, embeddings, metadatas=metadatas)
77
- print("Vektör veritabanı başarıyla oluşturuldu.")
78
- return db, embeddings
 
 
 
 
79
 
 
80
  vector_db, embeddings_model = None, None
81
  if os.path.exists(PDF_PATH):
82
  vector_db, embeddings_model = create_vector_db_from_pdf(PDF_PATH)
83
  else:
84
- print(f"Hata: {PDF_PATH} bulunamadı. Lütfen PDF dosyasını projenizin kök dizinine yükleyin.")
85
 
86
- print(f"LLM modeli yükleniyor: {LLM_MODEL_NAME} - Cihaz: {DEVICE}")
 
87
  tokenizer = None
88
  llm = None
89
  try:
@@ -96,7 +126,7 @@ try:
96
  quantization_config = BitsAndBytesConfig(
97
  load_in_4bit=True,
98
  bnb_4bit_quant_type="nf4",
99
- bnb_4bit_compute_dtype="torch.float16", # "torch.float16" olarak tırnak içine alındı
100
  bnb_4bit_use_double_quant=True,
101
  )
102
  model = GPT2LMHeadModel.from_pretrained(LLM_MODEL_NAME,
@@ -110,47 +140,82 @@ try:
110
  model=model,
111
  tokenizer=tokenizer,
112
  max_new_tokens=MAX_NEW_TOKENS,
113
- temperature=0.4,
114
  do_sample=True,
115
- top_k=40,
116
  num_return_sequences=1,
117
  device=0 if DEVICE == "cuda" else -1,
118
  pad_token_id=tokenizer.pad_token_id,
119
- return_full_text=False
120
  )
121
  llm = HuggingFacePipeline(pipeline=text_generation_pipeline)
122
- print("LLM modeli başarıyla yüklendi.")
123
  except Exception as e:
124
- print(f"LLM modeli yüklenirken hata oluştu: {e}")
125
- llm = None
126
 
127
  # --- 2. Gradio Arayüzü İçin Soru Cevaplama Fonksiyonu ---
128
  def answer_question(question, chat_history):
129
  global tokenizer
130
  if vector_db is None or llm is None or tokenizer is None:
131
- 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."]]
 
 
132
 
133
- print(f"Gelen soru: {question}")
134
-
135
- # Soruyu da küçük harfe çevirerek normalizasyonu sağla
136
- normalized_question = clean_and_normalize_text(question)
137
 
138
- retrieved_docs_with_scores = vector_db.similarity_search_with_score(normalized_question, k=4)
 
 
 
 
 
 
 
 
139
 
 
 
 
 
140
  context_parts = []
141
- source_pages = set()
142
 
143
- for doc, score in retrieved_docs_with_scores:
144
- context_parts.append(doc.page_content)
145
- if 'source_page' in doc.metadata:
146
- source_pages.add(doc.metadata['source_page'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
  context = "\n\n".join(context_parts)
149
 
150
- print(f"Bağlam için {len(retrieved_docs_with_scores)} belge bulundu. İlk 200 karakter:\n{context[:200]}...")
 
 
 
 
151
 
152
- # Geliştirilmiş Prompt
153
- prompt = f"""Sen bir Fırat Üniversitesi mevzuat uzmanısın. Yalnızca aşağıdaki 'Bağlam' bölümünde verilen bilgilere dayanarak 'Soru'yu detaylı, anlaşılır ve madde madde olacak şekilde cevapla. Eğer soruya cevap verecek bilgi bağlamda yoksa, 'Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.' şeklinde cevap ver ve kesinlikle uydurma bilgi üretme. Cevabın sonunda, bilginin geçtiği sayfa numaralarını 'Kaynak: Sayfa X, Y, Z' şeklinde açıkça belirt. Bağlamda sayfa bilgisi yoksa belirtme.
 
 
 
 
154
 
155
  Bağlam:
156
  {context}
@@ -159,21 +224,23 @@ Soru: {question}
159
 
160
  Cevap:
161
  """
162
- print("LLM'e gönderilen prompt'ın başlangıcı:", prompt[:500])
163
 
 
164
  max_input_tokens = MODEL_MAX_LENGTH - MAX_NEW_TOKENS
165
 
166
  prompt_tokens = tokenizer.encode(prompt, return_tensors='pt')[0]
167
  tokenized_prompt_length = prompt_tokens.shape[0]
168
 
169
- print(f"Oluşturulan prompt'un token uzunluğu: {tokenized_prompt_length}")
170
 
171
  if tokenized_prompt_length > max_input_tokens:
172
- 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...")
 
173
  prompt_tokens = prompt_tokens[:max_input_tokens]
174
  prompt = tokenizer.decode(prompt_tokens, skip_special_tokens=True)
175
- print(f"Prompt kısaltıldı. Yeni token uzunluğu: {len(tokenizer.encode(prompt))}")
176
- print("Kısaltılmış prompt'ın başlangıcı:", prompt[:500])
177
 
178
  final_answer = ""
179
  try:
@@ -184,25 +251,41 @@ Cevap:
184
  else:
185
  final_answer = str(response_data).strip()
186
 
 
 
187
  if final_answer.lower().startswith(prompt.lower()):
188
  final_answer = final_answer[len(prompt):].strip()
189
 
 
190
  if "Cevap:" in final_answer:
191
  final_answer = final_answer.split("Cevap:", 1)[-1].strip()
192
 
193
- if final_answer.lower().startswith("aşağıdaki bağlamı kullanarak"):
194
- final_answer = final_answer.split("Cevap:", 1)[-1].strip() if "Cevap:" in final_answer else final_answer.strip()
 
 
 
 
 
 
 
 
 
195
 
196
- if not final_answer or final_answer.strip().lower() == "üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.":
 
197
  final_answer = "Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır."
198
 
 
199
  if source_pages and final_answer != "Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.":
200
  sorted_pages = sorted(list(source_pages))
201
  final_answer += f"\n\n**Kaynak:** Sayfa {', '.join(map(str, sorted_pages))}"
 
 
202
 
203
  except Exception as e:
204
- final_answer = f"Cevap üretilirken bir hata oluştu: {e}"
205
- print(final_answer)
206
 
207
  chat_history.append((question, final_answer))
208
  return "", chat_history
@@ -214,7 +297,7 @@ with gr.Blocks() as demo:
214
  # Fırat Üniversitesi Mini RAG Botu
215
  Fırat Üniversitesi mevzuat.firat.edu.tr adresinden alınan PDF dosyası üzerinde soru-cevap yapın.
216
  Sorularınıza belgedeki bilgilere dayanarak cevaplar verilecektir.
217
- Bağlamda bilgi yoksa, bot uydurma yapmayacaktır.
218
  **Cevaplar artık kaynak sayfa numarasını da içermektedir.**
219
  """
220
  )
 
8
  import os
9
  import torch
10
  import re
11
+ import unicodedata # Unicode normalizasyonu için
12
 
13
  # --- 0. Global Değişkenler ve Ayarlar ---
14
  PDF_PATH = "mevzuat.pdf"
15
+ # Mevzuat için daha uygun Chunk boyutları
16
+ CHUNK_SIZE = 700 # Daha da artırıldı
17
+ CHUNK_OVERLAP = 150 # Daha da artırıldı
18
  EMBEDDING_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
19
  LLM_MODEL_NAME = "ytu-ce-cosmos/turkish-gpt2-medium"
20
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
21
 
22
  MODEL_MAX_LENGTH = 1024
23
+ MAX_NEW_TOKENS = 350 # Daha detaylı cevaplar için artırıldı
24
+
25
+ # --- Yardımcı Metin Temizleme ve Normalizasyon Fonksiyonu (Daha Kapsamlı) ---
26
+ def normalize_text(text):
27
+ if not isinstance(text, str):
28
+ return ""
29
+
30
+ # Unicode normalizasyonu (aksansız karakterlere çevirme, vb.)
31
+ text = unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('utf-8', 'ignore')
32
+
33
+ # Küçük harfe çevir
34
+ text = text.lower()
35
+
36
+ # Noktalama işaretlerini kaldır (kelime bazlı arama için önemli)
37
+ text = re.sub(r'[^\w\s]', '', text)
38
+
39
+ # Birden fazla boşluğu tek boşluğa çevir ve baştaki/sondaki boşlukları kaldır
40
+ text = re.sub(r'\s+', ' ', text).strip()
41
+
42
  return text
43
 
44
  # --- 1. PDF'i İşle ve Vektör Veritabanı Oluştur ---
45
  def create_vector_db_from_pdf(pdf_path):
46
+ print(f"[{os.path.basename(__file__)}] PDF okunuyor: {pdf_path}")
47
 
48
  chunks_with_metadata = []
49
 
50
  try:
51
  with open(pdf_path, "rb") as file:
52
  reader = PyPDF2.PdfReader(file)
53
+ total_pages = len(reader.pages)
54
+ print(f"[{os.path.basename(__file__)}] Toplam {total_pages} sayfa bulundu.")
55
+
56
  for page_num, page in enumerate(reader.pages):
57
+ try:
58
+ page_text = page.extract_text()
59
+ if page_text:
60
+ # Metin temizliği ve normalizasyonunu uygula
61
+ cleaned_and_normalized_page_text = normalize_text(page_text)
62
+
63
+ text_splitter = RecursiveCharacterTextSplitter(
64
+ chunk_size=CHUNK_SIZE,
65
+ chunk_overlap=CHUNK_OVERLAP,
66
+ length_function=len,
67
+ )
68
+ page_chunks = text_splitter.split_text(cleaned_and_normalized_page_text)
69
+
70
+ for chunk in page_chunks:
71
+ chunks_with_metadata.append({
72
+ "content": chunk,
73
+ "source_page": page_num + 1
74
+ })
75
+ except Exception as page_e:
76
+ print(f"[{os.path.basename(__file__)}] Sayfa {page_num + 1} okunurken veya işlenirken hata oluştu: {page_e}")
77
+ continue # Bir sonraki sayfaya geç
78
+
79
  except Exception as e:
80
+ print(f"[{os.path.basename(__file__)}] PDF dosyası okunurken genel hata oluştu: {e}")
81
  return None
82
 
83
  if not chunks_with_metadata:
84
+ print(f"[{os.path.basename(__file__)}] PDF'ten metin çıkarılamadı veya parçalanamadı. Lütfen PDF içeriğini kontrol edin.")
85
  return None
86
 
87
+ print(f"[{os.path.basename(__file__)}] Metin {len(chunks_with_metadata)} parçaya ayrıldı (metadata dahil).")
88
 
89
+ print(f"[{os.path.basename(__file__)}] Embedding modeli yükleniyor: {EMBEDDING_MODEL_NAME}")
90
+ try:
91
+ embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)
92
+ except Exception as e:
93
+ print(f"[{os.path.basename(__file__)}] Embedding modeli yüklenirken hata oluştu: {e}")
94
+ return None
95
 
96
  texts = [item["content"] for item in chunks_with_metadata]
97
  metadatas = [{"source_page": item["source_page"]} for item in chunks_with_metadata]
98
 
99
+ print(f"[{os.path.basename(__file__)}] FAISS vektör veritabanı oluşturuluyor...")
100
+ try:
101
+ db = FAISS.from_texts(texts, embeddings, metadatas=metadatas)
102
+ print(f"[{os.path.basename(__file__)}] Vektör veritabanı başarıyla oluşturuldu.")
103
+ return db, embeddings
104
+ except Exception as e:
105
+ print(f"[{os.path.basename(__file__)}] FAISS veritabanı oluşturulurken hata oluştu: {e}")
106
+ return None
107
 
108
+ # --- Vektör veritabanını başlatma ---
109
  vector_db, embeddings_model = None, None
110
  if os.path.exists(PDF_PATH):
111
  vector_db, embeddings_model = create_vector_db_from_pdf(PDF_PATH)
112
  else:
113
+ print(f"[{os.path.basename(__file__)}] Hata: {PDF_PATH} bulunamadı. Lütfen PDF dosyasını projenizin kök dizinine yükleyin ve doğru yolda olduğundan emin olun.")
114
 
115
+ # --- LLM modelini yükleme ---
116
+ print(f"[{os.path.basename(__file__)}] LLM modeli yükleniyor: {LLM_MODEL_NAME} - Cihaz: {DEVICE}")
117
  tokenizer = None
118
  llm = None
119
  try:
 
126
  quantization_config = BitsAndBytesConfig(
127
  load_in_4bit=True,
128
  bnb_4bit_quant_type="nf4",
129
+ bnb_4bit_compute_dtype=torch.float16, # Doğru format "torch.float16" veya torch.float16
130
  bnb_4bit_use_double_quant=True,
131
  )
132
  model = GPT2LMHeadModel.from_pretrained(LLM_MODEL_NAME,
 
140
  model=model,
141
  tokenizer=tokenizer,
142
  max_new_tokens=MAX_NEW_TOKENS,
143
+ temperature=0.3, # Daha kesin ve tutarlı cevaplar için biraz daha düşürüldü
144
  do_sample=True,
145
+ top_k=30, # Daha odaklı cevaplar için biraz daha düşürüldü
146
  num_return_sequences=1,
147
  device=0 if DEVICE == "cuda" else -1,
148
  pad_token_id=tokenizer.pad_token_id,
149
+ return_full_text=False # Sadece üretilen metni döndür
150
  )
151
  llm = HuggingFacePipeline(pipeline=text_generation_pipeline)
152
+ print(f"[{os.path.basename(__file__)}] LLM modeli başarıyla yüklendi.")
153
  except Exception as e:
154
+ print(f"[{os.path.basename(__file__)}] LLM modeli yüklenirken kritik hata oluştu: {e}")
155
+ llm = None # Hata durumunda LLM'i None olarak bırak
156
 
157
  # --- 2. Gradio Arayüzü İçin Soru Cevaplama Fonksiyonu ---
158
  def answer_question(question, chat_history):
159
  global tokenizer
160
  if vector_db is None or llm is None or tokenizer is None:
161
+ error_msg = "Üzgünüm, sistem başlatılamadı (PDF, LLM veya Tokenizer yüklenemedi). Lütfen logları kontrol edin ve yöneticinizle iletişime geçin."
162
+ print(f"[{os.path.basename(__file__)}] Hata: {error_msg}")
163
+ return "", chat_history + [[question, error_msg]]
164
 
165
+ print(f"[{os.path.basename(__file__)}] Gelen soru: '{question}'")
 
 
 
166
 
167
+ # Soruyu da veritabanındaki metinlerle aynı şekilde normalize et
168
+ normalized_question = normalize_text(question)
169
+ print(f"[{os.path.basename(__file__)}] Normalize edilmiş soru: '{normalized_question}'")
170
+
171
+ if not normalized_question.strip():
172
+ return "", chat_history + [[question, "Lütfen geçerli bir soru girin."]]
173
+
174
+ # Daha fazla potansiyel ilgili belge getir
175
+ retrieved_docs_with_scores = vector_db.similarity_search_with_score(normalized_question, k=6) # k değeri 4'ten 6'ya çıkarıldı
176
 
177
+ if not retrieved_docs_with_scores:
178
+ print(f"[{os.path.basename(__file__)}] Sorunuza uygun ilgili belge bulunamadı.")
179
+ return "", chat_history + [[question, "Üzgünüm, sorunuzla ilgili bağlamda herhangi bir bilgi bulunamadı. Lütfen sorunuzu farklı bir şekilde ifade etmeyi deneyin veya daha spesifik olun."]]
180
+
181
  context_parts = []
182
+ source_pages = set() # Kullanılan sayfa numaralarını tutmak için
183
 
184
+ for i, (doc, score) in enumerate(retrieved_docs_with_scores):
185
+ # Yüksek benzerlik skoruna sahip (düşük mesafe) belgeleri tercih et
186
+ # Çok alakasız görünenleri eleyebiliriz, eşik değeri deneyerek ayarlanmalı
187
+ # Örneğin, mesafe 0.5'ten büyükse ele (FAISS'te daha küçük mesafe daha iyi benzerliktir)
188
+ # print(f"Doc {i+1} Score: {score}, Content: {doc.page_content[:100]}...") # Debug için
189
+
190
+ # Eğer embedding modeli cosine similarity döndürüyorsa, 0'a yakınlık yüksek benzerlik demektir.
191
+ # Eğer L2 mesafesi döndürüyorsa, 0'a yakınlık yüksek benzerlik demektir.
192
+ # sentence-transformers genellikle cosine similarity döndürür, bu yüzden 1'e yakın değerler daha iyi.
193
+ # Ancak FAISS genellikle L2 mesafesi kullanır (veya cosine distance'ı 1-cos(theta) olarak temsil eder).
194
+ # Genellikle 0'a yakın değerler daha iyi (daha küçük mesafe).
195
+ # Eğer score çok yüksek (büyük bir sayı) ise, bu doküman çok alakasız olabilir.
196
+
197
+ # Basit bir filtreleme: İlk 3 dokümanı kesin al, diğerlerini skorlarına göre değerlendir
198
+ if i < 3 or score < 0.6: # Bu eşik değeri denemelerle ayarlanmalı
199
+ context_parts.append(doc.page_content)
200
+ if 'source_page' in doc.metadata:
201
+ source_pages.add(doc.metadata['source_page'])
202
+ else:
203
+ print(f"[{os.path.basename(__file__)}] Doküman {i+1} çok alakasız olduğu için bağlama eklenmedi (Skor: {score:.4f}).")
204
 
205
  context = "\n\n".join(context_parts)
206
 
207
+ if not context.strip():
208
+ print(f"[{os.path.basename(__file__)}] Filtreleme sonrası bağlam boş kaldı.")
209
+ return "", chat_history + [[question, "Üzgünüm, sorunuzla ilgili yeterince alakalı bilgi bulunamadı. Lütfen sorunuzu farklı bir şekilde ifade etmeyi deneyin veya daha spesifik olun."]]
210
+
211
+ print(f"[{os.path.basename(__file__)}] Bağlam için {len(context_parts)} belge parçası kullanıldı. İlk 200 karakter:\n'{context[:200]}...'")
212
 
213
+ # Geliştirilmiş, daha detaylı ve kısıtlayıcı Prompt
214
+ prompt = f"""Sen bir Fırat Üniversitesi mevzuat uzmanısın. Görevin, yalnızca ve kesinlikle aşağıdaki 'Bağlam' bölümünde verilen bilgilere dayanarak 'Soru'yu eksiksiz, net, anlaşılır ve madde madde olacak şekilde cevaplamaktır.
215
+
216
+ Eğer 'Bağlam'da soruya cevap verecek yeterli ve doğrudan bilgi bulunmuyorsa, kesinlikle kendi bilginizden uydurma yapmayın. Bu durumda, 'Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.' şeklinde açıkça belirtin.
217
+
218
+ Cevabın sonunda, bilginin alındığı sayfa numaralarını 'Kaynak: Sayfa X, Y, Z' formatında açıkça ve sadece bağlamda belirtilen sayfa numaralarını kullanarak listeleyin. Bağlamda sayfa bilgisi yoksa (ki bu olmamalı), kaynak belirtme.
219
 
220
  Bağlam:
221
  {context}
 
224
 
225
  Cevap:
226
  """
227
+ print(f"[{os.path.basename(__file__)}] LLM'e gönderilen prompt'ın başlangıcı: '{prompt[:500]}...'")
228
 
229
+ # --- Prompt uzunluğunu MODEL_MAX_LENGTH - MAX_NEW_TOKENS ile sınırlayalım ---
230
  max_input_tokens = MODEL_MAX_LENGTH - MAX_NEW_TOKENS
231
 
232
  prompt_tokens = tokenizer.encode(prompt, return_tensors='pt')[0]
233
  tokenized_prompt_length = prompt_tokens.shape[0]
234
 
235
+ print(f"[{os.path.basename(__file__)}] Oluşturulan prompt'un token uzunluğu: {tokenized_prompt_length}")
236
 
237
  if tokenized_prompt_length > max_input_tokens:
238
+ print(f"[{os.path.basename(__file__)}] 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...")
239
+ # Token bazında kısaltma
240
  prompt_tokens = prompt_tokens[:max_input_tokens]
241
  prompt = tokenizer.decode(prompt_tokens, skip_special_tokens=True)
242
+ print(f"[{os.path.basename(__file__)}] Prompt kısaltıldı. Yeni token uzunluğu: {len(tokenizer.encode(prompt))}")
243
+ print(f"[{os.path.basename(__file__)}] Kısaltılmış prompt'ın başlangıcı: '{prompt[:500]}...'")
244
 
245
  final_answer = ""
246
  try:
 
251
  else:
252
  final_answer = str(response_data).strip()
253
 
254
+ # LLM'in prompt'u tekrar etme veya gereksiz kısımları kaldırma
255
+ # Çoklu kontrol noktası
256
  if final_answer.lower().startswith(prompt.lower()):
257
  final_answer = final_answer[len(prompt):].strip()
258
 
259
+ # "Cevap:" kısmı LLM tarafından tekrar edildiyse temizle
260
  if "Cevap:" in final_answer:
261
  final_answer = final_answer.split("Cevap:", 1)[-1].strip()
262
 
263
+ # Prompt'un başlangıcından kalıntı kaldıysa temizle
264
+ if final_answer.lower().startswith("sen bir fırat üniversitesi mevzuat uzmanısın.") or \
265
+ final_answer.lower().startswith("aşağıdaki bağlamı kullanarak"):
266
+ # Daha sofistike bir temizleme: "Cevap:" ifadesini arayarak o kısımdan sonrasını al
267
+ match = re.search(r"cevap:\s*(.*)", final_answer, re.IGNORECASE | re.DOTALL)
268
+ if match:
269
+ final_answer = match.group(1).strip()
270
+ else: # Bulamazsa yine de bir kısmı sil
271
+ final_answer = final_answer.split("Soru:", 1)[-1].strip() if "Soru:" in final_answer else final_answer.strip()
272
+ final_answer = final_answer.split("Cevap:", 1)[-1].strip() if "Cevap:" in final_answer else final_answer.strip()
273
+
274
 
275
+ # Cevabın boş veya anlamsız olup olmadığını kontrol et
276
+ if not final_answer or final_answer.strip().lower() in ["üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.", "üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır", "bilgi bulunamadı.", "bilgi bulunamadı"]:
277
  final_answer = "Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır."
278
 
279
+ # Kaynakları cevaba ekle
280
  if source_pages and final_answer != "Üzgünüm, bu soruya bağlamda yeterli bilgi bulunmamaktadır.":
281
  sorted_pages = sorted(list(source_pages))
282
  final_answer += f"\n\n**Kaynak:** Sayfa {', '.join(map(str, sorted_pages))}"
283
+
284
+ print(f"[{os.path.basename(__file__)}] Üretilen son cevap: '{final_answer[:500]}...'")
285
 
286
  except Exception as e:
287
+ final_answer = f"Cevap üretilirken kritik bir hata oluştu: {e}. Lütfen daha sonra tekrar deneyin veya farklı bir soru sorun."
288
+ print(f"[{os.path.basename(__file__)}] Kritik cevap üretim hatası: {e}")
289
 
290
  chat_history.append((question, final_answer))
291
  return "", chat_history
 
297
  # Fırat Üniversitesi Mini RAG Botu
298
  Fırat Üniversitesi mevzuat.firat.edu.tr adresinden alınan PDF dosyası üzerinde soru-cevap yapın.
299
  Sorularınıza belgedeki bilgilere dayanarak cevaplar verilecektir.
300
+ Bağlamda bilgi yoksa, bot kesinlikle uydurma yapmayacaktır ve bunu açıkça belirtecektir.
301
  **Cevaplar artık kaynak sayfa numarasını da içermektedir.**
302
  """
303
  )