Dateien nach "gtm-architect/v2" hochladen

This commit is contained in:
2026-01-02 18:20:25 +00:00
parent 3aee685f23
commit efd9628fd4
5 changed files with 976 additions and 0 deletions

View File

@@ -0,0 +1 @@
GEMINI_API_KEY=PLACEHOLDER_API_KEY

24
gtm-architect/v2/.gitignore vendored Normal file
View 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?

View 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");
}
};

View 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>

View 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>
);