Király Zoltán commited on
Commit
26ee8cf
·
1 Parent(s): 3187c86

Fix: Clean up requirements.txt to resolve build conflicts

Browse files
Files changed (1) hide show
  1. web_indexer_universal_v7.py +141 -77
web_indexer_universal_v7.py CHANGED
@@ -1,5 +1,5 @@
1
  # web_indexer_universal_v7.py
2
- # VÉGLEGES, JAVÍTOTT VERZIÓ
3
 
4
  import os
5
  import time
@@ -24,7 +24,6 @@ try:
24
  TORCH_AVAILABLE = True
25
  except ImportError:
26
  TORCH_AVAILABLE = False
27
- print(f"{RED}FIGYELEM: Torch nincs telepítve.{RESET}")
28
 
29
  try:
30
  import together
@@ -85,44 +84,35 @@ CHUNK_OVERLAP_TOKENS = 50
85
  MIN_CHUNK_SIZE_CHARS = 50
86
  LLM_CHUNK_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
87
 
88
- # === Index Beállítások & Mapping (JAVÍTOTT VERZIÓ) ===
89
- INDEX_SETTINGS_SEPARATE_ANALYZER = {
90
  "analysis": {
91
  "filter": {
92
  "hungarian_stop": {"type": "stop", "stopwords": "_hungarian_"},
93
- "hungarian_stemmer": {"type": "stemmer", "language": "hungarian"},
94
- "synonym_filter": {
95
- "type": "synonym_graph",
96
- "synonyms_set": "synonyms-hu"
97
- }
98
  },
99
  "analyzer": {
100
- "hungarian_indexing_analyzer": {
101
  "tokenizer": "standard",
102
  "filter": ["lowercase", "hungarian_stop", "hungarian_stemmer"]
103
- },
104
- "hungarian_search_analyzer": {
105
- "tokenizer": "standard",
106
- "filter": ["lowercase", "hungarian_stop", "synonym_filter", "hungarian_stemmer"]
107
  }
108
  }
109
  }
110
  }
111
 
112
- INDEX_MAPPINGS_WEB = {
113
  "properties": {
114
- "text_content": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"},
115
  "embedding": {"type": "dense_vector", "dims": EMBEDDING_DIM, "index": True, "similarity": "cosine"},
116
  "source_origin": {"type": "keyword"},
117
  "source_url": {"type": "keyword"},
118
  "source_type": {"type": "keyword"},
119
- "category": {"type": "keyword"},
120
- "heading": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"},
121
- "summary": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"}
122
  }
123
  }
124
 
125
-
126
  # --- Segédfüggvények ---
127
  def initialize_es_client():
128
  print(f"\n{CYAN}Kapcsolódás az Elasticsearch-hez...{RESET}")
@@ -135,8 +125,7 @@ def initialize_es_client():
135
  api_key=ES_API_KEY,
136
  request_timeout=ES_CLIENT_TIMEOUT
137
  )
138
- if not client.ping():
139
- raise ConnectionError("Ping sikertelen.")
140
  print(f"{GREEN}Sikeres Elasticsearch kapcsolat!{RESET}")
141
  return client
142
  except Exception as e:
@@ -155,13 +144,83 @@ def load_embedding_model():
155
  model = SentenceTransformer(EMBEDDING_MODEL_NAME, device=device)
156
  embedding_model = model
157
  EMBEDDING_DIM = model.get_sentence_embedding_dimension()
158
- INDEX_MAPPINGS_WEB["properties"]["embedding"]["dims"] = EMBEDDING_DIM
159
  print(f"{GREEN}Embedding modell betöltve (dim: {EMBEDDING_DIM}, eszköz: {device}).{RESET}")
160
  except Exception as e:
161
  print(f"{RED}Hiba az embedding modell betöltésekor: {e}{RESET}")
162
  embedding_model = None
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  def get_embedding(text):
 
165
  if not embedding_model: return None
166
  try:
167
  return embedding_model.encode(text, normalize_embeddings=True).tolist()
@@ -169,7 +228,23 @@ def get_embedding(text):
169
  print(f"{RED}Hiba embedding közben: {e}{RESET}")
170
  return None
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  def extract_text_from_html(html_content):
 
173
  try:
174
  soup = BeautifulSoup(html_content, 'html.parser')
175
  for element in soup(["script", "style", "nav", "footer", "header", "aside", "form"]):
@@ -179,22 +254,29 @@ def extract_text_from_html(html_content):
179
  return "\n".join(line for line in text.splitlines() if line.strip())
180
  except Exception as e:
181
  print(f"{RED}Hiba a HTML tartalom kinyerésekor: {e}{RESET}")
