@edwinfom/resume-intel
Infrastructure d'extraction de CV par LLM. Agnostique au modèle · Extraction spatiale · Fallback OCR · Redaction PII · Streaming · Compatible JSON Resume
Nouveautés de la v0.2.1
- Option
redactPii— masque les emails, téléphones, adresses et URLs avant l'envoi au LLM. Les vraies valeurs sont réinjectées après l'extraction. Compatible RGPD. confidenceScoredanssectionResults— score de fiabilité par section (0.0–1.0) basé sur le nombre de retries et la complétude des champs.- Exports
redactPii,reinjectPii,describePiiRedaction— fonctions utilitaires pour les pipelines de redaction personnalisés.
Voir le Changelog complet pour les détails.
Le problème
Extraire des données structurées depuis des CVs PDF est plus difficile qu'il n'y paraît. La plupart des outils utilisent des regex fragiles qui cassent sur les designs modernes, ou ils s'appuient sur un seul fournisseur d'IA et vous enferment dans un écosystème.
Ce qui se passe réellement en production :
- Mises en page multi-colonnes — les colonnes s'entremêlent. Les dates se mélangent aux descriptions de poste. Le LLM reçoit du chaos sémantique et hallucine.
- DeepSeek et les modèles similaires ne lisent pas les PDFs bruts — ce sont des modèles texte uniquement. Il faut extraire et nettoyer le texte en amont.
- Les LLMs produisent du JSON cassé — accolades manquantes, virgules en trop, JSON enveloppé dans des balises markdown. Sans couche de réparation, le pipeline plante.
- Verrouillage fournisseur — si votre parser ne fonctionne qu'avec Claude ou GPT-4, vous ne pouvez pas passer à un modèle moins cher sans tout réécrire.
- PDFs scannés — aucune couche de texte. Un extracteur basique retourne une chaîne vide sans explication.
- Confidentialité des données — envoyer des CVs bruts avec emails et numéros de téléphone à des APIs LLM tierces peut violer le RGPD ou les politiques internes.
resume-intel est un pipeline en quatre couches qui résout tout cela :
import { parseResume } from '@edwinfom/resume-intel'
import { createDeepSeek } from '@ai-sdk/deepseek'
import { readFileSync } from 'node:fs'
const result = await parseResume(readFileSync('./cv.pdf'), {
model: createDeepSeek({ apiKey: process.env.DEEPSEEK_API_KEY })('deepseek-chat'),
redactPii: true, // RGPD : le LLM ne voit jamais les données personnelles brutes
})
console.log(result.data.basics?.name) // "Jean Dupont"
console.log(result.data.work?.length) // 3
console.log(result.meta.ocrFallback) // true si PDF scanné
console.log(result.meta.sectionResults) // diagnostics par section avec scores de confianceFonctionnement
1 — Détection de scan
Avant toute extraction, le package vérifie si le PDF contient une couche de texte intégrée. Si la densité de texte est insuffisante, le PDF est classifié comme scanné et le pipeline OCR se déclenche automatiquement.
2a — Extraction spatiale (PDFs natifs)
Les extracteurs standard lisent le texte dans l'ordre de rendu, pas dans l'ordre de lecture. Pour un CV à deux colonnes, cela produit :
2020 Ingénieur Senior TypeScript Node.js
2018 Ingénieur Junior React PostgreSQL
resume-intel extrait les coordonnées des boîtes englobantes, détecte les limites de colonnes par analyse des espaces, trie les blocs par position verticale dans chaque colonne, et concatène les colonnes de gauche à droite. Le LLM reçoit un texte propre et ordonné.
2b — Fallback OCR (PDFs scannés)
Quand un PDF scanné est détecté :
- Rastérisation de chaque page en PNG via
pdfjs-dist+@napi-rs/canvasà 150 DPI - Exécution de Tesseract.js (WASM) localement sur chaque image
- Nettoyage des artefacts OCR (barres de progression, séparateurs, numéros de page)
Aucun service OCR externe. Aucun appel réseau. Tout s'exécute localement.
2c — Redaction PII (optionnel)
Avec redactPii: true, les données personnelles sont remplacées par des placeholders déterministes avant l'appel LLM :
john.doe@gmail.com → __PII_EMAIL_0__
+33 6 12 34 56 78 → __PII_PHONE_1__
https://johndoe.dev → __PII_URL_2__
Après l'extraction, les vraies valeurs sont réinjectées. Le résultat final est identique à une extraction sans redaction.
3 — Décomposition de tâches
Au lieu d'un seul appel LLM monolithique, resume-intel exécute des extractions parallèles et ciblées par section — chacune avec son propre prompt, schéma, limite maxTokens et boucle de retry. Cela réduit les hallucinations et isole les échecs.
4 — Réparation et validation JSON
- Réparation — supprime les balises markdown, corrige les virgules en trop, les accolades manquantes via
jsonrepair - Validation — application du schéma Zod
- Auto-correction — les erreurs Zod sont renvoyées au LLM pour correction ciblée (jusqu'à
maxRetriesfois)
Fonctionnalités
| Fonctionnalité | Description |
|---|---|
| Extraction spatiale | Algorithme de boîtes englobantes pour reconstruire l'ordre de lecture multi-colonnes |
| Fallback OCR | Tesseract.js + @napi-rs/canvas pour les PDFs scannés, entièrement local |
| Redaction PII | Masque emails, téléphones, adresses, URLs avant le LLM — réinjecte après |
| Agnostique au modèle | Vercel AI SDK — fonctionne avec DeepSeek, OpenAI, Anthropic, Gemini, Ollama |
| JSON Resume v1 | Sortie conforme au standard ouvert utilisé par des centaines d'outils |
| 15 sections | basics, work, education, skills, languages, projects, awards, certificates, publications, volunteer, interests, references |
| Sections personnalisées | Option sections — extrayez uniquement ce dont vous avez besoin |
| Schéma personnalisé | Option outputSchema — remplacez JSON Resume par votre propre schéma Zod |
| Validation Zod | Application complète du schéma avec boucle de retry auto-correctrice |
| Réparation JSON | jsonrepair gère les balises markdown, virgules en trop, réponses tronquées |
| Décomposition de tâches | Extraction parallèle par section avec retry indépendant |
| Scores de confiance | Signal de fiabilité par section dans sectionResults |
| Streaming | streamResume() — AsyncGenerator pour les mises à jour UI progressives |
| CLI | resume-intel parse <fichier.pdf> — extraction depuis le terminal |
| Prêt pour le serverless | Worker via MessageChannel — fonctionne sur Vercel et AWS Lambda |
| TypeScript first | Typage complet, build dual ESM + CJS |