/** * LinkedIn Experience Generator * * Generates result-oriented LinkedIn experience descriptions from KB data. * Reads person experiences, skills, and projects, then produces optimized * descriptions with measurable achievements and relevant keywords. */ import type { Person, Experience, Organization, Skill, Project } from '../schemas/types'; import type { ExperienceOptions, ExperienceResult } from './types'; import { validateExperience, EXPERIENCE_CHAR_LIMIT } from './constraints'; import { readEntity } from '../io/entity-files'; // --- Internal Types --- interface ExperienceData { experience: Experience; orgName: string; skills: Skill[]; projects: Project[]; } // --- Experience Positioning Strategies --- /** * Maps experience IDs to positioning strategies that generate * result-oriented descriptions instead of task lists. */ const POSITIONING_STRATEGIES: Record PositionedContent> = { 'db-infrago-digitalisierung': positionDbInfraGo, 'data-hive-cassel-gruender': positionDataHive, 'db-netz-einfachbahn-jobsharing': positionJobSharing, 'db-netz-einfachbahn-leiter': positionEinfachbahnLeiter, 'uni-kassel-teilgruppenleiter': positionAcademicLeader, 'uni-kassel-doktorand': positionAcademicFoundation, 'uni-kassel-dozent-physik': positionAcademicTeaching, 'hochschule-fresenius-dozent': positionFreseniusDozent, }; interface PositionedContent { description: string; achievements: string[]; keywords: string[]; } // --- Public API --- /** * Generates LinkedIn experience descriptions from KB data. * * Reads the person's experiences, enriches them with skills and projects, * and produces result-oriented descriptions validated against LinkedIn constraints. */ export async function generateExperiences(options: ExperienceOptions): Promise { const { personId, experienceIds, keywords = [] } = options; const person = (await readEntity('person', personId)) as Person; const targetIds = experienceIds ?? person.experiences ?? []; const results: ExperienceResult[] = []; for (const expId of targetIds) { try { const data = await loadExperienceData(expId); const positioned = generatePositionedContent(data, keywords); const description = truncateToLimit(positioned.description); const validation = validateExperience(description); results.push({ experienceId: expId, title: data.experience.title, organization: data.orgName, description, keywords: positioned.keywords, achievements: positioned.achievements, validation, }); } catch { // Skip experiences that cannot be loaded } } return results; } // --- Data Loading --- async function loadExperienceData(expId: string): Promise { const experience = (await readEntity('experience', expId)) as Experience; let orgName = experience.organization; try { const org = (await readEntity('organization', experience.organization)) as Organization; orgName = org.name; } catch { // Use raw org ID as fallback } const skills: Skill[] = []; for (const skillId of experience.skillsUsed ?? []) { try { const skill = (await readEntity('skill', skillId)) as Skill; skills.push(skill); } catch { // Skip missing skills } } const projects: Project[] = []; for (const projId of experience.projects ?? []) { try { const project = (await readEntity('project', projId)) as Project; projects.push(project); } catch { // Skip missing projects } } return { experience, orgName, skills, projects }; } // --- Content Generation --- function generatePositionedContent(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const strategy = POSITIONING_STRATEGIES[data.experience.id]; if (strategy) { return strategy(data, requestedKeywords); } return generateDefaultContent(data, requestedKeywords); } // --- Positioning Strategies --- function positionDbInfraGo(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Digitalen Kundennutzen schaffen — mit einem Blick für Technologie und Menschen.', '', 'Als Experte für Digitalisierung bei DB InfraGO treibe ich die digitale Transformation der Schieneninfrastruktur voran. Mein Fokus: Projekte effizient zum Abschluss bringen und das System Schiene zukunftsfähig machen.', '', 'Schwerpunkte:', '→ Digitale Transformation großer Infrastrukturprojekte', '→ Change Management an der Schnittstelle von IT und Fachbereichen', '→ Teamführung mit Fokus auf Ergebnisse und Wirkung', ].join('\n'); const achievements = [ 'Digitale Transformation der Schieneninfrastruktur vorangetrieben', 'Effiziente Projektsteuerung in komplexem Konzernumfeld', ]; const keywords = mergeKeywords( ['Digitalisierung', 'Digitale Transformation', 'Change Management', 'Infrastruktur', 'DB', 'Schienenverkehr'], requestedKeywords, ); return { description, achievements, keywords }; } function positionDataHive(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Wir hassen Verschwendung und lieben Wandel.', '', 'Als Gründer von Data Hive Cassel verbinde ich KI-Expertise mit echtem Veränderungswillen. Mein Anspruch: nicht nur technische Lösungen liefern, sondern nachhaltigen Wandel ermöglichen.', '', 'Ergebnisse:', '→ 50+ Projekte erfolgreich umgesetzt', '→ 15+ Kunden aus Mittelstand und öffentlichem Sektor', '→ ROI > 2 für Kunden durch datengetriebene Optimierung', '→ KI praxistauglich gemacht — von der Strategie bis zur Implementierung', '', 'Schwerpunkte: KI-Beratung, Prozessautomatisierung, Energieeffizienz, Change Management', ].join('\n'); const achievements = [ '50+ Projekte erfolgreich umgesetzt', '15+ Kunden aus Mittelstand und öffentlichem Sektor', 'ROI > 2 für Kunden durch datengetriebene Optimierung', 'KI praxistauglich gemacht — von der Strategie bis zur Implementierung', ]; const keywords = mergeKeywords( ['Künstliche Intelligenz', 'KI-Beratung', 'Startup', 'Gründer', 'Prozessautomatisierung', 'Change Management', 'Energieeffizienz', 'ROI'], requestedKeywords, ); return { description, achievements, keywords }; } function positionJobSharing(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Führung ist keine One-Man-Show — und das haben wir bewiesen.', '', 'Im JobSharing-Tandem mit Claudia Froldi haben wir geteilte Führung auf Augenhöhe im Konzernumfeld etabliert. Ein bewusstes Experiment: Gehaltsverzicht für ein innovatives Führungsmodell.', '', 'Ergebnisse:', '→ Shared Leadership als Führungsinnovation im DB-Konzern etabliert', '→ Komplementäre Stärken: Fachliche Führung + disziplinarische Führung', '→ Team #Einfachbahn erfolgreich durch Transformation begleitet', '→ Vorbild für neue Arbeitsmodelle in der Bahnbranche', ].join('\n'); const achievements = [ 'Shared Leadership als Führungsinnovation im DB-Konzern etabliert', 'Bewusster Gehaltsverzicht für geteilte Führung auf Augenhöhe', 'Team erfolgreich durch Transformation begleitet', ]; const keywords = mergeKeywords( ['Shared Leadership', 'JobSharing', 'Führungsinnovation', 'New Work', 'Tandem-Führung', 'Transformation'], requestedKeywords, ); return { description, achievements, keywords }; } function positionEinfachbahnLeiter(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Die Digitalisierung auf die Schiene bringen — eines der ambitioniertesten Vorhaben im Konzernumfeld.', '', 'Als Leiter #Einfachbahn habe ich ein interdisziplinäres Team aufgebaut und zu Höchstleistungen geführt. Agile Methoden und innovative Arbeitsformen etabliert, Silos aufgebrochen.', '', 'Ergebnisse:', '→ Interdisziplinäres Team aufgebaut und agile Methoden etabliert', '→ Kultur des offenen Lernens geschaffen', '→ Technische und kulturelle Transformation nachhaltig vorangetrieben', ].join('\n'); const achievements = data.experience.achievements ?? [ 'Interdisziplinäres Team aufgebaut und agile Methoden etabliert', 'Silos aufgebrochen und Kultur des offenen Lernens geschaffen', ]; const keywords = mergeKeywords( ['Digitalisierung', 'Agile Führung', 'Transformation', 'Teamaufbau', 'Change Management'], requestedKeywords, ); return { description, achievements, keywords }; } function positionAcademicLeader(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Vom Labor in die Praxis — hier wurde das Fundament für meine KI-Expertise gelegt.', '', 'Als Teilgruppenleiter Spektroskopie habe ich Forschung, Teamführung und Technologieentwicklung vereint. Machine Learning seit 2012 in der Praxis angewandt — lange bevor es Mainstream wurde.', '', 'Ergebnisse:', '→ 50+ wissenschaftliche Veröffentlichungen, 2.000+ Zitationen', '→ 12 Mio. € Drittmittel eingeworben (DFG, BMBF, EU)', '→ Internationale Forschungsteams aufgebaut und geführt', '→ ML/PyTorch seit 2012 für Datenanalyse eingesetzt', ].join('\n'); const achievements = [ '50+ wissenschaftliche Veröffentlichungen, 2.000+ Zitationen', '12 Mio. € Drittmittel eingeworben (DFG, BMBF, EU)', 'Internationale Forschungsteams aufgebaut und geführt', 'ML/PyTorch seit 2012 für Datenanalyse eingesetzt', ]; const keywords = mergeKeywords( ['Machine Learning', 'Forschung', 'Künstliche Intelligenz', 'Teamführung', 'Drittmittel', 'Publikationen'], requestedKeywords, ); return { description, achievements, keywords }; } function positionAcademicFoundation(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Wissenschaftliche Exzellenz als Grundlage für praxisnahe KI-Anwendungen.', '', 'Promotion in Atom- und Molekülphysik mit Fokus auf experimentelle Methoden und Datenanalyse. Die analytische Denkweise und der systematische Ansatz prägen bis heute meine Arbeit mit KI und Digitalisierung.', '', 'Ergebnisse:', '→ 10 wissenschaftliche Paper und 10 internationale Konferenzbeiträge', '→ Elektronenstrahlquelle von Simulation bis Konstruktion aufgebaut', '→ Grundstein für 13+ Jahre KI-Erfahrung gelegt', ].join('\n'); const achievements = [ '10 wissenschaftliche Paper und 10 internationale Konferenzbeiträge', 'Elektronenstrahlquelle von Simulation bis Konstruktion aufgebaut', 'Grundstein für 13+ Jahre KI-Erfahrung gelegt', ]; const keywords = mergeKeywords( ['Promotion', 'Physik', 'Forschung', 'Datenanalyse', 'Wissenschaft'], requestedKeywords, ); return { description, achievements, keywords }; } function positionAcademicTeaching(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Komplexe Themen verständlich machen — eine Kompetenz, die von der Physik bis zur KI-Beratung trägt.', '', 'Als Dozent für experimentelle Atom- und Molekülphysik habe ich gelernt, anspruchsvolle Inhalte so aufzubereiten, dass sie begeistern und verstanden werden.', ].join('\n'); const achievements = [ 'Komplexe wissenschaftliche Inhalte verständlich vermittelt', ]; const keywords = mergeKeywords( ['Lehre', 'Wissensvermittlung', 'Physik', 'Universität'], requestedKeywords, ); return { description, achievements, keywords }; } function positionFreseniusDozent(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = [ 'Agilität, Change und Innovation erlebbar machen — nicht nur theoretisch, sondern am eigenen Leib.', '', 'Als Dozent an der Hochschule Fresenius habe ich MBA-Studierenden gezeigt, warum Agilität, Change, Innovation und Führung zusammengehören. Praxisnah, interaktiv und mit echten Erfahrungen aus Konzern und Startup.', ].join('\n'); const achievements = [ 'MBA-Studierende für Agilität und Innovation begeistert', 'Praxiswissen aus Konzern und Startup in die Lehre eingebracht', ]; const keywords = mergeKeywords( ['Agilität', 'Change Management', 'Innovation', 'Führung', 'Lehre', 'MBA'], requestedKeywords, ); return { description, achievements, keywords }; } // --- Default Content Generation --- function generateDefaultContent(data: ExperienceData, requestedKeywords: string[]): PositionedContent { const description = data.experience.description?.trim() ?? data.experience.title; const achievements = data.experience.achievements ?? []; const keywords = mergeKeywords( extractSkillKeywords(data.skills), requestedKeywords, ); return { description, achievements, keywords }; } // --- Keyword Helpers --- const SKILL_KEYWORD_MAP: Record = { 'kuenstliche-intelligenz': 'Künstliche Intelligenz', 'change-management': 'Change Management', 'digitale-transformation': 'Digitale Transformation', 'shared-leadership': 'Shared Leadership', 'agilitaet': 'Agilität', 'coaching': 'Coaching', 'team-management': 'Teamführung', 'it-strategie': 'IT-Strategie', }; function extractSkillKeywords(skills: Skill[]): string[] { const keywords: string[] = []; for (const skill of skills) { const mapped = SKILL_KEYWORD_MAP[skill.id]; if (mapped) { keywords.push(mapped); } else { keywords.push(skill.name); } } return keywords; } function mergeKeywords(base: string[], requested: string[]): string[] { const merged = new Set(base); for (const kw of requested) { merged.add(kw); } return Array.from(merged); } // --- Constraint Helpers --- function truncateToLimit(text: string): string { if (text.length <= EXPERIENCE_CHAR_LIMIT) { return text; } const truncated = text.slice(0, EXPERIENCE_CHAR_LIMIT - 1); const lastNewline = truncated.lastIndexOf('\n'); if (lastNewline > EXPERIENCE_CHAR_LIMIT * 0.7) { return truncated.slice(0, lastNewline); } const lastSpace = truncated.lastIndexOf(' '); if (lastSpace > EXPERIENCE_CHAR_LIMIT * 0.7) { return truncated.slice(0, lastSpace) + '…'; } return truncated + '…'; }