182
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  def crawl_and_index_website(start_url, max_depth, es_client, index_name):
185
- # Itt folytatódik a weboldal bejárásáért és indexeléséért felelős,
186
- # hosszú `crawl_and_index_website` függvény, ahogy korábban is volt.
187
- # A teljesség kedvéért ide másolom a korábban megadott kódodat.
188
- visited_urls = set()
189
- urls_to_visit = deque([(start_url, 0)])
190
- bulk_actions = []
191
- total_indexed = 0
192
  target_domain = urlparse(start_url).netloc
193
  print(f"Web crawling indítása: {start_url} (Max mélység: {max_depth}, Cél: {target_domain})")
194
 
195
  while urls_to_visit:
196
  current_url, current_depth = urls_to_visit.popleft()
197
- if current_url in visited_urls or current_depth > max_depth:
198
  continue
199
 
200
  print(f"\n--- Feldolgozás (Mélység: {current_depth}): {current_url} ---")
@@ -216,27 +298,20 @@ def crawl_and_index_website(start_url, max_depth, es_client, index_name):
216
  print(f" {YELLOW}-> Nem sikerült szöveget kinyerni vagy túl rövid.{RESET}")
217
  continue
218
 
219
- # A chunking, category, summary generálás függvények a korábbiak szerint
220
- # ... feltételezve, hogy azok léteznek és működnek ...
221
- final_chunks = [page_text] # Egyszerűsített példa, a te chunking logikád ide kerül
222
- url_category = "általános" # Egyszerűsített példa
223
- page_summary = page_text[:200] + "..." # Egyszerűsített példa
224
 
225
- print(f"{GREEN} Indexelésre előkészítve: {len(final_chunks)} darab (Kategória: {url_category}){RESET}")
226
 
227
- page_chunk_count = 0
228
  for chunk_text in final_chunks:
229
  element_vector = get_embedding(chunk_text)
230
  if element_vector:
231
- page_chunk_count += 1
232
  doc = {
233
- "text_content": chunk_text,
234
- "embedding": element_vector,
235
- "source_origin": "website",
236
- "source_url": current_url,
237
- "source_type": "token_chunking",
238
- "category": url_category,
239
- "summary": page_summary
240
  }
241
  bulk_actions.append({"_index": index_name, "_source": doc})
242
 
@@ -246,18 +321,11 @@ def crawl_and_index_website(start_url, max_depth, es_client, index_name):
246
  total_indexed += success_count
247
  bulk_actions = []
248
 
249
- print(f" Oldal feldolgozva, {page_chunk_count} chunk hozzáadva a kötegelt feltöltéshez.")
250
-
251
  if current_depth < max_depth:
252
- for a_tag in soup.find_all('a', href=True):
253
- href = a_tag['href'].strip()
254
- if href and not href.startswith(('#', 'mailto:', 'javascript:')):
255
- full_url = urljoin(base_url, href)
256
- parsed_url = urlparse(full_url)
257
- if parsed_url.scheme in ['http', 'https'] and parsed_url.netloc == target_domain:
258
- link = parsed_url._replace(fragment="").geturl()
259
- if link not in visited_urls:
260
- urls_to_visit.append((link, current_depth + 1))
261
 
262
  time.sleep(REQUEST_DELAY)
263
 
@@ -278,7 +346,7 @@ def crawl_and_index_website(start_url, max_depth, es_client, index_name):
278
 
279
  # === Fő Program ===
280
  if __name__ == "__main__":
281
- print("----- Web Crawler és Indexelő Indítása -----")
282
 
283
  load_embedding_model()
284
 
@@ -290,31 +358,27 @@ if __name__ == "__main__":
290
 
291
  if es_client:
292
  try:
293
- # Tiszta kezdés: töröljük a régi indexet, ha létezik
294
  if es_client.indices.exists(index=VECTOR_INDEX_NAME):
295
  print(f"{YELLOW}A '{VECTOR_INDEX_NAME}' index már létezik. Törlés...{RESET}")
296
  es_client.indices.delete(index=VECTOR_INDEX_NAME)
297
  print(f"{GREEN}Index sikeresen törölve.{RESET}")
298
 
299
- # Index létrehozása a javított beállításokkal
300
- print(f"'{VECTOR_INDEX_NAME}' index létrehozása a javított beállításokkal...")
301
- es_client.indices.create(
302
- index=VECTOR_INDEX_NAME,
303
- settings=INDEX_SETTINGS_SEPARATE_ANALYZER,
304
- mappings=INDEX_MAPPINGS_WEB
305
  )
