Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

347 lines
14KB

  1. using ToolsServices;
  2. using Services.ReActAgent;
  3. using System.Collections.ObjectModel;
  4. using System.Text;
  5. using System.Text.Json;
  6. using System.Text.Json.Serialization;
  7. namespace Services
  8. {
  9. public static class RechercheCVService
  10. {
  11. #region Variables
  12. private static readonly string NomFichierRechercheCV = FichiersInternesService.ListeRechercheCV;// "listeRechercheCV.json";
  13. private static readonly string NomFichierParametres = FichiersInternesService.ParamsRechercheCV;
  14. #endregion
  15. #region Méthodes publiques
  16. public static (string, string) LoadParametres()
  17. {
  18. LoggerService.LogInfo("RechercheCVService.LoadParametres");
  19. //ParametresOllamaService SelectedItem = new();
  20. try
  21. {
  22. string FicheMission="";
  23. string PathCV="";
  24. if (File.Exists(NomFichierParametres))
  25. {
  26. string[] lignes = File.ReadAllLines(NomFichierParametres);
  27. if (lignes.Length > 0)
  28. FicheMission = lignes[0];
  29. if (lignes.Length > 1)
  30. PathCV = lignes[1];
  31. }
  32. return (FicheMission, PathCV);
  33. }
  34. catch
  35. {
  36. LoggerService.LogError($"Erreur lors du chargement des paramètres depuis {NomFichierParametres}");
  37. return ("","");
  38. }
  39. }
  40. public static bool SaveParametres(string FicheMission, string PathCV)
  41. {
  42. LoggerService.LogInfo("RechercheCVService.SaveParametres");
  43. try
  44. {
  45. StringBuilder sb = new();
  46. sb.AppendLine(FicheMission);
  47. sb.AppendLine(PathCV);
  48. File.WriteAllText(NomFichierParametres, sb.ToString());
  49. return true;
  50. }
  51. catch
  52. {
  53. LoggerService.LogError($"Erreur lors de la sauvegarde des paramètres dans {NomFichierParametres}");
  54. return false;
  55. }
  56. }
  57. public static async Task<List<ReponseRechercheCV>?> RechercherCV(string ficheMission, string dossierCV, bool isGenererXML)
  58. {
  59. LoggerService.LogInfo("RechercheCVService.RechercheCV");
  60. var reActRagAgent = new ReActAgent.ReActAgent();
  61. var modeleIA = ReActAgent.ReActAgent.GetModeleIA(ModelsUseCases.TypeUseCase.AnalyseCVMission);
  62. LoggerService.LogInfo($"Analyse des CV");
  63. LoggerService.LogInfo($"Fiche mission : {ficheMission}");
  64. LoggerService.LogInfo($"Dossier CV : {dossierCV}");
  65. LoggerService.LogInfo($"Extraire les CV en XML : {isGenererXML}");
  66. LoggerService.LogInfo($"Modèle utilisé : {modeleIA}");
  67. if (!Directory.Exists(dossierCV))
  68. {
  69. LoggerService.LogWarning($"Le dossier {dossierCV} n'existe pas.");
  70. return null;
  71. }
  72. if (!File.Exists(ficheMission))
  73. {
  74. LoggerService.LogWarning($"Le fichier {ficheMission} n'existe pas.");
  75. return null;
  76. }
  77. var fichiers = Directory
  78. .EnumerateFiles(dossierCV, "*.*", SearchOption.AllDirectories)
  79. .Where(f => f.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)
  80. || f.EndsWith(".docx", StringComparison.OrdinalIgnoreCase)
  81. || f.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
  82. .ToArray();
  83. if (fichiers.Length == 0)
  84. {
  85. LoggerService.LogWarning($"Aucun fichier trouvé dans le dossier spécifié : {dossierCV}");
  86. return null;
  87. }
  88. string messagePromptInjection1 = "";
  89. bool isPromptInjection = false;
  90. var ficheMissionText = reActRagAgent.CleanUserInput(FilesService.ExtractText(ficheMission), out isPromptInjection);
  91. if(isPromptInjection)
  92. {
  93. var msg = "Attention, le texte de la fiche de mission contient des éléments suspects";
  94. messagePromptInjection1 = $"{msg}.\n";
  95. LoggerService.LogWarning(msg);
  96. }
  97. List<ReponseRechercheCV> retours = new();
  98. foreach (var fichier in fichiers)
  99. {
  100. var bIngest = await RAGService.IngestDocument(Domain.CV, true, false, FilesService.ExtractText(fichier), fichier);
  101. }
  102. foreach (var fichier in fichiers)
  103. {
  104. try
  105. {
  106. #region Analyse du CV
  107. ReponseRechercheCV retour = new();
  108. string messagePromptInjection2 = "";
  109. var texteCV = reActRagAgent.CleanUserInput(FilesService.ExtractText(fichier), out isPromptInjection);
  110. if(isPromptInjection)
  111. {
  112. var msg = "Attention, le texte de la fiche de mission contient des éléments suspects";
  113. messagePromptInjection2 = $"{msg}.\n";
  114. retour.PresenceSuspecte = true;
  115. LoggerService.LogWarning($"{msg}: {fichier}");
  116. }
  117. /*
  118. var prompt = $@"
  119. Tu es un expert en recrutement. Ton rôle est d'évaluer la compatibilité entre une offre d'emploi et un CV.
  120. Analyse comparative :
  121. 1. **Points forts** : liste les éléments du CV qui correspondent bien aux exigences de l'offre (compétences, expériences, formations, etc.).
  122. 2. **Points manquants ou faibles** : identifie les éléments de l'offre qui sont absents ou peu développés dans le CV.
  123. 3. **Note de compatibilité** : donne une note globale de correspondance entre le CV et l'offre, sur 10, avec une explication.
  124. Voici les documents à comparer :
  125. ---
  126. **Offre d’emploi** :
  127. {ficheMissionText}
  128. ---
  129. **CV du candidat** :
  130. {texteCV}
  131. ---
  132. Réponds en suivant la structure suivante :
  133. **Points forts :**
  134. - …
  135. **Points manquants ou faibles :**
  136. - …
  137. **Note de compatibilité (sur 10) :**
  138. X/10 – Explication : …
  139. ";
  140. */
  141. var prompt = PromptService.GetPrompt(PromptService.ePrompt.RechercheCVService_RechercheCV_Analyse, ficheMissionText, texteCV);
  142. LoggerService.LogInfo($"Analyse du CV : {fichier}");
  143. var (reponse,m1) = await reActRagAgent.AppelerLLMAsync(ModelsUseCases.TypeUseCase.AnalyseCVMission, true, prompt,"Analyse de CV");
  144. var reponseComplete = $"Pour le CV {fichier} :\n{reponse}\n";
  145. //var reponseClean = reponse.Replace("\\n", "\n").Replace("\\r", "\r");
  146. var reponseCompleteClean = reponseComplete.Replace("\\n", "\n").Replace("\\r", "\r");
  147. retour.FichierCV = fichier;
  148. retour.Avis = messagePromptInjection1 + messagePromptInjection2 + reponseCompleteClean;
  149. // Note : On peut ajouter une logique pour extraire la note de la réponse si nécessaire
  150. if (reponse.Contains("Note de compatibilité"))
  151. {
  152. var noteMatch = System.Text.RegularExpressions.Regex.Match(reponse, @"(\d+)/10");
  153. if (noteMatch.Success && int.TryParse(noteMatch.Groups[1].Value, out int note))
  154. {
  155. retour.Note = note;
  156. }
  157. }
  158. #endregion
  159. #region Renseigner le modèle IA
  160. retour.ModeleIA = modeleIA;
  161. #endregion
  162. #region Génération XML
  163. if (isGenererXML)
  164. {
  165. /*
  166. var promptXML = $@"
  167. Tu es un assistant spécialisé dans l'extraction d'informations depuis des CV.
  168. Je vais te fournir le contenu brut d’un CV (texte brut sans mise en forme).
  169. À partir de ce contenu, génère un document structuré au format XML en respectant la structure suivante :
  170. <CV>
  171. <Identite>
  172. <Nom></Nom>
  173. <Prenom></Prenom>
  174. <Email></Email>
  175. <Telephone></Telephone>
  176. <Adresse></Adresse>
  177. <DateDeNaissance></DateDeNaissance>
  178. <Nationalite></Nationalite>
  179. </Identite>
  180. <Profil></Profil>
  181. <Competences>
  182. <Competence nom=""..."" niveau=""..."" />
  183. ...
  184. </Competences>
  185. <Langues>
  186. <Langue nom=""..."" niveau=""..."" />
  187. ...
  188. </Langues>
  189. <Experiences>
  190. <Experience>
  191. <Poste></Poste>
  192. <Entreprise></Entreprise>
  193. <Lieu></Lieu>
  194. <DateDebut></DateDebut>
  195. <DateFin></DateFin>
  196. <Description></Description>
  197. </Experience>
  198. ...
  199. </Experiences>
  200. <Formations>
  201. <Formation>
  202. <Diplome></Diplome>
  203. <Etablissement></Etablissement>
  204. <DateDebut></DateDebut>
  205. <DateFin></DateFin>
  206. </Formation>
  207. ...
  208. </Formations>
  209. <Certifications>
  210. <Certification>
  211. <Nom></Nom>
  212. <Organisme></Organisme>
  213. <Date></Date>
  214. </Certification>
  215. ...
  216. </Certifications>
  217. </CV>
  218. Si une information est absente, laisse le champ vide. Ne fais aucun commentaire, retourne uniquement le XML.
  219. N'oublie aucune expérience, aucune formation, aucune langue, ni aucune certification.
  220. Formate le tout proprement au format XML. N'ajoute pas de texte explicatif. Commence directement par <document>.
  221. Voici un contenu PDF extrait :
  222. {texteCV}
  223. ";
  224. */
  225. var promptXML = PromptService.GetPrompt(PromptService.ePrompt.RechercheCVService_RechercheCV_Generate, texteCV);
  226. var (reponseXML,m2) = await reActRagAgent.AppelerLLMAsync(ModelsUseCases.TypeUseCase.AnalyseCVMission, true, promptXML,"CV en XML");
  227. var reponseCompleteCleanXML = reponseXML.Replace("\\n", "\n").Replace("\\r", "\r");
  228. retour.VersionXML = reponseCompleteCleanXML;
  229. }
  230. #endregion
  231. #region Vectorisation du CV
  232. await RAGService.IngestDocument(Domain.CV,true, false, texteCV, fichier);
  233. //reActRagAgent.IngestDocument(texteCV, fichier);
  234. #endregion
  235. retours.Add(retour);
  236. }
  237. catch (Exception ex)
  238. {
  239. LoggerService.LogError($"Erreur lors de l'extraction du texte de {fichier} : {ex.Message}");
  240. }
  241. }
  242. LoggerService.LogInfo($"Analyse des CV terminée");
  243. return retours;
  244. }
  245. public static async Task SauvegarderRechercheAsync(ObservableCollection<ReponseRechercheCV> ListeItems)
  246. {
  247. LoggerService.LogInfo("RechercheCVService.SauvegarderRechercheAsync");
  248. if (ListeItems == null)
  249. return;
  250. var options = new JsonSerializerOptions
  251. {
  252. WriteIndented = true,
  253. // Ignore les propriétés non sérialisables comme UniqueId si nécessaire
  254. IgnoreReadOnlyProperties = false,
  255. DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
  256. };
  257. using FileStream fs = File.Create(NomFichierRechercheCV);
  258. await JsonSerializer.SerializeAsync(fs, ListeItems, options);
  259. }
  260. public static async Task<ObservableCollection<ReponseRechercheCV>> ChargerCVDepuisJsonAsync(ObservableCollection<ReponseRechercheCV> ListeItems)
  261. {
  262. LoggerService.LogInfo("RechercheCVService.ChargerCVDepuisJsonAsync");
  263. if (!File.Exists(NomFichierRechercheCV))
  264. return new ObservableCollection<ReponseRechercheCV>();
  265. using FileStream fs = File.OpenRead(NomFichierRechercheCV);
  266. var items = await JsonSerializer.DeserializeAsync<ObservableCollection<ReponseRechercheCV>>(fs);
  267. if (items != null)
  268. {
  269. ListeItems = items;
  270. }
  271. return ListeItems;
  272. }
  273. public static async Task<string> SeekByKeyWord(string RechercheString)
  274. {
  275. LoggerService.LogInfo("RechercheCVService.SeekByKeyWord");
  276. return await RAGService.Search(Domain.CV, RechercheString);
  277. }
  278. #endregion
  279. #region Méthodes privées
  280. #endregion
  281. }
  282. }