Anrede: last name only, strip first name (keep titles/particles)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+29
-2
@@ -220,7 +220,11 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
|
|||||||
`(nur falls aus Stellenanzeige oder Notizen bekannt, sonst leer); ort = "PLZ Ort"; ` +
|
`(nur falls aus Stellenanzeige oder Notizen bekannt, sonst leer); ort = "PLZ Ort"; ` +
|
||||||
`ansprechpartner = konkrete Kontaktperson (nur falls bekannt, sonst leer). Nutze eine in den ` +
|
`ansprechpartner = konkrete Kontaktperson (nur falls bekannt, sonst leer). Nutze eine in den ` +
|
||||||
`Notizen angegebene vollständige Firmenanschrift und einen genannten Ansprechpartner. ` +
|
`Notizen angegebene vollständige Firmenanschrift und einen genannten Ansprechpartner. ` +
|
||||||
`Ist ein Ansprechpartner bekannt, passe auch die Anrede an (z. B. "Sehr geehrte Frau …,").\n` +
|
`Ist ein Ansprechpartner bekannt, passe die Anrede an - aber NUR als "Sehr geehrte Frau <Nachname>," ` +
|
||||||
|
`bzw. "Sehr geehrter Herr <Nachname>," mit ausschließlichem NACHNAMEN, niemals mit Vorname oder ` +
|
||||||
|
`vollem Namen (also "Sehr geehrte Frau Müller," nicht "Sehr geehrte Frau Anna Müller,"). Steht in ` +
|
||||||
|
`den Unterlagen ein voller Name (z. B. "Anna Schmidt"), verwende in der Anrede nur den Nachnamen ` +
|
||||||
|
`("Frau Schmidt"). Der ansprechpartner-Wert im JSON darf der volle Name sein (für "z. Hd. …").\n` +
|
||||||
`- anschreiben.absaetze: 3–4 kurze, überzeugende Absätze (kein Adressblock, kein Datum, ` +
|
`- anschreiben.absaetze: 3–4 kurze, überzeugende Absätze (kein Adressblock, kein Datum, ` +
|
||||||
`keine Grußformel hier). Sachlich und positiv, in Aktiv-Sätzen, ohne Floskeln und ` +
|
`keine Grußformel hier). Sachlich und positiv, in Aktiv-Sätzen, ohne Floskeln und ` +
|
||||||
`Weichspüler ("Ich würde gerne …"). KEINE Gehaltsvorstellungen, KEINE Kündigungsgründe, ` +
|
`Weichspüler ("Ich würde gerne …"). KEINE Gehaltsvorstellungen, KEINE Kündigungsgründe, ` +
|
||||||
@@ -400,6 +404,29 @@ function fixParagraphCasing(anrede, absaetze) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defensive: the salutation must be "Sehr geehrte Frau <Nachname>" /
|
||||||
|
// "Sehr geehrter Herr <Nachname>" with ONLY the last name — never a first
|
||||||
|
// name or full name. Strip any leading given names the model may have added,
|
||||||
|
// but keep academic titles (Dr., Prof., …) and name particles (van, von, …).
|
||||||
|
const SAL_TITLES = new Set(['dr', 'prof', 'dipl', 'ing', 'rer', 'nat', 'mag', 'phd', 'habil', 'dr.habil']);
|
||||||
|
const SAL_PARTICLES = new Set(['van', 'de', 'von', 'zu', 'der', 'den', 'ten', 'da', 'di', 'del', 'della', 'la', 'le', 'vom', 'zur', 'mc', 'mac']);
|
||||||
|
function cleanAnrede(anrede) {
|
||||||
|
return String(anrede).replace(
|
||||||
|
/^(Sehr geehrte\s+Frau|Sehr geehrter\s+Herr)\s+(.+?)(,?\s*)$/i,
|
||||||
|
(m, sal, name, tail) => {
|
||||||
|
const words = String(name).trim().split(/\s+/);
|
||||||
|
if (words.length <= 1) return m; // already a single name
|
||||||
|
const kept = words.filter((w, i) => {
|
||||||
|
if (i === words.length - 1) return true; // last word = surname
|
||||||
|
const lw = w.toLowerCase().replace(/[.]/g, '');
|
||||||
|
return SAL_TITLES.has(lw) || SAL_PARTICLES.has(lw); // keep titles / particles
|
||||||
|
});
|
||||||
|
if (kept.length === words.length) return m; // nothing to strip
|
||||||
|
return `${sal} ${kept.join(' ')}${tail}`;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Defensive: never let an HR red-flag phrase reach the PDF, even if the model
|
// Defensive: never let an HR red-flag phrase reach the PDF, even if the model
|
||||||
// ignores the instruction.
|
// ignores the instruction.
|
||||||
const RED_FLAG = /(nicht\s*beendet|abgebrochen|abbruch|ohne\s*abschluss|arbeits(los|suchend)|gek(ü|ue)ndigt|entlassen)/i;
|
const RED_FLAG = /(nicht\s*beendet|abgebrochen|abbruch|ohne\s*abschluss|arbeits(los|suchend)|gek(ü|ue)ndigt|entlassen)/i;
|
||||||
@@ -441,7 +468,7 @@ function normalizeResult(parsed) {
|
|||||||
fuehrerschein: str(pick(k, ['fuehrerschein', 'führerschein', 'driving_license', 'license', 'licence', 'fuehrerscheinklasse'])),
|
fuehrerschein: str(pick(k, ['fuehrerschein', 'führerschein', 'driving_license', 'license', 'licence', 'fuehrerscheinklasse'])),
|
||||||
},
|
},
|
||||||
anschreiben: (() => {
|
anschreiben: (() => {
|
||||||
const anrede = str(pick(a, ['anrede', 'salutation']));
|
const anrede = cleanAnrede(str(pick(a, ['anrede', 'salutation'])));
|
||||||
const absaetze = pickArray(a, ['absaetze', 'absätze', 'paragraphs', 'text', 'inhalt']).map(str).filter(Boolean);
|
const absaetze = pickArray(a, ['absaetze', 'absätze', 'paragraphs', 'text', 'inhalt']).map(str).filter(Boolean);
|
||||||
const e = a.empfaenger || a.recipient || a.empfänger || {};
|
const e = a.empfaenger || a.recipient || a.empfänger || {};
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user