608 lines
22 KiB
TypeScript
608 lines
22 KiB
TypeScript
import { GoogleGenAI, Type } from "@google/genai";
|
|
import { Phase1Data, Phase2Data, Phase3Data, Phase4Data, Phase6Data, Language } from "./types";
|
|
|
|
const getSystemInstruction = (lang: Language) => {
|
|
if (lang === 'de') {
|
|
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).
|
|
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 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."
|
|
`;
|
|
}
|
|
};
|
|
|
|
const getClient = () => {
|
|
const apiKey = process.env.API_KEY;
|
|
if (!apiKey) {
|
|
throw new Error("API_KEY not found in environment variables");
|
|
}
|
|
return new GoogleGenAI({ apiKey });
|
|
};
|
|
|
|
// Helper to safely parse JSON from AI response
|
|
const cleanAndParseJson = <T>(text: string | undefined): T => {
|
|
if (!text) throw new Error("AI returned empty response");
|
|
|
|
let cleaned = text.trim();
|
|
// Remove markdown formatting if present
|
|
if (cleaned.startsWith('```json')) {
|
|
cleaned = cleaned.replace(/^```json\n?/, '').replace(/\n?```$/, '');
|
|
} else if (cleaned.startsWith('```')) {
|
|
cleaned = cleaned.replace(/^```\n?/, '').replace(/\n?```$/, '');
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(cleaned);
|
|
} catch (e) {
|
|
console.error("JSON Parse Error. Raw text:", text);
|
|
throw new Error("Failed to parse AI response. The model output was likely malformed or truncated. Please try again with shorter input.");
|
|
}
|
|
};
|
|
|
|
// --- Phase 1: Product Analysis ---
|
|
export const analyzeProduct = async (input: string, lang: Language): Promise<Phase1Data> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
// STEP 1.1: Extract Specs & Constraints
|
|
const extractionPrompt = lang === 'de' ? `
|
|
PHASE 1-A: TECHNICAL EXTRACTION
|
|
Input Product Description: "${input.substring(0, 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.
|
|
` : `
|
|
PHASE 1-A: TECHNICAL EXTRACTION
|
|
Input Product Description: "${input.substring(0, 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.
|
|
`;
|
|
|
|
const extractionResponse = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: extractionPrompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
responseSchema: {
|
|
type: Type.OBJECT,
|
|
properties: {
|
|
features: { type: Type.ARRAY, items: { type: Type.STRING } },
|
|
constraints: { type: Type.ARRAY, items: { type: Type.STRING } },
|
|
rawAnalysis: { type: Type.STRING }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const extractionData = cleanAndParseJson<{
|
|
features: string[];
|
|
constraints: string[];
|
|
rawAnalysis: string;
|
|
}>(extractionResponse.text);
|
|
|
|
// STEP 1.2: Portfolio Conflict Check
|
|
const conflictPrompt = lang === 'de' ? `
|
|
PHASE 1-B: PORTFOLIO CONFLICT CHECK
|
|
|
|
Neue Produkt-Features: ${JSON.stringify(extractionData.features)}
|
|
Neue Produkt-Constraints: ${JSON.stringify(extractionData.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.
|
|
` : `
|
|
PHASE 1-B: PORTFOLIO CONFLICT CHECK
|
|
|
|
New Product Features: ${JSON.stringify(extractionData.features)}
|
|
New Product Constraints: ${JSON.stringify(extractionData.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.
|
|
`;
|
|
|
|
const conflictResponse = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: conflictPrompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
responseSchema: {
|
|
type: Type.OBJECT,
|
|
properties: {
|
|
conflictCheck: {
|
|
type: Type.OBJECT,
|
|
properties: {
|
|
hasConflict: { type: Type.BOOLEAN },
|
|
details: { type: Type.STRING },
|
|
relatedProduct: { type: Type.STRING, nullable: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const conflictData = cleanAndParseJson<{
|
|
conflictCheck: Phase1Data['conflictCheck'];
|
|
}>(conflictResponse.text);
|
|
|
|
return {
|
|
...extractionData,
|
|
...conflictData
|
|
};
|
|
};
|
|
|
|
// --- Phase 2: ICP Discovery ---
|
|
export const discoverICPs = async (phase1Data: Phase1Data, lang: Language): Promise<Phase2Data> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
const prompt = lang === 'de' ? `
|
|
PHASE 2: ICP DISCOVERY & DATA PROXIES
|
|
Basierend auf Features: ${JSON.stringify(phase1Data.features)}
|
|
Und Constraints: ${JSON.stringify(phase1Data.constraints)}
|
|
|
|
Aufgabe:
|
|
1. Negative Selektion: Welche Branchen sind unmöglich? (Denke an Vmax & Vakuum-Regeln!)
|
|
2. High Pain: Identifiziere Cluster A (Logistik/Industrie) und Cluster B (Shopping/Outlets).
|
|
3. Data Proxy Generierung: Wie finden wir diese digital (z.B. Satellit, Register)?
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"icps": [
|
|
{ "name": "Branchen Name", "rationale": "Warum das passt (max 1 Satz)" }
|
|
],
|
|
"dataProxies": [
|
|
{ "target": "Spezifisches Kriterium", "method": "Wie zu finden (z.B. Scraping)" }
|
|
]
|
|
}
|
|
` : `
|
|
PHASE 2: ICP DISCOVERY & DATA PROXIES
|
|
Based on the product features: ${JSON.stringify(phase1Data.features)}
|
|
And constraints: ${JSON.stringify(phase1Data.constraints)}
|
|
|
|
Task:
|
|
1. Negative Selection: Which industries are impossible? (Remember Vmax & Vacuum rules!)
|
|
2. High Pain: Identify Cluster A (Logistics/Industry) and Cluster B (Shopping/Outlets).
|
|
3. Data Proxy Generation: How to find them digitally via data traces (e.g. satellite, registries).
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"icps": [
|
|
{ "name": "Industry Name", "rationale": "Why this is a good fit (max 1 sentence)" }
|
|
],
|
|
"dataProxies": [
|
|
{ "target": "Specific criteria", "method": "How to find (e.g. scraping, satellite)" }
|
|
]
|
|
}
|
|
`;
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: prompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
}
|
|
});
|
|
|
|
return cleanAndParseJson<Phase2Data>(response.text);
|
|
};
|
|
|
|
// --- Phase 3: Whale Hunting ---
|
|
export const huntWhales = async (phase2Data: Phase2Data, lang: Language): Promise<Phase3Data> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
const prompt = lang === 'de' ? `
|
|
PHASE 3: WHALE HUNTING
|
|
Target ICPs (Branchen): ${JSON.stringify(phase2Data.icps)}
|
|
|
|
Aufgabe:
|
|
1. Gruppiere die "Whales" (Key Accounts) strikt nach den identifizierten ICP-Branchen.
|
|
2. Identifiziere pro Branche 3-5 konkrete Top-Unternehmen im DACH Markt.
|
|
3. Definiere die Buying Center Rollen.
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"whales": [
|
|
{ "industry": "Name der ICP Branche", "accounts": ["Firma A", "Firma B"] }
|
|
],
|
|
"roles": ["Jobtitel 1", "Jobtitel 2"]
|
|
}
|
|
` : `
|
|
PHASE 3: WHALE HUNTING
|
|
Target ICPs (Industries): ${JSON.stringify(phase2Data.icps)}
|
|
|
|
Task:
|
|
1. Group "Whales" (Key Accounts) strictly by the identified ICP industries.
|
|
2. Identify 3-5 concrete top companies in the DACH market per industry.
|
|
3. Define Buying Center Roles.
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"whales": [
|
|
{ "industry": "Name of ICP Industry", "accounts": ["Company A", "Company B"] }
|
|
],
|
|
"roles": ["Job Title 1", "Job Title 2"]
|
|
}
|
|
`;
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: prompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
}
|
|
});
|
|
|
|
return cleanAndParseJson<Phase3Data>(response.text);
|
|
};
|
|
|
|
// --- Phase 4: Strategy ---
|
|
export const developStrategy = async (phase3Data: Phase3Data, phase1Data: Phase1Data, lang: Language): Promise<Phase4Data> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
// Flatten accounts for the prompt to maintain context without complex structure
|
|
const allAccounts = phase3Data.whales.flatMap(w => w.accounts);
|
|
|
|
const prompt = lang === 'de' ? `
|
|
PHASE 4: STRATEGY & ANGLE DEVELOPMENT
|
|
Accounts: ${JSON.stringify(allAccounts)}
|
|
Target Industries: ${JSON.stringify(phase3Data.whales.map(w => w.industry))}
|
|
Features: ${JSON.stringify(phase1Data.features)}
|
|
|
|
Aufgabe:
|
|
1. Entwickle einen spezifischen "Angle" (Aufhänger) pro Zielgruppe/Industrie.
|
|
2. Consistency Check gegen Product Matrix (Differenzierung zu Scrubber 50/Bella).
|
|
3. **WICHTIG:** Wende die "Hybrid Service Logic" an, wenn technische Constraints existieren! (Wackler Manpower als Ergänzung).
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"strategyMatrix": [
|
|
{
|
|
"segment": "Zielsegment",
|
|
"painPoint": "Spezifischer Schmerz (Kurz)",
|
|
"angle": "Unser Marketing Angle (Kurz)",
|
|
"differentiation": "Wie es sich unterscheidet (Kurz)"
|
|
}
|
|
]
|
|
}
|
|
` : `
|
|
PHASE 4: STRATEGY & ANGLE DEVELOPMENT
|
|
Accounts: ${JSON.stringify(allAccounts)}
|
|
Target Industries: ${JSON.stringify(phase3Data.whales.map(w => w.industry))}
|
|
Product Features: ${JSON.stringify(phase1Data.features)}
|
|
|
|
Task:
|
|
1. Develop specific "Angle" per target/industry.
|
|
2. Consistency Check against Product Matrix (ensure differentiation from Scrubber 50/Bella).
|
|
3. **IMPORTANT:** Apply "Hybrid Service Logic" if technical constraints exist! (Wackler Manpower as supplement).
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"strategyMatrix": [
|
|
{
|
|
"segment": "Target Segment",
|
|
"painPoint": "Specific Pain (Short)",
|
|
"angle": "Our Marketing Angle (Short)",
|
|
"differentiation": "How it differs (Short)"
|
|
}
|
|
]
|
|
}
|
|
`;
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: prompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
}
|
|
});
|
|
|
|
return cleanAndParseJson<Phase4Data>(response.text);
|
|
};
|
|
|
|
// --- Phase 5: Assets & Report ---
|
|
export const generateAssets = async (
|
|
phase4Data: Phase4Data,
|
|
phase3Data: Phase3Data,
|
|
phase2Data: Phase2Data,
|
|
phase1Data: Phase1Data,
|
|
lang: Language
|
|
): Promise<string> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
const prompt = lang === 'de' ? `
|
|
PHASE 5: ASSET GENERATION & FINAL REPORT
|
|
|
|
CONTEXT DATA:
|
|
- Technical: ${JSON.stringify(phase1Data)}
|
|
- ICPs: ${JSON.stringify(phase2Data)}
|
|
- Targets (Whales): ${JSON.stringify(phase3Data)}
|
|
- Strategy: ${JSON.stringify(phase4Data)}
|
|
|
|
AUFGABE:
|
|
1. Erstelle einen "GTM STRATEGY REPORT" in Markdown.
|
|
2. Struktur des Reports:
|
|
- Executive Summary
|
|
- Produkt Analyse (Features & Constraints)
|
|
- Zielgruppen (ICPs & Data Proxies)
|
|
- **Target Accounts (Whales) & Buying Center (DIESE SEKTION MUSS ENTHALTEN SEIN UND ALLE ACCOUNTS GRUPPIERT AUFLISTEN!)**
|
|
- Strategie-Matrix (Pains & Angles)
|
|
- Assets (LinkedIn, Email, Landing Page Headline)
|
|
3. Self-Correction: Entferne "Marketing Bla-Bla". Nutze Fakten, TCO, ROI.
|
|
4. Hybrid-Check: Stelle sicher, dass die "Hybrid Service Logic" (Wackler Manpower) in der Strategie und den Assets sichtbar ist, falls Constraints vorliegen.
|
|
|
|
Output:
|
|
Gib striktes MARKDOWN zurück. Beginne mit "# GTM STRATEGY REPORT".
|
|
` : `
|
|
PHASE 5: ASSET GENERATION & FINAL REPORT
|
|
|
|
CONTEXT DATA:
|
|
- Technical: ${JSON.stringify(phase1Data)}
|
|
- ICPs: ${JSON.stringify(phase2Data)}
|
|
- Targets (Whales): ${JSON.stringify(phase3Data)}
|
|
- Strategy: ${JSON.stringify(phase4Data)}
|
|
|
|
TASK:
|
|
1. Create a "GTM STRATEGY REPORT" in Markdown.
|
|
2. Report Structure:
|
|
- Executive Summary
|
|
- Product Analysis (Features & Constraints)
|
|
- Target Audience (ICPs & Data Proxies)
|
|
- **Target Accounts (Whales) & Buying Center (THIS SECTION MUST BE INCLUDED AND LIST ALL ACCOUNTS GROUPED BY INDUSTRY!)**
|
|
- Strategy Matrix (Pains & Angles)
|
|
- Assets (LinkedIn, Email, Landing Page Headline)
|
|
3. Self-Correction: Remove "Marketing Fluff". Use Facts, TCO, ROI.
|
|
4. Hybrid-Check: Ensure "Hybrid Service Logic" (Wackler Manpower) is visible in strategy and assets if constraints exist.
|
|
|
|
Output:
|
|
Return strictly MARKDOWN formatted text. Start with "# GTM STRATEGY REPORT".
|
|
`;
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: prompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
}
|
|
});
|
|
|
|
return response.text || "Error generating assets.";
|
|
};
|
|
|
|
// --- Phase 6: Sales Enablement & Visuals ---
|
|
export const generateSalesEnablement = async (
|
|
phase4Data: Phase4Data,
|
|
phase3Data: Phase3Data,
|
|
phase1Data: Phase1Data,
|
|
lang: Language
|
|
): Promise<Phase6Data> => {
|
|
const ai = getClient();
|
|
const sysInstr = getSystemInstruction(lang);
|
|
|
|
const prompt = lang === 'de' ? `
|
|
PHASE 6: SALES ENABLEMENT & VISUALS (THE CLOSING KIT)
|
|
|
|
CONTEXT:
|
|
- Produkt Features: ${JSON.stringify(phase1Data.features)}
|
|
- Accounts (Personas): ${JSON.stringify(phase3Data.roles)}
|
|
- Strategie: ${JSON.stringify(phase4Data.strategyMatrix)}
|
|
|
|
AUFGABE:
|
|
1. **Anticipate Friction:** Versetze dich in die Lage der identifizierten Personas. Was ist deren härtester Einwand? (z.B. "Zu teuer", "Sicherheitsrisiko", "Job-Verlust").
|
|
2. **Deflect & Solve:** Formuliere eine präzise, faktenbasierte Antwort für den Vertriebler (Battlecard). Nutze Hybrid-Argumente falls nötig.
|
|
3. **Visual Context:** Erstelle präzise "Text-to-Image Prompts" (für Midjourney/DALL-E), die das Produkt im perfekten Nutzungskontext zeigen.
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"battlecards": [
|
|
{
|
|
"persona": "Rolle (z.B. Logistikleiter)",
|
|
"objection": "Härtester Einwand als Zitat",
|
|
"responseScript": "Die Antwort (Argumentation, Fakten, ROI)"
|
|
}
|
|
],
|
|
"visualPrompts": [
|
|
{
|
|
"title": "Titel (z.B. Efficiency Shot)",
|
|
"context": "Cluster A/B Kontext",
|
|
"prompt": "Detaillierter Prompt Code für Midjourney..."
|
|
}
|
|
]
|
|
}
|
|
Erstelle mindestens 2 Battlecards und 3 Visual Prompts (Efficiency, Experience, Detail).
|
|
` : `
|
|
PHASE 6: SALES ENABLEMENT & VISUALS (THE CLOSING KIT)
|
|
|
|
CONTEXT:
|
|
- Product Features: ${JSON.stringify(phase1Data.features)}
|
|
- Accounts (Personas): ${JSON.stringify(phase3Data.roles)}
|
|
- Strategy: ${JSON.stringify(phase4Data.strategyMatrix)}
|
|
|
|
TASK:
|
|
1. **Anticipate Friction:** Put yourself in the shoes of the personas. What is their hardest objection? (e.g. "Too expensive", "Safety risk", "Job loss").
|
|
2. **Deflect & Solve:** Formulate a precise, fact-based response for sales (Battlecard). Use hybrid arguments if necessary.
|
|
3. **Visual Context:** Create precise "Text-to-Image Prompts" (for Midjourney/DALL-E) showing the product in the perfect context.
|
|
|
|
Output JSON format ONLY:
|
|
{
|
|
"battlecards": [
|
|
{
|
|
"persona": "Role (e.g. Logistics Manager)",
|
|
"objection": "Hardest objection as quote",
|
|
"responseScript": "The response (Argumentation, Facts, ROI)"
|
|
}
|
|
],
|
|
"visualPrompts": [
|
|
{
|
|
"title": "Title (e.g. Efficiency Shot)",
|
|
"context": "Cluster A/B Context",
|
|
"prompt": "Detailed Prompt Code for Midjourney..."
|
|
}
|
|
]
|
|
}
|
|
Create at least 2 Battlecards and 3 Visual Prompts (Efficiency, Experience, Detail).
|
|
`;
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-3-flash-preview',
|
|
contents: prompt,
|
|
config: {
|
|
systemInstruction: sysInstr,
|
|
responseMimeType: "application/json",
|
|
}
|
|
});
|
|
|
|
return cleanAndParseJson<Phase6Data>(response.text);
|
|
};
|
|
|
|
export const generateConceptImage = async (prompt: string, referenceImagesBase64?: string[]): Promise<string> => {
|
|
const ai = getClient();
|
|
try {
|
|
const parts: any[] = [];
|
|
|
|
// If reference images are provided, add them to parts
|
|
if (referenceImagesBase64 && referenceImagesBase64.length > 0) {
|
|
referenceImagesBase64.forEach(img => {
|
|
const cleanBase64 = img.split(',')[1] || img;
|
|
const match = img.match(/data:(.*?);base64/);
|
|
const mimeType = match ? match[1] : 'image/png';
|
|
|
|
parts.push({
|
|
inlineData: {
|
|
mimeType: mimeType,
|
|
data: cleanBase64
|
|
}
|
|
});
|
|
});
|
|
|
|
parts.push({
|
|
text: "Show the product in these reference images in the following context. Keep the product appearance consistent. " + prompt
|
|
});
|
|
} else {
|
|
// Text-only prompt
|
|
parts.push({ text: prompt });
|
|
}
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-2.5-flash-image',
|
|
contents: { parts },
|
|
config: {
|
|
imageConfig: {
|
|
aspectRatio: "16:9",
|
|
}
|
|
}
|
|
});
|
|
|
|
for (const part of response.candidates?.[0]?.content?.parts || []) {
|
|
if (part.inlineData) {
|
|
return `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`;
|
|
}
|
|
}
|
|
throw new Error("No image generated");
|
|
} catch (e: any) {
|
|
console.error("Image generation failed", e);
|
|
throw new Error(e.message || "Failed to generate image");
|
|
}
|
|
}; |