diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 4b333a16..eb5b91fe 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ -Version: v1.5.2 +Version: v1.5.3 Datum: {aktuelles Datum} Git-Überschrift (max. 100 Zeichen): -1.5.2: Ergänzung heuristischer E-Mail-Generierung und Spaltenanpassung im Contacts-Blatt +1.5.3: Optimierung Batch-Modus und Alignment-Demo Integration für robustes Update Git-Änderungsbeschreibung: @@ -37,7 +37,7 @@ except ImportError: # ==================== KONFIGURATION ==================== class Config: - VERSION = "v1.5.2" + VERSION = "v1.5.3" LANG = "de" CREDENTIALS_FILE = "service_account.json" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" @@ -532,148 +532,189 @@ class DataProcessor: # ==================== NEUE FUNKTION: process_verification_only ==================== def process_verification_only(): + """ + Überarbeiteter Batch‑Prozess (Modus 51): + - Alle zu verifizierenden Zeilen werden in Paketen der Größe Config.BATCH_SIZE (z. B. 10 Zeilen) gebündelt. + - Für jedes Batch wird ein aggregierter Prompt erstellt und an ChatGPT gesendet. + - Die aggregierte Antwort wird zeilenweise geparst und auf die jeweiligen Zeilen (Google Sheet) aktualisiert. + - Umfassende Log‑Ausgaben informieren über den aktuellen Batch, Zeilennummern und Token-Zahlen. + """ debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess...") - try: - rows_limit = int(input("Wie viele Zeilen sollen im Batch verarbeitet werden? ")) - except Exception: - rows_limit = Config.BATCH_SIZE + # Ermittelt alle Zeilen, die noch keine ChatGPT-Auswertung (in Spalte AO) haben gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) sh = gc.open_by_url(Config.SHEET_URL) main_sheet = sh.sheet1 data = main_sheet.get_all_values() - batch_entries = [] - row_indices = [] - # Sammle Einträge, bei denen noch keine ChatGPT-Auswertung erfolgt ist (z. B. leeres Feld in Spalte AO) + + batch_size = Config.BATCH_SIZE # z. B. 10, anpassbar in der Config + batches = [] + row_numbers = [] for i, row in enumerate(data[1:], start=2): + # Überspringe Zeilen, in denen in Spalte AO (Index 40) bereits ein Wert steht if len(row) > 40 and row[40].strip() != "": continue - # Hier wird angenommen, dass der relevante Vergleichswert (z. B. Wikipedia-Daten) in Spalte Y oder AO erwartet wird. - # Passen Sie die Bedingung ggf. an. + # Wir erwarten relevante Daten in mindestens 25 Spalten (anpassbar, je nachdem) if len(row) <= 25 or row[24].strip() == "": entry_text = ( f"Eintrag {i}:\n" - f"Firmenname: {row[1] if len(row) > 1 else ''}\n" - f"CRM-Beschreibung: {row[7] if len(row) > 7 else ''}\n" - f"Wikipedia-URL: {row[11] if len(row) > 11 and row[11].strip() not in ['', 'k.A.'] else 'k.A.'}\n" - f"Wiki-Absatz: {row[12] if len(row) > 12 else 'k.A.'}\n" - f"Wiki-Kategorien: {row[16] if len(row) > 16 else 'k.A.'}\n" + f"Firmenname: {row[1] if len(row)>1 else ''}\n" + f"CRM-Beschreibung: {row[7] if len(row)>7 else ''}\n" + f"Wikipedia-URL: {row[11] if len(row)>11 and row[11].strip() not in ['', 'k.A.'] else 'k.A.'}\n" + f"Wiki-Absatz: {row[12] if len(row)>12 else 'k.A.'}\n" + f"Wiki-Kategorien: {row[16] if len(row)>16 else 'k.A.'}\n" "-----\n" ) - batch_entries.append(entry_text) - row_indices.append(i) - if len(batch_entries) == rows_limit: - break - if not batch_entries: - debug_print("Keine Einträge für die Verifizierung gefunden.") - return - aggregated_prompt = ( - "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " - "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " - "Gib das Ergebnis für jeden Eintrag im Format aus:\n" - "Eintrag : \n" - "Regeln:\n" - "- Bei Übereinstimmung: 'OK'\n" - "- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: | X | '\n" - "- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n" - ) - aggregated_prompt += "\n".join(batch_entries) - debug_print("Aggregierter Prompt für Verifizierungs-Batch erstellt.") - agg_token_count = "n.v." - if tiktoken: - try: - enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) - agg_token_count = len(enc.encode(aggregated_prompt)) - debug_print(f"Token-Zahl für Batch: {agg_token_count}") - except Exception as e: - debug_print(f"Fehler beim Token-Counting: {e}") - try: - with open("api_key.txt", "r") as f: - api_key = f.read().strip() - except Exception as e: - debug_print(f"Fehler beim Lesen des API-Tokens (Verifizierung): {e}") - return - openai.api_key = api_key - try: - response = openai.ChatCompletion.create( - model=Config.TOKEN_MODEL, - messages=[{"role": "system", "content": aggregated_prompt}], - temperature=0.0 + batches.append(entry_text) + row_numbers.append(i) + # Wenn wir die Batchgröße erreicht haben, verarbeite diesen Batch + if len(batches) == batch_size: + aggregated_prompt = ( + "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " + "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " + "Gib das Ergebnis für jeden Eintrag im Format aus:\n" + "Eintrag : \n" + "Regeln:\n" + "- Bei Übereinstimmung: 'OK'\n" + "- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: | X | '\n" + "- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n" + ) + aggregated_prompt += "\n".join(batches) + debug_print(f"Verarbeite Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.") + # Optional: Logge Token-Zahl des Prompts + agg_token_count = "n.v." + if tiktoken: + try: + enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) + agg_token_count = len(enc.encode(aggregated_prompt)) + debug_print(f"Token-Zahl für aktuellen Batch: {agg_token_count}") + except Exception as e: + debug_print(f"Fehler beim Token-Counting: {e}") + try: + with open("api_key.txt", "r") as f: + api_key = f.read().strip() + except Exception as e: + debug_print(f"Fehler beim Lesen des API-Tokens für Verifizierung: {e}") + return + openai.api_key = api_key + try: + response = openai.ChatCompletion.create( + model=Config.TOKEN_MODEL, + messages=[{"role": "user", "content": aggregated_prompt}], + temperature=0.0 + ) + result = response.choices[0].message.content.strip() + debug_print(f"Aggregierte Antwort für Batch {row_numbers[0]}-{row_numbers[-1]}: {result}") + except Exception as e: + debug_print(f"Fehler bei der ChatGPT Anfrage für Batch {row_numbers[0]}-{row_numbers[-1]}: {e}") + # Leere Antwort, sodass wir die Batch-Ergebnisse nicht aktualisieren + result = "" + answers = result.split("\n") + # Update pro Zeile des aktuellen Batches anhand der aggregierten Antwort + for current_row in row_numbers: + answer = "k.A." + for line in answers: + if line.strip().startswith(f"Eintrag {current_row}:"): + answer = line.split(":", 1)[1].strip() + break + if answer.upper() == "OK": + wiki_confirm = "OK" + alt_article = "" + wiki_explanation = "" + elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": + wiki_confirm = "" + alt_article = "Kein Wikipedia-Eintrag vorhanden." + wiki_explanation = "" + elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): + parts = answer.split(":", 1)[1].split("|") + alt_article = parts[0].strip() if len(parts) > 0 else "k.A." + wiki_explanation = parts[2].strip() if len(parts) > 2 else "" + wiki_confirm = "X" + else: + wiki_confirm = "" + alt_article = answer + wiki_explanation = answer + + try: + main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}") + main_sheet.update(values=[[alt_article]], range_name=f"U{current_row}") + main_sheet.update(values=[[wiki_explanation]], range_name=f"V{current_row}") + debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}") + except Exception as e: + debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}") + time.sleep(Config.RETRY_DELAY) + # Nach Bearbeitung eines Batches zurücksetzen + batches = [] + row_numbers = [] + # Falls nach Schleifendurchlauf noch ein unvollständiges Batch übrig ist + if batches: + aggregated_prompt = ( + "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " + "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel plausibel passt. " + "Gib das Ergebnis im Format:\n" + "Eintrag : \n\n" ) - result = response.choices[0].message.content.strip() - debug_print(f"Antwort ChatGPT Verifizierung Batch: {result}") - except Exception as e: - debug_print(f"Fehler bei der ChatGPT Anfrage für Verifizierung: {e}") - return - answers = result.split("\n") - for idx, row_num in enumerate(row_indices): - answer = "k.A." - for line in answers: - if line.strip().startswith(f"Eintrag {row_num}:"): - answer = line.split(":", 1)[1].strip() - break - - if answer.upper() == "OK": - wiki_confirm = "OK" - alt_article = "" - wiki_explanation = "" - elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": - wiki_confirm = "" - alt_article = "Kein Wikipedia-Eintrag vorhanden." - wiki_explanation = "" - elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): - parts = answer.split(":", 1)[1].split("|") - alt_article = parts[0].strip() if len(parts) > 0 else "k.A." - wiki_explanation = parts[2].strip() if len(parts) > 2 else "" - wiki_confirm = "X" - else: - wiki_confirm = "" - alt_article = answer - wiki_explanation = answer - - # Update Wiki-Validierungsergebnisse - main_sheet.update(values=[[wiki_confirm]], range_name=f"S{row_num}") - main_sheet.update(values=[[alt_article]], range_name=f"U{row_num}") - main_sheet.update(values=[[wiki_explanation]], range_name=f"V{row_num}") - - # *** Neuer Abschnitt: Website-Daten auslesen und in Spalte AR/AS schreiben *** - website_url = data[row_num-1][3] if len(data[row_num-1]) > 3 else "k.A." - website_raw = "k.A." - website_summary = "k.A." - if website_url.strip() != "" and website_url.strip().lower() != "k.a.": - website_raw = get_website_raw(website_url) - website_summary = summarize_website_content(website_raw) + aggregated_prompt += "\n".join(batches) + debug_print(f"Verarbeite letztes Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.") + agg_token_count = "n.v." + if tiktoken: try: - main_sheet.update(values=[[website_raw]], range_name=f"AR{row_num}") - debug_print(f"Zeile {row_num}: Spalte AR (Website Rohtext) aktualisiert: {website_raw[:100]}...") + enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) + agg_token_count = len(enc.encode(aggregated_prompt)) + debug_print(f"Token-Zahl für letztes Batch: {agg_token_count}") except Exception as e: - debug_print(f"Zeile {row_num}: Fehler beim Update von Spalte AR: {e}") - try: - main_sheet.update(values=[[website_summary]], range_name=f"AS{row_num}") - debug_print(f"Zeile {row_num}: Spalte AS (Website Zusammenfassung) aktualisiert: {website_summary}") - except Exception as e: - debug_print(f"Zeile {row_num}: Fehler beim Update von Spalte AS: {e}") - else: - debug_print(f"Zeile {row_num}: Kein gültiger Website-URL vorhanden, Website-Scraping wird übersprungen.") + debug_print(f"Fehler beim Token-Counting im letzten Batch: {e}") + try: + with open("api_key.txt", "r") as f: + api_key = f.read().strip() + except Exception as e: + debug_print(f"Fehler beim Lesen des API-Tokens (Letztes Batch): {e}") + return + openai.api_key = api_key + try: + response = openai.ChatCompletion.create( + model=Config.TOKEN_MODEL, + messages=[{"role": "user", "content": aggregated_prompt}], + temperature=0.0 + ) + result = response.choices[0].message.content.strip() + debug_print(f"Aggregierte Antwort für letztes Batch: {result}") + except Exception as e: + debug_print(f"Fehler bei der ChatGPT Anfrage für letztes Batch: {e}") + result = "" + answers = result.split("\n") + for current_row in row_numbers: + answer = "k.A." + for line in answers: + if line.strip().startswith(f"Eintrag {current_row}:"): + answer = line.split(":", 1)[1].strip() + break + if answer.upper() == "OK": + wiki_confirm = "OK" + alt_article = "" + wiki_explanation = "" + elif answer.upper() == "KEIN WIKIPEDIA-EINTRAG VORHANDEN.": + wiki_confirm = "" + alt_article = "Kein Wikipedia-Eintrag vorhanden." + wiki_explanation = "" + elif answer.startswith("Alternativer Wikipedia-Artikel vorgeschlagen:"): + parts = answer.split(":", 1)[1].split("|") + alt_article = parts[0].strip() if len(parts) > 0 else "k.A." + wiki_explanation = parts[2].strip() if len(parts) > 2 else "" + wiki_confirm = "X" + else: + wiki_confirm = "" + alt_article = answer + wiki_explanation = answer - # Branchenbewertung (Spalte W, X, Y) - crm_branch = data[row_num-1][6] if len(data[row_num-1]) > 6 else "k.A." - ext_branch = data[row_num-1][7] if len(data[row_num-1]) > 7 else "k.A." - wiki_branch = data[row_num-1][14] if len(data[row_num-1]) > 14 else "k.A." - wiki_cats = data[row_num-1][17] if len(data[row_num-1]) > 17 else "k.A." - branch_result = evaluate_branche_chatgpt(crm_branch, ext_branch, wiki_branch, wiki_cats, website_summary) - main_sheet.update(values=[[branch_result["branch"]]], range_name=f"W{row_num}") - main_sheet.update(values=[[branch_result["consistency"]]], range_name=f"X{row_num}") - main_sheet.update(values=[[branch_result["justification"]]], range_name=f"Y{row_num}") - - # Aktualisiere Timestamps und Versionen - current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - main_sheet.update(values=[[current_dt]], range_name=f"AO{row_num}") - main_sheet.update(values=[[Config.VERSION]], range_name=f"AP{row_num}") - main_sheet.update(values=[[str(agg_token_count)]], range_name=f"AQ{row_num}") - - debug_print(f"Zeile {row_num} verifiziert: Antwort: {answer}") - time.sleep(Config.RETRY_DELAY) + try: + main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}") + main_sheet.update(values=[[alt_article]], range_name=f"U{current_row}") + main_sheet.update(values=[[wiki_explanation]], range_name=f"V{current_row}") + debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}") + except Exception as e: + debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}") + time.sleep(Config.RETRY_DELAY) debug_print("Verifizierungs-Batch abgeschlossen.")