Florian.Moret commited on
Commit
f02db94
·
1 Parent(s): 0276406

rangement du repo

Browse files
app.py CHANGED
@@ -23,10 +23,10 @@ model_embedding = "mistral-embed"
23
  chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
24
  chunk_overlap = 15
25
  # 📌 Définition des chemins de stockage
26
- index_path = "faiss_index.bin"
27
- chunks_path = "chunked_docs.pkl"
28
- metadata_path = "metadata.pkl"
29
- embeddings_path = "embeddings.npy"
30
 
31
  print("🔄 Chargement des données existantes...")
32
  index = faiss.read_index(index_path)
 
23
  chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
24
  chunk_overlap = 15
25
  # 📌 Définition des chemins de stockage
26
+ index_path = "files/faiss_index.bin"
27
+ chunks_path = "files/chunked_docs.pkl"
28
+ metadata_path = "files/metadata.pkl"
29
+ embeddings_path = "files/embeddings.npy"
30
 
31
  print("🔄 Chargement des données existantes...")
32
  index = faiss.read_index(index_path)
.env_sample → files/.env_sample RENAMED
@@ -1,7 +1,7 @@
1
- REQUESTS_CA_BUNDLE=C:\ProgramData\Netskope\STAgent\data\nscacert.pem
2
- SSL_CERT_FILE=C:\ProgramData\Netskope\STAgent\data\nscacert.pem
3
-
4
- MISTRAL_API_KEY_static=
5
- LLAMA_API_KEY_static=
6
- ANTHROPIC_API_KEY_static=
7
  OPENAI_API_KEY_static=
 
1
+ REQUESTS_CA_BUNDLE=C:\ProgramData\Netskope\STAgent\data\nscacert.pem
2
+ SSL_CERT_FILE=C:\ProgramData\Netskope\STAgent\data\nscacert.pem
3
+
4
+ MISTRAL_API_KEY_static=
5
+ LLAMA_API_KEY_static=
6
+ ANTHROPIC_API_KEY_static=
7
  OPENAI_API_KEY_static=
