Files
Brancheneinstufung2/_legacy_gsheets_system/build_knowledge_base.py
Floke c6a37a3c17 feat(company-explorer): Initial Web UI & Backend with Enrichment Flow
This commit introduces the foundational elements for the new "Company Explorer" web application, marking a significant step away from the legacy Google Sheets / CLI system.

Key changes include:
- Project Structure: A new  directory with separate  (FastAPI) and  (React/Vite) components.
- Data Persistence: Migration from Google Sheets to a local SQLite database () using SQLAlchemy.
- Core Utilities: Extraction and cleanup of essential helper functions (LLM wrappers, text utilities) into .
- Backend Services: , ,  for AI-powered analysis, and  logic.
- Frontend UI: Basic React application with company table, import wizard, and dynamic inspector sidebar.
- Docker Integration: Updated  and  for multi-stage builds and sideloading.
- Deployment & Access: Integrated into central Nginx proxy and dashboard, accessible via .

Lessons Learned & Fixed during development:
- Frontend Asset Loading: Addressed issues with Vite's  path and FastAPI's .
- TypeScript Configuration: Added  and .
- Database Schema Evolution: Solved  errors by forcing a new database file and correcting  override.
- Logging: Implemented robust file-based logging ().

This new foundation provides a powerful and maintainable platform for future B2B robotics lead generation.
2026-01-07 17:55:08 +00:00

157 lines
9.0 KiB
Python

