abdelom commited on
Commit
f798c9d
·
verified ·
1 Parent(s): 063548c

Upload 7 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Chatbot[[:space:]]myinwi.xlsx filter=lfs diff=lfs merge=lfs -text
Accueil.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import streamlit as st
3
+ from pathlib import Path
4
+
5
+ def get_base64_of_bin_file(bin_file_path: str) -> str:
6
+ file_bytes = Path(bin_file_path).read_bytes()
7
+ return base64.b64encode(file_bytes).decode()
8
+
9
+ def main():
10
+ st.set_page_config(page_title="INWI Chatbot - Accueil", layout="wide")
11
+
12
+ # Read local image and convert to Base64
13
+ img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
14
+ css_logo = f"""
15
+ <style>
16
+ [data-testid="stSidebarNav"]::before {{
17
+ content: "";
18
+ display: block;
19
+ margin: 0 auto 20px auto;
20
+ width: 80%;
21
+ height: 100px;
22
+ background-image: url("data:image/png;base64,{img_base64}");
23
+ background-size: contain;
24
+ background-repeat: no-repeat;
25
+ background-position: center;
26
+ }}
27
+ </style>
28
+ """
29
+
30
+ st.markdown(css_logo, unsafe_allow_html=True)
31
+
32
+ st.title("👋 Bienvenue sur le Chatbot INWI")
33
+ st.markdown(
34
+ """
35
+ Ceci est la page principale.
36
+ Vous pouvez choisir le **Chatbot en Français** ou le **Chatbot en Arabe** en naviguant dans le menu de gauche (sous "Pages" ou "Select a page").
37
+ """
38
+ )
39
+ st.write("Veuillez sélectionner la langue désirée dans la barre latérale.")
40
+
41
+ if __name__ == "__main__":
42
+ main()
Chatbot myinwi.xlsx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:090d7da624e8a495525ff7f76b7ada897e30f91cbe3a8871f42532ef5e45a323
3
+ size 435917
README.md CHANGED
@@ -1,12 +1 @@
1
- ---
2
- title: Test Inwi Cleverlytics V0
3
- emoji: 🚀
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: streamlit
7
- sdk_version: 1.41.1
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ Test du prompte chatbot inwi
 
 
 
 
 
 
 
 
 
 
 