chunked_docs.pkl → files/chunked_docs.pkl RENAMED
File without changes
chunked_docs_openai.pkl → files/chunked_docs_openai.pkl RENAMED
File without changes
embeddings.npy → files/embeddings.npy RENAMED
File without changes
embeddings_openai.npy → files/embeddings_openai.npy RENAMED
File without changes
faiss_index.bin → files/faiss_index.bin RENAMED
File without changes
faiss_index_openai.bin → files/faiss_index_openai.bin RENAMED
File without changes
metadata.pkl → files/metadata.pkl RENAMED
File without changes
metadata_openai.pkl → files/metadata_openai.pkl RENAMED
File without changes
RAG_Mistral.py → scripts/RAG_Mistral.py RENAMED
@@ -1,137 +1,137 @@
1
- import os
2
- import numpy as np
3
- import fitz # PyMuPDF pour extraction PDF
4
- import faiss
5
- import pickle
6
- import matplotlib.pyplot as plt
7
- from concurrent.futures import ThreadPoolExecutor
8
- from mistralai import Mistral
9
- from sklearn.manifold import TSNE
10
- from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
11
- from dotenv import load_dotenv
12
-
13
- # Charger les variables d'environnement
14
- load_dotenv()
15
- MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY_static')
16
-
17
- # 📌 Initialisation du client Mistral
18
- client = Mistral(api_key=MISTRAL_API_KEY)
19
- model_embedding = "mistral-embed"
20
- model_chat = "ministral-8b-latest"
21
- temperature = 0.1 # Réduction de la température pour privilégier la RAG
22
-
23
- # 📌 Paramètres de segmentation
24
- chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
25
- chunk_overlap = 15
26
-
27
- # 📌 Définition des chemins de stockage
28
- index_path = "faiss_index.bin"
29
- chunks_path = "chunked_docs.pkl"
30
- metadata_path = "metadata.pkl"
31
- embeddings_path = "embeddings.npy"
32
-
33
- # 📌 Vérification et chargement des données
34
- if os.path.exists(index_path) and os.path.exists(chunks_path) and os.path.exists(metadata_path) and os.path.exists(embeddings_path):
35
- print("🔄 Chargement des données existantes...")
36
- index = faiss.read_index(index_path)
37
- with open(chunks_path, "rb") as f:
38
- chunked_docs = pickle.load(f)
39
- with open(metadata_path, "rb") as f:
40
- metadata_list = pickle.load(f)
41
- embeddings = np.load(embeddings_path)
42
- print("✅ Index, chunks, embeddings et métadonnées chargés avec succès !")
43
- else:
44
- print("⚡ Création et stockage d'un nouvel index FAISS...")
45
-
46
- # 📌 Extraction des documents et métadonnées
47
- def extract_and_chunk_pdfs(pdf_folder):
48
- documents = SimpleDirectoryReader(pdf_folder, recursive=True).load_data()
49
- chunked_docs, metadata_list = [], []
50
- for doc in documents:
51
- doc_text = doc.text
52
- file_name = doc.metadata.get("file_name", "Inconnu")
53
- title = doc.metadata.get("title") or os.path.splitext(file_name)[0] # Utilisation du nom de fichier comme fallback
54
- doc_metadata = {"source": file_name, "title": title}
55
- for i in range(0, len(doc_text), chunk_size):
56
- chunk = doc_text[i:i + chunk_size]
57
- chunked_docs.append({"text": chunk, "metadata": doc_metadata})
58
- metadata_list.append(doc_metadata)
59
- return chunked_docs, metadata_list
60
-
61
- pdf_folder = 'C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/Docs_pdf/'
62
- chunked_docs, metadata_list = extract_and_chunk_pdfs(pdf_folder)
63
-
64
- # 📌 Génération des embeddings en parallèle
65
- def get_embeddings_in_batches(text_chunks, batch_size=5):
66
- embeddings = []
67
-
68
- def process_batch(batch):
69
- response = client.embeddings.create(model=model_embedding, inputs=[chunk["text"] for chunk in batch])
70
- return [data.embedding for data in response.data]
71
-
72
- with ThreadPoolExecutor(max_workers=5) as executor:
73
- future_batches = [executor.submit(process_batch, text_chunks[i:i+batch_size]) for i in range(0, len(text_chunks), batch_size)]
74
- for future in future_batches:
75
- embeddings.extend(future.result())
76
-
77
- return np.array(embeddings).astype('float32')
78
-
79
- embeddings = get_embeddings_in_batches(chunked_docs)
80
-
81
- # 📌 Création et stockage de l'index FAISS
82
- dimension = embeddings.shape[1]
83
- index = faiss.IndexFlatL2(dimension)
84
- index.add(embeddings)
85
- faiss.write_index(index, index_path)
86
-
87
- # 📌 Sauvegarde des données
88
- with open(chunks_path, "wb") as f:
89
- pickle.dump(chunked_docs, f)
90
- with open(metadata_path, "wb") as f:
91
- pickle.dump(metadata_list, f)
92
- np.save(embeddings_path, embeddings) # Sauvegarde des embeddings
93
- print("✅ Index, chunks, embeddings et métadonnées sauvegardés !")
94
-
95
- # 📌 Récupération des chunks les plus pertinents
96
- def retrieve_relevant_chunks(question, k=5):
97
- question_embedding_response = client.embeddings.create(
98
- model=model_embedding,
99
- inputs=[question],
100
- )
101
- question_embedding = np.array(question_embedding_response.data[0].embedding).astype('float32').reshape(1, -1)
102
- distances, indices = index.search(question_embedding, k)
103
- if len(indices[0]) == 0:
104
- print("⚠️ Avertissement : Aucun chunk pertinent trouvé, réponse possible moins précise.")
105
- return [], []
106
- return [chunked_docs[i] for i in indices[0]]
107
-
108
- # 📌 Génération de réponse avec MistralAI
109
- def generate_response(context, question, sources):
110
- chunk_references = [f"[{i+1}]" for i in range(len(sources))]
111
- chunk_texts = "\n\n".join([f"{chunk_references[i]} (Source: {src['metadata']['source']}) :\n{src['text']}" for i, src in enumerate(sources)])
112
- messages = [
113
- {"role": "system", "content": f"Voici les informations extraites des documents :\n{chunk_texts}\n\nUtilise ces informations pour répondre."},
114
- {"role": "user", "content": question}
115
- ]
116
- response = client.chat.complete(model=model_chat, messages=messages, temperature=temperature)
117
- return response.choices[0].message.content + " " + "".join(chunk_references), chunk_texts
118
-
119
- # 📌 Exécuter une requête utilisateur
120
- user_question = "Quels sont les besoins en protéines des poulets de chair en phase de croissance ?"
121
- relevant_chunks = retrieve_relevant_chunks(user_question)
122
- context = "\n".join([chunk["text"] for chunk in relevant_chunks])
123
- answer, citations = generate_response(context, user_question, relevant_chunks)
124
-
125
- # 📊 Affichage de la réponse avec sources
126
- print("\n🔹 Réponse Mistral :")
127
- print(answer)
128
- print("\n📌 **Chunks utilisés :**")
129
- print(citations)
130
-
131
- # 💾 Sauvegarde des résultats
132
- with open("mistral_response_types.txt", "w", encoding="utf-8") as f:
133
- f.write(f"Question : {user_question}\n")
134
- f.write(f"Réponse :\n{answer}\n")
135
- f.write(f"{citations}\n")
136
-
137
- print("\n✅ Réponse enregistrée avec les chunks exacts et références dans 'mistral_response_types.txt'")
 