306
- print(f"{GREEN}Index sikeresen létrehozva.{RESET}")
307
-
308
- # Crawling és indexelés indítása
309
- final_success_count = crawl_and_index_website(START_URL, MAX_DEPTH, es_client, VECTOR_INDEX_NAME)
310
-
311
- if final_success_count > 0:
312
- print(f"\n{GREEN}A folyamat sikeresen lefutott. {final_success_count} dokumentum indexelve.{RESET}")
313
  else:
314
- print(f"\n{YELLOW}A folyamat lefutott, de 0 új dokumentum került indexelésre.{RESET}")
315
-
316
  except Exception as e:
317
  print(f"{RED}Hiba a fő programrészben: {e}{RESET}")
318
- traceback.print_exc()
319
  else:
320
- print(f"{RED}Hiba: Az Elasticsearch kliens nem elérhető. A program leáll.{RESET}")
 
1
  # web_indexer_universal_v7.py
2
+ # VÉGLEGES VERZIÓ 2.0: Szinonimák nélkül, dinamikus AI kategorizálással.
3
 
4
  import os
5
  import time
 
24
  TORCH_AVAILABLE = True
25
  except ImportError:
26
  TORCH_AVAILABLE = False
 
27
 
28
  try:
29
  import together
 
84
  MIN_CHUNK_SIZE_CHARS = 50
85
  LLM_CHUNK_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
86
 
87
+ # === Index Beállítások & Mapping (EGYSZERŰSÍTETT, SZINONIMÁK NÉLKÜL) ===
88
+ INDEX_SETTINGS_SIMPLE = {
89
  "analysis": {
90
  "filter": {
91
  "hungarian_stop": {"type": "stop", "stopwords": "_hungarian_"},
92
+ "hungarian_stemmer": {"type": "stemmer", "language": "hungarian"}
 
 
 
 
93
  },
94
  "analyzer": {
95
+ "hungarian_analyzer": {
96
  "tokenizer": "standard",
97
  "filter": ["lowercase", "hungarian_stop", "hungarian_stemmer"]
 
 
 
 
98
  }
99
  }
100
  }
101
  }
102
 
103
+ INDEX_MAPPINGS_SIMPLE = {
104
  "properties": {
105
+ "text_content": {"type": "text", "analyzer": "hungarian_analyzer"},
106
  "embedding": {"type": "dense_vector", "dims": EMBEDDING_DIM, "index": True, "similarity": "cosine"},
107
  "source_origin": {"type": "keyword"},
108
  "source_url": {"type": "keyword"},
109
  "source_type": {"type": "keyword"},
110
+ "category": {"type": "keyword"}, # A 'keyword' típus listákat is tud kezelni
111
+ "heading": {"type": "text", "analyzer": "hungarian_analyzer"},
112
+ "summary": {"type": "text", "analyzer": "hungarian_analyzer"}
113
  }
114
  }
115
 
 
116
  # --- Segédfüggvények ---
117
  def initialize_es_client():
118
  print(f"\n{CYAN}Kapcsolódás az Elasticsearch-hez...{RESET}")
 
125
  api_key=ES_API_KEY,
126
  request_timeout=ES_CLIENT_TIMEOUT
127
  )
128
+ if not client.ping(): raise ConnectionError("Ping sikertelen.")
 
129
  print(f"{GREEN}Sikeres Elasticsearch kapcsolat!{RESET}")
130
  return client
131
  except Exception as e:
 
144
  model = SentenceTransformer(EMBEDDING_MODEL_NAME, device=device)
145
  embedding_model = model
146
  EMBEDDING_DIM = model.get_sentence_embedding_dimension()
147
+ INDEX_MAPPINGS_SIMPLE["properties"]["embedding"]["dims"] = EMBEDDING_DIM
148
  print(f"{GREEN}Embedding modell betöltve (dim: {EMBEDDING_DIM}, eszköz: {device}).{RESET}")
149
  except Exception as e:
150
  print(f"{RED}Hiba az embedding modell betöltésekor: {e}{RESET}")
151
  embedding_model = None
152
 
