Shorten cover email + strip non-Latin script from AI text

The generated Begleit-E-Mail was too long for an HR reader and sometimes
contained stray non-Latin characters (e.g. CJK "包括"). Make the email a
minimal accompanying note (no repetition of the cover letter, max ~3
sentences), add a German-only/Latin-script instruction to both the
document and reply prompts, and add a backstop sanitizer in str() that
strips any non-Latin script characters the model still emits.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-03 19:28:34 +02:00
co-authored by Claude
parent fd11facadd
commit 482d8d86fa
+28 -15
View File
@@ -171,7 +171,10 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
'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.';
'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.';
const skeleton =
`{\n` +
@@ -186,7 +189,7 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
` },\n` +
` "email": {\n` +
` "betreff": "Bewerbung als … (ggf. mit Referenz-/Kennnummer)",\n` +
` "text": "Kurzer, sofort versendbarer E-Mail-Text: Anrede, 2-3 knappe Absätze, Grußformel, Name - Absätze durch Leerzeile getrennt"\n` +
` "text": "Sehr kurzer Begleittext nur für die E-Mail: Anrede, ein Satz (Stelle + Unterlagen im Anhang), optional ein kurzer Schlusssatz, Grußformel, Name - jeweils durch Leerzeile getrennt"\n` +
` },\n` +
` "lebenslauf": {\n` +
` "profil": "2-3 Sätze Kurzprofil, zugeschnitten auf die Zielstelle",\n` +
@@ -251,17 +254,18 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
`Nur der ERSTE Absatz beginnt mit einem KLEINbuchstaben (die Anrede endet mit Komma, der Satz wird ` +
`fortgesetzt), z. B. "mit …" oder "als …" — außer das erste Wort ist ein Substantiv, ein Eigenname oder die ` +
`Höflichkeitsform "Ihre/Ihr/Ihnen". Alle WEITEREN Absätze sind neue Sätze und beginnen normal mit GROSSbuchstaben.\n` +
`- email: ein KURZES, normgerechtes Begleit-E-Mail-Anschreiben, mit dem die Bewerbung samt Anhängen ` +
`(Lebenslauf, Anschreiben, ggf. Zeugnisse) per E-Mail versendet wird. Es ist NICHT das vollständige ` +
`Anschreiben - wiederhole dessen Inhalt nicht, fasse dich bewusst knapp (Faustregel: höchstens 6-8 Sätze). ` +
`email.betreff: prägnante Betreffzeile, i. d. R. "Bewerbung als <Stelle>" (eine in Anzeige/Notizen genannte ` +
`Referenz-/Kennnummer anhängen). email.text: vollständiger, sofort versendbarer E-Mail-Text mit exakt dieser ` +
`Struktur - (1) dieselbe Anrede wie im Anschreiben; (2) ein Satz, auf welche Stelle man sich bewirbt (falls ` +
`bekannt: wo die Stelle gefunden wurde); (3) ein bis zwei Sätze zur Kernmotivation/Kernqualifikation; ` +
`(4) ein Satz mit dem Hinweis, dass die vollständigen Bewerbungsunterlagen im Anhang beiliegen; (5) ein ` +
`freundlicher Ausblick auf ein persönliches Gespräch; (6) Grußformel "Mit freundlichen Grüßen"; (7) in der ` +
`letzten Zeile der vollständige Name des Bewerbers. Trenne Anrede, Absätze, Grußformel und Name jeweils durch ` +
`eine Leerzeile (\\n\\n). Höflich, sachlich, in Aktiv-Sätzen, ohne Floskeln und Weichspüler, keine Platzhalter.\n` +
`- email: ein SEHR KURZER Begleittext für die E-Mail, mit der die Bewerbung samt Anhängen ` +
`(Lebenslauf, Anschreiben, ggf. Zeugnisse) versendet wird. Es ist NUR die E-Mail-Begleitnotiz, NICHT das ` +
`vollständige Anschreiben - dessen Inhalt NICHT wiederholen. Ein HR-Experte überfliegt diese E-Mail im ` +
`Vorbeigehen: max. 3 Sätze gesamt, auf das absolute Minimum reduziert. KEINE Motivationssätze, KEINE ` +
`Qualifikationsauflistungen, KEINE Wiederholung des Anschreibens. ` +
`email.betreff: "Bewerbung als <Stelle>" (ggf. Referenz-/Kennnummer anhängen). ` +
`email.text: sofort versendbar, exakt diese Struktur - (1) dieselbe Anrede wie im Anschreiben; ` +
`(2) ein Satz: auf welche Stelle man sich bewirbt UND dass die vollständigen Bewerbungsunterlagen im Anhang ` +
`beiliegen; (3) ein kurzer, freundlicher Schlusssatz mit Ausblick auf ein persönliches Gespräch; ` +
`(4) Grußformel "Mit freundlichen Grüßen"; (5) vollständiger Name in der letzten Zeile. ` +
`Trenne Anrede, Text, Grußformel und Name jeweils durch eine Leerzeile (\\n\\n). ` +
`Höflich, sachlich, in Aktiv-Sätzen, ohne Floskeln und Weichspüler, keine Platzhalter.\n` +
`- profil: 23 Sätze Kurzprofil, konkret auf die Zielstelle zugeschnitten (der individuelle Text zur Stelle).\n` +
`- Stil/Sprache (gilt für Anschreiben, Profil UND E-Mail): Schreibe so natürlich, wie ein deutscher ` +
`Muttersprachler tatsächlich schreibt - konkrete Verben, kurze klare Sätze. KEINE leeren Floskeln oder ` +
@@ -373,7 +377,15 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
// Normalize one content string: trim, and force a plain hyphen "-" instead of
// en/em dash (""/"—") everywhere — applies to every field of both documents
// so the requirement holds even if the model still emits dashes.
const str = (v) => (typeof v === 'string' ? v.trim() : (v == null ? '' : String(v).trim())).replace(/[–—]/g, '-');
// Backstop: strip non-Latin script characters (e.g. stray CJK like "包括")
// the model may emit despite the German-only instruction. Safe for German
// application text — no legitimate field contains these scripts. Preserves
// newlines (paragraph breaks) and common German umlauts/punctuation.
const NON_LATIN_SCRIPT = /[㐀-鿿豈-﫿぀-ヿ가-힯 -〿]/g;
const str = (v) => (typeof v === 'string' ? v.trim() : (v == null ? '' : String(v).trim()))
.replace(/[–—]/g, '-')
.replace(NON_LATIN_SCRIPT, '')
.replace(/[ \t]{2,}/g, ' ');
function pick(obj, keys) {
if (!obj) return '';
@@ -1127,7 +1139,8 @@ async function generateEmailReply({ incoming, job = {}, settings = {}, hinweise
'die du nicht kennst (z. B. ein genauer Terminvorschlag), 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 ...".';
'Buzzword-Paare und ohne Selbstetiketten wie "Als Muttersprachler ...". Verwende ausschließlich ' +
'deutsche Sprache und das lateinische Alphabet - keine fremdsprachigen Wörter oder Schriftzeichen.';
const user =
`# Kontext der Bewerbung\n` +
`Unternehmen: ${job.firma || '-'}\n` +