Spaces:
Sleeping
Sleeping
Update src/agents/cv_agents.py
Browse files- src/agents/cv_agents.py +85 -25
src/agents/cv_agents.py
CHANGED
|
@@ -94,6 +94,7 @@ class CVAgentOrchestrator:
|
|
| 94 |
return self._parse_sections_result(result)
|
| 95 |
|
| 96 |
def extract_all_sections(self, sections: Dict[str, str]) -> Dict[str, Any]:
|
|
|
|
| 97 |
tasks = self._create_extraction_tasks(sections)
|
| 98 |
|
| 99 |
crew = Crew(
|
|
@@ -108,60 +109,104 @@ class CVAgentOrchestrator:
|
|
| 108 |
],
|
| 109 |
tasks=tasks,
|
| 110 |
process=Process.sequential,
|
| 111 |
-
verbose=
|
| 112 |
telemetry=False
|
| 113 |
)
|
| 114 |
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
return self._parse_final_result(result)
|
| 117 |
|
| 118 |
def _create_extraction_tasks(self, sections: Dict[str, str]) -> List[Task]:
|
| 119 |
contact_task = Task(
|
| 120 |
-
description=
|
|
|
|
|
|
|
|
|
|
| 121 |
expected_output='{"nom": "...", "email": "...", "numero_de_telephone": "...", "localisation": "..."}',
|
| 122 |
agent=self.contact_extractor
|
| 123 |
)
|
| 124 |
|
| 125 |
skills_task = Task(
|
| 126 |
-
description=
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
)
|
| 131 |
|
| 132 |
experience_task = Task(
|
| 133 |
-
description=
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
|
|
|
|
|
|
| 137 |
)
|
| 138 |
|
| 139 |
project_task = Task(
|
| 140 |
-
description=
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
)
|
| 145 |
|
| 146 |
education_task = Task(
|
| 147 |
-
description=
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
|
|
|
|
|
|
| 151 |
)
|
| 152 |
|
| 153 |
reconversion_task = Task(
|
| 154 |
-
description=
|
| 155 |
-
|
|
|
|
|
|
|
|
|
|
| 156 |
agent=self.reconversion_detector,
|
| 157 |
-
context=[
|
| 158 |
)
|
| 159 |
|
| 160 |
profile_task = Task(
|
| 161 |
-
description=
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
agent=self.profile_builder,
|
| 164 |
-
context=[reconversion_task]
|
| 165 |
)
|
| 166 |
|
| 167 |
return [contact_task, skills_task, experience_task, project_task, education_task, reconversion_task, profile_task]
|
|
@@ -177,6 +222,21 @@ class CVAgentOrchestrator:
|
|
| 177 |
result_str = parts[1].strip()
|
| 178 |
|
| 179 |
parsed = json.loads(result_str)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
return parsed
|
| 181 |
|
| 182 |
def _parse_final_result(self, result) -> Dict[str, Any]:
|
|
|
|
| 94 |
return self._parse_sections_result(result)
|
| 95 |
|
| 96 |
def extract_all_sections(self, sections: Dict[str, str]) -> Dict[str, Any]:
|
| 97 |
+
# Créer les tâches avec les sections en input
|
| 98 |
tasks = self._create_extraction_tasks(sections)
|
| 99 |
|
| 100 |
crew = Crew(
|
|
|
|
| 109 |
],
|
| 110 |
tasks=tasks,
|
| 111 |
process=Process.sequential,
|
| 112 |
+
verbose=True, # Activer pour debug
|
| 113 |
telemetry=False
|
| 114 |
)
|
| 115 |
|
| 116 |
+
# Passer les sections comme inputs
|
| 117 |
+
inputs = {
|
| 118 |
+
"contact": sections.get("contact", ""),
|
| 119 |
+
"experiences": sections.get("experiences", ""),
|
| 120 |
+
"projects": sections.get("projects", ""),
|
| 121 |
+
"education": sections.get("education", ""),
|
| 122 |
+
"skills": sections.get("skills", ""),
|
| 123 |
+
"other": sections.get("other", "")
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
logger.info(f"Starting crew with inputs: {list(inputs.keys())}")
|
| 127 |
+
result = crew.kickoff(inputs=inputs)
|
| 128 |
+
logger.info(f"Crew completed. Raw result: {result.raw if hasattr(result, 'raw') else str(result)[:200]}...")
|
| 129 |
+
|
| 130 |
return self._parse_final_result(result)
|
| 131 |
|
| 132 |
def _create_extraction_tasks(self, sections: Dict[str, str]) -> List[Task]:
|
| 133 |
contact_task = Task(
|
| 134 |
+
description=(
|
| 135 |
+
"Voici la section contact du CV : {contact}\n"
|
| 136 |
+
"Extraire précisément le nom, email, téléphone et localisation du candidat."
|
| 137 |
+
),
|
| 138 |
expected_output='{"nom": "...", "email": "...", "numero_de_telephone": "...", "localisation": "..."}',
|
| 139 |
agent=self.contact_extractor
|
| 140 |
)
|
| 141 |
|
| 142 |
skills_task = Task(
|
| 143 |
+
description=(
|
| 144 |
+
"Voici les sections pertinentes du CV :\n"
|
| 145 |
+
"Expériences: {experiences}\n"
|
| 146 |
+
"Projets: {projects}\n"
|
| 147 |
+
"Compétences: {skills}\n"
|
| 148 |
+
"Extraire toutes les compétences techniques (hard skills) et comportementales (soft skills) mentionnées."
|
| 149 |
+
),
|
| 150 |
+
expected_output='{"hard_skills": ["compétence1", "compétence2"], "soft_skills": ["compétence1", "compétence2"]}',
|
| 151 |
+
agent=self.skills_extractor
|
| 152 |
)
|
| 153 |
|
| 154 |
experience_task = Task(
|
| 155 |
+
description=(
|
| 156 |
+
"Voici la section expériences du CV : {experiences}\n"
|
| 157 |
+
"Extraire toutes les expériences professionnelles avec poste, entreprise, dates et responsabilités."
|
| 158 |
+
),
|
| 159 |
+
expected_output='[{"Poste": "titre", "Entreprise": "nom", "start_date": "date", "end_date": "date", "responsabilités": ["resp1", "resp2"]}]',
|
| 160 |
+
agent=self.experience_extractor
|
| 161 |
)
|
| 162 |
|
| 163 |
project_task = Task(
|
| 164 |
+
description=(
|
| 165 |
+
"Voici les sections projets et expériences du CV :\n"
|
| 166 |
+
"Projets: {projects}\n"
|
| 167 |
+
"Expériences: {experiences}\n"
|
| 168 |
+
"Identifier et extraire les projets professionnels et personnels distincts des responsabilités générales."
|
| 169 |
+
),
|
| 170 |
+
expected_output='{"professional": [{"title": "titre", "role": "rôle", "technologies": ["tech1"], "outcomes": ["résultat1"]}], "personal": []}',
|
| 171 |
+
agent=self.project_extractor
|
| 172 |
)
|
| 173 |
|
| 174 |
education_task = Task(
|
| 175 |
+
description=(
|
| 176 |
+
"Voici la section formations du CV : {education}\n"
|
| 177 |
+
"Extraire toutes les formations, diplômes et certifications avec institution et dates."
|
| 178 |
+
),
|
| 179 |
+
expected_output='[{"degree": "diplôme", "institution": "établissement", "start_date": "date", "end_date": "date"}]',
|
| 180 |
+
agent=self.education_extractor
|
| 181 |
)
|
| 182 |
|
| 183 |
reconversion_task = Task(
|
| 184 |
+
description=(
|
| 185 |
+
"En analysant les expériences extraites précédemment, déterminer si le candidat est en reconversion professionnelle. "
|
| 186 |
+
"Chercher des changements de secteur, de type de poste ou des transitions significatives."
|
| 187 |
+
),
|
| 188 |
+
expected_output='{"reconversion_analysis": {"is_reconversion": true, "analysis": "Explication détaillée..."}}',
|
| 189 |
agent=self.reconversion_detector,
|
| 190 |
+
context=[experience_task]
|
| 191 |
)
|
| 192 |
|
| 193 |
profile_task = Task(
|
| 194 |
+
description=(
|
| 195 |
+
"Assembler toutes les informations extraites des tâches précédentes en un profil candidat complet. "
|
| 196 |
+
"Créer un JSON valide avec une clé 'candidat' contenant toutes les sections."
|
| 197 |
+
),
|
| 198 |
+
expected_output=(
|
| 199 |
+
'{"candidat": {'
|
| 200 |
+
'"informations_personnelles": {...}, '
|
| 201 |
+
'"compétences": {...}, '
|
| 202 |
+
'"expériences": [...], '
|
| 203 |
+
'"projets": {...}, '
|
| 204 |
+
'"formations": [...], '
|
| 205 |
+
'"reconversion": {...}'
|
| 206 |
+
'}}'
|
| 207 |
+
),
|
| 208 |
agent=self.profile_builder,
|
| 209 |
+
context=[contact_task, skills_task, experience_task, project_task, education_task, reconversion_task]
|
| 210 |
)
|
| 211 |
|
| 212 |
return [contact_task, skills_task, experience_task, project_task, education_task, reconversion_task, profile_task]
|
|
|
|
| 222 |
result_str = parts[1].strip()
|
| 223 |
|
| 224 |
parsed = json.loads(result_str)
|
| 225 |
+
|
| 226 |
+
# Assurer que toutes les sections nécessaires existent
|
| 227 |
+
default_sections = {
|
| 228 |
+
"contact": "",
|
| 229 |
+
"experiences": "",
|
| 230 |
+
"projects": "",
|
| 231 |
+
"education": "",
|
| 232 |
+
"skills": "",
|
| 233 |
+
"other": ""
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
for key in default_sections:
|
| 237 |
+
if key not in parsed:
|
| 238 |
+
parsed[key] = default_sections[key]
|
| 239 |
+
|
| 240 |
return parsed
|
| 241 |
|
| 242 |
def _parse_final_result(self, result) -> Dict[str, Any]:
|