Aidahaouas commited on
Commit
3e0117c
·
1 Parent(s): 0ed3de9

Architecture C Updated

Browse files
__pycache__/config.cpython-310.pyc CHANGED
Binary files a/__pycache__/config.cpython-310.pyc and b/__pycache__/config.cpython-310.pyc differ
 
__pycache__/graph_agentB.cpython-310.pyc CHANGED
Binary files a/__pycache__/graph_agentB.cpython-310.pyc and b/__pycache__/graph_agentB.cpython-310.pyc differ
 
__pycache__/pdf_processing.cpython-310.pyc CHANGED
Binary files a/__pycache__/pdf_processing.cpython-310.pyc and b/__pycache__/pdf_processing.cpython-310.pyc differ
 
__pycache__/pinecone_utilsB.cpython-310.pyc CHANGED
Binary files a/__pycache__/pinecone_utilsB.cpython-310.pyc and b/__pycache__/pinecone_utilsB.cpython-310.pyc differ
 
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import streamlit as st
2
  from graph_agentA import agent as agent_A
3
  from graph_agentB import agent as agent_B
 
4
  from config import *
5
  from dotenv import load_dotenv
6
  from pinecone_utilsB import *
@@ -28,7 +29,7 @@ def check_indexes_ready():
28
  st.error(f"Erreur lors de la vérification des index Pinecone : {e}")
29
  return False
30
 
31
- def process_query(query, architecture: Literal["A", "B"]):
32
  """Traite la requête de l'utilisateur avec l'architecture A."""
33
 
34
  # Reload conversation
@@ -40,6 +41,8 @@ def process_query(query, architecture: Literal["A", "B"]):
40
  agent = agent_A
41
  elif architecture == "B":
42
  agent = agent_B
 
 
43
 
44
  st.session_state.chat_history.append({"role": "user", "content": query})
45
 
@@ -121,6 +124,8 @@ def main():
121
  if query:
122
  if architecture == "Intermédiaire":
123
  architecture = "B"
 
 
124
  else:
125
  architecture = "A"
126
 
 
1
  import streamlit as st
2
  from graph_agentA import agent as agent_A
3
  from graph_agentB import agent as agent_B
4
+ from graph_agentC import agent as agent_C
5
  from config import *
6
  from dotenv import load_dotenv
7
  from pinecone_utilsB import *
 
29
  st.error(f"Erreur lors de la vérification des index Pinecone : {e}")
30
  return False
31
 
32
+ def process_query(query, architecture: Literal["A", "B", "C"]):
33
  """Traite la requête de l'utilisateur avec l'architecture A."""
34
 
35
  # Reload conversation
 
41
  agent = agent_A
42
  elif architecture == "B":
43
  agent = agent_B
44
+ elif architecture == "C":
45
+ agent = agent_C
46
 
47
  st.session_state.chat_history.append({"role": "user", "content": query})
48
 
 
124
  if query:
125
  if architecture == "Intermédiaire":
126
  architecture = "B"
127
+ elif architecture == "Avancée":
128
+ architecture = "C"
129
  else:
130
  architecture = "A"
131
 
graph_agentB.py CHANGED
@@ -66,3 +66,4 @@ graph_builder.add_edge("generate", "post_process")
66
  graph_builder.add_edge("post_process", END)
67
 
68
  agent = graph_builder.compile()
 
 
66
  graph_builder.add_edge("post_process", END)
67
 
68
  agent = graph_builder.compile()
69
+
graph_agentC.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TypedDict, Annotated, Sequence
2
+ from langchain_core.messages import BaseMessage
3
+ from langgraph.graph import StateGraph, END
4
+ from langgraph.graph.message import add_messages
5
+ from neo4j_utils import unified_search
6
+ from config import llm
7
+
8
+ class GraphState(TypedDict):
9
+ messages: Annotated[Sequence[BaseMessage], add_messages]
10
+ query: str
11
+ relevant_docs: list # Résultats de la recherche hybride
12
+ neo4j_results: list # Résultats de la recherche Neo4j
13
+ response: str
14
+
15
+
16
+ def retrieve_unified(state: GraphState) -> dict:
17
+ """Récupération unifiée : recherche hybride (Pinecone + BM25) + Neo4j."""
18
+ # Appeler la fonction unified_search
19
+ unified_results = unified_search(state["query"])
20
+
21
+ # Séparer les résultats hybrides et Neo4j
22
+ hybrid_results = [result for result in unified_results if result["type"] == "text"]
23
+ neo4j_results = [result for result in unified_results if result["type"] == "entity"]
24
+
25
+ return {
26
+ "relevant_docs": hybrid_results,
27
+ "neo4j_results": neo4j_results
28
+ }
29
+
30
+ def generate_response(state: GraphState) -> dict:
31
+ """Génération de réponse en combinant informations sémantiques, mots-clés et données Neo4j."""
32
+ # Concaténer les documents pertinents
33
+ context = "\n\n".join([doc["content"] for doc in state["relevant_docs"]])
34
+
35
+ # Ajouter les résultats Neo4j
36
+ neo4j_context = "\n\n".join([str(record["content"]) for record in state["neo4j_results"]])
37
+
38
+ # Créer le prompt
39
+ prompt = f"""
40
+ Vous êtes un expert en analyse de texte et en données structurées.
41
+ Votre tâche consiste à répondre à la question de l'utilisateur en utilisant les informations pertinentes fournies.
42
+ Intégrez à la fois les éléments sémantiques (contexte, sens), les mots-clés exacts et les données structurées pour fournir une réponse fluide et cohérente.
43
+
44
+ **Instructions supplémentaires** :
45
+ - Utilisez les mots-clés pertinents de manière naturelle dans votre réponse.
46
+ - Expliquez les concepts en vous appuyant sur le contexte sémantique.
47
+ - Intégrez les données structurées (entités, relations) de manière claire et concise.
48
+ - Ne mentionnez pas explicitement les termes "recherche sémantique", "recherche par mots-clés" ou "base de données Neo4j".
49
+
50
+ **Informations pertinentes trouvées (texte)** :
51
+ {context}
52
+
53
+ **Informations pertinentes trouvées (données structurées)** :
54
+ {neo4j_context}
55
+
56
+ **Question de l'utilisateur** :
57
+ {state["query"]}
58
+
59
+ **Réponse :**
60
+ [Fournissez une réponse cohérente qui intègre à la fois les éléments sémantiques, les mots-clés pertinents et les données structurées sans les distinguer explicitement.]
61
+ """
62
+
63
+ # Générer la réponse avec le LLM
64
+ response = llm.invoke(prompt)
65
+ return {"response": response.content}
66
+
67
+ def post_process_response(state: GraphState) -> dict:
68
+ """Nettoie et valide la réponse."""
69
+ response = state["response"].strip()
70
+
71
+ # Vérifier si la réponse est pertinente
72
+ if not response or response.lower() in ["je ne sais pas", "i don't know"]:
73
+ response = "Désolé, je n'ai pas trouvé d'informations pertinentes pour votre question."
74
+
75
+ return {"response": response}
76
+
77
+
78
+ # Construction du graphe
79
+ graph_builder = StateGraph(GraphState)
80
+
81
+ # Ajouter les nœuds
82
+ graph_builder.add_node("retrieve", retrieve_unified)
83
+ graph_builder.add_node("generate", generate_response)
84
+ graph_builder.add_node("post_process", post_process_response)
85
+
86
+ # Définir les transitions
87
+ graph_builder.set_entry_point("retrieve")
88
+ graph_builder.add_edge("retrieve", "generate")
89
+ graph_builder.add_edge("generate", "post_process")
90
+ graph_builder.add_edge("post_process", END)
91
+
92
+ # Compiler le graphe
93
+ agent = graph_builder.compile()
langchain-neo4j.ipynb CHANGED
@@ -2,15 +2,12 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 4,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
9
  "import os\n",
10
  "from langchain_neo4j import Neo4jGraph\n",
11
- "from dotenv import load_dotenv\n",
12
- "\n",
13
- "load_dotenv()\n",
14
  "\n",
15
  "neo4j_uri = os.getenv(\"NEO4J_URI\")\n",
16
  "neo4j_username = os.getenv(\"NEO4J_USERNAME\")\n",
@@ -21,7 +18,7 @@
21
  },