img/logo inwi celeverlytics.png ADDED
pages/1_Chatbot_FR.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import os
4
+ from pathlib import Path
5
+ import base64
6
+
7
+ # LangChain & Hugging Face
8
+ from langchain.embeddings import HuggingFaceEmbeddings
9
+ from langchain.vectorstores import Chroma
10
+ from langchain.schema import Document
11
+ from langchain.prompts import PromptTemplate
12
+ from langchain.llms import HuggingFaceHub
13
+ from langchain.chains import LLMChain
14
+
15
+ import pysqlite3
16
+ import sys
17
+ sys.modules["sqlite3"] = pysqlite3
18
+
19
+ #####################
20
+ # 1. HELPER FUNCTIONS
21
+ #####################
22
+
23
+ def get_base64_of_bin_file(bin_file_path: str) -> str:
24
+ file_bytes = Path(bin_file_path).read_bytes()
25
+ return base64.b64encode(file_bytes).decode()
26
+
27
+ def find_parent_fr(data, r, col):
28
+ """
29
+ Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version FR).
30
+ """
31
+ i = r - 1
32
+ parent = None
33
+ while i >= 0 and pd.isna(parent):
34
+ parent = data.iloc[i, col]
35
+ i -= 1
36
+ return parent
37
+
38
+ def create_contextual_fr(df, category, strat_id=0):
39
+ """
40
+ Crée un DataFrame avec questions-réponses contextuelles (version FR).
41
+ """
42
+ rows = []
43
+ columns_qna = list(df.columns)
44
+
45
+ for r, row in df.iterrows():
46
+ for level, col in enumerate(df.columns):
47
+ question = row[col]
48
+ if pd.isna(question):
49
+ continue
50
+
51
+ # Si la question est un "leaf node"
52
+ if level == 4 or pd.isna(row[columns_qna[level + 1]]):
53
+ # Gérer des sous-questions multiples
54
+ if "\n*Si" in question or "\n *" in question or "\n*" in question:
55
+ questions = question.replace("\n*Si", "\n*").replace("\n *", "\n*").split("\n*")
56
+ for subquestion in questions:
57
+ if len(subquestion.strip()) == 0:
58
+ continue
59
+
60
+ context = []
61
+ for i in range(level - 1, -1, -1):
62
+ parent = df.iloc[r, i]
63
+ if pd.isna(parent):
64
+ parent = find_parent_fr(df, r, i)
65
+ if pd.notna(parent):
66
+ context = [parent] + context
67
+
68
+ rows.append({
69
+ "id": strat_id + len(rows) + 1,
70
+ "question": " > ".join(context),
71
+ "answer": subquestion.strip(),
72
+ "category": category,
73
+ })
74
+ else:
75
+ context = []
76
+ for i in range(level - 1, -1, -1):
77
+ parent = df.iloc[r, i]
78
+ if pd.isna(parent):
79
+ parent = find_parent_fr(df, r, i)
80
+ if pd.notna(parent):
81
+ context = [parent] + context
82
+
83
+ rows.append({
84
+ "id": strat_id + len(rows) + 1,
85
+ "question": " > ".join(context),
86
+ "answer": question.strip(),
87
+ "category": category,
88
+ })
89
+
90
+ return pd.DataFrame(rows)
91
+
92
+ def load_excel_and_create_vectorstore_fr(excel_path: str, persist_dir: str = "./chroma_db_fr"):
93
+ """
94
+ Charge les données depuis plusieurs feuilles Excel (version FR),
95
+ construit & stocke un Chroma VectorStore.
96
+ """
97
+ # 1. Charger les feuilles Excel
98
+ qna_tree_fr0 = pd.read_excel(excel_path, sheet_name="Prépayé (FR)", skiprows=1).iloc[:, :5]
99
+ qna_tree_fr1 = pd.read_excel(excel_path, sheet_name="Postpayé (FR)", skiprows=1).iloc[:, :5]
100
+ qna_tree_fr2 = pd.read_excel(excel_path, sheet_name="Wifi (FR)", skiprows=1).iloc[:, :5]
101
+
102
+ # 2. Construire le contexte
103
+ context_fr0 = create_contextual_fr(qna_tree_fr0, "Prépayé", strat_id = 0)
104
+ context_fr1 = create_contextual_fr(qna_tree_fr1, "Postpayé", strat_id = len(context_fr0))
105
+ context_fr2 = create_contextual_fr(qna_tree_fr2, "Wifi", strat_id = len(context_fr0) + len(context_fr1))
106
+
107
+ # 3. Concaténer les DataFrame
108
+ context_fr = pd.concat([context_fr0, context_fr1, context_fr2], axis=0)
109
+
110
+ # 4. Créer une colonne "context"
111
+ context_fr["context"] = context_fr.apply(
112
+ lambda row: f"{row['question']} > {row['answer']}",
113
+ axis=1
114
+ )
115
+
116
+ # 5. Convertir chaque ligne en Document
117
+ documents_fr = [
118
+ Document(
119
+ page_content=row["context"],
120
+ metadata={"id": row["id"], "category": row["category"]}
121
+ )
122
+ for _, row in context_fr.iterrows()
123
+ ]
124
+
125
+ # 6. Créer & persister le vecteur
126
+ embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
+ vectorstore_fr = Chroma.from_documents(documents_fr, embedding_model_fr, persist_directory=persist_dir)
128
+ vectorstore_fr.persist()
129
+
130
+ return vectorstore_fr
131
+
132
+ def load_existing_vectorstore_fr(persist_dir: str = "./chroma_db_fr"):
133
+ """
134
+ Charge un VectorStore Chroma déjà stocké (version FR).
135
+ """
136
+ embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
+ vectorstore_fr = Chroma(
138
+ persist_directory=persist_dir,
139
+ embedding_function=embedding_model_fr
140
+ )
141
+ return vectorstore_fr
142
+
143
+ def retrieve_context_fr(retriever_fr, query, top_k=5):
144
+ """
145
+ Récupère les top_k résultats pour la question (version FR).
146
+ """
147
+ results_fr = retriever_fr.get_relevant_documents(query)
148
+ context_fr_list = []
149
+ for _, result in enumerate(results_fr[:top_k], start=1):
150
+ context_fr_list.append(result.page_content)
151
+ return context_fr_list
152
+
153
+
154
+ #########################
155
+ # 2. PROMPT & LLM FR #
156
+ #########################
157
+
158
+ prompt_template_fr = PromptTemplate(
159
+ input_variables=["context", "query"],
160
+ template=(
161
+ """[SYSTEM]
162
+ Vous êtes un assistant client professionnel, expérimenté et bienveillant pour l'opérateur téléphonique INWI.
163
+ Vous excellez dans la gestion des clients, en répondant à leurs problèmes et questions.
164
+ Fournir un service client et des conseils en se basant sur les contextes fournis :
165
+ - Répondre aux salutations de manière courtoise et amicale, par exemple : "Je suis l'assistant IA d'INWI'. Comment puis-je vous aider aujourd'hui ?"
166
+ - Identifier le besoin du client et demander des clarifications si nécessaire, tout en s'appuyant uniquement sur le contexte.
167
+ - Si la question n'est pas liée au contexte d'INWI, veuillez informer poliment que vous ne pouvez pas répondre à des questions hors contexte INWI.
168
+ - Si la réponse ne figure pas dans le contexte, vous pouvez dire "Je n'ai pas assez d'information" et proposer d'appeler le service client au 120.
169
+ - Structurer les réponses de manière concise et efficace. Et n'inventez pas d'infos non présentes dans le contexte.
170
+ - Informer le client qu’il peut vous recontacter pour toute assistance supplémentaire.
171
+ - Ne parlez pas des concurrents qui offrent la meme service d'INWI.
172
+ - Ne jamais insulter ou répondre à une insulte.
173
+ - Ne demandez pas d’informations personnelles ou d’identification du client.
174
+ - Orientez vers le catalogue sur le site web INWI si la question concerne une offre du catalogue.
175
+ - Donnez des solutions standard pour les problèmes techniques avec des options.
176
+ - Avant de générer votre réponse, éliminez toutes les structures comme '[Action] [texte]' et gardez uniquement les informations utiles.
177
+ - Ne jamais parler des sujets suivants : [
178
+ "politique", "élections", "partis", "gouvernement", "lois", "réformes",
179
+ "religion", "croyances", "pratiques religieuses", "théologie",
180
+ "moralité", "débat", "philosophie", "éthique", "discrimination",
181
+ "concurrence", "Maroc Telecom", "IAM", "Orange", "comparaison",
182
+ "sécurité", "fraude", "santé", "médicaments", "traitement", "diagnostic", "maladie",
183
+ "finance", "investissement", "bourse", "crypto", "banque", "assurance",
184
+ "violence", "haine", "contenu explicite", "sexe", "adultes",
185
+ "illégal", "faux documents", "streaming illégal"
186
+ ]
187
+ INWI est un opérateur de télécommunications marocain offrant des services mobiles, Internet et solutions de télécommunications
188
+ pour les particuliers et les entreprises. Il se distingue par son engagement à fournir des services de qualité, innovants et
189
+ accessibles, tout en contribuant au développement numérique du pays.
190
+ Les clients sont notre priorité, et notre but est de résoudre leurs problèmes.
191
+ Votre rôle est de fournir un service client professionnel et efficace sans inventer d'informations.
192
+
193
+ [CONTEXTE]
194
+ {context}
195
+
196
+ [QUESTION DU CLIENT]
197
+ {query}
198
+
199
+ [RÉPONSE]"""
200
+ )
201
+ )
202
+
203
+ # Configuration du LLM HuggingFace (FR)
204
+ os.environ["HUGGINGFACEHUB_API"]
205
+ llm_fr = HuggingFaceHub(
206
+ repo_id="mistralai/Mistral-7B-Instruct-v0.3",
207
+ model_kwargs={
208
+ "temperature": 0.5,
209
+ "max_length": 500
210
+ }
211
+ )
212
+
213
+ # Chaîne FR
214
+ llm_chain_fr = LLMChain(llm=llm_fr, prompt=prompt_template_fr)
215
+
216
+
217
+ #########################
218
+ # 3. STREAMLIT MAIN APP #
219
+ #########################
220
+
221
+ def main():
222
+ st.subheader("INWI IA Chatbot - Français")
223
+
224
+ # Read local image and convert to Base64
225
+ img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
226
+ css_logo = f"""
227
+ <style>
228
+ [data-testid="stSidebarNav"]::before {{
229
+ content: "";
230
+ display: block;
231
+ margin: 0 auto 20px auto;
232
+ width: 80%;
233
+ height: 100px;
234
+ background-image: url("data:image/png;base64,{img_base64}");
235
+ background-size: contain;
236
+ background-repeat: no-repeat;
237
+ background-position: center;
238
+ }}
239
+ </style>
240
+ """
241
+
242
+ st.markdown(css_logo, unsafe_allow_html=True)
243
+
244
+ # Charger ou créer le retriever
245
+ if "retriever_fr" not in st.session_state:
246
+ st.session_state["retriever_fr"] = None
247
+
248
+ st.sidebar.header("Vector Store Options (FR)")
249
+
250
+ if st.sidebar.button("Créer la Vector Store (FR)"):
251
+ with st.spinner("Extraction et création de la vector store FR..."):
252
+ excel_path = "Chatbot myinwi.xlsx"
253
+ persist_directory_fr = "./chroma_db_fr"
254
+ vectorstore_fr = load_excel_and_create_vectorstore_fr(
255
+ excel_path=excel_path,
256
+ persist_dir=persist_directory_fr
257
+ )
258
+ st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
259
+ search_type="mmr",
260
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
261
+ )
262
+ st.success("Vector store FR créée et chargée avec succès !")
263
+
264
+ if st.sidebar.button("Charger la Vector Store existante (FR)"):
265
+ with st.spinner("Chargement de la vector store FR existante..."):
266
+ persist_directory_fr = "./chroma_db_fr"
267
+ vectorstore_fr = load_existing_vectorstore_fr(persist_directory_fr)
268
+ st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
269
+ search_type="mmr",
270
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
271
+ )
272
+ st.success("Vector store FR chargée avec succès !")
273
+
274
+ st.write("""Je suis là pour répondre à toutes vos questions concernant nos
275
+ services, nos offres mobiles et Internet, ainsi que nos solutions adaptées à vos besoins (FR).""")
276
+
277
+ # Zone de texte
278
+ user_query_fr = st.chat_input("Posez votre question ici (FR)...")
279
+
280
+ if user_query_fr:
281
+ if not st.session_state["retriever_fr"]:
282
+ st.warning("Veuillez d'abord créer ou charger la Vector Store (FR).")
283
+ return
284
+
285
+ # Récupération du contexte
286
+ context_fr_list = retrieve_context_fr(st.session_state["retriever_fr"], user_query_fr, top_k=5)
287
+
288
+ if context_fr_list:
289
+ with st.spinner("Génération de la réponse..."):
290
+ response_fr = llm_chain_fr.run({"context": "\n".join(context_fr_list), "query": user_query_fr})
291
+ # Séparer si jamais le prompt contient [RÉPONSE], sinon on affiche tout
292
+ response_fr = response_fr.split("[RÉPONSE]")[-1]
293
+ st.write("**Question :**")
294
+ st.write(user_query_fr)
295
+ st.write("**Réponse :**")
296
+ st.write(response_fr)
297
+ else:
298
+ st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
299
+
300
+
301
+ if __name__ == "__main__":
302
+ main()
303
+
304
+
pages/2_Chatbot_AR.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import os
4
+ from pathlib import Path
5
+ import base64
6
+
7
+ # LangChain & Hugging Face
8
+ from langchain.embeddings import HuggingFaceEmbeddings
9
+ from langchain.vectorstores import Chroma
10
+ from langchain.schema import Document
11
+ from langchain.prompts import PromptTemplate
12
+ from langchain.llms import HuggingFaceHub
13
+ from langchain.chains import LLMChain
14
+
15
+ import pysqlite3
16
+ import sys
17
+ sys.modules["sqlite3"] = pysqlite3
18
+
19
+ #####################
20
+ # 1. HELPER FUNCTIONS
21
+ #####################
22
+
23
+ def get_base64_of_bin_file(bin_file_path: str) -> str:
24
+ file_bytes = Path(bin_file_path).read_bytes()
25
+ return base64.b64encode(file_bytes).decode()
26
+
27
+ def find_parent_ar(data, r, col):
28
+ """
29
+ Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version AR).
30
+ """
31
+ i = r - 1
32
+ parent = None
33
+ while i >= 0 and pd.isna(parent):
34
+ parent = data.iloc[i, col]
35
+ i -= 1
36
+ return parent
37
+
38
+ def create_contextual_ar(df, category, strat_id=0):
39
+ """
40
+ Crée un DataFrame avec questions-réponses contextuelles (version AR).
41
+ """
42
+ rows = []
43
+ columns_qna = list(df.columns)
44
+
45
+ for r, row in df.iterrows():
46
+ for level, col in enumerate(df.columns):
47
+ question = row[col]
48
+ if pd.isna(question):
49
+ continue
50
+
51
+ # Si la question est un "leaf node"
52
+ if level == 4 or pd.isna(row[columns_qna[level + 1]]):
53
+ # Gérer des sous-questions multiples
54
+ if "\n*Si" in question or "\n *" in question or "\n*" in question:
55
+ questions = question.replace("\n*Si", "\n*").replace("\n *", "\n*").split("\n*")
56
+ for subquestion in questions:
57
+ if len(subquestion.strip()) == 0:
58
+ continue
59
+
60
+ context = []
61
+ for i in range(level - 1, -1, -1):
62
+ parent = df.iloc[r, i]
63
+ if pd.isna(parent):
64
+ parent = find_parent_ar(df, r, i)
65
+ if pd.notna(parent):
66
+ context = [parent] + context
67
+
68
+ rows.append({
69
+ "id": strat_id + len(rows) + 1,
70
+ "question": " > ".join(context),
71
+ "answer": subquestion.strip(),
72
+ "category": category,
73
+ })
74
+ else:
75
+ context = []
76
+ for i in range(level - 1, -1, -1):
77
+ parent = df.iloc[r, i]
78
+ if pd.isna(parent):
79
+ parent = find_parent_ar(df, r, i)
80
+ if pd.notna(parent):
81
+ context = [parent] + context
82
+
83
+ rows.append({
84
+ "id": strat_id + len(rows) + 1,
85
+ "question": " > ".join(context),
86
+ "answer": question.strip(),
87
+ "category": category,
88
+ })
89
+
90
+ return pd.DataFrame(rows)
91
+
92
+ def load_excel_and_create_vectorstore_ar(excel_path: str, persist_dir: str = "./chroma_db_ar"):
93
+ """
94
+ Charge les données depuis plusieurs feuilles Excel (version AR),
95
+ construit & stocke un Chroma VectorStore.
96
+ """
97
+ # 1. Charger les feuilles Excel
98
+ qna_tree_ar0 = pd.read_excel(excel_path, sheet_name="Prépayé (AR)", skiprows=1).iloc[:, :5]
99
+ qna_tree_ar1 = pd.read_excel(excel_path, sheet_name="Postpayé (AR)", skiprows=1).iloc[:, :5]
100
+ qna_tree_ar2 = pd.read_excel(excel_path, sheet_name="Wifi (AR)", skiprows=1).iloc[:, :5]
101
+
102
+ # 2. Construire le contexte
103
+ context_ar0 = create_contextual_ar(qna_tree_ar0, "دفع مسبق", strat_id = 0)
104
+ context_ar1 = create_contextual_ar(qna_tree_ar1, "دفع لاحق", strat_id = len(context_ar0))
105
+ context_ar2 = create_contextual_ar(qna_tree_ar2, "واي فاي", strat_id = len(context_ar0) + len(context_ar1))
106
+
107
+ # 3. Concaténer les DataFrame
108
+ context_ar = pd.concat([context_ar0, context_ar1, context_ar2], axis=0)
109
+
110
+ # 4. Créer une colonne "context"
111
+ context_ar["context"] = context_ar.apply(
112
+ lambda row: f"{row['question']} > {row['answer']}",
113
+ axis=1
114
+ )
115
+
116
+ # 5. Convertir chaque ligne en Document
117
+ documents_ar = [
118
+ Document(
119
+ page_content=row["context"],
120
+ metadata={"id": row["id"], "category": row["category"]}
121
+ )
122
+ for _, row in context_ar.iterrows()
123
+ ]
124
+
125
+ # 6. Créer & persister le vecteur
126
+ embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
+ vectorstore_ar = Chroma.from_documents(documents_ar, embedding_model_ar, persist_directory=persist_dir)
128
+ vectorstore_ar.persist()
129
+
130
+ return vectorstore_ar
131
+
132
+ def load_existing_vectorstore_ar(persist_dir: str = "./chroma_db_ar"):
133
+ """
134
+ Charge un VectorStore Chroma déjà stocké (version AR).
135
+ """
136
+ embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
+ vectorstore_ar = Chroma(
138
+ persist_directory=persist_dir,
139
+ embedding_function=embedding_model_ar
140
+ )
141
+ return vectorstore_ar
142
+
143
+ def retrieve_context_ar(retriever_ar, query, top_k=5):
144
+ """
145
+ Récupère les top_k résultats pour la question (version AR).
146
+ """
147
+ results_ar = retriever_ar.get_relevant_documents(query)
148
+ context_ar_list = []
149
+ for _, result in enumerate(results_ar[:top_k], start=1):
150
+ context_ar_list.append(result.page_content)
151
+ return context_ar_list
152
+
153
+
154
+ #########################
155
+ # 2. PROMPT & LLM (AR) #
156
+ #########################
157
+
158
+ prompt_template_ar = PromptTemplate(
159
+ input_variables=["context", "query"],
160
+ template=(
161
+ """[SYSTEM]
162
+ أنت مساعد لخدمة عملاء INWI، محترف وخبير ومتعاون. تتقن التعامل مع استفسارات ومشاكل العملاء.
163
+ استند فقط إلى المعلومات المتوفرة في السياقات التالية دون اختراع معلومات غير موجودة:
164
+ - استخدم تحية مهذبة وودّية، على سبيل المثال: "مرحباً، أنا المساعد الذكي من إنوي. كيف يمكنني خدمتك اليوم؟"
165
+ - تعرّف على احتياج العميل واطلب التوضيح إذا لزم الأمر بالاعتماد على المعلومات المتوفرة فقط.
166
+ - إن لم يكن السؤال ضمن سياق إنوي، أخبر العميل بلطف أنك غير قادر على الإجابة خارج سياق إنوي.
167
+ - إذا لم تجد إجابة واضحة في السياق، يمكنك إبلاغ العميل بعدم توفر المعلومات واقتراح الاتصال بخدمة العملاء على الرقم 120.
168
+ - احرص على أن تكون ردودك موجزة وفعالة. وتجنّب اختلاق أي تفاصيل غير موجودة في السياق.
169
+ - أخبر العميل بأنه يمكنه التواصل معك مجدداً لمزيد من المساعدة.
170
+ - لا تتحدث عن المنافسين الذين يقدمون نفس خدمات إنوي.
171
+ - امتنع تماماً عن أي إهانة أو رد على إهانة.
172
+ - لا تطلب أي معلومات شخصية أو هوية العميل.
173
+ - وجّه العميل إلى كتالوج موقع إنوي إذا كان سؤاله يتعلق بعروض من الكتالوج.
174
+ - قدّم حلولاً قياسية للمشكلات التقنية مع عرض الخيارات المتاحة.
175
+ - قبل إرسال الجواب، تجنب أي تنسيق مثل "[Action] [نص]" واحتفظ فقط بالمعلومات المفيدة.
176
+ - لا تتحدث عن المواضيع التالية إطلاقاً: [
177
+ "السياسة", "الانتخابات", "الأحزاب", "الحكومة", "القوانين", "الإصلاحات",
178
+ "الدين", "العقائد", "الممارسات الدينية", "علم اللاهوت",
179
+ "الأخلاق", "الجدل", "الفلسفة", "المعايير", "التمييز",
180
+ "المنافسة", "مقارنة إنوي مع شركات أخرى",
181
+ "الأمن", "الاحتيال", "الصحة", "الأدوية", "التشخيص الطبي",
182
+ "التمويل", "الاستثمار", "البورصة", "العملات الرقمية", "البنوك", "التأمين",
183
+ "العنف", "الكراهية", "المحتوى الفاضح", "الجنس",
184
+ "المخالفات القانونية", "الوثائق المزورة", "البث غير الشرعي"
185
+ ]
186
+ إنوي (INWI) هي شركة اتصالات مغربية تقدم خدمات الهاتف المحمول والإنترنت وحلول الاتصالات للأفراد والشركات.
187
+ تتميز بالتزامها بتوفير خدمات عالية الجودة ومبتكرة، والمساهمة في التطور الرقمي في المغرب.
188
+ العملاء هم أولويتنا، وهدفنا مساعدتهم وحل مشاكلهم.
189
+ دورك هو تقديم خدمة عملاء احترافية وفعالة بدون اختراع معلومات من خارج السياق.
190
+
191
+ [السياق]
192
+ {context}
193
+
194
+ [سؤال العميل]
195
+ {query}
196
+
197
+ [الإجابة]"""
198
+ )
199
+ )
200
+
201
+ # Configuration du LLM HuggingFace (AR)
202
+ os.environ["HUGGINGFACEHUB_API_TOKEN"]
203
+ llm_ar = HuggingFaceHub(
204
+ repo_id="MBZUAI-Paris/Atlas-Chat-9B",
205
+ model_kwargs={
206
+ "temperature": 0.5,
207
+ "max_length": 500
208
+ }
209
+ )
210
+
211
+ # Chaîne AR
212
+ llm_chain_ar = LLMChain(llm=llm_ar, prompt=prompt_template_ar)
213
+
214
+
215
+ #########################
216
+ # 3. STREAMLIT MAIN APP #
217
+ #########################
218
+
219
+ def main():
220
+ st.subheader("INWI IA Chatbot - Arabe")
221
+
222
+ # Read local image and convert to Base64
223
+ img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
224
+ css_logo = f"""
225
+ <style>
226
+ [data-testid="stSidebarNav"]::before {{
227
+ content: "";
228
+ display: block;
229
+ margin: 0 auto 20px auto;
230
+ width: 80%;
231
+ height: 100px;
232
+ background-image: url("data:image/png;base64,{img_base64}");
233
+ background-size: contain;
234
+ background-repeat: no-repeat;
235
+ background-position: center;
236
+ }}
237
+ </style>
238
+ """
239
+
240
+ st.markdown(css_logo, unsafe_allow_html=True)
241
+
242
+ if "retriever_ar" not in st.session_state:
243
+ st.session_state["retriever_ar"] = None
244
+
245
+ st.sidebar.subheader("Vector Store Options (AR)")
246
+
247
+ if st.sidebar.button("Créer la Vector Store (AR)"):
248
+ with st.spinner("Extraction et création de la vector store AR..."):
249
+ excel_path = "Chatbot myinwi.xlsx"
250
+ persist_directory_ar = "./chroma_db_ar"
251
+ vectorstore_ar = load_excel_and_create_vectorstore_ar(
252
+ excel_path=excel_path,
253
+ persist_dir=persist_directory_ar
254
+ )
255
+ st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
256
+ search_type="mmr",
257
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
258
+ )
259
+ st.success("Vector store FR créée et chargée avec succès !")
260
+
261
+ if st.sidebar.button("Charger la Vector Store existante (AR)"):
262
+ with st.spinner("Chargement de la vector store FR existante..."):
263
+ persist_directory_ar = "./chroma_db_ar"
264
+ vectorstore_ar = load_existing_vectorstore_ar(persist_directory_ar)
265
+ st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
266
+ search_type="mmr",
267
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
268
+ )
269
+ st.success("Vector store AR chargée avec succès !")
270
+
271
+ st.write("""مرحباً! أنا هنا للإجابة على جميع أسئلتك المتعلقة بخدمات إنوي
272
+ وعروض الهاتف المحمول والإنترنت، وأي حلول أخرى قد تناسب احتياجاتك (AR).""")
273
+
274
+ user_query_ar = st.chat_input("Posez votre question ici (AR)...")
275
+
276
+ if user_query_ar:
277
+ if not st.session_state["retriever_ar"]:
278
+ st.warning("Veuillez d'abord créer ou charger la Vector Store (AR).")
279
+ return
280
+
281
+ # Récupération du contexte
282
+ context_ar_list = retrieve_context_ar(st.session_state["retriever_ar"], user_query_ar, top_k=5)
283
+
284
+ if context_ar_list:
285
+ with st.spinner("Génération de la réponse..."):
286
+ response_ar = llm_chain_ar.run({"context": "\n".join(context_ar_list), "query": user_query_ar})
287
+ response_ar = response_ar.split("[الإجابة]")[-1]
288
+ st.write("**سؤال العميل:**")
289
+ st.write(user_query_ar)
290
+ st.write("**الإجابة:**")
291
+ st.write(response_ar)
292
+ else:
293
+ st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
294
+
295
+ if __name__ == "__main__":
296
+ main()
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ langchain
4
+ huggingface_hub
5
+ torch
6
+ langchain_community
7
+ openpyxl
8
+ sentence_transformers
9
+ pysqlite3-binary
10
+ chromadb==0.4.0
11
+ langchain-chroma==0.1.4