fix(gtm): Implement ultimate prompt robustness, version logging, and final doc update
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:",
|
||||
Reference in New Issue
Block a user