Lección 29 · 10 min · Gratis

Flujo de trabajo multiagente para contratación

Open In Colab

Introducción

El Flujo de Trabajo Multiagente para Contratación es un sistema automatizado diseñado para ayudar a optimizar el proceso de contratación a través de agentes de IA especializados que trabajan en armonía para mejorar la evaluación de candidatos, ahorrar tiempo y recursos, y mejorar los resultados generales de contratación.

El Problema

El panorama actual de la contratación enfrenta tres desafíos críticos:

  1. Volumen abrumador: Los reclutadores luchan por procesar eficientemente un gran número de solicitudes, a menudo pasando por alto a candidatos calificados.

  2. Ineficiencia manual: La revisión tradicional de currículums consume mucho tiempo, es inconsistente y vulnerable a sesgos.

  3. Mala experiencia del candidato: Los tiempos de respuesta lentos y la comunicación fragmentada dañan la marca del empleador y hacen perder talento superior.

Por qué esto importa

La contratación ineficaz impacta directamente los resultados del negocio a través de:

  • Rendimiento reducido: Perder candidatos calificados lleva a contrataciones subóptimas y a un bajo rendimiento del equipo.
  • Retrasos en el negocio: Los ciclos de contratación prolongados posponen proyectos e iniciativas críticas.
  • Costos más altos: Los procesos ineficientes y las vacantes prolongadas aumentan los costos de contratación.

Nuestra Solución

El Flujo de Trabajo Multiagente para Contratación aborda estos desafíos a través de un sistema coordinado de agentes de IA especializados:

  1. DocumentAgent: Extrae y procesa inteligentemente texto de currículums y descripciones de puestos utilizando el OCR avanzado de Mistral.

  2. JobAnalysisAgent: Analiza las descripciones de puestos para identificar las habilidades, experiencia y cualificaciones requeridas.

  3. ResumeAnalysisAgent: Analiza currículums para crear perfiles de candidatos estructurados con capacidades clave.

  4. MatchingAgent: Evalúa a los candidatos según los requisitos del puesto con una comprensión matizada que va más allá de la coincidencia de palabras clave.

  5. EmailCommunicationAgent: Genera comunicaciones por correo electrónico personalizadas y programa entrevistas con candidatos calificados.

  6. CoordinatorAgent: Orquesta todo el flujo de trabajo entre los agentes para una operación sin interrupciones.

La solución utiliza Mistral LLM para la comprensión del lenguaje, mecanismos de salida estructurada para una extracción de datos consistente y Mistral OCR para el análisis de documentos.

Ejemplo: Contratación de un científico de datos

Para ilustrar cómo funciona en la práctica el Flujo de Trabajo Multiagente para Contratación, considera un ejemplo realista:

HireFive necesita contratar a un Científico de Datos Senior con experiencia en machine learning. La descripción del puesto especifica requisitos que incluyen más de 3 años de experiencia, dominio de Python y frameworks de deep learning, y una maestría en un campo cuantitativo. De un grupo de currículums de candidatos, el flujo de trabajo automáticamente:

  • Extrae requisitos estructurados de la descripción del puesto, identificando habilidades críticas.
  • Analiza todos los currículums, creando perfiles estandarizados con habilidades, experiencia y educación.
  • Evalúa a cada candidato, asignando puntuaciones como "Habilidades técnicas: 32/40" y "Experiencia: 25/30".
  • Identifica a los candidatos que superan el umbral de 70 puntos.
  • Envía automáticamente invitaciones personalizadas a entrevistas con enlaces de programación a estos candidatos.

Todo el proceso se completa en minutos, proporcionando al gerente de contratación de HireFive una lista clasificada de candidatos calificados mientras elimina horas de revisión manual de currículums.

Arquitectura de la solución

Solution Architecture

Instalación

!pip install mistralai
Collecting mistralai
  Downloading mistralai-1.6.0-py3-none-any.whl.metadata (30 kB)
Collecting eval-type-backport>=0.2.0 (from mistralai)
  Downloading eval_type_backport-0.2.2-py3-none-any.whl.metadata (2.2 kB)
