diff --git a/BUILDER_APPS_MIGRATION.md b/BUILDER_APPS_MIGRATION.md index 635271b2..86c68506 100644 --- a/BUILDER_APPS_MIGRATION.md +++ b/BUILDER_APPS_MIGRATION.md @@ -56,15 +56,16 @@ Multi-Line Prompts können in Docker-Umgebungen zu **sehr hartnäckigen Syntaxfe * **Das Problem:** Der Python-Parser (insbesondere bei `f-strings` in Kombination mit Zahlen/Punkten am Zeilenanfang oder verschachtelten Klammern) kann Multi-Line-Strings (`f"""..."""`) falsch interpretieren, was zu Fehlern wie `SyntaxError: invalid decimal literal` oder `unmatched ')'` führt, auch wenn der Code scheinbar korrekt ist. * **ULTIMATIVE LÖSUNG (Maximale Robustheit):** - 1. **Vermeide `f"""` komplett für komplexe Multi-Line-Prompts.** Definiere stattdessen den Prompt als normalen Multi-Line-String (ohne `f` Präfix). - 2. **Nutze die `.format()` Methode** zur Variablen-Injektion. Dies trennt die String-Definition komplett von der Variablen-Interpolation und ist die robusteste Methode. + 1. **Vermeide `f"""` komplett für komplexe Multi-Line-Prompts.** Definiere stattdessen den Prompt als **Liste von einzelnen String-Zeilen** und füge sie mit `"\n".join(prompt_parts)` zusammen. + 2. **Nutze die `.format()` Methode oder f-Strings in EINZEILIGEN Strings** zur Variablen-Injektion. Dies trennt die String-Definition komplett von der Variablen-Interpolation und ist die robusteste Methode. ```python - # Beispiel: So ist es maximal robust (bevorzugte Methode) - prompt_template = """ - 1) Mache dies: {variable_1} - 2) Mache das: {variable_2} - """ + # Beispiel: Maximal robust + prompt_template_parts = [ + "1) Mache dies: {variable_1}", + "2) Mache das: {variable_2}", + ] + prompt_template = "\n".join(prompt_template_parts) prompt = prompt_template.format(variable_1=wert1, variable_2=wert2) # System-Instruktion muss immer noch vorangestellt werden: full_prompt = sys_instr + "\n\n" + prompt diff --git a/gtm_architect_orchestrator.py b/gtm_architect_orchestrator.py index 44e4653b..28b16e42 100644 --- a/gtm_architect_orchestrator.py +++ b/gtm_architect_orchestrator.py @@ -11,7 +11,7 @@ sys.path.append(str(project_root)) from helpers import call_openai_chat import market_db_manager -__version__ = "1.3.0_SyntaxFix" +__version__ = "1.4.0_UltimatePromptFix" # Ensure DB is ready market_db_manager.init_db() @@ -133,35 +133,35 @@ def analyze_product(data): lang = data.get('language', 'de') sys_instr = get_system_instruction(lang) - # Statische Prompt-Teile (keine f-Strings) + # Prompts als Liste von Strings konstruieren, um Syntax-Fehler zu vermeiden if lang == 'en': - extraction_prompt_static_part = """ -PHASE 1-A: TECHNICAL EXTRACTION -Input Product Description: "{product_description}" - -Task: -1) Extract key technical features (specs, capabilities). -2) Derive "Hard Constraints". IMPORTANT: Check Vmax (<20km/h = Private Grounds) and Cleaning Type (Vacuum != Heavy Debris/Snow). -3) Create a short raw analysis summary. - -Output JSON format ONLY. -""" + extraction_prompt_parts = [ + "PHASE 1-A: TECHNICAL EXTRACTION", + f"Input Product Description: \"{product_input[:25000]}\"", + "", + "Task:", + "1) Extract key technical features (specs, capabilities).", + "2) Derive \"Hard Constraints\". IMPORTANT: Check Vmax (<20km/h = Private Grounds) and Cleaning Type (Vacuum != Heavy Debris/Snow).", + "3) Create a short raw analysis summary.", + "", + "Output JSON format ONLY." + ] else: - extraction_prompt_static_part = """ -PHASE 1-A: TECHNICAL EXTRACTION -Input Product Description: "{product_description}" - -Aufgabe: -1) Extrahiere technische Hauptmerkmale (Specs, Fähigkeiten). -2) Leite "Harte Constraints" ab. WICHTIG: Prüfe Vmax (<20km/h = Privatgelände) und Reinigungstyp (Vakuum != Grobschmutz/Schnee). -3) Erstelle eine kurze Rohanalyse-Zusammenfassung. - -Output JSON format ONLY. -""" - # Variablen separat formatieren - extraction_prompt = extraction_prompt_static_part.format(product_description=product_input[:25000]) + extraction_prompt_parts = [ + "PHASE 1-A: TECHNICAL EXTRACTION", + f"Input Product Description: \"{product_input[:25000]}\"", + "", + "Aufgabe:", + "1) Extrahiere technische Hauptmerkmale (Specs, Fähigkeiten).", + "2) Leite \"Harte Constraints\" ab. WICHTIG: Prüfe Vmax (<20km/h = Privatgelände) und Reinigungstyp (Vakuum != Grobschmutz/Schnee).", + "3) Erstelle eine kurze Rohanalyse-Zusammenfassung.", + "", + "Output JSON format ONLY." + ] + extraction_prompt = "\n".join(extraction_prompt_parts) full_extraction_prompt = sys_instr + "\n\n" + extraction_prompt + print(f"DEBUG: Full Extraction Prompt: \n{full_extraction_prompt[:1000]}...\n", file=sys.stderr) extraction_response = call_openai_chat(full_extraction_prompt, response_format_json=True) extraction_data = json.loads(extraction_response) @@ -169,169 +169,10 @@ Output JSON format ONLY. constraints_json = json.dumps(extraction_data.get('constraints')) if lang == 'en': - conflict_prompt_static_part = """ -PHASE 1-B: PORTFOLIO CONFLICT CHECK - -New Product Features: {features_json_var} -New Product Constraints: {constraints_json_var} - -Existing Portfolio: -1. "Indoor Scrubber 50": Indoor cleaning, hard floor, supermarkets. -2. "Service Bot Bella": Service/Gastro, indoor, restaurants. - -Task: -Check if the new product overlaps significantly with existing ones (is it just a clone?). - -Output JSON format ONLY. -""" - conflict_prompt = conflict_prompt_static_part.format(features_json_var=features_json, constraints_json_var=constraints_json) - else: - conflict_prompt_static_part = """ -PHASE 1-B: PORTFOLIO CONFLICT CHECK - -Neue Produkt-Features: {features_json_var} -Neue Produkt-Constraints: {constraints_json_var} - -Existierendes Portfolio: -1. "Indoor Scrubber 50": Innenreinigung, Hartboden, Supermärkte. -2. "Service Bot Bella": Service/Gastro, Indoor, Restaurants. - -Aufgabe: -Prüfe, ob das neue Produkt signifikant mit bestehenden Produkten überlappt (Ist es nur ein Klon?). - -Output JSON format ONLY. -""" - conflict_prompt = conflict_prompt_static_part.format(features_json_var=features_json, constraints_json_var=constraints_json) - - full_conflict_prompt = sys_instr + "\n\n" + conflict_prompt - conflict_response = call_openai_chat(full_conflict_prompt, response_format_json=True) - conflict_data = json.loads(conflict_response) - - final_result = {**extraction_data, **conflict_data} - print(json.dumps(final_result)) - -def discover_icps(data): - phase1_result = data.get('phase1Result') - lang = data.get('language', 'de') - sys_instr = get_system_instruction(lang) - - features_json = json.dumps(phase1_result.get('features')) - constraints_json = json.dumps(phase1_result.get('constraints')) - - if lang == 'en': - prompt_template = """ -PHASE 2: ICP DISCOVERY & DATA PROXIES -Based on the product features: {features_json_var} -And constraints: {constraints_json_var} - -Output JSON format ONLY: -{{ - "icps": [ {{ "name": "Industry Name", "rationale": "Rationale" }} ], - "dataProxies": [ {{ "target": "Criteria", "method": "How" }} ] -}} -""" - prompt = prompt_template.format(features_json_var=features_json, constraints_json_var=constraints_json) - else: - prompt_template = """ -PHASE 2: ICP DISCOVERY & DATA PROXIES -Basierend auf Features: {features_json_var} -Und Constraints: {constraints_json_var} - -Output JSON format ONLY: -{{ - "icps": [ {{ "name": "Branchen Name", "rationale": "Begründung" }} ], - "dataProxies": [ {{ "target": "Kriterium", "method": "Methode" }} ] -}} -""" - prompt = prompt_template.format(features_json_var=features_json, constraints_json_var=constraints_json) - - full_prompt = sys_instr + "\n\n" + prompt - response = call_openai_chat(full_prompt, response_format_json=True) - print(response) - -def hunt_whales(data): - phase2_result = data.get('phase2Result') - lang = data.get('language', 'de') - sys_instr = get_system_instruction(lang) - icps_json = json.dumps(phase2_result.get('icps')) - prompt_template = """ -PHASE 3: WHALE HUNTING for {icps_json_var}. Identify 3-5 concrete DACH companies per industry and buying center roles. Output JSON ONLY. -""" - prompt = prompt_template.format(icps_json_var=icps_json) - full_prompt = sys_instr + "\n\n" + prompt - response = call_openai_chat(full_prompt, response_format_json=True) - print(response) - -def develop_strategy(data): - phase3_result = data.get('phase3Result') - phase1_result = data.get('phase1Result') - lang = data.get('language', 'de') - sys_instr = get_system_instruction(lang) - phase3_json = json.dumps(phase3_result) - prompt_template = """ -PHASE 4: STRATEGY Matrix for {phase3_json_var}. Apply Hybrid logic. Output JSON ONLY. -""" - prompt = prompt_template.format(phase3_json_var=phase3_json) - full_prompt = sys_instr + "\n\n" + prompt - response = call_openai_chat(full_prompt, response_format_json=True) - print(response) - -def generate_assets(data): - lang = data.get('language', 'de') - sys_instr = get_system_instruction(lang) - data_json = json.dumps(data) - prompt_template = """ -PHASE 5: GTM STRATEGY REPORT Markdown. Use facts, TCO, ROI. Data: {data_json_var} -""" - prompt = prompt_template.format(data_json_var=data_json) - full_prompt = sys_instr + "\n\n" + prompt - response = call_openai_chat(full_prompt) - print(json.dumps(response)) - -def generate_sales_enablement(data): - lang = data.get('language', 'de') - sys_instr = get_system_instruction(lang) - data_json = json.dumps(data) - prompt_template = """ -PHASE 6: Battlecards and MJ Prompts. Data: {data_json_var}. Output JSON ONLY. -""" - prompt = prompt_template.format(data_json_var=data_json) - full_prompt = sys_instr + "\n\n" + prompt - response = call_openai_chat(full_prompt, response_format_json=True) - print(response) - -def main(): - parser = argparse.ArgumentParser(description="GTM Architect Orchestrator") - parser.add_argument("--mode", type=str, required=True, help="Execution mode") - args = parser.parse_args() - - print(f"DEBUG: Orchestrator v{__version__} loaded (Mode: {args.mode})", file=sys.stderr) - - if not sys.stdin.isatty(): - try: - data = json.loads(sys.stdin.read()) - except: - data = {} - else: - data = {} - - modes = { - "analyze_product": analyze_product, - "discover_icps": discover_icps, - "hunt_whales": hunt_whales, - "develop_strategy": develop_strategy, - "generate_assets": generate_assets, - "generate_sales_enablement": generate_sales_enablement, - "save_project": save_project_handler, - "list_projects": list_projects_handler, - "load_project": load_project_handler, - "delete_project": delete_project_handler - } - - if args.mode in modes: - modes[args.mode](data) - else: - print(json.dumps({"error": f"Unknown mode: {args.mode}"})) - -if __name__ == "__main__": - main() + conflict_prompt_parts = [ + "PHASE 1-B: PORTFOLIO CONFLICT CHECK", + "", + f"New Product Features: {features_json}", + f"New Product Constraints: {constraints_json}", + "", + "Existing Portfolio:", \ No newline at end of file