QuentinL52 commited on
Commit
2c35e00
·
verified ·
1 Parent(s): 9cc910c

Update src/rag_handler.py

Browse files
Files changed (1) hide show
  1. src/rag_handler.py +174 -57
src/rag_handler.py CHANGED
@@ -1,85 +1,202 @@
1
  import os
 
 
2
  from langchain_community.document_loaders import DirectoryLoader, TextLoader
3
  from langchain_community.vectorstores import FAISS
4
- from langchain_huggingface import HuggingFaceEmbeddings
5
  from langchain_text_splitters import RecursiveCharacterTextSplitter
6
 
7
- embeddings_model = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
8
- VECTOR_STORE_PATH = "/app/vector_store"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  class RAGHandler:
11
- def __init__(self, knowledge_base_path: str = "/app/knowledge_base"):
12
  """
13
  Initialise le RAG Handler.
14
 
15
  Args:
16
  knowledge_base_path (str): Le chemin vers le dossier contenant les documents de connaissances (.md).
 
17
  """
18
- self.embeddings = embeddings_model
19
- self.vector_store = self._load_or_create_vector_store(knowledge_base_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  def _load_documents(self, path: str) -> list:
22
  """Charge les documents depuis un chemin de répertoire spécifié."""
23
- loader = DirectoryLoader(
24
- path,
25
- glob="**/*.md",
26
- loader_cls=TextLoader,
27
- loader_kwargs={"encoding": "utf-8"}
28
- )
29
- print(f"Chargement des documents depuis : {path}")
30
- return loader.load()
 
 
 
 
 
 
 
 
 
 
31
 
32
- def _create_vector_store(self, knowledge_base_path: str) -> FAISS | None:
33
  """Crée et sauvegarde la base de données vectorielle à partir des documents."""
34
- documents = self._load_documents(knowledge_base_path)
35
- if not documents:
36
- print("Aucun document trouvé pour créer le vector store.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  return None
38
-
39
- print(f"{len(documents)} documents chargés. Création des vecteurs...")
40
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
41
- texts = text_splitter.split_documents(documents)
42
- vector_store = FAISS.from_documents(texts, self.embeddings)
43
- os.makedirs(VECTOR_STORE_PATH, exist_ok=True)
44
- vector_store.save_local(VECTOR_STORE_PATH)
45
- print(f"Vector store créé et sauvegardé dans : {VECTOR_STORE_PATH}")
46
- return vector_store
47
 
48
- def _load_or_create_vector_store(self, knowledge_base_path: str) -> FAISS | None:
49
  """Charge le vector store s'il existe, sinon le crée."""
50
- if os.path.exists(os.path.join(VECTOR_STORE_PATH, "index.faiss")):
51
- print(f"Chargement du vector store existant depuis : {VECTOR_STORE_PATH}")
52
- return FAISS.load_local(
53
- VECTOR_STORE_PATH,
54
- embeddings=self.embeddings,
55
- allow_dangerous_deserialization=True
56
- )
57
- else:
58
- print("Aucun vector store trouvé. Création d'un nouveau...")
59
- return self._create_vector_store(knowledge_base_path)
 
 
 
 
 
 
60
 
61
  def get_relevant_feedback(self, query: str, k: int = 1) -> list[str]:
62
  """Recherche les k conseils les plus pertinents pour une requête."""
 
 
 
 
63
  if not self.vector_store:
64
- return []
65
- results = self.vector_store.similarity_search(query, k=k)
66
- return [doc.page_content for doc in results]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  if __name__ == '__main__':
69
- print("Initialisation du RAG Handler en mode test...")
70
- handler = RAGHandler(knowledge_base_path="/app/knowledge_base")
71
- if handler.vector_store and hasattr(handler.vector_store, 'index'):
72
- print(f"Vector store chargé avec {handler.vector_store.index.ntotal} vecteurs.")
73
-
74
- test_query = "gestion du stress"
75
- feedback = handler.get_relevant_feedback(test_query, k=2)
76
-
77
- print(f"\nTest de recherche pour : '{test_query}'")
78
- if feedback:
79
- print("Feedback pertinent trouvé :")
80
- for f in feedback:
81
- print(f"- {f[:150]}...") # Affiche un aperçu
82
- else:
83
- print("Aucun feedback pertinent trouvé pour cette requête.")
84
  else:
85
- print("Le RAG Handler n'a pas pu être initialisé ou le vector store est vide.")
 
1
  import os
2
+ import logging
3
+ from typing import Optional
4
  from langchain_community.document_loaders import DirectoryLoader, TextLoader
5
  from langchain_community.vectorstores import FAISS
 
6
  from langchain_text_splitters import RecursiveCharacterTextSplitter
7
 
8
+ logger = logging.getLogger(__name__)
9
+
10
+ # Variables globales pour l'initialisation différée
11
+ _embeddings_model = None
12
+ _rag_handler_instance = None
13
+
14
+ # Utiliser /tmp qui est toujours writable dans les conteneurs
15
+ VECTOR_STORE_PATH = "/tmp/vector_store"
16
+
17
+ def get_embeddings_model():
18
+ """Obtient le modèle d'embeddings avec initialisation différée."""
19
+ global _embeddings_model
20
+ if _embeddings_model is None:
21
+ try:
22
+ from langchain_huggingface import HuggingFaceEmbeddings
23
+ logger.info("Initialisation du modèle d'embeddings...")
24
+ _embeddings_model = HuggingFaceEmbeddings(
25
+ model_name='sentence-transformers/all-MiniLM-L6-v2',
26
+ model_kwargs={'device': 'cpu'},
27
+ encode_kwargs={'normalize_embeddings': True}
28
+ )
29
+ logger.info("✅ Modèle d'embeddings initialisé avec succès")
30
+ except Exception as e:
31
+ logger.error(f"❌ Erreur lors de l'initialisation du modèle d'embeddings: {e}")
32
+ _embeddings_model = None
33
+ return _embeddings_model
34
 
35
  class RAGHandler:
36
+ def __init__(self, knowledge_base_path: str = "/app/knowledge_base", lazy_init: bool = True):
37
  """
38
  Initialise le RAG Handler.
39
 
40
  Args:
41
  knowledge_base_path (str): Le chemin vers le dossier contenant les documents de connaissances (.md).
42
+ lazy_init (bool): Si True, initialise le vector store seulement lors de la première utilisation.
43
  """
44
+ self.knowledge_base_path = knowledge_base_path
45
+ self.embeddings = None
46
+ self.vector_store = None
47
+ self._initialized = False
48
+
49
+ # S'assurer que le répertoire /tmp/vector_store existe
50
+ os.makedirs(VECTOR_STORE_PATH, exist_ok=True)
51
+
52
+ if not lazy_init:
53
+ self._initialize()
54
+
55
+ def _initialize(self):
56
+ """Initialise le RAG Handler de manière différée."""
57
+ if self._initialized:
58
+ return
59
+
60
+ try:
61
+ logger.info("Initialisation du RAG Handler...")
62
+ self.embeddings = get_embeddings_model()
63
+
64
+ if self.embeddings is None:
65
+ logger.error("Impossible d'initialiser les embeddings")
66
+ return
67
+
68
+ self.vector_store = self._load_or_create_vector_store(self.knowledge_base_path)
69
+ self._initialized = True
70
+ logger.info("✅ RAG Handler initialisé avec succès")
71
+
72
+ except Exception as e:
73
+ logger.error(f"❌ Erreur lors de l'initialisation du RAG Handler: {e}")
74
+ self._initialized = False
75
 
76
  def _load_documents(self, path: str) -> list:
77
  """Charge les documents depuis un chemin de répertoire spécifié."""
78
+ try:
79
+ if not os.path.exists(path):
80
+ logger.warning(f"Répertoire {path} non trouvé")
81
+ return []
82
+
83
+ loader = DirectoryLoader(
84
+ path,
85
+ glob="**/*.md",
86
+ loader_cls=TextLoader,
87
+ loader_kwargs={"encoding": "utf-8"}
88
+ )
89
+ logger.info(f"Chargement des documents depuis : {path}")
90
+ documents = loader.load()
91
+ logger.info(f"✅ {len(documents)} documents chargés")
92
+ return documents
93
+ except Exception as e:
94
+ logger.error(f"❌ Erreur lors du chargement des documents: {e}")
95
+ return []
96
 
97
+ def _create_vector_store(self, knowledge_base_path: str) -> Optional[FAISS]:
98
  """Crée et sauvegarde la base de données vectorielle à partir des documents."""
99
+ try:
100
+ documents = self._load_documents(knowledge_base_path)
101
+ if not documents:
102
+ logger.warning("Aucun document trouvé - création d'un vector store vide")
103
+ # Créer un document fictif pour initialiser le vector store
104
+ from langchain.schema import Document
105
+ dummy_doc = Document(
106
+ page_content="Document de test pour initialiser le vector store",
107
+ metadata={"source": "dummy"}
108
+ )
109
+ documents = [dummy_doc]
110
+
111
+ logger.info(f"{len(documents)} documents chargés. Création des vecteurs...")
112
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
113
+ texts = text_splitter.split_documents(documents)
114
+
115
+ vector_store = FAISS.from_documents(texts, self.embeddings)
116
+
117
+ # Sauvegarder dans /tmp
118
+ try:
119
+ vector_store.save_local(VECTOR_STORE_PATH)
120
+ logger.info(f"✅ Vector store créé et sauvegardé dans : {VECTOR_STORE_PATH}")
121
+ except Exception as save_error:
122
+ logger.warning(f"⚠️ Impossible de sauvegarder le vector store: {save_error}")
123
+ # Continuer sans sauvegarde - le vector store reste en mémoire
124
+
125
+ return vector_store
126
+
127
+ except Exception as e:
128
+ logger.error(f"❌ Erreur lors de la création du vector store: {e}")
129
  return None
 
 
 
 
 
 
 
 
 
130
 
131
+ def _load_or_create_vector_store(self, knowledge_base_path: str) -> Optional[FAISS]:
132
  """Charge le vector store s'il existe, sinon le crée."""
133
+ try:
134
+ index_path = os.path.join(VECTOR_STORE_PATH, "index.faiss")
135
+ if os.path.exists(index_path):
136
+ logger.info(f"Chargement du vector store existant depuis : {VECTOR_STORE_PATH}")
137
+ return FAISS.load_local(
138
+ VECTOR_STORE_PATH,
139
+ embeddings=self.embeddings,
140
+ allow_dangerous_deserialization=True
141
+ )
142
+ else:
143
+ logger.info("Aucun vector store trouvé. Création d'un nouveau...")
144
+ return self._create_vector_store(knowledge_base_path)
145
+ except Exception as e:
146
+ logger.error(f"❌ Erreur lors du chargement/création du vector store: {e}")
147
+ # En cas d'échec total, retourner None plutôt que planter
148
+ return None
149
 
150
  def get_relevant_feedback(self, query: str, k: int = 1) -> list[str]:
151
  """Recherche les k conseils les plus pertinents pour une requête."""
152
+ # Initialisation différée si nécessaire
153
+ if not self._initialized:
154
+ self._initialize()
155
+
156
  if not self.vector_store:
157
+ logger.warning("Vector store non disponible - retour de conseils génériques")
158
+ return [
159
+ "Préparez vos réponses aux questions comportementales",
160
+ "Montrez votre motivation pour le poste",
161
+ "Donnez des exemples concrets de vos réalisations"
162
+ ]
163
+
164
+ try:
165
+ results = self.vector_store.similarity_search(query, k=k)
166
+ feedback = [doc.page_content for doc in results if doc.page_content.strip()]
167
+
168
+ # Fallback si pas de résultats pertinents
169
+ if not feedback:
170
+ return ["Conseil général: Préparez-vous bien pour les entretiens futurs."]
171
+
172
+ return feedback
173
+ except Exception as e:
174
+ logger.error(f"❌ Erreur lors de la recherche: {e}")
175
+ return ["Conseil général: Travaillez sur vos compétences de communication."]
176
+
177
+ # Fonction pour obtenir une instance partagée
178
+ def get_rag_handler() -> Optional[RAGHandler]:
179
+ """Obtient une instance partagée du RAG Handler."""
180
+ global _rag_handler_instance
181
+ if _rag_handler_instance is None:
182
+ try:
183
+ _rag_handler_instance = RAGHandler(lazy_init=True)
184
+ except Exception as e:
185
+ logger.error(f"❌ Erreur lors de la création du RAG Handler: {e}")
186
+ _rag_handler_instance = None
187
+ return _rag_handler_instance
188
 
189
  if __name__ == '__main__':
190
+ print("Test du RAG Handler avec /tmp vector store...")
191
+ handler = RAGHandler(knowledge_base_path="/app/knowledge_base", lazy_init=False)
192
+
193
+ test_query = "gestion du stress"
194
+ feedback = handler.get_relevant_feedback(test_query, k=2)
195
+
196
+ print(f"\nTest de recherche pour : '{test_query}'")
197
+ if feedback:
198
+ print("Feedback trouvé :")
199
+ for f in feedback:
200
+ print(f"- {f[:150]}...")
 
 
 
 
201
  else:
202
+ print("Aucun feedback trouvé.")