153
+ def generate_dynamic_categories_with_llm(llm_client, soup, text):
154
+ if not llm_client: return ["általános"]
155
+
156
+ h1_text = ""
157
+ try:
158
+ h1_tag = soup.find('h1')
159
+ if h1_tag:
160
+ h1_text = h1_tag.get_text(strip=True)
161
+ except Exception:
162
+ pass
163
+
164
+ try:
165
+ prompt = f"""Elemezd a következő magyar nyelvű weboldal tartalmát, és adj meg 1-3 rövid, releváns kategóriát vagy címkét, ami a legjobban leírja azt. A kategóriákat vesszővel válaszd el. A válaszodban csak a kategóriák szerepeljenek, más magyarázat nélkül.
166
+ Weboldal címe: "{h1_text}"
167
+ Szöveg eleje: {text[:1500]}
168
+ Kategóriák:"""
169
+
170
+ response = llm_client.chat.completions.create(
171
+ model=LLM_CHUNK_MODEL,
172
+ messages=[{"role": "user", "content": prompt}],
173
+ temperature=0.2,
174
+ max_tokens=50
175
+ )
176
+
177
+ if response and response.choices:
178
+ categories_str = response.choices[0].message.content.strip()
179
+ # A válasz feldolgozása: vessző mentén darabolás, felesleges szóközök eltávolítása, kisbetűsítés
180
+ categories = [cat.strip().lower() for cat in categories_str.split(',') if cat.strip()]
181
+ print(f"{GREEN} -> Dinamikus kategóriák az AI alapján: {categories}{RESET}")
182
+ return categories if categories else ["általános"]
183
+ return ["általános"]
184
+ except Exception as e:
185
+ print(f"{RED}Hiba a dinamikus LLM kategorizáláskor: {e}{RESET}")
186
+ return ["általános"]
187
+
188
+ def generate_summary_with_llm(llm_client, text):
189
+ # Ez a függvény változatlan
190
+ if not llm_client: return text[:300] + "..."
191
+ try:
192
+ prompt = f"""Készíts egy rövid, de informatív összefoglalót a következő szövegről magyarul.
193
+ Szöveg: {text[:4000]}
194
+ Összefoglalás:"""
195
+ response = llm_client.chat.completions.create(model=LLM_CHUNK_MODEL, messages=[{"role": "user", "content": prompt}], temperature=0.5, max_tokens=500)
196
+ if response and response.choices:
197
+ summary = response.choices[0].message.content.strip()
198
+ print(f"{GREEN} -> Sikeres LLM összefoglalás generálás.{RESET}")
199
+ return summary
200
+ except Exception as e:
201
+ print(f"{RED}Hiba LLM összefoglaláskor: {e}{RESET}")
202
+ return text[:300] + "..."
203
+
204
+ def chunk_text_by_tokens(text, chunk_size, chunk_overlap):
205
+ # Ez a függvény változatlan
206
+ if not TIKTOKEN_AVAILABLE:
207
+ chunks, start = [], 0
208
+ while start < len(text):
209
+ end = start + (chunk_size * 4)
210
+ chunks.append(text[start:end])
211
+ start = end - (chunk_overlap * 4)
212
+ return chunks
213
+ tokens = tiktoken_encoder.encode(text)
214
+ chunks, start = [], 0
215
+ while start < len(tokens):
216
+ end = start + chunk_size
217
+ chunk_tokens = tokens[start:end]
218
+ chunks.append(tiktoken_encoder.decode(chunk_tokens))
219
+ start += chunk_size - chunk_overlap
220
+ return chunks
221
+
222
  def get_embedding(text):
223
+ # Ez a függvény változatlan
224
  if not embedding_model: return None
225
  try:
226
  return embedding_model.encode(text, normalize_embeddings=True).tolist()
 
228
  print(f"{RED}Hiba embedding közben: {e}{RESET}")
229
  return None
230
 
231
+ def create_es_index(client, index_name, index_settings, index_mappings):
232
+ # Ez a függvény változatlan
233
+ print(f"\n{CYAN}Index ellenőrzése: '{index_name}'...{RESET}")
234
+ try:
235
+ if not client.indices.exists(index=index_name):
236
+ print(f"'{index_name}' index létrehozása...")
237
+ client.indices.create(index=index_name, settings=index_settings, mappings=index_mappings)
238
+ print(f"{GREEN}Index sikeresen létrehozva.{RESET}")
239
+ else:
240
+ print(f"Index '{index_name}' már létezik.")
241
+ return True
242
+ except Exception as e:
243
+ print(f"{RED}!!! Hiba az index létrehozásakor: {e}{RESET}")
244
+ return False
245
+
246
  def extract_text_from_html(html_content):
247
+ # Ez a függvény változatlan
248
  try:
249
  soup = BeautifulSoup(html_content, 'html.parser')
250
  for element in soup(["script", "style", "nav", "footer", "header", "aside", "form"]):
 
254
  return "\n".join(line for line in text.splitlines() if line.strip())
255
  except Exception as e:
256
  print(f"{RED}Hiba a HTML tartalom kinyerésekor: {e}{RESET}")
