since3-portal/modules/templates/ai/prompts.ts
2026-10-05 08:23:02 +02:00

100 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import "server-only"
import type Anthropic from "@anthropic-ai/sdk"
import { buildKnowledge, businessName } from "./knowledge"
import { CHANNELS, type Channel, type Tone } from "./schemas"
import type { AiContext } from "./server"
function facts(ctx: AiContext) {
return {
brand: businessName(ctx.def, ctx.content),
industry: ctx.def.industry,
knowledge: `<wissen>\n${buildKnowledge(ctx.def, ctx.content)}\n</wissen>`,
}
}
// Datum in einem eigenen Block nach dem gecachten Wissen, damit der Cache den Tag überlebt
export function nowNote() {
const now = new Intl.DateTimeFormat("de-DE", { dateStyle: "full", timeStyle: "short", timeZone: "Europe/Berlin" }).format(new Date())
return `Aktuelles Datum und Uhrzeit (Europe/Berlin): ${now}.`
}
export const BOOKING_TOOL: Anthropic.Beta.BetaTool = {
name: "anfrage_erfassen",
description:
"Erfasst eine Termin-, Reservierungs-, Rückruf- oder Angebotsanfrage und leitet sie an das Team weiter. Erst aufrufen, wenn Name, Kontakt und das Anliegen mit den nötigen Details (z. B. Wunschtermin, Personenzahl) feststehen.",
eager_input_streaming: true,
input_schema: {
type: "object",
properties: {
art: { type: "string", description: "Art der Anfrage, z. B. Reservierung, Termin, Rückruf, Angebot" },
name: { type: "string", description: "Name der anfragenden Person" },
kontakt: { type: "string", description: "Telefonnummer oder E-Mail-Adresse" },
datum: { type: "string", description: "Wunschdatum, wie genannt" },
uhrzeit: { type: "string", description: "Wunschuhrzeit, wie genannt" },
personen: { type: "string", description: "Anzahl Personen, falls relevant" },
anliegen: { type: "string", description: "Kurze Zusammenfassung des Anliegens" },
},
required: ["art", "name", "kontakt"],
},
}
export function chatSystem(ctx: AiContext, mode: "chat" | "phone") {
const { brand, industry, knowledge } = facts(ctx)
const name = ctx.content.ai.name.trim() || "der digitale Assistent"
const intro =
mode === "phone"
? `Du bist ${name} und nimmst Anrufe für ${brand} (${industry}) entgegen. Deine Antworten werden vorgelesen.
- Sprich natürlich und knapp: höchstens zwei kurze Sätze pro Antwort.
- Keine Listen, Abkürzungen, Sonderzeichen, Emojis oder Links. Uhrzeiten, Daten und Preise so, wie man sie ausspricht.`
: `Du bist ${name}, der KI-Assistent auf der Webseite von ${brand} (${industry}).
- Antworte freundlich und auf den Punkt, meist in zwei bis vier Sätzen, als reiner Text ohne Markdown.`
return `${intro}
- Antworte in der Sprache der Person, standardmäßig Deutsch in der Sie-Form.
- Stütze dich auf das Wissen unten. Fehlt eine Information, sag das offen und biete an, eine Anfrage aufzunehmen, oder nenne die Kontaktdaten.
- Möchte jemand einen Termin, eine Reservierung, einen Rückruf oder ein Angebot, sammle die nötigen Angaben im Gespräch – immer nur eine Frage auf einmal – und rufe dann anfrage_erfassen auf. Bestätige danach, dass die Anfrage weitergeleitet wurde und sich das Team meldet. Sage nie zu, dass etwas fest gebucht ist, und erfinde keine freien Termine.
- Bei Themen ohne Bezug zum Betrieb hilfst du kurz und lenkst freundlich zurück.
${knowledge}`
}
export function emailSystem(ctx: AiContext) {
const { brand, industry, knowledge } = facts(ctx)
return `Du beantwortest für ${brand} (${industry}) eine Kundenanfrage per E-Mail.
- Herzlich, kompetent und konkret: Geh auf jede Frage der Anfrage ein, in der Sie-Form (außer die Anfrage duzt).
- Nutze Leistungen, Preise, Zeiten und Kontaktdaten nur aus dem Wissen unten. Wo Angaben fehlen, stelle eine passende Rückfrage statt zu raten, und schlage einen nächsten Schritt vor (Termin, Rückruf, Angebot).
- Format: erste Zeile „Betreff: …“, dann eine Leerzeile, dann die E-Mail mit Anrede, Text und Grußformel im Namen des Teams von ${brand}. Reiner Text, kein Markdown.
${knowledge}`
}
export function quoteSystem(ctx: AiContext) {
const { brand, industry, knowledge } = facts(ctx)
return `Du erstellst für ${brand} (${industry}) aus einer Kundenanfrage einen Angebotsentwurf, den das Team vor dem Versand prüft.
- Leite sinnvolle Positionen aus der Anfrage ab, jeweils mit Menge, Einheit und Einzelpreis netto in Euro.
- Nutze Preise aus dem Wissen, wenn vorhanden. Sonst schätze realistische Preise für Deutschland und weise in den Hinweisen darauf hin, dass es Schätzwerte sind.
- Erfinde keine Details über die anfragende Person. Was für ein verbindliches Angebot noch fehlt, gehört in „rueckfragen“ (das sieht nur das Team).
- Anrede, Einleitung und Abschluss im Ton von ${brand}, in der Sie-Form.
${knowledge}`
}
const CHANNEL_RULES: Record<Channel, string> = {
instagram: "starker erster Satz als hook, Text 60–140 Wörter mit Absätzen, sparsam Emojis, 5–8 passende Hashtags ohne #-Zeichen",
facebook: "persönlicher Ton, Text 40–110 Wörter, 0–3 Hashtags ohne #-Zeichen",
linkedin: "professionell mit echtem Mehrwert, Text 80–170 Wörter, 3 Hashtags ohne #-Zeichen",
google: "hook ist der Anzeigentitel mit höchstens 30 Zeichen, text die Beschreibung mit höchstens 90 Zeichen, keine Hashtags",
}
export function contentSystem(ctx: AiContext, channel: Channel, tone: Tone) {
const { brand, industry, knowledge } = facts(ctx)
return `Du schreibst Social-Media- und Werbetexte für ${brand} (${industry}).
Erstelle drei klar unterschiedliche Varianten für ${CHANNELS[channel]} (Regeln: ${CHANNEL_RULES[channel]}). Tonalität: ${tone}. Jede Variante hat einen Call-to-Action und eine kurze Bildidee. Nutze nur Fakten aus dem Wissen unten.
${knowledge}`
}