Spaces:
Runtime error
Runtime error
add files
Browse files- app_gradio.py +71 -0
- basesrc/__pycache__/strategies.cpython-310.pyc +0 -0
- basesrc/strategies.py +64 -0
- chatpdf.py +178 -0
- main.py +76 -0
- prompt_query.py +53 -0
- requirements.txt +7 -0
app_gradio.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import gradio as gr
|
3 |
+
import tiktoken
|
4 |
+
import os
|
5 |
+
import numpy as np
|
6 |
+
import requests
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
from sentence_transformers import SentenceTransformer
|
9 |
+
from langchain_community.chat_models import ChatOllama
|
10 |
+
from basesrc.strategies import convert_pdf_to_text, split_pages_into_chunks, vectorize
|
11 |
+
from prompt_query import generate_prompt
|
12 |
+
|
13 |
+
load_dotenv()
|
14 |
+
CACHE_FOLDER = os.environ["CACHE_FOLDER"]
|
15 |
+
CACHE_FOLDER = "./cache" # si toutefois (¨_^)
|
16 |
+
|
17 |
+
def create_interface(pdf_url, embedding_model, llm_model, top_k=5):
|
18 |
+
os.makedirs(CACHE_FOLDER, exist_ok=True)
|
19 |
+
response = requests.get(pdf_url)
|
20 |
+
pdf_filename = pdf_url.split('/')[-1]
|
21 |
+
pdf_path = os.path.join(CACHE_FOLDER, 'document.pdf')
|
22 |
+
with open(pdf_path, 'wb') as f:
|
23 |
+
f.write(response.content)
|
24 |
+
|
25 |
+
pages = convert_pdf_to_text(pdf_path)
|
26 |
+
print(
|
27 |
+
"""
|
28 |
+
Je suis Llama3, de l'équipe DREAMS TEAM, votre assistant QA pour répondre à vos questions liés aux documents 🙂 !
|
29 |
+
Déjà pour info, le nombre de pages de vote document est: """, len(pages)
|
30 |
+
)
|
31 |
+
tokenizer = tiktoken.get_encoding("cl100k_base")
|
32 |
+
chunks = split_pages_into_chunks(pages, 128, tokenizer)
|
33 |
+
embedding_model = SentenceTransformer(embedding_model)
|
34 |
+
|
35 |
+
knowledge_base = vectorize(chunks, embedding_model)
|
36 |
+
chunks, embeddings = list(zip(*knowledge_base))
|
37 |
+
corpus_embeddings = np.vstack(embeddings)
|
38 |
+
|
39 |
+
llm_model = ChatOllama(model=llm_model)
|
40 |
+
|
41 |
+
def chat_interface(question):
|
42 |
+
return generate_prompt(question, chunks, corpus_embeddings, embedding_model, llm_model, top_k)
|
43 |
+
|
44 |
+
examples = [
|
45 |
+
["Quel est ton rôle ? "],
|
46 |
+
["Que nous parle ce document ?"],
|
47 |
+
["Fais-moi un résumé des éléments essentiels de ce document"],
|
48 |
+
["Comment puis-je présenter ce document dans une réunion d'équipe de professionnels ?"]
|
49 |
+
]
|
50 |
+
|
51 |
+
iface = gr.Interface(
|
52 |
+
fn=chat_interface,
|
53 |
+
inputs=gr.Textbox(lines=3, label="✨Posez votre question en français 😎", placeholder="Posez une question..."),
|
54 |
+
outputs=gr.Textbox(label="Réponse"),
|
55 |
+
title=f"Assistant de chat sur le document | {os.path.basename(pdf_filename)} | by PSW",
|
56 |
+
description="Posez votre question en français et obtenez une réponse basée sur le contexte du document.",
|
57 |
+
examples=examples
|
58 |
+
)
|
59 |
+
|
60 |
+
iface.launch(share=True)
|
61 |
+
|
62 |
+
if __name__ == "__main__":
|
63 |
+
create_interface(
|
64 |
+
"https://hellofuture.orange.com/app/uploads/2024/05/2024-Orange-white-paper-on-Mobile-Network-Technology-Evolutions-Beyond-2030.pdf",
|
65 |
+
"Sahajtomar/french_semantic",
|
66 |
+
"llama3"
|
67 |
+
)
|
68 |
+
|
69 |
+
""" RUN
|
70 |
+
python app_gradio.py
|
71 |
+
"""
|
basesrc/__pycache__/strategies.cpython-310.pyc
ADDED
Binary file (2.17 kB). View file
|
|
basesrc/strategies.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import fitz # PyMuPDF
|
3 |
+
import numpy as np
|
4 |
+
import itertools as it
|
5 |
+
import operator as op
|
6 |
+
from typing import List, Tuple
|
7 |
+
from sentence_transformers import SentenceTransformer
|
8 |
+
|
9 |
+
|
10 |
+
"""
|
11 |
+
Sur la partie strategies.py nous allons :
|
12 |
+
1. convertir notre pdf en texte
|
13 |
+
2. diviser le texte en plusieur paragraphes (chunks dans le code)
|
14 |
+
3. transformer le texte en vecteur (embeddings)
|
15 |
+
4. Aller chercher le bon candicat (trouver la réponse pertinente de l'utilisateur dans le pdf)
|
16 |
+
|
17 |
+
"""
|
18 |
+
|
19 |
+
def convert_pdf_to_text(pdf_data: str) -> List[str]:
|
20 |
+
document = fitz.open(pdf_data)
|
21 |
+
accumulator: List[str] = []
|
22 |
+
for page_num in range(len(document)):
|
23 |
+
page = document[page_num]
|
24 |
+
text = page.get_text()
|
25 |
+
accumulator.append(text)
|
26 |
+
document.close()
|
27 |
+
return accumulator
|
28 |
+
|
29 |
+
def split_pages_into_chunks(pages: List[str], chunk_size: int, tokenizer) -> List[str]:
|
30 |
+
page_tokens: List[List[int]] = [tokenizer.encode(page) for page in pages]
|
31 |
+
document_tokens = list(it.chain(*page_tokens))
|
32 |
+
|
33 |
+
nb_tokens = len(document_tokens)
|
34 |
+
nb_partitions = round(nb_tokens / chunk_size)
|
35 |
+
|
36 |
+
accumulator: List[str] = []
|
37 |
+
|
38 |
+
for chunck_tokens in np.array_split(document_tokens, nb_partitions):
|
39 |
+
paragraph = tokenizer.decode(chunck_tokens)
|
40 |
+
accumulator.append(paragraph)
|
41 |
+
|
42 |
+
return accumulator
|
43 |
+
|
44 |
+
def vectorize(chunks: List[str], transformer: SentenceTransformer, device: str = 'cpu') -> List[Tuple[str, np.ndarray]]:
|
45 |
+
embeddings = transformer.encode(
|
46 |
+
sentences=chunks,
|
47 |
+
batch_size=32,
|
48 |
+
device=device
|
49 |
+
,show_progress_bar=True,
|
50 |
+
)
|
51 |
+
|
52 |
+
return list(zip(chunks, embeddings))
|
53 |
+
|
54 |
+
def find_candidates(query_embedding: np.ndarray, chunks: List[str], corpus_embeddings: np.ndarray, top_k: int = 5) -> List[str]:
|
55 |
+
dot_product = query_embedding @ corpus_embeddings.T
|
56 |
+
norms = np.linalg.norm(query_embedding) * np.linalg.norm(corpus_embeddings, axis=1)
|
57 |
+
|
58 |
+
weighted_scores = dot_product / norms # Formule de cosine similarity cos(u,v) = u.v / |u||v|
|
59 |
+
|
60 |
+
zipped_chunks_scores = list(zip(chunks, weighted_scores))
|
61 |
+
sorted_chunks_scores = sorted(zipped_chunks_scores, key=op.itemgetter(1), reverse=True)
|
62 |
+
selected_candidates = sorted_chunks_scores[:top_k]
|
63 |
+
|
64 |
+
return list(map(op.itemgetter(0), selected_candidates))
|
chatpdf.py
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import click
|
3 |
+
import fitz # PyMuPDF
|
4 |
+
import numpy as np
|
5 |
+
import itertools as it
|
6 |
+
import operator as op
|
7 |
+
import requests
|
8 |
+
import tiktoken
|
9 |
+
from typing import List, Tuple
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
from sentence_transformers import SentenceTransformer
|
12 |
+
from langchain_community.chat_models import ChatOllama
|
13 |
+
from langchain_core.output_parsers import StrOutputParser
|
14 |
+
from langchain_core.prompts import ChatPromptTemplate
|
15 |
+
|
16 |
+
load_dotenv()
|
17 |
+
protocol_buffers_python_implementation = os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"]
|
18 |
+
CACHE_FOLDER = os.environ["CACHE_FOLDER"]
|
19 |
+
CACHE_FOLDER = "./cache" # Chemin du dossier cache
|
20 |
+
|
21 |
+
# Fonctions existantes pour convertir les PDFs et diviser les pages en chunks
|
22 |
+
def convert_pdf_to_text(pdf_data: str) -> List[str]:
|
23 |
+
document = fitz.open(pdf_data)
|
24 |
+
accumulator: List[str] = []
|
25 |
+
for page_num in range(len(document)):
|
26 |
+
page = document[page_num]
|
27 |
+
text = page.get_text()
|
28 |
+
accumulator.append(text)
|
29 |
+
document.close()
|
30 |
+
return accumulator
|
31 |
+
|
32 |
+
def split_pages_into_chunks(pages: List[str], chunk_size: int, tokenizer) -> List[str]:
|
33 |
+
page_tokens: List[List[int]] = [tokenizer.encode(page) for page in pages]
|
34 |
+
document_tokens = list(it.chain(*page_tokens))
|
35 |
+
|
36 |
+
nb_tokens = len(document_tokens)
|
37 |
+
nb_partitions = round(nb_tokens / chunk_size)
|
38 |
+
|
39 |
+
accumulator: List[str] = []
|
40 |
+
|
41 |
+
for chunck_tokens in np.array_split(document_tokens, nb_partitions):
|
42 |
+
paragraph = tokenizer.decode(chunck_tokens)
|
43 |
+
accumulator.append(paragraph)
|
44 |
+
|
45 |
+
return accumulator
|
46 |
+
|
47 |
+
def vectorize(chunks: List[str], transformer: SentenceTransformer, device: str = 'cpu') -> List[Tuple[str, np.ndarray]]:
|
48 |
+
embeddings = transformer.encode(
|
49 |
+
sentences=chunks,
|
50 |
+
batch_size=32,
|
51 |
+
device=device
|
52 |
+
,show_progress_bar=True,
|
53 |
+
)
|
54 |
+
|
55 |
+
return list(zip(chunks, embeddings))
|
56 |
+
|
57 |
+
def find_candidates(query_embedding: np.ndarray, chunks: List[str], corpus_embeddings: np.ndarray, top_k: int = 5) -> List[str]:
|
58 |
+
dot_product = query_embedding @ corpus_embeddings.T
|
59 |
+
norms = np.linalg.norm(query_embedding) * np.linalg.norm(corpus_embeddings, axis=1)
|
60 |
+
|
61 |
+
weighted_scores = dot_product / norms # cosine similarity
|
62 |
+
|
63 |
+
zipped_chunks_scores = list(zip(chunks, weighted_scores))
|
64 |
+
sorted_chunks_scores = sorted(zipped_chunks_scores, key=op.itemgetter(1), reverse=True)
|
65 |
+
selected_candidates = sorted_chunks_scores[:top_k]
|
66 |
+
|
67 |
+
return list(map(op.itemgetter(0), selected_candidates))
|
68 |
+
|
69 |
+
|
70 |
+
def generate_prompt(question, chunks, corpus_embeddings, embedding_model, llm_model, top_k, document_type: str = "ce pdf"):
|
71 |
+
query_embedding = embedding_model.encode([question])[0]
|
72 |
+
candidates = find_candidates(query_embedding=query_embedding,
|
73 |
+
chunks=chunks,
|
74 |
+
corpus_embeddings=corpus_embeddings,
|
75 |
+
top_k=top_k)
|
76 |
+
context = "\n".join(candidates)
|
77 |
+
prompt = f"""
|
78 |
+
ROLE: Tu es un assistant QA. Tu as été crée par Papa Séga de Orange INNVO.
|
79 |
+
Ton rôle est d'aider les utilisateurs à trouver la bonne réponse.
|
80 |
+
FONCTIONNEMENT:
|
81 |
+
Voici un ensemble de documents dans {document_type}: {context}.
|
82 |
+
Tu dois analyser ces documents pour répondre à la question de l'utilisateur.
|
83 |
+
Tu dois répondre aux utilisateurs uniquement en français.
|
84 |
+
Tu ne dois pas répondre en anglais.
|
85 |
+
Vérifie bien que la question est en rapport avec ces documents :
|
86 |
+
-SI OUI ALORS :
|
87 |
+
- construit une réponse.
|
88 |
+
- tu peux reformuler la réponse
|
89 |
+
- SI NON ALORS:
|
90 |
+
- Si la question est en rapport avec ton fonctionnement alors:
|
91 |
+
- rappelle-lui que tu es juste un modèle de QA et ne réponds pas à la question.
|
92 |
+
Si Non Alors:
|
93 |
+
- il ne faut jamais répondre à cette question qui a été posée par l'utilisateur !
|
94 |
+
RAPPEL :
|
95 |
+
Ton objectif est d'aider l'utilisateur à trouver les réponses pertinentes sur ces questions !
|
96 |
+
|
97 |
+
Question de l'utilisateur : {question}
|
98 |
+
SI la question posée est hors contexte,
|
99 |
+
- ALORS informe l'utilisateur que la question est hors contexte et que tu ne pourras lui donner une réponse.
|
100 |
+
|
101 |
+
Attention
|
102 |
+
- Tu dois être courtois avec les utilisateurs
|
103 |
+
- Tu n'as pas le droit de faire des hallucinations.
|
104 |
+
|
105 |
+
Réponds à la question en te basant UNIQUEMENT sur le contexte suivant :\n{context}\nQuestion : {question}
|
106 |
+
|
107 |
+
"""
|
108 |
+
|
109 |
+
chain_prompt = ChatPromptTemplate.from_template(prompt)
|
110 |
+
chain = chain_prompt | llm_model | StrOutputParser()
|
111 |
+
|
112 |
+
response = chain.invoke({"topic": question})
|
113 |
+
|
114 |
+
return response
|
115 |
+
|
116 |
+
@click.command()
|
117 |
+
@click.option('--pdf_url', required=True, help='URL du fichier PDF à télécharger')
|
118 |
+
@click.option('--pdf_path', default='document.pdf', help='Chemin local pour enregistrer le fichier PDF téléchargé')
|
119 |
+
@click.option('--embedding_model', required=True, help="Modèle d'embedding à utiliser")
|
120 |
+
@click.option('--llm_model', required=True, help='Modèle LLM à utiliser')
|
121 |
+
@click.option('--top_k', type=int, default=5, help='Nombre de candidats pour la recherche de similarité')
|
122 |
+
def main(pdf_url, pdf_path, embedding_model, llm_model, top_k):
|
123 |
+
os.makedirs(CACHE_FOLDER, exist_ok=True)
|
124 |
+
|
125 |
+
response = requests.get(pdf_url)
|
126 |
+
with open(pdf_path, 'wb') as f:
|
127 |
+
f.write(response.content)
|
128 |
+
|
129 |
+
pages = convert_pdf_to_text(pdf_path)
|
130 |
+
print(
|
131 |
+
"""
|
132 |
+
Je suis Llama3, de l'équipe DREAMS TEAM, votre assistant QA pour répondre à vos questions liés aux documents 🙂 !
|
133 |
+
Déjà pour info, le nombre de pages de vote document est: """, len(pages)
|
134 |
+
)
|
135 |
+
tokenizer = tiktoken.get_encoding("cl100k_base")
|
136 |
+
chunks = split_pages_into_chunks(pages, 128, tokenizer)
|
137 |
+
|
138 |
+
embedding_model = SentenceTransformer(embedding_model)
|
139 |
+
|
140 |
+
knowledge_base = vectorize(chunks, embedding_model)
|
141 |
+
chunks, embeddings = list(zip(*knowledge_base))
|
142 |
+
corpus_embeddings = np.vstack(embeddings)
|
143 |
+
|
144 |
+
llm_model = ChatOllama(model=llm_model)
|
145 |
+
print('📑 Voici le contenu de la première page du document 😎:\n', pages[0])
|
146 |
+
|
147 |
+
keep_looping = True
|
148 |
+
while keep_looping:
|
149 |
+
try:
|
150 |
+
question = input("Entrez votre question ✍️ | (ou tapez 'exit' pour quitter) ✨: ")
|
151 |
+
if question.lower() == 'exit':
|
152 |
+
break
|
153 |
+
|
154 |
+
response = generate_prompt(question, chunks, corpus_embeddings, embedding_model, llm_model, top_k)
|
155 |
+
print(response)
|
156 |
+
|
157 |
+
except KeyboardInterrupt:
|
158 |
+
print("\nFin de la session de chat 👋.")
|
159 |
+
keep_looping = False
|
160 |
+
|
161 |
+
if __name__ == "__main__":
|
162 |
+
main()
|
163 |
+
|
164 |
+
|
165 |
+
"""
|
166 |
+
export CACHE_FOLDER="./cache" #"/home/pswia/veileAI/volume_models_cache"
|
167 |
+
|
168 |
+
python chatpdf.py --pdf_url "https://diomayepresident.org/wp-content/uploads/2024/03/Programme-Diomaye-President.pdf" --embedding_model "Sahajtomar/french_semantic" --llm_model "llama3"
|
169 |
+
|
170 |
+
python chatpdf.py --pdf_url "https://arxiv.org/pdf/2302.09928" --embedding_model "Sahajtomar/french_semantic" --llm_model "llama3"
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
|
175 |
+
python chatpdf.py --pdf_url "https://hellofuture.orange.com/app/uploads/2024/05/2024-Orange-white-paper-on-Mobile-Network-Technology-Evolutions-Beyond-2030.pdf" --embedding_model "Sahajtomar/french_semantic" --llm_model "llama3" --top_k 5
|
176 |
+
|
177 |
+
|
178 |
+
"""
|
main.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import click
|
2 |
+
import numpy as np
|
3 |
+
import os
|
4 |
+
import tiktoken
|
5 |
+
from typing import List, Tuple
|
6 |
+
import requests
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
from sentence_transformers import SentenceTransformer
|
9 |
+
from langchain_community.chat_models import ChatOllama
|
10 |
+
from basesrc.strategies import convert_pdf_to_text, split_pages_into_chunks, vectorize
|
11 |
+
from prompt_query import generate_prompt
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
load_dotenv()
|
16 |
+
CACHE_FOLDER = os.environ["CACHE_FOLDER"]
|
17 |
+
CACHE_FOLDER = "./cache" # si toutefois (¨_^)
|
18 |
+
|
19 |
+
|
20 |
+
@click.command()
|
21 |
+
@click.option('--pdf_url', required=True, help='URL du fichier PDF à télécharger')
|
22 |
+
@click.option('--pdf_path', default='document.pdf', help='Chemin local pour enregistrer le fichier PDF téléchargé')
|
23 |
+
@click.option('--embedding_model', required=True, help="Modèle d'embedding à utiliser")
|
24 |
+
@click.option('--llm_model', required=True, help='Modèle LLM à utiliser')
|
25 |
+
@click.option('--top_k', type=int, default=5, help='Nombre de candidats pour la recherche de similarité')
|
26 |
+
def main(pdf_url:str, pdf_path:str, embedding_model:str, llm_model:str, top_k:int):
|
27 |
+
os.makedirs(CACHE_FOLDER, exist_ok=True)
|
28 |
+
|
29 |
+
response = requests.get(pdf_url)
|
30 |
+
with open(pdf_path, 'wb') as f:
|
31 |
+
f.write(response.content)
|
32 |
+
|
33 |
+
pages = convert_pdf_to_text(pdf_path)
|
34 |
+
print(
|
35 |
+
"""
|
36 |
+
Je suis Llama3, de l'équipe DREAMS TEAM, votre assistant QA pour répondre à vos questions liés aux documents 🙂 !
|
37 |
+
Déjà pour info, le nombre de pages de vote document est: """, len(pages)
|
38 |
+
)
|
39 |
+
tokenizer = tiktoken.get_encoding("cl100k_base")
|
40 |
+
chunks = split_pages_into_chunks(pages, 128, tokenizer)
|
41 |
+
|
42 |
+
embedding_model = SentenceTransformer(embedding_model)
|
43 |
+
|
44 |
+
knowledge_base = vectorize(chunks, embedding_model)
|
45 |
+
chunks, embeddings = list(zip(*knowledge_base))
|
46 |
+
corpus_embeddings = np.vstack(embeddings)
|
47 |
+
|
48 |
+
llm_model = ChatOllama(model=llm_model)
|
49 |
+
print('📑 Voici le contenu de la première page du document 😎:\n', pages[0])
|
50 |
+
|
51 |
+
keep_looping = True
|
52 |
+
while keep_looping:
|
53 |
+
try:
|
54 |
+
question = input("Entrez votre question ✍️ | (ou tapez 'exit' pour quitter) ✨: ")
|
55 |
+
if question.lower() == 'exit':
|
56 |
+
break
|
57 |
+
|
58 |
+
response = generate_prompt(question, chunks, corpus_embeddings, embedding_model, llm_model, top_k)
|
59 |
+
colored_response = f"Llama3 : {response}" # la réponse de Llama
|
60 |
+
print(colored_response)
|
61 |
+
|
62 |
+
except KeyboardInterrupt:
|
63 |
+
print("\nFin de la session de chat 👋.")
|
64 |
+
keep_looping = False
|
65 |
+
|
66 |
+
if __name__ == "__main__":
|
67 |
+
main()
|
68 |
+
|
69 |
+
|
70 |
+
"""
|
71 |
+
export CACHE_FOLDER="./cache"
|
72 |
+
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python3
|
73 |
+
|
74 |
+
python main.py --pdf_url "https://hellofuture.orange.com/app/uploads/2024/05/2024-Orange-white-paper-on-Mobile-Network-Technology-Evolutions-Beyond-2030.pdf" --embedding_model "Sahajtomar/french_semantic" --llm_model "llama3" --top_k 5
|
75 |
+
|
76 |
+
"""
|
prompt_query.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import tiktoken
|
3 |
+
import numpy as np
|
4 |
+
from langchain_core.prompts import ChatPromptTemplate
|
5 |
+
from langchain_core.output_parsers import StrOutputParser
|
6 |
+
from basesrc.strategies import find_candidates
|
7 |
+
|
8 |
+
|
9 |
+
def generate_prompt(question, chunks, corpus_embeddings, embedding_model, llm_model, top_k):
|
10 |
+
query_embedding = embedding_model.encode([question])[0]
|
11 |
+
candidates = find_candidates(query_embedding=query_embedding,
|
12 |
+
chunks=chunks,
|
13 |
+
corpus_embeddings=corpus_embeddings,
|
14 |
+
top_k=top_k)
|
15 |
+
context = "\n".join(candidates)
|
16 |
+
prompt = f"""
|
17 |
+
ROLE: Tu es un assistant QA. Tu as été crée par Papa Séga de Orange INNVO.
|
18 |
+
Ton rôle est d'aider les utilisateurs à trouver la bonne réponse.
|
19 |
+
FONCTIONNEMENT:
|
20 |
+
Voici un ensemble de documents dans : {context}.
|
21 |
+
Tu dois analyser ces documents pour répondre à la question de l'utilisateur.
|
22 |
+
Tu dois répondre aux utilisateurs uniquement en français.
|
23 |
+
Tu ne dois pas répondre en anglais.
|
24 |
+
Vérifie bien que la question est en rapport avec ces documents :
|
25 |
+
-SI OUI ALORS :
|
26 |
+
- construit une réponse.
|
27 |
+
- tu peux reformuler la réponse
|
28 |
+
- SI NON ALORS:
|
29 |
+
- Si la question est en rapport avec ton fonctionnement alors:
|
30 |
+
- rappelle-lui que tu es juste un modèle de QA et ne réponds pas à la question.
|
31 |
+
Si Non Alors:
|
32 |
+
- il ne faut jamais répondre à cette question qui a été posée par l'utilisateur !
|
33 |
+
RAPPEL :
|
34 |
+
Ton objectif est d'aider l'utilisateur à trouver les réponses pertinentes sur ces questions !
|
35 |
+
|
36 |
+
Question de l'utilisateur : {question}
|
37 |
+
SI la question posée est hors contexte,
|
38 |
+
- ALORS informe l'utilisateur que la question est hors contexte et que tu ne pourras lui donner une réponse.
|
39 |
+
|
40 |
+
Attention
|
41 |
+
- Tu dois être courtois avec les utilisateurs
|
42 |
+
- Tu n'as pas le droit de faire des hallucinations.
|
43 |
+
|
44 |
+
Réponds à la question en te basant UNIQUEMENT sur le contexte suivant :\n{context}\nQuestion : {question}
|
45 |
+
|
46 |
+
"""
|
47 |
+
|
48 |
+
chain_prompt = ChatPromptTemplate.from_template(prompt)
|
49 |
+
chain = chain_prompt | llm_model | StrOutputParser()
|
50 |
+
|
51 |
+
response = chain.invoke({"topic": question})
|
52 |
+
|
53 |
+
return response
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
PyMuPDF
|
2 |
+
tiktoken
|
3 |
+
sentencepiece
|
4 |
+
sentence-transformers
|
5 |
+
click
|
6 |
+
langchain_community
|
7 |
+
langchain_core
|