257
+ return ""
258
+
259
+ def extract_and_filter_links(soup, base_url, target_domain):
260
+ # Ez a függvény változatlan
261
+ links = set()
262
+ for a_tag in soup.find_all('a', href=True):
263
+ href = a_tag['href'].strip()
264
+ if href and not href.startswith(('#', 'mailto:', 'javascript:')):
265
+ full_url = urljoin(base_url, href)
266
+ parsed_url = urlparse(full_url)
267
+ if parsed_url.scheme in ['http', 'https'] and parsed_url.netloc == target_domain:
268
+ links.add(parsed_url._replace(fragment="").geturl())
269
+ return links
270
 
271
  def crawl_and_index_website(start_url, max_depth, es_client, index_name):
272
+ visited_urls, urls_to_visit = set(), deque([(start_url, 0)])
273
+ bulk_actions, total_indexed = [], 0
 
 
 
 
 
274
  target_domain = urlparse(start_url).netloc
275
  print(f"Web crawling indítása: {start_url} (Max mélység: {max_depth}, Cél: {target_domain})")
276
 
277
  while urls_to_visit:
278
  current_url, current_depth = urls_to_visit.popleft()
279
+ if current_url in visited_urls:
280
  continue
281
 
282
  print(f"\n--- Feldolgozás (Mélység: {current_depth}): {current_url} ---")
 
298
  print(f" {YELLOW}-> Nem sikerült szöveget kinyerni vagy túl rövid.{RESET}")
299
  continue
300
 
301
+ final_chunks = chunk_text_by_tokens(page_text, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS)
302
+ # JAVÍTVA: Az új, dinamikus kategória generáló függvény hívása
303
+ categories = generate_dynamic_categories_with_llm(together_client, soup, page_text)
304
+ page_summary = generate_summary_with_llm(together_client, page_text)
 
305
 
306
+ print(f"{GREEN} Indexelésre előkészítve: {len(final_chunks)} darab (Kategóriák: {categories}){RESET}")
307
 
 
308
  for chunk_text in final_chunks:
309
  element_vector = get_embedding(chunk_text)
310
  if element_vector:
 
311
  doc = {
312
+ "text_content": chunk_text, "embedding": element_vector, "source_origin": "website",
313
+ "source_url": current_url, "source_type": "token_chunking",
314
+ "category": categories, "summary": page_summary
 
 
 
 
315
  }
316
  bulk_actions.append({"_index": index_name, "_source": doc})
317
 
 
321
  total_indexed += success_count
322
  bulk_actions = []
323
 
 
 
324
  if current_depth < max_depth:
325
+ new_links = extract_and_filter_links(soup, current_url, target_domain)
326
+ for link in new_links:
327
+ if link not in visited_urls:
328
+ urls_to_visit.append((link, current_depth + 1))
 
 
 
 
 
329
 
330
  time.sleep(REQUEST_DELAY)
331
 
 
346
 
347
  # === Fő Program ===
348
  if __name__ == "__main__":
349
+ print("----- Web Crawler és Indexelő Indítása (Dinamikus AI Kategorizálással) -----")
350
 
351
  load_embedding_model()
352
 
 
358
 
359
  if es_client:
360
  try:
 
361
  if es_client.indices.exists(index=VECTOR_INDEX_NAME):
362
  print(f"{YELLOW}A '{VECTOR_INDEX_NAME}' index már létezik. Törlés...{RESET}")
363
  es_client.indices.delete(index=VECTOR_INDEX_NAME)
364
  print(f"{GREEN}Index sikeresen törölve.{RESET}")
365
 
366
+ index_ready = create_es_index(
367
+ client=es_client,
368
+ index_name=VECTOR_INDEX_NAME,
369
+ index_settings=INDEX_SETTINGS_SIMPLE,
370
+ index_mappings=INDEX_MAPPINGS_SIMPLE
 
371
  )
372
+
373
+ if index_ready:
374
+ final_success_count = crawl_and_index_website(START_URL, MAX_DEPTH, es_client, VECTOR_INDEX_NAME)
375
+ if final_success_count > 0:
376
+ print(f"\n{GREEN}A folyamat sikeresen lefutott. {final_success_count} dokumentum indexelve.{RESET}")
377
+ else:
378
+ print(f"\n{YELLOW}A folyamat lefutott, de 0 új dokumentum került indexelésre.{RESET}")
379
  else:
380
+ print(f"{RED}Hiba: Az index nem áll készen a használatra.{RESET}")
 
381
  except Exception as e:
382
  print(f"{RED}Hiba a fő programrészben: {e}{RESET}")
 
383
  else:
384
+ print(f"{RED}Hiba: Az Elasticsearch kliens nem elérhető.{RESET}")