1
+ import os
2
+ import numpy as np
3
+ import fitz # PyMuPDF pour extraction PDF
4
+ import faiss
5
+ import pickle
6
+ import matplotlib.pyplot as plt
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from mistralai import Mistral
9
+ from sklearn.manifold import TSNE
10
+ from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
11
+ from dotenv import load_dotenv
12
+
13
+ # Charger les variables d'environnement
14
+ load_dotenv()
15
+ MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY_static')
16
+
17
+ # 📌 Initialisation du client Mistral
18
+ client = Mistral(api_key=MISTRAL_API_KEY)
19
+ model_embedding = "mistral-embed"
20
+ model_chat = "ministral-8b-latest"
21
+ temperature = 0.1 # Réduction de la température pour privilégier la RAG
22
+
23
+ # 📌 Paramètres de segmentation
24
+ chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
25
+ chunk_overlap = 15
26
+
27
+ # 📌 Définition des chemins de stockage
28
+ index_path = "faiss_index.bin"
29
+ chunks_path = "chunked_docs.pkl"
30
+ metadata_path = "metadata.pkl"
31
+ embeddings_path = "embeddings.npy"
32
+
33
+ # 📌 Vérification et chargement des données
34
+ if os.path.exists(index_path) and os.path.exists(chunks_path) and os.path.exists(metadata_path) and os.path.exists(embeddings_path):
35
+ print("🔄 Chargement des données existantes...")
36
+ index = faiss.read_index(index_path)
37
+ with open(chunks_path, "rb") as f:
38
+ chunked_docs = pickle.load(f)
39
+ with open(metadata_path, "rb") as f:
40
+ metadata_list = pickle.load(f)
41
+ embeddings = np.load(embeddings_path)
42
+ print("✅ Index, chunks, embeddings et métadonnées chargés avec succès !")
43
+ else:
44
+ print("⚡ Création et stockage d'un nouvel index FAISS...")
45
+
46
+ # 📌 Extraction des documents et métadonnées
47
+ def extract_and_chunk_pdfs(pdf_folder):
48
+ documents = SimpleDirectoryReader(pdf_folder, recursive=True).load_data()
49
+ chunked_docs, metadata_list = [], []
50
+ for doc in documents:
51
+ doc_text = doc.text
52
+ file_name = doc.metadata.get("file_name", "Inconnu")
53
+ title = doc.metadata.get("title") or os.path.splitext(file_name)[0] # Utilisation du nom de fichier comme fallback
54
+ doc_metadata = {"source": file_name, "title": title}
55
+ for i in range(0, len(doc_text), chunk_size):
56
+ chunk = doc_text[i:i + chunk_size]
57
+ chunked_docs.append({"text": chunk, "metadata": doc_metadata})
58
+ metadata_list.append(doc_metadata)
59
+ return chunked_docs, metadata_list
60
+
61
+ pdf_folder = 'C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/Docs_pdf/'
62
+ chunked_docs, metadata_list = extract_and_chunk_pdfs(pdf_folder)
63
+
64
+ # 📌 Génération des embeddings en parallèle
65
+ def get_embeddings_in_batches(text_chunks, batch_size=5):
66
+ embeddings = []
67
+
68
+ def process_batch(batch):
69
+ response = client.embeddings.create(model=model_embedding, inputs=[chunk["text"] for chunk in batch])
70
+ return [data.embedding for data in response.data]
71
+
72
+ with ThreadPoolExecutor(max_workers=5) as executor:
73
+ future_batches = [executor.submit(process_batch, text_chunks[i:i+batch_size]) for i in range(0, len(text_chunks), batch_size)]
74
+ for future in future_batches:
75
+ embeddings.extend(future.result())
76
+
77
+ return np.array(embeddings).astype('float32')
78
+
79
+ embeddings = get_embeddings_in_batches(chunked_docs)
80
+
81
+ # 📌 Création et stockage de l'index FAISS
82
+ dimension = embeddings.shape[1]
83
+ index = faiss.IndexFlatL2(dimension)
84
+ index.add(embeddings)
85
+ faiss.write_index(index, index_path)
86
+
87
+ # 📌 Sauvegarde des données
88
+ with open(chunks_path, "wb") as f:
89
+ pickle.dump(chunked_docs, f)
90
+ with open(metadata_path, "wb") as f:
91
+ pickle.dump(metadata_list, f)
92
+ np.save(embeddings_path, embeddings) # Sauvegarde des embeddings
93
+ print("✅ Index, chunks, embeddings et métadonnées sauvegardés !")
94
+
95
+ # 📌 Récupération des chunks les plus pertinents
96
+ def retrieve_relevant_chunks(question, k=5):
97
+ question_embedding_response = client.embeddings.create(
98
+ model=model_embedding,
99
+ inputs=[question],
100
+ )
101
+ question_embedding = np.array(question_embedding_response.data[0].embedding).astype('float32').reshape(1, -1)
102
+ distances, indices = index.search(question_embedding, k)
103
+ if len(indices[0]) == 0:
104
+ print("⚠️ Avertissement : Aucun chunk pertinent trouvé, réponse possible moins précise.")
105
+ return [], []
106
+ return [chunked_docs[i] for i in indices[0]]
107
+
108
+ # 📌 Génération de réponse avec MistralAI
109
+ def generate_response(context, question, sources):
110
+ chunk_references = [f"[{i+1}]" for i in range(len(sources))]
111
+ chunk_texts = "\n\n".join([f"{chunk_references[i]} (Source: {src['metadata']['source']}) :\n{src['text']}" for i, src in enumerate(sources)])
112
+ messages = [
113
+ {"role": "system", "content": f"Voici les informations extraites des documents :\n{chunk_texts}\n\nUtilise ces informations pour répondre."},
114
+ {"role": "user", "content": question}
115
+ ]
116
+ response = client.chat.complete(model=model_chat, messages=messages, temperature=temperature)
117
+ return response.choices[0].message.content + " " + "".join(chunk_references), chunk_texts
118
+
119
+ # 📌 Exécuter une requête utilisateur
120
+ user_question = "Quels sont les besoins en protéines des poulets de chair en phase de croissance ?"
121
+ relevant_chunks = retrieve_relevant_chunks(user_question)
122
+ context = "\n".join([chunk["text"] for chunk in relevant_chunks])
123
+ answer, citations = generate_response(context, user_question, relevant_chunks)
124
+
125
+ # 📊 Affichage de la réponse avec sources
126
+ print("\n🔹 Réponse Mistral :")
127
+ print(answer)
128
+ print("\n📌 **Chunks utilisés :**")
129
+ print(citations)
130
+
131
+ # 💾 Sauvegarde des résultats
132
+ with open("mistral_response_types.txt", "w", encoding="utf-8") as f:
133
+ f.write(f"Question : {user_question}\n")
134
+ f.write(f"Réponse :\n{answer}\n")
135
+ f.write(f"{citations}\n")
136
+
137
+ print("\n✅ Réponse enregistrée avec les chunks exacts et références dans 'mistral_response_types.txt'")
RAG_OpenAI.py → scripts/RAG_OpenAI.py RENAMED
@@ -1,165 +1,165 @@
1
- import os
2
- import numpy as np
3
- import fitz # PyMuPDF pour extraction PDF
4
- import faiss
5
- import pickle
6
- import matplotlib.pyplot as plt
7
- from concurrent.futures import ThreadPoolExecutor
8
- from openai import OpenAI
9
- from sklearn.manifold import TSNE
10
- from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
11
- from dotenv import load_dotenv
12
- import seaborn as sns
13
-
14
- # Charger les variables d'environnement
15
- load_dotenv()
16
- OPENAI_API_KEY = os.getenv('OPENAI_API_KEY_static')
17
-
18
- # 📌 Initialisation du client OpenAI
19
- client = OpenAI(api_key=OPENAI_API_KEY)
20
- model_embedding = "text-embedding-ada-002"
21
- model_chat = "gpt-4-turbo"
22
- temperature = 0.1 # Réduction de la température pour privilégier la RAG
23
-
24
- # 📌 Paramètres de segmentation
25
- chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
26
- chunk_overlap = 15
27
-
28
- # 📌 Définition des chemins de stockage
29
- index_path = "faiss_index_openai.bin"
30
- chunks_path = "chunked_docs_openai.pkl"
31
- metadata_path = "metadata_openai.pkl"
32
- embeddings_path = "embeddings_openai.npy"
33
-
34
- # 📌 Vérification et chargement des données
35
- if os.path.exists(index_path) and os.path.exists(chunks_path) and os.path.exists(metadata_path) and os.path.exists(embeddings_path):
36
- print("🔄 Chargement des données existantes...")
37
- index = faiss.read_index(index_path)
38
- with open(chunks_path, "rb") as f:
39
- chunked_docs = pickle.load(f)
40
- with open(metadata_path, "rb") as f:
41
- metadata_list = pickle.load(f)
42
- embeddings = np.load(embeddings_path)
43
- print("✅ Index, chunks, embeddings et métadonnées chargés avec succès !")
44
- else:
45
- print("⚡ Création et stockage d'un nouvel index FAISS...")
46
-
47
- # 📌 Extraction des documents et métadonnées
48
- def extract_and_chunk_pdfs(pdf_folder):
49
- documents = SimpleDirectoryReader(pdf_folder, recursive=True).load_data()
50
- chunked_docs, metadata_list = [], []
51
- for doc in documents:
52
- doc_text = doc.text
53
- file_name = doc.metadata.get("file_name", "Inconnu")
54
- title = doc.metadata.get("title") or os.path.splitext(file_name)[0]
55
- doc_metadata = {"source": file_name, "title": title}
56
- for i in range(0, len(doc_text), chunk_size):
57
- chunk = doc_text[i:i + chunk_size]
58
- chunked_docs.append({"text": chunk, "metadata": doc_metadata})
59
- metadata_list.append(doc_metadata)
60
- return chunked_docs, metadata_list
61
-
62
- pdf_folder = 'C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/Docs_pdf/'
63
- chunked_docs, metadata_list = extract_and_chunk_pdfs(pdf_folder)
64
-
65
- # 📌 Génération des embeddings en parallèle
66
- def get_embeddings_in_batches(text_chunks, batch_size=5):
67
- embeddings = []
68
-
69
- def process_batch(batch):
70
- response = client.embeddings.create(input=[chunk["text"] for chunk in batch], model=model_embedding)
71
- return [data.embedding for data in response.data]
72
-
73
- with ThreadPoolExecutor(max_workers=5) as executor:
74
- future_batches = [executor.submit(process_batch, text_chunks[i:i+batch_size]) for i in range(0, len(text_chunks), batch_size)]
75
- for future in future_batches:
76
- embeddings.extend(future.result())
77
-
78
- return np.array(embeddings).astype('float32')
79
-
80
- embeddings = get_embeddings_in_batches(chunked_docs)
81
-
82
- # 📌 Création et stockage de l'index FAISS
83
- dimension = embeddings.shape[1]
84
- index = faiss.IndexFlatL2(dimension)
85
- index.add(embeddings)
86
- faiss.write_index(index, index_path)
87
-
88
- # 📌 Sauvegarde des données
89
- with open(chunks_path, "wb") as f:
90
- pickle.dump(chunked_docs, f)
91
- with open(metadata_path, "wb") as f:
92
- pickle.dump(metadata_list, f)
93
- np.save(embeddings_path, embeddings)
94
- print("✅ Index, chunks, embeddings et métadonnées sauvegardés !")
95
-
96
- # 📌 Récupération des chunks les plus pertinents
97
- def retrieve_relevant_chunks(question, k=5):
98
- question_embedding_response = client.embeddings.create(
99
- input=[question],
100
- model=model_embedding
101
- )
102
- question_embedding = np.array(question_embedding_response.data[0].embedding).astype('float32').reshape(1, -1)
103
- distances, indices = index.search(question_embedding, k)
104
- if len(indices[0]) == 0:
105
- print("⚠️ Aucun chunk pertinent trouvé.")
106
- return []
107
- return [chunked_docs[i] for i in indices[0]]
108
-
109
- # 📌 Génération de réponse avec OpenAI
110
- def generate_response(context, question, sources):
111
- chunk_references = [f"[{i+1}]" for i in range(len(sources))]
112
- chunk_texts = "\n\n".join([f"{chunk_references[i]} (Source: {src['metadata']['source']}) :\n{src['text']}" for i, src in enumerate(sources)])
113
- messages = [
114
- {"role": "system", "content": f"Voici les informations extraites des documents :\n{chunk_texts}\n\nUtilise ces informations pour répondre."},
115
- {"role": "user", "content": question}
116
- ]
117
- response = client.chat.completions.create(
118
- model=model_chat,
119
- messages=messages,
120
- temperature=temperature
121
- )
122
- return response.choices[0].message.content + " " + "".join(chunk_references), chunk_texts
123
-
124
- # 📌 Exécuter une requête utilisateur
125
- user_question = "Quels sont les besoins en protéines des poulets de chair en phase de croissance ?"
126
- relevant_chunks = retrieve_relevant_chunks(user_question)
127
- context = "\n".join([chunk["text"] for chunk in relevant_chunks])
128
- answer, citations = generate_response(context, user_question, relevant_chunks)
129
-
130
- # 💾 Sauvegarde des résultats
131
- with open("openai_response.txt", "w", encoding="utf-8") as f:
132
- f.write(f"Question : {user_question}\n")
133
- f.write(f"Réponse :\n{answer}\n")
134
- f.write(f"{citations}\n")
135
-
136
- print("\n✅ Réponse enregistrée dans 'openai_response.txt'")
137
-
138
- # 📊 Visualisation des embeddings avec t-SNE :
139
-
140
- # Nos documents ont un vocabulaire et un contenu très similaires, donc les embeddings générés par Mistral sont proches les uns des autres.
141
- # t-SNE réduit la dimensionnalité, mais si les embeddings de base sont très proches, la distinction entre eux est moins visible en 2D.
142
- # Un clustering plus clair apparaîtrait si nos documents couvraient des thématiques très variées (ex: alimentation, maladies, croissance de différents animaux).
143
-
144
- # Génération de la réduction de dimension avec t-SNE
145
- tsne = TSNE(n_components=2, perplexity=min(30, max(2, embeddings.shape[0] - 1)), random_state=42)
146
- embeddings_2d = tsne.fit_transform(embeddings)
147
-
148
- # Récupération des étiquettes des sources
149
- source_labels = [chunk['metadata']['source'] for chunk in chunked_docs]
150
-
151
- # Création du graphique
152
- plt.figure(figsize=(10, 8))
153
- sns.scatterplot(x=embeddings_2d[:, 0], y=embeddings_2d[:, 1], hue=source_labels, palette='tab10', alpha=0.7)
154
- plt.title('Visualisation des embeddings avec t-SNE')
155
- plt.xlabel('Dimension 1')
156
- plt.ylabel('Dimension 2')
157
- # Limiter la légende aux 10 premières sources
158
- handles, labels = plt.gca().get_legend_handles_labels()
159
- plt.legend(handles[:10], labels[:10], title="Fichiers sources (Top 10)", loc="upper right", bbox_to_anchor=(1.2, 1))
160
-
161
-
162
- # Sauvegarde du graphique en PNG
163
- output_path = "C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/embeddings_visualization.png"
164
- plt.savefig(output_path, dpi=300, bbox_inches='tight')
165
-
 
