Lab 3 - Pré-qualifier des Candidats avec l’IA

Navigation : Lab 2 << | Index | >> Lab 4

Objectifs d’apprentissage

À la fin de ce laboratoire, vous saurez : 1. Utiliser un LLM pour analyser des documents non structurés (CVs) 2. Comparer plusieurs documents à une référence (offre d’emploi) 3. Structurer la sortie du LLM en format JSON exploitable 4. Noter et classer des candidats de manière objective

Prérequis

  • Avoir complété le Lab 2 (analyse de documents avec LangChain)
  • Clé API OpenAI configurée (OPENAI_API_KEY)
  • Compréhension de base des prompts JSON

Durée estimée : 30-40 minutes


Ce notebook démontre comment utiliser un agent d’IA pour automatiser la pré-qualification de CVs. L’objectif est de comparer des CVs à une offre d’emploi, d’extraire les informations pertinentes, de noter l’adéquation et de fournir un résumé pour aider les recruteurs à se concentrer sur les candidats les plus prometteurs.

  • Étape 1 : Charger l’Offre et les CVs
with open('offre_datascience.txt', 'r', encoding='utf-8') as f:
    offre_emploi = f.read()

with open('cv_candidat_A.txt', 'r', encoding='utf-8') as f:
    cv_candidat_A = f.read()

with open('cv_candidat_B.txt', 'r', encoding='utf-8') as f:
    cv_candidat_B = f.read()

print("--- Offre d'emploi ---")
print(offre_emploi)
print("\n--- CV Candidat A ---")
print(cv_candidat_A)
print("\n--- CV Candidat B ---")
print(cv_candidat_B)
--- Offre d'emploi ---
Titre : Data Scientist Senior
Entreprise : Innovatech Solutions
Description : Nous recherchons un Data Scientist expérimenté pour développer des modèles prédictifs et des solutions d'analyse de données. Vous travaillerez sur des problématiques client complexes, de la conception à la mise en production.
Compétences requises :
- Python (expert)
- Scikit-Learn, Pandas
- SQL
- Expérience en modélisation de séries temporelles
- Excellente communication et capacité à vulgariser des résultats techniques.

--- CV Candidat A ---
Nom : Alice Martin
Expérience : 5 ans en tant que Data Scientist chez DataCorp.
Projets : Développement d'un moteur de recommandation, modèle de prédiction de churn client.
Compétences techniques :
- Langages : Python (avancé), SQL
- Librairies : Scikit-Learn, Pandas, TensorFlow
- Spécialisation : Modèles de séries temporelles pour la prévision de la demande.
Soft Skills : Habituée à présenter les résultats aux comités de direction.

--- CV Candidat B ---
Nom : Bob Durand
Expérience : 2 ans en tant que Data Analyst chez WebAnalytics.
Tâches : Création de dashboards, requêtes SQL pour reporting.
Compétences techniques :
- Langages : R, SQL (intermédiaire)
- Outils : Tableau, PowerBI
Formation : Master en Marketing Digital.
  • Étape 2 : Définir l’Agent de Recrutement IA

Prompt engineering pour l’extraction structurée

Ce laboratoire repose sur le prompt engineering : on pilote le LLM uniquement par la formulation du prompt, sans aucun entraînement ni fine-tuning. Deux techniques sont combinées dans le template ci-dessous :

  • Rôle prompting (ou persona prompting) : on assigne un rôle au modèle (« Vous êtes un recruteur senior spécialisé en Data Science ») pour orienter son registre et ses critères d’évaluation.
  • Format de sortie structuré : on spécifie explicitement le schéma JSON attendu (competences_correspondantes, score_adequation, …). Le temperature=0 favorise une sortie déterministe et fidèle à ce schéma.