22
  {
23
  "cell_type": "code",
24
- "execution_count": 5,
25
  "metadata": {},
26
  "outputs": [],
27
  "source": [
@@ -43,9 +40,33 @@
43
  },
44
  {
45
  "cell_type": "code",
46
- "execution_count": 6,
47
  "metadata": {},
48
- "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  "source": [
50
  "from transformers import pipeline\n",
51
  "\n",
@@ -71,7 +92,7 @@
71
  },
72
  {
73
  "cell_type": "code",
74
- "execution_count": 7,
75
  "metadata": {},
76
  "outputs": [
77
  {
@@ -83,7 +104,6 @@
83
  }
84
  ],
85
  "source": [
86
- "\n",
87
  "from pdf_processing import get_existing_pdf, load_and_preprocess_pdf, split_text\n",
88
  "\n",
89
  "path_pdf_file = get_existing_pdf()\n",
@@ -95,7 +115,7 @@
95
  },
96
  {
97
  "cell_type": "code",
98
- "execution_count": 8,
99
  "metadata": {},
100
  "outputs": [
101
  {
@@ -130,37 +150,25 @@
130
  "cell_type": "markdown",
131
  "metadata": {},
132
  "source": [
133
- "# Convert to Graph"
134
  ]
135
  },
136
  {
137
  "cell_type": "code",
138
- "execution_count": 9,
139
  "metadata": {},
140
  "outputs": [
141
  {
142
  "name": "stdout",
143
  "output_type": "stream",
144
  "text": [
145
- "nodes=[Node(id='Gaspard Boréal', type='Personnage', properties={}), Node(id='La Confession Muette', type='Objet', properties={}), Node(id='Théo', type='Personnage', properties={}), Node(id='Robin', type='Personnage', properties={}), Node(id='Aurélien', type='Personnage', properties={}), Node(id='Keziah', type='Personnage', properties={}), Node(id='Gaston Bachelard', type='Personnage', properties={}), Node(id='Tristan', type='Personnage', properties={}), Node(id='Anne-Hélène', type='Personnage', properties={}), Node(id='Musée', type='Lieu', properties={}), Node(id='Bruxelles', type='Lieu', properties={})] relationships=[Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='La Confession Muette', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Théo', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Robin', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Aurélien', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Keziah', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Tristan', type='Personnage', properties={}), target=Node(id='Musée', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Anne-Hélène', type='Personnage', properties={}), target=Node(id='Bruxelles', type='Lieu', properties={}), type='SITUE_DANS', properties={})] source=Document(metadata={}, page_content=\"Gaspard Boréal\\nLa Confession muette\\n© Gaspard Boréal, 2025\\nISBN numérique : 979-10-405-7304-3\\nwww.librinova.com\\nLe Code de la propriété intellectuelle interdit les copies ou reproductions destinées à une utilisation collective. Toute\\nreprésentation ou reproduction intégrale ou partielle faite par quelque procédé que ce soit, sans le consentement de l’auteur ou de\\nses ayants cause, est illicite et constitue une contrefaçon sanctionnée par les articles L335-2 et suivants du Code de la propriété\\nintellectuelle.\\n« À Théo, Robin, Aurélien, Keziah »\\nC’est à vos côtés que ce récit a tracé ses premières lignes\\nlaissant l’imagina\\x00on y poser sa signature\\n« Imaginer, c’est hausser le réel d’un ton. »\\nGaston Bachelard\\nScène 01 : Le guide du Musée\\nC'est son instant préféré !\\nObserver, scruter, détecter quels sont ceux qui vont préférer entrer dans\\ncet immense ascenseur dont il est le guide depuis cinq ans…\\nPosi\\x00onné inlassablement dans le même angle, au fond, proche du tableau\\nde bord lui perme\\x00ant d'ac\\x00ver toutes les op\\x00ons disponibles, pour\\nmonter aux étages les tableaux et fresques des nouvelles collec\\x00ons ou\\npour accueillir les groupes d'enfants et visiteurs, préférant le confort à la\\nmontée par les escaliers du musée.\\nPourtant si elles-ils savaient !\\nTristan n'est pas très grand, la trentaine, avec ce type de costume gris que\\nl'on ne regarde pas, un peu trop sobre, trop neutre pour a\\x00rer l'œil.\\nC'est sa pe\\x00te oreille\\x00e blanche que l'on peut remarquer si on lui prête\\na\\x00en\\x00on. Pour renforcer son rôle apparemment bien normé, Tristan\\nadopte toujours la même posture afin que les visiteurs ne s'intéressent pas\\nà lui, ni aux mouvements discrets de son corps, de ses yeux ou de ses\\nmains. Seul détail pouvant surprendre les plus a\\x00en\\x00fs : les sub\\x00les\\norchidées ton sur ton finement brodées sur ses poignets de chemise.\\nEncore faut-il pour cela le regarder, lui, si discret, si effacé !\\nPourtant, il lui suffit d'une simple montée du rez-de-chaussée vers le 3ième\\nétage de l'exposi\\x00on Magri\\x00e, de quelques sourcillements et tapotements,\\npour disposer de toutes les informa\\x00ons lui perme\\x00ant de choisir celui ou\\ncelle qu’il va portraiturer !\\nScène 02 : La directrice du musée\\n« Aujourd'hui je vais rencontrer le futur ».\\nCe\\x00e pensée amuse Anne-Hélène, alors qu'elle se \\x00ent devant le miroir\\nornementé de sa chambre baignée par la lumière douce de Bruxelles. Le\\nsilence de la pièce n’est troublé que par le léger bruissement de ses gestes.\")\n",
146
- "nodes=[Node(id='Chambre', type='Lieu', properties={}), Node(id='Bruxelles', type='Lieu', properties={}), Node(id='Elle', type='Personnage', properties={}), Node(id='Brosse En Ivoire', type='Objet', properties={}), Node(id='Artiste Sculpteur', type='Personnage', properties={}), Node(id='Cheveux', type='Objet', properties={}), Node(id='Mèches Argentées', type='Objet', properties={}), Node(id='Reflet', type='Objet', properties={}), Node(id='Musée', type='Lieu', properties={}), Node(id='Exposition', type='Événement', properties={}), Node(id='René Magritte', type='Personnage', properties={}), Node(id='Œuvres', type='Objet', properties={}), Node(id='Visiteurs', type='Personnage', properties={}), Node(id='Idées Créatives', type='Motif', properties={}), Node(id='Anne Hélène', type='Personnage', properties={}), Node(id='Gaîté Lyrique', type='Lieu', properties={}), Node(id='Paris', type='Lieu', properties={}), Node(id='Musées Royaux Des Beaux-Arts De Belgique', type='Lieu', properties={}), Node(id='Prédécesseur', type='Personnage', properties={}), Node(id='Collectif', type='Personnage', properties={}), Node(id='Ancien Directeur', type='Personnage', properties={}), Node(id='Esprit Magritte', type='Motif', properties={}), Node(id='Avant-Garde Surréaliste Belge', type='Motif', properties={}), Node(id='Humour Subtil', type='Motif', properties={}), Node(id='Paradoxes', type='Motif', properties={}), Node(id='Public', type='Personnage', properties={}), Node(id='Réalité', type='Thème', properties={}), Node(id='Groupe De Travail', type='Personnage', properties={}), Node(id='Jeunes Artistes Engagés', type='Personnage', properties={})] relationships=[Relationship(source=Node(id='Chambre', type='Lieu', properties={}), target=Node(id='Bruxelles', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Elle', type='Personnage', properties={}), target=Node(id='Brosse En Ivoire', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Brosse En Ivoire', type='Objet', properties={}), target=Node(id='Artiste Sculpteur', type='Personnage', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Elle', type='Personnage', properties={}), target=Node(id='Cheveux', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Cheveux', type='Objet', properties={}), target=Node(id='Mèches Argentées', type='Objet', properties={}), type='FAIT_PARTIE_DE', properties={}), Relationship(source=Node(id='Elle', type='Personnage', properties={}), target=Node(id='Reflet', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Elle', type='Personnage', properties={}), target=Node(id='Musée', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Musée', type='Lieu', properties={}), target=Node(id='Exposition', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='René Magritte', type='Personnage', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Œuvres', type='Objet', properties={}), target=Node(id='Visiteurs', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Visiteurs', type='Personnage', properties={}), target=Node(id='Idées Créatives', type='Motif', properties={}), type='INSPIRÉ_PAR', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Gaîté Lyrique', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaîté Lyrique', type='Lieu', properties={}), target=Node(id='Paris', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Musées Royaux Des Beaux-Arts De Belgique', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Prédécesseur', type='Personnage', properties={}), target=Node(id='Collectif', type='Personnage', properties={}), type=\"S'OPPOSE_À\", properties={}), Relationship(source=Node(id='Collectif', type='Personnage', properties={}), target=Node(id='Ancien Directeur', type='Personnage', properties={}), type=\"S'OPPOSE_À\", properties={}), Relationship(source=Node(id='Esprit Magritte', type='Motif', properties={}), target=Node(id='Avant-Garde Surréaliste Belge', type='Motif', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Avant-Garde Surréaliste Belge', type='Motif', properties={}), target=Node(id='Humour Subtil', type='Motif', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Humour Subtil', type='Motif', properties={}), target=Node(id='Paradoxes', type='Motif', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Paradoxes', type='Motif', properties={}), target=Node(id='Public', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Public', type='Personnage', properties={}), target=Node(id='Réalité', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Groupe De Travail', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Groupe De Travail', type='Personnage', properties={}), target=Node(id='Jeunes Artistes Engagés', type='Personnage', properties={}), type='CONNAÎT', properties={})] source=Document(metadata={}, page_content=\"ornementé de sa chambre baignée par la lumière douce de Bruxelles. Le\\nsilence de la pièce n’est troublé que par le léger bruissement de ses gestes.\\nElle saisit la brosse en ivoire, objet précieux que lui a offert un ar\\x00ste\\nsculpteur, amoureux secret et discret, et commence à démêler ses cheveux\\navec précau\\x00on. Chaque coup de brosse semble une caresse, un rituel\\nin\\x00me qu'elle accomplit avec une précision presque cérémoniale. Ses\\ncheveux, autrefois d’un noir profond, sont maintenant parsemés de légères\\nmèches argentées, témoins silencieux du passage du temps.\\nElle observe son reflet avec une a\\x00en\\x00on par\\x00culière. Elle aime éprouver\\nla sérénité de ces moments de solitude où elle proje\\x00e le futur de ses\\njournées en contemplant les signes discrets de son passé. Une ques\\x00on lui\\nrevient à l’esprit :\\n« Quel ar\\x00ste vais-je pouvoir convaincre de par\\x00ciper à la créa\\x00on de ce\\x00e\\nprochaine exposi\\x00on ? »\\nAlors que sa main con\\x00nue de parcourir ses longs cheveux, dans un va et\\nvient régulier et souple pour bien les lisser, elle prend la mesure de\\nl'incroyable challenge qu'elle a proposé à la gouvernance du musée :\\n« Limites de l’imaginaire ou Limites planétaires ? ». Une exposi\\x00on\\naudacieuse et provocante où un ar\\x00ste contemporain va explorer les neuf\\nlimites planétaires à travers le prisme surréaliste des tableaux de René\\nMagri\\x00e.\\nL’enjeu de l’exposi\\x00on : confronter les œuvres de l’ar\\x00ste et les visiteurs\\npour inspirer à ces derniers des idées créa\\x00ves.\\nIl y a seulement trois mois, Anne Hélène avait accepté de qui\\x00er la\\ndirec\\x00on de la Gaîté Lyrique à Paris pour l'une des plus pres\\x00gieuses\\nins\\x00tu\\x00ons culturelles d’Europe : les Musées Royaux des Beaux-Arts de\\nBelgique. Elle avait alors découvert une situa\\x00on catastrophique laissée par\\nson prédécesseur ! Un collec\\x00f rassemblant plus de trente personnes\\ndénonçait des « condi\\x00ons de travail épouvantables », des « menaces\\nrégulières » et une « ges\\x00on calamiteuse » et il avait contraint l'ancien\\ndirecteur à démissionner !\\nIl était urgent de relancer l'ins\\x00tu\\x00on en y restaurant l'esprit « Magri\\x00e » :\\ncelui de l’avant-garde surréaliste belge d’un humour sub\\x00l, jouant avec les\\nparadoxes pour surprendre son public, et posant des ques\\x00ons\\nphilosophiques sur la réalité. C'est ainsi qu'était née ce\\x00e idée, avec le\\ngroupe de travail qu'elle avait mis en place, dès le premier mois de sa prise\\nde poste.\\nIl fallait désormais recruter de jeunes ar\\x00stes engagés pour créer, passer\")\n"
147
- ]
148
- },
149
- {
150
- "ename": "KeyboardInterrupt",
151
- "evalue": "",
152
- "output_type": "error",
153
- "traceback": [
154
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
155
- "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
156
- "Cell \u001b[1;32mIn[9], line 6\u001b[0m\n\u001b[0;32m 3\u001b[0m graph_documents_filtered \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m 5\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m doc \u001b[38;5;129;01min\u001b[39;00m documents:\n\u001b[1;32m----> 6\u001b[0m graph_doc \u001b[38;5;241m=\u001b[39m \u001b[43mllm_transformer_tuple\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconvert_to_graph_documents\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mdoc\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 7\u001b[0m \u001b[38;5;28mprint\u001b[39m(graph_doc[\u001b[38;5;241m0\u001b[39m])\n\u001b[0;32m 9\u001b[0m graph_documents_filtered\u001b[38;5;241m.\u001b[39mappend(graph_doc[\u001b[38;5;241m0\u001b[39m])\n",
157
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\langchain_experimental\\graph_transformers\\llm.py:932\u001b[0m, in \u001b[0;36mLLMGraphTransformer.convert_to_graph_documents\u001b[1;34m(self, documents, config)\u001b[0m\n\u001b[0;32m 920\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mconvert_to_graph_documents\u001b[39m(\n\u001b[0;32m 921\u001b[0m \u001b[38;5;28mself\u001b[39m, documents: Sequence[Document], config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 922\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[GraphDocument]:\n\u001b[0;32m 923\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Convert a sequence of documents into graph documents.\u001b[39;00m\n\u001b[0;32m 924\u001b[0m \n\u001b[0;32m 925\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 930\u001b[0m \u001b[38;5;124;03m Sequence[GraphDocument]: The transformed documents as graphs.\u001b[39;00m\n\u001b[0;32m 931\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 932\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprocess_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdocument\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m document \u001b[38;5;129;01min\u001b[39;00m documents]\n",
158
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\langchain_experimental\\graph_transformers\\llm.py:839\u001b[0m, in \u001b[0;36mLLMGraphTransformer.process_response\u001b[1;34m(self, document, config)\u001b[0m\n\u001b[0;32m 834\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 835\u001b[0m \u001b[38;5;124;03mProcesses a single document, transforming it into a graph document using\u001b[39;00m\n\u001b[0;32m 836\u001b[0m \u001b[38;5;124;03man LLM based on the model's schema and constraints.\u001b[39;00m\n\u001b[0;32m 837\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 838\u001b[0m text \u001b[38;5;241m=\u001b[39m document\u001b[38;5;241m.\u001b[39mpage_content\n\u001b[1;32m--> 839\u001b[0m raw_schema \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minput\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtext\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 840\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_function_call:\n\u001b[0;32m 841\u001b[0m raw_schema \u001b[38;5;241m=\u001b[39m cast(Dict[Any, Any], raw_schema)\n",
159
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\langchain_core\\runnables\\base.py:3016\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m 3014\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m context\u001b[38;5;241m.\u001b[39mrun(step\u001b[38;5;241m.\u001b[39minvoke, \u001b[38;5;28minput\u001b[39m, config, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 3015\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 3016\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3017\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[0;32m 3018\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
160
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\langchain_core\\runnables\\base.py:3721\u001b[0m, in \u001b[0;36mRunnableParallel.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m 3716\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m get_executor_for_config(config) \u001b[38;5;28;01mas\u001b[39;00m executor:\n\u001b[0;32m 3717\u001b[0m futures \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m 3718\u001b[0m executor\u001b[38;5;241m.\u001b[39msubmit(_invoke_step, step, \u001b[38;5;28minput\u001b[39m, config, key)\n\u001b[0;32m 3719\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, step \u001b[38;5;129;01min\u001b[39;00m steps\u001b[38;5;241m.\u001b[39mitems()\n\u001b[0;32m 3720\u001b[0m ]\n\u001b[1;32m-> 3721\u001b[0m output \u001b[38;5;241m=\u001b[39m {key: \u001b[43mfuture\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m key, future \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(steps, futures)}\n\u001b[0;32m 3722\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[0;32m 3723\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
161
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\concurrent\\futures\\_base.py:451\u001b[0m, in \u001b[0;36mFuture.result\u001b[1;34m(self, timeout)\u001b[0m\n\u001b[0;32m 448\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;241m==\u001b[39m FINISHED:\n\u001b[0;32m 449\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__get_result()\n\u001b[1;32m--> 451\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_condition\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwait\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 453\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n\u001b[0;32m 454\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n",
162
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\threading.py:355\u001b[0m, in \u001b[0;36mCondition.wait\u001b[1;34m(self, timeout)\u001b[0m\n\u001b[0;32m 353\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m: \u001b[38;5;66;03m# restore state no matter what (e.g., KeyboardInterrupt)\u001b[39;00m\n\u001b[0;32m 354\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m timeout \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m--> 355\u001b[0m \u001b[43mwaiter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43macquire\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 356\u001b[0m gotit \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m 357\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n",
163
- "\u001b[1;31mKeyboardInterrupt\u001b[0m: "
164
  ]
165
  }
166
  ],
@@ -181,97 +189,11 @@
181
  "\n",
182
  "graph.add_graph_documents(graph_documents_filtered, baseEntityLabel=True)"
183
  ]
184
- },
185
- {
186
- "cell_type": "markdown",
187
- "metadata": {},
188
- "source": []
189
- },
190
- {
191
- "cell_type": "code",
192
- "execution_count": null,
193
- "metadata": {},
194
- "outputs": [
195
- {
196
- "name": "stderr",
197
- "output_type": "stream",
198
- "text": [
199
- "Device set to use cpu\n",
200
- "Token indices sequence length is longer than the specified maximum sequence length for this model (44145 > 1024). Running this sequence through the model will result in indexing errors\n"
201
- ]
202
- },
203
- {
204
- "name": "stdout",
205
- "output_type": "stream",
206
- "text": [
207
- "Nombre de chunks : 2088\n",
208
- "Chunk 1: 513 tokens\n",
209
- "Chunk 2: 514 tokens\n",
210
- "Chunk 3: 514 tokens\n",
211
- "Chunk 4: 514 tokens\n",
212
- "Chunk 5: 514 tokens\n"
213
- ]
214
- }
215
- ],
216
- "source": [
217
- "import multiprocessing\n",
218
- "from pdf_processing import get_existing_pdf, load_and_preprocess_pdf, split_text\n",
219
- "from transformers import pipeline, AutoTokenizer\n",
220
- "\n",
221
- "# 🔹 Choisir un modèle plus léger (compatible CPU)\n",
222
- "model_name = \"sshleifer/distilbart-cnn-12-6\" # Plus rapide sur CPU\n",
223
- "summarizer = pipeline(\"summarization\", model=model_name, device=-1) # CPU uniquement\n",
224
- "tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
225
- "\n",
226
- "# 🔹 Charger les PDF\n",
227
- "pdf_files = get_existing_pdf()\n",
228
- "texts = [load_and_preprocess_pdf(pdf_file) for pdf_file in pdf_files]\n",
229
- "\n",
230
- "# 🔹 Fonction pour découper le texte en morceaux max 512 tokens\n",
231
- "def chunk_text(text, max_tokens=512):\n",
232
- " tokens = tokenizer.encode(text, truncation=False) # Convertir en tokens\n",
233
- " chunks = [tokens[i:i + max_tokens] for i in range(0, len(tokens), max_tokens)]\n",
234
- " return [tokenizer.decode(chunk, skip_special_tokens=True) for chunk in chunks]\n",
235
- "\n",
236
- "text_chunks = []\n",
237
- "for text in texts:\n",
238
- " text_chunks.extend(chunk_text(text)) # Stocker tous les morceaux\n",
239
- "\n",
240
- "# 🔹 Vérification : chaque chunk doit être < 1024 tokens\n",
241
- "print(f\"Nombre de chunks : {len(text_chunks)}\")\n",
242
- "for i, chunk in enumerate(text_chunks[:5]): # Afficher les 5 premiers pour contrôle\n",
243
- " print(f\"Chunk {i+1}: {len(tokenizer.encode(chunk))} tokens\")\n",
244
- "\n",
245
- "# 🔹 Fonction pour résumer chaque chunk\n",
246
- "def summarize_chunk(chunk):\n",
247
- " try:\n",
248
- " summary = summarizer(chunk, max_length=200, min_length=50, do_sample=False)[0]['summary_text']\n",
249
- " return summary\n",
250
- " except Exception as e:\n",
251
- " print(f\"Erreur lors du résumé : {e}\")\n",
252
- " return \"\" # Éviter les crashs\n",
253
- "\n",
254
- "# 🔹 Parallélisation (Utiliser plusieurs cœurs CPU)\n",
255
- "cpu_cores = max(1, multiprocessing.cpu_count() - 1)\n",
256
- "\n",
257
- "with multiprocessing.Pool(processes=cpu_cores) as pool:\n",
258
- " compressed_texts = pool.map(summarize_chunk, text_chunks)\n",
259
- "\n",
260
- "# 🔹 Fusion des textes résumés\n",
261
- "final_text = \" \".join(compressed_texts)\n",
262
- "\n",
263
- "# 🔹 Création des documents pour le LLM\n",
264
- "documents = [Document(page_content=final_text)]\n",
265
- "graph_documents_filtered = llm_transformer_tuple.convert_to_graph_documents(documents)\n",
266
- "# Afficher les résultats (limité à 10 nodes et 10 relationships)\n",
267
- "print(f\"Nodes (first 10): {graph_documents_filtered[0].nodes[:10]}\")\n",
268
- "print(f\"Relationships (first 10): {graph_documents_filtered[0].relationships[:10]}\")"
269
- ]
270
  }
271
  ],
272
  "metadata": {
273
  "kernelspec": {
274
- "display_name": "Python 3",
275
  "language": "python",
276
  "name": "python3"
277
  },
@@ -285,7 +207,12 @@
285
  "name": "python",
286
  "nbconvert_exporter": "python",
287
  "pygments_lexer": "ipython3",
288
- "version": "3.12.2"
 
 
 
 
 
289
  }
290
  },
291
  "nbformat": 4,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 1,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
9
  "import os\n",
10
  "from langchain_neo4j import Neo4jGraph\n",
 
 
 
11
  "\n",
12
  "neo4j_uri = os.getenv(\"NEO4J_URI\")\n",
13
  "neo4j_username = os.getenv(\"NEO4J_USERNAME\")\n",
 
18
  },
19
  {
20
  "cell_type": "code",
21
+ "execution_count": 2,
22
  "metadata": {},
23
  "outputs": [],
24
  "source": [
 
40
  },
41
  {
42
  "cell_type": "code",
43
+ "execution_count": 3,
44
  "metadata": {},
45
+ "outputs": [
46
+ {
47
+ "name": "stderr",
48
+ "output_type": "stream",
49
+ "text": [
50
+ "c:\\Users\\etulyon1\\Anaconda3\\envs\\python_env\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
51
+ " from .autonotebook import tqdm as notebook_tqdm\n"
52
+ ]
53
+ },
54
+ {
55
+ "name": "stdout",
56
+ "output_type": "stream",
57
+ "text": [
58
+ "WARNING:tensorflow:From c:\\Users\\etulyon1\\Anaconda3\\envs\\python_env\\lib\\site-packages\\tf_keras\\src\\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.\n",
59
+ "\n"
60
+ ]
61
+ },
62
+ {
63
+ "name": "stderr",
64
+ "output_type": "stream",
65
+ "text": [
66
+ "Device set to use cpu\n"
67
+ ]
68
+ }
69
+ ],
70
  "source": [
71
  "from transformers import pipeline\n",
72
  "\n",
 
92
  },
93
  {
94
  "cell_type": "code",
95
+ "execution_count": 4,
96
  "metadata": {},
97
  "outputs": [
98
  {
 
104
  }
105
  ],
106
  "source": [
 
107
  "from pdf_processing import get_existing_pdf, load_and_preprocess_pdf, split_text\n",
108
  "\n",
109
  "path_pdf_file = get_existing_pdf()\n",
 
115
  },
116
  {
117
  "cell_type": "code",
118
+ "execution_count": 5,
119
  "metadata": {},
120
  "outputs": [
121
  {
 
150
  "cell_type": "markdown",
151
  "metadata": {},
152
  "source": [
153
+ "**Convert to Graph**\n"
154
  ]
155
  },
156
  {
157
  "cell_type": "code",
158
+ "execution_count": 6,
159
  "metadata": {},
160
  "outputs": [
161
  {
162
  "name": "stdout",
163
  "output_type": "stream",
164
  "text": [
165
+ "nodes=[Node(id='Gaspard Boréal', type='Personnage', properties={}), Node(id='La Confession Muette', type='Objet', properties={}), Node(id='Théo', type='Personnage', properties={}), Node(id='Robin', type='Personnage', properties={}), Node(id='Aurélien', type='Personnage', properties={}), Node(id='Keziah', type='Personnage', properties={}), Node(id='Gaston Bachelard', type='Personnage', properties={}), Node(id='Tristan', type='Personnage', properties={}), Node(id='Anne-Hélène', type='Personnage', properties={}), Node(id='Musée', type='Lieu', properties={}), Node(id='Bruxelles', type='Lieu', properties={})] relationships=[Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='La Confession Muette', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Théo', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Robin', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Aurélien', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Keziah', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Gaston Bachelard', type='Personnage', properties={}), type='INSPIRÉ_PAR', properties={}), Relationship(source=Node(id='Tristan', type='Personnage', properties={}), target=Node(id='Musée', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Anne-Hélène', type='Personnage', properties={}), target=Node(id='Bruxelles', type='Lieu', properties={}), type='SITUE_DANS', properties={})] source=Document(metadata={}, page_content=\"Gaspard Boréal\\nLa Confession muette\\n© Gaspard Boréal, 2025\\nISBN numérique : 979-10-405-7304-3\\nwww.librinova.com\\nLe Code de la propriété intellectuelle interdit les copies ou reproductions destinées à une utilisation collective. Toute\\nreprésentation ou reproduction intégrale ou partielle faite par quelque procédé que ce soit, sans le consentement de l’auteur ou de\\nses ayants cause, est illicite et constitue une contrefaçon sanctionnée par les articles L335-2 et suivants du Code de la propriété\\nintellectuelle.\\n« À Théo, Robin, Aurélien, Keziah »\\nC’est à vos côtés que ce récit a tracé ses premières lignes\\nlaissant l’imagina\\x00on y poser sa signature\\n« Imaginer, c’est hausser le réel d’un ton. »\\nGaston Bachelard\\nScène 01 : Le guide du Musée\\nC'est son instant préféré !\\nObserver, scruter, détecter quels sont ceux qui vont préférer entrer dans\\ncet immense ascenseur dont il est le guide depuis cinq ans…\\nPosi\\x00onné inlassablement dans le même angle, au fond, proche du tableau\\nde bord lui perme\\x00ant d'ac\\x00ver toutes les op\\x00ons disponibles, pour\\nmonter aux étages les tableaux et fresques des nouvelles collec\\x00ons ou\\npour accueillir les groupes d'enfants et visiteurs, préférant le confort à la\\nmontée par les escaliers du musée.\\nPourtant si elles-ils savaient !\\nTristan n'est pas très grand, la trentaine, avec ce type de costume gris que\\nl'on ne regarde pas, un peu trop sobre, trop neutre pour a\\x00rer l'œil.\\nC'est sa pe\\x00te oreille\\x00e blanche que l'on peut remarquer si on lui prête\\na\\x00en\\x00on. Pour renforcer son rôle apparemment bien normé, Tristan\\nadopte toujours la même posture afin que les visiteurs ne s'intéressent pas\\nà lui, ni aux mouvements discrets de son corps, de ses yeux ou de ses\\nmains. Seul détail pouvant surprendre les plus a\\x00en\\x00fs : les sub\\x00les\\norchidées ton sur ton finement brodées sur ses poignets de chemise.\\nEncore faut-il pour cela le regarder, lui, si discret, si effacé !\\nPourtant, il lui suffit d'une simple montée du rez-de-chaussée vers le 3ième\\nétage de l'exposi\\x00on Magri\\x00e, de quelques sourcillements et tapotements,\\npour disposer de toutes les informa\\x00ons lui perme\\x00ant de choisir celui ou\\ncelle qu’il va portraiturer !\\nScène 02 : La directrice du musée\\n« Aujourd'hui je vais rencontrer le futur ».\\nCe\\x00e pensée amuse Anne-Hélène, alors qu'elle se \\x00ent devant le miroir\\nornementé de sa chambre baignée par la lumière douce de Bruxelles. Le\\nsilence de la pièce n’est troublé que par le léger bruissement de ses gestes.\")\n",
166
+ "nodes=[Node(id='Chambre', type='Lieu', properties={}), Node(id='Bruxelles', type='Lieu', properties={}), Node(id='Elle', type='Protagoniste', properties={}), Node(id='Brosse En Ivoire', type='Objet', properties={}), Node(id='Artiste Sculpteur', type='Personnagesecondaire', properties={}), Node(id='Cheveux', type='Objet', properties={}), Node(id='Mèches Argentées', type='Objet', properties={}), Node(id='Temps', type='Périodetemporelle', properties={}), Node(id='Reflet', type='Objet', properties={}), Node(id='Sérénité', type='Symbole', properties={}), Node(id='Moments De Solitude', type='Périodetemporelle', properties={}), Node(id='Journées', type='Périodetemporelle', properties={}), Node(id='Signes Discrets', type='Symbole', properties={}), Node(id='Passé', type='Périodetemporelle', properties={}), Node(id='Exposition', type='Événement', properties={}), Node(id='Main', type='Objet', properties={}), Node(id='Musée', type='Lieu', properties={}), Node(id='Limites De L’Imaginaire', type='Thème', properties={}), Node(id='Limites Planétaires', type='Thème', properties={}), Node(id='Artiste Contemporain', type='Personnagesecondaire', properties={}), Node(id='Limites Planétaires', type='Thème', properties={}), Node(id='Tableaux De René Magritte', type='Objet', properties={}), Node(id='Œuvres De L’Artiste', type='Objet', properties={}), Node(id='Visiteurs', type='Personnagesecondaire', properties={}), Node(id='Idées Créatives', type='Symbole', properties={}), Node(id='Anne Hélène', type='Protagoniste', properties={}), Node(id='Gaîté Lyrique', type='Lieu', properties={}), Node(id='Paris', type='Lieu', properties={}), Node(id='Musées Royaux Des Beaux-Arts De Belgique', type='Lieu', properties={}), Node(id='Situation Catastrophique', type='Événement', properties={}), Node(id='Prédécesseur', type='Personnagesecondaire', properties={}), Node(id='Collectif', type='Personnagesecondaire', properties={}), Node(id='Trente Personnes', type='Personnagesecondaire', properties={}), Node(id='Conditions De Travail Épouvantables', type='Événement', properties={}), Node(id='Menaces Régulières', type='Événement', properties={}), Node(id='Gestion Calamiteuse', type='Événement', properties={}), Node(id='Ancien Directeur', type='Personnagesecondaire', properties={}), Node(id='Esprit Magritte', type='Thème', properties={}), Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), Node(id='Humour Subtil', type='Thème', properties={}), Node(id='Paradoxes', type='Thème', properties={}), Node(id='Public', type='Personnagesecondaire', properties={}), Node(id='Réalité', type='Thème', properties={}), Node(id='Groupe De Travail', type='Personnagesecondaire', properties={}), Node(id='Jeunes Artistes Engagés', type='Personnagesecondaire', properties={})] relationships=[Relationship(source=Node(id='Chambre', type='Lieu', properties={}), target=Node(id='Bruxelles', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Brosse En Ivoire', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Brosse En Ivoire', type='Objet', properties={}), target=Node(id='Artiste Sculpteur', type='Personnagesecondaire', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Cheveux', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Cheveux', type='Objet', properties={}), target=Node(id='Mèches Argentées', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Mèches Argentées', type='Objet', properties={}), target=Node(id='Temps', type='Périodetemporelle', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Reflet', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Sérénité', type='Symbole', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Sérénité', type='Symbole', properties={}), target=Node(id='Moments De Solitude', type='Périodetemporelle', properties={}), type='SE_PRODUIT_PENDANT', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Journées', type='Périodetemporelle', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Signes Discrets', type='Symbole', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Signes Discrets', type='Symbole', properties={}), target=Node(id='Passé', type='Périodetemporelle', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Exposition', type='Événement', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Main', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Elle', type='Protagoniste', properties={}), target=Node(id='Musée', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='Limites De L’Imaginaire', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='Limites Planétaires', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='Artiste Contemporain', type='Personnagesecondaire', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Artiste Contemporain', type='Personnagesecondaire', properties={}), target=Node(id='Limites Planétaires', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Artiste Contemporain', type='Personnagesecondaire', properties={}), target=Node(id='Tableaux De René Magritte', type='Objet', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='Œuvres De L’Artiste', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Exposition', type='Événement', properties={}), target=Node(id='Visiteurs', type='Personnagesecondaire', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Visiteurs', type='Personnagesecondaire', properties={}), target=Node(id='Idées Créatives', type='Symbole', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Gaîté Lyrique', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaîté Lyrique', type='Lieu', properties={}), target=Node(id='Paris', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Musées Royaux Des Beaux-Arts De Belgique', type='Lieu', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Situation Catastrophique', type='Événement', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Situation Catastrophique', type='Événement', properties={}), target=Node(id='Prédécesseur', type='Personnagesecondaire', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Situation Catastrophique', type='Événement', properties={}), target=Node(id='Collectif', type='Personnagesecondaire', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Collectif', type='Personnagesecondaire', properties={}), target=Node(id='Trente Personnes', type='Personnagesecondaire', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Collectif', type='Personnagesecondaire', properties={}), target=Node(id='Conditions De Travail Épouvantables', type='Événement', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Collectif', type='Personnagesecondaire', properties={}), target=Node(id='Menaces Régulières', type='Événement', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Collectif', type='Personnagesecondaire', properties={}), target=Node(id='Gestion Calamiteuse', type='Événement', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Situation Catastrophique', type='Événement', properties={}), target=Node(id='Ancien Directeur', type='Personnagesecondaire', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Esprit Magritte', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Esprit Magritte', type='Thème', properties={}), target=Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), target=Node(id='Humour Subtil', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), target=Node(id='Paradoxes', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), target=Node(id='Public', type='Personnagesecondaire', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Avant-Garde Surréaliste Belge', type='Thème', properties={}), target=Node(id='Réalité', type='Thème', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Groupe De Travail', type='Personnagesecondaire', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Protagoniste', properties={}), target=Node(id='Jeunes Artistes Engagés', type='Personnagesecondaire', properties={}), type='CONNAÎT', properties={})] source=Document(metadata={}, page_content=\"ornementé de sa chambre baignée par la lumière douce de Bruxelles. Le\\nsilence de la pièce n’est troublé que par le léger bruissement de ses gestes.\\nElle saisit la brosse en ivoire, objet précieux que lui a offert un ar\\x00ste\\nsculpteur, amoureux secret et discret, et commence à démêler ses cheveux\\navec précau\\x00on. Chaque coup de brosse semble une caresse, un rituel\\nin\\x00me qu'elle accomplit avec une précision presque cérémoniale. Ses\\ncheveux, autrefois d’un noir profond, sont maintenant parsemés de légères\\nmèches argentées, témoins silencieux du passage du temps.\\nElle observe son reflet avec une a\\x00en\\x00on par\\x00culière. Elle aime éprouver\\nla sérénité de ces moments de solitude où elle proje\\x00e le futur de ses\\njournées en contemplant les signes discrets de son passé. Une ques\\x00on lui\\nrevient à l’esprit :\\n« Quel ar\\x00ste vais-je pouvoir convaincre de par\\x00ciper à la créa\\x00on de ce\\x00e\\nprochaine exposi\\x00on ? »\\nAlors que sa main con\\x00nue de parcourir ses longs cheveux, dans un va et\\nvient régulier et souple pour bien les lisser, elle prend la mesure de\\nl'incroyable challenge qu'elle a proposé à la gouvernance du musée :\\n« Limites de l’imaginaire ou Limites planétaires ? ». Une exposi\\x00on\\naudacieuse et provocante où un ar\\x00ste contemporain va explorer les neuf\\nlimites planétaires à travers le prisme surréaliste des tableaux de René\\nMagri\\x00e.\\nL’enjeu de l’exposi\\x00on : confronter les œuvres de l’ar\\x00ste et les visiteurs\\npour inspirer à ces derniers des idées créa\\x00ves.\\nIl y a seulement trois mois, Anne Hélène avait accepté de qui\\x00er la\\ndirec\\x00on de la Gaîté Lyrique à Paris pour l'une des plus pres\\x00gieuses\\nins\\x00tu\\x00ons culturelles d’Europe : les Musées Royaux des Beaux-Arts de\\nBelgique. Elle avait alors découvert une situa\\x00on catastrophique laissée par\\nson prédécesseur ! Un collec\\x00f rassemblant plus de trente personnes\\ndénonçait des « condi\\x00ons de travail épouvantables », des « menaces\\nrégulières » et une « ges\\x00on calamiteuse » et il avait contraint l'ancien\\ndirecteur à démissionner !\\nIl était urgent de relancer l'ins\\x00tu\\x00on en y restaurant l'esprit « Magri\\x00e » :\\ncelui de l’avant-garde surréaliste belge d’un humour sub\\x00l, jouant avec les\\nparadoxes pour surprendre son public, et posant des ques\\x00ons\\nphilosophiques sur la réalité. C'est ainsi qu'était née ce\\x00e idée, avec le\\ngroupe de travail qu'elle avait mis en place, dès le premier mois de sa prise\\nde poste.\\nIl fallait désormais recruter de jeunes ar\\x00stes engagés pour créer, passer\")\n",
167
+ "nodes=[Node(id='Anne Hélène', type='Personnage', properties={}), Node(id='Groupe De Travail', type='Événement', properties={}), Node(id='Jeunes Artistes', type='Personnage', properties={}), Node(id='Gaité Lyrique De Paris', type='Lieu', properties={}), Node(id='Événement', type='Événement', properties={}), Node(id='Thématique', type='Thème', properties={}), Node(id='Fils', type='Personnage', properties={}), Node(id='Askéhi', type='Personnage', properties={}), Node(id='Téléphone Bijou', type='Objet', properties={}), Node(id='Magri', type='Personnage', properties={}), Node(id='Tenue', type='Objet', properties={}), Node(id='Dressing', type='Lieu', properties={}), Node(id='Assistant Numérique', type='Personnage', properties={}), Node(id='Journée', type='Périodetemporelle', properties={}), Node(id='Jad', type='Personnage', properties={}), Node(id='Salle De Conférence', type='Lieu', properties={}), Node(id='Décoration', type='Objet', properties={}), Node(id='Murs Végétaux', type='Objet', properties={}), Node(id='Fauteuils', type='Objet', properties={}), Node(id='Allées', type='Lieu', properties={}), Node(id='Plafonds', type='Lieu', properties={}), Node(id='Repose-Pieds En Bois', type='Objet', properties={}), Node(id='Accoudoirs En Cuirs Végétaux', type='Objet', properties={}), Node(id='Écrans', type='Objet', properties={}), Node(id='Scène', type='Lieu', properties={}), Node(id='Table Ronde', type='Objet', properties={}), Node(id='Cube Rectangulaire Noir', type='Objet', properties={}), Node(id='Lumière', type='Symbole', properties={}), Node(id='Ia Avatar', type='Personnage', properties={}), Node(id='Intervention', type='Événement', properties={}), Node(id='Artiste', type='Personnage', properties={}), Node(id='Maître De Conférences', type='Personnage', properties={}), Node(id='Public', type='Personnage', properties={}), Node(id='Espace Urbain En 2100', type='Thème', properties={})] relationships=[Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Groupe De Travail', type='Événement', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Gaité Lyrique De Paris', type='Lieu', properties={}), target=Node(id='Événement', type='Événement', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Événement', type='Événement', properties={}), target=Node(id='Thématique', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Fils', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Askéhi', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Téléphone Bijou', type='Objet', properties={}), target=Node(id='Magri', type='Personnage', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Anne Hélène', type='Personnage', properties={}), target=Node(id='Assistant Numérique', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Assistant Numérique', type='Personnage', properties={}), target=Node(id='Journée', type='Périodetemporelle', properties={}), type='SE_PRODUIT_PENDANT', properties={}), Relationship(source=Node(id='Jad', type='Personnage', properties={}), target=Node(id='Salle De Conférence', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Cube Rectangulaire Noir', type='Objet', properties={}), target=Node(id='Ia Avatar', type='Personnage', properties={}), type='REPRÉSENTE', properties={}), Relationship(source=Node(id='Jad', type='Personnage', properties={}), target=Node(id='Artiste', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Jad', type='Personnage', properties={}), target=Node(id='Maître De Conférences', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Intervention', type='Événement', properties={}), target=Node(id='Espace Urbain En 2100', type='Thème', properties={}), type='IMPLIQUE', properties={})] source=Document(metadata={}, page_content=\"groupe de travail qu'elle avait mis en place, dès le premier mois de sa prise\\nde poste.\\nIl fallait désormais recruter de jeunes ar\\x00stes engagés pour créer, passer\\nde l'idée au projet et convaincre les financeurs, ins\\x00tu\\x00ons et médias pour\\nle soutenir.\\nHasard de calendrier, la Gaité Lyrique de Paris organisait ce\\x00e semaine un\\névénement avec une théma\\x00que inspirante « Repenser le design et\\nl'architecture pour mieux respecter les limites planétaires ». Revenir à Paris\\nallait également lui perme\\x00re de revoir son fils et de faire la connaissance\\nde sa première pe\\x00te fille Askéhi née depuis à peine une semaine.\\nL'heure du départ approchant, un peu machinalement, Anne Hélène se\\nsaisit de son téléphone bijou pour le porter à son cou. Celui-ci se mit à lui\\nparler :\\n« Anne Hélène, laisse-moi être ton Magri\\x00e personnel ce ma\\x00n. Que\\ndirais-tu d'une tenue originale parfaitement adaptée à tes rendez-vous sur\\nParis ? »\\n« Merci mais je peux choisir seule ! » et d’un pas décidé, elle se dirigea vers\\nson dressing.\\nAvec une pointe d'ironie, son assistant numérique lui répondit :\\n« Ravi de te voir si réac\\x00ve Anne-Hélène. Ma proposi\\x00on, bien que rejetée,\\na eu au moins le mérite de te reme\\x00re dans le tempo de ce\\x00e journée\\ncruciale ! Belle journée ! »\\nScène 03 : L'ar\\x00ste\\n« Je ne pensais pas découvrir une décora\\x00on de la sorte dans une salle de\\nconférence et encore moins à la Gaîté Lyrique ! » pensa Jad.\\n« Entrer dans une salle de colloque couverte de murs végétaux, c’est sûr\\nque depuis dix ans ce n’est plus une nouveauté ! Par contre là il y en a\\npartout : autour des fauteuils, des allées, aux murs, aux plafonds, seuls les\\nrepose-pieds en bois et les accoudoirs en cuirs végétaux perme\\x00ent de s'y\\nposer sans écraser des plantes. Et bien sûr pour enfoncer la sensa\\x00on\\nd'immersion végétale et technologique, des écrans, des écrans, des\\nécrans ! Trois géants trônent autour de la scène : un à gauche, un à droite\\net, plus surprenant, un dernier sous les pieds de la table ronde. Et au\\ncentre sur la table basse, un pe\\x00t cube rectangulaire noir avec une faible\\nlumière qui clignote à la vitesse d'une pulsa\\x00on lente : l’IA avatar de la\\nconférence j'en suis sûr ! ».\\nJad analysa la scène afin de préparer son interven\\x00on qui allait débuter\\ndans quelques minutes. Il s’y trouve avec un autre ar\\x00ste et un maître de\\nconférences assis face au public. Le thème de la table ronde : « Quelle\\nnouvelle place pour le vivant dans l'espace urbain en 2100 ! ». Pari risqué\")\n",
168
+ "nodes=[Node(id='Conférences', type='Événement', properties={}), Node(id='Table Ronde', type='Événement', properties={}), Node(id='Thème', type='Thème', properties={}), Node(id='Pari', type='Événement', properties={}), Node(id='Congrès', type='Événement', properties={}), Node(id='Animateur', type='Personnage', properties={}), Node(id='Public', type='Personnage', properties={}), Node(id=\"Forces De L'Ordre\", type='Personnage', properties={}), Node(id='Œuvres-Performances', type='Objet', properties={}), Node(id='Données D’Enregistrement', type='Objet', properties={}), Node(id='Images', type='Objet', properties={}), Node(id='Sons', type='Objet', properties={}), Node(id='Oufils', type='Objet', properties={}), Node(id='Événement', type='Événement', properties={}), Node(id='Organisateur', type='Personnage', properties={}), Node(id='Parficipants', type='Personnage', properties={}), Node(id='Débat', type='Événement', properties={}), Node(id='Arfistes Internafionaux', type='Personnage', properties={}), Node(id='Réflexion Collecfive', type='Événement', properties={}), Node(id='Design', type='Thème', properties={}), Node(id='Architecture Urbaine', type='Thème', properties={}), Node(id='Horizon 2100', type='Périodetemporelle', properties={}), Node(id='Préambule', type='Événement', properties={}), Node(id='Société', type='Personnage', properties={}), Node(id='Sensibilité Environnementale', type='Thème', properties={}), Node(id='Biophilie', type='Thème', properties={}), Node(id='Dérèglement Climafique', type='Événement', properties={}), Node(id='Pays Émergents', type='Personnage', properties={}), Node(id='Nafions', type='Personnage', properties={}), Node(id='Financement', type='Thème', properties={}), Node(id='Construcfions', type='Événement', properties={}), Node(id='Rénovafions Énergéfiques Urbaines', type='Événement', properties={}), Node(id='Revendicafions', type='Événement', properties={}), Node(id='Étude', type='Événement', properties={}), Node(id='Onu', type='Personnage', properties={}), Node(id='Personnalités', type='Personnage', properties={}), Node(id=\"Groupes D'Acfivités\", type='Personnage', properties={}), Node(id='Pistes', type='Thème', properties={}), Node(id='Voix Crédibles', type='Thème', properties={}), Node(id='Arfistes', type='Personnage', properties={}), Node(id='Explorateurs', type='Personnage', properties={}), Node(id='Innovateurs', type='Personnage', properties={}), Node(id='Figures Héroïques', type='Personnage', properties={}), Node(id='Êtres', type='Personnage', properties={}), Node(id='Mots', type='Symbole', properties={}), Node(id='Alexandre Kwan', type='Personnage', properties={})] relationships=[Relationship(source=Node(id='Conférences', type='Événement', properties={}), target=Node(id='Table Ronde', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Table Ronde', type='Événement', properties={}), target=Node(id='Thème', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Pari', type='Événement', properties={}), target=Node(id='Thème', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Congrès', type='Événement', properties={}), target=Node(id='Animateur', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Congrès', type='Événement', properties={}), target=Node(id='Public', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id=\"Forces De L'Ordre\", type='Personnage', properties={}), target=Node(id='Œuvres-Performances', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id=\"Forces De L'Ordre\", type='Personnage', properties={}), target=Node(id='Données D’Enregistrement', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Images', type='Objet', properties={}), target=Node(id='Sons', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Oufils', type='Objet', properties={}), target=Node(id='Images', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Oufils', type='Objet', properties={}), target=Node(id='Sons', type='Objet', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Événement', type='Événement', properties={}), target=Node(id='Organisateur', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Événement', type='Événement', properties={}), target=Node(id='Parficipants', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Événement', type='Événement', properties={}), target=Node(id='Thème', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Animateur', type='Personnage', properties={}), target=Node(id='Débat', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Débat', type='Événement', properties={}), target=Node(id='Arfistes Internafionaux', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Débat', type='Événement', properties={}), target=Node(id='Réflexion Collecfive', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Réflexion Collecfive', type='Événement', properties={}), target=Node(id='Design', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Réflexion Collecfive', type='Événement', properties={}), target=Node(id='Architecture Urbaine', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Réflexion Collecfive', type='Événement', properties={}), target=Node(id='Horizon 2100', type='Périodetemporelle', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Animateur', type='Personnage', properties={}), target=Node(id='Préambule', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Préambule', type='Événement', properties={}), target=Node(id='Société', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Société', type='Personnage', properties={}), target=Node(id='Sensibilité Environnementale', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Sensibilité Environnementale', type='Thème', properties={}), target=Node(id='Biophilie', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Dérèglement Climafique', type='Événement', properties={}), target=Node(id='Pays Émergents', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Pays Émergents', type='Personnage', properties={}), target=Node(id='Nafions', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Nafions', type='Personnage', properties={}), target=Node(id='Financement', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Financement', type='Thème', properties={}), target=Node(id='Construcfions', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Financement', type='Thème', properties={}), target=Node(id='Rénovafions Énergéfiques Urbaines', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Revendicafions', type='Événement', properties={}), target=Node(id='Étude', type='Événement', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Étude', type='Événement', properties={}), target=Node(id='Onu', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Étude', type='Événement', properties={}), target=Node(id='Personnalités', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Étude', type='Événement', properties={}), target=Node(id=\"Groupes D'Acfivités\", type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Étude', type='Événement', properties={}), target=Node(id='Pistes', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Étude', type='Événement', properties={}), target=Node(id='Voix Crédibles', type='Thème', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Voix Crédibles', type='Thème', properties={}), target=Node(id='Arfistes', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Voix Crédibles', type='Thème', properties={}), target=Node(id='Explorateurs', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Voix Crédibles', type='Thème', properties={}), target=Node(id='Innovateurs', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Arfistes', type='Personnage', properties={}), target=Node(id='Figures Héroïques', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Explorateurs', type='Personnage', properties={}), target=Node(id='Figures Héroïques', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Innovateurs', type='Personnage', properties={}), target=Node(id='Figures Héroïques', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Figures Héroïques', type='Personnage', properties={}), target=Node(id='Êtres', type='Personnage', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Êtres', type='Personnage', properties={}), target=Node(id='Mots', type='Symbole', properties={}), type='IMPLIQUE', properties={}), Relationship(source=Node(id='Animateur', type='Personnage', properties={}), target=Node(id='Alexandre Kwan', type='Personnage', properties={}), type='IMPLIQUE', properties={})] source=Document(metadata={}, page_content=\"conférences assis face au public. Le thème de la table ronde : « Quelle\\nnouvelle place pour le vivant dans l'espace urbain en 2100 ! ». Pari risqué\\nen 2037 de se projeter à plus de soixante ans sur ce type de sujet !\\nIl s’est promis de ne pas basculer trop vite dans l'alterca\\x00on. Il n’a pas\\noublié le dernier Congrès où il provoqua l'animateur et le public. Cela s'est\\nfini au poste. Deux heures à répondre aux ques\\x00ons des forces de l'ordre\\nsur l'origine des fonds pour financer ses œuvres-performances, le lieu de\\nstockage des données d’enregistrement des conférences, les ou\\x00ls u\\x00lisés\\npour projeter les images et les sons. Il a bien cru qu’il ne pourrait sor\\x00r\\nsans avoir à donner les clés de ses ou\\x00ls ! Du coup, il ne pensait pas être de\\nnouveau invité aussi vite sur ce type d’événement. Ce\\x00e fois-ci, il s’est dit :\\n« tu te prépares, tu écoutes, tu regardes, tu “hackes” discrètement les\\ninforma\\x00ons sur l’organisateur, les par\\x00cipants et le thème afin d'être\\nprêt… si besoin. »\\nL'animateur ouvre le débat :\\n« Enchanté d'accueillir ici, deux grands ar\\x00stes interna\\x00onaux pour ce\\x00e\\nréflexion collec\\x00ve : repenser la place du vivant dans le design et\\nl'architecture urbaine à l’horizon 2100 et si vous le perme\\x00ez, je\\ncommencerai par un court préambule pour resituer ce débat :\\nDepuis le début du XXIe siècle, la société a évolué vers une sensibilité\\nenvironnementale exacerbée - la culture de la 'biophilie' qui place la\\nconnexion humaine avec la nature au centre des préoccupa\\x00ons, s'est\\nimposée dans l'architecture et le design urbain. Pour autant, le\\ndérèglement clima\\x00que ne cesse de s'accentuer ainsi que la pression des\\npays émergents sur les grandes na\\x00ons pour leur faire payer leurs parts de\\nfinancement des nouvelles construc\\x00ons et rénova\\x00ons énergé\\x00ques\\nurbaines. En parallèle de ces revendica\\x00ons, la dernière étude menée par\\nl'ONU sur les personnalités et groupes d'ac\\x00vités les mieux classés pour\\ninventer, explorer de nouvelles pistes vous placent, vous les ar\\x00stes, dans\\nle top trois des voix crédibles.\\nJe cite :\\n« Les ar\\x00stes, explorateurs, innovateurs, sont les figures héroïques qui ont\\nla sensibilité de ce qui se passe, de ce qui se joue autour d'eux. Ce sont les\\nêtres qui captent et éme\\x00ent ce pourquoi nous n'avons pas encore les\\nmots, les images ou les sons. »\\nAlors, aujourd'hui en 2037, vous qui avez ce\\x00e sensibilité si par\\x00culière,\\ndites-nous :\\n« Quelle nouvelle place pour le vivant dans l'espace urbain à horizon\\n2100 ? Qu’en pensez-vous Alexandre Kwan ? »\")\n",
169
+ "nodes=[Node(id='Alexandre Kwan', type='Personnage', properties={}), Node(id='Jad Wahid', type='Personnage', properties={}), Node(id='Jad', type='Personnage', properties={}), Node(id='Ia Avatar', type='Personnage', properties={}), Node(id='Anne Hélène', type='Personnage', properties={}), Node(id='Ville-Forêt', type='Lieu', properties={}), Node(id='Pont Futuriste', type='Objet', properties={}), Node(id='Écoquartier Nocturne', type='Lieu', properties={}), Node(id='Ville', type='Lieu', properties={}), Node(id='Brouillard Matinal', type='Événement', properties={}), Node(id='Sommets En Gratti-Ciel Arborisés', type='Objet', properties={}), Node(id='Brume', type='Événement', properties={}), Node(id='Îles De Verdure', type='Lieu', properties={}), Node(id='2100', type='Périodetemporelle', properties={})] relationships=[Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Jad Wahid', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='2100', type='Périodetemporelle', properties={}), type='SE_PRODUIT_PENDANT', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Ville-Forêt', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Pont Futuriste', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Écoquartier Nocturne', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Ville', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Brouillard Matinal', type='Événement', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Sommets En Gratti-Ciel Arborisés', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Brume', type='Événement', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Alexandre Kwan', type='Personnage', properties={}), target=Node(id='Îles De Verdure', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Jad', type='Personnage', properties={}), target=Node(id='Alexandre Kwan', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Ville-Forêt', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Pont Futuriste', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Écoquartier Nocturne', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Ville', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Brouillard Matinal', type='Événement', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Sommets En Gratti-Ciel Arborisés', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Brume', type='Événement', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Ia Avatar', type='Personnage', properties={}), target=Node(id='Îles De Verdure', type='Lieu', properties={}), type='CRÉÉ_PAR', properties={})] source=Document(metadata={}, page_content=\"dites-nous :\\n« Quelle nouvelle place pour le vivant dans l'espace urbain à horizon\\n2100 ? Qu’en pensez-vous Alexandre Kwan ? »\\nCheveux noirs grisonnant aux tempes, Alexandre a ce\\x00e apparence qui\\nincarne l’équilibre entre modernité maîtrisée et nature. « Pas étonnant\\nqu’il lui donne la parole en premier ! Avec lui pas de risque de\\ndébordement » se dit intérieurement Jad.\\nD’une voix calme, Alexandre Kwan répond :\\n« De mon point de vue, et je laisserai bien sûr Jad Wahid réagir, en 2100 la\\nville ne sera plus un simple agglomérat de béton et de verre végétalisé plus\\nou moins agrémenté d’espaces verts. Ce sera un véritable écosystème\\nrespirant comme un organisme. Le vivant y retrouvera sa place non pas\\ncomme un élément décora\\x00f ou de confort, mais comme une composante\\nessen\\x00elle. J’imagine des murs, des toits, des rues en\\x00èrement colonisés\\npar les plantes, où chaque espace urbain deviendrait un habitat pour des\\nespèces végétales et animales. Le béton ne fera plus suffoquer la nature, il\\nla portera. Le défi sera d’harmoniser ces vies, de choisir les espèces en\\nfonc\\x00on des microclimats urbains, comme je l'ai fait avec mes dernières\\nréalisa\\x00ons. 2100, ce sera le futur où l’homme ne se contentera plus de\\nbâ\\x00r, mais de cohabiter, d’inviter la nature à redessiner nos cités. »\\n« Pas de surprise ! J’avais vu juste » se dit Jad.\\nL’IA avatar de la conférence se mit à projeter sur les trois écrans des images\\nfuturistes de Ville-Forêt ver\\x00cale, d’infrastructure bioluminescente\\nreprenant les thèmes abordés par Alexandre Kwan. Le flux d’images, rapide\\nau début, ralen\\x00t progressivement. Jad évalua l’audience de la salle : il y\\navait bien 200 personnes, la plupart les yeux rivés sur les écrans. Facile\\npour l’IA de la conférence de détecter les 3 images ayant suscité le plus\\nd’admira\\x00on auprès des par\\x00cipants et d’arrêter leur flux sur trois\\nconfigura\\x00ons provoquant un souffle d’admira\\x00on dans la salle : un pont\\nfuturiste en matériaux éco-éclairants, enjambant une rivière avec des\\nreflets verts et bleus, un écoquar\\x00er nocturne illuminé par des lanternes\\nen bois le long de chemins bordés de végéta\\x00on, une ville plongée dans un\\nbrouillard ma\\x00nal avec des sommets en gra\\x00e-ciel arborisés émergeant\\ncomme des îles de verdure au-dessus de la brume.\\nAu premier rang, Anne Hélène était comme suffoquée par ce qui se\\ndéroulait sous ses yeux. Partagée par l’émerveillement de la scénographie\\net le contenu du message porté par ces images : du design, du design du\")\n",
170
+ "Nodes:[Node(id='Gaspard Boréal', type='Personnage', properties={}), Node(id='La Confession Muette', type='Objet', properties={}), Node(id='Théo', type='Personnage', properties={}), Node(id='Robin', type='Personnage', properties={}), Node(id='Aurélien', type='Personnage', properties={}), Node(id='Keziah', type='Personnage', properties={}), Node(id='Gaston Bachelard', type='Personnage', properties={}), Node(id='Tristan', type='Personnage', properties={}), Node(id='Anne-Hélène', type='Personnage', properties={}), Node(id='Musée', type='Lieu', properties={}), Node(id='Bruxelles', type='Lieu', properties={})]\n",
171
+ "Relationships:[Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='La Confession Muette', type='Objet', properties={}), type='CRÉÉ_PAR', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Théo', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Robin', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Aurélien', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Keziah', type='Personnage', properties={}), type='CONNAÎT', properties={}), Relationship(source=Node(id='Gaspard Boréal', type='Personnage', properties={}), target=Node(id='Gaston Bachelard', type='Personnage', properties={}), type='INSPIRÉ_PAR', properties={}), Relationship(source=Node(id='Tristan', type='Personnage', properties={}), target=Node(id='Musée', type='Lieu', properties={}), type='SITUE_DANS', properties={}), Relationship(source=Node(id='Anne-Hélène', type='Personnage', properties={}), target=Node(id='Bruxelles', type='Lieu', properties={}), type='SITUE_DANS', properties={})]\n"
 
 
 
 
 
 
 
 
 
 
 
 
172
  ]
173
  }
174
  ],
 
189
  "\n",
190
  "graph.add_graph_documents(graph_documents_filtered, baseEntityLabel=True)"
191
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  }
193
  ],
194
  "metadata": {
195
  "kernelspec": {
196
+ "display_name": "Python 3.10.16 ('python_env')",
197
  "language": "python",
198
  "name": "python3"
199
  },
 
207
  "name": "python",
208
  "nbconvert_exporter": "python",
209
  "pygments_lexer": "ipython3",
210
+ "version": "3.10.16"
211
+ },
212
+ "vscode": {
213
+ "interpreter": {
214
+ "hash": "61b9aa377fb83f63bad7741d096e61bd539c2bb481b5373a09d4b308f1781ad2"
215
+ }
216
  }
217
  },
218
  "nbformat": 4,
neo4j.ipynb DELETED
@@ -1,257 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 21,
6
- "metadata": {},
7
- "outputs": [
8
- {
9
- "data": {
10
- "text/plain": [
11
- "True"
12
- ]
13
- },
14
- "execution_count": 21,
15
- "metadata": {},
16
- "output_type": "execute_result"
17
- }
18
- ],
19
- "source": [
20
- "from dotenv import load_dotenv\n",
21
- "from neo4j import GraphDatabase\n",
22
- "\n",
23
- "# Charger les variables d'environnement\n",
24
- "load_dotenv()"
25
- ]
26
- },
27
- {
28
- "cell_type": "code",
29
- "execution_count": 22,
30
- "metadata": {},
31
- "outputs": [],
32
- "source": [
33
- "import os \n",
34
- "\n",
35
- "# Initialize Neo4j connection\n",
36
- "neo4j_uri = os.getenv(\"NEO4J_URI\")\n",
37
- "neo4j_username = os.getenv(\"NEO4J_USERNAME\")\n",
38
- "neo4j_password = os.getenv(\"NEO4J_PASSWORD\")\n",
39
- "neo4j_driver = GraphDatabase.driver(\n",
40
- " neo4j_uri,\n",
41
- " auth=(neo4j_username, neo4j_password)\n",
42
- ")\n",
43
- "\n",
44
- "neo4j_driver.verify_connectivity()\n"
45
- ]
46
- },
47
- {
48
- "cell_type": "markdown",
49
- "metadata": {},
50
- "source": [
51
- "# LLM"
52
- ]
53
- },
54
- {
55
- "cell_type": "code",
56
- "execution_count": 30,
57
- "metadata": {},
58
- "outputs": [],
59
- "source": [
60
- "from langchain_mistralai.chat_models import ChatMistralAI\n",
61
- "\n",
62
- "mistral_api_key = os.getenv(\"MISTRAL_API_KEY\")\n",
63
- "# neo4j_llm = ChatMistralAI(\n",
64
- "# model=\"mistral-large-latest\",\n",
65
- "# api_key=mistral_api_key,\n",
66
- "# temperature=0,\n",
67
- "# stream=False,\n",
68
- "# verbose=True\n",
69
- "# )\n",
70
- "\n",
71
- "from neo4j_graphrag.llm import MistralAILLM\n",
72
- "\n",
73
- "neo4j_llm = MistralAILLM(\n",
74
- " model_name=\"mistral-large-latest\"\n",
75
- ")"
76
- ]
77
- },
78
- {
79
- "cell_type": "markdown",
80
- "metadata": {},
81
- "source": [
82
- "# Emdedding"
83
- ]
84
- },
85
- {
86
- "cell_type": "code",
87
- "execution_count": 24,
88
- "metadata": {},
89
- "outputs": [],
90
- "source": [
91
- "from langchain.embeddings import HuggingFaceEmbeddings\n",
92
- "\n",
93
- "embeddings = HuggingFaceEmbeddings(model_name=\"intfloat/multilingual-e5-large\")"
94
- ]
95
- },
96
- {
97
- "cell_type": "markdown",
98
- "metadata": {},
99
- "source": [
100
- "# Graph Initialization"
101
- ]
102
- },
103
- {
104
- "cell_type": "code",
105
- "execution_count": 25,
106
- "metadata": {},
107
- "outputs": [],
108
- "source": [
109
- "basic_node_labels = [ \n",
110
- " \"Personnage\",\n",
111
- " \"Objet\",\n",
112
- " \"Lieu\",\n",
113
- " \"Événement\",\n",
114
- " \"PériodeTemporelle\"\n",
115
- "]\n",
116
- "\n",
117
- "story_node_labels = [\"Protagoniste\", \"Antagoniste\", \"PersonnageSecondaire\", \"CréatureMythique\",\n",
118
- " \"FigureHistorique\", \"Narrateur\"]\n",
119
- "\n",
120
- "literary_node_labels = [\"Thème\", \"Motif\", \"Symbole\"]\n",
121
- "\n",
122
- "node_labels = basic_node_labels + story_node_labels + literary_node_labels\n",
123
- "\n",
124
- "rel_types = [\"CONNAÎT\", \"SITUE_DANS\", \"FAIT_PARTIE_DE\", \"SE_PRODUIT_PENDANT\", \"IMPLIQUE\", \n",
125
- " \"S'OPPOSE_À\", \"CRÉÉ_PAR\", \"INSPIRÉ_PAR\", \"REPRÉSENTE\", \"TRANSFORME\"]\n"
126
- ]
127
- },
128
- {
129
- "cell_type": "markdown",
130
- "metadata": {},
131
- "source": [
132
- "# Prompt template"
133
- ]
134
- },
135
- {
136
- "cell_type": "code",
137
- "execution_count": 26,
138
- "metadata": {},
139
- "outputs": [],
140
- "source": [
141
- "prompt_template = '''\n",
142
- " Vous êtes un expert en analyse de texte chargé d'extraire des informations à partir d'un récit et de les structurer sous forme de graphe de propriétés pour faciliter la compréhension et l'analyse du texte.\n",
143
- "\n",
144
- " Extrayez les entités (nœuds) et spécifiez leur type à partir du texte d'entrée suivant.\n",
145
- " Extrayez également les relations entre ces nœuds. La direction de la relation va du nœud de départ au nœud d'arrivée.\n",
146
- "\n",
147
- " Retournez le résultat au format JSON en utilisant le modèle suivant :\n",
148
- " {{\n",
149
- " \"nodes\": [\n",
150
- " {{\n",
151
- " \"id\": \"0\",\n",
152
- " \"label\": \"type d'entité\",\n",
153
- " \"properties\": {{\n",
154
- " \"name\": \"nom de l'entité\"\n",
155
- " }}\n",
156
- " }}\n",
157
- " ],\n",
158
- " \"relationships\": [\n",
159
- " {{\n",
160
- " \"type\": \"TYPE_DE_RELATION\",\n",
161
- " \"start_node_id\": \"0\",\n",
162
- " \"end_node_id\": \"1\",\n",
163
- " \"properties\": {{\n",
164
- " \"details\": \"Description de la relation\"\n",
165
- " }}\n",
166
- " }}\n",
167
- " ]\n",
168
- " }}\n",
169
- "\n",
170
- " - Utilisez uniquement les informations du texte d'entrée. N'ajoutez aucune information supplémentaire.\n",
171
- " - Si le texte d'entrée est vide, retournez un JSON vide.\n",
172
- " - Créez autant de nœuds et de relations que nécessaire pour offrir un contexte riche et détaillé.\n",
173
- " - Un assistant de connaissance basé sur l'IA doit pouvoir lire ce graphe et comprendre immédiatement le contexte pour poser des questions détaillées.\n",
174
- " Utilisez uniquement les nœuds et relations suivants (s'ils sont fournis) :\n",
175
- " \n",
176
- "\n",
177
- " Attribuez un identifiant unique (chaîne de caractères) à chaque nœud et réutilisez-le pour définir les relations.\n",
178
- " Respectez les types de nœuds source et cible pour les relations, ainsi que la direction des relations.\n",
179
- "\n",
180
- " Ne retournez aucune information supplémentaire autre que le JSON.\n",
181
- "\n",
182
- " \n",
183
- "\n",
184
- " Texte d'entrée :\n",
185
- "\n",
186
- " '''"
187
- ]
188
- },
189
- {
190
- "cell_type": "markdown",
191
- "metadata": {},
192
- "source": [
193
- "# Generate Pipeline"
194
- ]
195
- },
196
- {
197
- "cell_type": "code",
198
- "execution_count": 31,
199
- "metadata": {},
200
- "outputs": [
201
- {
202
- "ename": "PipelineDefinitionError",
203
- "evalue": "",
204
- "output_type": "error",
205
- "traceback": [
206
- "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
207
- "\u001b[1;31mValidationError\u001b[0m Traceback (most recent call last)",
208
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\neo4j_graphrag\\experimental\\pipeline\\kg_builder.py:95\u001b[0m, in \u001b[0;36mSimpleKGPipeline.__init__\u001b[1;34m(self, llm, driver, embedder, entities, relations, potential_schema, from_pdf, text_splitter, pdf_loader, kg_writer, on_error, prompt_template, perform_entity_resolution, lexical_graph_config, neo4j_database)\u001b[0m\n\u001b[0;32m 94\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 95\u001b[0m config \u001b[38;5;241m=\u001b[39m \u001b[43mSimpleKGPipelineConfig\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 96\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# argument type are fixed in the Config object\u001b[39;49;00m\n\u001b[0;32m 97\u001b[0m \u001b[43m \u001b[49m\u001b[43mllm_config\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mllm\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[0;32m 98\u001b[0m \u001b[43m \u001b[49m\u001b[43mneo4j_config\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdriver\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[0;32m 99\u001b[0m \u001b[43m \u001b[49m\u001b[43membedder_config\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43membedder\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[0;32m 100\u001b[0m \u001b[43m \u001b[49m\u001b[43mentities\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mentities\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 101\u001b[0m \u001b[43m \u001b[49m\u001b[43mrelations\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrelations\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 102\u001b[0m \u001b[43m \u001b[49m\u001b[43mpotential_schema\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpotential_schema\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 103\u001b[0m \u001b[43m \u001b[49m\u001b[43mfrom_pdf\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfrom_pdf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 104\u001b[0m \u001b[43m \u001b[49m\u001b[43mpdf_loader\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mComponentType\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpdf_loader\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mpdf_loader\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 105\u001b[0m \u001b[43m \u001b[49m\u001b[43mkg_writer\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mComponentType\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkg_writer\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkg_writer\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 106\u001b[0m \u001b[43m \u001b[49m\u001b[43mtext_splitter\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mComponentType\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext_splitter\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mtext_splitter\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 107\u001b[0m \u001b[43m \u001b[49m\u001b[43mon_error\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mOnError\u001b[49m\u001b[43m(\u001b[49m\u001b[43mon_error\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 108\u001b[0m \u001b[43m \u001b[49m\u001b[43mprompt_template\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprompt_template\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 109\u001b[0m \u001b[43m \u001b[49m\u001b[43mperform_entity_resolution\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mperform_entity_resolution\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 110\u001b[0m \u001b[43m \u001b[49m\u001b[43mlexical_graph_config\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mlexical_graph_config\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 111\u001b[0m \u001b[43m \u001b[49m\u001b[43mneo4j_database\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mneo4j_database\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 112\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (ValidationError, \u001b[38;5;167;01mValueError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n",
209
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\pydantic\\main.py:214\u001b[0m, in \u001b[0;36mBaseModel.__init__\u001b[1;34m(self, **data)\u001b[0m\n\u001b[0;32m 213\u001b[0m __tracebackhide__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m--> 214\u001b[0m validated_self \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mself_instance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 215\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m validated_self:\n",
210
- "\u001b[1;31mValidationError\u001b[0m: 2 validation errors for SimpleKGPipelineConfig\nembedder_config.default.is-instance[Embedder]\n Input should be an instance of Embedder [type=is_instance_of, input_value=HuggingFaceEmbeddings(cli...se, show_progress=False), input_type=HuggingFaceEmbeddings]\n For further information visit https://errors.pydantic.dev/2.10/v/is_instance_of\nembedder_config.default.EmbedderConfig\n Input should be a valid dictionary or instance of EmbedderConfig [type=model_type, input_value=HuggingFaceEmbeddings(cli...se, show_progress=False), input_type=HuggingFaceEmbeddings]\n For further information visit https://errors.pydantic.dev/2.10/v/model_type",
211
- "\nThe above exception was the direct cause of the following exception:\n",
212
- "\u001b[1;31mPipelineDefinitionError\u001b[0m Traceback (most recent call last)",
213
- "Cell \u001b[1;32mIn[31], line 4\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mneo4j_graphrag\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mexperimental\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcomponents\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtext_splitters\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfixed_size_splitter\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FixedSizeSplitter\n\u001b[0;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mneo4j_graphrag\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mexperimental\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpipeline\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mkg_builder\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m SimpleKGPipeline\n\u001b[1;32m----> 4\u001b[0m kg_builder_pdf \u001b[38;5;241m=\u001b[39m \u001b[43mSimpleKGPipeline\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 5\u001b[0m \u001b[43m \u001b[49m\u001b[43mllm\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mneo4j_llm\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 6\u001b[0m \u001b[43m \u001b[49m\u001b[43mdriver\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mneo4j_driver\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 7\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# text_splitter=FixedSizeSplitter(chunk_size=1024, chunk_overlap=200),\u001b[39;49;00m\n\u001b[0;32m 8\u001b[0m \u001b[43m \u001b[49m\u001b[43membedder\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43membeddings\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 9\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# entities=node_labels,\u001b[39;49;00m\n\u001b[0;32m 10\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# relations=rel_types,\u001b[39;49;00m\n\u001b[0;32m 11\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# prompt_template=prompt_template,\u001b[39;49;00m\n\u001b[0;32m 12\u001b[0m \u001b[43m \u001b[49m\u001b[43mfrom_pdf\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\n\u001b[0;32m 13\u001b[0m \u001b[43m)\u001b[49m\n",
214
- "File \u001b[1;32mc:\\Users\\cd\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\neo4j_graphrag\\experimental\\pipeline\\kg_builder.py:114\u001b[0m, in \u001b[0;36mSimpleKGPipeline.__init__\u001b[1;34m(self, llm, driver, embedder, entities, relations, potential_schema, from_pdf, text_splitter, pdf_loader, kg_writer, on_error, prompt_template, perform_entity_resolution, lexical_graph_config, neo4j_database)\u001b[0m\n\u001b[0;32m 95\u001b[0m config \u001b[38;5;241m=\u001b[39m SimpleKGPipelineConfig(\n\u001b[0;32m 96\u001b[0m \u001b[38;5;66;03m# argument type are fixed in the Config object\u001b[39;00m\n\u001b[0;32m 97\u001b[0m llm_config\u001b[38;5;241m=\u001b[39mllm, \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 111\u001b[0m neo4j_database\u001b[38;5;241m=\u001b[39mneo4j_database,\n\u001b[0;32m 112\u001b[0m )\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (ValidationError, \u001b[38;5;167;01mValueError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m--> 114\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m PipelineDefinitionError() \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[0;32m 116\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrunner \u001b[38;5;241m=\u001b[39m PipelineRunner\u001b[38;5;241m.\u001b[39mfrom_config(config)\n",
215
- "\u001b[1;31mPipelineDefinitionError\u001b[0m: "
216
- ]
217
- }
218
- ],
219
- "source": [
220
- "from neo4j_graphrag.experimental.components.text_splitters.fixed_size_splitter import FixedSizeSplitter\n",
221
- "from neo4j_graphrag.experimental.pipeline.kg_builder import SimpleKGPipeline\n",
222
- "\n",
223
- "kg_builder_pdf = SimpleKGPipeline(\n",
224
- " llm=neo4j_llm,\n",
225
- " driver=neo4j_driver,\n",
226
- " # text_splitter=FixedSizeSplitter(chunk_size=1024, chunk_overlap=200),\n",
227
- " embedder=embeddings,\n",
228
- " # entities=node_labels,\n",
229
- " # relations=rel_types,\n",
230
- " # prompt_template=prompt_template,\n",
231
- " from_pdf=False\n",
232
- ")"
233
- ]
234
- }
235
- ],
236
- "metadata": {
237
- "kernelspec": {
238
- "display_name": "Python 3",
239
- "language": "python",
240
- "name": "python3"
241
- },
242
- "language_info": {
243
- "codemirror_mode": {
244
- "name": "ipython",
245
- "version": 3
246
- },
247
- "file_extension": ".py",
248
- "mimetype": "text/x-python",
249
- "name": "python",
250
- "nbconvert_exporter": "python",
251
- "pygments_lexer": "ipython3",
252
- "version": "3.12.2"
253
- }
254
- },
255
- "nbformat": 4,
256
- "nbformat_minor": 2
257
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
neo4j_initialize.py DELETED
@@ -1,90 +0,0 @@
1
- import asyncio
2
- import os
3
- from pathlib import Path
4
- from dotenv import load_dotenv
5
- from langchain_community.document_loaders import PyPDFLoader
6
- from langchain_text_splitters import CharacterTextSplitter
7
- from neo4j import AsyncGraphDatabase
8
- from neo4j_graphrag.experimental.components.pdf_loader import DataLoader
9
- from neo4j_graphrag.experimental.components.text_splitters.langchain import LangChainTextSplitterAdapter
10
- from neo4j_graphrag.experimental.components.types import PdfDocument, DocumentInfo
11
- from neo4j_graphrag.experimental.pipeline.kg_builder import SimpleKGPipeline
12
- from langchain.embeddings import HuggingFaceEmbeddings
13
- from langchain_mistralai.chat_models import ChatMistralAI
14
- from langchain_mistralai.embeddings import MistralAIEmbeddings
15
- import os
16
- import traceback
17
-
18
-
19
-
20
- os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
21
-
22
- load_dotenv()
23
- NEO4J_URI = os.getenv("NEO4J_URI")
24
- NEO4J_USERNAME = os.getenv("NEO4J_USERNAME")
25
- NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD")
26
- MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY")
27
-
28
- NODE_LABELS = [
29
- "Personnage", "Objet", "Lieu", "Événement", "PériodeTemporelle",
30
- "Protagoniste", "Antagoniste", "PersonnageSecondaire", "CréatureMythique",
31
- "FigureHistorique", "Narrateur", "Thème", "Motif", "Symbole"
32
- ]
33
-
34
- REL_TYPES = [
35
- "CONNAÎT", "SITUE_DANS", "FAIT_PARTIE_DE", "SE_PRODUIT_PENDANT", "IMPLIQUE",
36
- "S'OPPOSE_À", "CRÉÉ_PAR", "INSPIRÉ_PAR", "REPRÉSENTE", "TRANSFORME"
37
- ]
38
- # Create DocumentLoader
39
- class PdfLoaderWithPageBreaks(DataLoader):
40
- async def run(self, filepath: Path) -> PdfDocument:
41
- loader = PyPDFLoader(filepath)
42
- text = ''
43
- async for page in loader.alazy_load():
44
- text = text + " __PAGE__BREAK__ " + page.page_content
45
- return PdfDocument(
46
- text=text,
47
- document_info=DocumentInfo(path=filepath), )
48
-
49
-
50
- async def main():
51
- try:
52
- # Connect to the Neo4j database
53
- async with AsyncGraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD)) as driver:
54
- # Create components
55
- splitter = LangChainTextSplitterAdapter(
56
- CharacterTextSplitter(chunk_size=15000, chunk_overlap=200, separator=" __PAGE__BREAK__ ")
57
- )
58
- embedder = MistralAIEmbeddings(model="mistral-embed", mistral_api_key=MISTRAL_API_KEY)
59
- llm = ChatMistralAI(
60
- model="mistral-large-latest",
61
- api_key=MISTRAL_API_KEY,
62
- temperature=0,
63
- stream=False,
64
- verbose=True
65
- )
66
-
67
- # Instantiate the SimpleKGPipeline
68
- kg_builder = SimpleKGPipeline(
69
- llm=llm,
70
- driver=driver,
71
- pdf_loader=PdfLoaderWithPageBreaks(),
72
- text_splitter=splitter,
73
- embedder=embedder,
74
- entities=NODE_LABELS,
75
- relations=REL_TYPES,
76
- on_error="IGNORE",
77
- from_pdf=True,
78
- )
79
-
80
- # Load PDF and build knowledge graph
81
- await kg_builder.run_async(file_path='La Confession muette.pdf')
82
-
83
- print("Knowledge Graph Building Complete")
84
-
85
- except Exception as e:
86
- print(f"An error occurred: {str(e)}")
87
- traceback.print_exc()
88
-
89
- if __name__ == "__main__":
90
- asyncio.run(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
neo4j_utils.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sentence_transformers import SentenceTransformer
2
+ from pinecone_text.sparse import BM25Encoder
3
+ from langchain.embeddings import HuggingFaceEmbeddings
4
+ from langchain.schema import HumanMessage
5
+ import json
6
+ import streamlit as st
7
+ from config import neo4j_driver, sparse_index as indexB, llm
8
+ from pinecone_utilsB import hybrid_search
9
+ from neo4j.exceptions import CypherSyntaxError
10
+ import re
11
+
12
+
13
+ # Initialiser les modèles et encodeurs
14
+ model = SentenceTransformer("intfloat/multilingual-e5-large")
15
+ sparse_encoder = BM25Encoder().default()
16
+ embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large")
17
+
18
+
19
+ def extract_cypher_query(llm_output):
20
+ """
21
+ Extrait la requête Cypher valide de la sortie du LLM.
22
+ """
23
+ # Utiliser une expression régulière pour trouver la requête Cypher
24
+ cypher_pattern = r"(MATCH|CREATE|MERGE|RETURN|WITH|DELETE|SET|REMOVE|UNWIND|CALL)[\s\S]*?;"
25
+ match = re.search(cypher_pattern, llm_output)
26
+
27
+ if match:
28
+ return match.group(0) # Retourne la requête Cypher trouvée
29
+ else:
30
+ # Si aucune requête n'est trouvée, essayer de retourner la dernière ligne
31
+ lines = llm_output.split("\n")
32
+ for line in reversed(lines):
33
+ if line.strip().endswith(";"):
34
+ return line.strip()
35
+ raise ValueError("Aucune requête Cypher valide trouvée dans la sortie du LLM.")
36
+
37
+ # Function to generate Cypher queries using an LLM
38
+ def generate_cypher_query(user_query):
39
+ """Génère une requête Cypher à l'aide du LLM et extrait la partie valide."""
40
+ # Liste des nœuds et relations autorisés
41
+ allowed_relationships = [
42
+ "CONNAÎT", "CRÉÉ_PAR", "IMPLIQUE", "INSPIRÉ_PAR", "REPRÉSENTE", "SE_PRODUIT_PENDANT", "SITUE_DANS"
43
+ ]
44
+ allowed_nodes = [
45
+ "__Entity__", "Événement", "Lieu", "Objet", "Périodetemporelle", "Personnage", "Personnagesecondaire",
46
+ "Protagoniste", "Symbole", "Thème"
47
+ ]
48
+
49
+ # Construire le prompt avec les informations supplémentaires
50
+ prompt = f"""
51
+ Vous êtes un générateur de requêtes Cypher pour une base de données Neo4j.
52
+ Étant donné une demande de l'utilisateur, générez une requête Cypher correspondante.
53
+
54
+ **Instructions supplémentaires** :
55
+ - Utilisez `DISTINCT` pour éviter les répétitions.
56
+ - Optimisez la requête pour éviter les chemins redondants.
57
+ - Tous les nœuds ont uniquement la propriété `id`. Utilisez `id` pour filtrer ou retourner des valeurs.
58
+ - Utilisez uniquement les nœuds et relations autorisés suivants :
59
+
60
+ **Nœuds autorisés** :
61
+ {", ".join(allowed_nodes)}
62
+
63
+ **Relations autorisées** :
64
+ {", ".join(allowed_relationships)}
65
+
66
+ **Exemples de requêtes** :
67
+ - Demande : "Trouver tous les lieux mentionnés dans une histoire."
68
+ Requête : MATCH (n:Personnage) RETURN n.id;
69
+
70
+ - Demande : "Lister tous les personnages qui connaissent Zéphyrine."
71
+ Requête : MATCH (p1:Personnage)-[:CONNAÎT]->(p2:Personnage {{name: "Zéphyrine"}}) RETURN DISTINCT p1.name
72
+
73
+
74
+ **Format de la réponse** :
75
+ - Ne fournissez que la requête Cypher, sans explications ni commentaires.
76
+ - La requête doit commencer par `MATCH`, `CREATE`, `MERGE`, `RETURN`, etc.
77
+ - La requête doit se terminer par un point-virgule (`;`).
78
+
79
+ **Demande de l'utilisateur** :
80
+ {user_query}
81
+
82
+ **Requête Cypher** :
83
+ """
84
+
85
+ # Générer la réponse avec le LLM
86
+ response = llm.invoke(prompt)
87
+ print(f"Sortie brute du LLM : {response.content}") # Afficher la sortie brute
88
+
89
+
90
+ # Extraire la requête Cypher valide
91
+ try:
92
+ cypher_query = extract_cypher_query(response.content)
93
+ return cypher_query
94
+ except ValueError as e:
95
+ print(f"Erreur lors de l'extraction de la requête Cypher : {e}")
96
+ return None
97
+
98
+ # Function to execute Cypher queries in Neo4j
99
+ def execute_cypher_query(cypher_query):
100
+ """Exécute une requête Cypher dans Neo4j et retourne les résultats."""
101
+ if not cypher_query:
102
+ return []
103
+
104
+ try:
105
+ with neo4j_driver.session() as session:
106
+ result = session.run(cypher_query)
107
+ return [record for record in result]
108
+ except CypherSyntaxError as e:
109
+ print(f"Erreur de syntaxe Cypher : {e}")
110
+ return []
111
+
112
+ # Function to merge hybrid search and Neo4j results
113
+ def merge_results(hybrid_results, neo4j_results):
114
+ """Merge results from hybrid search and Neo4j into a unified list."""
115
+ combined_results = []
116
+
117
+ # Add hybrid search results
118
+ for doc in hybrid_results:
119
+ combined_results.append({
120
+ "type": "text",
121
+ "content": doc,
122
+ "score": 1.0
123
+ })
124
+
125
+ # Add Neo4j search results
126
+ for record in neo4j_results:
127
+ #if "MATCH" not in str(record["content"]) and "RETURN" not in str(record["content"]): # Vérifie que ce n'est pas une requête
128
+ combined_results.append({
129
+ "type": "entity",
130
+ "content": record,
131
+ "score": 1.0
132
+ })
133
+
134
+ # Sort combined results by score (or any other criteria)
135
+ combined_results.sort(key=lambda x: x["score"], reverse=True)
136
+
137
+ return combined_results
138
+
139
+ # Unified search function
140
+ def unified_search(query):
141
+ """Perform a unified search combining hybrid search and Neo4j knowledge graph search."""
142
+ # Step 1: Perform hybrid search
143
+ hybrid_results = hybrid_search(query)
144
+ #print(f"Résultats de recherche hybride: {hybrid_results}")
145
+
146
+ # Step 2: Generate and execute Cypher query
147
+ cypher_query = generate_cypher_query(query)
148
+ print(f"Generated Cypher query: {cypher_query}")
149
+
150
+ neo4j_results = execute_cypher_query(cypher_query)
151
+ print("Résultats Neo4j AVANT filtration :", neo4j_results)
152
+
153
+ # Step 3: Merge results
154
+ final_results = merge_results(hybrid_results, neo4j_results)
155
+
156
+ return final_results
requirements.txt CHANGED
@@ -34,7 +34,7 @@ nltk>=3.8.1
34
 
35
  pinecone_text
36
  # neo4j==5.17.0
37
- neo4j-graphrag
38
  #neo4j_graphrag[mistralai]
39
 
40
  langchain_neo4j
 
34
 
35
  pinecone_text
36
  # neo4j==5.17.0
37
+ #neo4j-graphrag
38
  #neo4j_graphrag[mistralai]
39
 
40
  langchain_neo4j