Loren commited on
Commit
7c107bb
·
verified ·
1 Parent(s): 17a7d82

Upload 3 files

Browse files
Files changed (3) hide show
  1. app/database.py +248 -247
  2. app/main.py +47 -0
  3. app/templates/prompt_mistral_rag.py +19 -0
app/database.py CHANGED
@@ -1,247 +1,248 @@
1
- import os
2
- from typing import List, Dict, Any
3
- import duckdb
4
- import faiss
5
- import pandas as pd
6
- from huggingface_hub import hf_hub_download
7
- from sentence_transformers import SentenceTransformer, CrossEncoder
8
- import torch
9
- from datasets import load_dataset
10
- from dotenv import load_dotenv
11
- import pyarrow as pa
12
- import pyarrow.compute as pc
13
-
14
- # Initialisations
15
- load_dotenv()
16
- HF_TOKEN = os.getenv('API_HF_TOKEN')
17
-
18
- REPO_ID = "Loren/articles_database"
19
- FAISS_REPO_ID = "Loren/articles_faiss"
20
- FAISS_INDEX_FILE = "faiss_index.bin"
21
- MODEL_NAME = "intfloat/multilingual-e5-small"
22
- CROSS_ENCODER_NAME = "cross-encoder/ms-marco-MiniLM-L12-v2"
23
-
24
- cache_dir = "/tmp"
25
- os.makedirs(cache_dir, exist_ok=True)
26
- # Rediriger le cache HF globalement
27
- os.environ["HF_HOME"] = cache_dir
28
- os.environ["HF_DATASETS_CACHE"] = cache_dir
29
- os.environ["TRANSFORMERS_CACHE"] = cache_dir
30
-
31
- # Téléchargement des fichiers Parquet depuis Hugging Face
32
- articles_parquet = hf_hub_download(
33
- repo_id=REPO_ID,
34
- filename="articles_checked.parquet",
35
- repo_type="dataset",
36
- cache_dir=cache_dir)
37
- tags_parquet = hf_hub_download(
38
- repo_id=REPO_ID,
39
- filename="tags.parquet",
40
- repo_type="dataset",
41
- cache_dir=cache_dir)
42
- tag_article_parquet = hf_hub_download(
43
- repo_id=REPO_ID,
44
- filename="tag_article.parquet",
45
- repo_type="dataset",
46
- cache_dir=cache_dir)
47
-
48
- # Connexion DuckDB en mémoire
49
- con = duckdb.connect()
50
-
51
- # Créer des tables DuckDB directement à partir des fichiers Parquet
52
- con.execute(f"CREATE VIEW articles AS SELECT * FROM parquet_scan('{articles_parquet}')")
53
- con.execute(f"CREATE VIEW tags AS SELECT * FROM parquet_scan('{tags_parquet}')")
54
- con.execute(f"CREATE VIEW tag_article AS SELECT * FROM parquet_scan('{tag_article_parquet}')")
55
-
56
- # Téléchargement des fichiers de la base faiss depuis le dataset Hugging Face
57
- hf_faiss_index = hf_hub_download(
58
- repo_id=FAISS_REPO_ID,
59
- filename=FAISS_INDEX_FILE,
60
- repo_type="dataset",
61
- token=HF_TOKEN,
62
- cache_dir=cache_dir
63
- )
64
-
65
- # Chargement de l’index FAISS
66
- faiss_index = faiss.read_index(hf_faiss_index)
67
-
68
- # Téléchargement des metadatas Faiss depuis le dataset Hugging Face
69
- dataset = load_dataset(FAISS_REPO_ID, split="train", token=HF_TOKEN)
70
- arrow_table = dataset.data
71
-
72
- # Creation du Sentence transformer model
73
- device = "cuda" if torch.cuda.is_available() else "cpu"
74
- print(f"*** Device: {device}")
75
- model = SentenceTransformer(MODEL_NAME, device=device)
76
-
77
- # Création du cross-encoder
78
- cross_encoder = CrossEncoder(CROSS_ENCODER_NAME, device=device,
79
- trust_remote_code=True)
80
-
81
-
82
- # Fonctions d'accès aux données
83
-
84
- def fetch_tags() -> List[str]:
85
- """
86
- Récupère la liste de tous les tags disponibles dans la base de données.
87
-
88
- Returns:
89
- Dict: Un dictionnaire contenant le statut et les résultats.
90
- - Si succès :
91
- {
92
- "status": "ok",
93
- "result": List[str] # Liste des noms de tags triés par ordre alphabétique
94
- }
95
- - En cas d'erreur :
96
- {
97
- "status": "error",
98
- "code": str, # Nom de l'exception
99
- "message": str # Message de l'exception
100
- }
101
- """
102
- try:
103
- query = "SELECT tag_name FROM tags ORDER BY tag_name"
104
- result = con.execute(query).fetchall()
105
- return {"status": "ok", "result": [row[0] for row in result]}
106
- except Exception as e:
107
- return {"status": "error", "code": type(e).__name__, "message": str(e)}
108
-
109
- def fetch_articles_by_tags(tags: List[str]) -> List[Dict]:
110
- """
111
- Récupère les articles associés à un ou plusieurs tags.
112
-
113
- Args:
114
- tags (List[str]): Une liste de noms de tags pour filtrer les articles.
115
-
116
- Returns:
117
- Dict: Un dictionnaire contenant le statut et les résultats.
118
- - Si succès :
119
- {
120
- "status": "ok",
121
- "result": List[Dict] # Liste de dictionnaires représentant les articles
122
- }
123
- Chaque dictionnaire contient les clés :
124
- - 'article_id': int, ID de l'article
125
- - 'article_title': str, Titre de l'article
126
- - 'article_url': str, URL de l'article
127
- - En cas d'erreur ou si aucun tag fourni :
128
- {
129
- "status": "error",
130
- "code": str, # Code d'erreur ou nom de l'exception
131
- "message": str # Message d'erreur
132
- }
133
-
134
- Notes:
135
- - Si la liste `tags` est vide, la fonction retourne une liste vide.
136
- - Les résultats incluent uniquement les articles correspondant à au moins un des tags fournis.
137
- """
138
- if not tags:
139
- return {"status": "error", "code": "no_tags", "message": "Aucun tag fourni."}
140
-
141
- try:
142
- placeholders = ",".join(["?"] * len(tags))
143
- query = f"""SELECT distinct a.article_id, a.article_title, a.article_url,
144
- CASE WHEN a.article_online
145
- THEN a.article_url
146
- ELSE 'Article unavailable' END AS url,
147
- FROM tags t, tag_article ta, articles a
148
- WHERE t.tag_id = ta.tag_id
149
- AND ta.article_id = a.article_id
150
- AND t.tag_name IN ({placeholders})
151
- """
152
- result = con.execute(query, tags).fetchdf()
153
- return {"status": "ok", "result": result.to_dict(orient="records")}
154
- except Exception as e:
155
- return {"status": "error", "code": type(e).__name__, "message": str(e)}
156
-
157
- def fetch_query_results(query: str, k_model: int = 10, k_cross: int = 5) -> Dict[str, Any]:
158
- """
159
- Exécute une requête de recherche sémantique avec FAISS, puis rerank avec un cross-encoder
160
- et retourne les meilleurs passages enrichis avec des métadonnées provenant de DuckDB.
161
-
162
- Paramètres
163
- ----------
164
- query : str
165
- La requête texte fournie par l'utilisateur.
166
- k_model : int, optionnel (défaut = 10)
167
- Nombre de résultats les plus proches à récupérer depuis l'index FAISS.
168
- k_cross : int, optionnel (défaut = 5)
169
- Nombre de résultats finaux à conserver après reranking avec le cross-encoder.
170
-
171
- Retour
172
- ------
173
- Dict[str, Any]
174
- Un dictionnaire contenant :
175
- - status : "ok" si succès, sinon "error"
176
- - result : liste de résultats (si succès)
177
- - code et message : informations d'erreur (si échec)
178
- """
179
- if not query:
180
- return {"status": "error", "code": "no_query", "message": "Aucun query fourni."}
181
- try:
182
- query_vec = model.encode(["query: "+query], convert_to_numpy=True, normalize_embeddings=True)
183
- distances, indices = faiss_index.search(query_vec, k_model)
184
-
185
- # Résultats FAISS
186
- faiss_ids_list = indices[0].tolist()
187
- distances_list = distances[0].tolist()
188
-
189
- # Filtrer Arrow sur les IDs trouvés
190
- filtered_table = arrow_table.filter(
191
- pc.is_in(arrow_table['faiss_id'],
192
- value_set=pa.array(faiss_ids_list))
193
- )
194
-
195
- # Convertir Arrow → pandas pour ajouter la distance
196
- df = filtered_table.to_pandas()
197
-
198
- # Ajouter la distance en gardant l'ordre faiss_ids_list
199
- distance_map = dict(zip(faiss_ids_list, distances_list))
200
- df["distance"] = df["faiss_id"].map(distance_map)
201
-
202
- # Cross-encoder
203
- top_passages = df["chunk_text"].tolist()
204
- cross_input = [(query, p) for p in top_passages]
205
- cross_scores = cross_encoder.predict(cross_input)
206
-
207
- # Rerank
208
- df["cross_score"] = cross_scores
209
- df = df.sort_values(by="cross_score", ascending=False)
210
-
211
- # Garder top k_cross
212
- df_top = df.head(k_cross)
213
-
214
- # Enregistrer dans DuckDB
215
- con.register("faiss_tmp", df_top)
216
-
217
- sql = """
218
- SELECT
219
- f.faiss_id,
220
- f.document_id,
221
- f.distance,
222
- f.cross_score,
223
- f.chunk_text,
224
- a.article_title,
225
- a.article_url,
226
- CASE WHEN a.article_online
227
- THEN a.article_url
228
- ELSE 'Article unavailable' END AS url,
229
- STRING_AGG(t.tag_name, ', ') AS tags
230
- FROM faiss_tmp f
231
- JOIN articles a ON f.document_id = a.article_id
232
- JOIN tag_article ta ON a.article_id = ta.article_id
233
- JOIN tags t ON ta.tag_id = t.tag_id
234
- WHERE (LENGTH(article_text) - LENGTH(REPLACE(article_text, ' ', '')) + 1) >= 100
235
- GROUP BY f.faiss_id, f.document_id, f.distance, f.cross_score, f.chunk_text,
236
- a.article_title, a.article_online, a.article_url
237
- ORDER BY AVG(f.cross_score) DESC
238
- """
239
-
240
- duck_res = con.execute(sql).fetchdf()
241
-
242
- # Liste finale de dictionnaires
243
- list_result = duck_res.to_dict(orient="records")
244
-
245
- return {"status": "ok", "result": list_result}
246
- except Exception as e:
247
- return {"status": "error", "code": type(e).__name__, "message": str(e)}
 
 
1
+ import os
2
+ from typing import List, Dict, Any
3
+ import duckdb
4
+ import faiss
5
+ import pandas as pd
6
+ from huggingface_hub import hf_hub_download
7
+ from sentence_transformers import SentenceTransformer, CrossEncoder
8
+ import torch
9
+ from datasets import load_dataset
10
+ from dotenv import load_dotenv
11
+ import pyarrow as pa
12
+ import pyarrow.compute as pc
13
+
14
+ # Initialisations
15
+ load_dotenv()
16
+ HF_TOKEN = os.getenv('API_HF_TOKEN')
17
+
18
+ REPO_ID = "Loren/articles_database"
19
+ FAISS_REPO_ID = "Loren/articles_faiss"
20
+ FAISS_INDEX_FILE = "faiss_index.bin"
21
+ MODEL_NAME = "intfloat/multilingual-e5-small"
22
+ CROSS_ENCODER_NAME = "cross-encoder/ms-marco-MiniLM-L12-v2"
23
+
24
+ cache_dir = "/tmp"
25
+ os.makedirs(cache_dir, exist_ok=True)
26
+ # Rediriger le cache HF globalement
27
+ os.environ["HF_HOME"] = cache_dir
28
+ os.environ["HF_DATASETS_CACHE"] = cache_dir
29
+ os.environ["TRANSFORMERS_CACHE"] = cache_dir
30
+
31
+ # Téléchargement des fichiers Parquet depuis Hugging Face
32
+ articles_parquet = hf_hub_download(
33
+ repo_id=REPO_ID,
34
+ filename="articles_checked.parquet",
35
+ repo_type="dataset",
36
+ cache_dir=cache_dir)
37
+ tags_parquet = hf_hub_download(
38
+ repo_id=REPO_ID,
39
+ filename="tags.parquet",
40
+ repo_type="dataset",
41
+ cache_dir=cache_dir)
42
+ tag_article_parquet = hf_hub_download(
43
+ repo_id=REPO_ID,
44
+ filename="tag_article.parquet",
45
+ repo_type="dataset",
46
+ cache_dir=cache_dir)
47
+
48
+ # Connexion DuckDB en mémoire
49
+ con = duckdb.connect()
50
+
51
+ # Créer des tables DuckDB directement à partir des fichiers Parquet
52
+ con.execute(f"CREATE VIEW articles AS SELECT * FROM parquet_scan('{articles_parquet}')")
53
+ con.execute(f"CREATE VIEW tags AS SELECT * FROM parquet_scan('{tags_parquet}')")
54
+ con.execute(f"CREATE VIEW tag_article AS SELECT * FROM parquet_scan('{tag_article_parquet}')")
55
+
56
+ # Téléchargement des fichiers de la base faiss depuis le dataset Hugging Face
57
+ hf_faiss_index = hf_hub_download(
58
+ repo_id=FAISS_REPO_ID,
59
+ filename=FAISS_INDEX_FILE,
60
+ repo_type="dataset",
61
+ token=HF_TOKEN,
62
+ cache_dir=cache_dir
63
+ )
64
+
65
+ # Chargement de l’index FAISS
66
+ faiss_index = faiss.read_index(hf_faiss_index)
67
+
68
+ # Téléchargement des metadatas Faiss depuis le dataset Hugging Face
69
+ dataset = load_dataset(FAISS_REPO_ID, split="train", token=HF_TOKEN)
70
+ arrow_table = dataset.data
71
+
72
+ # Creation du Sentence transformer model
73
+ device = "cuda" if torch.cuda.is_available() else "cpu"
74
+ print(f"*** Device: {device}")
75
+ model = SentenceTransformer(MODEL_NAME, device=device)
76
+
77
+ # Création du cross-encoder
78
+ cross_encoder = CrossEncoder(CROSS_ENCODER_NAME, device=device,
79
+ trust_remote_code=True)
80
+
81
+
82
+ # Fonctions d'accès aux données
83
+
84
+ def fetch_tags() -> List[str]:
85
+ """
86
+ Récupère la liste de tous les tags disponibles dans la base de données.
87
+
88
+ Returns:
89
+ Dict: Un dictionnaire contenant le statut et les résultats.
90
+ - Si succès :
91
+ {
92
+ "status": "ok",
93
+ "result": List[str] # Liste des noms de tags triés par ordre alphabétique
94
+ }
95
+ - En cas d'erreur :
96
+ {
97
+ "status": "error",
98
+ "code": str, # Nom de l'exception
99
+ "message": str # Message de l'exception
100
+ }
101
+ """
102
+ try:
103
+ query = "SELECT tag_name FROM tags ORDER BY tag_name"
104
+ result = con.execute(query).fetchall()
105
+ return {"status": "ok", "result": [row[0] for row in result]}
106
+ except Exception as e:
107
+ return {"status": "error", "code": type(e).__name__, "message": str(e)}
108
+
109
+ def fetch_articles_by_tags(tags: List[str]) -> List[Dict]:
110
+ """
111
+ Récupère les articles associés à un ou plusieurs tags.
112
+
113
+ Args:
114
+ tags (List[str]): Une liste de noms de tags pour filtrer les articles.
115
+
116
+ Returns:
117
+ Dict: Un dictionnaire contenant le statut et les résultats.
118
+ - Si succès :
119
+ {
120
+ "status": "ok",
121
+ "result": List[Dict] # Liste de dictionnaires représentant les articles
122
+ }
123
+ Chaque dictionnaire contient les clés :
124
+ - 'article_id': int, ID de l'article
125
+ - 'article_title': str, Titre de l'article
126
+ - 'article_url': str, URL de l'article
127
+ - En cas d'erreur ou si aucun tag fourni :
128
+ {
129
+ "status": "error",
130
+ "code": str, # Code d'erreur ou nom de l'exception
131
+ "message": str # Message d'erreur
132
+ }
133
+
134
+ Notes:
135
+ - Si la liste `tags` est vide, la fonction retourne une liste vide.
136
+ - Les résultats incluent uniquement les articles correspondant à au moins un des tags fournis.
137
+ """
138
+ if not tags:
139
+ return {"status": "error", "code": "no_tags", "message": "Aucun tag fourni."}
140
+
141
+ try:
142
+ placeholders = ",".join(["?"] * len(tags))
143
+ query = f"""SELECT distinct a.article_id, a.article_title, a.article_url,
144
+ CASE WHEN a.article_online
145
+ THEN a.article_url
146
+ ELSE 'Article unavailable' END AS url,
147
+ FROM tags t, tag_article ta, articles a
148
+ WHERE t.tag_id = ta.tag_id
149
+ AND ta.article_id = a.article_id
150
+ AND t.tag_name IN ({placeholders})
151
+ """
152
+ result = con.execute(query, tags).fetchdf()
153
+ return {"status": "ok", "result": result.to_dict(orient="records")}
154
+ except Exception as e:
155
+ return {"status": "error", "code": type(e).__name__, "message": str(e)}
156
+
157
+ def fetch_query_results(query: str, k_model: int = 10, k_cross: int = 5) -> Dict[str, Any]:
158
+ """
159
+ Exécute une requête de recherche s��mantique avec FAISS, puis rerank avec un cross-encoder
160
+ et retourne les meilleurs passages enrichis avec des métadonnées provenant de DuckDB.
161
+
162
+ Paramètres
163
+ ----------
164
+ query : str
165
+ La requête texte fournie par l'utilisateur.
166
+ k_model : int, optionnel (défaut = 10)
167
+ Nombre de résultats les plus proches à récupérer depuis l'index FAISS.
168
+ k_cross : int, optionnel (défaut = 5)
169
+ Nombre de résultats finaux à conserver après reranking avec le cross-encoder.
170
+
171
+ Retour
172
+ ------
173
+ Dict[str, Any]
174
+ Un dictionnaire contenant :
175
+ - status : "ok" si succès, sinon "error"
176
+ - result : liste de résultats (si succès)
177
+ - code et message : informations d'erreur (si échec)
178
+ """
179
+ if not query:
180
+ return {"status": "error", "code": "no_query", "message": "Aucun query fourni."}
181
+ try:
182
+ query_vec = model.encode(["query: "+query], convert_to_numpy=True, normalize_embeddings=True)
183
+ distances, indices = faiss_index.search(query_vec, k_model)
184
+
185
+ # Résultats FAISS
186
+ faiss_ids_list = indices[0].tolist()
187
+ distances_list = distances[0].tolist()
188
+
189
+ # Filtrer Arrow sur les IDs trouvés
190
+ filtered_table = arrow_table.filter(
191
+ pc.is_in(arrow_table['faiss_id'],
192
+ value_set=pa.array(faiss_ids_list))
193
+ )
194
+
195
+ # Convertir Arrow → pandas pour ajouter la distance
196
+ df = filtered_table.to_pandas()
197
+
198
+ # Ajouter la distance en gardant l'ordre faiss_ids_list
199
+ distance_map = dict(zip(faiss_ids_list, distances_list))
200
+ df["distance"] = df["faiss_id"].map(distance_map)
201
+
202
+ # Cross-encoder
203
+ top_passages = df["chunk_text"].tolist()
204
+ cross_input = [(query, p) for p in top_passages]
205
+ cross_scores = cross_encoder.predict(cross_input)
206
+
207
+ # Rerank
208
+ df["cross_score"] = cross_scores
209
+ df = df.sort_values(by="cross_score", ascending=False)
210
+
211
+ # Garder top k_cross
212
+ df_top = df.head(k_cross)
213
+
214
+ # Enregistrer dans DuckDB
215
+ con.register("faiss_tmp", df_top)
216
+
217
+ sql = """
218
+ SELECT
219
+ f.faiss_id,
220
+ f.document_id,
221
+ f.distance,
222
+ f.cross_score,
223
+ f.chunk_text,
224
+ a.article_title,
225
+ a.article_url,
226
+ CASE WHEN a.article_online
227
+ THEN a.article_url
228
+ ELSE 'Article unavailable' END AS url,
229
+ STRING_AGG(t.tag_name, ', ') AS tags
230
+ FROM faiss_tmp f
231
+ JOIN articles a ON f.document_id = a.article_id
232
+ JOIN tag_article ta ON a.article_id = ta.article_id
233
+ JOIN tags t ON ta.tag_id = t.tag_id
234
+ WHERE (LENGTH(article_text) - LENGTH(REPLACE(article_text, ' ', '')) + 1) >= 100
235
+ GROUP BY f.faiss_id, f.document_id, f.distance, f.cross_score, f.chunk_text,
236
+ a.article_title, a.article_online, a.article_url
237
+ ORDER BY AVG(f.cross_score) DESC
238
+ """
239
+
240
+ duck_res = con.execute(sql).fetchdf()
241
+
242
+ # Liste finale de dictionnaires
243
+ list_result = duck_res.to_dict(orient="records")
244
+
245
+ return {"status": "ok", "result": list_result}
246
+ except Exception as e:
247
+ return {"status": "error", "code": type(e).__name__, "message": str(e)}
248
+
app/main.py CHANGED
@@ -3,12 +3,25 @@ from typing import List, Optional, Dict, Any
3
  from app import database