Cette capacité à extraire de l’information structurée à partir de texte non structuré (les CVs) par simple instruction est l’illustration directe du paradigme du prompting / in-context learning introduit par GPT-3 (Brown et al., 2020) : un même modèle pré-entraîné accomplit des tâches variées sans mise à jour de ses poids. La chaîne LangChain (prompt | llm) est assemblée en LCEL — voir Lab 2 pour la primitive.

import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
import json

# Assurez-vous d'avoir votre clé API OpenAI dans vos variables d'environnement
# os.environ['OPENAI_API_KEY'] = 'sk-...' 

llm = ChatOpenAI(temperature=0, model="gpt-5-mini")

template = """
Vous êtes un recruteur senior spécialisé en Data Science.
Votre mission est d'analyser le CV suivant par rapport à l'offre d'emploi fournie.
Fournissez votre analyse dans un format JSON structuré.

Offre d'emploi :
{offre}

CV du candidat :
{cv}

Format de sortie attendu (JSON) :
{{
  "competences_correspondantes": ["Liste des compétences du CV qui correspondent à l'offre"],
  "competences_manquantes": ["Liste des compétences requises dans l'offre mais non trouvées dans le CV"],
  "score_adequation": "Note de 1 à 10 sur l'adéquation du profil",
  "resume_analyse": "Un court paragraphe expliquant la note et justifiant votre évaluation."
}}
"""

# Utiliser ChatPromptTemplate (API moderne)
prompt = ChatPromptTemplate.from_template(template)

# Utiliser LCEL (LangChain Expression Language) au lieu de LLMChain
chain = prompt | llm

print("Agent de recrutement IA prêt.")
Agent de recrutement IA prêt.
  • Étape 3 : Évaluer les candidats
# Évaluation du Candidat A - utiliser invoke() et extraire le contenu
response_A = chain.invoke({"offre": offre_emploi, "cv": cv_candidat_A})
result_A_json = json.loads(response_A.content)

print("--- Analyse du Candidat A (Alice Martin) ---")
print(json.dumps(result_A_json, indent=2, ensure_ascii=False))

# Évaluation du Candidat B
response_B = chain.invoke({"offre": offre_emploi, "cv": cv_candidat_B})
result_B_json = json.loads(response_B.content)

print("\n--- Analyse du Candidat B (Bob Durand) ---")
print(json.dumps(result_B_json, indent=2, ensure_ascii=False))
--- Analyse du Candidat A (Alice Martin) ---
{
  "competences_correspondantes": [
    "Python (mentionné 'avancé')",
    "Scikit-Learn",
    "Pandas",
    "SQL",
    "Modélisation de séries temporelles (prévision de la demande)",
    "Communication / présentation aux comités de direction (vulgarisation)"
  ],
  "competences_manquantes": [
    "Python (niveau 'expert' exigé) — le CV indique 'avancé' mais pas explicitement 'expert'",
    "Mise en production / déploiement de modèles (CI/CD, APIs, MLOps) — non mentionné explicitement"
  ],
  "score_adequation": "8",
  "resume_analyse": "Alice Martin est un bon candidat pour le poste : elle possède 5 ans d'expérience, maîtrise les bibliothèques demandées (Scikit-Learn, Pandas), SQL et a une spécialisation claire en séries temporelles — un point clé de l'offre. Sa capacité à présenter aux comités de direction indique qu'elle sait vulgariser les résultats. Les seuls bémols sont l'absence d'une mention explicite du niveau 'expert' en Python (le CV dit 'avancé') et l'absence d'information claire sur l'expérience de mise en production de modèles. Sur ces bases, je lui attribue une note de 8/10, recommandant un échange technique focalisé sur l'industrialisation et le niveau Python pour confirmer l'adéquation complète."
}

