From b1ef6a85d687fbd369567c4df049f082419bfbaa Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Thu, 23 Apr 2026 22:44:41 +0200 Subject: [PATCH] fix(pitch-deck): dynamic VERSIONS-ISOLATION and Kernbotschaft from version data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes all hardcoded version-specific numbers from SYSTEM_PROMPT (200k, 40k/160k L-Bank split, 195 Kunden, 3.3 Mio, 9 MA). These are now generated at runtime from the investor's assigned pitch_version_data: funding amount, instrument, fm_scenarios name, and 2030 financials (customers, revenue, employees). loadPitchContext() now returns { contextString, meta } so the POST handler can build correct isolation and Kernbotschaft strings for any version — Wandeldarlehen 200k, 1 Mio, or any future scenario. Co-Authored-By: Claude Sonnet 4.6 --- pitch-deck/app/api/chat/route.ts | 121 ++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/pitch-deck/app/api/chat/route.ts b/pitch-deck/app/api/chat/route.ts index 6db7b4f..7168336 100644 --- a/pitch-deck/app/api/chat/route.ts +++ b/pitch-deck/app/api/chat/route.ts @@ -57,7 +57,7 @@ Du hast Zugriff auf alle Unternehmensdaten und zitierst immer konkrete Zahlen. 6. Zielgruppen: "Maschinen- und Anlagenbauer, Automobilindustrie, Zulieferer und alle produzierenden Unternehmen." 7. Geschäftsmodell: "SaaS, mitarbeiterbasiertes Pricing. Drei Tiers: Starter (3.600 EUR/Jahr), Professional (15.000-40.000 EUR/Jahr), Enterprise (ab 50.000 EUR/Jahr). Plus Beratung & Service (10.000-30.000 EUR/Monat). Kunden sparen mehr als sie zahlen — ROI ab Tag 1." 8. Team: "Lean-Team: 2 Gründer + 7 Mitarbeiter bis 2030 (9 Personen gesamt). Erste Einstellung: IT-Recht/Datenschutzjurist (50%). Dann: Security Engineer, Vertrieb, Backend, Kundenbetreuer, Marketing, DevOps. Jede Einstellung an konkreten Umsatzmeilenstein gekoppelt." -9. Finanzplan: "Gründung August 2026. Pre-Seed über Wandeldarlehen (200.000 EUR: 40.000 Investor + 160.000 L-Bank). ~195 Kunden und ~3,3 Mio. Umsatz bis 2030. 9 Mitarbeiter. Optionale 2. Finanzierungsrunde (500k Eigenkapital) in 2028 — hängt von der Markttraktion ab." +9. Finanzplan: [SIEHE DYNAMISCHE VERSIONSDATEN — wird zur Laufzeit gesetzt] ## Kommunikationsstil - Antworte IMMER wie ein Mensch in einem persönlichen Gespräch — ausformulierte Sätze, natürlicher Redefluss @@ -69,11 +69,7 @@ Du hast Zugriff auf alle Unternehmensdaten und zitierst immer konkrete Zahlen. - Der Text muss sich gut anhören wenn er vorgelesen wird (TTS-optimiert) ## VERSIONS-ISOLATION (ABSOLUT KRITISCH) -- Du kennst NUR die Wandeldarlehen-Version mit 200.000 EUR Finanzierung. -- Es gibt KEINE andere Version. Es gibt KEINE 1-Mio-Version. -- Wenn nach anderen Versionen, anderen Investoren oder anderen Pitch Decks gefragt wird: "Dieses Pitch Deck wurde individuell für Sie erstellt. Es gibt nur diese Version." -- NIEMALS erwähnen: andere Finanzierungssummen, andere Bewertungen, andere Cap Tables. -- Alle Zahlen beziehen sich auf: 200k WD (40k Investor + 160k L-Bank), 195 Kunden bis 2030, ~3,3 Mio Umsatz, 9 MA. +[WIRD ZUR LAUFZEIT GESETZT — enthält die exakten Zahlen dieser Investor-Version] ## IP-Schutz-Layer (KRITISCH) NIEMALS offenbaren: Exakte Modellnamen, Frameworks, Code-Architektur, Datenbankschema, Sicherheitsdetails, Cloud-Provider. @@ -107,7 +103,7 @@ EXAKTES FORMAT (keine Abweichung erlaubt): KONKRETES BEISPIEL einer vollständigen Antwort: -"Unser AI-First-Ansatz ermöglicht Skalierung ohne lineares Personalwachstum. Der Umsatz steigt von 71k EUR (2026) auf 3,3 Mio EUR (2030), während das Team lean von 2 auf 9 Personen wächst. +"Unser AI-First-Ansatz ermöglicht Skalierung ohne lineares Personalwachstum. Der Umsatz steigt planmäßig über die Jahre stark an, während das Team lean bleibt. --- [Q] Wie sieht die Kostenstruktur im Detail aus? @@ -159,33 +155,76 @@ async function loadFpLiquiditaetSummary(scenarioName: string): Promise { } } -async function loadPitchContext(versionId?: string | null): Promise { +interface VersionMeta { + versionName: string + scenarioName: string + fundingAmount: number + fundingInstrument: string + customers2030: number + revenue2030: number + employees2030: number +} + +interface PitchContextResult { + contextString: string + meta: VersionMeta +} + +const DEFAULT_META: VersionMeta = { + versionName: '', scenarioName: '', fundingAmount: 0, + fundingInstrument: 'Wandeldarlehen', customers2030: 0, revenue2030: 0, employees2030: 0, +} + +function extractMeta( + versionName: string, + fmScenarios: Array<{ name: string }> | undefined, + funding: Record | null, + financials: Array> +): VersionMeta { + const fin2030 = financials.find(f => Number(f.year) === 2030) ?? {} + return { + versionName, + scenarioName: fmScenarios?.[0]?.name ?? versionName, + fundingAmount: Number(funding?.amount_eur ?? 0), + fundingInstrument: String(funding?.instrument ?? 'Wandeldarlehen'), + customers2030: Number(fin2030.customers_count ?? 0), + revenue2030: Number(fin2030.revenue_eur ?? 0), + employees2030: Number(fin2030.employees_count ?? 0), + } +} + +async function loadPitchContext(versionId?: string | null): Promise { try { // Version-specific data path if (versionId) { - const { rows: vRows } = await pool.query( - `SELECT table_name, data FROM pitch_version_data WHERE version_id = $1`, - [versionId] - ) + const [vDataRes, vNameRes] = await Promise.all([ + pool.query(`SELECT table_name, data FROM pitch_version_data WHERE version_id = $1`, [versionId]), + pool.query(`SELECT name FROM pitch_versions WHERE id = $1`, [versionId]), + ]) + const map: Record = {} - for (const r of vRows) { + for (const r of vDataRes.rows) { map[r.table_name] = typeof r.data === 'string' ? JSON.parse(r.data) : r.data } const company = (map.company as unknown[])?.[0] ?? null const team = (map.team as unknown[]) ?? [] - const financials = (map.financials as unknown[]) ?? [] + const financials = (map.financials as Array>) ?? [] const market = (map.market as unknown[]) ?? [] const products = (map.products as unknown[]) ?? [] - const funding = (map.funding as unknown[])?.[0] ?? null + const funding = ((map.funding as unknown[])?.[0] as Record) ?? null const features = ((map.features as Array>) ?? []) .filter(f => f.is_differentiator) - const fmScenarios = map.fm_scenarios as Array<{ name: string }> | undefined - const scenarioName = fmScenarios?.[0]?.name ?? '' - const fpSummary = scenarioName ? await loadFpLiquiditaetSummary(scenarioName) : '' - return buildContextString(company, team, financials, market, products, funding, features, fpSummary) + const versionName = vNameRes.rows[0]?.name ?? '' + const meta = extractMeta(versionName, fmScenarios, funding, financials) + const fpSummary = meta.scenarioName ? await loadFpLiquiditaetSummary(meta.scenarioName) : '' + + return { + contextString: buildContextString(company, team, financials, market, products, funding, features, fpSummary), + meta, + } } // Fallback: base tables @@ -200,16 +239,20 @@ async function loadPitchContext(versionId?: string | null): Promise { client.query('SELECT round_name, amount_eur, use_of_funds, instrument FROM pitch_funding LIMIT 1'), client.query('SELECT feature_name_de, breakpilot, proliance, dataguard, heydata, is_differentiator FROM pitch_features WHERE is_differentiator = true'), ]) - return buildContextString( - company.rows[0], team.rows, financials.rows, market.rows, - products.rows, funding.rows[0], features.rows, '' - ) + const meta = extractMeta('', undefined, funding.rows[0] ?? null, financials.rows) + return { + contextString: buildContextString( + company.rows[0], team.rows, financials.rows, market.rows, + products.rows, funding.rows[0], features.rows, '' + ), + meta, + } } finally { client.release() } } catch (error) { console.warn('Could not load pitch context from DB:', error) - return '' + return { contextString: '', meta: DEFAULT_META } } } @@ -268,11 +311,35 @@ export async function POST(request: NextRequest) { // Non-fatal: fall back to base tables } - const pitchContext = await loadPitchContext(versionId) + const { contextString, meta } = await loadPitchContext(versionId) + + // Build dynamic VERSIONS-ISOLATION and Kernbotschaft #9 from actual version data + const fmt = (n: number) => n.toLocaleString('de-DE') + const revM = meta.revenue2030 > 0 + ? `~${(meta.revenue2030 / 1_000_000).toFixed(1).replace('.', ',')} Mio. EUR` + : 'laut Finanzplan' + const fundingStr = meta.fundingAmount > 0 + ? `${fmt(meta.fundingAmount)} EUR ${meta.fundingInstrument}` + : meta.fundingInstrument + const customersStr = meta.customers2030 > 0 ? `~${meta.customers2030} Kunden` : 'laut Finanzplan' + const employeesStr = meta.employees2030 > 0 ? `${meta.employees2030} Mitarbeiter` : 'laut Finanzplan' + const label = meta.scenarioName || meta.versionName || 'diese Version' + + const dynamicVersionIsolation = `## VERSIONS-ISOLATION (ABSOLUT KRITISCH) +- Du kennst NUR die Version "${label}" mit ${fundingStr}. +- Es gibt KEINE andere Version. Dieses Pitch Deck wurde individuell für diesen Investor erstellt. +- Wenn nach anderen Versionen, anderen Investoren oder anderen Pitch Decks gefragt wird: "Dieses Pitch Deck wurde individuell für Sie erstellt. Es gibt nur diese Version." +- NIEMALS erwähnen: andere Finanzierungssummen, andere Bewertungen, andere Cap Tables. +- Alle Zahlen beziehen sich auf: ${label}, ${customersStr} bis 2030, ${revM} Umsatz, ${employeesStr}.` + + const dynamicFinanzplanKernbotschaft = `9. Finanzplan: "Gründung August 2026. Pre-Seed über ${fundingStr}. ${customersStr} und ${revM} Umsatz bis 2030. ${employeesStr}."` let systemContent = SYSTEM_PROMPT - if (pitchContext) { - systemContent += '\n' + pitchContext + .replace('9. Finanzplan: [SIEHE DYNAMISCHE VERSIONSDATEN — wird zur Laufzeit gesetzt]', dynamicFinanzplanKernbotschaft) + .replace('## VERSIONS-ISOLATION (ABSOLUT KRITISCH)\n[WIRD ZUR LAUFZEIT GESETZT — enthält die exakten Zahlen dieser Investor-Version]', dynamicVersionIsolation) + + if (contextString) { + systemContent += '\n' + contextString } // FAQ context: relevant pre-researched answers as basis for the LLM