Dateien nach "gtm-architect/v2" hochladen
This commit is contained in:
1
gtm-architect/v2/.env.local
Normal file
1
gtm-architect/v2/.env.local
Normal file
@@ -0,0 +1 @@
|
||||
GEMINI_API_KEY=PLACEHOLDER_API_KEY
|
||||
24
gtm-architect/v2/.gitignore
vendored
Normal file
24
gtm-architect/v2/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
845
gtm-architect/v2/geminiService.ts
Normal file
845
gtm-architect/v2/geminiService.ts
Normal file
@@ -0,0 +1,845 @@
|
||||
import { GoogleGenAI, Type } from "@google/genai";
|
||||
import { Phase1Data, Phase2Data, Phase3Data, Phase4Data, Phase6Data, Phase7Data, Phase8Data, Phase9Data, 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
- 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
|
||||
- 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
|
||||
|
||||
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?
|
||||
2. **Deflect & Solve:** Formuliere eine präzise, faktenbasierte Antwort für den Vertriebler (Battlecard).
|
||||
3. **Visual Context:** Erstelle präzise "Text-to-Image Prompts" (für Midjourney/DALL-E).
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"battlecards": [
|
||||
{
|
||||
"persona": "Rolle",
|
||||
"objection": "Einwand",
|
||||
"responseScript": "Antwort"
|
||||
}
|
||||
],
|
||||
"visualPrompts": [
|
||||
{
|
||||
"title": "Titel",
|
||||
"context": "Kontext",
|
||||
"prompt": "Prompt..."
|
||||
}
|
||||
]
|
||||
}
|
||||
` : `
|
||||
PHASE 6: SALES ENABLEMENT & VISUALS
|
||||
|
||||
CONTEXT:
|
||||
- Product Features: ${JSON.stringify(phase1Data.features)}
|
||||
- Accounts (Personas): ${JSON.stringify(phase3Data.roles)}
|
||||
- Strategy: ${JSON.stringify(phase4Data.strategyMatrix)}
|
||||
|
||||
TASK:
|
||||
1. **Anticipate Friction:** Identify hardest objections.
|
||||
2. **Deflect & Solve:** Formulate response script (Battlecard).
|
||||
3. **Visual Context:** Create "Text-to-Image Prompts".
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"battlecards": [
|
||||
{
|
||||
"persona": "Role",
|
||||
"objection": "Objection",
|
||||
"responseScript": "Response"
|
||||
}
|
||||
],
|
||||
"visualPrompts": [
|
||||
{
|
||||
"title": "Title",
|
||||
"context": "Context",
|
||||
"prompt": "Prompt..."
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt,
|
||||
config: {
|
||||
systemInstruction: sysInstr,
|
||||
responseMimeType: "application/json",
|
||||
}
|
||||
});
|
||||
|
||||
return cleanAndParseJson<Phase6Data>(response.text);
|
||||
};
|
||||
|
||||
// --- Phase 7: Vertical Landing Page Copy ---
|
||||
export const generateLandingPageCopy = async (
|
||||
phase4Data: Phase4Data,
|
||||
phase2Data: Phase2Data,
|
||||
lang: Language
|
||||
): Promise<Phase7Data> => {
|
||||
const ai = getClient();
|
||||
const sysInstr = getSystemInstruction(lang);
|
||||
|
||||
const prompt = lang === 'de' ? `
|
||||
PHASE 7: VERTICAL LANDING PAGE COPY (Conversion Optimization)
|
||||
|
||||
ICPs: ${JSON.stringify(phase2Data.icps)}
|
||||
Strategy: ${JSON.stringify(phase4Data.strategyMatrix)}
|
||||
|
||||
AUFGABE:
|
||||
1. Identifiziere generische Aussagen und transformiere sie in spezifische Nutzenargumente für die Top 2 ICPs.
|
||||
2. Wende die "Wackler-Symbiose" an: Erwähne Service/Hygiene als Trust-Element.
|
||||
|
||||
Erstelle für die Top 2 ICPs jeweils einen Landingpage-Entwurf (Hero Section).
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"landingPages": [
|
||||
{
|
||||
"industry": "Zielbranche",
|
||||
"headline": "Nutzenorientierte Headline (Kein Produktname)",
|
||||
"subline": "Tech Enabler + Wackler Service Garantie",
|
||||
"bullets": ["Vorteil 1", "Vorteil 2", "Vorteil 3"],
|
||||
"cta": "Branchen-spezifischer CTA"
|
||||
}
|
||||
]
|
||||
}
|
||||
` : `
|
||||
PHASE 7: VERTICAL LANDING PAGE COPY (Conversion Optimization)
|
||||
|
||||
ICPs: ${JSON.stringify(phase2Data.icps)}
|
||||
Strategy: ${JSON.stringify(phase4Data.strategyMatrix)}
|
||||
|
||||
TASK:
|
||||
1. Transform generic features into specific benefits for the Top 2 ICPs.
|
||||
2. Apply "Wackler Symbiosis": Mention service/hygiene as trust element.
|
||||
|
||||
Create Landing Page Drafts (Hero Section) for Top 2 ICPs.
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"landingPages": [
|
||||
{
|
||||
"industry": "Target Industry",
|
||||
"headline": "Benefit-oriented Headline",
|
||||
"subline": "Tech Enabler + Wackler Service Guarantee",
|
||||
"bullets": ["Benefit 1", "Benefit 2", "Benefit 3"],
|
||||
"cta": "Industry-specific CTA"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt,
|
||||
config: {
|
||||
systemInstruction: sysInstr,
|
||||
responseMimeType: "application/json",
|
||||
}
|
||||
});
|
||||
|
||||
return cleanAndParseJson<Phase7Data>(response.text);
|
||||
};
|
||||
|
||||
// --- Phase 8: Business Case Builder ---
|
||||
export const buildBusinessCase = async (
|
||||
phase2Data: Phase2Data,
|
||||
phase1Data: Phase1Data,
|
||||
lang: Language
|
||||
): Promise<Phase8Data> => {
|
||||
const ai = getClient();
|
||||
const sysInstr = getSystemInstruction(lang);
|
||||
|
||||
const prompt = lang === 'de' ? `
|
||||
PHASE 8: BUSINESS CASE BUILDER (The CFO Pitch)
|
||||
|
||||
Input:
|
||||
ICPs: ${JSON.stringify(phase2Data.icps)}
|
||||
Features: ${JSON.stringify(phase1Data.features)}
|
||||
|
||||
AUFGABE:
|
||||
1. Schätze Lohnkosten/Probleme der Zielgruppe (Personalmangel, Krankheitsstand).
|
||||
2. Setze den Roboter (Leasing ca. 330-600€/Monat) dagegen.
|
||||
3. Entwickle eine ROI-Logik.
|
||||
|
||||
Erstelle für jeden ICP einen "Financial Argumentation Guide".
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"businessCases": [
|
||||
{
|
||||
"industry": "Branche",
|
||||
"costDriver": "Was kostet der Status Quo? (z.B. Laufwege)",
|
||||
"efficiencyGain": "Wie amortisiert sich der Roboter?",
|
||||
"riskArgument": "Warum Miete + Wackler Service sicherer ist als Kauf"
|
||||
}
|
||||
]
|
||||
}
|
||||
` : `
|
||||
PHASE 8: BUSINESS CASE BUILDER (The CFO Pitch)
|
||||
|
||||
Input:
|
||||
ICPs: ${JSON.stringify(phase2Data.icps)}
|
||||
Features: ${JSON.stringify(phase1Data.features)}
|
||||
|
||||
TASK:
|
||||
1. Estimate labor costs/pain points (shortage, sick leave).
|
||||
2. Compare against Robot Leasing (approx 330-600€/month).
|
||||
3. Develop ROI logic.
|
||||
|
||||
Create a "Financial Argumentation Guide" for each ICP.
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"businessCases": [
|
||||
{
|
||||
"industry": "Industry",
|
||||
"costDriver": "Status Quo Cost",
|
||||
"efficiencyGain": "Amortization Logic",
|
||||
"riskArgument": "Why Lease + Service is safer than buying"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt,
|
||||
config: {
|
||||
systemInstruction: sysInstr,
|
||||
responseMimeType: "application/json",
|
||||
}
|
||||
});
|
||||
|
||||
return cleanAndParseJson<Phase8Data>(response.text);
|
||||
};
|
||||
|
||||
// --- Phase 9: Feature-to-Value Translator ---
|
||||
export const translateTech = async (
|
||||
phase1Data: Phase1Data,
|
||||
phase4Data: Phase4Data,
|
||||
lang: Language
|
||||
): Promise<Phase9Data> => {
|
||||
const ai = getClient();
|
||||
const sysInstr = getSystemInstruction(lang);
|
||||
|
||||
const prompt = lang === 'de' ? `
|
||||
PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR
|
||||
|
||||
Input Features: ${JSON.stringify(phase1Data.features)}
|
||||
Strategy Pains: ${JSON.stringify(phase4Data.strategyMatrix.map(s => s.painPoint))}
|
||||
|
||||
AUFGABE:
|
||||
1. Nimm ein technisches Feature (z.B. "LiDAR").
|
||||
2. Frage: "So what?" (Was bringt das im Alltag?).
|
||||
3. Frage nochmal: "So what?" (Was ist der emotionale oder finanzielle Vorteil?).
|
||||
* Kette: LiDAR -> Erkennt Hindernisse -> Stößt nicht an -> Keine Unfälle/Haftung -> **Sicherheit & Ruhe**.
|
||||
4. Formuliere den Benefit **ohne** Fachchinesisch. Nutze emotionale Trigger.
|
||||
|
||||
Erstelle eine Tabelle für die Website-Kommunikation:
|
||||
* **feature:** (Klein gedruckt für Techies)
|
||||
* **story:** (Die Story) Der emotionale/wirtschaftliche Nutzen in der Sprache der Zielgruppe.
|
||||
* **headline:** (Die Headline) Ein knackiger Slogan für dieses Feature.
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"techTranslations": [
|
||||
{
|
||||
"feature": "Technisches Feature",
|
||||
"story": "Benefit Story (So what chain)",
|
||||
"headline": "Knackiger Slogan"
|
||||
}
|
||||
]
|
||||
}
|
||||
` : `
|
||||
PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR
|
||||
|
||||
Input Features: ${JSON.stringify(phase1Data.features)}
|
||||
Strategy Pains: ${JSON.stringify(phase4Data.strategyMatrix.map(s => s.painPoint))}
|
||||
|
||||
TASK:
|
||||
1. Take a technical feature (e.g. "LiDAR").
|
||||
2. Ask: "So what?" (What does it do in daily life?).
|
||||
3. Ask again: "So what?" (What is the emotional or financial benefit?).
|
||||
* Chain: LiDAR -> See obstacles -> No collision -> No accidents/liability -> **Safety & Peace of Mind**.
|
||||
4. Formulate the benefit **without** jargon. Use emotional triggers.
|
||||
|
||||
Create a table for website communication:
|
||||
* **feature:** (Small print for techies)
|
||||
* **story:** (The Story) The emotional/economic benefit in target language.
|
||||
* **headline:** (The Headline) A punchy slogan for this feature.
|
||||
|
||||
Output JSON format ONLY:
|
||||
{
|
||||
"techTranslations": [
|
||||
{
|
||||
"feature": "Tech Feature",
|
||||
"story": "Benefit Story (So what chain)",
|
||||
"headline": "Punchy Slogan"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt,
|
||||
config: {
|
||||
systemInstruction: sysInstr,
|
||||
responseMimeType: "application/json",
|
||||
}
|
||||
});
|
||||
|
||||
return cleanAndParseJson<Phase9Data>(response.text);
|
||||
};
|
||||
|
||||
export const translateReportToEnglish = async (reportMarkdown: string): Promise<string> => {
|
||||
const ai = getClient();
|
||||
const prompt = `
|
||||
TASK: Translate the following GTM Strategy Report into professional Business English.
|
||||
CONTEXT: It is a B2B robotic strategy document.
|
||||
CONSTRAINT: Keep the Markdown structure (#, ##, -, | tables) EXACTLY as is. Only translate the content.
|
||||
|
||||
INPUT:
|
||||
${reportMarkdown}
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt
|
||||
});
|
||||
|
||||
return response.text || reportMarkdown;
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
};
|
||||
91
gtm-architect/v2/index.html
Normal file
91
gtm-architect/v2/index.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Roboplanet GTM Architect</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
mono: ['JetBrains Mono', 'monospace'],
|
||||
},
|
||||
colors: {
|
||||
robo: {
|
||||
900: '#0f172a',
|
||||
800: '#1e293b',
|
||||
700: '#334155',
|
||||
500: '#3b82f6',
|
||||
400: '#60a5fa',
|
||||
accent: '#06b6d4'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
/* Default Light Mode colors */
|
||||
background-color: #f8fafc;
|
||||
color: #0f172a;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
/* Dark Mode overrides via class */
|
||||
.dark body {
|
||||
background-color: #0f172a;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1; /* slate-300 */
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8; /* slate-400 */
|
||||
}
|
||||
|
||||
.dark ::-webkit-scrollbar-track {
|
||||
background: #1e293b;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
background: #475569;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-thumb:hover {
|
||||
background: #64748b;
|
||||
}
|
||||
</style>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"@google/genai": "https://esm.sh/@google/genai@^1.34.0",
|
||||
"react/": "https://esm.sh/react@^19.2.3/",
|
||||
"react": "https://esm.sh/react@^19.2.3",
|
||||
"react-markdown": "https://esm.sh/react-markdown@^10.1.0",
|
||||
"remark-gfm": "https://esm.sh/remark-gfm@^4.0.0",
|
||||
"react-dom/": "https://esm.sh/react-dom@^19.2.3/",
|
||||
"lucide-react": "https://esm.sh/lucide-react@^0.562.0"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
15
gtm-architect/v2/index.tsx
Normal file
15
gtm-architect/v2/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
throw new Error("Could not find root element to mount to");
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
Reference in New Issue
Block a user