Implementierung der kontextbasierten Brancheneinstufung

- FEATURE: Brancheneinstufung 2.0 implementiert; nutzt nun die reichhaltigen Definitionen und Beispiele aus `config.py` für ein hochpräzises, kontextuelles Matching.
- REFACTOR: `evaluate_branche_chatgpt` in `helpers.py` komplett neugeschrieben; gibt nun eine detaillierte Begründung für die Zuordnung zurück.
- FEATURE: Neuer Batch-Modus `reclassify_branches` in `data_processor.py` hinzugefügt, um eine vollständige Neubewertung aller Accounts zu ermöglichen.
This commit is contained in:
2025-07-28 09:48:41 +00:00
parent cba7dac9ac
commit d33b1bd37d

View File

@@ -877,94 +877,91 @@ def summarize_wikipedia_article(full_text, company_name):
# ==============================================================================
@retry_on_failure
def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary, schema_data):
def evaluate_branche_chatgpt(company_name, website_summary, wiki_absatz):
"""
Bewertet die Branche eines Unternehmens. Stellt die stabile System-Prompt- und Parsing-Logik
aus v1.7.9 wieder her und integriert den intelligenten Fallback.
Führt eine kontextbasierte Brancheneinstufung (v2.0) durch.
Das Unternehmensprofil wird mit dem vollständigen, definierten Branchenschema
abgeglichen, um die bestmögliche Zuordnung zu finden.
"""
logger = logging.getLogger(__name__)
if not schema_data or not schema_data.get("allowed_branches"):
return {"branch": "FEHLER - SCHEMA FEHLT", "confidence": "N/A", "consistency": "error_schema_missing", "justification": "Fehler: Schema-Daten fehlen."}
allowed_branches = schema_data["allowed_branches"]
allowed_branches_lookup = {b.lower(): b for b in allowed_branches}
# 1. Baue das Unternehmensprofil zusammen
unternehmens_profil = [f"- Name: {company_name}"]
if website_summary and 'k.a.' not in website_summary.lower():
unternehmens_profil.append(f"- Website-Zusammenfassung: {website_summary}")
if wiki_absatz and 'k.a.' not in wiki_absatz.lower():
unternehmens_profil.append(f"- Wikipedia-Auszug: {wiki_absatz}")
system_prompt_content = (
"Du bist ein Wirtschaftsanalyst, der die Branche eines Unternehmens bewertet.\n"
"Deine Aufgabe ist es, aus den untenstehenden Informationen die am besten passende Branche aus dem 'Ziel-Branchenschema' auszuwählen.\n"
"Bewerte Wikipedia-Daten und externe Branchenbeschreibungen höher als allgemeine Website-Texte.\n"
"Antworte NUR mit den folgenden drei Zeilen im exakten Format:\n"
"Branche: <Exakter Name der Branche aus der Liste>\n"
"Konfidenz: <Hoch, Mittel oder Niedrig>\n"
"Begruendung: <Sehr kurze Begründung für deine Wahl basierend auf den Quelldaten>"
)
if len(unternehmens_profil) == 1:
logger.warning(f"Zu wenige Informationen für Brancheneinstufung von '{company_name}'. Breche ab.")
return {
"branch": "FEHLER (Mangelnde Daten)",
"confidence": "N/A",
"justification": "Keine Website- oder Wikipedia-Daten zur Analyse vorhanden."
}
user_prompt_parts = [
"--- ZIEL-BRANCHENSCHEMA ---",
"\n".join(f"- {b}" for b in allowed_branches),
"\n--- UNTERNEHMENSDATEN ---"
# 2. Baue das Ziel-Branchenschema als Textblock auf
schema_text_parts = []
for i, (branch, details) in enumerate(Config.BRANCH_GROUP_MAPPING.items()):
schema_text_parts.append(f"{i+1}. Branche: {branch}")
if details.get("definition"):
schema_text_parts.append(f" Definition: {details['definition']}")
if details.get("beispiele"):
schema_text_parts.append(f" Beispiele: {details['beispiele']}")
ziel_branchenschema_text = "\n".join(schema_text_parts)
# 3. Baue den Master-Prompt zusammen
prompt_parts = [
"Du bist ein erfahrener Branchenanalyst bei einer führenden Unternehmensberatung. Deine Aufgabe ist es, ein Unternehmen präzise einer von 54 vordefinierten Branchenkategorien zuzuordnen. Nutze dafür ausschließlich die bereitgestellten Definitionen.",
"\n--- UNTERNEHMENS-PROFIL ---",
"\n".join(unternehmens_profil),
"\n--- ZIEL-BRANCHENSCHEMA (Deine einzig gültige Wissensbasis) ---",
ziel_branchenschema_text,
"\n--- DEINE AUFGABE (DENKPROZESS) ---",
"1. **Analysiere das Unternehmens-Profil:** Was ist die exakte, primäre Geschäftstätigkeit des Unternehmens?",
"2. **Vergleiche diese Tätigkeit mit ALLEN Definitionen** im Ziel-Branchenschema. Achte genau auf die Abgrenzungen.",
"3. **Identifiziere die Definition, die am besten passt.** Nutze die Beispielunternehmen zur Validierung deiner Wahl.",
"4. **Triff eine Entscheidung** und gib das Ergebnis im folgenden JSON-Format aus:",
'''
{
"Branche": "<Exakter Name der am besten passenden Branche aus dem Schema>",
"Konfidenz": "<Hoch, Mittel oder Niedrig>",
"Begruendung": "<Eine sehr kurze Begründung für deine Wahl und warum andere ähnliche Branchen (falls zutreffend) ausgeschlossen wurden.>"
}
'''
]
if crm_branche and str(crm_branche).strip() and str(crm_branche).strip().lower() != "k.a.": user_prompt_parts.append(f"- CRM-Branche (Referenz): {str(crm_branche).strip()}")
if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.":
if beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": user_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"): user_prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...")
user_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.": user_prompt_parts.append(f"- Wikipedia-Kategorien: {str(wiki_kategorien).strip()[:500]}...")
else:
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"): user_prompt_parts.append(f"- Website-Zusammenfassung (als Hauptbeschreibung): {str(website_summary).strip()[:800]}...")
elif beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": user_prompt_parts.append(f"- Beschreibung (CRM, als Hauptbeschreibung): {str(beschreibung).strip()[:800]}...")
prompt = "\n".join(prompt_parts)
full_prompt = system_prompt_content + "\n\n" + "\n".join(user_prompt_parts)
# 4. API-Aufruf
try:
chat_response = call_openai_chat(full_prompt, temperature=0.1)
if not chat_response: raise APIError("Keine Antwort von OpenAI erhalten.")
response_str = call_openai_chat(prompt, temperature=0.0, model="gpt-4o", response_format_json=True)
if not response_str:
raise APIError("Keine Antwort von OpenAI erhalten.")
response_json = json.loads(response_str)
# Validierung des Ergebnisses
final_branch = response_json.get("Branche")
if final_branch not in Config.BRANCH_GROUP_MAPPING:
logger.warning(f"KI hat eine ungültige Branche zurückgegeben: '{final_branch}'. Markiere als Fehler.")
response_json["Branche"] = "FEHLER (Ungültige Antwort)"
response_json["Begruendung"] = f"Originalantwort: {final_branch}. " + response_json.get("Begruendung", "")
return {
"branch": response_json.get("Branche", "FEHLER (Parsing)"),
"confidence": response_json.get("Konfidenz", "N/A"),
"justification": response_json.get("Begruendung", "Keine Begründung erhalten.")
}
except Exception as e:
return {"branch": "FEHLER API", "confidence": "N/A", "consistency": "error_api_failed", "justification": f"Fehler API: {str(e)[:100]}"}
lines = chat_response.strip().split("\n")
result = {"confidence": "N/A", "justification": ""}
suggested_branch = ""
for line in lines:
if line.lower().strip().startswith("branche:"):
suggested_branch = line.split(":", 1)[1].strip().strip('"\'')
break
if not suggested_branch and lines:
suggested_branch = lines[0].strip().split(":", 1)[-1].strip().strip('"\'')
for line in lines:
if line.lower().strip().startswith("konfidenz:"): result["confidence"] = line.split(":", 1)[1].strip()
elif line.lower().strip().startswith("begruendung:"): result["justification"] = line.split(":", 1)[1].strip()
if not suggested_branch:
return {"branch": "FEHLER PARSING", "confidence": "N/A", "consistency": "error_parsing", "justification": f"Antwort unklar: {chat_response[:100]}"}
final_branch = None
suggested_branch_lower = suggested_branch.lower()
if suggested_branch_lower in allowed_branches_lookup:
final_branch = allowed_branches_lookup[suggested_branch_lower]
else:
best_suggestion_match = next((val for key, val in allowed_branches_lookup.items() if suggested_branch_lower in key.lower()), None)
if best_suggestion_match: final_branch = best_suggestion_match
if final_branch:
result["branch"] = final_branch
result["consistency"] = "ok" if final_branch.lower() == crm_branche.strip().lower() else "X"
else:
crm_short_branch_lower = crm_branche.strip().lower()
best_crm_fallback = next((val for key, val in allowed_branches_lookup.items() if crm_short_branch_lower and crm_short_branch_lower in key.lower()), None)
if best_crm_fallback:
result.update({"branch": best_crm_fallback, "consistency": "fallback_crm_substring", "justification": f"Fallback: KI-Vorschlag ungültig. CRM-Branche '{crm_branche}' passt zu '{best_crm_fallback}'.", "confidence": "N/A (Fallback)"})
else:
result.update({"branch": "FEHLER - UNGUELTIGE ZUWEISUNG", "consistency": "fallback_invalid", "justification": f"Fehler: Weder KI ('{suggested_branch}') noch CRM ('{crm_branche}') passen.", "confidence": "N/A (Fehler)"})
logger.debug(f"Finale Branch-Evaluation: {result}")
return result
logger.error(f"Endgültiger FEHLER beim OpenAI-Aufruf für Brancheneinstufung von {company_name}: {e}")
return {
"branch": "FEHLER (API)",
"confidence": "N/A",
"justification": f"Fehler bei der API-Kommunikation: {str(e)[:100]}"
}
@retry_on_failure
def verify_wiki_article_chatgpt(company_name, parent_name, website, wiki_title, wiki_summary):