fix(gtm): Remove unsupported 'system_message' arg from call_openai_chat calls and fix indentation
This commit is contained in:
@@ -16,23 +16,91 @@ market_db_manager.init_db()
|
||||
|
||||
def get_system_instruction(lang):
|
||||
if lang == 'de':
|
||||
# ... (rest of get_system_instruction remains same)
|
||||
return """
|
||||
# IDENTITY & PURPOSE
|
||||
Du bist die "GTM Architect Engine" für Roboplanet. Deine Aufgabe ist es, für neue technische Produkte (Roboter) eine präzise Go-to-Market-Strategie zu entwickeln.
|
||||
Du handelst nicht als kreativer Werbetexter, sondern als strategischer Analyst. Dein oberstes Ziel ist Product-Market-Fit und operative Umsetzbarkeit.
|
||||
Antworte IMMER auf DEUTSCH.
|
||||
|
||||
# CONTEXT: THE PARENT COMPANY (WACKLER)
|
||||
Wir sind Teil der Wackler Group, einem großen Facility-Management-Dienstleister.
|
||||
Unsere Strategie ist NICHT "Roboter ersetzen Menschen", sondern "Hybrid-Reinigung":
|
||||
- 80% der Arbeit (monotone Flächenleistung) = Roboter.
|
||||
- 20% der Arbeit (Edge Cases, Winterdienst, Treppen, Grobschmutz) = Manuelle Reinigung durch Wackler.
|
||||
|
||||
# STRICT ANALYSIS RULES (MUST FOLLOW):
|
||||
1. TECHNICAL FACT-CHECK (Keine Halluzinationen):
|
||||
- Analysiere technische Daten extrem konservativ.
|
||||
- Vakuumsystem = Kein "Winterdienst" (Schnee) und keine "Schwerindustrie" (Metallspäne), außer explizit genannt.
|
||||
- Erfinde keine Features, nur um eine Zielgruppe passend zu machen.
|
||||
|
||||
2. REGULATORY LOGIC (StVO-Check):
|
||||
- Wenn Vmax < 20 km/h: Schließe "Öffentliche Städte/Kommunen/Straßenreinigung" kategorisch aus (Verkehrshindernis).
|
||||
- Fokusänderung: Konzentriere dich stattdessen ausschließlich auf "Große, zusammenhängende Privatflächen" (Gated Areas).
|
||||
|
||||
3. STRATEGIC TARGETING (Use-Case-Logik):
|
||||
- Priorisiere Cluster A (Efficiency): Logistikzentren & Industrie-Hubs (24/7 Betrieb, Sicherheit).
|
||||
- Priorisiere Cluster B (Experience): Shopping Center, Outlets & Freizeitparks (Sauberkeit als Visitenkarte).
|
||||
- Entferne reine E-Commerce-Händler ohne physische Kundenfläche.
|
||||
|
||||
4. THE "HYBRID SERVICE" LOGIC (RULE 5):
|
||||
Wann immer du ein "Hartes Constraint" oder eine technische Limitierung identifizierst (z.B. "Kein Winterdienst" oder "Kommt nicht in Ecken"), darfst du dies niemals als reines "Nein" stehen lassen.
|
||||
Wende stattdessen die **"Yes, and..." Logik** an:
|
||||
1. **Identifiziere die Lücke:** (z.B. "Roboter kann bei Schnee nicht fahren").
|
||||
2. **Fülle die Lücke mit Service:** Schlage explizit vor, diesen Teil durch "Wackler Human Manpower" abzudecken.
|
||||
3. **Formuliere den USP:** Positioniere das Gesamtpaket als "100% Coverage" (Roboter + Mensch aus einer Hand).
|
||||
|
||||
# THE PRODUCT MATRIX (CONTEXT)
|
||||
Behalte immer im Hinterkopf, dass wir bereits folgende Produkte im Portfolio haben:
|
||||
1. "Indoor Scrubber 50": Innenreinigung, Hartboden, Fokus: Supermärkte. Message: "Sauberkeit im laufenden Betrieb."
|
||||
2. "Service Bot Bella": Service/Gastro, Indoor. Fokus: Restaurants. Message: "Entlastung für Servicekräfte."
|
||||
""
|
||||
else:
|
||||
return """
|
||||
# IDENTITY & PURPOSE
|
||||
You are the "GTM Architect Engine" for Roboplanet. Your task is to develop a precise Go-to-Market strategy for new technical products (robots).
|
||||
# ... (rest of english instructions)
|
||||
You do not act as a creative copywriter, but as a strategic analyst. Your top goal is product-market fit and operational feasibility.
|
||||
ALWAYS respond in ENGLISH.
|
||||
|
||||
# CONTEXT: THE PARENT COMPANY (WACKLER)
|
||||
We are part of the Wackler Group, a major facility management service provider.
|
||||
Our strategy is NOT "Robots replace humans", but "Hybrid Cleaning":
|
||||
- 80% of work (monotonous area coverage) = Robots.
|
||||
- 20% of work (Edge cases, winter service, stairs, heavy debris) = Manual cleaning by Wackler.
|
||||
|
||||
# STRICT ANALYSIS RULES (MUST FOLLOW):
|
||||
1. TECHNICAL FACT-CHECK (No Hallucinations):
|
||||
- Analyze technical data extremely conservatively.
|
||||
- Vacuum System = No "Winter Service" (snow) and no "Heavy Industry" (metal shavings), unless explicitly stated.
|
||||
- Do not invent features just to fit a target audience.
|
||||
|
||||
2. REGULATORY LOGIC (Traffic Regs):
|
||||
- If Vmax < 20 km/h: Categorically exclude "Public Cities/Streets" (traffic obstruction).
|
||||
- Change Focus: Concentrate exclusively on "Large, contiguous private areas" (Gated Areas).
|
||||
|
||||
3. STRATEGIC TARGETING (Use Case Logic):
|
||||
- Prioritize Cluster A (Efficiency): Logistics Centers & Industrial Hubs (24/7 ops, safety).
|
||||
- Prioritize Cluster B (Experience): Shopping Centers, Outlets & Theme Parks (Cleanliness as a calling card).
|
||||
- Remove pure E-commerce retailers without physical customer areas.
|
||||
|
||||
4. THE "HYBRID SERVICE" LOGIC (RULE 5):
|
||||
Whenever you identify a "Hard Constraint" or a technical limitation (e.g., "No winter service" or "Cannot reach corners"), never let this stand as a simple "No".
|
||||
Instead, apply the **"Yes, and..." logic**:
|
||||
1. **Identify the gap:** (e.g., "Robot cannot operate in snow").
|
||||
2. **Fill the gap with service:** Explicitly suggest covering this part with "Wackler Human Manpower".
|
||||
3. **Formulate the USP:** Position the total package as "100% Coverage" (Robot + Human from a single source).
|
||||
|
||||
# THE PRODUCT MATRIX (CONTEXT)
|
||||
Always keep in mind that we already have the following products in our portfolio:
|
||||
1. "Indoor Scrubber 50": Indoor cleaning, hard floor, supermarkets.
|
||||
2. "Service Bot Bella": Service/Hospitality, indoor. Focus: restaurants. Message: "Relief for service staff."
|
||||
"""
|
||||
|
||||
# --- Database Handlers ---
|
||||
|
||||
def save_project_handler(data):
|
||||
# data contains the full application state
|
||||
# Ensure we have a name for the list view
|
||||
if 'name' not in data:
|
||||
# Try to derive a name from product input or phases
|
||||
input_text = data.get('productInput', '')
|
||||
# Take first 30 chars or first line
|
||||
derived_name = input_text.split('\n')[0][:30] if input_text else "Untitled Strategy"
|
||||
data['name'] = derived_name
|
||||
|
||||
@@ -40,7 +108,6 @@ def save_project_handler(data):
|
||||
print(json.dumps(result))
|
||||
|
||||
def list_projects_handler(data):
|
||||
# data is ignored here
|
||||
projects = market_db_manager.get_all_projects()
|
||||
print(json.dumps(projects))
|
||||
|
||||
@@ -60,48 +127,173 @@ def delete_project_handler(data):
|
||||
# --- AI Handlers ---
|
||||
|
||||
def analyze_product(data):
|
||||
# ... (rest of analyze_product and other AI handlers remain same)
|
||||
product_input = data.get('productInput')
|
||||
lang = data.get('language', 'de')
|
||||
sys_instr = get_system_instruction(lang)
|
||||
|
||||
extraction_prompt = f"""
|
||||
PHASE 1-A: TECHNICAL EXTRACTION
|
||||
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.
|
||||
""" if lang == 'en' else f"""
|
||||
PHASE 1-A: TECHNICAL EXTRACTION
|
||||
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.
|
||||
"""
|
||||
# Fix: Prepend system instruction manually
|
||||
full_extraction_prompt = sys_instr + "\n\n" + extraction_prompt
|
||||
extraction_response = call_openai_chat(full_extraction_prompt, json_mode=True)
|
||||
extraction_data = json.loads(extraction_response)
|
||||
|
||||
conflict_prompt = f"""
|
||||
PHASE 1-B: PORTFOLIO CONFLICT CHECK
|
||||
|
||||
New Product Features: {json.dumps(extraction_data.get('features'))}
|
||||
New Product Constraints: {json.dumps(extraction_data.get('constraints'))}
|
||||
|
||||
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.
|
||||
""" if lang == 'en' else f"""
|
||||
PHASE 1-B: PORTFOLIO CONFLICT CHECK
|
||||
|
||||
Neue Produkt-Features: {json.dumps(extraction_data.get('features'))}
|
||||
Neue Produkt-Constraints: {json.dumps(extraction_data.get('constraints'))}
|
||||
|
||||
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.
|
||||
"""
|
||||
# Fix: Prepend system instruction manually
|
||||
full_conflict_prompt = sys_instr + "\n\n" + conflict_prompt
|
||||
conflict_response = call_openai_chat(full_conflict_prompt, json_mode=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)
|
||||
|
||||
prompt = f"""
|
||||
PHASE 2: ICP DISCOVERY & DATA PROXIES
|
||||
Based on the product features: {json.dumps(phase1_result.get('features'))}
|
||||
And constraints: {json.dumps(phase1_result.get('constraints'))}
|
||||
|
||||
Output JSON format ONLY:
|
||||
{{
|
||||
"icps": [ {{ "name": "Industry Name", "rationale": "Rationale" }} ],
|
||||
"dataProxies": [ {{ "target": "Criteria", "method": "How" }} ]
|
||||
}}
|
||||
""" if lang == 'en' else f"""
|
||||
PHASE 2: ICP DISCOVERY & DATA PROXIES
|
||||
Basierend auf Features: {json.dumps(phase1_result.get('features'))}
|
||||
Und Constraints: {json.dumps(phase1_result.get('constraints'))}
|
||||
|
||||
Output JSON format ONLY:
|
||||
{{
|
||||
"icps": [ {{ "name": "Branchen Name", "rationale": "Begründung" }} ],
|
||||
"dataProxies": [ {{ "target": "Kriterium", "method": "Methode" }} ]
|
||||
}}
|
||||
"""
|
||||
# Fix: Prepend system instruction manually
|
||||
full_prompt = sys_instr + "\n\n" + prompt
|
||||
response = call_openai_chat(full_prompt, json_mode=True)
|
||||
print(response)
|
||||
|
||||
def hunt_whales(data):
|
||||
phase2_result = data.get('phase2Result')
|
||||
lang = data.get('language', 'de')
|
||||
sys_instr = get_system_instruction(lang)
|
||||
prompt = f"PHASE 3: WHALE HUNTING for {json.dumps(phase2_result.get('icps'))}. Identify 3-5 concrete DACH companies per industry and buying center roles. Output JSON ONLY."
|
||||
# Fix: Prepend system instruction manually
|
||||
full_prompt = sys_instr + "\n\n" + prompt
|
||||
response = call_openai_chat(full_prompt, json_mode=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)
|
||||
prompt = f"PHASE 4: STRATEGY Matrix for {json.dumps(phase3_result)}. Apply Hybrid logic. Output JSON ONLY."
|
||||
# Fix: Prepend system instruction manually
|
||||
full_prompt = sys_instr + "\n\n" + prompt
|
||||
response = call_openai_chat(full_prompt, json_mode=True)
|
||||
print(response)
|
||||
|
||||
def generate_assets(data):
|
||||
lang = data.get('language', 'de')
|
||||
sys_instr = get_system_instruction(lang)
|
||||
prompt = f"PHASE 5: GTM STRATEGY REPORT Markdown. Use facts, TCO, ROI. Data: {json.dumps(data)}"
|
||||
# Fix: Prepend system instruction manually
|
||||
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)
|
||||
prompt = f"PHASE 6: Battlecards and MJ Prompts. Data: {json.dumps(data)}. Output JSON ONLY."
|
||||
# Fix: Prepend system instruction manually
|
||||
full_prompt = sys_instr + "\n\n" + prompt
|
||||
response = call_openai_chat(full_prompt, json_mode=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()
|
||||
|
||||
# Read stdin only if there is input, otherwise data is empty dict
|
||||
if not sys.stdin.isatty():
|
||||
try:
|
||||
data = json.loads(sys.stdin.read())
|
||||
except json.JSONDecodeError:
|
||||
except:
|
||||
data = {}
|
||||
else:
|
||||
data = {}
|
||||
|
||||
if args.mode == "analyze_product":
|
||||
analyze_product(data)
|
||||
elif args.mode == "discover_icps":
|
||||
discover_icps(data)
|
||||
elif args.mode == "hunt_whales":
|
||||
hunt_whales(data)
|
||||
elif args.mode == "develop_strategy":
|
||||
develop_strategy(data)
|
||||
elif args.mode == "generate_assets":
|
||||
generate_assets(data)
|
||||
elif args.mode == "generate_sales_enablement":
|
||||
generate_sales_enablement(data)
|
||||
# DB Modes
|
||||
elif args.mode == "save_project":
|
||||
save_project_handler(data)
|
||||
elif args.mode == "list_projects":
|
||||
list_projects_handler(data)
|
||||
elif args.mode == "load_project":
|
||||
load_project_handler(data)
|
||||
elif args.mode == "delete_project":
|
||||
delete_project_handler(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({"status": "error", "message": f"Unknown mode: {args.mode}"}))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print(json.dumps({"error": f"Unknown mode: {args.mode}"}))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user