# build_knowledge_base.py
import os
import yaml
import logging
import time
import openai
import argparse
from config import Config
# --- Konfiguration ---
OUTPUT_FILE = "marketing_wissen_final.yaml"
MODEL_TO_USE = "gpt-4o"
DOSSIER_FOLDER = "industries" # Der Ordner für die generierten Branchen-Dossiers
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def call_openai_with_retry(prompt, is_extraction=False, max_retries=3, delay=5):
"""Ruft die OpenAI API auf."""
# ... (Diese Funktion bleibt unverändert, ich füge sie hier der Vollständigkeit halber ein) ...
for attempt in range(max_retries):
try:
logging.info(f"Sende Prompt an OpenAI (Länge: {len(prompt)} Zeichen)...")
response_format = {"type": "json_object"} if is_extraction else {"type": "text"}
response = openai.ChatCompletion.create(
model=MODEL_TO_USE,
response_format=response_format,
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=2048
)
content = response.choices[0].message['content'].strip()
return content
except Exception as e:
logging.error(f"Fehler bei OpenAI-API-Aufruf: {e}")
if attempt < max_retries - 1:
time.sleep(delay)
else:
return None
def generate_research_prompt(branch_name, branch_info):
"""Erstellt den Prompt, um ein Branchen-Dossier zu erstellen, basierend auf dem reichen Kontext."""
context_parts = [f"Branche: '{branch_name}'"]
if branch_info.get("definition"):
context_parts.append(f"Fokus / Abgrenzung: {branch_info['definition']}")
if branch_info.get("beispiele"):
context_parts.append(f"Beispielunternehmen: {branch_info['beispiele']}")
context_str = "\n".join(context_parts)
return (
f"Erstelle ein prägnantes Branchen-Dossier (ca. 300-400 Wörter) für die folgende, spezifische Branche:\n\n"
f"--- BRanchen-Kontext ---\n{context_str}\n\n"
"Struktur des Dossiers:\n"
"1. **Geschäftsmodelle & Field Service:** Beschreibe die typischen Geschäftsmodelle und die Rolle des Außendienstes, basierend auf dem oben genannten Fokus.\n"
"2. **Herausforderungen & Trends:** Nenne die wichtigsten Herausforderungen und Trends für den Service-Bereich in diesem spezifischen Segment.\n"
"3. **Branchenspezifisches Wording:** Liste typische Fachbegriffe auf, die in diesem Kontext üblich sind."
)
def generate_extraction_prompt(dossier_content):
"""Erstellt den Prompt, um die strukturierten Daten aus dem Dossier zu extrahieren."""
return (
"Du bist ein Branchenanalyst mit dem Spezialgebiet Field Service Management. Deine Aufgabe ist es, aus einem Branchen-Dossier die Kernaussagen zu extrahieren.\n"
"Gib das Ergebnis ausschließlich als sauberes JSON-Objekt mit den Schlüsseln 'summary', 'pain_points' (eine Liste von 5 operativen Schmerzpunkten des Außendienstes) und 'key_terms' (eine Liste von 5-7 Begriffen) aus.\n\n"
"WICHTIGE REGELN FÜR 'pain_points':\n"
"- Extrahiere 5 **operative Schmerzpunkte, die direkt den technischen Außendienst betreffen**.\n"
"- Formuliere sie als konkrete Probleme, die ein Service-Leiter lösen muss (z.B. 'Sicherstellung der Anlagenverfügbarkeit', 'Lückenlose Dokumentation für Audits').\n"
"- Vermeide allgemeine Management-Themen wie 'Komplexität der Geschäftsmodelle' oder reine HR-Themen wie 'Fachkräftemangel'.\n\n"
"--- DOSSIER ---\n"
f"{dossier_content}"
)
def main(branches_to_process=None):
"""Baut die komplette Wissensbasis auf, basierend auf den Definitionen in config.py."""
logging.info("Starte den Aufbau der vollständigen Wissensbasis...")
Config.load_api_keys()
openai.api_key = Config.API_KEYS.get('openai')
if not openai.api_key:
logging.critical("OpenAI API Key nicht gefunden.")
return
# Die finale Wissensbasis wird von Grund auf neu erstellt
knowledge_base = {
'Positionen': {
'Field Service Management': {'name_DE': 'Leiter Kundenservice / Field Service', 'pains_DE': ['Das Team ist zu klein, überlastet und gestresst, was zu hoher Fluktuation führen kann.', 'Zu viele Anrufe und ungeplante Einsätze mit zu wenigen verfügbaren Ressourcen.', 'Ineffiziente, undurchsichtige und komplexe Prozesse bei der Einsatzplanung.']},
'IT': {'name_DE': 'IT-Leiter', 'pains_DE': ['Hoher Implementierungsaufwand und unklare Gesamtkosten (TCO) bei neuen Systemen.', 'Sicherheitsbedenken und die nahtlose Integration in die bestehende IT-Infrastruktur.', 'Mangelhafte Dokumentation oder unzureichende APIs neuer Softwarelösungen.']},
'Management / GF / C-Level': {'name_DE': 'Geschäftsführer / C-Level', 'pains_DE': ['Die richtigen, zukunftssicheren Investitionsentscheidungen treffen, um wettbewerbsfähig zu bleiben.', 'Den Überblick über die operative Effizienz behalten, um Wachstum und Profitabilität zu steuern.', 'Im "War for Talents" gute Mitarbeiter finden und durch moderne Werkzeuge langfristig halten.']},
'Procurement / Einkauf': {'name_DE': 'Einkaufsleiter', 'pains_DE': ['Unklare Amortisationszeit (ROI) und versteckte Kosten einer neuen Softwarelösung.', 'Sicherstellen, dass das Preis-Leistungs-Verhältnis das beste auf dem Markt ist.', 'Das Risiko einer Fehlinvestition minimieren und vertragliche Sicherheit gewährleisten.']},
'Finanzen': {'name_DE': 'Finanzleiter / CFO', 'pains_DE': ['Schwierigkeit, die Service-Einsätze verursachungsgerecht und präzise abzurechnen.', 'Mangelnde Transparenz über die tatsächliche Profitabilität einzelner Service-Aufträge.', 'Hoher manueller Aufwand bei der Reisekostenabrechnung und Materialbuchung der Techniker.']}
},
'Branchen': {}
}
all_branches_from_config = Config.BRANCH_GROUP_MAPPING
if branches_to_process:
target_branches = {k: v for k, v in all_branches_from_config.items() if k in branches_to_process}
if not target_branches:
logging.error("Keine der angegebenen Branchen ist gültig. Bitte prüfen Sie die Schreibweise.")
return
logging.info(f"Verarbeite die {len(target_branches)} explizit angegebenen Branchen...")
else:
target_branches = all_branches_from_config
logging.info(f"Es werden alle {len(target_branches)} Branchen aus der Config verarbeitet...")
os.makedirs(DOSSIER_FOLDER, exist_ok=True)
for branch_name, branch_info in target_branches.items():
logging.info(f"\n--- Verarbeite Branche: {branch_name} ---")
research_prompt = generate_research_prompt(branch_name, branch_info)
dossier = call_openai_with_retry(research_prompt)
if not dossier: continue
try:
sanitized_branch_name = branch_name.replace('/', '-').replace('\\', '-')
dossier_filepath = os.path.join(DOSSIER_FOLDER, f"{sanitized_branch_name}.txt")
with open(dossier_filepath, 'w', encoding='utf-8') as f: f.write(dossier)
logging.info(f" -> Dossier erfolgreich in '{dossier_filepath}' gespeichert.")
except Exception as e:
logging.error(f" -> Fehler beim Speichern des Dossiers für {branch_name}: {e}")
time.sleep(1)
extraction_prompt = generate_extraction_prompt(dossier)
extracted_data_str = call_openai_with_retry(extraction_prompt, is_extraction=True)
if not extracted_data_str: continue
try:
if extracted_data_str.startswith("```"):
extracted_data_str = extracted_data_str.split('\n', 1)[1].rsplit('```', 1)[0]
extracted_data = yaml.safe_load(extracted_data_str)
# Referenzen direkt aus der Config übernehmen
extracted_data['references_DE'] = branch_info.get('beispiele', '[KEINE REFERENZEN IN CONFIG GEFUNDEN]')
extracted_data['references_GB'] = '[HIER ENGLISCHE REFERENZKUNDEN EINTRAGEN]'
knowledge_base['Branchen'][branch_name] = extracted_data
logging.info(f" -> {branch_name} erfolgreich zur Wissensbasis hinzugefügt.")
except Exception as e:
logging.error(f" Fehler beim Parsen der extrahierten Daten für {branch_name}: {e}")
time.sleep(1)
try:
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
yaml.dump(knowledge_base, f, allow_unicode=True, sort_keys=False, width=120)
logging.info(f"\nErfolgreich! Die finale Wissensbasis wurde in '{OUTPUT_FILE}' gespeichert.")
except Exception as e:
logging.error(f"Fehler beim Speichern der finalen YAML-Datei: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Baut die komplette Marketing-Wissensbasis auf.")
parser.add_argument("--branches", nargs='+', type=str, help="Eine oder mehrere spezifische Branchen, die verarbeitet werden sollen.")
args = parser.parse_args()
main(branches_to_process=args.branches)