@odda-ai/matching-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,621 @@
1
+ export const CV_ANALYSIS_PROMPT_V1 = (
2
+ cvText: string,
3
+ ) => `
4
+ Sei un esperto analista HR specializzato nell’analisi di curriculum vitae.
5
+ Il tuo compito è analizzare il seguente CV ed estrarre informazioni strutturate in formato JSON.
6
+
7
+ **CV da Analizzare:**
8
+ ${cvText}
9
+
10
+ ---
11
+
12
+ ## Istruzioni Ultra-Stabili:
13
+
14
+ ### 0. Dati Anagrafici (Massima Priorità)
15
+ - Estrai tutti i dati personali disponibili:
16
+ - \`firstName\` (OBBLIGATORIO, se non trovato usare \`"N/A"\`)
17
+ - \`lastName\` (OBBLIGATORIO, se non trovato usare \`"N/A"\`)
18
+ - \`email\`, \`phone\`, \`address\`, \`dateOfBirth\` (YYYY-MM-DD), \`nationality\`, \`linkedIn\`, \`github\`, \`website\` (opzionali, usare \`null\` se mancanti)
19
+ - Cerca nell’intestazione e nel corpo del CV.
20
+ - Mantieni sempre lo stesso ordine dei campi JSON.
21
+
22
+ ### 1. Profilo Professionale
23
+ - Breve descrizione (max 2-3 frasi) con:
24
+ - Ruolo principale
25
+ - Anni totali di esperienza
26
+ - Aree di specializzazione chiave
27
+ - Tono professionale e oggettivo.
28
+
29
+ ### 2. Skills Tecniche (Massima Stabilità)
30
+ - Estrai **solo skills esplicitamente menzionate**.
31
+ - Per ciascuna skill:
32
+ - \`proficiency\`: 0-100, calcolato rigidamente:
33
+ - Hobby/progetti personali: 20
34
+ - Progetti professionali base: 50
35
+ - Progetti professionali avanzati / responsabilità senior: 80
36
+ - Expertise avanzata / certificazioni: 90
37
+ - Bonus certificazioni avanzate: +10 (non oltre 100)
38
+ - \`seniority\`: basato sugli anni di esperienza specifica:
39
+ - JUNIOR: 0-2 anni
40
+ - MID: 2-5 anni
41
+ - SENIOR: 5-10 anni
42
+ - LEAD: 10-15 anni
43
+ - PRINCIPAL: 15+ anni
44
+ - \`experience\`: mesi di esperienza se menzionati, altrimenti \`null\`
45
+ - \`isInferred\`: false
46
+ - Inferisci **solo skills strettamente correlate** (max 5):
47
+ - React → JavaScript, HTML, CSS
48
+ - NestJS → Node.js, TypeScript
49
+ - PostgreSQL → SQL
50
+ - DevOps → Linux, Docker, Git
51
+ - Attingi anche da descrizioni di progetti e responsabilità o da una statistica generale, non solo dalla sezione skills.
52
+ - Per skill inferite:
53
+ - \`proficiency\`: 30-50
54
+ - \`seniority\`: uguale o inferiore alla skill correlata
55
+ - \`isInferred\`: true
56
+ - Usa \`referenceSkills\` per matching e inferenza coerente.
57
+ - Ordina sempre le skills in ordine alfabetico per consistenza.
58
+
59
+ ### 3. Esperienze Lavorative
60
+ - Seleziona 2-3 posizioni più rilevanti (cronologico inverso).
61
+ - Per ciascuna posizione:
62
+ - \`companyName\`
63
+ - \`role\`
64
+ - \`durationInYears\` (arrotondato a 0.5 anni)
65
+ - \`mainResponsibility\` (max 1 frase)
66
+ - Massimo 4-5 frasi totali.
67
+
68
+ ### 4. Certificazioni
69
+ - Estrai tutte le certificazioni con:
70
+ - \`name\`
71
+ - \`issuer\` (se disponibile)
72
+ - \`year\` (se disponibile)
73
+ - Se assenti, restituire array vuoto.
74
+
75
+ ### 5. Seniority Complessiva
76
+ - Basata su:
77
+ - Anni totali esperienza
78
+ - Livello dei ruoli ricoperti
79
+ - Numero di skills con \`proficiency ≥ 80\`
80
+ - Presenza di certificazioni avanzate
81
+ - Scala rigida:
82
+ - JUNIOR: 0-2 anni
83
+ - MID: 2-5 anni
84
+ - SENIOR: 5-10 anni
85
+ - LEAD: 10-15 anni
86
+ - PRINCIPAL: 15+ anni
87
+
88
+ ### 6. Anni di Esperienza Totale
89
+ - Somma esperienze lavorative professionali, **escludendo stage e hobby projects**.
90
+ - Se non esplicitati, inferire dai ruoli principali.
91
+
92
+ ---
93
+
94
+ ## Formato JSON Richiesto
95
+ Restituisci **solo un JSON valido**, senza markdown:
96
+
97
+ \`\`\`json
98
+ {
99
+ "personalInfo": {
100
+ "firstName": "Mario",
101
+ "lastName": "Rossi",
102
+ "email": "mario.rossi@example.com",
103
+ "phone": "+39 348 1234567",
104
+ "address": "Via Roma 123, Milano, Italia",
105
+ "dateOfBirth": "1990-05-15",
106
+ "nationality": "Italiana",
107
+ "linkedIn": "https://linkedin.com/in/mariorossi",
108
+ "github": "https://github.com/mariorossi",
109
+ "website": "https://mariorossi.dev"
110
+ },
111
+ "description": "Breve descrizione professionale",
112
+ "technicalSkills": [
113
+ {
114
+ "name": "NomeSkill",
115
+ "proficiency": 85,
116
+ "isInferred": false,
117
+ "seniority": "SENIOR",
118
+ "experience": 36
119
+ }
120
+ ],
121
+ "workExperienceSummary": "Riassunto esperienze rilevanti",
122
+ "certifications": [
123
+ {
124
+ "name": "Nome Certificazione",
125
+ "issuer": "Ente Certificatore",
126
+ "year": 2023
127
+ }
128
+ ],
129
+ "overallSeniority": "SENIOR",
130
+ "yearsOfExperience": 8
131
+ }
132
+
133
+ \`\`\`
134
+
135
+ Note Finali
136
+ - Rispondi SOLO con JSON valido.
137
+ - firstName e lastName obbligatori ("N/A" se mancanti).
138
+ - Tutti gli altri campi obbligatori (array vuoti se assenti).
139
+ - Mantieni coerenza e ripetibilità dei punteggi e inferenze tra CV simili.
140
+ `;
141
+
142
+
143
+ export const CV_ANALYSIS_PROMPT = (
144
+ cvText: string,
145
+ ) => `# Prompt Analisi CV in JSON – Versione Ultra-Stabile
146
+
147
+ Sei un esperto analista HR specializzato nell’analisi di curriculum vitae.
148
+ Il tuo compito è analizzare il CV fornito e restituire un JSON strutturato con tutte le informazioni principali.
149
+
150
+ **CV da Analizzare:**
151
+ ${cvText}
152
+
153
+ ---
154
+
155
+ ## Istruzioni Ultra-Stabili:
156
+
157
+ ### 0. Dati Anagrafici (Massima Priorità)
158
+ - Estrai tutti i dati personali disponibili:
159
+ - \`firstName\` (OBBLIGATORIO, se non trovato usare \`"N/A"\`)
160
+ - \`lastName\` (OBBLIGATORIO, se non trovato usare \`"N/A"\`)
161
+ - \`email\`, \`phone\`, \`address\`, \`dateOfBirth\` (YYYY-MM-DD), \`nationality\`, \`linkedIn\`, \`github\`, \`website\` (opzionali, usare \`null\` se mancanti)
162
+ - Cerca informazioni nell’intestazione e nel corpo del CV.
163
+ - Se la data di nascita contiene solo anno/mese, completare con giorno \`01\`.
164
+
165
+ ---
166
+
167
+ ### 1. Profilo Professionale
168
+ - Breve descrizione (max 2-3 frasi) con:
169
+ - Ruolo principale
170
+ - Anni totali di esperienza
171
+ - Settore/industria
172
+ - Aree di specializzazione chiave
173
+ - Tono professionale e oggettivo.
174
+
175
+ ---
176
+
177
+ ### 2. Skills Tecniche (Massima Stabilità)
178
+
179
+ #### 2.1 Estrazione esplicita
180
+ - Considera **solo le skills menzionate testualmente** nelle sezioni “Competenze”, “Skills”, “Progetti”, “Esperienze” o “Certificazioni tecniche”.
181
+ - Per ciascuna skill:
182
+ - \`name\` → come scritto nel CV (normalizzato per coerenza)
183
+ - \`proficiency\` → calcolato rigidamente:
184
+ - Hobby/progetti personali: 20
185
+ - Progetti professionali base: 50
186
+ - Progetti professionali avanzati / responsabilità senior: 80
187
+ - Expertise avanzata / certificazioni: 90
188
+ - Bonus certificazioni avanzate: +10 (max 100)
189
+ - \`seniority\` → basato sugli anni di esperienza specifica:
190
+ - JUNIOR: 0-2 anni
191
+ - MID: 2-5 anni
192
+ - SENIOR: 5-10 anni
193
+ - LEAD: 10-15 anni
194
+ - PRINCIPAL: 15+ anni
195
+ - \`experience\` → mesi esplicitamente menzionati o \`null\`
196
+ - \`isInferred\` → false
197
+
198
+ #### 2.2 Inferenza di skill correlate
199
+ - Solo per skill tecniche note, massimo 5 inferenze per CV.
200
+ - Mappe di inferenza (referenceSkills):
201
+ - React → JavaScript, TypeScript, HTML, CSS, JSX, Redux, Hooks, Context API, Router, SSR, Testing, Recoil, Zustand, Framer Motion, GSAP, Storybook, Playwright, Web Vitals
202
+ - Angular → TypeScript, HTML, CSS, RxJS, NgRx, CLI, DI, Testing, PWA, Lazy Loading
203
+ - Vue → JavaScript, TypeScript, HTML, CSS, Vuex, Router, Composition API, SSR, Testing, Pinia
204
+ - Node.js → JavaScript, TypeScript, Async/Await, Express.js, NestJS, REST, GraphQL, WebSockets, Microservices, Testing, gRPC, Thrift, Kafka, Celery, BullMQ
205
+ - NestJS → Node.js, TypeScript, REST, GraphQL, DI, Modules, Middleware, Pipes, Guards, Interceptors, TypeORM/Prisma
206
+ - Python → OOP, Async, Flask, Django, FastAPI, Testing, Pandas, NumPy, ML, gRPC, Kafka, Airflow, Prefect, MLflow, Kubeflow, Triton
207
+ - Java → OOP, Spring Boot, REST, Microservices, Concurrency, Testing, gRPC, Kafka
208
+ - C# → OOP, .NET Core, ASP.NET, REST, Microservices, Testing, gRPC
209
+ - SQL → PostgreSQL, MySQL, SQL Server, Oracle, Indexing, Joins, Transactions, Views, Procedures, TimescaleDB
210
+ - NoSQL → MongoDB, Redis, Cassandra, Aggregation, Replication, Sharding, Caching, Neo4j, ArangoDB, Elasticsearch, Meilisearch
211
+ - Redis → Caching, Pub/Sub, Session Management, Performance
212
+ - Messaging → RabbitMQ, Kafka, Event-driven, Queues, Retry, Streaming
213
+ - Docker → Containers, Images, Volumes, Networking, CI/CD, Security
214
+ - Kubernetes → Pods, Deployments, Services, Ingress, ConfigMaps, Secrets, Helm, Scaling, Observability
215
+ - DevOps → Linux, Git, CI/CD, Docker, Kubernetes, Monitoring, Logging, Cloud, Security, Terraform, Pulumi, CloudFormation, Vault, SOPS, Serverless Framework, SST
216
+ - Git → Version Control, Branching, Merging, PR
217
+ - Linux → Shell, Networking, Permissions, Cron, Systemd
218
+ - Cloud → AWS (EC2,S3,Lambda,RDS,IAM), GCP (Compute,Storage,Functions,BigQuery), Azure (VM,App,Functions,SQL), Edge/CDN (Cloudflare Workers, Fastly)
219
+ - REST API → HTTP, JSON, CRUD, Auth, Rate Limiting, Testing
220
+ - GraphQL → Schema, Resolvers, Subscriptions, Apollo, Caching
221
+ - Testing → Unit, Integration, E2E, TDD, BDD, Jest, Mocha, Cypress, Selenium, Postman/Newman
222
+ - CI/CD → Jenkins, GitHub Actions, GitLab CI, Pipelines, Rollbacks, Blue/Green
223
+ - Security → Auth, JWT, OAuth2, HTTPS, CORS, Encryption, Input Validation, Secrets, SAST, DAST, PenTesting, Keycloak, Auth0, Container Security
224
+ - Monitoring → Prometheus, Grafana, ELK, CloudWatch, Metrics, Tracing, OpenTelemetry, Datadog
225
+ - Microservices → Service Discovery, API Gateway, Event-driven, Circuit Breaker, CQRS, Saga, Serverless
226
+ - Architecture → MVC, MVVM, Layered, Event-driven, Serverless, Monolith vs Microservices, DDD, Hexagonal, Clean Architecture, Multi-tenant, SaaS Patterns
227
+ - Performance → Caching, Load Balancing, Scaling, DB Optimization, Code Splitting, Lazy Loading, CDN
228
+
229
+ - Per skill inferite:
230
+ - \`proficiency\` : 30-70 (inferiore o uguale alla skill madre, assegna un valore realistico in base alla relazione)
231
+ - \`seniority\`: uguale o inferiore alla skill madre
232
+ - \`experience\`: null
233
+ - \`isInferred\`: true
234
+ - \`source\`: opzionale, “inferred from {skill madre}”
235
+
236
+ #### 2.3 Ordinamento
237
+ - Ordinare alfabeticamente tutte le skill, prima esplicite, poi inferite.
238
+
239
+ ---
240
+
241
+ ### 3. Esperienze Lavorative
242
+ - Seleziona 2-3 posizioni più rilevanti, cronologico inverso.
243
+ - Per ciascuna posizione:
244
+ - \`companyName\`
245
+ - \`role\`
246
+ - \`durationInYears\` (arrotondato a 0.5)
247
+ - \`mainResponsibility\` (max 1 frase)
248
+ - Escludere stage o hobby projects dai calcoli di esperienza.
249
+
250
+ ---
251
+
252
+ ### 4. Certificazioni
253
+ - Estrarre tutte le certificazioni con:
254
+ - \`name\` (OBBLIGATORIO)
255
+ - \`issuer\` (null se non disponibile)
256
+ - \`year\` (null se non disponibile)
257
+ - Se assenti, restituire array vuoto.
258
+
259
+ ---
260
+
261
+ ### 5. Seniority Complessiva
262
+ - Basata su:
263
+ - Anni totali esperienza
264
+ - Livello dei ruoli ricoperti
265
+ - Numero di skill con \`proficiency ≥ 80\`
266
+ - Presenza di certificazioni avanzate
267
+ - Scala:
268
+ - JUNIOR: 0-2 anni
269
+ - MID: 2-5 anni
270
+ - SENIOR: 5-10 anni
271
+ - LEAD: 10-15 anni
272
+ - PRINCIPAL: 15+ anni
273
+
274
+ ---
275
+
276
+ ### 6. Anni di Esperienza Totale
277
+ - Somma esperienze lavorative professionali, **escludendo stage e hobby projects**.
278
+ - Se non esplicitati, inferire dai ruoli principali.
279
+
280
+ ---
281
+
282
+ ## Formato JSON Richiesto
283
+ Restituire **solo un JSON valido**, senza markdown.
284
+
285
+ \`\`\`json
286
+ {
287
+ "personalInfo": {
288
+ "firstName": "N/A",
289
+ "lastName": "N/A",
290
+ "email": null,
291
+ "phone": null,
292
+ "address": null,
293
+ "dateOfBirth": null,
294
+ "nationality": null,
295
+ "linkedIn": null,
296
+ "github": null,
297
+ "website": null
298
+ },
299
+ "description": "Breve descrizione professionale",
300
+ "technicalSkills": [
301
+ {
302
+ "name": "NomeSkill",
303
+ "proficiency": 85,
304
+ "isInferred": false,
305
+ "seniority": "SENIOR",
306
+ "experience": 36
307
+ }
308
+ ],
309
+ "workExperienceSummary": "Riassunto esperienze rilevanti",
310
+ "certifications": [
311
+ {
312
+ "name": "Nome Certificazione",
313
+ "issuer": "Ente Certificatore",
314
+ "year": 2023
315
+ }
316
+ ],
317
+ "overallSeniority": "SENIOR",
318
+ "yearsOfExperience": 8
319
+ }
320
+ `;
321
+
322
+ export const JOB_MATCHING_PROMPT_V1 = (
323
+ jobDescription: string,
324
+ skillsList: string[],
325
+ ) => `Task: Perform end-to-end skill extraction, semantic matching, and scoring.
326
+
327
+ Input:
328
+ - Job Description: ${jobDescription}
329
+ - List of Reference Skills:
330
+ ${skillsList.map((skill) => ` - ${skill}`).join("\n")}
331
+
332
+ Instructions:
333
+
334
+ 1. Skill Extraction & Normalization
335
+ - Extract all relevant technical and soft skills from the Job Description
336
+ - Include explicit and clearly inferable implicit skills
337
+ - Normalize all skills to canonical names
338
+ - Map overly specific skills to their broader parent skills when appropriate
339
+
340
+ 2. Skill Importance Classification
341
+ For each extracted skill, determine its importance for the role:
342
+ - core: essential to perform the job
343
+ - enabling: improves effectiveness
344
+ - nice_to_have: optional or contextual
345
+
346
+ 3. Semantic Matching
347
+ For each Reference Skill:
348
+ - Evaluate semantic similarity with extracted skills
349
+ - Consider hierarchical and functional relationships (general ↔ specific)
350
+ - Recognize equivalent or alternative skills covering the same domain
351
+ - Do not penalize missing overly specific skills when a broader equivalent exists
352
+
353
+ 4. Scoring
354
+ Assign a multiplier between 0.00 and 1.00 based on:
355
+ - Semantic similarity
356
+ - Importance of the matched extracted skill
357
+ - Functional coverage by alternative skills
358
+ - Realistic expectations in a recruiting context
359
+
360
+ Scoring guidelines:
361
+ - 0.80–1.00 → crucial and strongly matched
362
+ - 0.50–0.79 → relevant or partially matched
363
+ - 0.20–0.49 → weak or indirect relevance
364
+ - < 0.20 → exclude from output
365
+
366
+ 5. Output
367
+ Return ONLY a valid JSON array.
368
+ Include only Reference Skills with multiplier > 0.
369
+
370
+ Output format:
371
+ [
372
+ { "skill": "Exact Reference Skill Name", "multiplier": 0.00 }
373
+ ]
374
+
375
+ Constraints:
376
+ - Do NOT include markdown
377
+ - Do NOT include explanations
378
+ - Do NOT include intermediate steps
379
+ - Return ONLY the final JSON array
380
+
381
+ Example:
382
+
383
+ Job Description:
384
+ "Looking for a Frontend Engineer with strong React experience.
385
+ You will collaborate with designers and backend engineers.
386
+ Experience with modern state management libraries is a plus."
387
+
388
+ Reference Skills:
389
+ - React
390
+ - Redux
391
+ - Zustand
392
+ - Communication
393
+ - AWS
394
+
395
+ Expected Output:
396
+ [
397
+ { "skill": "React", "multiplier": 0.95 },
398
+ { "skill": "Redux", "multiplier": 0.60 },
399
+ { "skill": "Zustand", "multiplier": 0.55 },
400
+ { "skill": "Communication", "multiplier": 0.40 }
401
+ ]
402
+ `;
403
+
404
+ export const JOB_MATCHING_PROMPT = (
405
+ jobDescription: string,
406
+ skillsList: string[],
407
+ ) => `# TASK
408
+ Perform two-pass deterministic skill extraction with validation, normalization, semantic alignment, classification, and scoring.
409
+
410
+ Precision target: ~90%.
411
+ Priority: avoid hallucinations and unjustified inferences.
412
+
413
+ Extraction must be text-grounded and validated before final output.
414
+
415
+ ---
416
+
417
+ # INPUT
418
+
419
+ ## Job Description
420
+ ${jobDescription}
421
+
422
+ ## Reference Skills
423
+ ${skillsList.map((skill) => `- ${skill}`).join("\n")}
424
+
425
+ ---
426
+
427
+ # GLOBAL RULES
428
+
429
+ 1. Do NOT invent skills.
430
+ 2. Do NOT use external knowledge.
431
+ 3. Do NOT assume standard tech stacks.
432
+ 4. Do NOT infer from job title alone.
433
+ 5. Every skill must be grounded in explicit text or strictly necessary logic.
434
+ 6. If uncertain → remove the skill.
435
+ 7. Precision > Recall.
436
+
437
+ ---
438
+
439
+ # PASS 1 — CONTROLLED EXTRACTION
440
+
441
+ Extract ALL candidate skills that satisfy one of the following:
442
+
443
+ ### A) Explicit Mention
444
+ Skill is directly written in the Job Description.
445
+
446
+ ### B) Deterministic Logical Necessity (STRICT)
447
+ Infer ONLY if:
448
+ - The responsibility cannot be performed without that skill.
449
+ - The inference is structurally required.
450
+ - The inference does not depend on market assumptions.
451
+
452
+ Allowed examples:
453
+ - “Build REST APIs” → REST
454
+ - “Write unit tests” → Unit Testing
455
+ - “Design relational database schema” → SQL
456
+
457
+ Forbidden examples:
458
+ - “Frontend Developer” → React
459
+ - “Cloud environment” → AWS
460
+ - “Agile team” → Scrum
461
+ - “Microservices” → Docker
462
+
463
+ For each candidate skill:
464
+ - Normalize to canonical name.
465
+ - Remove duplicates.
466
+ - Classify category:
467
+ - technical
468
+ - soft
469
+
470
+ ---
471
+
472
+ # PASS 2 — VALIDATION & PRUNING (CRITICAL FOR 90% PRECISION)
473
+
474
+ For EACH extracted skill, validate:
475
+
476
+ 1. Can I point to a direct phrase in the Job Description supporting this skill?
477
+ 2. Is the inference strictly necessary (not assumed)?
478
+ 3. Would a human reviewer agree this skill is clearly implied?
479
+
480
+ If ANY answer is uncertain → REMOVE the skill.
481
+
482
+ Do NOT keep borderline skills.
483
+ Do NOT keep ecosystem expansions.
484
+ Do NOT broaden vendor-specific tools unless explicitly stated.
485
+
486
+ After pruning:
487
+ The remaining list becomes the FINAL extracted skill set.
488
+
489
+ ---
490
+
491
+ # IMPORTANCE CLASSIFICATION (TEXT-DRIVEN ONLY)
492
+
493
+ Use only explicit textual signals.
494
+
495
+ CORE:
496
+ - must
497
+ - required
498
+ - essential
499
+ - primary responsibility
500
+ - strongly emphasized
501
+
502
+ ENABLING:
503
+ - preferred
504
+ - experience with
505
+ - plus
506
+ - good to have
507
+ - improves execution
508
+
509
+ NICE_TO_HAVE:
510
+ - optional
511
+ - secondary section
512
+ - clearly non-blocking
513
+
514
+ If ambiguous → classify as ENABLING.
515
+ Never upgrade importance without textual evidence.
516
+
517
+ ---
518
+
519
+ # REFERENCE SKILL MAPPING (AFTER VALIDATION ONLY)
520
+
521
+ For each validated extracted skill:
522
+
523
+ Step 1: Attempt exact match (case-insensitive).
524
+ Step 2: If no exact match → attempt strict semantic equivalence.
525
+ Step 3: If semantic confidence is not high → DO NOT MAP.
526
+
527
+ Mapping constraints:
528
+ - Only map when meaning is substantially identical.
529
+ - Do NOT map broader ↔ narrower unless coverage is complete.
530
+ - Do NOT force mapping.
531
+
532
+ If mapped:
533
+ - Use exact Reference Skill name.
534
+ - "isReferenceSkill": true
535
+
536
+ If not mapped:
537
+ - Keep normalized extracted name.
538
+ - "isReferenceSkill": false
539
+
540
+ Never remove unmatched skills.
541
+
542
+ ---
543
+
544
+ # MULTIPLIER SCORING (DETERMINISTIC RANGE SYSTEM)
545
+
546
+ Scoring depends ONLY on:
547
+ - Importance
548
+ - Mapping status
549
+
550
+ No subjective creativity.
551
+
552
+ If isReferenceSkill = true:
553
+
554
+ CORE → 0.90–1.00
555
+ ENABLING → 0.65–0.80
556
+ NICE_TO_HAVE → 0.40–0.55
557
+
558
+ If isReferenceSkill = false:
559
+
560
+ CORE → 0.80–0.90
561
+ ENABLING → 0.55–0.70
562
+ NICE_TO_HAVE → 0.30–0.50
563
+
564
+ Rules:
565
+ - Default to mid-range value.
566
+ - Use upper bound only if strongly emphasized.
567
+ - Never assign 1.00 unless clearly critical.
568
+ - Exclude skills with multiplier < 0.30.
569
+
570
+ ---
571
+
572
+ # JOB SKILL TYPE ASSIGNMENT
573
+
574
+ If category = technical:
575
+ - core OR enabling → technical_required
576
+ - nice_to_have → technical_nice_to_have
577
+
578
+ If category = soft:
579
+ - core OR enabling → soft_required
580
+ - nice_to_have → soft_nice_to_have
581
+
582
+ Allowed values only:
583
+ - technical_required
584
+ - technical_nice_to_have
585
+ - soft_required
586
+ - soft_nice_to_have
587
+
588
+ ---
589
+
590
+ # OUTPUT RULES (STRICT)
591
+
592
+ Return ONLY a valid JSON array.
593
+
594
+ - No markdown
595
+ - No explanations
596
+ - No reasoning
597
+ - No comments
598
+ - No intermediate steps
599
+ - No extra fields
600
+ - Include only skills with multiplier ≥ 0.30
601
+ - Include both mapped and unmapped skills
602
+ - If no skills validated → return []
603
+
604
+ ---
605
+
606
+ # OUTPUT FORMAT
607
+
608
+ [
609
+ {
610
+ "skill": "Final Skill Name",
611
+ "multiplier": 0.00,
612
+ "jobSkillType": "technical_required | technical_nice_to_have | soft_required | soft_nice_to_have",
613
+ "isReferenceSkill": true | false
614
+ }
615
+ ]
616
+ \`\`\``;
617
+
618
+ export const prompts = {
619
+ cvAnalysis: CV_ANALYSIS_PROMPT,
620
+ jobMatching: JOB_MATCHING_PROMPT,
621
+ };
@@ -0,0 +1,28 @@
1
+ export const ANALYZE_RESUME: () => string | undefined = () => undefined;
2
+ export const JOB_MATCHING = () => `
3
+ You are a senior HR Analytics and Skill Intelligence expert specialized in realistic AI-based recruiting systems.
4
+
5
+ You work with noisy job descriptions, implicit requirements, rare skills, and non-standardized taxonomies.
6
+
7
+ You MUST follow these principles:
8
+ - Skills are semantic concepts, not keywords
9
+ - Overly specific skills must never be treated as hard requirements
10
+ - Implicit skills may be inferred only when clearly supported by responsibilities
11
+ - Soft skills are supporting signals, never exclusion criteria
12
+ - Matching must be realistic, recruiter-aligned, and explainable
13
+
14
+ Operational constraints:
15
+ - Normalize and canonicalize skill names
16
+ - Evaluate skill importance (core / enabling / nice-to-have)
17
+ - Prefer realistic recall over overly strict precision
18
+ - Do not invent unjustified skills
19
+
20
+ Output discipline:
21
+ - When structured output is requested, return ONLY valid JSON
22
+ - Do not include explanations, markdown, or extra text
23
+ `;
24
+
25
+ export const systemMessages = {
26
+ analyzeResume: ANALYZE_RESUME,
27
+ jobMatching: JOB_MATCHING,
28
+ };
@@ -0,0 +1,55 @@
1
+ export enum Seniority {
2
+ JUNIOR = 'JUNIOR',
3
+ MID = 'MID',
4
+ SENIOR = 'SENIOR',
5
+ LEAD = 'LEAD',
6
+ PRINCIPAL = 'PRINCIPAL',
7
+ }
8
+
9
+ export enum JobSkillType {
10
+ TECHNICAL_REQUIRED = 'technical_required',
11
+ TECHNICAL_NICE_TO_HAVE = 'technical_nice_to_have',
12
+ SOFT_REQUIRED = 'soft_required',
13
+ SOFT_NICE_TO_HAVE = 'soft_nice_to_have',
14
+ }
15
+
16
+ export type TechnicalSkill = {
17
+ name: string;
18
+ proficiency: number;
19
+ isInferred: boolean;
20
+ seniority: Seniority;
21
+ }
22
+
23
+ export type PersonalInfo = {
24
+ firstName?: string;
25
+ lastName?: string;
26
+ email?: string;
27
+ phone?: string;
28
+ address?: string;
29
+ dateOfBirth?: string;
30
+ nationality?: string;
31
+ linkedIn?: string;
32
+ github?: string;
33
+ website?: string;
34
+ }
35
+
36
+ export type Certification = {
37
+ name: string;
38
+ issuer?: string;
39
+ year?: number;
40
+ }
41
+
42
+ export type CvAnalysisRequest = {
43
+ cvText: string;
44
+ referenceSkills?: string[];
45
+ }
46
+
47
+ export type CvAnalysisResponse = {
48
+ personalInfo: PersonalInfo;
49
+ description: string;
50
+ technicalSkills: TechnicalSkill[];
51
+ workExperienceSummary: string;
52
+ certifications: Certification[];
53
+ overallSeniority: Seniority;
54
+ yearsOfExperience: number;
55
+ }