1
+ import os
2
+ import numpy as np
3
+ import fitz # PyMuPDF pour extraction PDF
4
+ import faiss
5
+ import pickle
6
+ import matplotlib.pyplot as plt
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from openai import OpenAI
9
+ from sklearn.manifold import TSNE
10
+ from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
11
+ from dotenv import load_dotenv
12
+ import seaborn as sns
13
+
14
+ # Charger les variables d'environnement
15
+ load_dotenv()
16
+ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY_static')
17
+
18
+ # 📌 Initialisation du client OpenAI
19
+ client = OpenAI(api_key=OPENAI_API_KEY)
20
+ model_embedding = "text-embedding-ada-002"
21
+ model_chat = "gpt-4-turbo"
22
+ temperature = 0.1 # Réduction de la température pour privilégier la RAG
23
+
24
+ # 📌 Paramètres de segmentation
25
+ chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
26
+ chunk_overlap = 15
27
+
28
+ # 📌 Définition des chemins de stockage
29
+ index_path = "faiss_index_openai.bin"
30
+ chunks_path = "chunked_docs_openai.pkl"
31
+ metadata_path = "metadata_openai.pkl"
32
+ embeddings_path = "embeddings_openai.npy"
33
+
34
+ # 📌 Vérification et chargement des données
35
+ if os.path.exists(index_path) and os.path.exists(chunks_path) and os.path.exists(metadata_path) and os.path.exists(embeddings_path):
36
+ print("🔄 Chargement des données existantes...")
37
+ index = faiss.read_index(index_path)
38
+ with open(chunks_path, "rb") as f:
39
+ chunked_docs = pickle.load(f)
40
+ with open(metadata_path, "rb") as f:
41
+ metadata_list = pickle.load(f)
42
+ embeddings = np.load(embeddings_path)
43
+ print("✅ Index, chunks, embeddings et métadonnées chargés avec succès !")
44
+ else:
45
+ print("⚡ Création et stockage d'un nouvel index FAISS...")
46
+
47
+ # 📌 Extraction des documents et métadonnées
48
+ def extract_and_chunk_pdfs(pdf_folder):
49
+ documents = SimpleDirectoryReader(pdf_folder, recursive=True).load_data()
50
+ chunked_docs, metadata_list = [], []
51
+ for doc in documents:
52
+ doc_text = doc.text
53
+ file_name = doc.metadata.get("file_name", "Inconnu")
54
+ title = doc.metadata.get("title") or os.path.splitext(file_name)[0]
55
+ doc_metadata = {"source": file_name, "title": title}
56
+ for i in range(0, len(doc_text), chunk_size):
57
+ chunk = doc_text[i:i + chunk_size]
58
+ chunked_docs.append({"text": chunk, "metadata": doc_metadata})
59
+ metadata_list.append(doc_metadata)
60
+ return chunked_docs, metadata_list
61
+
62
+ pdf_folder = 'C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/Docs_pdf/'
63
+ chunked_docs, metadata_list = extract_and_chunk_pdfs(pdf_folder)
64
+
65
+ # 📌 Génération des embeddings en parallèle
66
+ def get_embeddings_in_batches(text_chunks, batch_size=5):
67
+ embeddings = []
68
+
69
+ def process_batch(batch):
70
+ response = client.embeddings.create(input=[chunk["text"] for chunk in batch], model=model_embedding)
71
+ return [data.embedding for data in response.data]
72
+
73
+ with ThreadPoolExecutor(max_workers=5) as executor:
74
+ future_batches = [executor.submit(process_batch, text_chunks[i:i+batch_size]) for i in range(0, len(text_chunks), batch_size)]
75
+ for future in future_batches:
76
+ embeddings.extend(future.result())
77
+
78
+ return np.array(embeddings).astype('float32')
79
+
80
+ embeddings = get_embeddings_in_batches(chunked_docs)
81
+
82
+ # 📌 Création et stockage de l'index FAISS
83
+ dimension = embeddings.shape[1]
84
+ index = faiss.IndexFlatL2(dimension)
85
+ index.add(embeddings)
86
+ faiss.write_index(index, index_path)
87
+
88
+ # 📌 Sauvegarde des données
89
+ with open(chunks_path, "wb") as f:
90
+ pickle.dump(chunked_docs, f)
91
+ with open(metadata_path, "wb") as f:
92
+ pickle.dump(metadata_list, f)
93
+ np.save(embeddings_path, embeddings)
94
+ print("✅ Index, chunks, embeddings et métadonnées sauvegardés !")
95
+
96
+ # 📌 Récupération des chunks les plus pertinents
97
+ def retrieve_relevant_chunks(question, k=5):
98
+ question_embedding_response = client.embeddings.create(
99
+ input=[question],
100
+ model=model_embedding
101
+ )
102
+ question_embedding = np.array(question_embedding_response.data[0].embedding).astype('float32').reshape(1, -1)
103
+ distances, indices = index.search(question_embedding, k)
104
+ if len(indices[0]) == 0:
105
+ print("⚠️ Aucun chunk pertinent trouvé.")
106
+ return []
107
+ return [chunked_docs[i] for i in indices[0]]
108
+
109
+ # 📌 Génération de réponse avec OpenAI
110
+ def generate_response(context, question, sources):
111
+ chunk_references = [f"[{i+1}]" for i in range(len(sources))]
112
+ chunk_texts = "\n\n".join([f"{chunk_references[i]} (Source: {src['metadata']['source']}) :\n{src['text']}" for i, src in enumerate(sources)])
113
+ messages = [
114
+ {"role": "system", "content": f"Voici les informations extraites des documents :\n{chunk_texts}\n\nUtilise ces informations pour répondre."},
115
+ {"role": "user", "content": question}
116
+ ]
117
+ response = client.chat.completions.create(
118
+ model=model_chat,
119
+ messages=messages,
120
+ temperature=temperature
121
+ )
122
+ return response.choices[0].message.content + " " + "".join(chunk_references), chunk_texts
123
+
124
+ # 📌 Exécuter une requête utilisateur
125
+ user_question = "Quels sont les besoins en protéines des poulets de chair en phase de croissance ?"
126
+ relevant_chunks = retrieve_relevant_chunks(user_question)
127
+ context = "\n".join([chunk["text"] for chunk in relevant_chunks])
128
+ answer, citations = generate_response(context, user_question, relevant_chunks)
129
+
130
+ # 💾 Sauvegarde des résultats
131
+ with open("openai_response.txt", "w", encoding="utf-8") as f:
132
+ f.write(f"Question : {user_question}\n")
133
+ f.write(f"Réponse :\n{answer}\n")
134
+ f.write(f"{citations}\n")
135
+
136
+ print("\n✅ Réponse enregistrée dans 'openai_response.txt'")
137
+
138
+ # 📊 Visualisation des embeddings avec t-SNE :
139
+
140
+ # Nos documents ont un vocabulaire et un contenu très similaires, donc les embeddings générés par Mistral sont proches les uns des autres.
141
+ # t-SNE réduit la dimensionnalité, mais si les embeddings de base sont très proches, la distinction entre eux est moins visible en 2D.
142
+ # Un clustering plus clair apparaîtrait si nos documents couvraient des thématiques très variées (ex: alimentation, maladies, croissance de différents animaux).
143
+
144
+ # Génération de la réduction de dimension avec t-SNE
145
+ tsne = TSNE(n_components=2, perplexity=min(30, max(2, embeddings.shape[0] - 1)), random_state=42)
146
+ embeddings_2d = tsne.fit_transform(embeddings)
147
+
148
+ # Récupération des étiquettes des sources
149
+ source_labels = [chunk['metadata']['source'] for chunk in chunked_docs]
150
+
151
+ # Création du graphique
152
+ plt.figure(figsize=(10, 8))
153
+ sns.scatterplot(x=embeddings_2d[:, 0], y=embeddings_2d[:, 1], hue=source_labels, palette='tab10', alpha=0.7)
154
+ plt.title('Visualisation des embeddings avec t-SNE')
155
+ plt.xlabel('Dimension 1')
156
+ plt.ylabel('Dimension 2')
157
+ # Limiter la légende aux 10 premières sources
158
+ handles, labels = plt.gca().get_legend_handles_labels()
159
+ plt.legend(handles[:10], labels[:10], title="Fichiers sources (Top 10)", loc="upper right", bbox_to_anchor=(1.2, 1))
160
+
161
+
162
+ # Sauvegarde du graphique en PNG
163
+ output_path = "C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/embeddings_visualization.png"
164
+ plt.savefig(output_path, dpi=300, bbox_inches='tight')
165
+