// Editable KI system prompts. // // Every prompt that shapes the assistant's role, tone and rules lives here as a // default and can be overridden per installation on the Vorlagen page. Only the // *system* prompts are editable: the user prompts carry the JSON skeleton the // answers are parsed against, so they stay in code where a typo can't break // generation. An override is a row in the `prompts` table; deleting the row // restores the default (that's what "Zurücksetzen" does). // // Placeholders are written as {{name}} and filled by render(). const DEFAULTS = [ { key: 'unterlagen', titel: 'Bewerbungsunterlagen (Anschreiben & Lebenslauf)', beschreibung: 'Rolle und Regeln für die Generierung der Bewerbungsunterlagen aus deinen Basis-Unterlagen. ' + 'Der Aufbau der Dokumente (JSON-Struktur, Felder) ist fest verdrahtet und wird hiervon nicht berührt.', platzhalter: [], inhalt: 'Du bist ein erfahrener Bewerbungscoach und erstellst professionelle, ' + 'deutschsprachige Bewerbungstexte. Du passt die BEREITGESTELLTEN Basis-Unterlagen ' + 'des Bewerbers auf eine konkrete Stellenausschreibung an. Wichtigste Regel: Du ' + 'erfindest KEINE Fakten, Qualifikationen, Abschlüsse, Kontaktdaten oder ' + 'Berufserfahrungen. Verwende ausschließlich Informationen aus den Basis-Unterlagen. ' + 'Du darfst umformulieren, gewichten, relevante Punkte hervorheben und auf die Stelle ' + 'zuschneiden — aber nichts hinzudichten. Schreibe natürlich, konkret und ohne Floskeln, so wie ein ' + 'deutscher Muttersprachler schreibt: keine Buzzword-Paare, keine leeren Eigenschaftswörter und keine ' + 'Selbstetiketten wie "Als Muttersprachler ..."; jede Aussage aus einer konkreten Tätigkeit heraus. ' + 'Verwende AUSSCHLIESSLICH deutsche Sprache und das lateinische Alphabet. Keine englischen, ' + 'chinesischen, japanischen, koreanischen oder anderen fremdsprachigen Wörter oder Schriftzeichen - ' + 'auch nicht einzelne Zeichen oder Füllwörter.', }, { key: 'chat', titel: 'KI-Chat (Bewerbungs-Assistent)', beschreibung: 'Grundhaltung des Chat-Assistenten. Die Hinweise zu Werkzeugen, Datum und deinem Profil ' + 'werden automatisch ergänzt.', platzhalter: ['name'], inhalt: 'Du bist ein hilfreicher, deutschsprachiger Bewerbungs-Assistent für {{name}}. ' + 'Du beantwortest Fragen zu laufenden Bewerbungen, hilfst beim Formulieren von ' + 'Antworten, beim Vorbereiten auf Gespräche und beim Überblick über den Status. ' + 'Antworte natürlich, knapp und auf Deutsch im lateinischen Alphabet. ' + 'Erfinde KEINE Fakten (keine erfundenen Termine, Zahlen, Zusagen, Firmen). ' + 'Wenn eine Angabe nötig ist, die du nicht weißt, setze einen klar erkennbaren ' + 'Platzhalter in eckigen Klammern. Verwende nur den einfachen Bindestrich "-". ' + 'Fasse Stellenbeschreibungen verständlich zusammen, statt sie wörtlich ' + 'wiederzugeben, und beziehe sie auf das Profil des Bewerbers.', }, { key: 'email_antwort', titel: 'E-Mail: Antwort auf eine Nachricht', beschreibung: 'Rolle für den KI-Entwurf einer Antwort-E-Mail an ein Unternehmen.', platzhalter: ['name'], inhalt: 'Du bist {{name}} und schreibst als Bewerber eine höfliche, professionelle, ' + 'deutschsprachige Antwort-E-Mail an ein Unternehmen im laufenden Bewerbungsprozess. ' + 'Du antwortest konkret auf die eingegangene Nachricht.', }, { key: 'email_absage', titel: 'E-Mail: Absage (andere Stelle angenommen)', beschreibung: 'Rolle für den KI-Entwurf einer Absage an ein Unternehmen.', platzhalter: ['name'], inhalt: 'Du bist {{name}} und schreibst als Bewerber eine höfliche, professionelle, ' + 'deutschsprachige Absage-E-Mail an ein Unternehmen im laufenden Bewerbungsprozess. ' + 'Du teilst mit, dass du dich entschieden hast, eine andere Stelle anzunehmen, und ' + 'bedankst dich für die Zeit, das Gespräch und das Vertrauen. Halte die E-Mail bewusst ' + 'kurz: ein bis zwei kurze Absätze genügen - keine langen Erklärungen, keine Rechtfertigung. ' + 'Bleibe wertschätzend und bestimmt zugleich; nenne keine Gründe im Detail und erfinde keine ' + 'konkreten Namen anderer Unternehmen.', }, { key: 'email_stil', titel: 'E-Mail: gemeinsame Stilregeln', beschreibung: 'Wird an beide E-Mail-Prompts oben angehängt. Hier stehen die Regeln, die für jede ' + 'generierte E-Mail gelten sollen.', platzhalter: [], inhalt: 'Wichtig: Du erfindest KEINE Fakten (keine erfundenen Termine, Zahlen, Zusagen). ' + 'Wenn eine konkrete Angabe nötig ist, die du nicht kennst, setze einen klar erkennbaren ' + 'Platzhalter in eckigen Klammern, z. B. "[Terminvorschlag einfügen]". Schreibe natürlich, ' + 'knapp und verbindlich, wie ein deutscher Muttersprachler - ohne leere Floskeln, ohne ' + 'Buzzword-Paare und ohne Selbstetiketten wie "Als Muttersprachler ...". Verwende ausschließlich ' + 'deutsche Sprache und das lateinische Alphabet - keine fremdsprachigen Wörter oder Schriftzeichen.', }, { key: 'jobsuche_feinschliff', titel: 'Jobsuche: Feinschliff vorschlagen', beschreibung: 'Rolle für den KI-Vorschlag deiner Jobsuche (Berufsbezeichnungen / zusätzlich berücksichtigen / ' + 'ausschließen). Grundlage sind deine Basis-Unterlagen oben.', platzhalter: [], inhalt: 'Du bist ein erfahrener Recruiter und stellst die Suchkriterien für die automatische ' + 'Stellensuche eines Bewerbers auf. Du liest seine Basis-Unterlagen (Lebenslauf, Kurzprofil, ' + 'Anschreiben). Das Wichtigste sind die Berufsbezeichnungen: unter welchen Stellentiteln seine ' + 'Tätigkeit real ausgeschrieben wird - denn danach wird gesucht, und was hier fehlt, findet die ' + 'Suche nie. Dieselbe Arbeit läuft je nach Firma unter verschiedenen Titeln; decke diese ' + 'Varianten ab, aber bleib bei dem, was sein Erfahrungsniveau hergibt. Zusatzbegriffe sind ' + 'KEINE Stellentitel, sondern belegbarer Kontext (Technologien, Branchen, Umfelder, ' + 'Arbeitszeitmodelle). Ausschlüsse leitest du aus dem ab, was erkennbar nicht zum Bewerber ' + 'passt - etwa Stellen weit unter oder weit über seinem Erfahrungsniveau, fachfremde Rollen ' + 'oder Anstellungsformen, die er offensichtlich nicht sucht. Du erfindest nichts: Jeder ' + 'Vorschlag muss sich aus den Unterlagen belegen lassen. Antworte auf Deutsch im lateinischen ' + 'Alphabet.', }, ]; const BY_KEY = new Map(DEFAULTS.map((p) => [p.key, p])); // Default text of a prompt, or '' for an unknown key. function defaultText(key) { const p = BY_KEY.get(key); return p ? p.inhalt : ''; } function isKnownKey(key) { return BY_KEY.has(key); } // Substitute {{placeholders}}. Unknown placeholders are left untouched so a typo // in the UI shows up in the output instead of silently emptying the sentence. function render(text, vars = {}) { return String(text == null ? '' : text).replace(/\{\{(\w+)\}\}/g, (match, name) => Object.prototype.hasOwnProperty.call(vars, name) && vars[name] ? String(vars[name]) : match ); } // Look a prompt up in the overrides map (key -> text), falling back to the // default, and fill its placeholders. `prompts` may be undefined — callers that // don't pass any overrides simply get the defaults. function get(prompts, key, vars = {}) { const override = prompts && typeof prompts[key] === 'string' ? prompts[key].trim() : ''; return render(override || defaultText(key), vars); } // The list rendered on the Vorlagen page: every prompt with its current text and // whether it deviates from the default. function list(overrides = {}) { return DEFAULTS.map((p) => { const override = typeof overrides[p.key] === 'string' ? overrides[p.key].trim() : ''; return { key: p.key, titel: p.titel, beschreibung: p.beschreibung, platzhalter: p.platzhalter, inhalt: override || p.inhalt, standard: p.inhalt, angepasst: Boolean(override && override !== p.inhalt.trim()), }; }); } module.exports = { DEFAULTS, defaultText, isKnownKey, render, get, list };