1.5.3: Optimierung Batch-Modus und Alignment-Demo Integration für robustes Update

- Anpassbare Batchgröße (Config.BATCH_SIZE, z. B. 10 Zeilen) zur Bündelung von ChatGPT-Anfragen.
- Aggregierte Prompts werden pro Batch erstellt und zeilenweise ausgewertet, mit erweitertem Logging.
- Ergebnisse der Aggregation werden als Paket verarbeitet und Google Sheets effizient aktualisiert.
- Alignment-Demo als eigenständiger Funktionsbaustein (alignment_demo_full) wiederhergestellt.
This commit is contained in:
2025-04-10 09:31:17 +00:00
parent 957bc5646e
commit ec2933229b

View File

@@ -1,9 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Version: v1.5.2 Version: v1.5.3
Datum: {aktuelles Datum} Datum: {aktuelles Datum}
Git-Überschrift (max. 100 Zeichen): 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: Git-Änderungsbeschreibung:
@@ -37,7 +37,7 @@ except ImportError:
# ==================== KONFIGURATION ==================== # ==================== KONFIGURATION ====================
class Config: class Config:
VERSION = "v1.5.2" VERSION = "v1.5.3"
LANG = "de" LANG = "de"
CREDENTIALS_FILE = "service_account.json" CREDENTIALS_FILE = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
@@ -532,148 +532,189 @@ class DataProcessor:
# ==================== NEUE FUNKTION: process_verification_only ==================== # ==================== NEUE FUNKTION: process_verification_only ====================
def process_verification_only(): def process_verification_only():
"""
Überarbeiteter BatchProzess (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 LogAusgaben informieren über den aktuellen Batch, Zeilennummern und Token-Zahlen.
"""
debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess...") debug_print("Starte Verifizierungsmodus (Modus 51) im Batch-Prozess...")
try: # Ermittelt alle Zeilen, die noch keine ChatGPT-Auswertung (in Spalte AO) haben
rows_limit = int(input("Wie viele Zeilen sollen im Batch verarbeitet werden? "))
except Exception:
rows_limit = Config.BATCH_SIZE
gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name( gc = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name(
Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"])) Config.CREDENTIALS_FILE, ["https://www.googleapis.com/auth/spreadsheets"]))
sh = gc.open_by_url(Config.SHEET_URL) sh = gc.open_by_url(Config.SHEET_URL)
main_sheet = sh.sheet1 main_sheet = sh.sheet1
data = main_sheet.get_all_values() data = main_sheet.get_all_values()
batch_entries = []
row_indices = [] batch_size = Config.BATCH_SIZE # z.B. 10, anpassbar in der Config
# Sammle Einträge, bei denen noch keine ChatGPT-Auswertung erfolgt ist (z.B. leeres Feld in Spalte AO) batches = []
row_numbers = []
for i, row in enumerate(data[1:], start=2): 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() != "": if len(row) > 40 and row[40].strip() != "":
continue continue
# Hier wird angenommen, dass der relevante Vergleichswert (z.B. Wikipedia-Daten) in Spalte Y oder AO erwartet wird. # Wir erwarten relevante Daten in mindestens 25 Spalten (anpassbar, je nachdem)
# Passen Sie die Bedingung ggf. an.
if len(row) <= 25 or row[24].strip() == "": if len(row) <= 25 or row[24].strip() == "":
entry_text = ( entry_text = (
f"Eintrag {i}:\n" f"Eintrag {i}:\n"
f"Firmenname: {row[1] if len(row) > 1 else ''}\n" f"Firmenname: {row[1] if len(row)>1 else ''}\n"
f"CRM-Beschreibung: {row[7] if len(row) > 7 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"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-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"Wiki-Kategorien: {row[16] if len(row)>16 else 'k.A.'}\n"
"-----\n" "-----\n"
) )
batch_entries.append(entry_text) batches.append(entry_text)
row_indices.append(i) row_numbers.append(i)
if len(batch_entries) == rows_limit: # Wenn wir die Batchgröße erreicht haben, verarbeite diesen Batch
break if len(batches) == batch_size:
if not batch_entries: aggregated_prompt = (
debug_print("Keine Einträge für die Verifizierung gefunden.") "Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. "
return "Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. "
aggregated_prompt = ( "Gib das Ergebnis für jeden Eintrag im Format aus:\n"
"Du bist ein Experte in der Verifizierung von Wikipedia-Artikeln für Unternehmen. " "Eintrag <Zeilennummer>: <Antwort>\n"
"Für jeden der folgenden Einträge prüfe, ob der vorhandene Wikipedia-Artikel (URL, Absatz, Kategorien) plausibel passt. " "Regeln:\n"
"Gib das Ergebnis für jeden Eintrag im Format aus:\n" "- Bei Übereinstimmung: 'OK'\n"
"Eintrag <Zeilennummer>: <Antwort>\n" "- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: <URL> | X | <Begründung>'\n"
"Regeln:\n" "- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n"
"- Bei Übereinstimmung: 'OK'\n" )
"- Bei Nichtübereinstimmung: 'Alternativer Wikipedia-Artikel vorgeschlagen: <URL> | X | <Begründung>'\n" aggregated_prompt += "\n".join(batches)
"- Falls kein Artikel gefunden wurde: 'Kein Wikipedia-Eintrag vorhanden.'\n\n" debug_print(f"Verarbeite Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.")
) # Optional: Logge Token-Zahl des Prompts
aggregated_prompt += "\n".join(batch_entries) agg_token_count = "n.v."
debug_print("Aggregierter Prompt für Verifizierungs-Batch erstellt.") if tiktoken:
agg_token_count = "n.v." try:
if tiktoken: enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL)
try: agg_token_count = len(enc.encode(aggregated_prompt))
enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL) debug_print(f"Token-Zahl für aktuellen Batch: {agg_token_count}")
agg_token_count = len(enc.encode(aggregated_prompt)) except Exception as e:
debug_print(f"Token-Zahl für Batch: {agg_token_count}") debug_print(f"Fehler beim Token-Counting: {e}")
except Exception as e: try:
debug_print(f"Fehler beim Token-Counting: {e}") with open("api_key.txt", "r") as f:
try: api_key = f.read().strip()
with open("api_key.txt", "r") as f: except Exception as e:
api_key = f.read().strip() debug_print(f"Fehler beim Lesen des API-Tokens für Verifizierung: {e}")
except Exception as e: return
debug_print(f"Fehler beim Lesen des API-Tokens (Verifizierung): {e}") openai.api_key = api_key
return try:
openai.api_key = api_key response = openai.ChatCompletion.create(
try: model=Config.TOKEN_MODEL,
response = openai.ChatCompletion.create( messages=[{"role": "user", "content": aggregated_prompt}],
model=Config.TOKEN_MODEL, temperature=0.0
messages=[{"role": "system", "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 <Zeilennummer>: <Antwort>\n\n"
) )
result = response.choices[0].message.content.strip() aggregated_prompt += "\n".join(batches)
debug_print(f"Antwort ChatGPT Verifizierung Batch: {result}") debug_print(f"Verarbeite letztes Batch für Zeilen {row_numbers[0]} bis {row_numbers[-1]}.")
except Exception as e: agg_token_count = "n.v."
debug_print(f"Fehler bei der ChatGPT Anfrage für Verifizierung: {e}") if tiktoken:
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)
try: try:
main_sheet.update(values=[[website_raw]], range_name=f"AR{row_num}") enc = tiktoken.encoding_for_model(Config.TOKEN_MODEL)
debug_print(f"Zeile {row_num}: Spalte AR (Website Rohtext) aktualisiert: {website_raw[:100]}...") agg_token_count = len(enc.encode(aggregated_prompt))
debug_print(f"Token-Zahl für letztes Batch: {agg_token_count}")
except Exception as e: except Exception as e:
debug_print(f"Zeile {row_num}: Fehler beim Update von Spalte AR: {e}") debug_print(f"Fehler beim Token-Counting im letzten Batch: {e}")
try: try:
main_sheet.update(values=[[website_summary]], range_name=f"AS{row_num}") with open("api_key.txt", "r") as f:
debug_print(f"Zeile {row_num}: Spalte AS (Website Zusammenfassung) aktualisiert: {website_summary}") api_key = f.read().strip()
except Exception as e: except Exception as e:
debug_print(f"Zeile {row_num}: Fehler beim Update von Spalte AS: {e}") debug_print(f"Fehler beim Lesen des API-Tokens (Letztes Batch): {e}")
else: return
debug_print(f"Zeile {row_num}: Kein gültiger Website-URL vorhanden, Website-Scraping wird übersprungen.") 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) try:
crm_branch = data[row_num-1][6] if len(data[row_num-1]) > 6 else "k.A." main_sheet.update(values=[[wiki_confirm]], range_name=f"S{current_row}")
ext_branch = data[row_num-1][7] if len(data[row_num-1]) > 7 else "k.A." main_sheet.update(values=[[alt_article]], range_name=f"U{current_row}")
wiki_branch = data[row_num-1][14] if len(data[row_num-1]) > 14 else "k.A." main_sheet.update(values=[[wiki_explanation]], range_name=f"V{current_row}")
wiki_cats = data[row_num-1][17] if len(data[row_num-1]) > 17 else "k.A." debug_print(f"Zeile {current_row} verifiziert: Antwort: {answer}")
branch_result = evaluate_branche_chatgpt(crm_branch, ext_branch, wiki_branch, wiki_cats, website_summary) except Exception as e:
main_sheet.update(values=[[branch_result["branch"]]], range_name=f"W{row_num}") debug_print(f"Fehler beim Updaten der Zeile {current_row}: {e}")
main_sheet.update(values=[[branch_result["consistency"]]], range_name=f"X{row_num}") time.sleep(Config.RETRY_DELAY)
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)
debug_print("Verifizierungs-Batch abgeschlossen.") debug_print("Verifizierungs-Batch abgeschlossen.")