Fokusbranchen-Logik in Branchenevaluation integriert
Wiedereinführung und Verbesserung der Funktionalität zur Priorisierung von Fokusbranchen bei der automatisierten Branchenklassifizierung durch ChatGPT.
**Änderungen:**
- **Fokusbranchen aus CSV:**
- Die Definition von Fokusbranchen erfolgt nun über eine zusätzliche Spalte in der `ziel_Branchenschema.csv`-Datei. Dies ermöglicht eine flexible Konfiguration ohne Code-Änderungen.
- Die Funktion `load_target_schema` wurde erweitert, um diese Fokus-Markierungen einzulesen und eine separate Liste `FOCUS_TARGET_BRANCHES` sowie einen spezifischen Prompt-Teil `FOCUS_BRANCHES_PROMPT_PART` zu generieren.
- **Angepasste `evaluate_branche_chatgpt` Funktion:**
- Nutzt nun die global geladenen Listen `ALLOWED_TARGET_BRANCHES` und `FOCUS_TARGET_BRANCHES` sowie die Prompt-Teile `TARGET_SCHEMA_STRING` und `FOCUS_BRANCHES_PROMPT_PART`.
- Der an ChatGPT gesendete Prompt wurde um einen expliziten Hinweis erweitert, Fokusbranchen bei der Klassifizierung zu priorisieren, falls mehrere Branchen plausibel erscheinen.
- Die Fallback-Logik für die zu verwendende Beschreibungsquelle (CRM-Beschreibung vs. Website-Zusammenfassung bei fehlenden Wiki-Daten) wurde aus der früheren Funktionsversion übernommen und verfeinert.
- Die API-Key-Handhabung wurde entfernt, da diese nun global über `Config.API_KEYS` und `call_openai_chat` erfolgt.
- `debug_print` Aufrufe wurden durch Standard-Logging (`logger.debug`) ersetzt.
- **Globale Variablen:** Neue globale Variablen für Fokusbranchen und deren Prompt-Teil wurden eingeführt.
**Ziel:**
- Erhöhung der Genauigkeit der Branchenklassifizierung, indem vordefinierte, strategisch wichtige Branchen bei der KI-gestützten Bewertung bevorzugt werden.
- Verbesserung der Flexibilität und Wartbarkeit der Fokusbranchen-Definition durch Auslagerung in die zentrale CSV-Datei.
This commit is contained in:
@@ -191,6 +191,9 @@ COLUMN_MAP = {
|
||||
BRANCH_MAPPING = {}
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
||||
ALLOWED_TARGET_BRANCHES = []
|
||||
FOCUS_TARGET_BRANCHES = [] # NEU
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
||||
FOCUS_BRANCHES_PROMPT_PART = "" # NEU: Für den Prompt-Teil der Fokusbranchen
|
||||
|
||||
# Marker für URLs, die erneut per SERP gesucht werden sollen
|
||||
URL_CHECK_MARKER = "URL_CHECK_NEEDED" # <<< NEU HINZUFÜGEN
|
||||
@@ -975,91 +978,83 @@ ALLOWED_TARGET_BRANCHES = [] # Liste der erlaubten Kurzformen
|
||||
|
||||
def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
||||
"""
|
||||
Laedt Liste erlaubter Ziel-Branchen (Kurzformen) aus Spalte A der CSV-Datei.
|
||||
Befuellt die globalen Variablen ALLOWED_TARGET_BRANCHES und TARGET_SCHEMA_STRING.
|
||||
|
||||
Args:
|
||||
csv_filepath (str, optional): Pfad zur CSV-Datei mit dem Branchenschema.
|
||||
Defaults to the global BRANCH_MAPPING_FILE.
|
||||
Laedt Liste erlaubter Ziel-Branchen und Fokus-Branchen aus der CSV-Datei.
|
||||
Befuellt die globalen Variablen ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES,
|
||||
TARGET_SCHEMA_STRING und FOCUS_BRANCHES_PROMPT_PART.
|
||||
"""
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Zugriff auf die globalen Variablen
|
||||
global BRANCH_MAPPING, TARGET_SCHEMA_STRING, ALLOWED_TARGET_BRANCHES
|
||||
logger = logging.getLogger(__name__)
|
||||
global ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
||||
|
||||
# Setzen Sie BRANCH_MAPPING zurueck, da es in dieser Version nicht primaer genutzt wird
|
||||
BRANCH_MAPPING = {}
|
||||
|
||||
allowed_branches_set = set() # Nutzt ein Set, um Duplikate automatisch zu behandeln
|
||||
ALLOWED_TARGET_BRANCHES = []
|
||||
FOCUS_TARGET_BRANCHES = []
|
||||
allowed_branches_set = set()
|
||||
focus_branches_set = set() # Für Fokusbranchen
|
||||
line_count = 0
|
||||
|
||||
logger.info(f"Lade Ziel-Schema (Kurzformen) aus '{csv_filepath}' Spalte A...") # Jetzt sollte logger definiert sein
|
||||
logger.info(f"Lade Ziel-Schema und Fokus-Branchen aus '{csv_filepath}'...")
|
||||
|
||||
try:
|
||||
# Versuche, die Datei mit UTF-8-BOM-Signatur oder normalem UTF-8 zu oeffnen
|
||||
# Verwenden Sie "r" fuer Textmodus und geben Sie das Encoding an
|
||||
with open(csv_filepath, "r", encoding="utf-8-sig") as f:
|
||||
# Verwenden Sie den CSV-Reader, um Zeilen zu lesen
|
||||
reader = csv.reader(f)
|
||||
# Versuche, die erste Zeile als Header zu ueberspringen (Heuristik)
|
||||
try:
|
||||
header_row = next(reader)
|
||||
# logger.debug(f"Ueberspringe Header-Zeile: {header_row}") # Zu viel Laerm im Debug
|
||||
header_row = next(reader) # Überspringe Header
|
||||
logger.debug(f"Ueberspringe Header-Zeile im Schema: {header_row}")
|
||||
except StopIteration:
|
||||
# Wenn die Datei leer ist
|
||||
logger.warning(f"Schema-Datei '{csv_filepath}' ist leer.")
|
||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei leer)." # Setze Fehler-String
|
||||
return # Beende die Funktion, da nichts zu tun ist
|
||||
logger.warning(f"Schema-Datei '{csv_filepath}' ist leer oder hat keinen Header.")
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei leer)."
|
||||
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||
return
|
||||
|
||||
# Iteriere ueber die verbleibenden Zeilen
|
||||
for row in reader:
|
||||
line_count += 1
|
||||
# logger.debug(f"Schema-Laden: Lese Zeile {line_count}: {row}") # Zu viel Laerm im Debug
|
||||
for row_num, row in enumerate(reader, 1): # Starte Zählung bei 1 für Zeilennummern nach Header
|
||||
line_count = row_num
|
||||
if not row: # Leere Zeile überspringen
|
||||
continue
|
||||
|
||||
# Pruefe, ob die Zeile mindestens eine Spalte hat (Spalte A ist Index 0)
|
||||
if len(row) >= 1:
|
||||
target = row[0].strip() # Hole den Wert aus Spalte A und entferne Whitespace
|
||||
if target: # Fuege den Wert zum Set hinzu, wenn er nicht leer ist
|
||||
allowed_branches_set.add(target)
|
||||
# logger.debug(f" -> '{target}' zum Set hinzugefuegt.") # Zu viel Laerm im Debug
|
||||
|
||||
target_branch = row[0].strip()
|
||||
if target_branch:
|
||||
allowed_branches_set.add(target_branch)
|
||||
# Prüfe Spalte B (Index 1) für Fokus-Markierung
|
||||
if len(row) >= 2 and row[1].strip().upper() in ["X", "FOKUS", "JA", "TRUE", "1"]:
|
||||
focus_branches_set.add(target_branch)
|
||||
logger.debug(f" -> Fokusbranche gefunden: '{target_branch}'")
|
||||
except FileNotFoundError:
|
||||
# Wenn die Schema-Datei nicht gefunden wird
|
||||
logger.critical(f"FEHLER: Schema-Datei '{csv_filepath}' nicht gefunden.")
|
||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei nicht gefunden)." # Setze Fehler-String
|
||||
return # Beende die Funktion, da die Datei fehlt
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei nicht gefunden)."
|
||||
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||
return
|
||||
except Exception as e:
|
||||
# Fange andere unerwartete Fehler beim Lesen der Datei ab
|
||||
logger.critical(f"FEHLER beim Laden des Ziel-Schemas aus '{csv_filepath}' (Zeile {line_count if line_count > 0 else 'vor erster Zeile'}): {e}")
|
||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Fehler beim Lesen)." # Setze Fehler-String
|
||||
return # Beende die Funktion, da ein Fehler aufgetreten ist
|
||||
logger.critical(f"FEHLER beim Laden des Ziel-Schemas aus '{csv_filepath}' (Zeile {line_count}): {e}")
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Fehler beim Lesen)."
|
||||
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||
return
|
||||
|
||||
|
||||
# Konvertiere das Set in eine sortierte Liste
|
||||
ALLOWED_TARGET_BRANCHES = sorted(list(allowed_branches_set), key=str.lower)
|
||||
logger.info(f"Ziel-Schema geladen. {len(ALLOWED_TARGET_BRANCHES)} eindeutige Zielbranchen gefunden.")
|
||||
FOCUS_TARGET_BRANCHES = sorted(list(focus_branches_set), key=str.lower)
|
||||
|
||||
logger.info(f"Ziel-Schema geladen: {len(ALLOWED_TARGET_BRANCHES)} eindeutige Zielbranchen, davon {len(FOCUS_TARGET_BRANCHES)} Fokusbranchen.")
|
||||
|
||||
# Erstelle den Prompt-String fuer ChatGPT, wenn gueltige Branchen gefunden wurden
|
||||
if ALLOWED_TARGET_BRANCHES:
|
||||
# logger.debug(f"Erste 10 geladene Zielbranchen: {ALLOWED_TARGET_BRANCHES[:10]}") # Zu viel Laerm im Debug
|
||||
schema_lines = ["Ziel-Branchenschema: Folgende Branchenbereiche sind gueltig (Kurzformen):"]
|
||||
# Fuege jede erlaubte Branche als Listeneintrag hinzu
|
||||
schema_lines.extend(f"- {branch}" for branch in ALLOWED_TARGET_BRANCHES)
|
||||
# Fuege strenge Anweisungen fuer das Antwortformat hinzu
|
||||
schema_lines.append("\nBitte ordne das Unternehmen ausschliesslich in einen dieser Bereiche ein. Gib NUR den exakten Kurznamen der Branche zurueck (keine Praefixe oder zusaetzliche Erklaerungen ausser im 'Begruendung'-Feld).") # Verwende Umlaute nicht, um Encoding-Probleme im Prompt zu vermeiden
|
||||
# Anweisungen für das Antwortformat (unverändert)
|
||||
schema_lines.append("\nBitte ordne das Unternehmen ausschliesslich in einen dieser Bereiche ein. Gib NUR den exakten Kurznamen der Branche zurueck (keine Praefixe oder zusaetzliche Erklaerungen ausser im 'Begruendung'-Feld).")
|
||||
schema_lines.append("Antworte ausschliesslich im folgenden Format (keine Einleitung, kein Schlusssatz):")
|
||||
schema_lines.append("Branche: <Exakter Kurzname der Branche aus der Liste>")
|
||||
schema_lines.append("Uebereinstimmung: <ok oder X (Vergleich deines Vorschlags mit der extrahierten Kurzform der CRM-Referenz)>") # Verwende Umlaute nicht
|
||||
schema_lines.append("Begruendung: <Sehr kurze Begruendung fuer deinen Branchenvorschlag>") # Verwende Umlaute nicht
|
||||
|
||||
# Verbinde die Zeilen zum finalen Prompt-String
|
||||
schema_lines.append("Uebereinstimmung: <ok oder X (Vergleich deines Vorschlags mit der extrahierten Kurzform der CRM-Referenz)>")
|
||||
schema_lines.append("Begruendung: <Sehr kurze Begruendung fuer deinen Branchenvorschlag>")
|
||||
TARGET_SCHEMA_STRING = "\n".join(schema_lines)
|
||||
# logger.debug(f"Generierter TARGET_SCHEMA_STRING:\n{TARGET_SCHEMA_STRING}") # Zu viel Laerm im Debug
|
||||
|
||||
if FOCUS_TARGET_BRANCHES:
|
||||
focus_prompt_lines = ["\nZusätzlicher Hinweis: Wenn die Wahl zwischen mehreren passenden Branchen besteht, priorisiere bitte, wenn möglich, eine der folgenden Fokusbranchen:"]
|
||||
focus_prompt_lines.extend(f"- {branch}" for branch in FOCUS_TARGET_BRANCHES)
|
||||
FOCUS_BRANCHES_PROMPT_PART = "\n".join(focus_prompt_lines)
|
||||
else:
|
||||
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||
logger.info("Keine Fokusbranchen im Schema definiert.")
|
||||
else:
|
||||
# Wenn keine gueltigen Branchen gefunden wurden
|
||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Keine gueltigen Branchen in Datei gefunden)."
|
||||
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||
logger.warning("Keine gueltigen Zielbranchen im Schema gefunden. Branchenbewertung ist nicht moeglich.")
|
||||
|
||||
|
||||
@@ -1404,201 +1399,156 @@ def summarize_batch_openai(tasks_data):
|
||||
# Funktion zur Branchenbewertung mittels OpenAI.
|
||||
# Nutzt globale Helfer: ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING,
|
||||
# call_openai_chat, logger, re, retry_on_failure.
|
||||
@retry_on_failure # Wende den Decorator auf diese Funktion an, da sie call_openai_chat aufruft
|
||||
@retry_on_failure
|
||||
def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary):
|
||||
"""
|
||||
Ordnet das Unternehmen basierend auf den angegebenen Informationen exakt einer Branche
|
||||
aus dem Ziel-Branchenschema (nur Kurzformen) zu. Validiert den ChatGPT-Vorschlag
|
||||
strikt gegen die erlaubten Kurzformen und fuehrt einen Fallback auf die (extrahierte)
|
||||
CRM-Kurzform durch, falls der Vorschlag ungueltig ist.
|
||||
logger = logging.getLogger(__name__)
|
||||
# Zugriff auf die globalen, durch load_target_schema() befüllten Variablen
|
||||
global ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
||||
|
||||
Args:
|
||||
crm_branche (str): Branche laut CRM (kann noch Praefix enthalten).
|
||||
beschreibung (str): Unternehmensbeschreibung (CRM).
|
||||
wiki_branche (str): Branche aus Wikipedia (falls vorhanden).
|
||||
wiki_kategorien (str): Wikipedia-Kategorien.
|
||||
website_summary (str): Zusammenfassung des Website-Inhalts.
|
||||
|
||||
Returns:
|
||||
dict: Enthaehlt "branch" (die finale, gueltige Kurzform oder Fehler),
|
||||
"consistency" ('ok', 'X', 'fallback_crm_valid', 'fallback_invalid', 'error_...'),
|
||||
"justification" (Begruendung von ChatGPT oder Fallback-Info).
|
||||
Wirft Exception bei API-Fehlern nach Retries (von call_openai_chat).
|
||||
"""
|
||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
||||
# Zugriff auf globale Variablen (befuellt von load_target_schema im Block 6)
|
||||
global ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING
|
||||
|
||||
# Grundlegende Pruefung: Ist das Schema ueberhaupt geladen?
|
||||
if not ALLOWED_TARGET_BRANCHES:
|
||||
logger.critical("FEHLER in evaluate_branche_chatgpt: Ziel-Branchenschema (ALLOWED_TARGET_BRANCHES) ist leer. Kann Branchen nicht validieren.")
|
||||
# Geben Sie ein Fehlerergebnis zurueck
|
||||
logger.critical("FEHLER in evaluate_branche_chatgpt: Ziel-Branchenschema nicht geladen.")
|
||||
return {"branch": "FEHLER - SCHEMA FEHLT", "consistency": "error_schema_missing", "justification": "Fehler: Ziel-Schema nicht geladen"}
|
||||
|
||||
# Erstelle Lookup fuer erlaubte Branches (case-insensitive)
|
||||
allowed_branches_lookup = {b.lower(): b for b in ALLOWED_TARGET_BRANCHES}
|
||||
|
||||
# --- Prompt fuer ChatGPT erstellen ---
|
||||
# Beginne mit den Regeln und der Liste der gueltigen Kurzformen
|
||||
prompt_parts = [TARGET_SCHEMA_STRING] # Enthält bereits die Liste und Anweisungen
|
||||
# --- Prompt für ChatGPT erstellen ---
|
||||
# Beginnt mit den Regeln und der Liste der gueltigen Kurzformen (TARGET_SCHEMA_STRING)
|
||||
# Fügt dann den Hinweis auf Fokusbranchen hinzu (FOCUS_BRANCHES_PROMPT_PART)
|
||||
prompt_parts = [TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART] # <<< HIER FOCUS_BRANCHES_PROMPT_PART EINFÜGEN
|
||||
prompt_parts.append("\nOrdne das Unternehmen anhand folgender Angaben exakt einer Branche des Ziel-Branchenschemas (Kurzformen) zu:")
|
||||
|
||||
# Fuege nur vorhandene Informationen hinzu und kuerze sie ggf.
|
||||
# Stellen Sie sicher, dass die Werte keine None-Typen sind
|
||||
# Informationen hinzufügen (unverändert)
|
||||
if crm_branche and str(crm_branche).strip() and str(crm_branche).strip().lower() != "k.a.": prompt_parts.append(f"- CRM-Branche (Referenz): {str(crm_branche).strip()}")
|
||||
if beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": prompt_parts.append(f"- Beschreibung: {str(beschreibung).strip()[:500]}...") # Kuerzen
|
||||
if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.": prompt_parts.append(f"- Wikipedia-Branche: {str(wiki_branche).strip()[:300]}...") # Kuerzen
|
||||
if wiki_kategorien and str(wiki_kategorien).strip() and str(wiki_kategorien).strip().lower() != "k.a.": prompt_parts.append(f"- Wikipedia-Kategorien: {str(wiki_kategorien).strip()[:500]}...") # Kuerzen
|
||||
if website_summary and str(website_summary).strip() and str(website_summary).strip().lower() != "k.a." and not str(website_summary).strip().startswith("k.A. (Fehler"): # Pruefe auch auf Website Summary Fehlerwerte
|
||||
prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...") # Kuerzen
|
||||
# Fallback-Logik für beschreibung vs. website_summary (basierend auf Ihrer alten Funktion)
|
||||
if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.":
|
||||
# Wenn Wiki-Daten vorhanden sind, nutzen wir die CRM-Beschreibung als Hauptbeschreibung
|
||||
if beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.":
|
||||
prompt_parts.append(f"- Beschreibung (CRM): {str(beschreibung).strip()[:500]}...")
|
||||
if website_summary and str(website_summary).strip() and str(website_summary).strip().lower() != "k.a." and not str(website_summary).strip().startswith("k.A. (Fehler"):
|
||||
prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...")
|
||||
prompt_parts.append(f"- Wikipedia-Branche: {str(wiki_branche).strip()[:300]}...")
|
||||
if wiki_kategorien and str(wiki_kategorien).strip() and str(wiki_kategorien).strip().lower() != "k.a.":
|
||||
prompt_parts.append(f"- Wikipedia-Kategorien: {str(wiki_kategorien).strip()[:500]}...")
|
||||
else: # Keine validen Wiki-Daten
|
||||
logger.debug("evaluate_branche_chatgpt: Keine validen Wiki-Daten, nutze Website-Zusammenfassung als primäre Beschreibung (falls vorhanden).")
|
||||
if website_summary and str(website_summary).strip() and str(website_summary).strip().lower() != "k.a." and not str(website_summary).strip().startswith("k.A. (Fehler"):
|
||||
prompt_parts.append(f"- Website-Zusammenfassung (als Hauptbeschreibung): {str(website_summary).strip()[:800]}...") # Ggf. länger, da Hauptinfo
|
||||
elif beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": # Fallback auf CRM Beschreibung
|
||||
prompt_parts.append(f"- Beschreibung (CRM, als Hauptbeschreibung): {str(beschreibung).strip()[:800]}...")
|
||||
|
||||
|
||||
# Fallback, wenn zu wenige Infos da sind (mindestens 2 relevante Zeilen im Prompt neben dem Schema)
|
||||
# Der Prompt hat immer mindestens 1 Zeile (Schema) + 1 Zeile (Instruktion "Ordne zu...").
|
||||
# Pruefen wir, ob mindestens 2 Info-Zeilen hinzugefuegt wurden.
|
||||
if len(prompt_parts) < 3: # 1 (Schema) + 1 (Instruktion) + <2 (Infos)
|
||||
logger.warning("Warnung in evaluate_branche_chatgpt: Zu wenige Informationen (<2 Quellen) fuer Branchenevaluierung.")
|
||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
||||
return {"branch": crm_branche, "consistency": "error_no_info", "justification": "Fehler: Zu wenige Informationen fuer eine Einschaetzung"}
|
||||
if len(prompt_parts) < (3 + (1 if FOCUS_BRANCHES_PROMPT_PART else 0) )): # Mind. TARGET_SCHEMA + FOCUS_PART (falls vorhanden) + Instruktion + 1 Infoquelle
|
||||
logger.warning("Warnung in evaluate_branche_chatgpt: Zu wenige Informationen für Branchenevaluierung.")
|
||||
crm_short_branch_for_fallback = "k.A."
|
||||
if crm_branche and isinstance(crm_branche, str):
|
||||
parts = crm_branche.split(">", 1)
|
||||
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER",
|
||||
"consistency": "error_no_info",
|
||||
"justification": "Fehler: Zu wenige Informationen fuer eine Einschaetzung"}
|
||||
|
||||
# Prompt fuer das Antwortformat ist bereits in TARGET_SCHEMA_STRING enthalten.
|
||||
prompt = "\n".join(filter(None, prompt_parts)) # filter(None, ...) um leere Strings aus FOCUS_BRANCHES_PROMPT_PART zu entfernen
|
||||
# logger.debug(f"Erstellter Prompt fuer Branchenevaluierung:\n---\n{prompt}\n---")
|
||||
|
||||
prompt = "\n".join(prompt_parts)
|
||||
# logger.debug(f"Erstellter Prompt fuer Branchenevaluierung:\n---\n{prompt}\n---") # Zu viel Laerm im Debug
|
||||
# --- ChatGPT aufrufen (Rest der Funktion bleibt wie in Block 10/20) ---
|
||||
# ... (ChatGPT Call, Parsing, Validierung, Fallback-Logik) ...
|
||||
# Wichtig: Die API-Key Ladung und der openai-Import sollten jetzt global (Block 1) erfolgen
|
||||
# und nicht mehr innerhalb dieser Funktion.
|
||||
# Ihre alte Funktion hat api_key.txt gelesen, das ist jetzt Teil von Config.load_api_keys().
|
||||
|
||||
# --- ChatGPT aufrufen ---
|
||||
# call_openai_chat nutzt den retry_on_failure Decorator und wirft bei endgueltigem Fehler eine Exception
|
||||
chat_response = None
|
||||
try:
|
||||
chat_response = call_openai_chat(prompt, temperature=0.0) # Niedrige Temperatur fuer konsistente Zuordnung
|
||||
|
||||
chat_response = call_openai_chat(prompt, temperature=0.0) # Nutzt globale Funktion
|
||||
if not chat_response:
|
||||
# Dieser Fall sollte nach der Aenderung in call_openai_chat nicht mehr auftreten (wuerde Exception werfen)
|
||||
logger.error("call_openai_chat gab unerwarteterweise None zurueck fuer Branchenevaluation.")
|
||||
raise openai.error.APIError("Keine Antwort von OpenAI erhalten fuer Branchenevaluation.") # Wirf eine Exception
|
||||
|
||||
logger.error("call_openai_chat gab unerwarteterweise None/leer zurueck fuer Branchenevaluation.")
|
||||
raise APIError("Keine Antwort von OpenAI erhalten fuer Branchenevaluation.")
|
||||
except Exception as e:
|
||||
# Wenn call_openai_chat nach Retries eine Exception wirft
|
||||
# Der Fehler wird bereits vom retry_on_failure Decorator geloggt.
|
||||
logger.error(f"Endgueltiger FEHLER beim OpenAI-Aufruf fuer Branchenevaluation: {e}")
|
||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
||||
# Haengen Sie die Fehlermeldung an die Begruendung an.
|
||||
return {"branch": crm_branche, "consistency": "error_api_failed", "justification": f"Fehler API: {str(e)[:100]}"}
|
||||
crm_short_branch_for_fallback = "k.A."
|
||||
if crm_branche and isinstance(crm_branche, str):
|
||||
parts = crm_branche.split(">", 1)
|
||||
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER API",
|
||||
"consistency": "error_api_failed",
|
||||
"justification": f"Fehler API: {str(e)[:100]}"}
|
||||
|
||||
|
||||
# --- Antwort parsen ---
|
||||
# --- Antwort parsen (wie gehabt) ---
|
||||
lines = chat_response.strip().split("\n")
|
||||
# Initialisiere Ergebnisdict mit Fallback-Werten oder leeren Strings
|
||||
result = {"branch": None, "consistency": None, "justification": ""}
|
||||
suggested_branch = ""
|
||||
parsed_branch = False
|
||||
for line in lines:
|
||||
line_lower = line.lower(); line_stripped = line.strip()
|
||||
if line_lower.startswith("branche:"):
|
||||
# Extrahiere die vorgeschlagene Branche, bereinige Leerzeichen und Anfuehrungszeichen
|
||||
suggested_branch = line_stripped.split(":", 1)[1].strip().strip('"\'')
|
||||
parsed_branch = True
|
||||
elif line_lower.startswith("uebereinstimmung:") or line_lower.startswith("ubereinstimmung:"): # Beruecksichtige Umlaute und keine Umlaute
|
||||
# Wir ueberschreiben die Konsistenz spaeter basierend auf unserer Logik, ignorieren Sie die KI-Antwort hier
|
||||
elif line_lower.startswith("uebereinstimmung:") or line_lower.startswith("ubereinstimmung:"):
|
||||
pass
|
||||
elif line_lower.startswith("begruendung:") or line_lower.startswith("begruendung:"): # Beruecksichtige Umlaute und keine Umlaute
|
||||
# Erfasse die Begruendung. Wenn es mehrere Begruendungszeilen gibt, haenge sie an.
|
||||
elif line_lower.startswith("begruendung:") or line_lower.startswith("begruendung:"):
|
||||
justification_text = line_stripped.split(":", 1)[1].strip()
|
||||
if result["justification"]: result["justification"] += " " + justification_text
|
||||
else: result["justification"] = justification_text
|
||||
# Behandle andere moegliche unerwartete Zeilen (optional)
|
||||
# elif line_lower.startswith(("resultat", "eintrag", "antwort")):
|
||||
# logger.warning(f"Unerwartete Zeile im Branchen-Prompt gefunden: {line[:100]}...")
|
||||
|
||||
if not parsed_branch or not suggested_branch or suggested_branch.lower() in ["k.a.", "n/a"]:
|
||||
logger.error(f"Fehler in evaluate_branche_chatgpt: Konnte 'Branche:' nicht oder nur leer/k.A. aus Antwort parsen: {chat_response[:500]}...")
|
||||
crm_short_branch_for_fallback = "k.A."
|
||||
if crm_branche and isinstance(crm_branche, str):
|
||||
parts = crm_branche.split(">", 1)
|
||||
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER PARSING",
|
||||
"consistency": "error_parsing",
|
||||
"justification": f"Fehler Parsing: Antwortformat unerwartet."}
|
||||
|
||||
# Pruefe, ob Branch geparst wurde UND nicht leer ist
|
||||
if not parsed_branch or not suggested_branch or suggested_branch.lower() in ["k.a.", "n/a"]: # Fuege "k.a." zur Pruefung hinzu
|
||||
logger.error(f"Fehler in evaluate_branche_chatgpt: Konnte 'Branche:' nicht oder nur leer/k.A. aus Antwort parsen: {chat_response[:500]}...") # Logge Anfang der Antwort
|
||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
||||
return {"branch": crm_branche, "consistency": "error_parsing", "justification": f"Fehler Parsing: Antwortformat unerwartet."}
|
||||
|
||||
|
||||
# --- Validierung des ChatGPT-Vorschlags ---
|
||||
# --- Validierung und Fallback (wie gehabt) ---
|
||||
final_branch = None
|
||||
suggested_branch_lower = suggested_branch.lower()
|
||||
|
||||
# 1. Ist der vorgeschlagene Branch EXAKT im Ziel-Schema enthalten?
|
||||
if suggested_branch_lower in allowed_branches_lookup:
|
||||
final_branch = allowed_branches_lookup[suggested_branch_lower] # Nimm die korrekte Schreibweise aus der Liste
|
||||
final_branch = allowed_branches_lookup[suggested_branch_lower]
|
||||
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist gueltig ('{final_branch}').")
|
||||
result["consistency"] = "pending_comparison" # Temporaer Status vor Vergleich mit CRM
|
||||
|
||||
result["consistency"] = "pending_comparison"
|
||||
else:
|
||||
# --- Fallback-Logik, wenn Vorschlag ungueltig ist ---
|
||||
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist NICHT im Ziel-Schema ({len(ALLOWED_TARGET_BRANCHES)} Eintraege). Starte Fallback...")
|
||||
|
||||
# Versuche Kurzform aus CRM-Branche zu extrahieren
|
||||
crm_short_branch = "k.A." # Default
|
||||
# Stellen Sie sicher, dass crm_branche ein String ist
|
||||
if crm_branche and isinstance(crm_branche, str) and ">" in crm_branche:
|
||||
crm_short_branch = crm_branche.split(">", 1)[1].strip()
|
||||
# Wenn CRM schon Kurzform sein koennte (nicht leer/k.A. und kein Praefix > enthalten)
|
||||
elif crm_branche and isinstance(crm_branche, str) and crm_branche.strip() and crm_branche.strip().lower() != "k.a.":
|
||||
crm_short_branch = crm_branche.strip()
|
||||
|
||||
logger.debug(f" Fallback: Pruefe extrahierte CRM-Kurzform: '{crm_short_branch}'")
|
||||
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist NICHT im Ziel-Schema. Starte Fallback...")
|
||||
crm_short_branch = "k.A."
|
||||
if crm_branche and isinstance(crm_branche, str):
|
||||
parts = crm_branche.split(">", 1)
|
||||
crm_short_branch = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||
|
||||
crm_short_branch_lower = crm_short_branch.lower()
|
||||
|
||||
# 2. Ist die extrahierte CRM-Kurzform EXAKT im Ziel-Schema enthalten?
|
||||
if crm_short_branch != "k.A." and crm_short_branch_lower in allowed_branches_lookup:
|
||||
final_branch = allowed_branches_lookup[crm_short_branch_lower] # Nimm korrekte Schreibweise
|
||||
result["consistency"] = "fallback_crm_valid" # Setze Fallback-Status
|
||||
# Kombiniere ChatGPT Begruendung (falls vorhanden) mit Fallback-Info
|
||||
final_branch = allowed_branches_lookup[crm_short_branch_lower]
|
||||
result["consistency"] = "fallback_crm_valid"
|
||||
fallback_reason = f"Fallback: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}'). Gueltige CRM-Kurzform '{final_branch}' verwendet."
|
||||
result["justification"] = f"{fallback_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
||||
logger.info(f"Fallback auf gueltige CRM-Kurzform erfolgreich: '{final_branch}'")
|
||||
else:
|
||||
# 3. Wenn auch CRM-Kurzform ungueltig
|
||||
final_branch = suggested_branch # Behalte ungueltigen Vorschlag
|
||||
result["consistency"] = "fallback_invalid" # Setze Fehler-Fallback-Status
|
||||
error_reason = f"Fehler: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}') und keine gueltige CRM-Kurzform ('{crm_short_branch}') als Fallback verfuegbar."
|
||||
final_branch = suggested_branch # Behalte ungueltigen Vorschlag für Info, wird aber als Fehler markiert
|
||||
result["consistency"] = "fallback_invalid"
|
||||
error_reason = f"Fehler: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}') und keine gueltige CRM-Kurzform ('{crm_short_branch}') als Fallback."
|
||||
result["justification"] = f"{error_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
||||
logger.warning(f"Fallback fehlgeschlagen. Ungueltiger Vorschlag: '{final_branch}', Ungueltige CRM-Kurzform: '{crm_short_branch}'")
|
||||
# Alternativ: Setze final_branch auf einen expliziten Fehlerwert, um es im Sheet hervorzuheben
|
||||
# final_branch = "FEHLER - UNGUELTIGE ZUWEISUNG" # Optional
|
||||
final_branch = "FEHLER - UNGUELTIGE ZUWEISUNG" # Expliziter Fehlerwert
|
||||
|
||||
# Setze den finalen Branch im Ergebnis-Dictionary
|
||||
# Verwenden Sie einen Standard-Fehlerwert, falls final_branch aus irgendeinem Grund immer noch None ist
|
||||
result["branch"] = final_branch if final_branch else "FEHLER"
|
||||
|
||||
# --- Konsistenzpruefung (Finale Bewertung des final_branch vs. CRM-Kurzform) ---
|
||||
# Extrahiere CRM-Kurzform fuer den Vergleich (erneut oder Variable von oben)
|
||||
crm_short_to_compare = "k.A."
|
||||
if crm_branche and isinstance(crm_branche, str) and ">" in crm_branche:
|
||||
crm_short_to_compare = crm_branche.split(">", 1)[1].strip()
|
||||
elif crm_branche and isinstance(crm_branche, str) and crm_branche.strip() and crm_branche.strip().lower() != "k.a.":
|
||||
crm_short_to_compare = crm_branche.strip()
|
||||
if crm_branche and isinstance(crm_branche, str):
|
||||
parts = crm_branche.split(">", 1)
|
||||
crm_short_to_compare = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||
|
||||
|
||||
# Vergleiche finalen Branch (falls nicht FEHLER) mit CRM-Kurzform (case-insensitive)
|
||||
# Aktualisiere den Consistency-Status, WENN er noch 'pending_comparison' ist.
|
||||
# Fallback-Status ('fallback_crm_valid', 'fallback_invalid') sollen erhalten bleiben.
|
||||
if result["consistency"] == "pending_comparison" and result["branch"] != "FEHLER":
|
||||
if result["consistency"] == "pending_comparison" and result["branch"] != "FEHLER" and not result["branch"].startswith("FEHLER"):
|
||||
if result["branch"].lower() == crm_short_to_compare.lower():
|
||||
result["consistency"] = "ok" # Uebereinstimmung mit CRM
|
||||
result["consistency"] = "ok"
|
||||
else:
|
||||
result["consistency"] = "X" # Keine Uebereinstimmung mit CRM
|
||||
|
||||
|
||||
# Entferne den temporaeren Status, falls er noch da ist (sollte nicht passieren)
|
||||
result["consistency"] = "X"
|
||||
|
||||
if result["consistency"] == "pending_comparison":
|
||||
logger.warning("Konsistenzpruefung blieb im Status 'pending_comparison', setze auf 'error_comparison_failed'.")
|
||||
result["consistency"] = "error_comparison_failed"
|
||||
elif result["consistency"] is None: # Sollte nicht passieren
|
||||
logger.error("Konsistenz blieb unerwartet None, setze auf 'error_unknown_state'.")
|
||||
elif result["consistency"] is None:
|
||||
result["consistency"] = "error_unknown_state"
|
||||
|
||||
|
||||
# Debug-Ausgabe des finalen Ergebnisses vor Rueckgabe
|
||||
logger.debug(f"Finale Branch-Evaluation Ergebnis: Branch='{result.get('branch')}', Consistency='{result.get('consistency')}', Justification='{result.get('justification', '')[:100]}...'")
|
||||
|
||||
return result # Rueckgabe des Ergebnis-Dictionarys
|
||||
return result
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
Reference in New Issue
Block a user