4
  from fastapi.middleware.cors import CORSMiddleware
5
 
 
 
 
 
 
6
  app = FastAPI(
7
  title="Articles API",
8
  description="API pour récupérer articles et tags depuis SQLite",
9
  version="1.0"
10
  )
11
 
 
 
 
 
 
 
 
 
12
  # CORS pour permettre l'accès depuis le navigateur
13
  app.add_middleware(
14
  CORSMiddleware,
@@ -123,3 +136,37 @@ def get_query_results(query: str = Query(..., description="Requête de recherche
123
  return dict_result
124
  except Exception as e:
125
  return {"status": "error", "code": type(e).__name__, "message": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from app import database
4
  from fastapi.middleware.cors import CORSMiddleware
5
 
6
+ from pydantic import BaseModel
7
+ from transformers import AutoTokenizer, AutoModelForCausalLM
8
+ import torch
9
+ from templates.prompt_mistral_rag import RAG_PROMPT_TEMPLATE
10
+
11
  app = FastAPI(
12
  title="Articles API",
13
  description="API pour récupérer articles et tags depuis SQLite",
14
  version="1.0"
15
  )
16
 
17
+ # Chargement du modèle génératif
18
+ MODEL_NAME = "mistralai/Mistral-7B-Instruct-v0.3"
19
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
20
+ model = AutoModelForCausalLM.from_pretrained(MODEL_NAME,
21
+ torch_dtype=torch.float16,
22
+ device_map="auto"
23
+ )
24
+
25
  # CORS pour permettre l'accès depuis le navigateur
26
  app.add_middleware(
27
  CORSMiddleware,
 
136
  return dict_result
137
  except Exception as e:
138
  return {"status": "error", "code": type(e).__name__, "message": str(e)}
139
+
140
+ # 🔹 Exemple de modèle d'entrée utilisateur
141
+ class QueryRequest(BaseModel):
142
+ question: str
143
+
144
+ @app.post("/ask")
145
+ async def ask_question(request: QueryRequest):
146
+ try:
147
+ user_query = request.question.strip()
148
+ dict_result = database.fetch_query_results(user_query, k_model=10, k_cross=5)
149
+ if dict_result["status"] == "ok":
150
+ list_chunks = [resp['chunk_text'] for resp in dict_result['results']]
151
+ if not list_chunks:
152
+ answer = ("Je ne dispose pas d’informations sur ce sujet. "
153
+ "Je peux uniquement répondre à des questions sur les articles " \
154
+ "du jeu de données.")
155
+ else:
156
+ # Construction du prompt
157
+ prompt = RAG_PROMPT_TEMPLATE.format(
158
+ context="\n".join(list_chunks),
159
+ question=user_query
160
+ )
161
+ # Génération de la réponse
162
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
163
+ outputs = model.generate(**inputs, max_new_tokens=500)
164
+ answer = tokenizer.decode(outputs[0], skip_special_tokens=True)
165
+ else:
166
+ answer = f"Une erreur est survenue lors de la récupération des informations : \
167
+ {dict_result['code']} - {dict_result['message']}."
168
+ return {"answer": answer}
169
+ except Exception as e:
170
+ answer = f"Une erreur est survenue lors de la récupération des informations : \
171
+ {type(e).__name__} - {str(e)}."
172
+ return {"answer": answer}
app/templates/prompt_mistral_rag.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ RAG_PROMPT_TEMPLATE = """[INST]
2
+ You are an AI assistant that answers questions based solely on the CONTEXT provided.
3
+ The context consists of excerpts from blog articles on a wide variety of topics.
4
+
5
+ RULES:
6
+ 1. Only use information present in the CONTEXT to answer the user's question.
7
+ 2. If the information is not in the CONTEXT, politely say that you do not know the answer.
8
+ 3. Do not invent, speculate, or add any information from outside sources.
9
+ 4. If the user's question is vague or unclear, ask for clarification before answering.
10
+ 5. Provide answers that are clear, concise, and natural in English.
11
+ 7. Do not cite or refer to sources outside of the provided CONTEXT.
12
+
13
+ CONTEXT:
14
+ {context}
15
+
16
+ USER QUESTION:
17
+ {question}
18
+ [/INST]
19
+ """