채용용 멀티 에이전트 워크플로우
채용용 멀티 에이전트 워크플로우 (Multi Agent Workflow For Recruitment)
이 쿡북은 전문 AI 에이전트들이 협력해서 채용 절차를 간소화해주는 자동화 시스템을 소개해요. 후보자 평가를 개선하고, 시간과 자원을 절약하며, 전반적인 채용 성과를 높이는 데 목표를 두고 있죠.
출처: 문서
본문
소개 (Introduction)
**채용용 멀티 에이전트 워크플로우(Multi Agent Workflow For Recruitment)**는 전문 AI 에이전트들이 조화롭게 협력해서 채용 절차를 간소화하는 자동화 시스템이에요. 후보자 평가를 개선하고, 시간과 자원을 절약하며, 전반적인 채용 성과를 높이는 데 도움을 줘요.
문제점 (The Problem)
오늘날의 채용 환경은 세 가지 치명적인 과제에 직면해 있어요.
- 압도적인 물량 (Overwhelming Volume): 채용 담당자는 많은 수의 지원서를 효율적으로 처리하기 어려워하고, 종종 우수한 후보자를 놓쳐요.
- 수동 처리의 비효율 (Manual Inefficiency): 전통적인 이력서 스크리닝은 시간이 많이 들고 일관성이 없으며, 편향에 취약해요.
- 좋지 않은 후보자 경험 (Poor Candidate Experience): 느린 응답과 분산된 커뮤니케이션은 고용주 브랜드를 손상시키고 최고 인재를 잃게 해요.
왜 중요한가 (Why This Matters)
비효율적인 채용은 다음을 통해 비즈니스 성과에 직접 영향을 미쳐요.
- 성과 저하 (Reduced Performance): 우수한 후보자를 놓치면 최선이 아닌 고용과 팀 성과 저하로 이어져요.
- 비즈니스 지연 (Business Delays): 채용 주기가 길어지면 중요 프로젝트와 이니셔티브가 미뤄져요.
- 더 높은 비용 (Higher Costs): 비효율적인 프로세스와 장기간의 공석은 채용 비용을 증가시켜요.
우리의 솔루션 (Our Solution)
채용용 멀티 에이전트 워크플로우는 전문 AI 에이전트들의 조정된 시스템을 통해 이런 과제를 해결해요.
- DocumentAgent: Mistral의 고급 OCR을 사용해 이력서와 직무 설명에서 텍스트를 지능적으로 추출·처리해요.
- JobAnalysisAgent: 직무 설명을 분석해서 필요한 기술, 경험, 자격을 식별해요.
- ResumeAnalysisAgent: 이력서를 파싱해서 핵심 역량이 담긴 구조화된 후보자 프로필을 만들어요.
- MatchingAgent: 키워드 매칭을 넘어선 미묘한 이해로 직무 요구사항에 대한 후보자를 평가해요.
- EmailCommunicationAgent: 적격 후보자에게 개인화된 이메일 커뮤니케이션을 생성하고 면접을 일정 조정해요.
- CoordinatorAgent: 에이전트 간의 전체 워크플로우를 원활하게 작동하도록 오케스트레이션해요.
이 솔루션은 언어 이해에 Mistral LLM, 일관된 데이터 추출을 위한 구조화된 출력 메커니즘, 문서 파싱에 Mistral OCR을 사용해요.
예시: 데이터 사이언티스트 채용 (Example: Data Scientist Hiring)
채용용 멀티 에이전트 워크플로우가 실제로 어떻게 작동하는지 현실적인 예시로 살펴볼게요.
HireFive는 머신러닝 전문성을 가진 시니어 데이터 사이언티스트를 채용해야 해요. 직무 설명에는 3년 이상의 경험, Python과 딥러닝 프레임워크 숙련도, 정량 분야 석사 학위 같은 요구사항이 명시되어 있어요. 지원 이력서 풀에서 워크플로우는 자동으로:
- 직무 설명에서 구조화된 요구사항을 추출해서 핵심 기술을 식별해요.
- 모든 이력서를 파싱해서 기술, 경험, 교육이 담긴 표준화된 프로필을 만들어요.
- 각 후보자를 평가해서 "Technical Skills: 32/40", "Experience: 25/30" 같은 점수를 매겨요.
- 70점 임계값을 넘는 후보자를 식별해요.
- 이 후보자들에게 일정 링크가 포함된 개인화된 면접 초대장을 자동으로 보내요.
전체 프로세스는 몇 분 만에 완료되어, HireFive의 채용 담당자에게 우수한 후보자의 순위 목록을 제공하면서 수 시간의 수동 이력서 스크리닝을 없애줘요.
솔루션 아키텍처 (Solution Architecture)

