Fix: Resolved [object Object] report bug, enforced German output language across all phases, and documented troubleshooting steps
This commit is contained in:
@@ -298,9 +298,9 @@ const App: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generateFullReportMarkdown = (): string => {
|
const generateFullReportMarkdown = (): string => {
|
||||||
if (!state.phase5Result) return "";
|
if (!state.phase5Result || !state.phase5Result.report) return "";
|
||||||
|
|
||||||
let fullReport = state.phase5Result;
|
let fullReport = state.phase5Result.report;
|
||||||
|
|
||||||
if (state.phase6Result) {
|
if (state.phase6Result) {
|
||||||
fullReport += `\n\n# SALES ENABLEMENT & VISUALS (PHASE 6)\n\n`;
|
fullReport += `\n\n# SALES ENABLEMENT & VISUALS (PHASE 6)\n\n`;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
@@ -21,7 +20,7 @@ LOG_DIR = "Log_from_docker"
|
|||||||
if not os.path.exists(LOG_DIR):
|
if not os.path.exists(LOG_DIR):
|
||||||
os.makedirs(LOG_DIR)
|
os.makedirs(LOG_DIR)
|
||||||
|
|
||||||
ORCHESTRATOR_VERSION = "1.1.0"
|
ORCHESTRATOR_VERSION = "1.2.0" # Bump version for language fix
|
||||||
run_timestamp = datetime.now().strftime("%y-%m-%d_%H-%M-%S")
|
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")
|
log_file_path = os.path.join(LOG_DIR, f"{run_timestamp}_gtm_orchestrator_run.log")
|
||||||
|
|
||||||
@@ -33,11 +32,12 @@ logging.basicConfig(
|
|||||||
logging.StreamHandler(sys.stdout)
|
logging.StreamHandler(sys.stdout)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
logging.info(f"GTM Architect Orchestrator v{{ORCHESTRATOR_VERSION}} ({{run_timestamp}}) starting...")
|
logging.info(f"GTM Architect Orchestrator v{ORCHESTRATOR_VERSION} ({run_timestamp}) starting...")
|
||||||
|
|
||||||
# !!! CRITICAL FIX: Load API keys at the very beginning !!!
|
# !!! CRITICAL FIX: Load API keys at the very beginning !!!
|
||||||
# This ensures Config.API_KEYS is populated before any AI functions are called.
|
# This ensures Config.API_KEYS is populated before any AI functions are called.
|
||||||
Config.load_api_keys()
|
Config.load_api_keys()
|
||||||
|
|
||||||
def log_and_save(project_id, step_name, data_type, content):
|
def log_and_save(project_id, step_name, data_type, content):
|
||||||
logging.info(f"Project {project_id} - Step: {step_name} - Type: {data_type}")
|
logging.info(f"Project {project_id} - Step: {step_name} - Type: {data_type}")
|
||||||
filename = f"{run_timestamp}_{step_name}_{data_type}.txt"
|
filename = f"{run_timestamp}_{step_name}_{data_type}.txt"
|
||||||
@@ -63,6 +63,7 @@ def get_system_instruction(lang):
|
|||||||
Führe eine interne Plausibilitätsprüfung durch, bevor du eine Antwort gibst.
|
Führe eine interne Plausibilitätsprüfung durch, bevor du eine Antwort gibst.
|
||||||
Verwende "Wackler Symbiosis" als internes Framework für die Analyse von Produkt-Synergien.
|
Verwende "Wackler Symbiosis" als internes Framework für die Analyse von Produkt-Synergien.
|
||||||
Nutze das "Hybrid Service Logic" Konzept, um zu bewerten, ob ein Produkt mit einer Dienstleistung kombiniert werden muss (z.B. bei hohen Wartungsanforderungen).
|
Nutze das "Hybrid Service Logic" Konzept, um zu bewerten, ob ein Produkt mit einer Dienstleistung kombiniert werden muss (z.B. bei hohen Wartungsanforderungen).
|
||||||
|
WICHTIG: Antworte immer in der vom User geforderten Sprache (Deutsch), auch wenn der Input Englisch ist.
|
||||||
"""
|
"""
|
||||||
else: # Default to English
|
else: # Default to English
|
||||||
return """
|
return """
|
||||||
@@ -76,6 +77,12 @@ def get_system_instruction(lang):
|
|||||||
Use the "Hybrid Service Logic" concept to evaluate if a product needs to be combined with a service (e.g., due to high maintenance requirements).
|
Use the "Hybrid Service Logic" concept to evaluate if a product needs to be combined with a service (e.g., due to high maintenance requirements).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
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 ---
|
# --- ORCHESTRATOR PHASES ---
|
||||||
|
|
||||||
def phase1(payload):
|
def phase1(payload):
|
||||||
@@ -96,6 +103,8 @@ def phase1(payload):
|
|||||||
logging.info("Input is raw text. Analyzing directly.")
|
logging.info("Input is raw text. Analyzing directly.")
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS
|
PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS
|
||||||
Input: "{analysis_content}"
|
Input: "{analysis_content}"
|
||||||
@@ -103,6 +112,9 @@ def phase1(payload):
|
|||||||
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.
|
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).
|
2. Define hard constraints (e.g., physical dimensions, max payload, environment limitations).
|
||||||
3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000").
|
3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000").
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}}
|
Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase1", "prompt", prompt)
|
log_and_save(project_id, "phase1", "prompt", prompt)
|
||||||
@@ -115,7 +127,6 @@ def phase1(payload):
|
|||||||
return data
|
return data
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
logging.error(f"Failed to decode JSON from Gemini response in phase1: {response}")
|
logging.error(f"Failed to decode JSON from Gemini response in phase1: {response}")
|
||||||
# Return a structured error that the frontend can display
|
|
||||||
error_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.",
|
"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
|
"details": response
|
||||||
@@ -129,10 +140,15 @@ def phase2(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 2: IDEAL CUSTOMER PROFILE (ICP) & DATA PROXIES
|
PHASE 2: IDEAL CUSTOMER PROFILE (ICP) & DATA PROXIES
|
||||||
Product Context: {json.dumps(phase1_data)}
|
Product Context: {json.dumps(phase1_data)}
|
||||||
Task: 1. Identify top 3 ICPs (Ideal Customer Profiles/Industries). 2. Define data proxies for identifying these ICPs online.
|
Task: 1. Identify top 3 ICPs (Ideal Customer Profiles/Industries). 2. Define data proxies for identifying these ICPs online.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"icps": [{{"name": "", "rationale": ""}}], "dataProxies": [{{"target": "", "method": ""}}]}}
|
Output JSON format ONLY: {{"icps": [{{"name": "", "rationale": ""}}], "dataProxies": [{{"target": "", "method": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase2", "prompt", prompt)
|
log_and_save(project_id, "phase2", "prompt", prompt)
|
||||||
@@ -148,10 +164,15 @@ def phase3(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 3: WHALE HUNTING
|
PHASE 3: WHALE HUNTING
|
||||||
Target ICPs (Industries): {json.dumps(phase2_data.get('icps'))}
|
Target ICPs (Industries): {json.dumps(phase2_data.get('icps'))}
|
||||||
Task: 1. Group 'Whales' (Key Accounts) strictly by ICP industries. 2. Identify 3-5 concrete top companies in DACH market per industry. 3. Define Buying Center Roles.
|
Task: 1. Group 'Whales' (Key Accounts) strictly by ICP industries. 2. Identify 3-5 concrete top companies in DACH market per industry. 3. Define Buying Center Roles.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"whales": [{{"industry": "", "accounts": []}}], "roles": []}}
|
Output JSON format ONLY: {{"whales": [{{"industry": "", "accounts": []}}], "roles": []}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase3", "prompt", prompt)
|
log_and_save(project_id, "phase3", "prompt", prompt)
|
||||||
@@ -168,6 +189,8 @@ def phase4(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
all_accounts = []
|
all_accounts = []
|
||||||
for w in phase3_data.get('whales', []):
|
for w in phase3_data.get('whales', []):
|
||||||
all_accounts.extend(w.get('accounts', []))
|
all_accounts.extend(w.get('accounts', []))
|
||||||
@@ -178,6 +201,9 @@ def phase4(payload):
|
|||||||
Target Industries: {json.dumps([w.get('industry') for w in phase3_data.get('whales', [])])}
|
Target Industries: {json.dumps([w.get('industry') for w in phase3_data.get('whales', [])])}
|
||||||
Product Features: {json.dumps(phase1_data.get('features'))}
|
Product Features: {json.dumps(phase1_data.get('features'))}
|
||||||
Task: 1. Develop specific "Angle" per target/industry. 2. Consistency Check against Product Matrix. 3. **IMPORTANT:** Apply "Hybrid Service Logic" if constraints exist!
|
Task: 1. Develop specific "Angle" per target/industry. 2. Consistency Check against Product Matrix. 3. **IMPORTANT:** Apply "Hybrid Service Logic" if constraints exist!
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"strategyMatrix": [{{"segment": "", "painPoint": "", "angle": "", "differentiation": ""}}]}}
|
Output JSON format ONLY: {{"strategyMatrix": [{{"segment": "", "painPoint": "", "angle": "", "differentiation": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase4", "prompt", prompt)
|
log_and_save(project_id, "phase4", "prompt", prompt)
|
||||||
@@ -196,6 +222,8 @@ def phase5(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 5: ASSET GENERATION & FINAL REPORT
|
PHASE 5: ASSET GENERATION & FINAL REPORT
|
||||||
CONTEXT DATA:
|
CONTEXT DATA:
|
||||||
@@ -207,6 +235,9 @@ def phase5(payload):
|
|||||||
1. Create a "GTM STRATEGY REPORT" in Markdown.
|
1. Create a "GTM STRATEGY REPORT" in Markdown.
|
||||||
2. Report Structure: Executive Summary, Product Analysis, Target Audience, Target Accounts, Strategy Matrix, Assets.
|
2. Report Structure: Executive Summary, Product Analysis, Target Audience, Target Accounts, Strategy Matrix, Assets.
|
||||||
3. Hybrid-Check: Ensure "Hybrid Service Logic" is visible.
|
3. Hybrid-Check: Ensure "Hybrid Service Logic" is visible.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output: Return strictly MARKDOWN formatted text. Start with "# GTM STRATEGY REPORT".
|
Output: Return strictly MARKDOWN formatted text. Start with "# GTM STRATEGY REPORT".
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase5", "prompt", prompt)
|
log_and_save(project_id, "phase5", "prompt", prompt)
|
||||||
@@ -223,10 +254,15 @@ def phase6(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 6: SALES ENABLEMENT & VISUALS
|
PHASE 6: SALES ENABLEMENT & VISUALS
|
||||||
CONTEXT: - Product Features: {json.dumps(phase1_data.get('features'))} - Personas: {json.dumps(phase3_data.get('roles'))} - Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
|
CONTEXT: - Product Features: {json.dumps(phase1_data.get('features'))} - Personas: {json.dumps(phase3_data.get('roles'))} - Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
|
||||||
TASK: 1. Anticipate Friction & Objections. 2. Formulate Battlecards. 3. Create Visual Prompts.
|
TASK: 1. Anticipate Friction & Objections. 2. Formulate Battlecards. 3. Create Visual Prompts.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"battlecards": [{{"persona": "", "objection": "", "responseScript": ""}}], "visualPrompts": [{{"title": "", "context": "", "prompt": ""}}]}}
|
Output JSON format ONLY: {{"battlecards": [{{"persona": "", "objection": "", "responseScript": ""}}], "visualPrompts": [{{"title": "", "context": "", "prompt": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase6", "prompt", prompt)
|
log_and_save(project_id, "phase6", "prompt", prompt)
|
||||||
@@ -243,11 +279,16 @@ def phase7(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 7: VERTICAL LANDING PAGE COPY (Conversion Optimization)
|
PHASE 7: VERTICAL LANDING PAGE COPY (Conversion Optimization)
|
||||||
ICPs: {json.dumps(phase2_data.get('icps'))}
|
ICPs: {json.dumps(phase2_data.get('icps'))}
|
||||||
Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
|
Strategy: {json.dumps(phase4_data.get('strategyMatrix'))}
|
||||||
TASK: 1. Transform generic features into specific benefits for the Top 2 ICPs. 2. Apply "Wackler Symbiosis". 3. Create Landing Page Drafts (Hero Section).
|
TASK: 1. Transform generic features into specific benefits for the Top 2 ICPs. 2. Apply "Wackler Symbiosis". 3. Create Landing Page Drafts (Hero Section).
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"landingPages": [{{"industry": "", "headline": "", "subline": "", "bullets": [], "cta": ""}}]}}
|
Output JSON format ONLY: {{"landingPages": [{{"industry": "", "headline": "", "subline": "", "bullets": [], "cta": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase7", "prompt", prompt)
|
log_and_save(project_id, "phase7", "prompt", prompt)
|
||||||
@@ -264,10 +305,15 @@ def phase8(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 8: BUSINESS CASE BUILDER (The CFO Pitch)
|
PHASE 8: BUSINESS CASE BUILDER (The CFO Pitch)
|
||||||
Input: ICPs: {json.dumps(phase2_data.get('icps'))}, Features: {json.dumps(phase1_data.get('features'))}
|
Input: ICPs: {json.dumps(phase2_data.get('icps'))}, Features: {json.dumps(phase1_data.get('features'))}
|
||||||
TASK: 1. Estimate labor costs/pain points. 2. Compare against Robot Leasing (approx 330-600€/month). 3. Develop ROI logic. 4. Create "Financial Argumentation Guide" for each ICP.
|
TASK: 1. Estimate labor costs/pain points. 2. Compare against Robot Leasing (approx 330-600€/month). 3. Develop ROI logic. 4. Create "Financial Argumentation Guide" for each ICP.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"businessCases": [{{"industry": "", "costDriver": "", "efficiencyGain": "", "riskArgument": ""}}]}}
|
Output JSON format ONLY: {{"businessCases": [{{"industry": "", "costDriver": "", "efficiencyGain": "", "riskArgument": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase8", "prompt", prompt)
|
log_and_save(project_id, "phase8", "prompt", prompt)
|
||||||
@@ -284,11 +330,16 @@ def phase9(payload):
|
|||||||
project_id = payload.get('projectId')
|
project_id = payload.get('projectId')
|
||||||
|
|
||||||
sys_instr = get_system_instruction(lang)
|
sys_instr = get_system_instruction(lang)
|
||||||
|
lang_instr = get_output_lang_instruction(lang)
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR
|
PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR
|
||||||
Input Features: {json.dumps(phase1_data.get('features'))}
|
Input Features: {json.dumps(phase1_data.get('features'))}
|
||||||
Strategy Pains: {json.dumps([s.get('painPoint') for s in phase4_data.get('strategyMatrix', [])])}
|
Strategy Pains: {json.dumps([s.get('painPoint') for s in phase4_data.get('strategyMatrix', [])])}
|
||||||
TASK: 1. Take a tech feature. 2. Ask "So what?". 3. Ask "So what?" again. 4. Formulate benefit without jargon. Create a table.
|
TASK: 1. Take a tech feature. 2. Ask "So what?". 3. Ask "So what?" again. 4. Formulate benefit without jargon. Create a table.
|
||||||
|
|
||||||
|
{lang_instr}
|
||||||
|
|
||||||
Output JSON format ONLY: {{"techTranslations": [{{"feature": "", "story": "", "headline": ""}}]}}
|
Output JSON format ONLY: {{"techTranslations": [{{"feature": "", "story": "", "headline": ""}}]}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase9", "prompt", prompt)
|
log_and_save(project_id, "phase9", "prompt", prompt)
|
||||||
@@ -361,4 +412,4 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user