Király Zoltán commited on
Commit
051c4a2
·
1 Parent(s): 144d9e0
.github/workflows/run-indexer.yml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # .github/workflows/run-indexer.yml
2
+ name: "Weboldal Indexelő Futtatása"
3
+
4
+ on:
5
+ workflow_dispatch: # Manuális indítás engedélyezése
6
+
7
+ jobs:
8
+ build-and-index:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ # 1. Kód letöltése a repositoryból
12
+ - name: "Repository letöltése"
13
+ uses: actions/checkout@v4
14
+
15
+ # 2. Python környezet beállítása
16
+ - name: "Python 3.10 beállítása"
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version: '3.10'
20
+
21
+ # 3. Python függőségek telepítése
22
+ - name: "Függőségek telepítése"
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install -r requirements.txt
26
+
27
+ # 4. Az indexelő script futtatása
28
+ - name: "Indexer script futtatása"
29
+ # Itt adjuk át a titkokat a Python scriptnek környezeti változókként
30
+ run: python web_indexer_universal_v7.py
31
+ env:
32
+ ES_CLOUD_ID: ${{ secrets.ES_CLOUD_ID }}
33
+ ES_API_KEY: ${{ secrets.ES_API_KEY }}
34
+ TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
web_indexer_universal_v7.py ADDED
@@ -0,0 +1,615 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # web_indexer_universal_v7.py
2
+ # JAVÍTVA: A hitelesítő adatok és a konfiguráció a GitHub Actions Secrets-ből érkeznek.
3
+ # Robusztusabb logikával, a backendből importált funkciók helyett.
4
+
5
+ import os
6
+ import time
7
+ import traceback
8
+ import requests
9
+ from bs4 import BeautifulSoup
10
+ from urllib.parse import urljoin, urlparse
11
+ from collections import deque
12
+ from elasticsearch import Elasticsearch, helpers, exceptions as es_exceptions
13
+ import sys
14
+ import warnings
15
+
16
+ # === ANSI Színkódok (konzol loggoláshoz) ===
17
+ GREEN = '\033[92m'
18
+ YELLOW = '\033[93m'
19
+ RED = '\033[91m'
20
+ RESET = '\033[0m'
21
+ BLUE = '\033[94m'
22
+ CYAN = '\033[96m'
23
+ MAGENTA = '\033[95m'
24
+
25
+ # --- LLM és egyéb könyvtárak ellenőrzése és importálása ---
26
+ try:
27
+ import torch
28
+ TORCH_AVAILABLE = True
29
+ except ImportError:
30
+ TORCH_AVAILABLE = False
31
+ print(f"{RED}FIGYELEM: Torch nincs telepítve. Egyes funkciók nem működnek.{RESET}")
32
+
33
+ try:
34
+ import together
35
+ from dotenv import load_dotenv
36
+
37
+ load_dotenv()
38
+ # <<< JAVÍTVA: A together_api_key betöltése a környezeti változókból
39
+ together_api_key = os.getenv("TOGETHER_API_KEY")
40
+ if not together_api_key:
41
+ print(f"{RED}Hiba: TOGETHER_API_KEY környezeti változó nincs beállítva. LLM funkciók nem működnek.{RESET}")
42
+ together_client = None
43
+ else:
44
+ together_client = together.Together(api_key=together_api_key)
45
+ print(f"{GREEN}Together AI kliens inicializálva.{RESET}")
46
+ except ImportError:
47
+ print(f"{YELLOW}Figyelem: together könyvtár nincs telepítve. LLM funkciók nem fognak működni.{RESET}")
48
+ together_client = None
49
+ except Exception as e:
50
+ print(f"{RED}Hiba LLM backend inicializálásakor: {e}{RESET}")
51
+ together_client = None
52
+
53
+ # ... (a többi import változatlan)
54
+ try:
55
+ import tiktoken
56
+ tiktoken_encoder = tiktoken.get_encoding("cl100k_base")
57
+ TIKTOKEN_AVAILABLE = True
58
+ except ImportError:
59
+ TIKTOKEN_AVAILABLE = False
60
+ print(f"{YELLOW}Figyelem: tiktoken nincs telepítve. Token darabolás a beállított karakterszámmal történik.{RESET}")
61
+
62
+ try:
63
+ import nltk
64
+ try:
65
+ nltk.data.find('tokenizers/punkt')
66
+ except LookupError:
67
+ print(f"{CYAN}NLTK 'punkt' letöltése...{RESET}");
68
+ nltk.download('punkt', quiet=True)
69
+ NLTK_AVAILABLE = True
70
+ except ImportError:
71
+ NLTK_AVAILABLE = False
72
+ print(f"{RED}HIBA: 'nltk' nincs telepítve! Szövegtördelés nem lesz pontos.{RESET}")
73
+
74
+ try:
75
+ from sentence_transformers import SentenceTransformer
76
+ SENTENCE_TRANSFORMER_AVAILABLE = True
77
+ except ImportError:
78
+ SENTENCE_TRANSFORMER_AVAILABLE = False
79
+ print(f"{RED}HIBA: 'sentence-transformers' nincs telepítve! Embedding nem működik.{RESET}")
80
+
81
+ try:
82
+ sys.stdout.reconfigure(encoding='utf-8')
83
+ sys.stderr.reconfigure(encoding='utf-8')
84
+ except AttributeError:
85
+ print(f"{YELLOW}Figyelem: Kódolás beállítása nem sikerült.{RESET}")
86
+
87
+ # --- Konfiguráció ---
88
+ # <<< JAVÍTVA: A hitelesítő adatok betöltése környezeti változókból
89
+ # Ezeket a GitHub Actions Secrets-ben kell beállítanod!
90
+ ES_CLOUD_ID = os.getenv("ES_CLOUD_ID")
91
+ ES_API_KEY = os.getenv("ES_API_KEY")
92
+ # A TOGETHER_API_KEY már korábban betöltésre került
93
+
94
+ START_URL = "https://www.dunaelektronika.com/"
95
+ TARGET_DOMAIN = "dunaelektronika.com"
96
+ MAX_DEPTH = 2
97
+ REQUEST_DELAY = 1
98
+ USER_AGENT = "MyPythonCrawler/1.0 (+http://example.com/botinfo)"
99
+ VECTOR_INDEX_NAME = "dunawebindexai"
100
+ SYNONYM_FILE_PATH_IN_ES_CONFIG = "analysis/synonyms_hu.txt"
101
+ BATCH_SIZE = 50
102
+ ES_CLIENT_TIMEOUT = 120
103
+ EMBEDDING_MODEL_NAME = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
104
+ embedding_model = None
105
+ EMBEDDING_DIM = None
106
+ device = 'cpu'
107
+ CHUNK_SIZE_TOKENS = 500
108
+ CHUNK_OVERLAP_TOKENS = 50
109
+ MIN_CHUNK_SIZE_CHARS = 50
110
+ DEBUG_MODE = True
111
+ LLM_MODEL_NAME = "meta-llama/Llama-3.3-70B-Instruct-Turbo-Free"
112
+ LLM_CHUNK_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
113
+
114
+
115
+ # --- LLM HÁTTÉR FUNKCIÓK ---
116
+ # ... (ez a rész változatlan)
117
+ def generate_categories_with_llm(llm_client, soup, text):
118
+ """
119
+ Kategóriát generál HTML menüből/címből, vagy LLM-mel, ha az előző nem sikerül.
120
+ """
121
+ # Előre definiált kategórialista, hogy az LLM mindig pontosan egyező kategóriát adjon.
122
+ category_list = ['IT biztonsági szolgáltatások', 'szolgáltatások', 'hardver', 'szoftver', 'hírek',
123
+ 'audiovizuális konferenciatechnika']
124
+
125
+ # 1. Keresés HTML menüpontok/útvonalak alapján (breadcrumb)
126
+ try:
127
+ breadcrumb = soup.find('nav', class_='breadcrumb')
128
+ if breadcrumb:
129
+ categories = [li.get_text(strip=True) for li in breadcrumb.find_all('li')]
130
+ if categories:
131
+ final_category_from_html = categories[-1]
132
+ # Ezt a kategóriát is egyeztesse a listával
133
+ for cat in category_list:
134
+ if cat.lower() in final_category_from_html.lower():
135
+ print(f"{GREEN} -> Kategória a breadcrumb alapján: '{cat}'{RESET}")
136
+ return [cat]
137
+ except Exception as e:
138
+ print(f"{YELLOW} Figyelem: Hiba a breadcrumb feldolgozásakor: {e}{RESET}")
139
+
140
+ # 2. Keresés <h1> cím alapján
141
+ try:
142
+ h1_tag = soup.find('h1')
143
+ if h1_tag and h1_tag.get_text(strip=True):
144
+ h1_text = h1_tag.get_text(strip=True)
145
+ for cat in category_list:
146
+ if cat.lower() in h1_text.lower():
147
+ print(f"{GREEN} -> Kategória a H1 cím alapján: '{cat}'{RESET}")
148
+ return [cat]
149
+ except Exception as e:
150
+ print(f"{YELLOW} Figyelem: Hiba a H1 cím feldolgozásakor: {e}{RESET}")
151
+
152
+ # 3. LLM hívás, ha semmi más nem működik
153
+ if not llm_client: return ['egyéb']
154
+ try:
155
+ categories_text = ", ".join([f"'{cat}'" for cat in category_list])
156
+ prompt = f"""Adott egy weboldal szövege. Adj meg egyetlen, rövid kategóriát a következő listából, ami a legjobban jellemzi a tartalmát. A válaszodban csak a kategória szerepeljen, más szöveg, magyarázat, vagy írásjelek nélkül.
157
+ Lehetséges kategóriák: {categories_text}
158
+ Szöveg: {text[:1000]}
159
+ Kategória:"""
160
+ response = llm_client.chat.completions.create(model=LLM_CHUNK_MODEL,
161
+ messages=[{"role": "user", "content": prompt}], temperature=0.1,
162
+ max_tokens=30)
163
+ if response and response.choices:
164
+ category = response.choices[0].message.content.strip().replace("'", "").replace("`", "")
165
+ for cat in category_list:
166
+ if cat.lower() in category.lower():
167
+ print(f"{GREEN} -> Kategória LLM generálás alapján: '{cat}'{RESET}")
168
+ return [cat]
169
+ print(
170
+ f"{YELLOW} -> Az LLM nem talált megfelelő kategóriát a listán. 'egyéb' kategória használata.{RESET}")
171
+ return ['egyéb']
172
+ else:
173
+ return ["egyéb"]
174
+ except Exception as e:
175
+ print(f"{RED}Hiba LLM kategorizáláskor: {e}{RESET}")
176
+ return ['egyéb']
177
+
178
+ def generate_summary_with_llm(llm_client, text):
179
+ """
180
+ Összefoglalást generál a szöveg első feléből egy LLM segítségével.
181
+ """
182
+ if not llm_client: return text[:300] + "..."
183
+ try:
184
+ prompt = f"""Készíts egy rövid, de informatív összefoglalót a következő szövegről. A lényeges pontokat emeld ki, de ne lépd túl a 200 szó terjedelmet.
185
+ Szöveg: {text}
186
+ Összefoglalás:"""
187
+ # Csak a szöveg első 4000 tokenjét használjuk, hogy elkerüljük a token limitet
188
+ text_for_llm = text[:4000]
189
+ response = llm_client.chat.completions.create(model=LLM_CHUNK_MODEL,
190
+ messages=[{"role": "user", "content": prompt}], temperature=0.5,
191
+ max_tokens=500)
192
+ if response and response.choices:
193
+ summary = response.choices[0].message.content.strip()
194
+ print(f"{GREEN} -> Sikeres LLM összefoglalás generálás.{RESET}")
195
+ return summary
196
+ else:
197
+ return text[:300] + "..."
198
+ except Exception as e:
199
+ print(f"{RED}Hiba LLM összefoglaláskor: {e}{RESET}")
200
+ return text[:300] + "..." # Visszaesés a manuális csonkolásra hiba esetén
201
+
202
+ def chunk_text_by_tokens(text, chunk_size, chunk_overlap):
203
+ """
204
+ Szöveg feldarabolása tokenek szerint, átfedéssel.
205
+ """
206
+ if not TIKTOKEN_AVAILABLE or not NLTK_AVAILABLE:
207
+ # Fallback a karakterszám alapú darabolásra, ha a tokenizáló nincs telepítve
208
+ print(f"{YELLOW}Figyelmeztetés: Tiktoken/NLTK hiányzik. Karakterszám alapú darabolás.{RESET}")
209
+ # Egyszerű karakter alapú tördelés
210
+ chunks = []
211
+ start = 0
212
+ while start < len(text):
213
+ end = start + chunk_size
214
+ chunks.append(text[start:end])
215
+ start += chunk_size - chunk_overlap
216
+ return chunks
217
+
218
+ tokens = tiktoken_encoder.encode(text)
219
+ chunks = []
220
+ start = 0
221
+ while start < len(tokens):
222
+ end = start + chunk_size
223
+ chunk_tokens = tokens[start:end]
224
+ chunks.append(tiktoken_encoder.decode(chunk_tokens))
225
+ start += chunk_size - chunk_overlap
226
+ return chunks
227
+
228
+ # --- Modellek és Eszközök Inicializálása ---
229
+ # ... (ez a rész változatlan)
230
+ def load_embedding_model():
231
+ global embedding_model, EMBEDDING_DIM, device
232
+ if not TORCH_AVAILABLE or not SENTENCE_TRANSFORMER_AVAILABLE:
233
+ EMBEDDING_DIM = 768
234
+ device = 'cpu'
235
+ print(f"{RED}Hiba: PyTorch vagy SentenceTransformer nincs telepítve.{RESET}")
236
+ return None, EMBEDDING_DIM, device
237
+ if embedding_model and EMBEDDING_DIM:
238
+ return embedding_model, EMBEDDING_DIM, device
239
+ print(f"\n'{EMBEDDING_MODEL_NAME}' embedding modell betöltése (SentenceTransformer)...")
240
+ try:
241
+ current_device = 'cuda' if torch.cuda.is_available() else 'cpu'
242
+ model = SentenceTransformer(EMBEDDING_MODEL_NAME, device=current_device)
243
+ print(f"ST modell betöltve, használt eszköz: {model.device}")
244
+ dim = model.get_sentence_embedding_dimension()
245
+ if not dim: raise ValueError("Nem sikerült meghatározni az embedding dimenziót.")
246
+ embedding_model = model
247
+ EMBEDDING_DIM = dim
248
+ device = current_device
249
+ return embedding_model, EMBEDDING_DIM, device
250
+ except Exception as e:
251
+ print(f"{RED}Hiba embedding modell betöltésekor: {e}{RESET}")
252
+ traceback.print_exc()
253
+ embedding_model = None
254
+ EMBEDDING_DIM = 768
255
+ device = 'cpu'
256
+ return None, EMBEDDING_DIM, device
257
+
258
+ embedding_model, EMBEDDING_DIM, device = load_embedding_model()
259
+
260
+ # === Index Beállítások & Mapping ===
261
+ # ... (ez a rész változatlan)
262
+ INDEX_SETTINGS_SEPARATE_ANALYZER = {
263
+ "analysis": {
264
+ "filter": {
265
+ "hungarian_stop": {"type": "stop", "stopwords": "_hungarian_"},
266
+ "hungarian_stemmer": {"type": "stemmer", "language": "hungarian"},
267
+ "synonym_filter": {"type": "synonym_graph", "synonyms_path": SYNONYM_FILE_PATH_IN_ES_CONFIG,
268
+ "updateable": True}
269
+ },
270
+ "analyzer": {
271
+ "hungarian_indexing_analyzer": {"tokenizer": "standard",
272
+ "filter": ["lowercase", "hungarian_stop", "hungarian_stemmer"]},
273
+ "hungarian_search_analyzer": {"tokenizer": "standard",
274
+ "filter": ["lowercase", "hungarian_stop", "synonym_filter",
275
+ "hungarian_stemmer"]}
276
+ }
277
+ }
278
+ }
279
+
280
+ INDEX_MAPPINGS_WEB = {
281
+ "properties": {
282
+ "text_content": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
283
+ "search_analyzer": "hungarian_search_analyzer"},
284
+ "embedding": {"type": "dense_vector", "dims": EMBEDDING_DIM, "index": True, "similarity": "cosine"},
285
+ "source_origin": {"type": "keyword"},
286
+ "source_url": {"type": "keyword"},
287
+ "source_type": {"type": "keyword"},
288
+ "category": {"type": "keyword"},
289
+ "heading": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
290
+ "search_analyzer": "hungarian_search_analyzer"},
291
+ "summary": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
292
+ "search_analyzer": "hungarian_search_analyzer"}
293
+ }
294
+ }
295
+
296
+
297
+ # --- Segédfüggvények ---
298
+ # <<< JAVÍTVA: A függvény most már a környezeti változókat használja
299
+ def initialize_es_client():
300
+ if DEBUG_MODE: print("\nKapcsolódás az Elasticsearch-hez a GitHub Secrets adatokkal...")
301
+
302
+ # Ellenőrizzük, hogy a szükséges környezeti változók be vannak-e állítva
303
+ if not ES_CLOUD_ID:
304
+ print(f"{RED}Hiba: ES_CLOUD_ID környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
305
+ return None
306
+ if not ES_API_KEY:
307
+ print(f"{RED}Hiba: ES_API_KEY környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
308
+ return None
309
+
310
+ client = None
311
+ try:
312
+ # A kliens inicializálása cloud_id és api_key segítségével
313
+ client = Elasticsearch(
314
+ cloud_id=ES_CLOUD_ID,
315
+ api_key=ES_API_KEY,
316
+ request_timeout=ES_CLIENT_TIMEOUT
317
+ )
318
+ if not client.ping():
319
+ raise ConnectionError("Nem sikerült pingelni az Elasticsearch-t.")
320
+ if DEBUG_MODE: print(f"{GREEN}Sikeres Elasticsearch kapcsolat!{RESET}")
321
+ return client
322
+ except Exception as e:
323
+ print(f"{RED}Hiba az Elasticsearch kapcsolódás során: {e}{RESET}")
324
+ traceback.print_exc()
325
+ return None
326
+
327
+ # ... (a többi segédfüggvény változatlan)
328
+ def get_embedding(text):
329
+ if not embedding_model: return None
330
+ if not text or not isinstance(text, str): return None
331
+ try:
332
+ vector = embedding_model.encode(text, normalize_embeddings=True)
333
+ return vector.tolist()
334
+ except Exception as e:
335
+ print(f"{RED}Hiba embedding közben: {e}{RESET}")
336
+ return None
337
+
338
+
339
+ def create_es_index(client, index_name, index_settings, index_mappings):
340
+ if not EMBEDDING_DIM:
341
+ print(f"{RED}Hiba: Embed dim nincs.{RESET}")
342
+ return False
343
+ try:
344
+ embedding_mapping = index_mappings.get("properties", {}).get("embedding", {})
345
+ if not embedding_mapping: raise KeyError("Az 'embedding' kulcs hiányzik a mapping 'properties'-ből!")
346
+ if embedding_mapping.get("dims") != EMBEDDING_DIM:
347
+ print(f"{YELLOW}FIGYELEM: Mapping dim != Modell dim. Mapping frissítése {EMBEDDING_DIM}-re.{RESET}")
348
+ index_mappings["properties"]["embedding"]["dims"] = EMBEDDING_DIM
349
+ except KeyError as e:
350
+ print(f"{RED}Hiba: Mapping struktúra érvénytelen! Kulcs: {e}{RESET}")
351
+ return False
352
+ except Exception as e:
353
+ print(f"{RED}Hiba: Mapping hiba! {e}{RESET}")
354
+ return False
355
+ if DEBUG_MODE: print(f"\nIndex check: '{index_name}'?")
356
+ try:
357
+ if not client.indices.exists(index=index_name):
358
+ print(f"'{index_name}' létrehozása...")
359
+ resp = client.indices.create(index=index_name, settings=index_settings, mappings=index_mappings,
360
+ request_timeout=ES_CLIENT_TIMEOUT)
361
+ if resp.get('acknowledged'):
362
+ print(f"{GREEN}Index OK.{RESET}")
363
+ time.sleep(2)
364
+ return True
365
+ else:
366
+ print(f"{RED}Hiba: Create no ack.{RESET}")
367
+ return False
368
+ else:
369
+ print(f"Index '{index_name}' már létezik.")
370
+ return True
371
+ except es_exceptions.RequestError as e:
372
+ err_str = str(e).lower()
373
+ if 'resource_already_exists_exception' in err_str:
374
+ if DEBUG_MODE: print("Index már létezik (exception).")
375
+ return True
376
+ elif 'resource_not_found_exception' in err_str and ('synonyms_path' in err_str or (
377
+ SYNONYM_FILE_PATH_IN_ES_CONFIG and SYNONYM_FILE_PATH_IN_ES_CONFIG.split('/')[-1] in err_str)):
378
+ print(f"{RED}!!! Hiba: Szinonima fájl nincs ES-ben: '{SYNONYM_FILE_PATH_IN_ES_CONFIG}'?{RESET}")
379
+ else:
380
+ print(f"{RED}!!! Hiba index create (RequestError): {e}{RESET}")
381
+ return False
382
+ except Exception as e:
383
+ print(f"{RED}!!! Hiba index check/create: {e}{RESET}")
384
+ traceback.print_exc()
385
+ return False
386
+
387
+ def extract_text_from_html(html_content):
388
+ try:
389
+ soup = BeautifulSoup(html_content, 'html.parser')
390
+ for element in soup(["script", "style", "nav", "footer", "header", "aside", "form"]):
391
+ if element: element.decompose()
392
+ main_content = soup.find('main') or soup.find('article') or soup.body
393
+ if main_content:
394
+ text = main_content.get_text(separator='\n', strip=True)
395
+ cleaned_text = "\n".join(line for line in text.splitlines() if line.strip())
396
+ return cleaned_text
397
+ else:
398
+ return ""
399
+ except Exception as e:
400
+ print(f"{RED}Hiba HTML elemzés: {e}{RESET}")
401
+ return ""
402
+
403
+
404
+ def extract_and_filter_links(soup, base_url, target_domain):
405
+ links = set()
406
+ try:
407
+ for a_tag in soup.find_all('a', href=True):
408
+ href = a_tag['href'].strip()
409
+ if href and not href.startswith(('#', 'mailto:', 'javascript:')):
410
+ full_url = urljoin(base_url, href)
411
+ parsed_url = urlparse(full_url)
412
+ if parsed_url.scheme in ['http', 'https'] and parsed_url.netloc == target_domain:
413
+ normalized_url = parsed_url._replace(fragment="").geturl()
414
+ links.add(normalized_url)
415
+ except Exception as e:
416
+ print(f"{RED}Hiba link kinyerés: {e}{RESET}")
417
+ return links
418
+
419
+ def crawl_and_index_website(start_url, max_depth, es_client, index_name):
420
+ if not es_client: print(f"{RED}Hiba: ES kliens nincs init.{RESET}"); return 0
421
+ if not embedding_model: print(f"{RED}Hiba: Embedding modell nincs init.{RESET}"); return 0
422
+ try:
423
+ import requests;
424
+ from bs4 import BeautifulSoup;
425
+ from urllib.parse import urljoin, \
426
+ urlparse;
427
+ from collections import deque
428
+ except ImportError:
429
+ print(f"{RED}Hiba: Crawling könyvtárak hiányoznak.{RESET}");
430
+ return 0
431
+
432
+ global together_client, BATCH_SIZE, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS, MIN_CHUNK_SIZE_CHARS
433
+
434
+ visited_urls = set()
435
+ urls_to_visit = deque([(start_url, 0)])
436
+ bulk_actions = []
437
+ total_prepared = 0;
438
+ total_indexed = 0
439
+ try:
440
+ target_domain = urlparse(start_url).netloc;
441
+ except Exception as url_err:
442
+ print(f"{RED}Hiba: Start URL feldolgozása ({start_url}): {url_err}{RESET}");
443
+ return 0
444
+ print(f"Web crawling indítása: {start_url} (Max mélység: {max_depth}, Cél: {target_domain})")
445
+
446
+ while urls_to_visit:
447
+ current_url = None
448
+ try:
449
+ current_url, current_depth = urls_to_visit.popleft()
450
+ try:
451
+ parsed_check = urlparse(current_url);
452
+ except Exception as parse_err:
453
+ print(f" {YELLOW}-> Hibás URL formátum, kihagyva: {current_url}{RESET}");
454
+ continue
455
+ if current_url in visited_urls: continue
456
+ if current_depth > max_depth: continue
457
+ print(f"\n--- Feldolgozás (Mélység: {current_depth}): {current_url} ---")
458
+ visited_urls.add(current_url)
459
+ html_content = None
460
+ try:
461
+ headers = {'User-Agent': USER_AGENT}
462
+ response = requests.get(current_url, headers=headers, timeout=15)
463
+ response.raise_for_status()
464
+ content_type = response.headers.get('content-type', '').lower()
465
+ if 'text/html' not in content_type: print(
466
+ f" {YELLOW}-> Nem HTML ({content_type}), kihagyva.{RESET}"); continue
467
+ html_content = response.content
468
+ except requests.exceptions.RequestException as req_err:
469
+ print(f" {RED}!!! Hiba letöltés: {req_err}{RESET}");
470
+ continue
471
+ except Exception as fetch_err:
472
+ print(f" {RED}!!! Váratlan hiba letöltés: {fetch_err}{RESET}");
473
+ continue
474
+
475
+ if DEBUG_MODE: print(" HTML elemzése és kategorizálása...")
476
+ soup = BeautifulSoup(html_content, 'html.parser')
477
+ page_text = extract_text_from_html(html_content)
478
+ if not page_text or len(page_text) < MIN_CHUNK_SIZE_CHARS:
479
+ print(f" {YELLOW}-> Nem sikerült szöveget kinyerni vagy túl rövid.{RESET}");
480
+ continue
481
+
482
+ # --- ÚJ, JAVÍTOTT LOGIKA ---
483
+ # 1. Szövegtördelés token alapján
484
+ final_chunks = chunk_text_by_tokens(page_text, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS)
485
+ chunk_type = "token_chunking"
486
+
487
+ # 2. LLM-alapú kategorizálás (az első 1000 karakter alapján)
488
+ url_category = generate_categories_with_llm(together_client, soup, page_text[:1000])[0]
489
+
490
+ # 3. LLM-alapú összefoglalás a teljes oldalhoz, ha van LLM kliens
491
+ page_summary = generate_summary_with_llm(together_client, page_text)
492
+
493
+ # --- INDEXELÉS ELŐKÉSZÍTÉSE ---
494
+ if final_chunks:
495
+ print(
496
+ f"{GREEN} Indexelendő chunkok: {len(final_chunks)} (Típus: {chunk_type}, Kategória: {url_category}){RESET}")
497
+ else:
498
+ print(f"{RED} Nincs indexelendő chunk ({chunk_type}).{RESET}");
499
+ continue
500
+
501
+ if DEBUG_MODE: print(f" Chunkok indexelésének előkészítése...")
502
+ page_chunk_count = 0
503
+ for chunk_text in final_chunks:
504
+ element_vector = get_embedding(chunk_text)
505
+ if element_vector:
506
+ total_prepared += 1;
507
+ page_chunk_count += 1
508
+ doc = {"text_content": chunk_text, "embedding": element_vector, "source_origin": "website",
509
+ "source_url": current_url, "source_type": chunk_type, "category": url_category,
510
+ "summary": page_summary}
511
+ bulk_actions.append({"_index": index_name, "_source": doc})
512
+ if len(bulk_actions) >= BATCH_SIZE:
513
+ if DEBUG_MODE: print(f" -> {len(bulk_actions)} web chunk indexelése (batch)...")
514
+ try:
515
+ success_count, errors = helpers.bulk(es_client, bulk_actions, raise_on_error=False,
516
+ request_timeout=ES_CLIENT_TIMEOUT)
517
+ total_indexed += success_count
518
+ if errors: print(f"{RED}!!! Hiba web bulk: {len(errors)} sikertelen.{RESET}")
519
+ except Exception as be:
520
+ print(f"{RED}!!! Váratlan web bulk hiba: {be}{RESET}")
521
+ finally:
522
+ bulk_actions = []
523
+ print(f" Oldal ({current_url}) feldolgozása kész ({page_chunk_count} chunk indexelve).")
524
+ if current_depth < max_depth:
525
+ if DEBUG_MODE: print(" Linkek keresése...")
526
+ try:
527
+ soup_for_links = BeautifulSoup(html_content, 'html.parser')
528
+ new_links = extract_and_filter_links(soup_for_links, current_url, target_domain)
529
+ if DEBUG_MODE: print(f" Talált {len(new_links)} új, belső linket.")
530
+ for link in new_links:
531
+ if link not in visited_urls:
532
+ if len(urls_to_visit) < 5000:
533
+ urls_to_visit.append((link, current_depth + 1))
534
+ else:
535
+ print(
536
+ f"{YELLOW}Figyelmeztetés: A bejárási sor túl hosszú, új link kihagyva: {link}{RESET}")
537
+ except Exception as link_err:
538
+ print(f"{RED}!!! Hiba linkek kinyerése: {link_err}{RESET}")
539
+ if DEBUG_MODE: print(f" Várakozás {REQUEST_DELAY} mp..."); time.sleep(REQUEST_DELAY)
540
+ except KeyboardInterrupt:
541
+ print("\nFolyamat megszakítva.");
542
+ break
543
+ except Exception as loop_err:
544
+ print(f"{RED}!!! Hiba ciklusban ({current_url}): {loop_err}{RESET}");
545
+ traceback.print_exc();
546
+ time.sleep(5)
547
+
548
+ if bulk_actions:
549
+ if DEBUG_MODE: print(f" -> Maradék {len(bulk_actions)} web chunk indexelése...")
550
+ try:
551
+ success_count, errors = helpers.bulk(es_client, bulk_actions, raise_on_error=False,
552
+ request_timeout=ES_CLIENT_TIMEOUT)
553
+ total_indexed += success_count
554
+ if errors: print(f"{RED}!!! Hiba maradék web bulk: {len(errors)} sikertelen.{RESET}")
555
+ except Exception as be:
556
+ print(f"{RED}!!! Maradék web bulk hiba: {be}{RESET}")
557
+
558
+ print(f"\n--- Web Crawling és Indexelés Befejezve ---")
559
+ print(f"Meglátogatott URL-ek: {len(visited_urls)}")
560
+ print(f"Előkészített chunk: {total_prepared}")
561
+ final_success = min(total_indexed, total_prepared)
562
+ print(f"Sikeresen indexelt chunk: {final_success}")
563
+ return final_success
564
+
565
+ # ... (a main függvény változatlan)
566
+ if __name__ == "__main__":
567
+ print(f"----- Web Crawler és Indexelő Indítása a '{VECTOR_INDEX_NAME}' indexbe (LLM-alapú) -----")
568
+ print(f"----- Cél URL: {START_URL} (Max mélység: {MAX_DEPTH}) -----")
569
+ print("****** FIGYELEM ******")
570
+ print(f"Ez a script létrehozza/használja a '{VECTOR_INDEX_NAME}' indexet.")
571
+ print(f"Győződj meg róla, hogy a szinonima beállítások az ES-ben léteznek ehhez az indexhez.")
572
+ print(f"{RED}Ha a '{VECTOR_INDEX_NAME}' index már létezik, TÖRÖLD manuálisan futtatás előtt!{RESET}")
573
+ print("SZÜKSÉGES KÖNYVTÁRAK: requests, beautifulsoup4, nltk, tiktoken, together stb.")
574
+ print("******------------******")
575
+
576
+ if not TORCH_AVAILABLE: print(f"{RED}Hiba: PyTorch.{RESET}"); exit(1)
577
+ if not SENTENCE_TRANSFORMER_AVAILABLE: print(f"{RED}Hiba: SentenceTransformer.{RESET}"); exit(1)
578
+ if not embedding_model: print(f"{RED}Hiba: Embedding modell.{RESET}"); exit(1)
579
+ if not EMBEDDING_DIM: print(f"{RED}Hiba: Embedding dim.{RESET}"); exit(1)
580
+ try:
581
+ import requests;
582
+ from bs4 import BeautifulSoup;
583
+ from urllib.parse import urljoin, \
584
+ urlparse;
585
+ from collections import deque
586
+ except ImportError:
587
+ print(f"{RED}Hiba: Crawling könyvtárak.{RESET}");
588
+ exit(1)
589
+
590
+ es_client = initialize_es_client()
591
+ final_success_count = 0
592
+ index_ready = False
593
+ if es_client:
594
+ index_ready = create_es_index(client=es_client, index_name=VECTOR_INDEX_NAME,
595
+ index_settings=INDEX_SETTINGS_SEPARATE_ANALYZER,
596
+ index_mappings=INDEX_MAPPINGS_WEB)
597
+ if index_ready:
598
+ print(f"\nIndex '{VECTOR_INDEX_NAME}' kész. Web crawling és indexelés indítása...")
599
+ final_success_count = crawl_and_index_website(START_URL, MAX_DEPTH, es_client, VECTOR_INDEX_NAME)
600
+ else:
601
+ print(f"{RED}Hiba: Index nem áll készen.{RESET}")
602
+ else:
603
+ print(f"{RED}Hiba: ES kliens nem elérhető.{RESET}")
604
+ print("\n----- Feldolgozás Befejezve -----")
605
+ if index_ready and final_success_count > 0:
606
+ print(
607
+ f"\n{GREEN}Crawling és indexelés sikeres. {final_success_count} chunk indexelve '{VECTOR_INDEX_NAME}'-be.{RESET}");
608
+ print(f"Ellenőrzés: GET /{VECTOR_INDEX_NAME}/_count");
609
+ print(f"\nFontos: A RAG scriptet módosítani kell, hogy '{VECTOR_INDEX_NAME}' indexben IS keressen.")
610
+ elif index_ready and final_success_count == 0:
611
+ print(f"{YELLOW}Crawling lefutott, de 0 chunk indexelve.{RESET}")
612
+ elif not index_ready:
613
+ print(f"{RED}Index nem jött létre.{RESET}")
614
+ else:
615
+ print(f"{RED}Folyamat hibával zárult.{RESET}")
Új Szöveges dokumentum.txt ADDED
File without changes