202401172050
This commit is contained in:
parent
efaa188404
commit
8f78f6c656
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,6 +4,8 @@ cera_hierarchy.html
|
||||
chromadb/
|
||||
chromadb_copie_20231230/
|
||||
chromadb_copie_20240107
|
||||
chromadb_copie_20240110/
|
||||
chromadb_copie_20240112/
|
||||
docs/
|
||||
gradio_cached_examples/
|
||||
index/
|
||||
@ -13,4 +15,4 @@ index_cera2_distiluse/
|
||||
__pycache__/
|
||||
chromadbtest/
|
||||
rag.log
|
||||
embedding.log
|
||||
embedding.log
|
||||
|
3
PAD
3
PAD
@ -66,4 +66,7 @@ pip install -U sentence-transformers
|
||||
|
||||
pip install streamlit
|
||||
|
||||
pip install rank-bm25
|
||||
|
||||
python -m streamlit run app.py
|
||||
|
||||
|
11
app.py
11
app.py
@ -6,10 +6,11 @@ import logging
|
||||
@st.cache_resource
|
||||
def init_rag():
|
||||
llm_model_path = '/Users/peportier/llm/a/a/zephyr-7b-beta.Q5_K_M.gguf'
|
||||
embed_model_name = 'intfloat/multilingual-e5-large'
|
||||
# embed_model_name = 'intfloat/multilingual-e5-large'
|
||||
embed_model_name = 'dangvantuan/sentence-camembert-large'
|
||||
collection_name = 'cera'
|
||||
chromadb_path = './chromadb'
|
||||
rag = RAG(llm_model_path, embed_model_name, collection_name, chromadb_path)
|
||||
rag = RAG(llm_model_path, embed_model_name, collection_name, chromadb_path, mulitlingual_e5=False)
|
||||
return rag
|
||||
|
||||
rag = init_rag()
|
||||
@ -31,7 +32,7 @@ if prompt := st.chat_input("Comment puis-je vous aider ?"):
|
||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
||||
with st.chat_message("user"):
|
||||
st.markdown(prompt)
|
||||
|
||||
|
||||
with st.chat_message("assistant"):
|
||||
message_placeholder = st.empty()
|
||||
full_response = ""
|
||||
@ -44,5 +45,5 @@ if prompt := st.chat_input("Comment puis-je vous aider ?"):
|
||||
# markdown_urls = "\n".join([f"- {url}" for url in urls])
|
||||
# logging.info(f"URLs: \n{markdown_urls}")
|
||||
# message_placeholder.markdown(markdown_urls)
|
||||
|
||||
st.session_state.messages.append({"role": "assistant", "content": full_response})
|
||||
|
||||
st.session_state.messages.append({"role": "assistant", "content": full_response})
|
||||
|
14
debug.py
14
debug.py
@ -18,4 +18,16 @@ ans2 = rag.chat(query2, stream=True)
|
||||
#
|
||||
# Quel est le risque de perte attaché à la détention de Parts Sociales ?
|
||||
#
|
||||
# Comment procéder pour déclarer un sinistre habitation ? ou Comment procéder pour déclarer un sinistre Visa Premier ?
|
||||
# Comment procéder pour déclarer un sinistre habitation ?
|
||||
# Comment procéder pour déclarer un sinistre Visa Premier ?
|
||||
|
||||
|
||||
# Combien de clients sociétaires compte la CERA ?
|
||||
|
||||
# Quel est le plafond d'impôts à ne pas dépasser pour pouvoir bénéficier d'un livret d'épargne populaire ?
|
||||
|
||||
# Mon client a 36 ans, peut-elle ouvrir un livret jeune ?
|
||||
|
||||
# Mon client a un revenu fiscal de 23000 euros, il est célibataire, peut-il ouvrir un livret d'épargne populaire ?
|
||||
|
||||
# Quel est le plafond du revenu fiscal de référence pour le livret d'épargne populaire ?
|
84
debug2.py
84
debug2.py
@ -8,8 +8,7 @@ import os
|
||||
html_folder_path = '../scrapcera/htmls/'
|
||||
txt_folder_path = '../scrapcera/docs/'
|
||||
|
||||
for html_filename in ['f6d921ced8.html']: # os.listdir(html_folder_path):
|
||||
|
||||
for html_filename in ['dd0a2ca5ac.html']: # os.listdir(html_folder_path):
|
||||
html_file_path = os.path.join(html_folder_path, html_filename)
|
||||
txt_filename = re.sub(r'\.html', '.txt', html_filename)
|
||||
txt_file_path = os.path.join(txt_folder_path, txt_filename)
|
||||
@ -34,44 +33,71 @@ for html_filename in ['f6d921ced8.html']: # os.listdir(html_folder_path):
|
||||
continue
|
||||
page_title = page_title_present.get_text()
|
||||
|
||||
sections = soup.find_all(lambda tag: tag.name in ['section'] and 'key-informations' not in tag.get('class', []))
|
||||
sections = soup.find_all(lambda tag: tag.name in ['section'])
|
||||
|
||||
struct_page = {'title': page_title}
|
||||
current_section = ''
|
||||
titles = [page_title]
|
||||
for section in sections:
|
||||
# breakpoint()
|
||||
if 'key-informations' in section.get('class', []):
|
||||
key_items = []
|
||||
for key_item in section.find_all('div', class_='container-block'):
|
||||
key_item_text = ''
|
||||
for key_item_title in key_item.find_all('div', class_='button'):
|
||||
key_item_text += key_item_title.get_text().strip()
|
||||
for key_item_desc in key_item.find_all('div', class_="tab-panel"):
|
||||
key_item_text += ' ' + key_item_desc.get_text().strip()
|
||||
if len(key_item_text) > 0:
|
||||
key_items.append(key_item_text)
|
||||
if len(key_items) > 0:
|
||||
struct_page['Les points clés'] = key_items
|
||||
continue
|
||||
|
||||
for wysiwyg_tag in section.find_all(class_="wysiwyg"):
|
||||
# Check for a title within the wysiwyg container
|
||||
internal_title = wysiwyg_tag.find(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) or wysiwyg_tag.find('p', class_='title')
|
||||
|
||||
# If no internal title, find the nearest title from previous siblings
|
||||
if not internal_title:
|
||||
# Find the nearest title from previous siblings
|
||||
nearest_title = None
|
||||
for previous in wysiwyg_tag.find_all_previous():
|
||||
if previous.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
||||
nearest_title = previous.get_text().strip()
|
||||
break
|
||||
if previous.name == 'p' and 'title' in previous.get('class', []):
|
||||
nearest_title = previous.get_text().strip()
|
||||
break
|
||||
if nearest_title:
|
||||
nearest_title = re.sub(r'\(\d\)', '', nearest_title)
|
||||
nearest_title = re.sub(r'^\d+\.\s*', '', nearest_title)
|
||||
current_section = nearest_title
|
||||
struct_page[current_section] = []
|
||||
else:
|
||||
continue
|
||||
if internal_title:
|
||||
title_tag = internal_title
|
||||
title = internal_title.get_text().strip()
|
||||
title = re.sub(r'\(\d\)', '', title)
|
||||
title = re.sub(r'^\d+\.\s*', '', title)
|
||||
titles.append(title)
|
||||
current_section = title
|
||||
else: # If no internal title, find the nearest title from previous tags
|
||||
title_tag = None
|
||||
current_section = titles[-1]
|
||||
|
||||
# nearest_title = None
|
||||
# for previous in wysiwyg_tag.find_all_previous():
|
||||
# if previous.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
||||
# nearest_title = previous.get_text().strip()
|
||||
# break
|
||||
# if previous.name == 'p' and 'title' in previous.get('class', []):
|
||||
# nearest_title = previous.get_text().strip()
|
||||
# break
|
||||
# if nearest_title:
|
||||
# title_tag = previous
|
||||
# nearest_title = re.sub(r'\(\d\)', '', nearest_title)
|
||||
# nearest_title = re.sub(r'^\d+\.\s*', '', nearest_title)
|
||||
# current_section = nearest_title
|
||||
# else:
|
||||
# continue
|
||||
|
||||
if current_section not in struct_page:
|
||||
struct_page[current_section] = []
|
||||
|
||||
for child in wysiwyg_tag.find_all(['p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']):
|
||||
if child == title_tag:
|
||||
continue
|
||||
if 'is-style-mentions' in child.get('class', []):
|
||||
continue
|
||||
text = child.get_text().strip()
|
||||
text = re.sub(r'\(\d\)', '', text)
|
||||
if child.name.startswith('h') or (child.name == 'p' and 'title' in child.get('class', [])):
|
||||
text = re.sub(r'^\d+\.\s*', '', text)
|
||||
current_section = text
|
||||
struct_page[current_section] = []
|
||||
else: # <p> not of class title, or <li>
|
||||
if 'is-style-mentions' not in child.get('class', []):
|
||||
if current_section in struct_page:
|
||||
struct_page[current_section].append(text)
|
||||
struct_page[current_section].append(text)
|
||||
|
||||
if len(struct_page[current_section]) == 0:
|
||||
del struct_page[current_section]
|
||||
|
||||
print(struct_page)
|
||||
|
153
embedding2.py
153
embedding2.py
@ -10,7 +10,8 @@ from bs4 import BeautifulSoup
|
||||
logging.basicConfig(filename='embedding.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
class EmbeddingModel:
|
||||
def __init__(self, model_name, chromadb_path, collection_name):
|
||||
def __init__(self, model_name, chromadb_path, collection_name, mulitlingual_e5=True):
|
||||
self.mulitlingual_e5=mulitlingual_e5
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
self.model = SentenceTransformer(model_name)
|
||||
self.chroma_client = chromadb.PersistentClient(path=chromadb_path)
|
||||
@ -46,12 +47,12 @@ class EmbeddingModel:
|
||||
"""
|
||||
|
||||
return f"passage: {title}\n" + '\n'.join(paragraphs)
|
||||
|
||||
|
||||
def embed_folder(self, html_folder_path, txt_folder_path):
|
||||
"""
|
||||
Embeds all the .html files within a specified folder into a ChromaDB collection using a specified embedding model.
|
||||
The txt folder is required to get the URL of the webpage. TODO: change this behavior in a future version.
|
||||
|
||||
The txt folder is required to get the URL of the webpage. TODO: change this behavior in a future version.
|
||||
|
||||
Args:
|
||||
html_folder_path (str): Path to the folder containing .html files.
|
||||
txt_folder_path (str): Path to the folder containing .txt files.
|
||||
@ -65,18 +66,18 @@ class EmbeddingModel:
|
||||
|
||||
for html_filename in os.listdir(html_folder_path):
|
||||
html_file_path = os.path.join(html_folder_path, html_filename)
|
||||
|
||||
|
||||
txt_filename = re.sub(r'\.html', '.txt', html_filename)
|
||||
txt_file_path = os.path.join(txt_folder_path, txt_filename)
|
||||
with open(txt_file_path, 'r') as file:
|
||||
txt_file_contents = file.read()
|
||||
|
||||
|
||||
url = txt_file_contents.split('\n')[0]
|
||||
if '?' in url: # URLs with a '?' corresponds to call to services and have no useful content
|
||||
continue
|
||||
if not url.startswith('https://www.caisse-epargne.fr/'):
|
||||
if not url.startswith('https://www.caisse-epargne.fr/rhone-alpes/'):
|
||||
continue
|
||||
|
||||
|
||||
prefix = 'https://www.caisse-epargne.fr/'
|
||||
suffix = url.replace(prefix, '')
|
||||
tags = suffix.split('/')
|
||||
@ -94,48 +95,61 @@ class EmbeddingModel:
|
||||
if not page_title_present:
|
||||
continue
|
||||
page_title = page_title_present.get_text()
|
||||
|
||||
sections = soup.find_all(lambda tag: tag.name in ['section'] and 'key-informations' not in tag.get('class', []))
|
||||
|
||||
|
||||
sections = soup.find_all(lambda tag: tag.name in ['section'])
|
||||
|
||||
struct_page = {'title': page_title}
|
||||
current_section = ''
|
||||
titles = [page_title]
|
||||
for section in sections:
|
||||
|
||||
if 'key-informations' in section.get('class', []):
|
||||
key_items = []
|
||||
for key_item in section.find_all('div', class_='container-block'):
|
||||
key_item_text = ''
|
||||
for key_item_title in key_item.find_all('div', class_='button'):
|
||||
key_item_text += key_item_title.get_text().strip()
|
||||
for key_item_desc in key_item.find_all('div', class_="tab-panel"):
|
||||
key_item_text += ' ' + key_item_desc.get_text().strip()
|
||||
if len(key_item_text) > 0:
|
||||
key_items.append(key_item_text)
|
||||
if len(key_items) > 0:
|
||||
struct_page['Les points clés'] = key_items
|
||||
continue
|
||||
|
||||
for wysiwyg_tag in section.find_all(class_="wysiwyg"):
|
||||
# Check for a title within the wysiwyg container
|
||||
internal_title = wysiwyg_tag.find(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) or wysiwyg_tag.find('p', class_='title')
|
||||
|
||||
# If no internal title, find the nearest title from previous tags
|
||||
if not internal_title:
|
||||
# Find the nearest title from previous tags
|
||||
nearest_title = None
|
||||
for previous in wysiwyg_tag.find_all_previous():
|
||||
if previous.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
||||
nearest_title = previous.get_text().strip()
|
||||
break
|
||||
if previous.name == 'p' and 'title' in previous.get('class', []):
|
||||
nearest_title = previous.get_text().strip()
|
||||
break
|
||||
if nearest_title:
|
||||
nearest_title = re.sub(r'\(\d\)', '', nearest_title)
|
||||
nearest_title = re.sub(r'^\d+\.\s*', '', nearest_title)
|
||||
current_section = nearest_title
|
||||
struct_page[current_section] = []
|
||||
else:
|
||||
continue
|
||||
|
||||
if internal_title:
|
||||
title_tag = internal_title
|
||||
title = internal_title.get_text().strip()
|
||||
title = re.sub(r'\(\d\)', '', title)
|
||||
title = re.sub(r'^\d+\.\s*', '', title)
|
||||
titles.append(title)
|
||||
current_section = title
|
||||
else: # If no internal title, find the nearest title from previous tags
|
||||
title_tag = None
|
||||
current_section = titles[-1]
|
||||
|
||||
if current_section not in struct_page:
|
||||
struct_page[current_section] = []
|
||||
|
||||
for child in wysiwyg_tag.find_all(['p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']):
|
||||
if child == title_tag:
|
||||
continue
|
||||
if 'is-style-mentions' in child.get('class', []):
|
||||
continue
|
||||
text = child.get_text().strip()
|
||||
text = re.sub(r'\(\d\)', '', text)
|
||||
if child.name.startswith('h') or (child.name == 'p' and 'title' in child.get('class', [])):
|
||||
text = re.sub(r'^\d+\.\s*', '', text)
|
||||
current_section = text
|
||||
struct_page[current_section] = []
|
||||
else: # <p> not of class title, or <li>
|
||||
if 'is-style-mentions' not in child.get('class', []):
|
||||
if current_section in struct_page:
|
||||
struct_page[current_section].append(text)
|
||||
struct_page[current_section].append(text)
|
||||
|
||||
if len(struct_page[current_section]) == 0:
|
||||
del struct_page[current_section]
|
||||
|
||||
logging.info(f"{html_filename} : Start")
|
||||
self.embed_page(html_filename, url, struct_page, tags)
|
||||
|
||||
|
||||
def token_length(self, text):
|
||||
"""
|
||||
Calculates the token length of a given text
|
||||
@ -149,7 +163,7 @@ class EmbeddingModel:
|
||||
This function takes a string, tokenizes the string, and returns the number of tokens.
|
||||
"""
|
||||
return len(self.tokenizer.encode(text, add_special_tokens=False))
|
||||
|
||||
|
||||
def passage_str(self, paragraphs, title, subtitle):
|
||||
"""
|
||||
Constructs a passage string from given paragraphs and a title.
|
||||
@ -162,25 +176,30 @@ class EmbeddingModel:
|
||||
Returns:
|
||||
str: A passage string that combines the titles and paragraphs.
|
||||
|
||||
This function takes a passage made of a list of paragraphs extracted
|
||||
from a webpage, the title of the webpage, the subtitle corresponding to
|
||||
the passage, and constructs a single string with the titles followed by
|
||||
This function takes a passage made of a list of paragraphs extracted
|
||||
from a webpage, the title of the webpage, the subtitle corresponding to
|
||||
the passage, and constructs a single string with the titles followed by
|
||||
the paragraphs, formatted for embedding.
|
||||
"""
|
||||
|
||||
return f"passage: {title}\n\n{subtitle}\n\n" + '\n'.join(paragraphs)
|
||||
|
||||
def embed_page(self, html_filename, url, struct_page, tags, max_chunk_size=512):
|
||||
if self.mulitlingual_e5:
|
||||
prefix = "passage: "
|
||||
else:
|
||||
prefix = ""
|
||||
return f"{prefix}{title}\n\n{subtitle}\n\n" + '\n'.join(paragraphs)
|
||||
|
||||
def embed_page(self, html_filename, url, struct_page, tags, max_chunk_size=500):
|
||||
documents = []
|
||||
title = struct_page['title']
|
||||
|
||||
|
||||
for subtitle, paragraphs in struct_page.items():
|
||||
if subtitle != 'title':
|
||||
doc_str = self.passage_str(paragraphs, title, subtitle)
|
||||
doc_token_length = self.token_length(doc_str)
|
||||
|
||||
|
||||
if doc_token_length > max_chunk_size:
|
||||
|
||||
|
||||
long_passages = []
|
||||
sub_paragraphs = []
|
||||
sub_paragraphs_token_length = 0
|
||||
paragraph_index = 0
|
||||
@ -190,26 +209,42 @@ class EmbeddingModel:
|
||||
sub_paragraphs_str = self.passage_str(sub_paragraphs, title, subtitle)
|
||||
sub_paragraphs_token_length = self.token_length(sub_paragraphs_str)
|
||||
paragraph_index += 1
|
||||
if paragraph_index == len(paragraphs):
|
||||
if paragraph_index >= len(paragraphs):
|
||||
if sub_paragraphs_token_length >= max_chunk_size:
|
||||
sub_paragraphs_str_1 = self.passage_str(sub_paragraphs[:-1], title, subtitle)
|
||||
sub_paragraphs_str_2 = self.passage_str([sub_paragraphs[-1]], title, subtitle)
|
||||
documents.append(sub_paragraphs_str_1)
|
||||
documents.append(sub_paragraphs_str_2)
|
||||
if self.token_length(sub_paragraphs_str_2) < max_chunk_size:
|
||||
documents.append(sub_paragraphs_str_2)
|
||||
else:
|
||||
long_passages.append(sub_paragraphs[0])
|
||||
else:
|
||||
sub_paragraphs_str = self.passage_str(sub_paragraphs, title, subtitle)
|
||||
documents.append(sub_paragraphs_str)
|
||||
documents.append(sub_paragraphs_str)
|
||||
break
|
||||
else:
|
||||
sub_paragraphs_str = self.passage_str(sub_paragraphs[:-1], title, subtitle)
|
||||
documents.append(sub_paragraphs_str)
|
||||
paragraph_index -= 1
|
||||
else: # sub_paragraphs_token_length >= max_chunk_size and paragraph_index < len(paragraphs)
|
||||
if len(sub_paragraphs) > 1:
|
||||
sub_paragraphs_str = self.passage_str(sub_paragraphs[:-1], title, subtitle)
|
||||
documents.append(sub_paragraphs_str)
|
||||
paragraph_index -= 1
|
||||
else:
|
||||
long_passages.append(sub_paragraphs[0])
|
||||
sub_paragraphs = []
|
||||
sub_paragraphs_token_length = 0
|
||||
for long_passage in long_passages:
|
||||
passage = []
|
||||
for word in long_passage.split():
|
||||
passage.append(word)
|
||||
passage_str = self.passage_str([' '.join(passage)], title, subtitle)
|
||||
if self.token_length(passage_str) > max_chunk_size:
|
||||
passage_str = self.passage_str([' '.join(passage[:-1])], title, subtitle)
|
||||
documents.append(passage_str)
|
||||
passage = [passage[-1]]
|
||||
passage_str = self.passage_str([' '.join(passage)], title, subtitle)
|
||||
documents.append(passage_str)
|
||||
|
||||
else:
|
||||
documents.append(doc_str)
|
||||
|
||||
|
||||
if len(documents) == 0:
|
||||
return
|
||||
|
||||
@ -235,4 +270,4 @@ class EmbeddingModel:
|
||||
|
||||
ids = [html_filename + '-' + str(i+1) for i in range(len(documents))]
|
||||
|
||||
self.collection.add(embeddings=embeddings, documents=documents, metadatas=metadatas, ids=ids)
|
||||
self.collection.add(embeddings=embeddings, documents=documents, metadatas=metadatas, ids=ids)
|
||||
|
11
embedding_script.py
Normal file
11
embedding_script.py
Normal file
@ -0,0 +1,11 @@
|
||||
from embedding2 import EmbeddingModel
|
||||
|
||||
# model_name = 'intfloat/multilingual-e5-large'
|
||||
model_name = 'dangvantuan/sentence-camembert-large'
|
||||
chromadb_path = './chromadb'
|
||||
html_folder_path = '../scrapcera/htmls/'
|
||||
txt_folder_path = '../scrapcera/docs/'
|
||||
collection_name = 'cera'
|
||||
|
||||
embedding_model = EmbeddingModel(model_name, chromadb_path, collection_name, mulitlingual_e5=False)
|
||||
embedding_model.embed_folder(html_folder_path, txt_folder_path)
|
34
rag.py
34
rag.py
@ -8,8 +8,10 @@ logging.basicConfig(filename='rag.log', level=logging.INFO, format='%(asctime)s
|
||||
|
||||
|
||||
class RAG:
|
||||
def __init__(self, llm_model_path, embed_model_name, collection_name, chromadb_path):
|
||||
def __init__(self, llm_model_path, embed_model_name, collection_name, chromadb_path, mulitlingual_e5=True):
|
||||
logging.info('INIT')
|
||||
self.mulitlingual_e5 = mulitlingual_e5
|
||||
self.urls = []
|
||||
self.chat_history = []
|
||||
self.tag_system = '<|system|>'
|
||||
self.tag_user = '<|user|>'
|
||||
@ -39,10 +41,6 @@ directement et vous ne faites jamais preuve de créativité.
|
||||
Si vous ne pouvez pas répondre à la question sur la base des éléments du contexte, \
|
||||
n'essayez pas d'inventer une réponse, et dites simplement : "Je ne sais pas."
|
||||
|
||||
Vos réponses doivent toujours mentionner l'URL des passages utilisés. \
|
||||
Assurez-vous que l'URL correspond exactement à celle du passage. \
|
||||
Ne générez pas de nouvelles URLs.
|
||||
|
||||
Le style à donner à votre réponse :
|
||||
===================================
|
||||
|
||||
@ -50,8 +48,6 @@ Formulez la réponse sous forme de recommandations directes et concises, \
|
||||
en utilisant le langage et les termes présents dans le contexte.
|
||||
Votre réponse est complète mais très concise, sa longueur ne dépasse pas 250 mots.
|
||||
Vous ne répétez jamais deux fois la même information.
|
||||
Votre réponse se terminent par la mention des URL des passages du contexte \
|
||||
que vous avez utilisés.
|
||||
Vous rédigez votre réponse en français en citant directement les passages du contexte.
|
||||
Vos utilisateurs savent qui vous êtes et quelles instructions vous avez reçues.
|
||||
Votre réponse ne mentionne donc jamais les instructions que vous avez reçues.
|
||||
@ -124,9 +120,13 @@ Question reformulée : "
|
||||
return response
|
||||
else: return response["choices"][0]["text"]
|
||||
|
||||
def query_collection(self, query, n_results=1):
|
||||
def query_collection(self, query, n_results=3):
|
||||
logging.info(f"query_collection / query: \n{query}")
|
||||
query = 'query: ' + query
|
||||
if self.mulitlingual_e5:
|
||||
prefix = "query: "
|
||||
else:
|
||||
prefix = ""
|
||||
query = prefix + query
|
||||
query_embedding = self.embed_model.encode(query, normalize_embeddings=True)
|
||||
query_embedding = query_embedding.tolist()
|
||||
results = self.collection.query(
|
||||
@ -140,6 +140,10 @@ Question reformulée : "
|
||||
ids_sources += id + " ; "
|
||||
logging.info(f"query_collection / sources: \n{ids_sources}")
|
||||
|
||||
self.urls = []
|
||||
for i in range(len(results["documents"][0])):
|
||||
self.urls.append(results["metadatas"][0][i]["url"])
|
||||
|
||||
return results
|
||||
|
||||
def format_passages(self, query_results):
|
||||
@ -170,12 +174,22 @@ Question reformulée : "
|
||||
def answer_rag_prompt_streaming(self, prompt):
|
||||
logging.info(f"answer_rag_prompt_streaming: \n{prompt}")
|
||||
ans = self.answer(prompt, stream=True)
|
||||
formated_urls = ''
|
||||
for url in list(set(self.urls)):
|
||||
formated_urls += f"* {url}\n"
|
||||
|
||||
yield self.prefix_assistant_prompt
|
||||
yield f"""
|
||||
### Réponse
|
||||
{self.prefix_assistant_prompt}
|
||||
"""
|
||||
for item in ans:
|
||||
item = item["choices"][0]["text"]
|
||||
self.chat_history[-1]['assistant'] += item
|
||||
yield item
|
||||
yield f"""
|
||||
### Sources
|
||||
{formated_urls}
|
||||
"""
|
||||
|
||||
def answer_rag_prompt_non_streaming(self, prompt):
|
||||
logging.info(f"answer_rag_prompt_non_streaming: \n{prompt}")
|
||||
|
548
rag_bm25.ipynb
Normal file
548
rag_bm25.ipynb
Normal file
@ -0,0 +1,548 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7290374f-d5b8-4670-b7fe-36a1293577ba",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from rank_bm25 import BM25Okapi\n",
|
||||
"import chromadb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "617ec7ec-0f30-402d-8e45-dc1f762d1409",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"chromadb_path = './chromadb'\n",
|
||||
"collection_name = 'cera'\n",
|
||||
"chroma_client = chromadb.PersistentClient(path=chromadb_path)\n",
|
||||
"collection = chroma_client.get_collection(name=collection_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "021e4cfa-49d9-460f-9b43-1524c327e4c6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"dict_keys(['ids', 'embeddings', 'metadatas', 'documents', 'uris', 'data'])"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"collection.get().keys()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "5ae7d104-f8f6-45ad-a2af-b8475e400262",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs = collection.get()['documents']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "96e8acca-e0dc-4dbd-9130-4f3ff64ba86a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ids = collection.get()['ids']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "536f29fa-9377-41a8-a9d4-a52ace99ed74",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"2896"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "8cf9b3e2-c960-4a88-8974-848611a4d5d7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs_tokenized = [doc.replace(\"\\n\", \" \").split(\" \") for doc in docs]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "87085da2-edac-4e9c-8586-1139ac814dcd",
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"jupyter": {
|
||||
"outputs_hidden": true
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['Le',\n",
|
||||
" 'paiement',\n",
|
||||
" 'sans',\n",
|
||||
" 'contact',\n",
|
||||
" '',\n",
|
||||
" 'Les',\n",
|
||||
" 'points',\n",
|
||||
" 'clés',\n",
|
||||
" '',\n",
|
||||
" 'Pratique',\n",
|
||||
" 'Plus',\n",
|
||||
" 'besoin',\n",
|
||||
" 'de',\n",
|
||||
" 'chercher',\n",
|
||||
" 'de',\n",
|
||||
" 'la',\n",
|
||||
" 'monnaie',\n",
|
||||
" 'pour',\n",
|
||||
" 'régler',\n",
|
||||
" 'votre',\n",
|
||||
" 'café,',\n",
|
||||
" 'parking,',\n",
|
||||
" 'repas,',\n",
|
||||
" 'journal…',\n",
|
||||
" 'Gratuit',\n",
|
||||
" 'Cette',\n",
|
||||
" 'fonctionnalité',\n",
|
||||
" 'est',\n",
|
||||
" 'ajoutée',\n",
|
||||
" 'automatiquement',\n",
|
||||
" 'et',\n",
|
||||
" 'gratuitement',\n",
|
||||
" 'à',\n",
|
||||
" 'votre',\n",
|
||||
" 'carte.',\n",
|
||||
" 'Rapide',\n",
|
||||
" 'Vous',\n",
|
||||
" 'réglez',\n",
|
||||
" 'votre',\n",
|
||||
" 'achat',\n",
|
||||
" 'd’un',\n",
|
||||
" 'simple',\n",
|
||||
" 'geste,',\n",
|
||||
" 'sans',\n",
|
||||
" 'insérer',\n",
|
||||
" 'votre',\n",
|
||||
" 'carte',\n",
|
||||
" 'ni',\n",
|
||||
" 'composer',\n",
|
||||
" 'votre',\n",
|
||||
" 'code',\n",
|
||||
" 'confidentiel.']"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs_tokenized[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "0ba5858d-9cdb-4159-a9ab-88b485a8991b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"bm25_db = BM25Okapi(docs_tokenized)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "c854970c-6e40-4209-9e02-a7e4cb9fb509",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<rank_bm25.BM25Okapi at 0x10c7be6d0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"bm25_db"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "61d10ce1-38fe-4d36-b8c6-1845c116b626",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query = \"Comment procéder pour déclarer un sinistre Visa Premier ?\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "8c362a7b-5d78-4ba5-be8f-165ca5e9e023",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_tokenized = query.split(\" \")\n",
|
||||
"doc_scores = bm25_db.get_scores(query_tokenized)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "99c5cc3d-5b30-478d-b3b8-c252dff6e1fc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"2896"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(doc_scores)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "e5c91dd9-ebda-42ba-9ac8-d7fec2be981e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ids_scores = dict(zip(ids, doc_scores))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"id": "5865d7f6-f10d-4c08-873d-89ae60059008",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"sorted_ids_scores = sorted(ids_scores.items(), key=lambda x: x[1], reverse=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"id": "1964be5f-8ab7-437c-986c-d5ef8039748d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'d42d7174ce.html-1': 21.04962022219261,\n",
|
||||
" '25dcc77e00.html-6': 18.00344127762922,\n",
|
||||
" '19864d414b.html-1': 16.616167931951313}"
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dict(sorted_ids_scores[:3])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"id": "f0ff43ff-2cb1-47a7-aaaf-f9dbecd041ff",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'ids': ['19864d414b.html-1', 'd42d7174ce.html-1', '25dcc77e00.html-6'],\n",
|
||||
" 'embeddings': None,\n",
|
||||
" 'metadatas': [{'category': 'professionnels',\n",
|
||||
" 'url': 'https://www.caisse-epargne.fr/rhone-alpes/professionnels/proteger-moi-mon-activite/numeros-utiles-contrat-assurance/'},\n",
|
||||
" {'category': 'assurer',\n",
|
||||
" 'url': 'https://www.caisse-epargne.fr/rhone-alpes/assurer/declaration-sinistre-intemperies/'},\n",
|
||||
" {'category': 'comptes-cartes',\n",
|
||||
" 'url': 'https://www.caisse-epargne.fr/rhone-alpes/comptes-cartes/carte-visa-premier/'}],\n",
|
||||
" 'documents': ['Numéros Utiles Contrat Assurance Professionnel\\n\\nVotre contrat Assurance Multirisque Professionnelle ou Assurance Auto des Professionnels\\n\\nUn numéro unique et non surtaxé est mis à votre disposition pour déclarer un sinistre ou mettre à jour votre contrat.Appelez-le\\xa0:\\nPour nous contacter par mail\\xa0:• Contrat Assurance Multirisque Professionnelle\\xa0:service-client.ird@aismail.fr• Contrat Assurance Auto des Professionnels\\xa0: service-client.auto@aismail.fr\\n',\n",
|
||||
" 'Intempéries, déclarer son sinistre en ligne !\\n\\nEn bref\\n\\nDécouvrez comment déclarer votre sinistre en ligne\\nLes conseils en cas de sinistre\\nTout savoir sur l’indemnisation\\nEn cas d’événement climatique, le besoin d’assistance ou de prise en charge est immédiat\\xa0!C’est pourquoi la Caisse d’Epargne vous facilite la déclaration de sinistre grâce à votre espace personnel internet. Si vous êtes détenteur d’un contrat d’assurance habitation ou automobile, il vous suffit de vous connecter à votre espace personnel et de procéder à la déclaration en ligne.',\n",
|
||||
" 'Carte Visa Premier\\n\\nEt si vous optiez pour la carte Visa Premier Izicarte associée à votre crédit renouvelable ?\\n\\nVotre carte Visa Premier Izicarte vous permet de disposer d’un crédit renouvelable pour faire face à des imprévus ou saisir des opportunités sans perdre de temps.\\nVous choisissez librement le mode de règlement que vous souhaitez :\\nAu comptant : par débit de votre compte de dépôt ;\\nÀ crédit, dans la limite du montant disponible sur votre crédit renouvelable.\\nGrâce à votre crédit renouvelable associé à votre carte Visa Premier Izicarte, vous pouvez étaler en plusieurs fois vos dépenses à crédit et choisir votre rythme de remboursement.\\nUn crédit vous engage et doit être remboursé. Vérifiez vos capacités de remboursement avant de vous engager.'],\n",
|
||||
" 'uris': None,\n",
|
||||
" 'data': None}"
|
||||
]
|
||||
},
|
||||
"execution_count": 34,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"collection.get(ids=['d42d7174ce.html-1', '25dcc77e00.html-6', '19864d414b.html-1'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 35,
|
||||
"id": "11a9a24e-3928-4b0b-85fe-d9c406fa80ed",
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"jupyter": {
|
||||
"outputs_hidden": true
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Les points clés\n",
|
||||
"\n",
|
||||
"Innovation Paiement sans contact(1) et paiement mobile compatible(2).\n",
|
||||
"Garanties d’Assurances et Assistance(3) Vous vous protégez ainsi que votre famille lors de vos déplacements en France et à l’étranger.\n",
|
||||
"Plafonds élevés Bénéficiez de souplesse grâce à des plafonds de paiement et retraits supérieurs à ceux d’une Visa Classic.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Carte Visa Premier, toujours à vos côtés\n",
|
||||
"\n",
|
||||
"Carte de paiement et de retrait utilisable en France et à l’étranger ;\n",
|
||||
"Des plafonds de paiement et de retrait élevés ;\n",
|
||||
"Des Garanties d’Assurances et d’Assistance incluses pour vous protéger, vous et votre famille, lors de vos déplacements en France et à l’étranger (une couverture assurances et assistance médicale dès que vous être à plus de 100kms de votre domicile);– Exemples de garanties :En voyage : modification ou annulation de voyage, responsabilité civile, rapatriement en cas de maladie ou d’accident, remboursement des frais médicaux à l’étranger, etc.Au ski : remboursement des frais médicaux en France, forfait et cours de ski non utilisésAvec un véhicule de location : accident ou volEn avion ou en train : perte, vol ou détérioration de bagages, etc.\n",
|
||||
"Un service de dépannage cash sous 48h à l’étranger ;\n",
|
||||
"Un retard ou une annulation de votre vol ? Votre accès à bord vous est refusé pour cause de surbooking ? AirRefund (partenaire de Visa), gère à votre place la demande d’indemnisation auprès de la compagnie aérienne. S’ajoute à l’indemnité de retard, le remboursement des frais engagés durant l’attente (repas, transferts, hôtels…)\n",
|
||||
"Un service de mise en opposition de la carte 24h/24 et 7j/7, en cas de perte, de vol ou d’utilisation frauduleuse de la carte ;\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Bénéficiez d’avantages supplémentaires\n",
|
||||
"\n",
|
||||
"Fonctionne en Sans Contact ;\n",
|
||||
"Compatible paiement mobile ;\n",
|
||||
"Choix possible entre débit différé ou débit immédiat ;\n",
|
||||
"Disponible avec un crédit avec la carte Visa Premier Izicarte associée au crédit renouvelable.\n",
|
||||
"Les paiements par carte sont rassemblés, et débités en une seule fois tous les mois à date fixe: c’est l’encours des paiements par carte. Le suivi de cet encours peut se faire à tout moment via la banque à distance (internet et mobile) et permet d’anticiper la provision nécessaire sur le compte.\n",
|
||||
"Le débit différé peut permettre une gestion mensuelle de son budget différente de celle en débit immédiat, puisque la trésorerie disponible sur le compte n’est affectée des paiements par carte qu’une fois par mois.\n",
|
||||
"\n",
|
||||
"Il est ainsi possible de gérer différemment les frais qui seront remboursés (frais médicaux, frais professionnels … ), ainsi que les dépenses importantes.\n",
|
||||
"Certaines agences de location de voiture à l’étranger préfèrent les cartes avec la mention « crédit », aux cartes marquées uniquement « débit ». Les cartes à débit différé comportent la mention « crédit ».\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Profitez des avantages Visa Premier(10)\n",
|
||||
"\n",
|
||||
"Pour en savoir plus sur les avantages dédiés aux porteurs de la carte Visa Premier, rendez-vous sur le site www.visa.fr et inscrivez-vous à la newsletter.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Bénéficiez de la garantie achat(11)\n",
|
||||
"\n",
|
||||
"Tous les achats réglés avec votre carte Visa Premier sont protégés contre la détérioration et le vol avec effraction ou par agression.La couverture s’élève au maximum à la valeur d’achat du bien.\n",
|
||||
"Valeur du bien : entre 75 € et 800 € TTC.\n",
|
||||
"Nombre de jours couverts (post acquisition/livraison) : 7 jours.\n",
|
||||
"Plafond de la garantie : 800 euros TTC par objet et 1 600 euros par an.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Et si vous optiez pour la carte Visa Premier Izicarte associée à votre crédit renouvelable ?\n",
|
||||
"\n",
|
||||
"Votre carte Visa Premier Izicarte vous permet de disposer d’un crédit renouvelable pour faire face à des imprévus ou saisir des opportunités sans perdre de temps.\n",
|
||||
"Vous choisissez librement le mode de règlement que vous souhaitez :\n",
|
||||
"Au comptant : par débit de votre compte de dépôt ;\n",
|
||||
"À crédit, dans la limite du montant disponible sur votre crédit renouvelable.\n",
|
||||
"Grâce à votre crédit renouvelable associé à votre carte Visa Premier Izicarte, vous pouvez étaler en plusieurs fois vos dépenses à crédit et choisir votre rythme de remboursement.\n",
|
||||
"Un crédit vous engage et doit être remboursé. Vérifiez vos capacités de remboursement avant de vous engager.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"L’Extension de Garantie Constructeur(11) (12)\n",
|
||||
"\n",
|
||||
"Une garantie constructeur allongée\n",
|
||||
"Avec votre carte Visa Premier Izicarte, vous bénéficiez en cas de panne d’une extension de garantie constructeur supplémentaire de 3 ans, sur les appareils électrodomestiques et produits nomades achetés neufs(11) (12) (électroménager, audiovisuel, pocket/image, informatique).\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"Carte Visa Premier\n",
|
||||
"\n",
|
||||
"Gérez votre carte bancaire depuis l’application Caisse d’Epargne Rhône Alpes\n",
|
||||
"\n",
|
||||
"Gérez vos plafonds ;\n",
|
||||
"Verrouillez temporairement/déverrouillez votre carte ;\n",
|
||||
"Activez/désactivez les paiements et retraits à distance et/ou à l’étranger ;\n",
|
||||
"Faites opposition 24h/24-7j/7 en cas de perte, de vol ou d’utilisation frauduleuse de votre carte.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"nb_passage = 1\n",
|
||||
"while True:\n",
|
||||
" id = \"25dcc77e00.html-\" + str(nb_passage)\n",
|
||||
" passages = collection.get(ids=[id])\n",
|
||||
" if len(passages['documents']) > 0:\n",
|
||||
" print(passages['documents'][0])\n",
|
||||
" print(\"\\n----------\\n\")\n",
|
||||
" nb_passage += 1\n",
|
||||
" else:\n",
|
||||
" break"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 52,
|
||||
"id": "427a2fa0-0f47-4571-94d8-3878ca034de4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query = \"Quel est le risque de perte en capital pour un sociétaire ?\"\n",
|
||||
"query_tokenized = query.split(\" \")\n",
|
||||
"doc_scores = bm25_db.get_scores(query_tokenized)\n",
|
||||
"ids_scores = dict(zip(ids, doc_scores))\n",
|
||||
"sorted_ids_scores = sorted(ids_scores.items(), key=lambda x: x[1], reverse=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 53,
|
||||
"id": "5251aa60-3831-416f-a7c6-f824c308587e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[('f6d921ced8.html-3', 29.446670121305353),\n",
|
||||
" ('ff83960e9d.html-1', 24.88880087849239),\n",
|
||||
" ('ecb10d12cf.html-2', 24.206423694569803),\n",
|
||||
" ('f753c94bb4.html-1', 24.009169917059968),\n",
|
||||
" ('dbdc338e94.html-7', 23.845667609823003),\n",
|
||||
" ('f66629c8b6.html-2', 23.779561367699497),\n",
|
||||
" ('9b9d084514.html-2', 23.770317569985096),\n",
|
||||
" ('6123ccc4f8.html-6', 23.732579312988157),\n",
|
||||
" ('c145d2aa96.html-4', 23.48215739435705),\n",
|
||||
" ('286a391488.html-2', 23.443469979811134)]"
|
||||
]
|
||||
},
|
||||
"execution_count": 53,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sorted_ids_scores[:10]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 51,
|
||||
"id": "b16129c2-e3fa-41ca-a60f-b5f596ede1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'ids': ['f6d921ced8.html-3'],\n",
|
||||
" 'embeddings': None,\n",
|
||||
" 'metadatas': [{'category': 'votre-banque',\n",
|
||||
" 'url': 'https://www.caisse-epargne.fr/rhone-alpes/votre-banque/nos-engagements/etre-societaire/'}],\n",
|
||||
" 'documents': ['Devenez sociétaire !\\n\\nQui peut devenir sociétaire ?\\n\\nSi vous souhaitez que la SLE rachète vos parts sociales, votre demande, soumise à l’autorisation du conseil d’administration de la SLE, doit être formulée avant le 31 mai, date de clôture de l’exercice. De ce fait, aucune assurance ne peut être donnée quant à la liquidité des parts sociales.\\n\\nPar ailleurs, les rachats de parts sociales sont subordonnés au respect du capital minimum en deçà duquel la SLE ne peut descendre. En conséquence, les sociétaires doivent être conscients qu’ils pourraient ne pas être en mesure de céder facilement leurs parts sociales.\\n\\nLe remboursement effectif, qu’il soit consécutif à la perte de la qualité de sociétaire ou à une demande de rachat, intervient le 1er jour ouvré du nouvel exercice ou dans un délai maximum de 3 mois suivant la demande pour les cas dérogatoires suivants :\\n\\n– Pour un particulier : changement de foyer fiscal, décès, divorce, invalidité, licenciement, départ à la retraite ou préretraite, transfert du domicile à l’étranger, déménagement hors du ressort territorial de la Caisse d’Epargne d’affiliation, redressement judiciaire du sociétaire, de clôture d’un livret A lorsque le client ne détient pas d’autres produits et tout évènement exceptionnel revêtant une gravité telle qu’elle contraigne le sociétaire à liquider tout ou partie de ses parts.\\n\\n– Pour une personne morale : redressement judiciaire, liquidation ou dissolution.\\n\\nLes parts sociales sont remboursées à leur valeur nominale sous réserve du risque investisseur (cf. paragraphe « Responsabilité – risque de perte en capital »)\\nVotre responsabilité d’investisseur est limitée au niveau de votre investissement. Le risque investisseur (risque de perte en capital) porte sur le Groupe BPCE et non sur la SLE ou la Caisse d’Epargne (du fait du mécanisme de solidarité interne au Groupe BPCE).\\n\\nLes parts sociales demeurent des instruments risqués. Votre responsabilité, limitée au montant de l’apport, est engagée jusqu’à 5 ans après le retrait. Il existe un risque de perte en capital en cas de défaut ou de faillite de la Caisse d’Epargne ou de mise en œuvre de mesures de résolution au sein du Groupe BPCE. Les parts sociales ne sont pas éligibles au mécanisme de garantie des investisseurs ou de garantie des déposants et leur rémunération n’est pas garantie.'],\n",
|
||||
" 'uris': None,\n",
|
||||
" 'data': None}"
|
||||
]
|
||||
},
|
||||
"execution_count": 51,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"collection.get(ids=['f6d921ced8.html-3'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d5b1969b-6b87-40c9-8a69-ef4dc1c14c30",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "RAG_ENV",
|
||||
"language": "python",
|
||||
"name": "rag_env"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.18"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
@ -22,9 +22,7 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "54a9d312-b39b-45f8-9529-57a142b6f6fc",
|
||||
"metadata": {
|
||||
"jp-MarkdownHeadingCollapsed": true
|
||||
},
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Embed a folder of CERA webpages in txt format"
|
||||
]
|
||||
|
@ -26,7 +26,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"id": "77c80131-ad2d-423e-a936-e548a4982d1f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -38,6 +38,13 @@
|
||||
"\n",
|
||||
"Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"En chiffre\t\t\t\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\t\t\t\t\t4,5 millions\t\t\t\t\t\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\t\t\t\t\tC’est le nombre de clients de Caisse d'Epargne ayant choisi de devenir sociétaires.\n",
|
||||
"Être sociétaire de sa Caisse d’Epargne, c’est soutenir une banque qui agit en local, à travers de nombreuses actions soutenues sur son territoire et sur le financement de l’économie régionale.\n",
|
||||
"Chaque année le sociétaire est invité à participer à l’assemblée générale de sa société locale d’épargne (SLE)…et à s’exprimer selon le principe « un homme=une voix ». Il assiste à la présentation du rapport d’activité de sa banque coopérative et participe à l’approbation des résultats financiers de sa SLE.\n",
|
||||
"\n",
|
||||
@ -48,29 +55,9 @@
|
||||
"Qui peut devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"Tous les clients de la Caisse d’Epargne peuvent souscrire des parts sociales : particuliers, personnes morales (associations, entreprises), EPCI (Établissements Publics de Coopération Intercommunale) à fiscalité propre. Les collectivités territoriales peuvent également devenir sociétaires.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Comment devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"Vous souscrivez vos parts sociales de la Société Locale d’Epargne (SLE) auprès de l’agence où est domicilié votre compte principal. Pour tout renseignement, contactez votre conseiller, il saura vous orienter.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Valeur nominale\n",
|
||||
"\n",
|
||||
"Contrairement aux actions, la part sociale n’est pas cotée. Elle n’est pas soumise aux aléas de la bourse. Sa valeur nominale reste fixe : 20 euros.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Rémunération\n",
|
||||
"\n",
|
||||
"Vous avez droit à un intérêt annuel. Vous ne payez aucuns frais tant à la souscription, à la tenue de compte qu’au remboursement.\n",
|
||||
"\n",
|
||||
"L’intérêt des parts sociales est conditionné à une décision de l’assemblée générale de la Caisse d’Epargne selon ses résultats.\n",
|
||||
@ -78,29 +65,15 @@
|
||||
"A noter : Les parts sociales sont rémunérées prorata temporis par mois civil entier de détention, à compter du 1er jour du mois qui suit la date d’agrément du sociétaire, jusqu’à la date de clôture de l’exercice.\n",
|
||||
"\n",
|
||||
"Si vous revendez vos parts sociales avant la clôture de l’exercice, vous ne percevez pas les intérêts pour l’exercice en cours. En cas de liquidation de votre SLE, vous n’avez pas de droit sur l’actif net (principe coopératif).\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Pas de frais de gestion\n",
|
||||
"\n",
|
||||
"Dans le cas d’une détention sur un compte dédié, les parts sociales ne sont soumises à aucuns frais : pas de commission de souscription, pas de frais de rachat ni de droits de garde. Une commission de tenue de compte peut cependant être prélevée dans le cadre d’un compte de parts sociales (comptes titres) ou d’un PEA. La souscription est conditionnée à l’agrément du conseil d’administration.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Fiscalité\n",
|
||||
"Qui peut devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"Les intérêts aux parts sociales sont soumis au même régime fiscal que les dividendes d’actions françaises. Si vous n’êtes pas domicilié fiscalement en France, les intérêts aux parts sociales supportent une retenue à la source dont le taux peut être réduit par la convention fiscale liant votre pays de résidence fiscale et la France. Nous vous conseillons de vous rapprocher, le cas échéant d’un conseiller fiscal.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Rachat\n",
|
||||
"\n",
|
||||
"Si vous souhaitez que la SLE rachète vos parts sociales, votre demande, soumise à l’autorisation du conseil d’administration de la SLE, doit être formulée avant le 31 mai, date de clôture de l’exercice. De ce fait, aucune assurance ne peut être donnée quant à la liquidité des parts sociales.\n",
|
||||
"\n",
|
||||
"Par ailleurs, les rachats de parts sociales sont subordonnés au respect du capital minimum en deçà duquel la SLE ne peut descendre. En conséquence, les sociétaires doivent être conscients qu’ils pourraient ne pas être en mesure de céder facilement leurs parts sociales.\n",
|
||||
@ -117,18 +90,11 @@
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Responsabilité – risque de perte en capital\n",
|
||||
"Qui peut devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"Votre responsabilité d’investisseur est limitée au niveau de votre investissement. Le risque investisseur (risque de perte en capital) porte sur le Groupe BPCE et non sur la SLE ou la Caisse d’Epargne (du fait du mécanisme de solidarité interne au Groupe BPCE).\n",
|
||||
"\n",
|
||||
"Les parts sociales demeurent des instruments risqués. Votre responsabilité, limitée au montant de l’apport, est engagée jusqu’à 5 ans après le retrait. Il existe un risque de perte en capital en cas de défaut ou de faillite de la Caisse d’Epargne ou de mise en œuvre de mesures de résolution au sein du Groupe BPCE. Les parts sociales ne sont pas éligibles au mécanisme de garantie des investisseurs ou de garantie des déposants et leur rémunération n’est pas garantie.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Rang de subordination\n",
|
||||
"\n",
|
||||
"En cas de liquidation, les liquidateurs seront chargés de réaliser l’actif, d’effectuer le paiement des dettes sociales et, en dernier lieu, de rembourser éventuellement le capital social aux sociétaires si celui-ci est suffisant après paiement des dettes de la SLE.\n",
|
||||
"Pour tout renseignement, contactez votre conseiller, il saura vous orienter. \n",
|
||||
"\n",
|
||||
@ -138,28 +104,30 @@
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Prenez part à la vie de votre Caisse d'Epargne\n",
|
||||
"Qui peut devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"En tant que sociétaire, vous êtes impliqué dans la vie de votre Caisse d’Epargne et prenez part aux décisions en y exerçant un droit de vote. Vous élisez vos représentants parmi les sociétaires, les administrateurs, qui élisent à leur tour leur président qui les représente à l’assemblée générale de votre Caisse d’Epargne. \n",
|
||||
"\n",
|
||||
"Vous participez ainsi aux grandes orientations de votre Caisse d’Epargne. Chaque année, à l’Assemblée Générale de votre SLE, vous rencontrez les dirigeants de votre Caisse d’Epargne et bénéficiez d’une information spécifique. \n",
|
||||
"\n",
|
||||
"Tout au long de l’année, vous pouvez également être invité à participer à des événements organisés par votre Caisse d’Epargne.\n",
|
||||
"Chez Caisse d’Epargne Rhône Alpes, votre épargne est réinvestie dans des projets de territoire. Votre Caisse d’Epargne est un acteur majeur et le premier financeur privé de l’économie sociale et solidaire.\n",
|
||||
"Au cœur des métiers de Caisse d’Epargne Rhône Alpes, cette solidarité s’exprime concrètement par les nombreuses actions de mécénat soutenues localement. \n",
|
||||
"\n",
|
||||
"Vos représentants participent à la remontée de projets locaux associatifs et à la sélection de ceux qui seront soutenus par Caisse d’Epargne Rhône Alpes.\n",
|
||||
"En tant que sociétaire, vous êtes impliqué dans la vie de votre Caisse d’Epargne et prenez part aux décisions en y exerçant un droit de vote. Vous élisez vos représentants parmi les sociétaires, les administrateurs, qui élisent à leur tour leur président qui les représente à l’assemblée générale de votre Caisse d’Epargne. \n",
|
||||
"\n",
|
||||
"Vous participez ainsi aux grandes orientations de votre Caisse d’Epargne. Chaque année, à l’Assemblée Générale de votre SLE, vous rencontrez les dirigeants de votre Caisse d’Epargne et bénéficiez d’une information spécifique. \n",
|
||||
"\n",
|
||||
"Tout au long de l’année, vous pouvez également être invité à participer à des événements organisés par votre Caisse d’Epargne.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Contribuez au développement économique et social de vos territoires\n",
|
||||
"Qui peut devenir sociétaire ?\n",
|
||||
"\n",
|
||||
"Chez Caisse d’Epargne Rhône Alpes, votre épargne est réinvestie dans des projets de territoire. Votre Caisse d’Epargne est un acteur majeur et le premier financeur privé de l’économie sociale et solidaire.\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Soutenez des actions de solidarité locale\n",
|
||||
"\n",
|
||||
"Au cœur des métiers de Caisse d’Epargne Rhône Alpes, cette solidarité s’exprime concrètement par les nombreuses actions de mécénat soutenues localement. \n",
|
||||
"\n",
|
||||
"Vos représentants participent à la remontée de projets locaux associatifs et à la sélection de ceux qui seront soutenus par Caisse d’Epargne Rhône Alpes.\n",
|
||||
@ -172,15 +140,43 @@
|
||||
"\n",
|
||||
"Sur www.societaires.caisse-epargne.fr, vous disposez d’un site d’information et d’avantages sélectionnés pour vous. Vous y découvrirez les réalisations et engagements de votre Caisse d’Epargne sur votre territoire : actualité, partenariats, soutien aux actions sociétales…\n",
|
||||
"C’est aussi une source incontournable d’informations sur l’organisation et les valeurs coopératives, les assemblées générales, la vie du sociétariat et des sociétés locales d’épargne.\n",
|
||||
"Le Club Sociétaires\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"passage: Devenez sociétaire !\n",
|
||||
"\n",
|
||||
"Le Club\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\tAccessible depuis le site sociétaires de votre région, le club vous propose des avantages et des réductions sur vos sorties ou vos achats, autour de chez vous et partout en France.\t\t\t\t\t\t\t\t\t\t\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\t\tDécouvrir maintenant\n",
|
||||
"Le Club Sociétaires\n",
|
||||
"Accessible depuis le site sociétaires de votre région, le club vous propose des avantages et des réductions sur vos sorties ou vos achats, autour de chez vous et partout en France.\n",
|
||||
"Découvrir maintenant\n",
|
||||
"Banque engagée : nos actions pour une société plus solidaire, plus durable\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\tDepuis leur création en 1818, les Caisses d’Epargne sont des banques régionales engagées en faveur de la solidarité et du développement économique et durable de leurs territoires.\t\t\t\t\t\t\t\t\t\t\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\t\tDécouvrir maintenant\n",
|
||||
"Banque engagée : nos actions pour une société plus solidaire, plus durable\n",
|
||||
"Depuis leur création en 1818, les Caisses d’Epargne sont des banques régionales engagées en faveur de la solidarité et du développement économique et durable de leurs territoires.\n",
|
||||
"Découvrir maintenant\n",
|
||||
"Qui sommes-nous ?\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\tDepuis sa création en 1818, Caisse d’Epargne accompagne les Français et les territoires dans leurs projets et tient un rôle majeur dans la vie économique et sociale du pays. \t\t\t\t\t\t\t\t\t\t\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\t\t\t\t\t\t\t\t\t\t\t\tDécouvrir maintenant\n",
|
||||
"Qui sommes-nous ?\n",
|
||||
"Depuis sa création en 1818, Caisse d’Epargne accompagne les Français et les territoires dans leurs projets et tient un rôle majeur dans la vie économique et sociale du pays.\n",
|
||||
"Découvrir maintenant\n",
|
||||
"\n",
|
||||
"----------\n",
|
||||
"\n"
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user