Advanced FSM Pitch Generation
- FEATURE: Prompt für `generate_fsm_argument` in `helpers.py` durch eine mehrstufige "Chain-of-Thought"-Anweisung ersetzt. - Die KI wird nun gezwungen, spezifische Produkte/Dienstleistungen aus dem Kontext zu extrahieren, was zu hochgradig personalisierten und weniger generischen Pitch-Sätzen führt.
This commit is contained in:
141
helpers.py
141
helpers.py
@@ -998,6 +998,7 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
|
||||
def generate_fsm_argument(company_name, crm_branche, website_summary, wiki_absatz, anzahl_ma, anzahl_techniker):
|
||||
"""
|
||||
Generiert einen maßgeschneiderten, nicht-werblichen Satz, warum das Unternehmen FSM einsetzen sollte.
|
||||
Nutzt eine "Chain-of-Thought"-Anweisung für spezifischere Ergebnisse.
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1008,141 +1009,47 @@ def generate_fsm_argument(company_name, crm_branche, website_summary, wiki_absat
|
||||
techniker_info = f"bei über {anzahl_ma} Mitarbeitern"
|
||||
else:
|
||||
techniker_info = "in einem Unternehmen Ihrer Größe"
|
||||
|
||||
|
||||
# Nimm die bessere Beschreibung, wenn vorhanden
|
||||
beschreibung = website_summary if website_summary and website_summary.lower() != 'k.a.' else wiki_absatz
|
||||
|
||||
prompt_parts = [
|
||||
"Du bist ein B2B-Kommunikationsexperte, der subtile und personalisierte Gesprächseinstiege formuliert.",
|
||||
"Aufgabe: Formuliere EINEN EINZIGEN, flüssig lesbaren Satz (ca. 20-35 Wörter), der als hochpersonalisierter Einstieg in einer E-Mail an ein Unternehmen dient.",
|
||||
"Du bist ein B2B-Stratege, der die verborgenen operativen Herausforderungen eines Unternehmens erkennt und präzise auf den Punkt bringt.",
|
||||
"Aufgabe: Formuliere EINEN EINZIGEN, flüssig lesbaren Satz (ca. 20-35 Wörter), der als hochpersonalisierter Einstieg in einer E-Mail dient.",
|
||||
"Stil-Regeln:",
|
||||
"- Absolut NICHT werblich klingen. Keine Produktnamen, keine direkten Lösungsangebote.",
|
||||
"- Formuliere es als eine nachvollziehbare Beobachtung oder eine implizite Herausforderung.",
|
||||
"- Beginne den Satz natürlich, z.B. mit 'Angesichts...', 'Gerade in der...' oder 'Bei...'.",
|
||||
"- Formuliere es als eine scharfsinnige Beobachtung über die operative Tätigkeit des Unternehmens.",
|
||||
"- Der Satz muss spezifische Keywords aus der Unternehmensbeschreibung aufgreifen.",
|
||||
|
||||
"\n--- Denkprozess (Schritt für Schritt): ---",
|
||||
"1. Analysiere die untenstehenden Unternehmensdaten.",
|
||||
"2. Identifiziere die **spezifischste genannte Dienstleistung oder das spezifischste Produkt** (z.B. 'Installation von Wärmepumpen', 'Wartung von Aufzügen', 'Verlegung von Glasfaser').",
|
||||
"3. Leite daraus die **konkrete operative Tätigkeit** ab, die von mobilen Teams durchgeführt wird (z.B. 'Installations-Termine', 'Störungsbehebungen', 'Wartungsarbeiten').",
|
||||
"4. Formuliere den finalen Satz, der diese spezifische Tätigkeit und die Personalinfo elegant verbindet.",
|
||||
|
||||
"\n--- Unternehmenskontext ---",
|
||||
f"Unternehmen: {company_name}",
|
||||
f"Branche: {crm_branche}",
|
||||
f"Beschreibung: {website_summary if website_summary and website_summary.lower() != 'k.a.' else wiki_absatz}",
|
||||
f"Beschreibung: {beschreibung}",
|
||||
f"Personalinfo für den Satz: {techniker_info}",
|
||||
"\n--- Beispiele für den gewünschten Stil ---",
|
||||
"Beispiel 1 (Stadtwerke): Angesichts des wachsenden Bedarfs an Ladeinfrastruktur ist die effiziente Koordination Ihrer mobilen Teams entscheidend für einen schnellen und bürgernahen Service.",
|
||||
"Beispiel 2 (Anlagenbau): Gerade im Anlagenbau hängt die Kundenzufriedenheit direkt von der reibungslosen Abstimmung der Serviceeinsätze Ihrer Techniker ab, um Stillstandzeiten zu minimieren.",
|
||||
|
||||
"\n--- Beispiele für den gewünschten Output-Stil ---",
|
||||
"Beispiel 1 (Kontext: Branche 'Energie', Beschreibung '...baut Ladeinfrastruktur aus...'): Angesichts des beschleunigten Ausbaus der Ladeinfrastruktur ist die reibungslose Koordination der Installationstermine Ihrer mobilen Teams entscheidend für den Projekterfolg.",
|
||||
"Beispiel 2 (Kontext: Branche 'Anlagenbau', Beschreibung '...Service für Produktionsanlagen...'): Bei der Wartung komplexer Produktionsanlagen hängt die Kundenzufriedenheit direkt von der pünktlichen und effizienten Durchführung der Serviceeinsätze ab.",
|
||||
|
||||
"\n--- Deine Aufgabe ---",
|
||||
"Formuliere jetzt den EINEN perfekten Satz für das oben genannte Unternehmen:",
|
||||
"Führe den Denkprozess durch und gib NUR den finalen, perfekten Satz für das oben genannte Unternehmen aus:",
|
||||
]
|
||||
|
||||
prompt = "\n".join(prompt_parts)
|
||||
|
||||
try:
|
||||
fsm_pitch = call_openai_chat(prompt, temperature=0.75)
|
||||
fsm_pitch = call_openai_chat(prompt, temperature=0.7)
|
||||
return fsm_pitch.strip().replace('"', '') if fsm_pitch else "k.A. (Pitch-Generierung fehlgeschlagen)"
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der Generierung des FSM-Pitches für {company_name}: {e}")
|
||||
return "k.A. (Fehler bei Pitch-Generierung)"
|
||||
|
||||
# ==============================================================================
|
||||
# 10. SERP API WRAPPERS (WIKIPEDIA, WEBSITE, LINKEDIN)
|
||||
# ==============================================================================
|
||||
|
||||
@retry_on_failure
|
||||
def serp_wikipedia_lookup(company_name, website=None, min_score=0.4):
|
||||
"""
|
||||
Sucht ueber SerpAPI (Google) nach dem wahrscheinlichsten Wikipedia-Artikel.
|
||||
Gibt die URL als String oder None bei Fehler/Nicht-Fund zurück.
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
serp_key = Config.API_KEYS.get('serpapi')
|
||||
if not serp_key:
|
||||
# Kein harter Fehler mehr, nur eine Warnung und Rückgabe von None
|
||||
logger.warning("SerpAPI Key nicht konfiguriert. Wikipedia-Suche via SerpAPI übersprungen.")
|
||||
return None
|
||||
|
||||
if not company_name or str(company_name).strip() == "":
|
||||
logger.warning("serp_wikipedia_lookup: Kein Firmenname angegeben.")
|
||||
return None
|
||||
|
||||
query = f'{company_name} Wikipedia'
|
||||
if website and simple_normalize_url(website) != "k.A.":
|
||||
query = f'{company_name} Wikipedia {simple_normalize_url(website)}'
|
||||
logger.info(f"Starte SerpAPI Wikipedia-Suche fuer '{company_name}' mit Query: '{query[:100]}...'")
|
||||
|
||||
params = {
|
||||
"engine": "google",
|
||||
"q": query,
|
||||
"api_key": serp_key,
|
||||
"hl": "de", "gl": "de", "num": 10
|
||||
}
|
||||
api_url = "https://serpapi.com/search"
|
||||
|
||||
try:
|
||||
response = requests.get(api_url, params=params, timeout=getattr(Config, 'REQUEST_TIMEOUT', 15))
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
candidates = []
|
||||
if "organic_results" in data:
|
||||
logger.debug(f" -> Pruefe {len(data['organic_results'])} organische Ergebnisse...")
|
||||
for result in data["organic_results"]:
|
||||
link = result.get("link")
|
||||
if link and isinstance(link, str) and "wikipedia.org/wiki/" in link.lower() \
|
||||
and (link.lower().startswith("https://de.wikipedia.org") or link.lower().startswith("https://en.wikipedia.org")) \
|
||||
and not any(x in link.lower() for x in ['datei:', 'spezial:', 'portal:', 'hilfe:', 'diskussion:', 'template:']):
|
||||
try:
|
||||
title_part = link.split('/wiki/', 1)[1].split('#')[0]
|
||||
title = unquote(title_part).replace('_', ' ')
|
||||
candidates.append({'url': link, 'title': title})
|
||||
except Exception as e_title_extract:
|
||||
logger.debug(f" -> Fehler beim Extrahieren des Titels aus Link {link[:100]}...: {e_title_extract}")
|
||||
continue
|
||||
|
||||
if not candidates:
|
||||
logger.warning(f" -> SerpAPI: Keine de/en Wikipedia-Kandidaten-URLs in Ergebnissen fuer '{company_name}' gefunden.")
|
||||
return None
|
||||
|
||||
best_match_url = None
|
||||
highest_score = -1.0
|
||||
normalized_search_name = normalize_company_name(company_name)
|
||||
|
||||
logger.debug(f" -> Bewerte {len(candidates)} Kandidaten...")
|
||||
for cand in candidates:
|
||||
url, title = cand['url'], cand['title']
|
||||
try:
|
||||
normalized_title = normalize_company_name(title)
|
||||
title_lower = title.lower()
|
||||
except Exception as e_norm:
|
||||
logger.warning(f"Fehler beim Normalisieren des Titels '{title[:100]}...': {e_norm}. Ueberspringe Kandidatenbewertung.")
|
||||
continue
|
||||
|
||||
similarity = SequenceMatcher(None, normalized_title, normalized_search_name).ratio()
|
||||
score = similarity
|
||||
bonus = 0.0
|
||||
|
||||
if normalized_search_name and normalized_title and (normalized_search_name in normalized_title or normalized_title in normalized_search_name):
|
||||
bonus += 0.3
|
||||
if "(unternehmen)" in title_lower:
|
||||
bonus += 0.15
|
||||
elif re.search(r'\b(?:gmbh|ag|kg|ltd|inc|corp|s\.?a\.?|se|group|holding)\b$', title_lower):
|
||||
bonus += 0.05
|
||||
if url.lower().startswith("https://de.wikipedia.org"):
|
||||
bonus += 0.05
|
||||
|
||||
total_score = score + bonus
|
||||
logger.debug(f" -> Gesamtscore fuer '{title[:100]}...': {total_score:.3f} (Aehnlichkeit={similarity:.2f}, Bonus={bonus:.2f})")
|
||||
|
||||
if total_score > highest_score and total_score >= min_score:
|
||||
highest_score = total_score
|
||||
best_match_url = url
|
||||
logger.debug(f" ====> Neuer bester Kandidat: {best_match_url[:100]}... (Score: {highest_score:.3f}) ====")
|
||||
|
||||
if best_match_url:
|
||||
logger.info(f" -> SerpAPI: Bester relevanter Wikipedia-Link ausgewaehlt: {best_match_url[:100]}... (Score: {highest_score:.3f})")
|
||||
return best_match_url
|
||||
else:
|
||||
logger.warning(f" -> SerpAPI: Keiner der {len(candidates)} Kandidaten erreichte den Mindestscore ({min_score}) fuer '{company_name}'.")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"FEHLER bei der SerpAPI Wikipedia Suche fuer '{company_name}': {e}")
|
||||
# Wir geben die Exception nicht mehr weiter, damit das Programm nicht abbricht
|
||||
return None
|
||||
|
||||
|
||||
@retry_on_failure
|
||||
def serp_website_lookup(company_name):
|
||||
|
||||
Reference in New Issue
Block a user