interview_agents_api / src /services /interview_service.py
QuentinL52's picture
Update src/services/interview_service.py
960c990 verified
raw
history blame
7.29 kB
# 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}