설치 (Installation)
Python Output
!pip install mistralai
임포트 (Imports)
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
API 키 설정 (Setup API Keys)
os.environ['MISTRAL_API_KEY'] = "YOUR MISTRALAI API KEY" # Get it from https://console.mistral.ai/api-keys
Mistral API 클라이언트 초기화 (Initialize Mistral API Client)
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
데이터 다운로드 (Download Data)
여기서 데모에 필요한 데이터를 다운로드해요.
- Job Descrition.
- Candidate Resumes.
직무 설명과 후보자 이력서 다운로드를 위한 헬퍼 함수 (Helper functions to download Job description and candidate resumes)
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}")
직무 설명 다운로드 (Download Job Description)
Python Output
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)
후보자 이력서 다운로드 (Download Candidate Resumes)
Python Output
download_resumes(
url = "https://api.github.com/repos/mistralai/cookbook/contents/mistral/agents/non_framework/recruitment_agent/resumes",
local_dir="resumes"
)
Pydantic 모델 정의 (Define Pydantic Models)
Pydantic 모델은 에이전트 간의 구조화된 데이터 검증을 제공해서, 후보자 프로필, 직무 요구사항, 평가 점수의 일관된 형식을 보장하면서 Mistral LLM의 파싱 기능과의 원활한 통합을 가능하게 해요. 사용하는 Pydantic 모델들은 다음과 같아요.
- Skill: 후보자의 기술적/소프트 스킬을 숙련도 수준과 경력 연수와 함께 나타내요.
- Education: 학위, 전공, 교육기관, 성과 지표를 포함한 학력 사항을 담아요.
- Experience: 역할 세부사항, 기간, 사용 기술, 주요 성과와 함께 전문 경력을 추적해요.
- ContactDetails: 이름, 이메일, 선택적 커뮤니케이션 채널을 포함한 후보자 연락처 정보를 저장해요.
- JobRequirements: 필수/선호 기술, 경력 수준, 학력 자격을 포함한 직무 요구사항을 정의해요.
- CandidateProfile: 연락처, 기술, 학력, 경력을 포함한 후보자의 전체 전문 프로필을 통합해요.
- SkillMatch: 신뢰도 점수와 함께 직무 요구사항과 후보자 역량 간의 개별 기술 일치도를 평가해요.
- CandidateScore: 핵심 평가 영역에 걸친 포괄적인 점수와 총점 계산, 강점/공백 식별을 제공해요.
- CandidateResult: 파일 정보와 추출된 후보자 데이터, 평가 점수를 연결해서 최종 순위와 선별에 사용해요.
구조화된 추출을 위한 Pydantic 모델 (Pydantic Models for structured extraction.)
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")
베이스 에이전트 클래스 (Base Agent Class)
Agent 클래스는 모든 전문 에이전트의 기반이 되며, 채용 워크플로우에서 에이전트 간 처리와 커뮤니케이션을 위한 표준화된 인터페이스를 제공해요.
각 에이전트는 정체성 관리와 커뮤니케이션 기능을 상속하면서 공통의 process() 메서드를 구현해요.
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: 문서 추출과 OCR 처리 (Handles document extraction and OCR)
DocumentAgent는 Mistral의 OCR 기능을 사용해 다양한 파일에서 구조화된 텍스트를 추출해서 문서 처리를 담당해요. 복잡한 이력서 PDF와 직무 설명을 텍스트로 변환해서, 전체 채용 워크플로우의 초기 데이터 게이트웨이 역할을 해요.
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: 직무 요구사항 추출과 분석 (Handles job requirement extraction and analysis)
JobAnalysisAgent는 Mistral LLM을 사용해 일반 텍스트 직무 설명에서 구조화된 직무 요구사항을 추출해요. 비정형 채용 공고를 후보자 매칭에 필요한 필수 기술, 경력 수준, 학력 자격을 담은 정돈된 데이터 모델로 변환해요.
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: 이력서 파싱과 프로필 추출 (Handles resume parsing and profile extraction)
ResumeAnalysisAgent는 Mistral LLM의 파싱 기능을 사용해 원시 이력서 텍스트를 구조화된 후보자 프로필로 변환해요. 연락처, 기술, 학력, 전문 경험을 포함한 핵심 정보를 추출·정리해서 일관된 평가를 위한 표준화된 데이터 구조로 만들죠.
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: 직무 요구사항에 대한 후보자 적합도 평가 (Evaluates candidate fit against job requirements)
MatchingAgent는 후보자 프로필을 직무 요구사항과 평가해서 기술, 경험, 학력, 추가 자격에 걸친 포괄적인 점수를 생성해요. Mistral LLM을 사용해 단순 키워드 매칭을 넘어 후보자 속성의 품질과 관련성을 평가하고, 신뢰도 지표와 식별된 강점·공백을 포함한 상세한 평가를 만들어요.
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: 이메일 생성과 발송 처리 (Handles email generation and sending)
EmailCommunicationAgent는 후보자에게 개인화된 이메일 커뮤니케이션을 생성하고 SMTP 통합을 통해 발송해요. 후보자 자격과 일정 정보를 바탕으로 상황에 맞는 메시지를 만들어, 채용 워크플로우에서 후보자 참여의 핵심 최종 단계를 관리해요.
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: 워크플로우 관리와 에이전트 간 조정 (Manages the workflow and coordinates between agents)
CoordinatorAgent는 모든 전문 에이전트 간의 커뮤니케이션과 데이터 흐름을 관리해서 전체 채용 워크플로우를 오케스트레이션해요. 직무 설명으로 프로세스를 시작하고, 이력서를 배포하며, 평가 결과를 수집하고, 임계값 기반 필터링을 적용하고, 후보자 커뮤니케이션을 트리거해서, 멀티 에이전트 채용 시스템의 원활한 실행을 보장하는 중앙 지능 역할을 해요.
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
워크플로우 실행 (Run the workflow)
채용용 멀티 에이전트 워크플로우를 실행하려면 다음만 하면 돼요.
- 직무 설명, 이력서 디렉터리, 출력 결과의 파일 경로를 설정해요.
- 이메일 자격 증명과 Calendly 일정 링크를 설정해요.
- Mistral 클라이언트로 CoordinatorAgent를 초기화해요.
- 발신자 자격 증명으로 EmailCommunicationAgent를 구성해요.
- 원하는 임계값 점수로 워크플로우를 실행해요.
경로 정의 (Define paths)
jd_file_path = "job_description.pdf"
resume_dir = "resumes/"
output_path = "candidate_results.json"
Gmail 앱 비밀번호 설정 (Gmail App Password Setup)
채용용 멀티 에이전트 워크플로우에서 Gmail로 이메일 기능을 사용하려면 앱 비밀번호(app password)를 만들어야 해요.
2단계 인증(2-Step Verification) 활성화:
- Google 계정 → 보안(Security)으로 이동
- "Google에 로그인"에서 2단계 인증(2-Step Verification) → 시작하기(Get started) 선택
앱 비밀번호 생성:
- Google 계정 → 보안(Security)으로 이동
- "Google에 로그인"에서 앱 비밀번호(App passwords) 선택
- 앱은 "Mail", 기기는 "Other" 선택하고 이름을 "Recruitment Workflow"로 지정
- "생성(Generate)" 클릭
- Google이 16자리 비밀번호(4자리 그룹 4개)를 표시
이 앱 비밀번호를 워크플로우 설정에 사용해요.
sender_email = "[email protected]"
app_password = "abcd efgh ijkl mnop" # Your generated app password
이 앱 비밀번호는 2FA를 우회해서, 실제 Google 비밀번호를 코드에 저장하지 않고 워크플로우가 Gmail 계정을 통해 안전하게 이메일을 보낼 수 있게 해줘요.
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"
코디네이터 에이전트 초기화 (Initialize coordinator agent)
coordinator = CoordinatorAgent(client)
이메일 자격 증명으로 커뮤니케이션 에이전트 설정 (Set up communication agent with email credentials)
coordinator.set_communication_agent(sender_email, app_password)
채용 워크플로우 실행 (Execute hiring workflow)
참고: 간단하게 하기 위해 후보자 이력서 5개를 고려했어요.
Python
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
)
각 후보자의 추출된 결과를 확인할 수 있어요.
Python
results