--- Analyse du Candidat B (Bob Durand) ---
{
  "competences_correspondantes": [
    "SQL (intermédiaire) — utilisé pour reporting",
    "Création de dashboards et visualisation — Tableau, PowerBI",
    "Expérience analytique : 2 ans en tant que Data Analyst (WebAnalytics)",
    "R (compétence en langage statistique, potentiellement transférable pour analyses)"
  ],
  "competences_manquantes": [
    "Python (expert)",
    "Scikit‑Learn",
    "Pandas",
    "Expérience en modélisation de séries temporelles",
    "Expérience de mise en production de modèles (déploiement)",
    "Excellente communication et capacité avérée à vulgariser des résultats techniques"
  ],
  "score_adequation": "2",
  "resume_analyse": "Le candidat possède des compétences analytiques et en visualisation (SQL, Tableau/PowerBI, 2 ans d'expérience) et maîtrise R, ce qui constitue une base utile. En revanche il lui manque les compétences techniques clés demandées pour un poste senior chez Innovatech (Python expert, Scikit‑Learn, Pandas), l'expérience en modélisation de séries temporelles et la mise en production de modèles. Le niveau d'expérience (2 ans) est insuffisant pour un rôle senior. Profil potentiellement reclassable pour un poste junior/intermédiaire après montée en compétences importante, mais inadéquat pour le poste Senior tel que décrit."
}

Conclusion

Ce laboratoire montre comment l’IA peut transformer le processus de recrutement. En structurant l’information non structurée des CVs, l’agent fournit une analyse objective et rapide.

Bénéfices clés : - Gain de temps : Automatisation du tri initial, permettant aux recruteurs de se concentrer sur les candidats qualifiés. - Objectivité accrue : L’analyse est basée sur des critères définis dans l’offre, réduisant les biais inconscients. - Aide à la décision : Le scoring et le résumé fournissent une base de données solide pour les décisions de recrutement.

Une telle solution peut être directement intégrée dans un Applicant Tracking System (ATS) pour automatiser entièrement le flux de travail.

Exercice 1 : Analyser un CV avec une grille a 5 critères

Enrichissez l’agent de recrutement avec une grille d’evaluation a 5 critères (expérience, competences techniques, soft skills, score, recommandation) au lieu des 3 critères actuels.

# Exercice : Analysez un nouveau CV avec une grille d'evaluation differente
# Creez un template qui evalue les CVs sur 5 criteres au lieu de 3

# TODO: Definissez un nouveau template avec 5 criteres d'evaluation
# Indice: ajoutez 'experience_annee' et 'competences_soft' au format JSON
# Le format de sortie attendu doit contenir:
#   - experience_annee: nombre d'annees d'experience
#   - competences_techniques: liste des competences techniques
#   - competences_soft: liste des soft skills
#   - score_adequation: note de 1 a 10
#   - recommandation: FAVORABLE / A ETUDIER / DEFAVORABLE
template_exo = """
Vous etes un recruteur senior specialise en Data Science.
Analysez le CV suivant par rapport a l'offre d'emploi et fournissez une evaluation sur 5 criteres.

Offre d'emploi :
{offre}

CV du candidat :
{cv}

Format de sortie attendu (JSON) :
{{...}}  <!-- TODO: definissez le schema JSON avec les 5 champs ci-dessus -->

JSON:
"""

# TODO: Creez le prompt et la chaine
prompt_exo = None  # Remplacez None (ChatPromptTemplate.from_template)
chain_exo = None   # Remplacez None (prompt | llm)

# TODO: Testez avec le candidat A
# Indice: utilisez chain_exo.invoke({"offre": offre_emploi, "cv": cv_candidat_A})
# response_exo_A = ...
# result_exo_A = json.loads(response_exo_A.content)
# print(json.dumps(result_exo_A, indent=2, ensure_ascii=False))

Exercice 2 : Generer un email de feedback personnalise

L’un des usages concrets de l’IA en recrutement est la redaction automatique de emails de feedback aux candidats. Vous allez créer un template qui genere un email professionnel, adapte au résultat de l’evaluation (favorable ou non).

Objectif : Créer une chaîne LangChain qui genere un email de feedback personnalise pour chaque candidat.

