Spaces:
Sleeping
Sleeping
File size: 7,293 Bytes
960c990 4f1959b 960c990 4f1959b 960c990 4f1959b 960c990 4f1959b 960c990 4f1959b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# Mise à jour pour src/services/interview_service.py
import os
from typing import Dict, List, Any
from typing_extensions import TypedDict
from langchain_core.messages import AIMessage, SystemMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from src.config import read_system_prompt, format_cv
class State(TypedDict):
messages: List[add_messages]
class EnhancedInterviewService:
def __init__(self, models: Dict[str, Any]):
self.models = models
self.llm = self._get_llm()
self.system_prompt_template = self._load_prompt_template()
self.graph = self._build_graph()
def _get_llm(self) -> ChatOpenAI:
openai_api_key = os.getenv("OPENAI_API_KEY")
return ChatOpenAI(
temperature=0.6,
model_name="gpt-4o-mini",
api_key=openai_api_key
)
def _load_prompt_template(self) -> str:
return read_system_prompt('prompts/enhanced_rag_prompt.txt')
def _analyze_candidate_profile(self, cv_data: Dict[str, Any]) -> Dict[str, str]:
"""Analyse le profil candidat pour générer des insights pour l'entretien"""
# Analyse des compétences avec niveaux
skills_analysis = self._generate_skills_analysis(cv_data)
# Analyse de reconversion
reconversion_analysis = self._generate_reconversion_analysis(cv_data)
return {
"skills_analysis": skills_analysis,
"reconversion_analysis": reconversion_analysis
}
def _generate_skills_analysis(self, cv_data: Dict[str, Any]) -> str:
"""Génère une analyse textuelle des compétences pour le prompt"""
competences = cv_data.get("analyse_competences", [])
if not competences:
return "Aucune analyse de compétences disponible."
# Grouper par niveau
levels_groups = {
"expert": [],
"avance": [],
"intermediaire": [],
"debutant": []
}
for comp in competences:
level = comp.get("level", "debutant")
skill = comp.get("skill", "")
if skill and level in levels_groups:
levels_groups[level].append(skill)
# Construire l'analyse textuelle
analysis_parts = []
if levels_groups["expert"]:
analysis_parts.append(f"COMPÉTENCES EXPERTES : {', '.join(levels_groups['expert'])}")
analysis_parts.append("→ Pose des questions techniques approfondies, demande des exemples d'innovation et de leadership technique")
if levels_groups["avance"]:
analysis_parts.append(f"COMPÉTENCES AVANCÉES : {', '.join(levels_groups['avance'])}")
analysis_parts.append("→ Explore les défis complexes, l'autonomie et la résolution de problèmes")
if levels_groups["intermediaire"]:
analysis_parts.append(f"COMPÉTENCES INTERMÉDIAIRES : {', '.join(levels_groups['intermediaire'])}")
analysis_parts.append("→ Vérifie la compréhension pratique avec des exemples concrets")
if levels_groups["debutant"]:
analysis_parts.append(f"COMPÉTENCES DÉBUTANTES : {', '.join(levels_groups['debutant'])}")
analysis_parts.append("→ Teste les connaissances de base et évalue la motivation à apprendre")
return "\n".join(analysis_parts) if analysis_parts else "Aucune compétence analysée."
def _generate_reconversion_analysis(self, cv_data: Dict[str, Any]) -> str:
"""Génère une analyse de reconversion pour le prompt"""
reconversion_data = cv_data.get("reconversion", {})
if not reconversion_data:
return "Aucune analyse de reconversion disponible."
is_reconversion = reconversion_data.get("is_reconversion", False)
analysis = reconversion_data.get("analysis", "")
if not is_reconversion:
return "PROFIL CLASSIQUE : Parcours cohérent dans le domaine. Focus sur l'évolution et les projets marquants."
reconversion_guidance = [
"CANDIDAT EN RECONVERSION DÉTECTÉE :",
f"Analyse : {analysis}",
"",
"POINTS À EXPLORER OBLIGATOIREMENT :",
"1. Motivations du changement de carrière",
"2. Compétences transférables de l'expérience passée",
"3. Démarches d'apprentissage et de formation",
"4. Engagement et projets dans la nouvelle voie",
"5. Vision à long terme dans ce nouveau domaine",
"",
"APPROCHE : Valorise l'expérience passée, rassure sur la pertinence de la reconversion"
]
return "\n".join(reconversion_guidance)
def _chatbot_node(self, state: State) -> Dict[str, Any]:
messages = state["messages"]
formatted_cv_str = format_cv(self.cv_data)
# Générer les analyses du profil candidat
profile_analysis = self._analyze_candidate_profile(self.cv_data)
# Formatage du prompt système enrichi
system_prompt = self.system_prompt_template.format(
entreprise=self.job_offer.get('entreprise', 'notre entreprise'),
poste=self.job_offer.get('poste', 'ce poste'),
mission=self.job_offer.get('mission', 'Non spécifiée'),
profil_recherche=self.job_offer.get('profil_recherche', 'Non spécifié'),
competences=self.job_offer.get('competences', 'Non spécifiées'),
pole=self.job_offer.get('pole', 'Non spécifié'),
cv=formatted_cv_str,
skills_analysis=profile_analysis["skills_analysis"],
reconversion_analysis=profile_analysis["reconversion_analysis"]
)
llm_messages = [SystemMessage(content=system_prompt)] + messages
response = self.llm.invoke(llm_messages)
return {"messages": [response]}
def _build_graph(self) -> any:
graph_builder = StateGraph(State)
graph_builder.add_node("chatbot", self._chatbot_node)
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)
return graph_builder.compile()
def process_conversation(
self,
cv_document: Dict[str, Any],
job_offer: Dict[str, Any],
conversation_history: List[Dict[str, Any]],
messages: List[Dict[str, Any]]
) -> Dict[str, Any]:
if not cv_document or 'candidat' not in cv_document:
raise ValueError("Document CV invalide fourni.")
if not job_offer:
raise ValueError("Données de l'offre d'emploi non fournies.")
self.job_offer = job_offer
self.cv_data = cv_document['candidat']
self.conversation_history = conversation_history
initial_state = conversation_history + messages
result = self.graph.invoke({"messages": initial_state})
response_content = result["messages"][-1].content
return {"response": response_content} |