Spaces:
Sleeping
Sleeping
File size: 6,133 Bytes
f02db94 |
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 |
import os
import numpy as np
import fitz # PyMuPDF pour extraction PDF
import faiss
import pickle
import matplotlib.pyplot as plt
from concurrent.futures import ThreadPoolExecutor
from mistralai import Mistral
from sklearn.manifold import TSNE
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from dotenv import load_dotenv
# Charger les variables d'environnement
load_dotenv()
MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY_static')
# 📌 Initialisation du client Mistral
client = Mistral(api_key=MISTRAL_API_KEY)
model_embedding = "mistral-embed"
model_chat = "ministral-8b-latest"
temperature = 0.1 # Réduction de la température pour privilégier la RAG
# 📌 Paramètres de segmentation
chunk_size = 256 # Réduction du chunk size pour un meilleur contrôle du contexte
chunk_overlap = 15
# 📌 Définition des chemins de stockage
index_path = "faiss_index.bin"
chunks_path = "chunked_docs.pkl"
metadata_path = "metadata.pkl"
embeddings_path = "embeddings.npy"
# 📌 Vérification et chargement des données
if os.path.exists(index_path) and os.path.exists(chunks_path) and os.path.exists(metadata_path) and os.path.exists(embeddings_path):
print("🔄 Chargement des données existantes...")
index = faiss.read_index(index_path)
with open(chunks_path, "rb") as f:
chunked_docs = pickle.load(f)
with open(metadata_path, "rb") as f:
metadata_list = pickle.load(f)
embeddings = np.load(embeddings_path)
print("✅ Index, chunks, embeddings et métadonnées chargés avec succès !")
else:
print("⚡ Création et stockage d'un nouvel index FAISS...")
# 📌 Extraction des documents et métadonnées
def extract_and_chunk_pdfs(pdf_folder):
documents = SimpleDirectoryReader(pdf_folder, recursive=True).load_data()
chunked_docs, metadata_list = [], []
for doc in documents:
doc_text = doc.text
file_name = doc.metadata.get("file_name", "Inconnu")
title = doc.metadata.get("title") or os.path.splitext(file_name)[0] # Utilisation du nom de fichier comme fallback
doc_metadata = {"source": file_name, "title": title}
for i in range(0, len(doc_text), chunk_size):
chunk = doc_text[i:i + chunk_size]
chunked_docs.append({"text": chunk, "metadata": doc_metadata})
metadata_list.append(doc_metadata)
return chunked_docs, metadata_list
pdf_folder = 'C:/Users/MIPO10053340/OneDrive - Groupe Avril/Bureau/Salon_Agriculture_2024/Micka_API_Call/Docs_pdf/'
chunked_docs, metadata_list = extract_and_chunk_pdfs(pdf_folder)
# 📌 Génération des embeddings en parallèle
def get_embeddings_in_batches(text_chunks, batch_size=5):
embeddings = []
def process_batch(batch):
response = client.embeddings.create(model=model_embedding, inputs=[chunk["text"] for chunk in batch])
return [data.embedding for data in response.data]
with ThreadPoolExecutor(max_workers=5) as executor:
future_batches = [executor.submit(process_batch, text_chunks[i:i+batch_size]) for i in range(0, len(text_chunks), batch_size)]
for future in future_batches:
embeddings.extend(future.result())
return np.array(embeddings).astype('float32')
embeddings = get_embeddings_in_batches(chunked_docs)
# 📌 Création et stockage de l'index FAISS
dimension = embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(embeddings)
faiss.write_index(index, index_path)
# 📌 Sauvegarde des données
with open(chunks_path, "wb") as f:
pickle.dump(chunked_docs, f)
with open(metadata_path, "wb") as f:
pickle.dump(metadata_list, f)
np.save(embeddings_path, embeddings) # Sauvegarde des embeddings
print("✅ Index, chunks, embeddings et métadonnées sauvegardés !")
# 📌 Récupération des chunks les plus pertinents
def retrieve_relevant_chunks(question, k=5):
question_embedding_response = client.embeddings.create(
model=model_embedding,
inputs=[question],
)
question_embedding = np.array(question_embedding_response.data[0].embedding).astype('float32').reshape(1, -1)
distances, indices = index.search(question_embedding, k)
if len(indices[0]) == 0:
print("⚠️ Avertissement : Aucun chunk pertinent trouvé, réponse possible moins précise.")
return [], []
return [chunked_docs[i] for i in indices[0]]
# 📌 Génération de réponse avec MistralAI
def generate_response(context, question, sources):
chunk_references = [f"[{i+1}]" for i in range(len(sources))]
chunk_texts = "\n\n".join([f"{chunk_references[i]} (Source: {src['metadata']['source']}) :\n{src['text']}" for i, src in enumerate(sources)])
messages = [
{"role": "system", "content": f"Voici les informations extraites des documents :\n{chunk_texts}\n\nUtilise ces informations pour répondre."},
{"role": "user", "content": question}
]
response = client.chat.complete(model=model_chat, messages=messages, temperature=temperature)
return response.choices[0].message.content + " " + "".join(chunk_references), chunk_texts
# 📌 Exécuter une requête utilisateur
user_question = "Quels sont les besoins en protéines des poulets de chair en phase de croissance ?"
relevant_chunks = retrieve_relevant_chunks(user_question)
context = "\n".join([chunk["text"] for chunk in relevant_chunks])
answer, citations = generate_response(context, user_question, relevant_chunks)
# 📊 Affichage de la réponse avec sources
print("\n🔹 Réponse Mistral :")
print(answer)
print("\n📌 **Chunks utilisés :**")
print(citations)
# 💾 Sauvegarde des résultats
with open("mistral_response_types.txt", "w", encoding="utf-8") as f:
f.write(f"Question : {user_question}\n")
f.write(f"Réponse :\n{answer}\n")
f.write(f"{citations}\n")
print("\n✅ Réponse enregistrée avec les chunks exacts et références dans 'mistral_response_types.txt'")
|