202401172050

This commit is contained in:
Pierre-Edouard Portier 2024-01-17 20:50:28 +01:00
parent efaa188404
commit 8f78f6c656
12 changed files with 1575 additions and 1248 deletions

4
.gitignore vendored
View File

@ -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
View File

@ -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
View File

@ -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})

View File

@ -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 ?

View File

@ -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)

View File

@ -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
View 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
View File

@ -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
View 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",
" 'dun',\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 lindemnisation\\nEn cas dévénement climatique, le besoin dassistance ou de prise en charge est immédiat\\xa0!Cest pourquoi la Caisse dEpargne vous facilite la déclaration de sinistre grâce à votre espace personnel internet. Si vous êtes détenteur dun contrat dassurance 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 dun 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 dAssurances 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 dune 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 dAssurances et dAssistance 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 daccident, 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 dindemnisation auprès de la compagnie aérienne. Sajoute à lindemnité de retard, le remboursement des frais engagés durant lattente (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 dutilisation frauduleuse de la carte ;\n",
"\n",
"----------\n",
"\n",
"Carte Visa Premier\n",
"\n",
"Bénéficiez davantages 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: cest lencours des paiements par carte. Le suivi de cet encours peut se faire à tout moment via la banque à distance (internet et mobile) et permet danticiper 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 nest affectée des paiements par carte quune 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 dachat 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 dun 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",
"LExtension 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 dune 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 lapplication Caisse dEpargne 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 dutilisation 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 à lautorisation du conseil dadministration de la SLE, doit être formulée avant le 31 mai, date de clôture de lexercice. 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 quils pourraient ne pas être en mesure de céder facilement leurs parts sociales.\\n\\nLe remboursement effectif, quil 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 dEpargne daffiliation, redressement judiciaire du sociétaire, de clôture dun livret A lorsque le client ne détient pas dautres produits et tout évènement exceptionnel revêtant une gravité telle quelle 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é dinvestisseur 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 dEpargne (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 lapport, 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 dEpargne 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 nest 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
}

View File

@ -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"
]

View File

@ -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\tCest le nombre de clients de Caisse d'Epargne ayant choisi de devenir sociétaires.\n",
"Être sociétaire de sa Caisse dEpargne, cest 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 à lassemblée générale de sa société locale dépargne (SLE)…et à sexprimer selon le principe « un homme=une voix ». Il assiste à la présentation du rapport dactivité de sa banque coopérative et participe à lapprobation 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 dEpargne 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 dEpargne (SLE) auprès de lagence 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 nest pas cotée. Elle nest 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 quau remboursement.\n",
"\n",
"Lintérêt des parts sociales est conditionné à une décision de lassemblée générale de la Caisse dEpargne 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 dagrément du sociétaire, jusquà la date de clôture de lexercice.\n",
"\n",
"Si vous revendez vos parts sociales avant la clôture de lexercice, vous ne percevez pas les intérêts pour lexercice en cours. En cas de liquidation de votre SLE, vous navez pas de droit sur lactif net (principe coopératif).\n",
"\n",
"----------\n",
"\n",
"passage: Devenez sociétaire !\n",
"\n",
"Pas de frais de gestion\n",
"\n",
"Dans le cas dune 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 dun compte de parts sociales (comptes titres) ou dun PEA. La souscription est conditionnée à lagrément du conseil dadministration.\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 dactions 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 dun 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 à lautorisation du conseil dadministration de la SLE, doit être formulée avant le 31 mai, date de clôture de lexercice. 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 quils 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é dinvestisseur 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 dEpargne (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 lapport, 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 dEpargne 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 nest 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 lactif, deffectuer 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 dEpargne 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 à lassemblée générale de votre Caisse dEpargne. \n",
"\n",
"Vous participez ainsi aux grandes orientations de votre Caisse dEpargne. Chaque année, à lAssemblée Générale de votre SLE, vous rencontrez les dirigeants de votre Caisse dEpargne et bénéficiez dune information spécifique. \n",
"\n",
"Tout au long de lannée, vous pouvez également être invité à participer à des événements organisés par votre Caisse dEpargne.\n",
"Chez Caisse dEpargne Rhône Alpes, votre épargne est réinvestie dans des projets de territoire. Votre Caisse dEpargne est un acteur majeur et le premier financeur privé de léconomie sociale et solidaire.\n",
"Au cœur des métiers de Caisse dEpargne Rhône Alpes, cette solidarité sexprime 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 dEpargne Rhône Alpes.\n",
"En tant que sociétaire, vous êtes impliqué dans la vie de votre Caisse dEpargne 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 à lassemblée générale de votre Caisse dEpargne. \n",
"\n",
"Vous participez ainsi aux grandes orientations de votre Caisse dEpargne. Chaque année, à lAssemblée Générale de votre SLE, vous rencontrez les dirigeants de votre Caisse dEpargne et bénéficiez dune information spécifique. \n",
"\n",
"Tout au long de lannée, vous pouvez également être invité à participer à des événements organisés par votre Caisse dEpargne.\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 dEpargne Rhône Alpes, votre épargne est réinvestie dans des projets de territoire. Votre Caisse dEpargne 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 dEpargne Rhône Alpes, cette solidarité sexprime 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 dEpargne Rhône Alpes.\n",
@ -172,15 +140,43 @@
"\n",
"Sur www.societaires.caisse-epargne.fr, vous disposez dun site dinformation et davantages sélectionnés pour vous. Vous y découvrirez les réalisations et engagements de votre Caisse dEpargne sur votre territoire : actualité, partenariats, soutien aux actions sociétales…\n",
"Cest aussi une source incontournable dinformations sur lorganisation 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 dEpargne 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 dEpargne 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 dEpargne 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 dEpargne 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