Indices : - Utilisez les résultats de l’evaluation précédente (result_A_json ou result_B_json) comme données d’entree - Le template doit contenir des variables comme {nom}, {score}, {competences_correspondantes}, {competences_manquantes} - Adaptez le ton du message selon que le score est eleve ou faible

# Exercice 2 : Email de feedback personnalise
# Creez une chaine qui genere un email professionnel pour le candidat

# Etape 1: Definissez le template d'email
# Indice: le template doit contenir des variables {nom}, {score}, {competences_ok}, {competences_manquantes}
email_template = None  # Remplacez None par votre template

# Etape 2: Creez le prompt et la chaine
# Indice: ChatPromptTemplate.from_template(email_template) | llm
email_prompt = None
email_chain = None

# Etape 3: Generez un email pour le Candidat A
# Indice: utilisez les donnees de result_A_json pour remplir les variables
# response_email = email_chain.invoke({"nom": "Alice Martin", "score": result_A_json["score_adequation"], ...})

print("Exercice 2 a completer : email de feedback personnalise")
Exercice 2 a completer : email de feedback personnalise

Exercice 3 : Classement automatique de multiples candidats

Dans un processus de recrutement reel, on recoit souvent des dizaines de CV. Vous allez ecrire une fonction qui automatise l’evaluation et le classement de plusieurs candidats en utilisant la chaîne d’evaluation existante.

Objectif : Créer une fonction classer_candidats() qui prend une liste de CVs et retourne un classement ordonne par score d’adequation decroissant.

Indices : - Utilisez la chain existante pour evaluer chaque candidat individuellement - Stockez les résultats dans une liste de dictionnaires contenant le nom et le score - Utilisez sorted() avec le paramètre key pour trier par score decroissant - Gerez les erreurs potentielles avec un try/except (le JSON peut etre mal formate)

# Exercice 3 : Classement automatique de candidats
# Creez une fonction qui evalue et classe plusieurs candidats

def classer_candidats(candidats, offre):
    """
    Evalue et classe des candidats par score d'adequation decroissant.
    
    Args:
        candidats: liste de tuples (nom, cv_texte)
        offre: texte de l'offre d'emploi
    
    Returns:
        Liste de dictionnaires tries par score decroissant
    """
    resultats = []
    
    for nom, cv in candidats:
        # Indice: utilisez chain.invoke() pour evaluer chaque candidat
        # Indice: parsez la reponse JSON et extrayez le score
        # Indice: ajoutez un try/except pour gerer les erreurs de parsing
        score = None  # Remplacez None par le score extrait
        resultats.append({"nom": nom, "score": score})
    
    # Indice: utilisez sorted(resultats, key=lambda x: x["score"], reverse=True)
    classement = None  # Remplacez None par le tri
    
    return classement

# Testez avec les deux candidats existants
candidats_test = [("Alice Martin", cv_candidat_A), ("Bob Durand", cv_candidat_B)]
# classement_final = classer_candidats(candidats_test, offre_emploi)
# print(json.dumps(classement_final, indent=2, ensure_ascii=False))

print("Exercice 3 a completer : classement automatique de candidats")
Exercice 3 a completer : classement automatique de candidats

References

  1. T. B. Brown, B. Mann, N. Ryder, et al., Language Models are Few-Shot Learners, Advances in Neural Information Processing Systems (NeurIPS) 33, 2020, pp. 1877-1901, arXiv:2005.14165. Article fondateur de GPT-3 établissant le paradigme du prompting et de l’in-context learning : un même modèle accomplit des tâches variées (dont l’extraction structurée) sans fine-tuning, uniquement par instruction. Concept central de ce laboratoire.
  2. LangChain, LangChain Expression Language (LCEL), 2023, python.langchain.com/docs/concepts/lcel. Syntaxe de composition de chaînes (prompt | llm) utilisée pour assembler l’agent de recrutement — primitive détaillée au Lab 2.
Retour au sommet