Requirement already satisfied: httpx>=0.28.1 in /usr/local/lib/python3.11/dist-packages (from mistralai) (0.28.1)
Requirement already satisfied: pydantic>=2.10.3 in /usr/local/lib/python3.11/dist-packages (from mistralai) (2.11.2)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from mistralai) (2.8.2)
Requirement already satisfied: typing-inspection>=0.4.0 in /usr/local/lib/python3.11/dist-packages (from mistralai) (0.4.0)
Requirement already satisfied: anyio in /usr/local/lib/python3.11/dist-packages (from httpx>=0.28.1->mistralai) (4.9.0)
Requirement already satisfied: certifi in /usr/local/lib/python3.11/dist-packages (from httpx>=0.28.1->mistralai) (2025.1.31)
Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/dist-packages (from httpx>=0.28.1->mistralai) (1.0.7)
Requirement already satisfied: idna in /usr/local/lib/python3.11/dist-packages (from httpx>=0.28.1->mistralai) (3.10)
Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/dist-packages (from httpcore==1.*->httpx>=0.28.1->mistralai) (0.14.0)
Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2.10.3->mistralai) (0.7.0)
Requirement already satisfied: pydantic-core==2.33.1 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2.10.3->mistralai) (2.33.1)
Requirement already satisfied: typing-extensions>=4.12.2 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2.10.3->mistralai) (4.13.1)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.8.2->mistralai) (1.17.0)
Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.11/dist-packages (from anyio->httpx>=0.28.1->mistralai) (1.3.1)
Downloading mistralai-1.6.0-py3-none-any.whl (288 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 288.7/288.7 kB 7.0 MB/s eta 0:00:00
[?25hDownloading eval_type_backport-0.2.2-py3-none-any.whl (5.8 kB)
Installing collected packages: eval-type-backport, mistralai
Successfully installed eval-type-backport-0.2.2 mistralai-1.6.0

Importaciones

import os
import time
import json
import requests
from typing import List, Optional, Dict, Any
from pydantic import BaseModel, Field
from mistralai.client import Mistral

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

Configurar claves de API

os.environ['MISTRAL_API_KEY'] = "YOUR MISTRALAI API KEY" # Get it from https://console.mistral.ai/api-keys

Inicializar cliente de API de Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

Descargar datos

Aquí, descargamos los datos necesarios para la demostración.

  1. Descripción del puesto.
  2. Currículums de los candidatos.
Funciones de ayuda para descargar la descripción del puesto y los currículums de los candidatos
def download_job_description(url, output_path = "job_description.pdf"):
    """
    Download job description from a given URL.
    """
    response = requests.get(url)
    with open(output_path, "wb") as f:
        f.write(response.content)
    print(f"Downloaded {output_path}")

def download_resumes(url, local_dir="resumes"):
    """
    Download resumes from the given URL.
    """

    response = requests.get(url)
    
    if response.status_code != 200:
        print("Failed to retrieve folder contents:", response.text)
        return
    
    data = response.json()
    os.makedirs(local_dir, exist_ok=True)

    print(f"{len(data)} files available for download:")
    for file in data:
        file_name = file["name"]
        download_url = file["download_url"]

        r = requests.get(download_url)
        with open(os.path.join(local_dir, file_name), "wb") as f:
            f.write(r.content)
        print(f"Downloaded {file_name}")

Descargar descripción del puesto

url = "https://raw.githubusercontent.com/mistralai/cookbook/main/mistral/agents/non_framework/recruitment_agent/job_description.pdf"
output_path = "job_description.pdf"

download_job_description(url, output_path)
Downloaded job_description.pdf

Descargar currículums de candidatos

download_resumes(
    url = "https://api.github.com/repos/mistralai/cookbook/contents/mistral/agents/non_framework/recruitment_agent/resumes",
    local_dir="resumes"
)
13 files available for download:
Downloaded Resume 10_ Carlos Mendez.pdf
Downloaded Resume 11_ Alex Patel.pdf
Downloaded Resume 12_ Taylor Williams.pdf
Downloaded Resume 13_ Jordan Smith.pdf
Downloaded Resume 1_ Sarah Chen.pdf
Downloaded Resume 2_ Michael Rodriguez.pdf
Downloaded Resume 3_ Jennifer Park.pdf
Downloaded Resume 4_ David Wilson.pdf
Downloaded Resume 5_ Priya Sharma.pdf
Downloaded Resume 6_ James Lee.pdf
Downloaded Resume 7_ Emily Johnson.pdf
Downloaded Resume 8_ Robert Thompson.pdf
Downloaded Resume 9_ Lisa Wang.pdf

Definir modelos Pydantic

Los modelos Pydantic proporcionan validación de datos estructurados entre agentes, asegurando formatos consistentes para perfiles de candidatos, requisitos de puestos y puntuaciones de evaluación, al tiempo que permiten una integración perfecta con las capacidades de análisis de Mistral LLM. A continuación, se presentan los diferentes modelos Pydantic que usamos para:

  • Skill: Representa una habilidad técnica o blanda de un candidato con su nivel de competencia y años de experiencia.

  • Education: Captura las cualificaciones educativas, incluyendo el título, campo de estudio, institución y métricas de rendimiento.

  • Experience: Rastrea la experiencia profesional con detalles del rol, duración, habilidades utilizadas y logros clave.

  • ContactDetails: Almacena la información de contacto del candidato, incluyendo nombre, correo electrónico y canales de comunicación opcionales.

  • JobRequirements: Define los requisitos del puesto, incluyendo habilidades obligatorias y preferidas, nivel de experiencia y cualificaciones educativas.

  • CandidateProfile: Consolida el perfil profesional completo de un candidato, incluyendo detalles de contacto, habilidades, educación e historial laboral.

  • SkillMatch: Evalúa la alineación de habilidades individuales entre los requisitos del puesto y las capacidades del candidato con puntuaciones de confianza.

  • CandidateScore: Proporciona una puntuación integral en áreas clave de evaluación con cálculo de puntuación total y fortalezas/brechas identificadas.

  • CandidateResult: Conecta la información del archivo con los datos del candidato extraídos y las puntuaciones de evaluación para la clasificación y selección final.

Modelos Pydantic para extracción estructurada.

class Skill(BaseModel):
    name: str = Field(description="Name of the skill or technology")
    level: Optional[str] = Field(description="Proficiency level (beginner, intermediate, advanced)")
    years: Optional[float] = Field(description="Years of experience with this skill")

class Education(BaseModel):
    degree: str = Field(description="Type of degree or certification obtained")
    field: str = Field(description="Field of study or specialization")
    institution: str = Field(description="Name of educational institution")
    year_completed: Optional[int] = Field(description="Year when degree was completed")
    gpa: Optional[float] = Field(description="Grade Point Average, typically on 4.0 scale")

class Experience(BaseModel):
    title: str = Field(description="Job title or position held")
    company: str = Field(description="Name of employer or organization")
    duration_years: float = Field(description="Duration of employment in years")
    skills_used: List[str] = Field(description="Skills utilized in this role")
    achievements: List[str] = Field(description="Key accomplishments or responsibilities")
    relevance_score: Optional[float] = Field(description="Relevance to current job opening (0-10 scale)")

class ContactDetails(BaseModel):
    name: str = Field(description="Full name of the candidate")
    email: str = Field(description="Primary email address for contact")
    phone: Optional[str] = Field(description="Phone number with country code if applicable")
    location: Optional[str] = Field(description="Current city and country/state")
    linkedin: Optional[str] = Field(description="LinkedIn profile URL")
    website: Optional[str] = Field(description="Personal or portfolio website URL")

class JobRequirements(BaseModel):
    required_skills: List[Skill] = Field(description="Skills that are mandatory for the position")
    preferred_skills: List[Skill] = Field(description="Skills that are desired but not required")
    min_experience_years: float = Field(description="Minimum years of experience required")
    required_education: List[Education] = Field(description="Mandatory educational qualifications")
    preferred_domains: List[str] = Field(description="Industry domains or sectors preferred for experience")

class CandidateProfile(BaseModel):
    contact_details: ContactDetails = Field(description="Candidate's personal and contact information")
    skills: List[Skill] = Field(description="Technical and soft skills possessed by the candidate")
    education: List[Education] = Field(description="Educational background and qualifications")
    experience: List[Experience] = Field(description="Professional work history and experience")

class SkillMatch(BaseModel):
    skill_name: str = Field(description="Name of the skill being evaluated")
    present: bool = Field(description="Whether the candidate possesses this skill")
    match_level: float = Field(description="How well the candidate's skill matches the requirement (0-10 scale)")
    confidence: float = Field(description="Confidence in the skill evaluation (0-1 scale)")
    notes: str = Field(description="Additional context about the skill match assessment")

class CandidateScore(BaseModel):
    technical_skills_score: float = Field(description="Assessment of technical capabilities (0-40 points)")
    experience_score: float = Field(description="Evaluation of relevant work experience (0-30 points)")
    education_score: float = Field(description="Rating of educational qualifications (0-15 points)")
    additional_score: float = Field(description="Score for other relevant factors (0-15 points)")
    total_score: float = Field(description="Aggregate candidate evaluation score (0-100)")
    key_strengths: List[str] = Field(description="Primary candidate advantages for this role")
    key_gaps: List[str] = Field(description="Areas where the candidate lacks desired qualifications")
    confidence: float = Field(description="Overall confidence in the evaluation accuracy (0-1 scale)")
    notes: str = Field(description="Supplementary observations about the candidate fit")

class CandidateResult(BaseModel):
    file_name: str = Field(description="Name of the source resume file")
    contact_details: ContactDetails = Field(description="Candidate's contact information")
    candidate_profile: CandidateProfile = Field(description="Complete extracted candidate profile")
    score: CandidateScore = Field(description="Detailed evaluation scores and assessment")

Clase de agente base

La clase Agent sirve como base para todos los agentes especializados, proporcionando una interfaz estandarizada para el procesamiento y la comunicación entre agentes en el flujo de trabajo de contratación.

Cada agente implementa el método común process() mientras hereda las capacidades de gestión de identidad y comunicación.

class Agent:
    def __init__(self, name: str, client: Mistral):
        self.name = name
        self.client = client

    def process(self, message):
        """Base process method - to be implemented by child classes"""
        raise NotImplementedError("Subclasses must implement process method")

    def communicate(self, recipient_agent, message):
        """Send message to another agent"""
        return recipient_agent.process(message)

DocumentAgent: Maneja la extracción de documentos y el OCR

El DocumentAgent maneja el procesamiento de documentos extrayendo texto estructurado de varios archivos utilizando las capacidades de OCR de Mistral. Transforma complejos PDF de currículums y descripciones de puestos en texto, sirviendo como la puerta de entrada de datos inicial para todo el flujo de trabajo de contratación.

class DocumentAgent(Agent):
    def __init__(self, client: Mistral):
        super().__init__("DocumentAgent", client)

    def process(self, file_info):
        """Process document extraction request"""
        file_path, file_name = file_info
        return self.extract_text_from_file(file_path, file_name)

    def extract_text_from_file(self, file_path: str, file_name: str) -> str:
        """Extract text from a file using Mistral OCR"""
        try:
            # Upload the file
            uploaded_file = self.client.files.upload(
                file={
                    "file_name": file_name,
                    "content": open(file_path, "rb"),
                },
                purpose="ocr"
            )

            # Get signed URL
            signed_url = self.client.files.get_signed_url(file_id=uploaded_file.id)

            # Process with OCR
            ocr_response = self.client.ocr.process(
                model="mistral-ocr-latest",
                document={
                    "type": "document_url",
                    "document_url": signed_url.url,
                }
            )

            # Extract and return the text
            extracted_text = ""
            for page in ocr_response.pages:
                extracted_text += page.markdown + "\n\n"

            return extracted_text

        except Exception as e:
            print(f"Error extracting text from {file_name}: {str(e)}")
            return ""

JobAnalysisAgent: Maneja la extracción y el análisis de requisitos de puestos

El JobAnalysisAgent extrae requisitos de puestos estructurados de descripciones de puestos en texto plano utilizando Mistral LLM. Transforma las publicaciones de puestos no estructuradas en modelos de datos organizados que capturan las habilidades, niveles de experiencia y cualificaciones educativas necesarias para la coincidencia de candidatos.

class JobAnalysisAgent(Agent):
    def __init__(self, client: Mistral):
        super().__init__("JobAnalysisAgent", client)

    def process(self, jd_text):
        """Process job description text"""
        return self.extract_job_requirements(jd_text)

    def extract_job_requirements(self, jd_text: str) -> JobRequirements:
        """Extract structured job requirements from a job description"""
        prompt = f"""
        Extract the key job requirements from the following job description.
        Focus on required skills, preferred skills, experience requirements, and education requirements.

        Job Description:
        {jd_text}
        """

        response = self.client.chat.parse(
            model="mistral-small-latest",
            messages=[
                {"role": "system", "content": "Extract structured job requirements from the job description."},
                {"role": "user", "content": prompt}
            ],
            response_format=JobRequirements,
            temperature=0
        )

        return json.loads(response.choices[0].message.content)

ResumeAnalysisAgent: Maneja el análisis de currículums y la extracción de perfiles

El ResumeAnalysisAgent transforma el texto sin procesar de los currículums en perfiles de candidatos estructurados utilizando las capacidades de análisis de Mistral LLM. Extrae y organiza información clave, incluyendo detalles de contacto, habilidades, historial educativo y experiencia profesional en estructuras de datos estandarizadas para una evaluación consistente.

class ResumeAnalysisAgent(Agent):
    def __init__(self, client: Mistral):
        super().__init__("ResumeAnalysisAgent", client)

    def process(self, resume_text):
        """Process resume text"""
        return self.extract_candidate_profile(resume_text)

    def extract_candidate_profile(self, resume_text: str) -> CandidateProfile:
        """Extract structured candidate information from resume text"""
        prompt = f"""
        Extract the candidate's contact details, skills, education, and experience from the following resume.
        Be thorough and include all relevant information.

        Resume:
        {resume_text}
        """

        response = self.client.chat.parse(
            model="mistral-small-latest",
            messages=[
                {"role": "system", "content": "Extract structured candidate information from the resume."},
                {"role": "user", "content": prompt}
            ],
            response_format=CandidateProfile,
            temperature=0
        )

        return json.loads(response.choices[0].message.content)

MatchingAgent: Evalúa la idoneidad del candidato según los requisitos del puesto

El MatchingAgent evalúa los perfiles de los candidatos según los requisitos del puesto para generar una puntuación integral en habilidades técnicas, experiencia, educación y cualificaciones adicionales. Emplea Mistral LLM para evaluar la calidad y relevancia de los atributos del candidato más allá de la simple coincidencia de palabras clave, produciendo una evaluación detallada con métricas de confianza y fortalezas y debilidades identificadas.

class MatchingAgent(Agent):
    def __init__(self, client: Mistral):
        super().__init__("MatchingAgent", client)

    def process(self, data):
        """Process job requirements and candidate profile to generate score"""
        job_requirements, candidate_profile, resume_text = data
        return self.evaluate_candidate(job_requirements, candidate_profile, resume_text)

    def evaluate_candidate(self, job_requirements: JobRequirements, candidate_profile: CandidateProfile, resume_text: str) -> CandidateScore:
        """Evaluate how well a candidate matches the job requirements"""
        # Convert to JSON for inclusion in the prompt
        job_req_json = json.dumps(job_requirements, indent=2)
        candidate_json = json.dumps(candidate_profile, indent=2)

        prompt = f"""
        Evaluate how well the candidate matches the job requirements.

        Job Requirements:
        {job_req_json}

        Candidate Profile:
        {candidate_json}

        Provide a detailed scoring breakdown, highlighting strengths and gaps.
        Assess the quality and relevance of the candidate's experience, not just keyword matches.
        Include confidence levels for your assessment.

        Technical skills should be scored out of 40 points.
        Experience should be scored out of 30 points.
        Education should be scored out of 15 points.
        Additional qualifications should be scored out of 15 points.
        The total score should be out of 100 points.
        """

        response = self.client.chat.parse(
            model="mistral-small-latest",
            messages=[
                {"role": "system", "content": "Evaluate the candidate's match to the job requirements with detailed scoring."},
                {"role": "user", "content": prompt}
            ],
            response_format=CandidateScore,
            temperature=0.2  # Slight randomness for nuanced evaluation
        )

        return json.loads(response.choices[0].message.content)

EmailCommunicationAgent: Maneja la generación y el envío de correos electrónicos

El EmailCommunicationAgent genera comunicaciones por correo electrónico personalizadas para los candidatos y las envía a través de la integración SMTP. Elabora mensajes contextualmente relevantes basados en las cualificaciones del candidato y la información de programación, gestionando el paso final crítico del compromiso del candidato en el flujo de trabajo de contratación.

class EmailCommunicationAgent(Agent):
    def __init__(self, client: Mistral, sender_email: str, app_password: str):
        super().__init__("EmailCommunicationAgent", client)
        self.sender_email = sender_email
        self.app_password = app_password

    def process(self, data):
        """Process email sending request"""
        candidate, calendly_link, subject = data
        return self.send_interview_invitation(candidate, calendly_link, subject)

    def send_interview_invitation(self, candidate, calendly_link: str, subject: str):
        """Generate and send personalized email to candidate"""
        name = candidate["contact_details"]['name']
        email = candidate["contact_details"]['email']

        # Create email HTML content
        html_content = f"""\
        <html>
          <body>
            <p>Hello {name},</p>
            <p>I'm the Hiring Manager from HireFive. Thank you for applying for the Data Scientist position at our company.</p>
            <p>We were impressed with your background and would like to schedule an initial screening call to discuss your experience and interest in the role.</p>
            <p>Please select a suitable time slot using our <a href="{calendly_link}">Calendly link</a>.</p>
            <p>Looking forward to speaking with you soon.</p>
            <p>Best regards,<br>
            Hiring Manager<br>
            HireFive</p>
          </body>
        </html>
        """

        if self.app_password:
            try:
                self.send_email(email, subject, html_content)
                return f"Email sent to {name} at {email}"
            except Exception as e:
                return f"Failed to send email to {name} ({email}): {str(e)}"
        else:
            return f"Would send email to {name} at {email} - Email subject: {subject}"

    def send_email(self, receiver_email, subject, html_content):
        """Send an email using Gmail SMTP"""
        # Create message container
        message = MIMEMultipart('alternative')
        message['From'] = self.sender_email
        message['To'] = receiver_email
        message['Subject'] = subject

        # Attach HTML part
        message.attach(MIMEText(html_content, 'html'))

        try:
            # Create SMTP session
            server = smtplib.SMTP('smtp.gmail.com', 587)
            server.starttls()  # Enable security

            # Login with Gmail account and app password
            server.login(self.sender_email, self.app_password)

            # Send email
            text = message.as_string()
            server.sendmail(self.sender_email, receiver_email, text)

        finally:
            server.quit()  # Close the connection

CoordinatorAgent: Gestiona el flujo de trabajo y coordina entre agentes

El CoordinatorAgent orquesta todo el flujo de trabajo de contratación gestionando la comunicación y el flujo de datos entre todos los agentes especializados. Inicia el proceso con descripciones de puestos, distribuye currículums, recopila resultados de evaluación, aplica filtros basados en umbrales y activa las comunicaciones con los candidatos, sirviendo como la inteligencia central que garantiza la ejecución perfecta del sistema de contratación multiagente.

class CoordinatorAgent(Agent):
    def __init__(self, client: Mistral):
        super().__init__("CoordinatorAgent", client)
        self.document_agent = DocumentAgent(client)
        self.job_analysis_agent = JobAnalysisAgent(client)
        self.resume_analysis_agent = ResumeAnalysisAgent(client)
        self.matching_agent = MatchingAgent(client)
        self.email_communication_agent = None  # Will be initialized later with email credentials

    def set_email_communication_agent(self, sender_email: str, app_password: str):
        """Initialize communication agent with email credentials"""
        self.email_communication_agent = EmailCommunicationAgent(self.client, sender_email, app_password)

    def process_hiring_workflow(self, jd_file_path: str, resume_dir: str, output_path: str,
                               threshold_score: float, calendly_link: str, email_subject: str):
        """
        Coordinate the entire hiring workflow from document processing to interview scheduling
        """
        results = []

        # Process job description
        print(f"🤖 DocumentAgent extracting text from job description...")
        jd_text = self.document_agent.process((jd_file_path, os.path.basename(jd_file_path)))

        if not jd_text:
            print("❌ Failed to extract text from job description. Aborting.")
            return results

        # Extract job requirements
        print(f"🤖 JobAnalysisAgent analyzing job description...")
        job_requirements = self.job_analysis_agent.process(jd_text)

        time.sleep(10)

        # Process each resume in the directory
        resume_files = [f for f in os.listdir(resume_dir) if os.path.isfile(os.path.join(resume_dir, f))]

        for filename in resume_files[:5]:
            file_path = os.path.join(resume_dir, filename)
            print(f"\n🤖 DocumentAgent processing resume: {filename}")

            # Extract text from resume
            resume_text = self.document_agent.process((file_path, filename))

            time.sleep(10)

            if resume_text:
                # Extract candidate profile
                print(f"🤖 ResumeAnalysisAgent extracting candidate profile...")
                candidate_profile = self.resume_analysis_agent.process(resume_text)

                # Evaluate candidate match
                print(f"🤖 MatchingAgent evaluating candidate {candidate_profile['contact_details']['name']}...")
                score = self.matching_agent.process((job_requirements, candidate_profile, resume_text))

                # Create result object
                result = {
                    "file_name": filename,
                    "contact_details": candidate_profile["contact_details"],
                    "candidate_profile": candidate_profile,
                    "score": score
                }

                results.append(result)

                # Add a small delay to avoid rate limits
                time.sleep(10)
            else:
                print(f"❌ DocumentAgent failed to extract text from {filename}. Skipping this resume.")

        # Sort results by total score
        results.sort(key=lambda x: x["score"]['total_score'], reverse=True)

        # Save results to file
        with open(output_path, 'w') as f:
            json.dump([result for result in results], f, indent=2)

        print(f"\n🤖 CoordinatorAgent saved results to {output_path}")

        # Print summary of results
        print("\n===== CANDIDATE RANKING =====")
        for i, result in enumerate(results, 1):
            name = result["contact_details"]['name']
            score = result["score"]['total_score']
            print(f"{i}. {name}: {score}/100")

        # Send interview invitations to candidates above threshold
        if self.email_communication_agent:
            selected_candidates = [r for r in results if r["score"]['total_score'] >= threshold_score]

            print(f"\n🤖 EmailCommunicationAgent preparing to send interview invitations to {len(selected_candidates)} candidates who scored {threshold_score}+ out of 100...\n")

            for candidate in selected_candidates:
                response = self.email_communication_agent.process((candidate, calendly_link, email_subject))
                time.sleep(1)

        return results

Ejecutar el flujo de trabajo

Para ejecutar el Flujo de Trabajo Multiagente para Contratación, simplemente necesitas:

  • Configurar las rutas de archivo para la descripción del puesto, el directorio de currículums y los resultados de salida.
  • Configurar las credenciales de correo electrónico y el enlace de programación de Calendly.
  • Inicializar el CoordinatorAgent con tu cliente de Mistral.
  • Configurar el EmailCommunicationAgent con las credenciales del remitente.
  • Ejecutar el flujo de trabajo con la puntuación umbral deseada.

Definir rutas

jd_file_path = "job_description.pdf"
resume_dir = "resumes/"
output_path = "candidate_results.json"

Configuración de la contraseña de la aplicación de Gmail

Para usar la funcionalidad de correo electrónico en el Flujo de Trabajo Multiagente para Contratación con Gmail, deberás crear una contraseña de aplicación:

  1. Habilita la Verificación en 2 Pasos en tu Cuenta de Google:

    • Ve a tu Cuenta de Google → Seguridad
    • En "Iniciar sesión en Google", selecciona Verificación en 2 Pasos → Empezar
  2. Genera una Contraseña de Aplicación:

    • Ve a tu Cuenta de Google → Seguridad
    • En "Iniciar sesión en Google", selecciona Contraseñas de aplicación
    • Selecciona "Correo" como la aplicación y "Otro" como el dispositivo (nómbralo "Flujo de trabajo de contratación")
    • Haz clic en "Generar"
    • Google mostrará una contraseña de 16 caracteres (cuatro grupos de cuatro caracteres)
  3. Usa esta contraseña de aplicación en la configuración de tu flujo de trabajo:

    sender_email = "[email protected]"
    app_password = "abcd efgh ijkl mnop"  # Your generated app password
    

Esta contraseña de aplicación omite la 2FA y permite que el flujo de trabajo envíe correos electrónicos a través de tu cuenta de Gmail de forma segura sin almacenar tu contraseña real de Google en el código.

sender_email = "<Your EmailID>"
app_password = "<Your generated app password>"
calendly_link = "<Your Calendly Link>"
email_subject = "HireFive: Next Steps for Your Data Scientist Application"

Inicializar agente coordinador

coordinator = CoordinatorAgent(client)

Configurar agente de comunicación con credenciales de correo electrónico

coordinator.set_communication_agent(sender_email, app_password)

Ejecutar flujo de trabajo de contratación

Nota: Hemos considerado 5 currículums de candidatos para simplificar.

threshold_score = 65  # Only send to candidates with 65+ overall score
results = coordinator.process_hiring_workflow(
    jd_file_path=jd_file_path,
    resume_dir=resume_dir,
    output_path=output_path,
    threshold_score=threshold_score,
    calendly_link=calendly_link,
    email_subject=email_subject
)
🤖 DocumentAgent extracting text from job description...
🤖 JobAnalysisAgent analyzing job description...

🤖 DocumentAgent processing resume: Resume 5_ Priya Sharma.pdf
🤖 ResumeAnalysisAgent extracting candidate profile...
🤖 MatchingAgent evaluating candidate Priya Sharma...

🤖 DocumentAgent processing resume: Resume 3_ Jennifer Park.pdf
🤖 ResumeAnalysisAgent extracting candidate profile...
🤖 MatchingAgent evaluating candidate Jennifer Park...

🤖 DocumentAgent processing resume: Resume 6_ James Lee.pdf
🤖 ResumeAnalysisAgent extracting candidate profile...
🤖 MatchingAgent evaluating candidate James Lee...

🤖 DocumentAgent processing resume: Resume 7_ Emily Johnson.pdf
🤖 ResumeAnalysisAgent extracting candidate profile...
🤖 MatchingAgent evaluating candidate Emily Johnson...

🤖 DocumentAgent processing resume: Resume 2_ Michael Rodriguez.pdf
🤖 ResumeAnalysisAgent extracting candidate profile...
🤖 MatchingAgent evaluating candidate Michael Rodriguez...

🤖 CoordinatorAgent saved results to candidate_results.json

===== CANDIDATE RANKING =====
1. Michael Rodriguez: 86/100
2. Priya Sharma: 85/100
3. Jennifer Park: 84/100
4. Emily Johnson: 67/100
5. James Lee: 54/100

🤖 EmailCommunicationAgent preparing to send interview invitations to 4 candidates who scored 65+ out of 100...

Puedes verificar los resultados extraídos de cada uno de los candidatos.

results
[{'file_name': 'Resume 2_ Michael Rodriguez.pdf',
  'contact_details': {'name': 'Michael Rodriguez',
   'email': '[email protected]',
   'phone': '(510) 555-7321',
   'location': 'Oakland, CA',
   'linkedin': 'linkedin.com/in/michaelrodriguez',
   'website': None},
  'candidate_profile': {'contact_details': {'name': 'Michael Rodriguez',
    'email': '[email protected]',
    'phone': '(510) 555-7321',
    'location': 'Oakland, CA',
    'linkedin': 'linkedin.com/in/michaelrodriguez',
    'website': None},
   'skills': [{'name': 'Python', 'level': 'Advanced', 'years': 4},
    {'name': 'SQL', 'level': 'Advanced', 'years': 4},
    {'name': 'R', 'level': 'Intermediate', 'years': 4},
    {'name': 'NumPy', 'level': 'Intermediate', 'years': 4},
    {'name': 'Pandas', 'level': 'Intermediate', 'years': 4},
    {'name': 'scikit-learn', 'level': 'Intermediate', 'years': 4},
    {'name': 'XGBoost', 'level': 'Intermediate', 'years': 4},
    {'name': 'TensorFlow', 'level': 'Intermediate', 'years': 4},
    {'name': 'AWS', 'level': 'Intermediate', 'years': 4},
    {'name': 'Spark', 'level': 'Intermediate', 'years': 4},
    {'name': 'Tableau', 'level': 'Intermediate', 'years': 4},
    {'name': 'Matplotlib', 'level': 'Intermediate', 'years': 4},
    {'name': 'Seaborn', 'level': 'Intermediate', 'years': 4},
    {'name': 'PostgreSQL', 'level': 'Intermediate', 'years': 4},
    {'name': 'MySQL', 'level': 'Intermediate', 'years': 4},
    {'name': 'Redshift', 'level': 'Intermediate', 'years': 4},
    {'name': 'Git', 'level': 'Intermediate', 'years': 4},
    {'name': 'Jupyter', 'level': 'Intermediate', 'years': 4},
    {'name': 'Docker', 'level': 'Intermediate', 'years': 4}],
   'education': [{'degree': 'MS',
     'field': 'Statistics',
     'institution': 'University of California, Berkeley',
     'year_completed': 2018,
     'gpa': 3.8},
    {'degree': 'BS',
     'field': 'Mathematics',
     'institution': 'University of California, Los Angeles',
     'year_completed': 2016,
     'gpa': 3.7}],
   'experience': [{'title': 'Data Scientist',
     'company': 'Fintech Solutions Inc.',
     'duration_years': 4,
     'skills_used': ['Python',
      'SQL',
      'Tableau',
      'Machine Learning',
      'Data Visualization'],
     'achievements': ['Built and deployed machine learning models for fraud detection, reducing false positives by 30% while maintaining 99% fraud capture rate',
      'Designed and implemented a customer segmentation model using clustering algorithms, leading to a 15% increase in marketing campaign conversion rates',
      'Developed a churn prediction model with 85% accuracy, enabling proactive retention strategies',
      'Created interactive dashboards for executive reporting using Tableau',
      'Mentored junior data scientists and analytics interns',
      'Collaborated with engineering team to optimize model deployment processes'],
     'relevance_score': 10},
    {'title': 'Data Analyst',
     'company': 'Retail Analytics
… (salida recortada)
Lección del curso «Mistral Cookbook» de Mistral AI, publicado con licencia MIT. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a Mistral AI. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios