Files
Brancheneinstufung2/gtm_architect_orchestrator.py
Floke 2b7c072ddc feat(gtm): Implement Meta-Framework for strategic analysis
Refactors the GTM orchestrator prompts (phases 2-9) to use a question-based strategic framework derived from the internal marketing blueprint. This new 'Meta-Framework' approach ensures strategic depth and prevents content pollution from irrelevant examples when analyzing new product categories.

- Updates orchestrator prompts in .
- Adds documentation in  explaining how to modify the new strategy logic.
- Includes minor fixes to the Node.js  and dependency updates in .
2026-01-14 15:34:15 +00:00

826 lines
39 KiB
Python

import argparse
import base64
import json
import logging
import re
import sys
import os
import requests
from bs4 import BeautifulSoup
from datetime import datetime
from config import Config
import gtm_db_manager as db_manager
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from helpers import call_gemini_flash, scrape_website_details, call_gemini_image
from config import Config, BASE_DIR # Import Config and BASE_DIR
LOG_DIR = "Log_from_docker"
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
ORCHESTRATOR_VERSION = "1.3.0" # Bump version for image fix & language enforcement
run_timestamp = datetime.now().strftime("%y-%m-%d_%H-%M-%S")
log_file_path = os.path.join(LOG_DIR, f"{run_timestamp}_gtm_orchestrator_run.log")
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file_path, mode='a', encoding='utf-8'),
logging.StreamHandler(sys.stderr)
]
)
logging.info(f"GTM Architect Orchestrator v{ORCHESTRATOR_VERSION} ({run_timestamp}) starting...")
# !!! CRITICAL FIX: Load API keys at the very beginning !!!
# This ensures Config.API_KEYS is populated before any AI functions are called.
Config.load_api_keys()
def log_and_save(project_id, step_name, data_type, content):
logging.info(f"Project {project_id} - Step: {step_name} - Type: {data_type}")
filename = f"{run_timestamp}_{step_name}_{data_type}.txt"
filepath = os.path.join(LOG_DIR, filename)
try:
with open(filepath, 'w', encoding='utf-8') as f:
if isinstance(content, (dict, list)):
json.dump(content, f, indent=4, ensure_ascii=False)
else:
f.write(str(content))
logging.info(f"Saved {data_type} to {filepath}")
except Exception as e:
logging.error(f"Failed to save {data_type} to file: {e}")
def get_system_instruction(lang):
if lang == 'de':
return """
Du bist ein internationaler Go-to-Market (GTM) Experte für B2B-Technologie-Unternehmen im Bereich Robotik, Facility Management und IoT.
Deine Aufgabe ist es, aus technischen Spezifikationen und Produktbeschreibungen eine umfassende GTM-Strategie zu entwickeln.
Du arbeitest strukturiert, datengetrieben und präzise. Deine Antworten sind immer klar, professionell und direkt auf den Punkt.
Wenn du JSON ausgeben sollst, gib NUR das JSON-Objekt aus, ohne umschließende Text- oder Code-Formatierungen.
Behalte während des gesamten Prozesses eine konsistente Logik bei. Alle Phasen bauen aufeinander auf.
Führe eine interne Plausibilitätsprüfung durch, bevor du eine Antwort gibst.
# CONTEXT: THE WACKLER GROUP ECOSYSTEM
Wir sind Teil der Wackler Group. Wir nutzen das gesamte Dienstleistungsportfolio der Muttergesellschaft, um Hardware-Schwächen in Service-Stärken zu verwandeln.
Unsere Sparten für die Symbiose:
1. Wackler Cleaning: Für Reinigungsroboter (Manuelle Nacharbeit, Winterdienst).
2. Wackler Security: Für Inspektions- & Sicherheitsroboter (Aufschaltung auf NSL/Leitstelle, Revierwachdienst, Intervention).
# REGEL 5: THE "DYNAMIC HYBRID SERVICE" LOGIC
Analysiere zuerst die **Kategorie** des Roboters und wende dann die passende Hybrid-Logik an:
FALL A: PRODUKT IST EIN REINIGUNGSROBOTER (z.B. Kehrmaschine)
* Robot Role: Monotone Flächenleistung (80%).
* Human Role (Wackler Cleaning): Kanten, Winterdienst, Sonderreinigung (20%).
* Pitch: "100% Sauberkeit durch Maschine + Mensch."
FALL B: PRODUKT IST EIN SICHERHEITS-/INSPEKTIONSROBOTER (z.B. Quadruped, Drohne)
* Robot Role: "Detektion & Präsenz". Der Roboter patrouilliert, nutzt Wärmebild/Sensoren, geht in Gefahrenbereiche. Er wird nicht müde und hat keine Angst.
* Human Role (Wackler Security): "Bewertung & Intervention".
* NSL (Notruf- & Serviceleitstelle): Bewertet den Video-Feed des Roboters bei Alarm.
* Revierwachdienst: Fährt raus zur Intervention, wenn der Roboter einen Eindringling oder Schaden meldet.
* Pitch: "Der Roboter sieht die Gefahr, Wackler beseitigt sie. (Automated Perimeter Protection)."
Wende diese Logik zwingend in PHASE 4 (Strategy) und PHASE 6 (Sales Enablement) an.
Wenn der Roboter ein Sicherheitsroboter ist, adressiere "Head of Security" und "Werkschutzleiter" als Buyer Persona.
WICHTIG: Antworte IMMER in der vom User geforderten Sprache (Deutsch), auch wenn der Input Englisch ist.
"""
else: # Default to English
return """
You are an international Go-to-Market (GTM) expert for B2B technology companies in robotics, facility management, and IoT.
Your task is to develop a comprehensive GTM strategy from technical specifications and product descriptions.
You are structured, data-driven, and precise. Your answers are always clear, professional, and to the point.
When asked to output JSON, provide ONLY the JSON object without any surrounding text or code formatting.
Maintain consistent logic throughout the process. All phases build on each other.
Perform an internal plausibility check before providing an answer.
# CONTEXT: THE WACKLER GROUP ECOSYSTEM
We are part of the Wackler Group. We leverage the full service portfolio of the parent company to turn hardware weaknesses into service strengths.
Our divisions for symbiosis:
1. Wackler Cleaning: For cleaning robots (manual rework, winter service).
2. Wackler Security: For inspection & security robots (connection to NSL/control center, mobile patrol service, intervention).
# RULE 5: THE "DYNAMIC HYBRID SERVICE" LOGIC
First analyze the **category** of the robot and then apply the appropriate hybrid logic:
CASE A: PRODUCT IS A CLEANING ROBOT (e.g. Sweeper)
* Robot Role: Monotonous area performance (80%).
* Human Role (Wackler Cleaning): Edges, winter service, special cleaning (20%).
* Pitch: "100% Cleanliness through Machine + Human."
CASE B: PRODUCT IS A SECURITY/INSPECTION ROBOT (e.g. Quadruped, Drone)
* Robot Role: "Detection & Presence". The robot patrols, uses thermal imaging/sensors, enters hazardous areas. It does not get tired and has no fear.
* Human Role (Wackler Security): "Evaluation & Intervention".
* NSL (Emergency & Service Control Center): Evaluates the robot's video feed in case of alarm.
* Mobile Patrol: Drives out for intervention if the robot reports an intruder or damage.
* Pitch: "The robot sees the danger, Wackler eliminates it. (Automated Perimeter Protection)."
Mandatory application of this logic in PHASE 4 (Strategy) and PHASE 6 (Sales Enablement).
If the robot is a security robot, address "Head of Security" and "Plant Security Manager" as Buyer Persona.
"""
def get_output_lang_instruction(lang):
"""Returns a strong instruction to enforce the output language."""
if lang == 'de':
return "ACHTUNG: Die gesamte Ausgabe (JSON-Werte, Texte, Analysen) MUSS in DEUTSCH sein. Übersetze englische Input-Daten."
return "IMPORTANT: The entire output MUST be in ENGLISH."
# --- ORCHESTRATOR PHASES ---
def list_history(payload):
projects = db_manager.get_all_projects()
return {"projects": projects}
def load_history(payload):
project_id = payload.get('projectId')
if not project_id:
raise ValueError("No projectId provided for loading history.")
data = db_manager.get_project_data(project_id)
if not data:
raise ValueError(f"Project {project_id} not found.")
# FIX: Check for and parse stringified JSON in phase results
if 'phases' in data and isinstance(data['phases'], dict):
for phase_name, phase_result in data['phases'].items():
if isinstance(phase_result, str):
try:
data['phases'][phase_name] = json.loads(phase_result)
except json.JSONDecodeError:
logging.warning(f"Could not decode JSON for {phase_name} in project {project_id}. Leaving as is.")
return data
def delete_session(payload):
project_id = payload.get('projectId')
if not project_id:
raise ValueError("No projectId provided for deletion.")
return db_manager.delete_project(project_id)
def phase1(payload):
product_input = payload.get('productInput', '')
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
# Check if input is a URL and scrape it
if product_input.strip().startswith('http'):
logging.info(f"Input detected as URL. Starting scrape for: {product_input}")
analysis_content = scrape_website_details(product_input)
if "Fehler:" in analysis_content:
# If scraping fails, use the URL itself with a note for the AI.
analysis_content = f"Scraping der URL {product_input} ist fehlgeschlagen. Analysiere das Produkt basierend auf der URL und deinem allgemeinen Wissen."
logging.warning("Scraping failed. Using URL as fallback content for analysis.")
else:
analysis_content = product_input
logging.info("Input is raw text. Analyzing directly.")
# AUTOMATISCHE PROJEKTERSTELLUNG
if not project_id:
# Generiere Namen aus Input
raw_name = product_input.strip()
if raw_name.startswith('http'):
name = f"Web Analysis: {raw_name[:30]}..."
else:
name = (raw_name[:30] + "...") if len(raw_name) > 30 else raw_name
logging.info(f"Creating new project: {name}")
new_proj = db_manager.create_project(name)
project_id = new_proj['id']
logging.info(f"New Project ID: {project_id}")
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS
Input: "{analysis_content}"
Task:
1. Extract and CONSOLIDATE technical features into 8-12 high-level core capabilities or value propositions. Group minor specs (e.g., specific ports like USB/Ethernet) into broader categories (e.g., "Connectivity & Integration"). Do NOT list every single hardware spec individually. Focus on what matters for the buyer.
2. Define hard constraints (e.g., physical dimensions, max payload, environment limitations).
3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000").
{lang_instr}
Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}}
"""
log_and_save(project_id, "phase1", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase1", "response", response)
try:
data = json.loads(response)
# --- PART 2: HARD FACTS EXTRACTION ---
spec_schema = """
{
"metadata": {
"product_id": "string (slug)",
"brand": "string",
"model_name": "string",
"description": "string (short marketing description of the product)",
"category": "cleaning | service | security | industrial",
"manufacturer_url": "string"
},
"core_specs": {
"battery_runtime_min": "integer (standardized to minutes)",
"charge_time_min": "integer (standardized to minutes)",
"weight_kg": "float",
"dimensions_cm": { "l": "float", "w": "float", "h": "float" },
"max_slope_deg": "float",
"ip_rating": "string",
"climb_height_cm": "float",
"navigation_type": "string (e.g. SLAM, LiDAR, VSLAM)",
"connectivity": ["string"]
},
"layers": {
"cleaning": {
"fresh_water_l": "float",
"dirty_water_l": "float",
"area_performance_sqm_h": "float",
"mop_pressure_kg": "float"
},
"service": {
"max_payload_kg": "float",
"number_of_trays": "integer",
"display_size_inch": "float",
"ads_capable": "boolean"
},
"security": {
"camera_types": ["string"],
"night_vision": "boolean",
"gas_detection": ["string"],
"at_interface": "boolean"
}
},
"extended_features": [
{ "feature": "string", "value": "string", "unit": "string" }
]
}
"""
specs_prompt = f"""
PHASE 1 (Part 2): HARD FACT EXTRACTION
Input: "{analysis_content}"
Task: Extract technical specifications strictly according to the provided JSON schema.
NORMALIZATION RULES (STRICTLY FOLLOW):
1. Time: Convert ALL time values (runtime, charging) to MINUTES (Integer). Example: "1:30 h" -> 90, "2 hours" -> 120.
2. Dimensions/Weight: All lengths in CM, weights in KG.
3. Performance: Area performance always in m²/h.
4. Booleans: Use true/false (not strings).
5. Unknowns: If a value is not in the text, set it to null. DO NOT HALLUCINATE.
LOGIC FOR LAYERS:
- If product uses water/brushes -> Fill 'layers.cleaning'.
- If product delivers items/trays -> Fill 'layers.service'.
- If product patrols/detects -> Fill 'layers.security'.
EXTENDED FEATURES:
- Put any technical feature that doesn't fit the schema into 'extended_features'.
Output JSON format ONLY based on this schema:
{spec_schema}
"""
log_and_save(project_id, "phase1_specs", "prompt", specs_prompt)
specs_response = call_gemini_flash(specs_prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase1_specs", "response", specs_response)
try:
specs_data = json.loads(specs_response)
# FORCE URL PERSISTENCE: If input was a URL, ensure it's in the metadata
if product_input.strip().startswith('http'):
if 'metadata' not in specs_data:
specs_data['metadata'] = {}
specs_data['metadata']['manufacturer_url'] = product_input.strip()
# AUTO-RENAME PROJECT based on extracted metadata
if 'metadata' in specs_data:
brand = specs_data['metadata'].get('brand', '')
model = specs_data['metadata'].get('model_name', '')
if brand or model:
new_name = f"{brand} {model}".strip()
if new_name:
logging.info(f"Renaming project {project_id} to: {new_name}")
db_manager.update_project_name(project_id, new_name)
data['specs'] = specs_data
except json.JSONDecodeError:
logging.error(f"Failed to decode JSON from Gemini response in phase1 (specs): {specs_response}")
data['specs'] = {"error": "Failed to extract specs", "raw": specs_response}
db_manager.save_gtm_result(project_id, 'phase1_result', json.dumps(data))
# WICHTIG: ID zurückgeben, damit Frontend sie speichert
data['projectId'] = project_id
return data
except json.JSONDecodeError:
logging.error(f"Failed to decode JSON from Gemini response in phase1: {response}")
error_response = {
"error": "Die Antwort des KI-Modells war kein gültiges JSON. Das passiert manchmal bei hoher Auslastung. Bitte versuchen Sie es in Kürze erneut.",
"details": response,
"projectId": project_id # Auch bei Fehler ID zurückgeben? Besser nicht, da noch nichts gespeichert.
}
return error_response
def phase2(payload):
phase1_data = payload.get('phase1Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 2: IDEAL CUSTOMER PROFILE (ICP) & DATA PROXIES - STRATEGIC ANALYSIS
**Product Context:**
{json.dumps(phase1_data)}
**Your Task:**
Answer the following strategic questions to determine the Ideal Customer Profiles (ICPs).
**Strategic Questions:**
1. **ICP Identification:** Based on the product's core capabilities, which 3 industries face the most significant operational challenges (e.g., safety, efficiency, high manual labor costs, security risks) that this product directly solves? Think about where the economic or safety impact is highest.
2. **Rationale:** For each identified ICP, provide a concise rationale. Why is this product a perfect fit for this specific industry? (e.g., "Reduces inspection costs by X%", "Improves safety in hazardous environments", "Automates a critical but repetitive task").
3. **Data Proxies:** How can we find these companies online? What specific digital footprints (data proxies) do they leave? Think about:
* Keywords on their websites (e.g., 'plant safety', 'autonomous inspection', 'logistics automation').
* Specific job titles on LinkedIn (e.g., 'Head of Security', 'Logistics Manager', 'Maintenance Lead').
* Their participation in specific industry trade shows or publications.
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"icps": [{{"name": "Industry Name", "rationale": "Why it's a fit."}}], "dataProxies": [{{"target": "e.g., Company Websites", "method": "How to find them."}}]}}
"""
log_and_save(project_id, "phase2", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase2", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase2_result', json.dumps(data))
return data
def phase3(payload):
phase2_data = payload.get('phase2Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 3: WHALE HUNTING & BUYING CENTER ANALYSIS - STRATEGIC ANALYSIS
**Target ICPs (Industries):**
{json.dumps(phase2_data.get('icps'))}
**Your Task:**
Answer the following strategic questions to identify key accounts and decision-makers.
**Strategic Questions:**
1. **Whale Identification:** For each ICP, identify 3-5 specific 'Whale' companies in the DACH market. These should be leaders, innovators, or companies with significant scale in that sector.
2. **Buying Center Roles:** For a technology investment of this scale, define the key roles within a typical customer's Buying Center. Use these specific archetypes and identify plausible job titles for each:
* **Decider:** The person with the final budget authority (e.g., Plant Manager, CEO, Head of Operations).
* **Evaluator:** The technical expert who vets the solution (e.g., Head of Engineering, IT Security Lead, Facility Manager).
* **User:** The person or team who will use the product day-to-day (e.g., Security Staff, Maintenance Crew).
* **Influencer:** A person with a strong opinion who can sway the decision (e.g., Head of Safety, Innovation Manager).
* **Gatekeeper:** A person who controls the flow of information (e.g., IT Administration, Purchasing Department).
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"whales": [{{"industry": "ICP Name", "accounts": ["Company A", "Company B"]}}], "roles": ["Role Archetype: Plausible Job Title 1, Plausible Job Title 2"]}}
"""
log_and_save(project_id, "phase3", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase3", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase3_result', json.dumps(data))
return data
def phase4(payload):
phase3_data = payload.get('phase3Data', {})
phase1_data = payload.get('phase1Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
all_accounts = []
for w in phase3_data.get('whales', []):
all_accounts.extend(w.get('accounts', []))
prompt = f"""
PHASE 4: STRATEGY & ANGLE DEVELOPMENT - STRATEGIC ANALYSIS
**Target Industries & Companies:**
{json.dumps([w.get('industry') for w in phase3_data.get('whales', [])])}
**Product Features:**
{json.dumps(phase1_data.get('features'))}
**Your Task:**
Answer the following strategic questions to build the core of our market approach.
**Strategic Questions:**
1. **Pain Point Analysis:** For each industry segment, what is the single most significant, measurable **Pain Point** this product solves? Frame it from the customer's operational perspective (e.g., "High rate of safety incidents during manual inspections", "Production downtime due to undetected equipment failure", "Inefficient inventory tracking").
2. **Develop the Angle:** What is our unique story or perspective for this segment? The "Angle" should directly connect a product capability to their primary pain point.
3. **Define Differentiation:** Why should they choose us over a competitor or simply doing nothing? You **must** connect this to our unique "Dynamic Hybrid Service" logic (the combination of robot capabilities and human expert services).
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"strategyMatrix": [{{"segment": "Target Industry", "painPoint": "The core problem.", "angle": "Our unique story.", "differentiation": "Why us (Hybrid Service)."}}]}}
"""
log_and_save(project_id, "phase4", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase4", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase4_result', json.dumps(data))
return data
def phase5(payload):
phase4_data = payload.get('phase4Data', {})
phase3_data = payload.get('phase3Data', {})
phase2_data = payload.get('phase2Data', {})
phase1_data = payload.get('phase1Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
# Logging zur Diagnose
strat_matrix = phase4_data.get('strategyMatrix', [])
logging.info(f"Phase 5 Input Check - Strategy Matrix Rows: {len(strat_matrix)}")
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
# Reduziere Input-Daten auf das Wesentliche, um den Output-Fokus zu verbessern
# FIX: Include 'specs' (Hard Facts) for the report
lean_phase1 = {
"features": phase1_data.get('features', []),
"constraints": phase1_data.get('constraints', []),
"specs": phase1_data.get('specs', {})
}
prompt = f"""
PHASE 5: FINAL REPORT GENERATION
INPUT DATA:
- Product: {json.dumps(lean_phase1)}
- ICPs: {json.dumps(phase2_data.get('icps', []))}
- Data Proxies: {json.dumps(phase2_data.get('dataProxies', []))}
- Targets: {json.dumps(phase3_data.get('whales', []))}
- Strategy Matrix: {json.dumps(phase4_data.get('strategyMatrix', []))}
TASK:
Write a professional "GTM STRATEGY REPORT" in Markdown.
REQUIRED STRUCTURE:
1. **Executive Summary**: A brief strategic overview.
2. **Product Analysis**: Key features & constraints.
3. **Technical Specifications (Hard Facts)**:
- Create a dedicated section displaying the technical data (Dimensions, Weight, Runtime, Layers, etc.) found in 'Product.specs'.
- Use a clear Markdown table or bullet points.
4. **Phase 2: ICP Discovery**:
- Present the selected ICPs (Industries) and the rationale behind them.
- List the identified Data Proxies used to find these customers.
5. **Target Accounts**: Top companies (Whales).
6. **Strategy Matrix**:
- Create a STRICT Markdown table.
- Columns: Segment | Pain Point | Angle | Differentiation
- Template:
| Segment | Pain Point | Angle | Differentiation |
| :--- | :--- | :--- | :--- |
| [Content] | [Content] | [Content] | [Content] |
- Use the data from the 'Strategy Matrix' input.
- Do NOT use newlines inside table cells (use <br> instead) to keep the table structure intact.
7. **Next Steps**: Actionable recommendations.
8. **Hybrid Service Logic**: Explain the machine/human symbiosis.
{lang_instr}
Output: Return strictly MARKDOWN formatted text. Start with "# GTM STRATEGY REPORT".
"""
log_and_save(project_id, "phase5", "prompt", prompt)
report = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=False)
# Clean up potentially fenced markdown code blocks
report = report.strip()
if report.startswith("```markdown"):
report = report.replace("```markdown", "", 1)
if report.startswith("```"):
report = report.replace("```", "", 1)
if report.endswith("```"):
report = report[:-3]
report = report.strip()
log_and_save(project_id, "phase5", "response", report)
db_manager.save_gtm_result(project_id, 'phase5_result', json.dumps({"report": report}))
return {"report": report}
def phase6(payload):
phase4_data = payload.get('phase4Data', {})
phase3_data = payload.get('phase3Data', {})
phase1_data = payload.get('phase1Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 6: SALES ENABLEMENT & VISUALS - STRATEGIC ANALYSIS
**Context:**
- Product Features: {json.dumps(phase1_data.get('features'))}
- Personas: {json.dumps(phase3_data.get('roles'))}
- Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
**Your Task:**
Answer the following strategic questions to create sales enablement materials.
**Strategic Questions:**
1. **Anticipate Objections:** For each key Buying Center persona, what is their most likely and critical **objection**? What is the core reason they would hesitate or say "no" to this investment?
2. **Formulate Battlecards:** For each objection, formulate a concise and powerful **response script**. The script should first acknowledge their concern, then reframe the issue, and finally present our solution as the clear, logical answer, quantifying the benefit where possible.
3. **Create Visual Prompts:** For the top 3 use cases, write a detailed **visual prompt** for an image generation AI. The prompt must describe a photorealistic scene that visually communicates the product's primary value proposition in that specific industry context. It should be clear what the robot is doing and what the positive outcome is.
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"battlecards": [{{"persona": "Buying Center Role", "objection": "The key objection.", "responseScript": "The compelling response."}}], "visualPrompts": [{{"title": "Image Title", "context": "Use case description.", "prompt": "Detailed photorealistic prompt."}}]}}
"""
log_and_save(project_id, "phase6", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase6", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase6_result', json.dumps(data))
return data
def phase7(payload):
phase4_data = payload.get('phase4Data', {})
phase2_data = payload.get('phase2Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 7: VERTICAL LANDING PAGE COPY - STRATEGIC ANALYSIS
**Context:**
- ICPs: {json.dumps(phase2_data.get('icps'))}
- Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
**Your Task:**
Create conversion-optimized landing page copy for the top 2 ICPs by answering the following questions.
**Strategic Questions:**
1. **Headline:** What is the most powerful **outcome** for this industry? The headline must grab the attention of a Decider and state this primary result.
2. **Subline:** How can you elaborate on the headline? Briefly mention the core problem this industry faces and introduce our solution as the answer.
3. **Benefit Bullets:** Transform 3-5 key technical features into tangible **benefit statements** for this specific industry. Each bullet point should answer the customer's question: "What's in it for me?".
4. **Call-to-Action (CTA):** What is the logical next step we want the user to take? The CTA should be clear, concise, and action-oriented.
5. **Apply Wackler Symbiosis:** Ensure the copy clearly communicates the value of the robot combined with the human expert service.
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"landingPages": [{{"industry": "ICP Name", "headline": "The compelling headline.", "subline": "The elaborating subline.", "bullets": ["Benefit 1", "Benefit 2"], "cta": "The call to action."}}]}}
"""
log_and_save(project_id, "phase7", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase7", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase7_result', json.dumps(data))
return data
def phase8(payload):
phase2_data = payload.get('phase2Data', {})
phase1_data = payload.get('phase1Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 8: BUSINESS CASE BUILDER (THE CFO PITCH) - STRATEGIC ANALYSIS
**Context:**
- ICPs: {json.dumps(phase2_data.get('icps'))}
- Features: {json.dumps(phase1_data.get('features'))}
**Your Task:**
Develop the financial argumentation for each ICP by answering the following questions.
**Strategic Questions:**
1. **Identify the Cost Driver:** What is the primary, quantifiable **cost driver** that our product addresses in this industry? (e.g., "High labor costs for manual inspections", "Financial losses from production downtime", "Insurance premiums due to safety incidents").
2. **Quantify Efficiency Gains:** How, specifically, does our product create a positive financial impact? Estimate the **efficiency gain** in plausible percentages or saved hours/costs. (e.g., "Reduces manual inspection time by 50%", "Prevents costly downtime worth an estimated X€ per hour").
3. **Formulate the Risk Argument:** How does our solution mitigate significant operational, financial, or safety **risks**? Frame this as a form of financial protection or cost avoidance. (e.g., "Mitigates the multi-million dollar risk of an environmental incident by providing early leak detection.").
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"businessCases": [{{"industry": "ICP Name", "costDriver": "The primary financial pain.", "efficiencyGain": "The quantifiable gain.", "riskArgument": "The cost of inaction."}}]}}
"""
log_and_save(project_id, "phase8", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase8", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase8_result', json.dumps(data))
return data
def phase9(payload):
phase1_data = payload.get('phase1Data', {})
phase4_data = payload.get('phase4Data', {})
lang = payload.get('lang', 'de')
project_id = payload.get('projectId')
sys_instr = get_system_instruction(lang)
lang_instr = get_output_lang_instruction(lang)
prompt = f"""
PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR - STRATEGIC ANALYSIS
**Context:**
- Input Features: {json.dumps(phase1_data.get('features'))}
- Strategy Pains: {json.dumps([s.get('painPoint') for s in phase4_data.get('strategyMatrix', [])])}
**Your Task:**
Translate technical features into compelling, value-oriented benefits by following this structured process for each key feature.
**Structured Process:**
1. **State the Feature:** Pick a key technical feature.
2. **Ask "So what?" (The Consequence):** What is the immediate consequence or capability that this feature enables?
3. **Ask "So what?" again (The Value):** What is the ultimate, tangible **value** or **benefit** that the consequence delivers to the customer? How does it solve one of their core pain points? This is the "Story".
4. **Formulate Headline:** Create a short, powerful, jargon-free **headline** that encapsulates this final value.
{lang_instr}
**Output:**
Provide your analysis ONLY in the following JSON format:
{{"techTranslations": [{{"feature": "The technical feature.", "story": "The 'So what? So what?' analysis.", "headline": "The final value headline."}}]}}
"""
log_and_save(project_id, "phase9", "prompt", prompt)
response = call_gemini_flash(prompt, system_instruction=sys_instr, json_mode=True)
log_and_save(project_id, "phase9", "response", response)
data = json.loads(response)
db_manager.save_gtm_result(project_id, 'phase9_result', json.dumps(data))
return data
def update_specs(payload):
"""
Updates the technical specifications (Hard Facts) for a project.
This allows manual correction of AI-extracted data.
"""
project_id = payload.get('projectId')
new_specs = payload.get('specs')
if not project_id:
raise ValueError("No projectId provided for update_specs.")
if not new_specs:
raise ValueError("No specs provided for update_specs.")
# Load current project data
project_data = db_manager.get_project_data(project_id)
if not project_data:
raise ValueError(f"Project {project_id} not found.")
phases = project_data.get('phases', {})
phase1_result = phases.get('phase1_result')
if not phase1_result:
raise ValueError("Phase 1 result not found. Cannot update specs.")
# FIX: Parse JSON string if necessary
if isinstance(phase1_result, str):
try:
phase1_result = json.loads(phase1_result)
except json.JSONDecodeError:
raise ValueError("Phase 1 result is corrupted (invalid JSON string).")
# Update specs
phase1_result['specs'] = new_specs
# Save back to DB
# We use save_gtm_result which expects a stringified JSON for the phase result
db_manager.save_gtm_result(project_id, 'phase1_result', json.dumps(phase1_result))
logging.info(f"Updated specs for project {project_id}")
return {"status": "success", "specs": new_specs}
def translate(payload):
# ... (to be implemented)
return {"report": "Translated report will be here."}
def image(payload):
prompt = payload.get('prompt', 'No Prompt')
project_id = payload.get('projectId')
aspect_ratio = payload.get('aspectRatio')
ref_images = payload.get('referenceImagesBase64')
ref_image = None
if ref_images and isinstance(ref_images, list) and len(ref_images) > 0:
ref_image = ref_images[0]
elif payload.get('referenceImage'):
ref_image = payload.get('referenceImage')
log_and_save(project_id, "image", "prompt", f"{prompt} (Ratio: {aspect_ratio or 'default'})")
if ref_image:
logging.info(f"Image-Mode: Reference Image found (Length: {len(ref_image)})")
try:
image_b64 = call_gemini_image(prompt, reference_image_b64=ref_image, aspect_ratio=aspect_ratio)
log_and_save(project_id, "image", "response_b64_preview", image_b64[:100] + "...")
return {"imageBase64": f"data:image/png;base64,{image_b64}"}
except Exception as e:
logging.error(f"Failed to generate image: {e}", exc_info=True)
return {"error": "Image generation failed.", "details": str(e)}
def main():
"""
Main entry point of the script.
Parses command-line arguments to determine which phase to run.
"""
parser = argparse.ArgumentParser(description="GTM Architect Orchestrator")
parser.add_argument("--mode", required=True, help="The execution mode (e.g., phase1, phase2).")
parser.add_argument("--payload_base64", help="The Base64 encoded JSON payload (deprecated, use payload_file).")
parser.add_argument("--payload_file", help="Path to a JSON file containing the payload (preferred).")
args = parser.parse_args()
payload = {}
try:
if args.payload_file:
if not os.path.exists(args.payload_file):
raise FileNotFoundError(f"Payload file not found: {args.payload_file}")
with open(args.payload_file, 'r', encoding='utf-8') as f:
payload = json.load(f)
elif args.payload_base64:
payload_str = base64.b64decode(args.payload_base64).decode('utf-8')
payload = json.loads(payload_str)
else:
raise ValueError("No payload provided (neither --payload_file nor --payload_base64).")
except (json.JSONDecodeError, base64.binascii.Error, ValueError, FileNotFoundError) as e:
logging.error(f"Failed to load payload: {e}")
# Print error as JSON to stdout for the server to catch
print(json.dumps({"error": "Invalid payload.", "details": str(e)}))
sys.exit(1)
# Function mapping to dynamically call the correct phase
modes = {
"phase1": phase1,
"phase2": phase2,
"phase3": phase3,
"phase4": phase4,
"phase5": phase5,
"phase6": phase6,
"phase7": phase7,
"phase8": phase8,
"phase9": phase9,
"update_specs": update_specs,
"translate": translate,
"image": image,
"list_history": list_history,
"load_history": load_history,
"delete_session": delete_session,
}
mode_function = modes.get(args.mode)
if not mode_function:
logging.error(f"Invalid mode specified: {args.mode}")
print(json.dumps({"error": f"Invalid mode: {args.mode}"}))
sys.exit(1)
try:
logging.info(f"Executing mode: {args.mode}")
result = mode_function(payload)
# Ensure the output is always a JSON string
print(json.dumps(result, ensure_ascii=False))
logging.info(f"Successfully executed mode: {args.mode}")
except Exception as e:
logging.error(f"An error occurred during execution of mode '{args.mode}': {e}", exc_info=True)
print(json.dumps({"error": f"An error occurred in {args.mode}.", "details": str(e)}))
sys.exit(1)
if __name__ == "__main__":
main()