feat: Enhanced Outreach UI - Top 5 default + specific role generation

- market_intel_orchestrator.py: Updated generate_outreach_campaign to identify all relevant roles, generate top 5, and return remaining as suggestions. Added specific_role mode.

- types.ts: Added OutreachResponse interface.

- geminiService.ts: Updated to handle new response structure and specificRole parameter.

- StepOutreach.tsx: Added sidebar section for Suggested Roles with on-demand generation buttons.
This commit is contained in:
2025-12-29 13:59:20 +00:00
parent 4e05bac827
commit 4b79f1aee2
4 changed files with 211 additions and 125 deletions

View File

@@ -544,25 +544,64 @@ def analyze_company(company_name, strategy, target_market):
"dataSource": "Error"
}
def generate_outreach_campaign(company_data_json, knowledge_base_content, reference_url):
def generate_outreach_campaign(company_data_json, knowledge_base_content, reference_url, specific_role=None):
"""
Erstellt personalisierte E-Mail-Kampagnen basierend auf Audit-Daten und einer strukturierten Wissensdatenbank.
Generiert spezifische Ansprachen für verschiedene Rollen (Personas).
Erstellt personalisierte E-Mail-Kampagnen.
Modus A (Default): Generiert Top 5 Kampagnen + Liste weiterer relevanter Rollen.
Modus B (specific_role): Generiert nur Kampagne für diese eine Rolle.
"""
company_name = company_data_json.get('companyName', 'Unknown')
logger.info(f"--- STARTING ROLE-BASED OUTREACH GENERATION FOR: {company_name} ---")
logger.info(f"--- STARTING OUTREACH GENERATION FOR: {company_name} (Role: {specific_role if specific_role else 'Top 5'}) ---")
api_key = load_gemini_api_key()
# Switch to stable 2.5-pro model
GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key={api_key}"
if specific_role:
# --- MODE B: SINGLE ROLE GENERATION ---
task_description = f"""
--- TASK ---
1. **Focus**: Create a highly specific 3-step email campaign ONLY for the role: '{specific_role}'.
2. **Analyze**: Use the Audit Facts to find specific hooks for this role.
3. **Draft**: Write the sequence (Opening, Follow-up, Break-up).
"""
output_format = """
--- OUTPUT FORMAT (Strictly JSON) ---
{
"target_role": "The requested role",
"rationale": "Why this fits...",
"emails": [ ... ]
}
"""
else:
# --- MODE A: INITIAL BATCH (TOP 5 + SUGGESTIONS) ---
task_description = f"""
--- TASK ---
1. **Analyze**: Match the Target Company (Input 2) to the most relevant 'Zielbranche/Segment' from the Knowledge Base (Input 1).
2. **Identify Roles**: Identify ALL relevant 'Rollen' (Personas) from the Knowledge Base that fit this company.
3. **Select Top 5**: Choose the 5 most promising roles for immediate outreach based on the Audit findings.
4. **Draft Campaigns**: For EACH of the Top 5 roles, write a 3-step email sequence.
5. **List Others**: List the names of the other relevant roles that you identified but did NOT generate campaigns for yet.
"""
output_format = """
--- OUTPUT FORMAT (Strictly JSON) ---
{
"campaigns": [
{
"target_role": "Role Name",
"rationale": "Why selected...",
"emails": [ ... ]
},
... (Top 5)
],
"available_roles": [ "Role 6", "Role 7", ... ]
}
"""
prompt = f"""
You are a Strategic Key Account Manager and deeply technical Industry Insider.
Your goal is to write highly personalized, **operationally specific** outreach emails to the company '{company_name}'.
--- INPUT 1: YOUR IDENTITY & STRATEGY (The Sender) ---
The following Markdown contains your company's identity, products, and strategy.
You act as the sales representative for the company described here:
{knowledge_base_content}
--- INPUT 2: THE TARGET COMPANY (Audit Facts) ---
@@ -571,52 +610,21 @@ def generate_outreach_campaign(company_data_json, knowledge_base_content, refere
--- INPUT 3: THE REFERENCE CLIENT (Social Proof) ---
Reference Client URL: {reference_url}
CRITICAL: This 'Reference Client' is an existing happy customer of ours. They are the "Seed Company" used to find the Target Company (Lookalike).
You MUST mention this Reference Client by name (derive it from the URL, e.g., 'schindler.com' -> 'Schindler') to establish trust.
CRITICAL: This 'Reference Client' is an existing happy customer of ours. You MUST mention them by name to establish trust.
--- TASK ---
1. **Analyze**: Match the Target Company (Input 2) to the most relevant 'Zielbranche/Segment' from the Knowledge Base (Input 1).
2. **Select Roles**: Identify **up to 5** of the most distinct and relevant 'Rollen' (Personas) from the Knowledge Base for this specific company situation.
- Prioritize roles where the audit findings (e.g., specific competitor tech, growth pains) offer the strongest hook.
- *Example:* If the audit says they use a competitor (risk of lock-in), select a role like "Strategic Purchaser" or "Head of R&D".
3. **Draft Campaigns**: For **EACH** of the selected roles, write a 3-step email sequence.
{task_description}
--- TONE & STYLE GUIDELINES (CRITICAL) ---
- **Perspective:** Operational Expert & Insider. NOT generic marketing.
- **Be Gritty & Specific:** Do NOT use fluff like "optimize efficiency" or "streamline processes" without context.
- Use **hard, operational keywords** from the Knowledge Base (e.g., "ASNs", "VMI", "8D-Reports", "Maverick Buying", "Bandstillstand", "Sonderfahrten", "PPAP").
- Show you understand their daily pain.
- **Be Gritty & Specific:** Use hard, operational keywords from the Knowledge Base (e.g., "ASNs", "8D-Reports").
- **Narrative Arc:**
1. "I noticed [Fact from Audit/Tech Stack]..." (e.g., "You rely on PDF orders via Jaggaer...")
2. "In [Industry], this often leads to [Operational Pain]..." (e.g., "missing ASNs causing delays at the hub.")
3. "We helped [Reference Client Name] solve exactly this by [Specific Solution]..."
4. "Let's discuss how to get [Operational Gain] without replacing your ERP."
- **Mandatory Social Proof:** You MUST mention the Reference Client Name (from Input 3) in the email body or footer.
- **Language:** German (as the inputs are German).
1. "I noticed [Fact from Audit]..."
2. "In [Industry], this often leads to [Pain]..."
3. "We helped [Reference Client] solve this..."
4. "Let's discuss [Gain]."
- **Language:** German.
--- OUTPUT FORMAT (Strictly JSON) ---
Returns a list of campaigns.
[
{{
"target_role": "Name of the Role (e.g. Leiter F&E)",
"rationale": "Why this role? (e.g. Because the audit found dependency on Competitor X...)",
"emails": [
{{
"subject": "Specific Subject Line",
"body": "Email Body..."
}},
{{
"subject": "Re: Subject",
"body": "Follow-up Body..."
}},
{{
"subject": "Final Check",
"body": "Final Body..."
}}
]
}},
... (Second Role)
]
{output_format}
"""
payload = {
@@ -626,12 +634,10 @@ def generate_outreach_campaign(company_data_json, knowledge_base_content, refere
try:
logger.info("Sende Campaign-Anfrage an Gemini API...")
# logger.debug(f"Rohe Gemini API-Anfrage (JSON): {json.dumps(payload, indent=2)}")
response = requests.post(GEMINI_API_URL, json=payload, headers={'Content-Type': 'application/json'})
response.raise_for_status()
response_data = response.json()
logger.info(f"Gemini API-Antwort erhalten (Status: {response.status_code}).")
# logger.debug(f"Rohe API-Antwort (JSON): {json.dumps(response_data, indent=2)}")
text = response_data['candidates'][0]['content']['parts'][0]['text']
result = _extract_json_from_text(text)
@@ -642,7 +648,7 @@ def generate_outreach_campaign(company_data_json, knowledge_base_content, refere
return result
except Exception as e:
logger.error(f"Campaign generation failed for {company_name}: {e}")
return [{"error": str(e)}]
return {"error": str(e)}
def main():
parser = argparse.ArgumentParser()
@@ -653,7 +659,8 @@ def main():
parser.add_argument("--company_name")
parser.add_argument("--strategy_json")
parser.add_argument("--summary_of_offer")
parser.add_argument("--company_data_file") # For generate_outreach
parser.add_argument("--company_data_file")
parser.add_argument("--specific_role") # New argument
args = parser.parse_args()
if args.mode == "generate_strategy":
@@ -671,7 +678,7 @@ def main():
elif args.mode == "generate_outreach":
with open(args.company_data_file, "r") as f: company_data = json.load(f)
with open(args.context_file, "r") as f: knowledge_base = f.read()
print(json.dumps(generate_outreach_campaign(company_data, knowledge_base, args.reference_url)))
print(json.dumps(generate_outreach_campaign(company_data, knowledge_base, args.reference_url, args.specific_role)))
if __name__ == "__main__":