├── .env ├── KnowledgeGraph ├── Battle of Waterloo.ipynb ├── Napoleon.ipynb ├── Nodes_and_Relationships.ipynb ├── Talleyrand.ipynb ├── __pycache__ │ └── chunking.cpython-312.pyc └── chunking.py ├── LLM ├── T5.py ├── __init__.py └── __pycache__ │ ├── __init__.cpython-312.pyc │ └── model.cpython-312.pyc ├── README.md ├── assets ├── Career.png ├── Death.png ├── KnowLedgeGraph_Design.txt └── general_info.png ├── data ├── cleaned │ ├── Battle of Waterloo.txt │ ├── Napoleon.txt │ └── Talleyrand.txt ├── json │ ├── Battle of Waterloo.json │ ├── Napoleon.json │ └── Talleyrand.json └── raw │ ├── Battle of Waterloo.html │ ├── Charles Maurice de Talleyrand-Périgord.html │ └── Napoleon.html ├── graphRAG_generation.py ├── main.py ├── neo4j_env.py ├── poetry.lock ├── preprocessing.py ├── pyproject.toml ├── txt2json.py └── vectorRAG_generation.py /.env: -------------------------------------------------------------------------------- 1 | NEO4J_URI= 2 | NEO4J_USERNAME= 3 | NEO4J_PASSWORD= 4 | NEO4J_DATABASE=neo4j 5 | OPENAI_API_KEY= 6 | OPENAI_BASE_URL='https://api.openai.com/v1' -------------------------------------------------------------------------------- /KnowledgeGraph/Battle of Waterloo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "import os\n", 11 | "\n", 12 | "# Langchain\n", 13 | "from langchain_community.graphs import Neo4jGraph\n", 14 | "import chunking\n", 15 | "# Warning control\n", 16 | "import warnings\n", 17 | "warnings.filterwarnings(\"ignore\")" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "# Load from environment\n", 27 | "load_dotenv('.env', override=True)\n", 28 | "NEO4J_URI = os.getenv('NEO4J_URI')\n", 29 | "NEO4J_USERNAME = os.getenv('NEO4J_USERNAME')\n", 30 | "NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD')\n", 31 | "NEO4J_DATABASE = os.getenv('NEO4J_DATABASE') or 'neo4j'\n", 32 | "OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\n", 33 | "OPENAI_ENDPOINT = os.getenv('OPENAI_BASE_URL') + '/embeddings'\n", 34 | "\n", 35 | "\n", 36 | "kg = Neo4jGraph(\n", 37 | " url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE\n", 38 | ")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": "### 1. Splitting the Chunks" 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 3, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# Load JSON file\n", 53 | "file = \"../data/json/Battle of Waterloo.json\"" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 4, 59 | "metadata": {}, 60 | "outputs": [ 61 | { 62 | "name": "stdout", 63 | "output_type": "stream", 64 | "text": [ 65 | "['General information', 'Reason', 'Combatant', 'Consequence', 'Source']\n", 66 | "Processing General information from ../data/json/Battle of Waterloo.json\n", 67 | "\tSplit into 5 chunks\n", 68 | "Processing Reason from ../data/json/Battle of Waterloo.json\n", 69 | "\tSplit into 5 chunks\n", 70 | "Processing Combatant from ../data/json/Battle of Waterloo.json\n", 71 | "\tSplit into 23 chunks\n", 72 | "Processing Consequence from ../data/json/Battle of Waterloo.json\n", 73 | "\tSplit into 28 chunks\n", 74 | "Processing Source from ../data/json/Battle of Waterloo.json\n", 75 | "\tSplit into 1 chunks\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "file_chunks = chunking.split_data_from_file(file)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": "### 2. Create Chunk node in KnowledgeGraph with properties extracted from file" 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 5, 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "data": { 95 | "text/plain": [ 96 | "[{'mergedChunk': {'formItem': 'General information',\n", 97 | " 'text': 'The Battle of Waterloo was fought on Sunday 18 June 1815, near Waterloo (at that time in the United Kingdom of the Netherlands, now in Belgium), marking the end of the Napoleonic Wars. A French army under the command of Napoleon was defeated by two armies of the Seventh Coalition. One of these was a British-led force with units from the United Kingdom, the Netherlands, Hanover, Brunswick, and Nassau, under the command of the Duke of Wellington (often referred to as the Anglo-allied army or Wellington\\'s army). The other comprised three corps (the 1st, 2nd and 4th corps) of the Prussian army under Field Marshal Blücher; a fourth corps (the 3rd) of this army fought at the Battle of Wavre on the same day. The battle was known contemporarily as the Battle of Mont Saint-Jean in France (after the hamlet of Mont-Saint-Jean) and La Belle Alliance in Prussia (\"the Beautiful Alliance\"; after the inn of La Belle Alliance).\\nUpon Napoleon\\'s return to power in March 1815 (beginning the Hundred Days), many states that had previously opposed him formed the Seventh Coalition and hurriedly mobilised their armies. Wellington\\'s and Blücher\\'s armies were cantoned close to the northeastern border of France. Napoleon planned to attack them separately, before they could link up and invade France with other members of the coalition.',\n", 98 | " 'source': 'Waterloo History',\n", 99 | " 'chunkId': 'Battle of Waterloo-General information-chunk0000',\n", 100 | " 'chunkSeqId': 0}}]" 101 | ] 102 | }, 103 | "execution_count": 5, 104 | "metadata": {}, 105 | "output_type": "execute_result" 106 | } 107 | ], 108 | "source": [ 109 | "# Create Napoleon_Chunk node and its properties\n", 110 | "merge_chunk_node_query = \"\"\"\n", 111 | "MERGE(mergedChunk:Waterloo_Chunk {chunkId: $chunkParam.chunkId})\n", 112 | " ON CREATE SET\n", 113 | " mergedChunk.text = $chunkParam.text, \n", 114 | " mergedChunk.source = $chunkParam.source, \n", 115 | " mergedChunk.formItem = $chunkParam.formItem, \n", 116 | " mergedChunk.chunkSeqId = $chunkParam.chunkSeqId\n", 117 | "RETURN mergedChunk\n", 118 | "\"\"\"\n", 119 | "kg.query(merge_chunk_node_query, \n", 120 | " params={'chunkParam':file_chunks[0]})" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": "### 3. Create a uniqueness constraint to avoid duplicate chunks" 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 7, 131 | "metadata": {}, 132 | "outputs": [ 133 | { 134 | "data": { 135 | "text/plain": [ 136 | "[]" 137 | ] 138 | }, 139 | "execution_count": 7, 140 | "metadata": {}, 141 | "output_type": "execute_result" 142 | } 143 | ], 144 | "source": [ 145 | "# Create a uniqueness constraint to avoid duplicate chunks\n", 146 | "avoid_duplicate_chunks = \"\"\"\n", 147 | "CREATE CONSTRAINT unique_chunk IF NOT EXISTS \n", 148 | " FOR (nc:Waterloo_Chunk) REQUIRE nc.chunkId IS UNIQUE\n", 149 | "\"\"\"\n", 150 | "\n", 151 | "kg.query(avoid_duplicate_chunks)\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": "### 4. Adding all chunks data to knowledegeGraph" 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 8, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "name": "stdout", 166 | "output_type": "stream", 167 | "text": [ 168 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-General information-chunk0000\n", 169 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-General information-chunk0001\n", 170 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-General information-chunk0002\n", 171 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-General information-chunk0003\n", 172 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-General information-chunk0004\n", 173 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Reason-chunk0000\n", 174 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Reason-chunk0001\n", 175 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Reason-chunk0002\n", 176 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Reason-chunk0003\n", 177 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Reason-chunk0004\n", 178 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0000\n", 179 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0001\n", 180 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0002\n", 181 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0003\n", 182 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0004\n", 183 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0005\n", 184 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0006\n", 185 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0007\n", 186 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0008\n", 187 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0009\n", 188 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0010\n", 189 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0011\n", 190 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0012\n", 191 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0013\n", 192 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0014\n", 193 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0015\n", 194 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0016\n", 195 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0017\n", 196 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0018\n", 197 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0019\n", 198 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0020\n", 199 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0021\n", 200 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Combatant-chunk0022\n", 201 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0000\n", 202 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0001\n", 203 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0002\n", 204 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0003\n", 205 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0004\n", 206 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0005\n", 207 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0006\n", 208 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0007\n", 209 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0008\n", 210 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0009\n", 211 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0010\n", 212 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0011\n", 213 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0012\n", 214 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0013\n", 215 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0014\n", 216 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0015\n", 217 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0016\n", 218 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0017\n", 219 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0018\n", 220 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0019\n", 221 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0020\n", 222 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0021\n", 223 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0022\n", 224 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0023\n", 225 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0024\n", 226 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0025\n", 227 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0026\n", 228 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Consequence-chunk0027\n", 229 | "Creating `:Chunk` node for chunk ID Battle of Waterloo-Source-chunk0000\n", 230 | "Created 62 nodes\n" 231 | ] 232 | } 233 | ], 234 | "source": [ 235 | "node_count = 0\n", 236 | "for chunk in file_chunks:\n", 237 | " print(f\"Creating `:Chunk` node for chunk ID {chunk['chunkId']}\")\n", 238 | " kg.query(merge_chunk_node_query, \n", 239 | " params={\n", 240 | " 'chunkParam': chunk\n", 241 | " })\n", 242 | " node_count += 1\n", 243 | "print(f\"Created {node_count} nodes\")" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": "### 5. Create a VectorIndex for the Waterloo_Chunk" 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": 9, 254 | "metadata": {}, 255 | "outputs": [ 256 | { 257 | "data": { 258 | "text/plain": [ 259 | "[]" 260 | ] 261 | }, 262 | "execution_count": 9, 263 | "metadata": {}, 264 | "output_type": "execute_result" 265 | } 266 | ], 267 | "source": [ 268 | "VectorIndex = \"\"\"\n", 269 | " CREATE VECTOR INDEX `WaterlooOpenAI` IF NOT EXISTS\n", 270 | " FOR (nc:Napoleon_Chunk) ON (nc.textEmbeddingOpenAI) \n", 271 | " OPTIONS { indexConfig: {\n", 272 | " `vector.dimensions`: 1536,\n", 273 | " `vector.similarity_function`: 'cosine' \n", 274 | " }}\n", 275 | "\"\"\"\n", 276 | "\n", 277 | "kg.query(VectorIndex)\n" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": "### 6. Embedding the text data using a provider and add it to the Napoleon_Chunk node as a property named textEmbeddingOpenAI" 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 10, 288 | "metadata": { 289 | "vscode": { 290 | "languageId": "powershell" 291 | } 292 | }, 293 | "outputs": [ 294 | { 295 | "data": { 296 | "text/plain": [ 297 | "[]" 298 | ] 299 | }, 300 | "execution_count": 10, 301 | "metadata": {}, 302 | "output_type": "execute_result" 303 | } 304 | ], 305 | "source": [ 306 | "kg.query(\"\"\"\n", 307 | " MATCH (Waterloo_Chunk:Waterloo_Chunk) WHERE Waterloo_Chunk.textEmbeddingOpenAI IS NULL\n", 308 | " WITH Waterloo_Chunk, genai.vector.encode(\n", 309 | " Waterloo_Chunk.text, \n", 310 | " \"OpenAI\", \n", 311 | " {\n", 312 | " token: $openAiApiKey, \n", 313 | " endpoint: $openAiEndpoint\n", 314 | " }) AS vector\n", 315 | " CALL db.create.setNodeVectorProperty(Waterloo_Chunk, \"textEmbeddingOpenAI\", vector)\n", 316 | " \"\"\", \n", 317 | " params={\"openAiApiKey\":OPENAI_API_KEY, \"openAiEndpoint\": OPENAI_ENDPOINT} )" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": { 323 | "vscode": { 324 | "languageId": "powershell" 325 | } 326 | }, 327 | "source": "### Optional: 6.1. If you want to Embed data using HuggingFace use this part" 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 10, 332 | "metadata": { 333 | "vscode": { 334 | "languageId": "powershell" 335 | } 336 | }, 337 | "outputs": [], 338 | "source": [ 339 | "# # I am using T5 base but you can add more model in LLM file for yourself\n", 340 | "# embedd_model = T5.t5_model()\n", 341 | "# chunks = kg.query(\"MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE Napoleon_Chunk.textEmbedding IS NULL RETURN id(Napoleon_Chunk) as id, Napoleon_Chunk.text as text\")\n", 342 | "\n", 343 | "\n", 344 | "# for chunk in chunks:\n", 345 | "# text = chunk[\"text\"]\n", 346 | "# print(f\"Embedding Chunk {chunk['id']}\")\n", 347 | "# vector = embedd_model.embed([text])\n", 348 | "\n", 349 | "# vector_list = vector.tolist()\n", 350 | "# print(vector_list)\n", 351 | "\n", 352 | "# kg.query(\n", 353 | "# \"\"\"\n", 354 | "# MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE id(Napoleon_Chunk) = $id\n", 355 | "# SET Napoleon_Chunk.textEmbedding = $vector\n", 356 | "# \"\"\",\n", 357 | "# params={\"id\": chunk[\"id\"], \"vector\": vector_list}\n", 358 | "# )" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": "### 7. Finding all Chunks from the same formItem" 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 12, 369 | "metadata": {}, 370 | "outputs": [ 371 | { 372 | "name": "stdout", 373 | "output_type": "stream", 374 | "text": [ 375 | "[{'chunkItemInfo': {'text': \"On 13 March 1815, six days before Napoleon reached Paris, the powers at the Congress of Vienna declared him an outlaw. Four days later, the United Kingdom, Russia, Austria, and Prussia mobilised armies to defeat Napoleon. Critically outnumbered, Napoleon knew that once his attempts at dissuading one or more members of the Seventh Coalition from invading France had failed, his only chance of remaining in power was to attack before the coalition mobilised.\\nHad Napoleon succeeded in destroying the existing coalition forces south of Brussels before they were reinforced, he might have been able to drive the British back to the sea and knock the Prussians out of the war. Crucially, this would have bought him time to recruit and train more men before turning his armies against the Austrians and Russians.\\nAn additional consideration for Napoleon was that a French victory might cause French-speaking sympathisers in Belgium to launch a friendly revolution. Also, coalition troops in Belgium were largely second-line, as many units were of dubious quality and loyalty.\\nThe initial dispositions of Wellington, the British commander, were intended to counter the threat of Napoleon enveloping the Coalition armies by moving through Mons to the south-west of Brussels. This would have pushed Wellington closer to the Prussian forces, led by Gebhard Leberecht von Blücher, but might have cut Wellington's communications with his base at Ostend. In order to delay Wellington's deployment, Napoleon spread false intelligence which suggested that Wellington's supply chain from the channel ports would be cut.\", 'formItem': 'Reason', 'chunkId': 'Battle of Waterloo-Reason-chunk0000', 'chunkSeqId': 0}}]\n", 376 | "[{'chunkItemInfo': {'text': 'The 80 guns of Napoleon\\'s grande batterie drew up in the centre. These opened fire at 11:50, according to Lord Hill (commander of the Anglo-allied II Corps),[m] while other sources put the time between noon and 13:30. The grande batterie was too far back to aim accurately, and the only other troops they could see were skirmishers of the regiments of Kempt and Pack, and Perponcher\\'s 2nd Dutch division (the others were employing Wellington\\'s characteristic \"reverse slope defence\").[n]\\nThe bombardment caused a large number of casualties. Although some projectiles buried themselves in the soft soil, most found their marks on the reverse slope of the ridge. The bombardment forced the cavalry of the Union Brigade (in third line) to move to its left, to reduce their casualty rate.\\nSection: Napoleon spots the Prussians[edit]\\nAt about 13:15, Napoleon saw the first columns of Prussians around the village of Lasne-Chapelle-Saint-Lambert, 4 to 5 miles (6.4 to 8.0\\xa0km) away from his right flank—about three hours march for an army. Napoleon\\'s reaction was to have Marshal Soult send a message to Grouchy telling him to come towards the battlefield and attack the arriving Prussians. Grouchy, however, had been executing Napoleon\\'s previous orders to follow the Prussians \"with your sword against his back\" towards Wavre, and was by then too far away to reach Waterloo.\\nGrouchy was advised by his subordinate, Gérard, to \"march to the sound of the guns\", but stuck to his orders and engaged the Prussian III Corps rearguard, under the command of Lieutenant-General Baron von Thielmann, at the Battle of Wavre. Moreover, Soult\\'s letter ordering Grouchy to move quickly to join Napoleon and attack Bülow would not actually reach Grouchy until after 20:00.', 'formItem': 'Consequence', 'chunkId': 'Battle of Waterloo-Consequence-chunk0000', 'chunkSeqId': 0}}]\n", 377 | "[{'chunkItemInfo': {'text': 'Wellington rose at around 02:00 or 03:00 on 18 June, and wrote letters until dawn. He had earlier written to Blücher confirming that he would give battle at Mont-Saint-Jean if Blücher could provide him with at least one corps; otherwise he would retreat towards Brussels. At a late-night council, Blücher\\'s chief of staff, August Neidhardt von Gneisenau, had been distrustful of Wellington\\'s strategy, but Blücher persuaded him that they should march to join Wellington\\'s army. In the morning Wellington duly received a reply from Blücher, promising to support him with three corps.\\nFrom 06:00 Wellington was in the field supervising the deployment of his forces. At Wavre, the Prussian IV Corps under Bülow was designated to lead the march to Waterloo as it was in the best shape, not having been involved in the Battle of Ligny. Although they had not taken casualties, IV Corps had been marching for two days, covering the retreat of the three other corps of the Prussian army from the battlefield of Ligny. They had been posted farthest away from the battlefield, and progress was very slow.\\nThe roads were in poor condition after the night\\'s heavy rain, and Bülow\\'s men had to pass through the congested streets of Wavre and move 88\\xa0artillery pieces. Matters were not helped when a fire broke out in Wavre, blocking several streets along Bülow\\'s intended route. As a result, the last part of the corps left at 10:00, six hours after the leading elements had moved out towards Waterloo. Bülow\\'s men were followed to Waterloo first by I Corps and then by II Corps.\\nNapoleon breakfasted off silver plate at Le Caillou, the house where he had spent the night. When Soult suggested that Grouchy should be recalled to join the main force, Napoleon said, \"Just because you have all been beaten by Wellington, you think he\\'s a good general. I tell you Wellington is a bad general, the English are bad troops, and this affair is nothing more than eating breakfast\".', 'formItem': 'Combatant', 'chunkId': 'Battle of Waterloo-Combatant-chunk0000', 'chunkSeqId': 0}}]\n", 378 | "[]\n" 379 | ] 380 | } 381 | ], 382 | "source": [ 383 | "# Each chunk is a small part of the document. To do this, first we need to find all chunks that belong together\n", 384 | "cypher = \"\"\"\n", 385 | " MATCH (from_same_chunk_item:Waterloo_Chunk)\n", 386 | " WHERE from_same_chunk_item.formItem = $WaterlooParam\n", 387 | " AND from_same_chunk_item.formItem = $WaterlooParam\n", 388 | " RETURN from_same_chunk_item {.text, .formItem, .chunkId, .chunkSeqId } as chunkItemInfo\n", 389 | " ORDER BY from_same_chunk_item.chunkSeqId ASC\n", 390 | " LIMIT 1\n", 391 | "\"\"\"\n", 392 | "\n", 393 | "items = ['Reason', 'Consequence', 'Combatant', 'General Information']\n", 394 | "for item in items:\n", 395 | " result = kg.query(cypher, params={'WaterlooParam':item})\n", 396 | " print(f\"{result}\")" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": {}, 402 | "source": "### 8. Adding NEXT relationship to connect chunks together based on the order of their ID" 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 13, 407 | "metadata": {}, 408 | "outputs": [ 409 | { 410 | "name": "stdout", 411 | "output_type": "stream", 412 | "text": [ 413 | "for Reason: [{'size(section_chunk_list)': 5}]\n", 414 | "for Consequence: [{'size(section_chunk_list)': 28}]\n", 415 | "for Combatant: [{'size(section_chunk_list)': 23}]\n", 416 | "for General Information: [{'size(section_chunk_list)': 0}]\n" 417 | ] 418 | } 419 | ], 420 | "source": [ 421 | "# ordering based on chunkSeqId\n", 422 | "cypher = \"\"\"\n", 423 | " MATCH (n:Waterloo_Chunk)\n", 424 | " WHERE n.formItem = $WaterlooParam\n", 425 | " WITH n\n", 426 | " ORDER BY n.chunkSeqId ASC\n", 427 | " WITH collect(n) as section_chunk_list\n", 428 | " CALL apoc.nodes.link(\n", 429 | " section_chunk_list, \n", 430 | " \"NEXT\", \n", 431 | " {avoidDuplicates: true}\n", 432 | " )\n", 433 | " RETURN size(section_chunk_list)\n", 434 | "\"\"\"\n", 435 | "\n", 436 | "items = ['Reason', 'Consequence', 'Combatant', 'General Information']\n", 437 | "for item in items:\n", 438 | " result = kg.query(cypher, params={'WaterlooParam':item})\n", 439 | " print(f\"for {item}: {result}\" )" 440 | ] 441 | } 442 | ], 443 | "metadata": { 444 | "kernelspec": { 445 | "display_name": "base", 446 | "language": "python", 447 | "name": "python3" 448 | }, 449 | "language_info": { 450 | "codemirror_mode": { 451 | "name": "ipython", 452 | "version": 3 453 | }, 454 | "file_extension": ".py", 455 | "mimetype": "text/x-python", 456 | "name": "python", 457 | "nbconvert_exporter": "python", 458 | "pygments_lexer": "ipython3", 459 | "version": "3.12.1" 460 | } 461 | }, 462 | "nbformat": 4, 463 | "nbformat_minor": 2 464 | } 465 | -------------------------------------------------------------------------------- /KnowledgeGraph/Napoleon.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "import os\n", 11 | "\n", 12 | "# Langchain\n", 13 | "from langchain_community.graphs import Neo4jGraph\n", 14 | "import chunking\n", 15 | "\n", 16 | "# Warning control\n", 17 | "import warnings\n", 18 | "warnings.filterwarnings(\"ignore\")" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "# Load from environment\n", 28 | "load_dotenv('.env', override=True)\n", 29 | "NEO4J_URI = os.getenv('NEO4J_URI')\n", 30 | "NEO4J_USERNAME = os.getenv('NEO4J_USERNAME')\n", 31 | "NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD')\n", 32 | "NEO4J_DATABASE = os.getenv('NEO4J_DATABASE') or 'neo4j'\n", 33 | "OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\n", 34 | "OPENAI_ENDPOINT = os.getenv('OPENAI_BASE_URL') + '/embeddings'\n", 35 | "\n", 36 | "\n", 37 | "kg = Neo4jGraph(\n", 38 | " url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE\n", 39 | ")" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": "### 1. Splitting the Chunks" 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 3, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# Load JSON file\n", 54 | "file = \"../data/json/Napoleon.json\"" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 4, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "['General Information', 'Career', 'Death', 'Source']\n", 67 | "Processing General Information from ../data/json/Napoleon.json\n", 68 | "\tSplit into 18 chunks\n", 69 | "Processing Career from ../data/json/Napoleon.json\n", 70 | "\tSplit into 34 chunks\n", 71 | "Processing Death from ../data/json/Napoleon.json\n", 72 | "\tSplit into 8 chunks\n", 73 | "Processing Source from ../data/json/Napoleon.json\n", 74 | "\tSplit into 1 chunks\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "file_chunks = chunking.split_data_from_file(file)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": "### 2. Create Chunk node in KnowledgeGraph with properties extracted from file" 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 5, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "[{'mergedChunk': {'formItem': 'General Information',\n", 96 | " 'text': \"Napoleon Bonaparte (born Napoleone di Buonaparte;[b] 15 August 1769 – 5 May 1821), later known by his regnal name Napoleon\\xa0I, was a French military and political leader who rose to prominence during the French Revolution and led a series of successful campaigns across Europe during the Revolutionary Wars and Napoleonic Wars from 1796 to 1815. He was the leader of the French Republic as First Consul from 1799 to 1804, then of the French Empire as Emperor of the French from 1804 to 1814, and briefly again in 1815.\\nBorn on the island of Corsica to a family of Italian origin, Napoleon moved to mainland France in 1779 and was commissioned as an officer in the French Army in 1785. He supported the French Revolution in 1789, and promoted its cause in Corsica. He rose rapidly in the ranks after breaking the siege of Toulon in 1793 and firing on royalist insurgents in Paris on 13 Vendémiaire in 1795. In 1796, Napoleon commanded a military campaign against the Austrians and their Italian allies in the War of the First Coalition, scoring decisive victories and becoming a national hero. He led an expedition to Egypt and Syria in 1798 which served as a springboard to political power. In November 1799, Napoleon engineered the Coup of 18 Brumaire against the Directory, and became First Consul of the Republic. He won the Battle of Marengo in 1800, which secured French victory in the War of the Second Coalition, and in 1803 sold the territory of Louisiana to the United States, which doubled the latter's area. In December 1804, Napoleon crowned himself Emperor of the French, further expanding his power.\",\n", 97 | " 'source': 'Napoleon History',\n", 98 | " 'chunkId': 'Napoleon-General Information-chunk0000',\n", 99 | " 'chunkSeqId': 0}}]" 100 | ] 101 | }, 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "output_type": "execute_result" 105 | } 106 | ], 107 | "source": [ 108 | "# Create Napoleon_Chunk node and its properties\n", 109 | "merge_chunk_node_query = \"\"\"\n", 110 | "MERGE(mergedChunk:Napoleon_Chunk {chunkId: $chunkParam.chunkId})\n", 111 | " ON CREATE SET\n", 112 | " mergedChunk.text = $chunkParam.text, \n", 113 | " mergedChunk.source = $chunkParam.source, \n", 114 | " mergedChunk.formItem = $chunkParam.formItem, \n", 115 | " mergedChunk.chunkSeqId = $chunkParam.chunkSeqId\n", 116 | "RETURN mergedChunk\n", 117 | "\"\"\"\n", 118 | "kg.query(merge_chunk_node_query, \n", 119 | " params={'chunkParam':file_chunks[0]})" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": "### 3. Create a uniqueness constraint to avoid duplicate chunks" 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 6, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "[]" 136 | ] 137 | }, 138 | "execution_count": 6, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "# Create a uniqueness constraint to avoid duplicate chunks\n", 145 | "avoid_duplicate_chunks = \"\"\"\n", 146 | "CREATE CONSTRAINT unique_chunk IF NOT EXISTS \n", 147 | " FOR (nc:Napoleon_Chunk) REQUIRE nc.chunkId IS UNIQUE\n", 148 | "\"\"\"\n", 149 | "\n", 150 | "kg.query(avoid_duplicate_chunks)\n" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": "### 4. Adding all chunks data to knowledegeGraph" 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 7, 161 | "metadata": {}, 162 | "outputs": [ 163 | { 164 | "name": "stdout", 165 | "output_type": "stream", 166 | "text": [ 167 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0000\n", 168 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0001\n", 169 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0002\n", 170 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0003\n", 171 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0004\n", 172 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0005\n", 173 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0006\n", 174 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0007\n", 175 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0008\n", 176 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0009\n", 177 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0010\n", 178 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0011\n", 179 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0012\n", 180 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0013\n", 181 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0014\n", 182 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0015\n", 183 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0016\n", 184 | "Creating `:Chunk` node for chunk ID Napoleon-General Information-chunk0017\n", 185 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0000\n", 186 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0001\n", 187 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0002\n", 188 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0003\n", 189 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0004\n", 190 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0005\n", 191 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0006\n", 192 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0007\n", 193 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0008\n", 194 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0009\n", 195 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0010\n", 196 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0011\n", 197 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0012\n", 198 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0013\n", 199 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0014\n", 200 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0015\n", 201 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0016\n", 202 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0017\n", 203 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0018\n", 204 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0019\n", 205 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0020\n", 206 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0021\n", 207 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0022\n", 208 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0023\n", 209 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0024\n", 210 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0025\n", 211 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0026\n", 212 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0027\n", 213 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0028\n", 214 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0029\n", 215 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0030\n", 216 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0031\n", 217 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0032\n", 218 | "Creating `:Chunk` node for chunk ID Napoleon-Career-chunk0033\n", 219 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0000\n", 220 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0001\n", 221 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0002\n", 222 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0003\n", 223 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0004\n", 224 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0005\n", 225 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0006\n", 226 | "Creating `:Chunk` node for chunk ID Napoleon-Death-chunk0007\n", 227 | "Creating `:Chunk` node for chunk ID Napoleon-Source-chunk0000\n", 228 | "Created 61 nodes\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "node_count = 0\n", 234 | "for chunk in file_chunks:\n", 235 | " print(f\"Creating `:Chunk` node for chunk ID {chunk['chunkId']}\")\n", 236 | " kg.query(merge_chunk_node_query, \n", 237 | " params={\n", 238 | " 'chunkParam': chunk\n", 239 | " })\n", 240 | " node_count += 1\n", 241 | "print(f\"Created {node_count} nodes\")" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": "### 5. Create a VectorIndex for the Napoleon_Chunk" 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 8, 252 | "metadata": {}, 253 | "outputs": [ 254 | { 255 | "data": { 256 | "text/plain": [ 257 | "[]" 258 | ] 259 | }, 260 | "execution_count": 8, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "VectorIndex = \"\"\"\n", 267 | " CREATE VECTOR INDEX `NapoleonOpenAI` IF NOT EXISTS\n", 268 | " FOR (nc:Napoleon_Chunk) ON (nc.textEmbeddingOpenAI) \n", 269 | " OPTIONS { indexConfig: {\n", 270 | " `vector.dimensions`: 1536,\n", 271 | " `vector.similarity_function`: 'cosine' \n", 272 | " }}\n", 273 | "\"\"\"\n", 274 | "\n", 275 | "kg.query(VectorIndex)\n" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": "### 6. Embedding the text data using a provider and add it to the Napoleon_Chunk node as a property named textEmbeddingOpenAI" 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 9, 286 | "metadata": { 287 | "vscode": { 288 | "languageId": "powershell" 289 | } 290 | }, 291 | "outputs": [ 292 | { 293 | "data": { 294 | "text/plain": [ 295 | "[]" 296 | ] 297 | }, 298 | "execution_count": 9, 299 | "metadata": {}, 300 | "output_type": "execute_result" 301 | } 302 | ], 303 | "source": [ 304 | "kg.query(\"\"\"\n", 305 | " MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE Napoleon_Chunk.textEmbeddingOpenAI IS NULL\n", 306 | " WITH Napoleon_Chunk, genai.vector.encode(\n", 307 | " Napoleon_Chunk.text, \n", 308 | " \"OpenAI\", \n", 309 | " {\n", 310 | " token: $openAiApiKey, \n", 311 | " endpoint: $openAiEndpoint\n", 312 | " }) AS vector\n", 313 | " CALL db.create.setNodeVectorProperty(Napoleon_Chunk, \"textEmbeddingOpenAI\", vector)\n", 314 | " \"\"\", \n", 315 | " params={\"openAiApiKey\":OPENAI_API_KEY, \"openAiEndpoint\": OPENAI_ENDPOINT} )" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": { 321 | "vscode": { 322 | "languageId": "powershell" 323 | } 324 | }, 325 | "source": "### Optional: 6.1. If you want to Embed data using HuggingFace use this part" 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": 10, 330 | "metadata": { 331 | "vscode": { 332 | "languageId": "powershell" 333 | } 334 | }, 335 | "outputs": [], 336 | "source": [ 337 | "# # I am using T5 base but you can add more model in LLM file for yourself\n", 338 | "# embedd_model = T5.t5_model()\n", 339 | "# chunks = kg.query(\"MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE Napoleon_Chunk.textEmbedding IS NULL RETURN id(Napoleon_Chunk) as id, Napoleon_Chunk.text as text\")\n", 340 | "\n", 341 | "\n", 342 | "# for chunk in chunks:\n", 343 | "# text = chunk[\"text\"]\n", 344 | "# print(f\"Embedding Chunk {chunk['id']}\")\n", 345 | "# vector = embedd_model.embed([text])\n", 346 | "\n", 347 | "# vector_list = vector.tolist()\n", 348 | "# print(vector_list)\n", 349 | "\n", 350 | "# kg.query(\n", 351 | "# \"\"\"\n", 352 | "# MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE id(Napoleon_Chunk) = $id\n", 353 | "# SET Napoleon_Chunk.textEmbedding = $vector\n", 354 | "# \"\"\",\n", 355 | "# params={\"id\": chunk[\"id\"], \"vector\": vector_list}\n", 356 | "# )" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": "### 7. Finding all Chunks from the same formItem (General-information, Career or Death)" 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 12, 367 | "metadata": {}, 368 | "outputs": [ 369 | { 370 | "name": "stdout", 371 | "output_type": "stream", 372 | "text": [ 373 | "[{'chunkItemInfo': {'text': \"Napoleon Bonaparte (born Napoleone di Buonaparte;[b] 15 August 1769 – 5 May 1821), later known by his regnal name Napoleon\\xa0I, was a French military and political leader who rose to prominence during the French Revolution and led a series of successful campaigns across Europe during the Revolutionary Wars and Napoleonic Wars from 1796 to 1815. He was the leader of the French Republic as First Consul from 1799 to 1804, then of the French Empire as Emperor of the French from 1804 to 1814, and briefly again in 1815.\\nBorn on the island of Corsica to a family of Italian origin, Napoleon moved to mainland France in 1779 and was commissioned as an officer in the French Army in 1785. He supported the French Revolution in 1789, and promoted its cause in Corsica. He rose rapidly in the ranks after breaking the siege of Toulon in 1793 and firing on royalist insurgents in Paris on 13 Vendémiaire in 1795. In 1796, Napoleon commanded a military campaign against the Austrians and their Italian allies in the War of the First Coalition, scoring decisive victories and becoming a national hero. He led an expedition to Egypt and Syria in 1798 which served as a springboard to political power. In November 1799, Napoleon engineered the Coup of 18 Brumaire against the Directory, and became First Consul of the Republic. He won the Battle of Marengo in 1800, which secured French victory in the War of the Second Coalition, and in 1803 sold the territory of Louisiana to the United States, which doubled the latter's area. In December 1804, Napoleon crowned himself Emperor of the French, further expanding his power.\", 'formItem': 'General Information', 'chunkId': 'Napoleon-General Information-chunk0000', 'chunkSeqId': 0}}]\n", 374 | "[{'chunkItemInfo': {'text': \"Upon graduating in September 1785, Bonaparte was commissioned a second lieutenant in La Fère artillery regiment. He served in Valence and Auxonne until after the outbreak of the French Revolution in 1789, but spent long periods of leave in Corsica which fed his Corsican nationalism. In September 1789, he returned to Corsica and promoted the French revolutionary cause. Paoli returned to the island in July 1790, but he had no sympathy for Bonaparte, as he deemed his father a traitor for having deserted the cause of Corsican independence.\\nBonaparte plunged into a complex three-way struggle among royalists, revolutionaries, and Corsican nationalists. He became a supporter of the Jacobins and joined the pro-French Corsican Republicans who opposed Paoli's policy and his aspirations to secede. He was given command over a battalion of Corsican volunteers and promoted to captain in the regular army in 1792, despite exceeding his leave of absence and a dispute between his volunteers and the French garrison in Ajaccio.\\nIn February 1793, Bonaparte took part in the failed French expedition to Sardinia. Following allegations that Paoli had sabotaged the expedition and that his regime was corrupt and incompetent, the French National Convention outlawed him. In early June, Bonaparte and 400 French troops failed to capture Ajaccio from Corsican volunteers and the island was now controlled by Paoli's supporters. When Bonaparte learned that the Corsican assembly had condemned him and his family, the Buonapartes fled to Toulon on the French mainland.\", 'formItem': 'Career', 'chunkId': 'Napoleon-Career-chunk0000', 'chunkSeqId': 0}}]\n", 375 | "[{'chunkItemInfo': {'text': 'Napoleon\\'s health continued to worsen, and in March 1821 he was confined to bed. In April he wrote two wills declaring that he had been murdered by the British, that the Bourbons would fall and that his son would rule France. He left his fortune to 97 legatees and asked to be buried by the Seine.\\nOn 3 May he was given the last rites but could not take communion due to his illness. He died on 5 May 1821 at age 51. His last words, variously recorded by those present, were either France, l\\'armée, tête d\\'armée, Joséphine (\"France, the army, head of the army, Joséphine\"), or qui recule...à la tête d\\'armée (\"who retreats... at the head of the army\") or \"France, my son, the Army.\"\\nAntommarchi and the British wrote separate autopsy reports, each concluding that Napoleon had died of internal bleeding caused by stomach cancer, the disease that had killed his father. A later theory, based on high concentrations of arsenic found in samples of Napoleon\\'s hair, held that Napoleon had died of arsenic poisoning. However, subsequent studies also found high concentrations of arsenic in hair samples from Napoleon\\'s childhood and from his son and Joséphine. Arsenic was widely used in medicines and products such as hair creams in the 19th century. A 2021 study by an international team of gastrointestinal pathologists once again concluded that Napoleon died of stomach cancer.', 'formItem': 'Death', 'chunkId': 'Napoleon-Death-chunk0000', 'chunkSeqId': 0}}]\n" 376 | ] 377 | } 378 | ], 379 | "source": [ 380 | "# Each chunk is a small part of the document. To do this, first we need to find all chunks that belong together\n", 381 | "cypher = \"\"\"\n", 382 | " MATCH (from_same_chunk_item:Napoleon_Chunk)\n", 383 | " WHERE from_same_chunk_item.formItem = $NapoleonParam\n", 384 | " AND from_same_chunk_item.formItem = $NapoleonParam\n", 385 | " RETURN from_same_chunk_item {.text, .formItem, .chunkId, .chunkSeqId } as chunkItemInfo\n", 386 | " ORDER BY from_same_chunk_item.chunkSeqId ASC\n", 387 | " LIMIT 1\n", 388 | "\"\"\"\n", 389 | "\n", 390 | "items = ['General Information', 'Career', 'Death']\n", 391 | "for item in items:\n", 392 | " result = kg.query(cypher, params={'NapoleonParam':item})\n", 393 | " print(result)\n", 394 | " \n", 395 | " " 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": "### 8. Adding NEXT relationship to connect chunks together based on the order of their ID" 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": 15, 406 | "metadata": {}, 407 | "outputs": [ 408 | { 409 | "name": "stdout", 410 | "output_type": "stream", 411 | "text": [ 412 | "for General Information: [{'size(section_chunk_list)': 18}]\n", 413 | "for Career: [{'size(section_chunk_list)': 34}]\n", 414 | "for Death: [{'size(section_chunk_list)': 8}]\n" 415 | ] 416 | } 417 | ], 418 | "source": [ 419 | "# Then we should remember to return all the chunks in order.\n", 420 | "cypher = \"\"\"\n", 421 | " MATCH (from_same_chunk_item:Napoleon_Chunk)\n", 422 | " WHERE from_same_chunk_item.formItem = $NapoleonParam\n", 423 | " AND from_same_chunk_item.formItem = $NapoleonParam\n", 424 | " WITH from_same_chunk_item\n", 425 | " ORDER BY from_same_chunk_item.chunkSeqId ASC\n", 426 | " WITH collect(from_same_chunk_item) as section_chunk_list\n", 427 | " CALL apoc.nodes.link(\n", 428 | " section_chunk_list, \n", 429 | " \"NEXT\", \n", 430 | " {avoidDuplicates: true}\n", 431 | " )\n", 432 | " RETURN size(section_chunk_list)\n", 433 | "\"\"\"\n", 434 | "items = ['General Information', 'Career', 'Death']\n", 435 | "for item in items:\n", 436 | " result = kg.query(cypher, params={'NapoleonParam':item})\n", 437 | " print(f\"for {item}: {result}\" )\n" 438 | ] 439 | }, 440 | { 441 | "metadata": {}, 442 | "cell_type": "markdown", 443 | "source": "# 9. Checking the Graph Schema" 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 21, 448 | "metadata": {}, 449 | "outputs": [ 450 | { 451 | "name": "stdout", 452 | "output_type": "stream", 453 | "text": [ 454 | "Node properties are the following:\n", 455 | "Person {name: STRING},Napoleon_Chunk {chunkId: STRING, formItem: STRING, chunkSeqId: INTEGER, text: STRING, source: STRING, textEmbeddingOpenAI: LIST},Talleyrand_Chunk {chunkId: STRING, formItem: STRING, chunkSeqId: INTEGER, text: STRING, source: STRING, textEmbeddingOpenAI: LIST},Waterloo_Chunk {chunkId: STRING, formItem: STRING, chunkSeqId: INTEGER, text: STRING, source: STRING, textEmbeddingOpenAI: LIST},Event {name: STRING},Career {period: STRING, position: STRING, chunk_info: STRING},Death {date: STRING, location: STRING, chunk_info: STRING},General_info {chunk_info: STRING, knownFor: STRING, nationality: STRING, deathDate: STRING, birthDate: STRING, location: STRING, battleDate: STRING, outcome: STRING, commander: STRING},Reason {chunk_info: STRING, cause: STRING, strategicMistake: STRING, politicalImpact: STRING},Combatant {chunk_info: STRING, frenchCommander: STRING, alliedCommander: STRING, prussianCommander: STRING, mainForces: STRING},Consequence {chunk_info: STRING, immediateResult: STRING, longTermImpact: STRING, geopoliticalEffect: STRING}\n", 456 | "Relationship properties are the following:\n", 457 | "\n", 458 | "The relationships are the following:\n", 459 | "(:Person)-[:RELATED_TO]->(:Person),(:Person)-[:RELATED_TO]->(:Event),(:Person)-[:HAS_Career_INFO]->(:Career),(:Person)-[:HAS_Death_INFO]->(:Death),(:Person)-[:HAS_General_INFO]->(:General_info),(:Napoleon_Chunk)-[:NEXT]->(:Napoleon_Chunk),(:Talleyrand_Chunk)-[:NEXT]->(:Talleyrand_Chunk),(:Waterloo_Chunk)-[:NEXT]->(:Waterloo_Chunk),(:Event)-[:HAS_Chunk_INFO]->(:Combatant),(:Event)-[:HAS_Chunk_INFO]->(:Consequence),(:Event)-[:HAS_Chunk_INFO]->(:General_info),(:Event)-[:HAS_Chunk_INFO]->(:Reason),(:Career)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk),(:Career)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk),(:Death)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk),(:Death)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk),(:General_info)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk),(:General_info)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk),(:General_info)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk),(:Reason)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk),(:Combatant)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk),(:Consequence)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk)\n" 460 | ] 461 | } 462 | ], 463 | "source": [ 464 | "kg.refresh_schema()\n", 465 | "print(kg.schema)" 466 | ] 467 | } 468 | ], 469 | "metadata": { 470 | "kernelspec": { 471 | "display_name": "base", 472 | "language": "python", 473 | "name": "python3" 474 | }, 475 | "language_info": { 476 | "codemirror_mode": { 477 | "name": "ipython", 478 | "version": 3 479 | }, 480 | "file_extension": ".py", 481 | "mimetype": "text/x-python", 482 | "name": "python", 483 | "nbconvert_exporter": "python", 484 | "pygments_lexer": "ipython3", 485 | "version": "3.12.1" 486 | } 487 | }, 488 | "nbformat": 4, 489 | "nbformat_minor": 2 490 | } 491 | -------------------------------------------------------------------------------- /KnowledgeGraph/Nodes_and_Relationships.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 7, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "load_dotenv()\n", 11 | "import os\n", 12 | "\n", 13 | "# Langchain\n", 14 | "from langchain_community.graphs import Neo4jGraph\n", 15 | "import chunking\n", 16 | "# Warning control\n", 17 | "import warnings\n", 18 | "warnings.filterwarnings(\"ignore\")" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 8, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "# Load from environment\n", 28 | "\n", 29 | "NEO4J_URI = os.getenv('NEO4J_URI')\n", 30 | "NEO4J_USERNAME = os.getenv('NEO4J_USERNAME')\n", 31 | "NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD')\n", 32 | "NEO4J_DATABASE = os.getenv('NEO4J_DATABASE') or 'neo4j'\n", 33 | "OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\n", 34 | "OPENAI_ENDPOINT = os.getenv('OPENAI_BASE_URL') + '/embeddings'\n", 35 | "\n", 36 | "\n", 37 | "kg = Neo4jGraph(\n", 38 | " url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE\n", 39 | ")" 40 | ] 41 | }, 42 | { 43 | "metadata": {}, 44 | "cell_type": "markdown", 45 | "source": "# Nodes" 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 14, 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "data": { 54 | "text/plain": [ 55 | "[]" 56 | ] 57 | }, 58 | "execution_count": 14, 59 | "metadata": {}, 60 | "output_type": "execute_result" 61 | } 62 | ], 63 | "source": [ 64 | "cypher = \"\"\" \n", 65 | "// Person Nodes\n", 66 | "CREATE (napoleon:Person {\n", 67 | " name: \"Napoleon Bonaparte\"\n", 68 | "})\n", 69 | "\n", 70 | "\n", 71 | "CREATE (talleyrand:Person {\n", 72 | " name: \"Charles-Maurice de Talleyrand\"\n", 73 | "})\n", 74 | "\n", 75 | "// Event node\n", 76 | "CREATE (waterloo:Event {\n", 77 | " name: \"Battle of Waterloo\"\n", 78 | "})\n", 79 | "\n", 80 | "\n", 81 | "// sub-event nodes\n", 82 | "CREATE (waterlooGeneral:General_info {\n", 83 | " chunk_info: \"General information\",\n", 84 | " battleDate: \"1815-06-18\",\n", 85 | " location: \"Waterloo, Belgium\",\n", 86 | " outcome: \"Decisive defeat for Napoleon\",\n", 87 | " commander: \"Duke of Wellington\"\n", 88 | "})\n", 89 | "CREATE (waterlooReason:Reason {\n", 90 | " chunk_info: \"Reason\",\n", 91 | " cause: \"Napoleon's return from exile\",\n", 92 | " strategicMistake: \"Failure to unite forces before battle\",\n", 93 | " politicalImpact: \"End of the Napoleonic Wars\"\n", 94 | "})\n", 95 | "\n", 96 | "CREATE (waterlooCombatant:Combatant {\n", 97 | " chunk_info: \"Combatant\",\n", 98 | " frenchCommander: \"Napoleon Bonaparte\",\n", 99 | " alliedCommander: \"Duke of Wellington\",\n", 100 | " prussianCommander: \"Gebhard Leberecht von Blücher\",\n", 101 | " mainForces: \"French Army, British Army, Prussian Army\"\n", 102 | "})\n", 103 | "\n", 104 | "CREATE (waterlooConsequence:Consequence {\n", 105 | " chunk_info: \"Consequence\",\n", 106 | " immediateResult: \"Napoleon's second abdication\",\n", 107 | " longTermImpact: \"Restoration of the Bourbon monarchy\",\n", 108 | " geopoliticalEffect: \"Redrawing of European borders\"\n", 109 | "})\n", 110 | "\n", 111 | "\n", 112 | "\n", 113 | "\n", 114 | "// Sub-person nodes: Napoleon-General-Info\n", 115 | "CREATE (napoleonGeneral:General_info {\n", 116 | " chunk_info: \"General Information\",\n", 117 | " birthDate: \"1769-08-15\",\n", 118 | " deathDate: \"1821-05-05\",\n", 119 | " nationality: \"French\",\n", 120 | " knownFor: \"Military and political leader\"\n", 121 | "})\n", 122 | "\n", 123 | "// Sub-person nodes: Talleyrand-General-Info\n", 124 | "CREATE (talleyrandGeneral:General_info {\n", 125 | " chunk_info: \"General Information\",\n", 126 | " birthDate: \"1754-02-02\",\n", 127 | " deathDate: \"1838-05-17\",\n", 128 | " nationality: \"French\",\n", 129 | " knownFor: \"Diplomat and statesman\"\n", 130 | "})\n", 131 | "\n", 132 | "// Sub-person nodes: Napoleon-Career\n", 133 | "CREATE (napoleonCareer:Career {\n", 134 | " position: \"Emperor\",\n", 135 | " period: \"1804-1814\",\n", 136 | " chunk_info: \"Career\"\n", 137 | "})\n", 138 | "\n", 139 | "// Sub-person nodes: Talleyrand-Career\n", 140 | "CREATE (talleyrandCareer:Career {\n", 141 | " position: \"Foreign Minister\",\n", 142 | " period: \"1799-1807\",\n", 143 | " chunk_info: \"Career\"\n", 144 | "})\n", 145 | "\n", 146 | "// Sub-person nodes: Napoleon-Death\n", 147 | "CREATE (napoleonDeath:Death {\n", 148 | " date: \"1821-05-05\",\n", 149 | " location: \"Longwood, Saint Helena\",\n", 150 | " chunk_info: \"Death\"\n", 151 | "})\n", 152 | "\n", 153 | "// Sub-person nodes: Talleyrand-Death\n", 154 | "CREATE (talleyrandDeath:Death {\n", 155 | " date: \"1838-05-17\",\n", 156 | " location: \"Paris, France\",\n", 157 | " chunk_info: \"Death\"\n", 158 | "})\n", 159 | "\n", 160 | "// Create relationships for career and death information\n", 161 | "CREATE (napoleon)-[:HAS_Career_INFO]->(napoleonCareer)\n", 162 | "CREATE (napoleon)-[:HAS_Death_INFO]->(napoleonDeath)\n", 163 | "CREATE (napoleon)-[:HAS_General_INFO]->(napoleonGeneral)\n", 164 | "\n", 165 | "CREATE (talleyrand)-[:HAS_Career_INFO]->(talleyrandCareer)\n", 166 | "CREATE (talleyrand)-[:HAS_Death_INFO]->(talleyrandDeath)\n", 167 | "CREATE (talleyrand)-[:HAS_General_INFO]->(talleyrandGeneral)\n", 168 | "\n", 169 | "\n", 170 | "// Create relationships between Person nodes\n", 171 | "CREATE (napoleon)-[:RELATED_TO]->(talleyrand)\n", 172 | "CREATE (talleyrand)-[:RELATED_TO]->(napoleon)\n", 173 | "// Create relationships between Person nodes and Event\n", 174 | "CREATE (napoleon)-[:RELATED_TO]->(waterloo)\n", 175 | "CREATE (talleyrand)-[:RELATED_TO]->(waterloo)\n", 176 | "\n", 177 | "\n", 178 | "// Create relationships between waterloo nodes\n", 179 | "CREATE (waterloo)-[:HAS_General_INFO]->(waterlooGeneral)\n", 180 | "CREATE (waterloo)-[:HAS_Reason_INFO]->(waterlooReason)\n", 181 | "CREATE (waterloo)-[:HAS_Combatant_INFO]->(waterlooCombatant)\n", 182 | "CREATE (waterloo)-[:HAS_Consequence_INFO]->(waterlooConsequence)\n", 183 | "\"\"\"\n", 184 | "\n", 185 | "\n", 186 | "\n", 187 | "kg.query(cypher)" 188 | ] 189 | }, 190 | { 191 | "metadata": {}, 192 | "cell_type": "markdown", 193 | "source": "# Relationship between nodes" 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 15, 198 | "metadata": {}, 199 | "outputs": [ 200 | { 201 | "data": { 202 | "text/plain": [ 203 | "[{'count(r)': 34}]" 204 | ] 205 | }, 206 | "execution_count": 15, 207 | "metadata": {}, 208 | "output_type": "execute_result" 209 | } 210 | ], 211 | "source": [ 212 | "# Connect napoleon Career node to each chunk of napoleon career\n", 213 | "cypher = \"\"\" \n", 214 | "MATCH (napoleonCareer:Career {position: \"Emperor\"}), (careerChunks:Napoleon_Chunk)\n", 215 | " WHERE napoleonCareer.chunk_info = careerChunks.formItem \n", 216 | " WITH napoleonCareer, careerChunks\n", 217 | "MERGE (napoleonCareer)-[r:HAS_Chunk_INFO]->(careerChunks)\n", 218 | "RETURN count(r)\n", 219 | "\"\"\"\n", 220 | "\n", 221 | "kg.query(cypher)" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 16, 227 | "metadata": {}, 228 | "outputs": [ 229 | { 230 | "data": { 231 | "text/plain": [ 232 | "[{'count(r)': 8}]" 233 | ] 234 | }, 235 | "execution_count": 16, 236 | "metadata": {}, 237 | "output_type": "execute_result" 238 | } 239 | ], 240 | "source": [ 241 | "# Connect napoleon death node to each chunk of napoleon death\n", 242 | "cypher = \"\"\" \n", 243 | "MATCH (napoleonDeath:Death {location: \"Longwood, Saint Helena\"}), (deathChunks:Napoleon_Chunk)\n", 244 | " WHERE napoleonDeath.chunk_info = deathChunks.formItem \n", 245 | " WITH napoleonDeath, deathChunks\n", 246 | "MERGE (napoleonDeath)-[r:HAS_Chunk_INFO]->(deathChunks)\n", 247 | "RETURN count(r)\n", 248 | "\"\"\"\n", 249 | "\n", 250 | "kg.query(cypher)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 17, 256 | "metadata": {}, 257 | "outputs": [ 258 | { 259 | "data": { 260 | "text/plain": [ 261 | "[{'count(r)': 18}]" 262 | ] 263 | }, 264 | "execution_count": 17, 265 | "metadata": {}, 266 | "output_type": "execute_result" 267 | } 268 | ], 269 | "source": [ 270 | "# Connect napoleon general info node to each chunk of napoleon general information\n", 271 | "cypher = \"\"\" \n", 272 | "MATCH (napoleonGeneral:General_info {knownFor: \"Military and political leader\"}), (generalChunks:Napoleon_Chunk)\n", 273 | " WHERE napoleonGeneral.chunk_info = generalChunks.formItem\n", 274 | " WITH napoleonGeneral, generalChunks\n", 275 | "MERGE (napoleonGeneral)-[r:HAS_Chunk_INFO]->(generalChunks)\n", 276 | "RETURN count(r)\n", 277 | "\"\"\"\n", 278 | "\n", 279 | "kg.query(cypher)" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": 18, 285 | "metadata": {}, 286 | "outputs": [ 287 | { 288 | "data": { 289 | "text/plain": [ 290 | "[{'count(r)': 13}]" 291 | ] 292 | }, 293 | "execution_count": 18, 294 | "metadata": {}, 295 | "output_type": "execute_result" 296 | } 297 | ], 298 | "source": [ 299 | "# Connect Talleyrand Career node to each chunk of Talleyrand career\n", 300 | "cypher = \"\"\" \n", 301 | "MATCH (TalleyrandCareer:Career {position: \"Foreign Minister\"}), (careerChunks:Talleyrand_Chunk)\n", 302 | " WHERE TalleyrandCareer.chunk_info = careerChunks.formItem \n", 303 | " WITH TalleyrandCareer, careerChunks\n", 304 | "MERGE (TalleyrandCareer)-[r:HAS_Chunk_INFO]->(careerChunks)\n", 305 | "RETURN count(r)\n", 306 | "\"\"\"\n", 307 | "\n", 308 | "kg.query(cypher)" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 19, 314 | "metadata": {}, 315 | "outputs": [ 316 | { 317 | "data": { 318 | "text/plain": [ 319 | "[{'count(r)': 1}]" 320 | ] 321 | }, 322 | "execution_count": 19, 323 | "metadata": {}, 324 | "output_type": "execute_result" 325 | } 326 | ], 327 | "source": [ 328 | "# Connect Talleyrand Career node to each chunk of Talleyrand career\n", 329 | "cypher = \"\"\" \n", 330 | "MATCH (TalleyrandDeath:Death {location: \"Paris, France\"}), (careerChunks:Talleyrand_Chunk)\n", 331 | " WHERE TalleyrandDeath.chunk_info = careerChunks.formItem \n", 332 | " WITH TalleyrandDeath, careerChunks\n", 333 | "MERGE (TalleyrandDeath)-[r:HAS_Chunk_INFO]->(careerChunks)\n", 334 | "RETURN count(r)\n", 335 | "\"\"\"\n", 336 | "\n", 337 | "kg.query(cypher)" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 20, 343 | "metadata": {}, 344 | "outputs": [ 345 | { 346 | "data": { 347 | "text/plain": [ 348 | "[{'count(r)': 12}]" 349 | ] 350 | }, 351 | "execution_count": 20, 352 | "metadata": {}, 353 | "output_type": "execute_result" 354 | } 355 | ], 356 | "source": [ 357 | "# Connect Talleyrand general info node to each chunk of Talleyrand general information\n", 358 | "cypher = \"\"\" \n", 359 | "MATCH (TalleyrandGeneral:General_info {knownFor: \"Diplomat and statesman\"}), (generalChunks:Talleyrand_Chunk)\n", 360 | " WHERE TalleyrandGeneral.chunk_info = generalChunks.formItem\n", 361 | " WITH TalleyrandGeneral, generalChunks\n", 362 | "MERGE (TalleyrandGeneral)-[r:HAS_Chunk_INFO]->(generalChunks)\n", 363 | "RETURN count(r)\n", 364 | "\"\"\"\n", 365 | "\n", 366 | "kg.query(cypher)" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 21, 372 | "metadata": {}, 373 | "outputs": [ 374 | { 375 | "data": { 376 | "text/plain": [ 377 | "[{'count(r)': 5}]" 378 | ] 379 | }, 380 | "execution_count": 21, 381 | "metadata": {}, 382 | "output_type": "execute_result" 383 | } 384 | ], 385 | "source": [ 386 | "# Connect waterloo general info node to each chunk of waterloo general information\n", 387 | "cypher = \"\"\" \n", 388 | "MATCH (waterlooGeneral:General_info {chunk_info: \"General information\"}), (waterlooChunks:Waterloo_Chunk)\n", 389 | " WHERE waterlooGeneral.chunk_info = waterlooChunks.formItem\n", 390 | " WITH waterlooGeneral, waterlooChunks\n", 391 | "MERGE (waterlooGeneral)-[r:HAS_Chunk_INFO]->(waterlooChunks)\n", 392 | "RETURN count(r)\n", 393 | "\"\"\"\n", 394 | "\n", 395 | "kg.query(cypher)" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 22, 401 | "metadata": {}, 402 | "outputs": [ 403 | { 404 | "data": { 405 | "text/plain": [ 406 | "[{'count(r)': 5}]" 407 | ] 408 | }, 409 | "execution_count": 22, 410 | "metadata": {}, 411 | "output_type": "execute_result" 412 | } 413 | ], 414 | "source": [ 415 | "# Connect waterloo Reason node to each chunk of waterloo Reason information\n", 416 | "cypher = \"\"\" \n", 417 | "MATCH (waterlooReason:Reason {chunk_info: \"Reason\"}), (waterlooChunks:Waterloo_Chunk)\n", 418 | " WHERE waterlooReason.chunk_info = waterlooChunks.formItem\n", 419 | " WITH waterlooReason, waterlooChunks\n", 420 | "MERGE (waterlooReason)-[r:HAS_Chunk_INFO]->(waterlooChunks)\n", 421 | "RETURN count(r)\n", 422 | "\"\"\"\n", 423 | "\n", 424 | "kg.query(cypher)" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": 23, 430 | "metadata": {}, 431 | "outputs": [ 432 | { 433 | "data": { 434 | "text/plain": [ 435 | "[{'count(r)': 23}]" 436 | ] 437 | }, 438 | "execution_count": 23, 439 | "metadata": {}, 440 | "output_type": "execute_result" 441 | } 442 | ], 443 | "source": [ 444 | "# Connect waterloo Combatant node to each chunk of waterloo Combatant information\n", 445 | "cypher = \"\"\" \n", 446 | "MATCH (waterlooCombatant:Combatant {chunk_info: \"Combatant\"}), (waterlooChunks:Waterloo_Chunk)\n", 447 | " WHERE waterlooCombatant.chunk_info = waterlooChunks.formItem\n", 448 | " WITH waterlooCombatant, waterlooChunks\n", 449 | "MERGE (waterlooCombatant)-[r:HAS_Chunk_INFO]->(waterlooChunks)\n", 450 | "RETURN count(r)\n", 451 | "\"\"\"\n", 452 | "\n", 453 | "kg.query(cypher)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": 24, 459 | "metadata": {}, 460 | "outputs": [ 461 | { 462 | "data": { 463 | "text/plain": [ 464 | "[{'count(r)': 28}]" 465 | ] 466 | }, 467 | "execution_count": 24, 468 | "metadata": {}, 469 | "output_type": "execute_result" 470 | } 471 | ], 472 | "source": [ 473 | "# Connect waterloo Consequence node to each chunk of waterloo Consequence information\n", 474 | "cypher = \"\"\" \n", 475 | "MATCH (waterlooConsequence:Consequence {chunk_info: \"Consequence\"}), (waterlooChunks:Waterloo_Chunk)\n", 476 | " WHERE waterlooConsequence.chunk_info = waterlooChunks.formItem\n", 477 | " WITH waterlooConsequence, waterlooChunks\n", 478 | "MERGE (waterlooConsequence)-[r:HAS_Chunk_INFO]->(waterlooChunks)\n", 479 | "RETURN count(r)\n", 480 | "\"\"\"\n", 481 | "\n", 482 | "kg.query(cypher)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": null, 488 | "metadata": {}, 489 | "outputs": [], 490 | "source": [] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": null, 495 | "metadata": {}, 496 | "outputs": [], 497 | "source": [] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": null, 502 | "metadata": {}, 503 | "outputs": [], 504 | "source": [] 505 | } 506 | ], 507 | "metadata": { 508 | "kernelspec": { 509 | "display_name": "base", 510 | "language": "python", 511 | "name": "python3" 512 | }, 513 | "language_info": { 514 | "codemirror_mode": { 515 | "name": "ipython", 516 | "version": 3 517 | }, 518 | "file_extension": ".py", 519 | "mimetype": "text/x-python", 520 | "name": "python", 521 | "nbconvert_exporter": "python", 522 | "pygments_lexer": "ipython3", 523 | "version": "3.12.2" 524 | } 525 | }, 526 | "nbformat": 4, 527 | "nbformat_minor": 2 528 | } 529 | -------------------------------------------------------------------------------- /KnowledgeGraph/Talleyrand.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "import os\n", 11 | "\n", 12 | "# Langchain\n", 13 | "from langchain_community.graphs import Neo4jGraph\n", 14 | "import chunking # personal package (dont add to poetry)\n", 15 | "# Warning control\n", 16 | "import warnings\n", 17 | "warnings.filterwarnings(\"ignore\")" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "# Load from environment\n", 27 | "load_dotenv('.env', override=True)\n", 28 | "NEO4J_URI = os.getenv('NEO4J_URI')\n", 29 | "NEO4J_USERNAME = os.getenv('NEO4J_USERNAME')\n", 30 | "NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD')\n", 31 | "NEO4J_DATABASE = os.getenv('NEO4J_DATABASE') or 'neo4j'\n", 32 | "OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\n", 33 | "OPENAI_ENDPOINT = os.getenv('OPENAI_BASE_URL') + '/embeddings'\n", 34 | "\n", 35 | "\n", 36 | "kg = Neo4jGraph(\n", 37 | " url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE\n", 38 | ")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "### 1. Splitting the Chunks" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 3, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# Load JSON file\n", 55 | "file = \"../data/json/Talleyrand.json\"" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 4, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "['General Information', 'Career', 'Death', 'Source']\n", 68 | "Processing General Information from ../data/json/Talleyrand.json\n", 69 | "\tSplit into 12 chunks\n", 70 | "Processing Career from ../data/json/Talleyrand.json\n", 71 | "\tSplit into 13 chunks\n", 72 | "Processing Death from ../data/json/Talleyrand.json\n", 73 | "\tSplit into 1 chunks\n", 74 | "Processing Source from ../data/json/Talleyrand.json\n", 75 | "\tSplit into 1 chunks\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "file_chunks = chunking.split_data_from_file(file)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "### 2. Create Chunk node in KnowledgeGraph with properties extracted from file" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "[{'mergedChunk': {'formItem': 'General Information',\n", 99 | " 'text': 'Charles-Maurice de Talleyrand-Périgord (/ˌtælɪrænd ˈpɛrɪɡɔːr/, French: [ʃaʁl mɔʁis də tal(ɛ)ʁɑ̃ peʁiɡɔʁ, – moʁ-]; 2 February 1754 – 17 May 1838), 1st Prince of Benevento, then Prince of Talleyrand, was a French secularized clergyman, statesman, and leading diplomat. After studying theology, he became Agent-General of the Clergy in 1780. In 1789, just before the French Revolution, he became Bishop of Autun. He worked at the highest levels of successive French governments, most commonly as foreign minister or in some other diplomatic capacity. His career spanned the regimes of Louis XVI, the years of the French Revolution, Napoleon, Louis XVIII, and Louis Philippe I. Those Talleyrand served often distrusted him but, like Napoleon, found him extremely useful. The name \"Talleyrand\" has become a byword for crafty and cynical diplomacy.\\nHe was Napoleon\\'s chief diplomat during the years when French military victories brought one European state after another under French hegemony. However, most of the time, Talleyrand worked for peace so as to consolidate France\\'s gains. He succeeded in obtaining peace with Austria through the 1801 Treaty of Lunéville and with Britain in the 1802 Treaty of Amiens. He could not prevent the renewal of war in 1803 but by 1805 he opposed his emperor\\'s renewed wars against Austria, Prussia, and Russia. He resigned as foreign minister in August 1807, but retained the trust of Napoleon. He conspired to undermine the emperor\\'s plans through secret dealings with Tsar Alexander I of Russia and the Austrian minister Klemens von Metternich. Talleyrand sought a negotiated secure peace so as to perpetuate the gains of the French Revolution. Napoleon rejected peace; when he fell in 1814, Talleyrand supported the Bourbon Restoration decided by the Allies. He played a major role at the Congress of Vienna in 1814–1815, where he negotiated a favorable settlement for France and played a role in unwinding the Napoleonic Wars.',\n", 100 | " 'source': 'Talleyrand History',\n", 101 | " 'chunkId': 'Talleyrand-General Information-chunk0000',\n", 102 | " 'chunkSeqId': 0}}]" 103 | ] 104 | }, 105 | "execution_count": 5, 106 | "metadata": {}, 107 | "output_type": "execute_result" 108 | } 109 | ], 110 | "source": [ 111 | "# Create Napoleon_Chunk node and its properties\n", 112 | "merge_chunk_node_query = \"\"\"\n", 113 | "MERGE(mergedChunk:Talleyrand_Chunk {chunkId: $chunkParam.chunkId})\n", 114 | " ON CREATE SET\n", 115 | " mergedChunk.text = $chunkParam.text, \n", 116 | " mergedChunk.source = $chunkParam.source, \n", 117 | " mergedChunk.formItem = $chunkParam.formItem, \n", 118 | " mergedChunk.chunkSeqId = $chunkParam.chunkSeqId\n", 119 | "RETURN mergedChunk\n", 120 | "\"\"\"\n", 121 | "kg.query(merge_chunk_node_query, \n", 122 | " params={'chunkParam':file_chunks[0]})" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### 3. Create a uniqueness constraint to avoid duplicate chunks" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 6, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "data": { 139 | "text/plain": [ 140 | "[]" 141 | ] 142 | }, 143 | "execution_count": 6, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "# Create a uniqueness constraint to avoid duplicate chunks\n", 150 | "avoid_duplicate_chunks = \"\"\"\n", 151 | "CREATE CONSTRAINT unique_chunk IF NOT EXISTS \n", 152 | " FOR (tc:Talleyrand_Chunk) REQUIRE tc.chunkId IS UNIQUE\n", 153 | "\"\"\"\n", 154 | "\n", 155 | "kg.query(avoid_duplicate_chunks)\n" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "### 4. Adding all chunks data to knowledegeGraph" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 7, 168 | "metadata": {}, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0000\n", 175 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0001\n", 176 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0002\n", 177 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0003\n", 178 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0004\n", 179 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0005\n", 180 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0006\n", 181 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0007\n", 182 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0008\n", 183 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0009\n", 184 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0010\n", 185 | "Creating `:Chunk` node for chunk ID Talleyrand-General Information-chunk0011\n", 186 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0000\n", 187 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0001\n", 188 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0002\n", 189 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0003\n", 190 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0004\n", 191 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0005\n", 192 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0006\n", 193 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0007\n", 194 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0008\n", 195 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0009\n", 196 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0010\n", 197 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0011\n", 198 | "Creating `:Chunk` node for chunk ID Talleyrand-Career-chunk0012\n", 199 | "Creating `:Chunk` node for chunk ID Talleyrand-Death-chunk0000\n", 200 | "Creating `:Chunk` node for chunk ID Talleyrand-Source-chunk0000\n", 201 | "Created 27 nodes\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "node_count = 0\n", 207 | "for chunk in file_chunks:\n", 208 | " print(f\"Creating `:Chunk` node for chunk ID {chunk['chunkId']}\")\n", 209 | " kg.query(merge_chunk_node_query, \n", 210 | " params={\n", 211 | " 'chunkParam': chunk\n", 212 | " })\n", 213 | " node_count += 1\n", 214 | "print(f\"Created {node_count} nodes\")" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "### 5. Create a VectorIndex for the Talleyrand_Chunk" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 8, 227 | "metadata": {}, 228 | "outputs": [ 229 | { 230 | "data": { 231 | "text/plain": [ 232 | "[]" 233 | ] 234 | }, 235 | "execution_count": 8, 236 | "metadata": {}, 237 | "output_type": "execute_result" 238 | } 239 | ], 240 | "source": [ 241 | "VectorIndex = \"\"\"\n", 242 | " CREATE VECTOR INDEX `TalleyrandOpenAI` IF NOT EXISTS\n", 243 | " FOR (nc:Napoleon_Chunk) ON (nc.textEmbeddingOpenAI) \n", 244 | " OPTIONS { indexConfig: {\n", 245 | " `vector.dimensions`: 1536,\n", 246 | " `vector.similarity_function`: 'cosine' \n", 247 | " }}\n", 248 | "\"\"\"\n", 249 | "\n", 250 | "kg.query(VectorIndex)\n" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": "### 6. Embedding the text data using a provider and add it to the Talleyrand_Chunk node as a property named textEmbeddingOpenAI" 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": 9, 261 | "metadata": { 262 | "vscode": { 263 | "languageId": "powershell" 264 | } 265 | }, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/plain": [ 270 | "[]" 271 | ] 272 | }, 273 | "execution_count": 9, 274 | "metadata": {}, 275 | "output_type": "execute_result" 276 | } 277 | ], 278 | "source": [ 279 | "# kg.query(\"\"\"\n", 280 | "# MATCH (Talleyrand_Chunk:Talleyrand_Chunk) WHERE Talleyrand_Chunk.textEmbeddingOpenAI IS NULL\n", 281 | "# WITH Talleyrand_Chunk, genai.vector.encode(\n", 282 | "# Talleyrand_Chunk.text, \n", 283 | "# \"OpenAI\", \n", 284 | "# {\n", 285 | "# token: $openAiApiKey, \n", 286 | "# endpoint: $openAiEndpoint\n", 287 | "# }) AS vector\n", 288 | "# CALL db.create.setNodeVectorProperty(Talleyrand_Chunk, \"textEmbeddingOpenAI\", vector) \n", 289 | "# \"\"\", \n", 290 | "# params={\"openAiApiKey\":OPENAI_API_KEY, \"openAiEndpoint\": OPENAI_ENDPOINT} )" 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": { 296 | "vscode": { 297 | "languageId": "powershell" 298 | } 299 | }, 300 | "source": "### Optional: 6.1. If you want to Embed data using HuggingFace use this part" 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": 10, 305 | "metadata": { 306 | "vscode": { 307 | "languageId": "powershell" 308 | } 309 | }, 310 | "outputs": [], 311 | "source": [ 312 | "# # I am using T5 base but you can add more model in LLM file for yourself\n", 313 | "# embedd_model = T5.t5_model()\n", 314 | "# chunks = kg.query(\"MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE Talleyrand_Chunk.textEmbedding IS NULL RETURN id(Napoleon_Chunk) as id, Napoleon_Chunk.text as text\")\n", 315 | "\n", 316 | "\n", 317 | "# for chunk in chunks:\n", 318 | "# text = chunk[\"text\"]\n", 319 | "# print(f\"Embedding Chunk {chunk['id']}\")\n", 320 | "# vector = embedd_model.embed([text])\n", 321 | "\n", 322 | "# vector_list = vector.tolist()\n", 323 | "# print(vector_list)\n", 324 | "\n", 325 | "# kg.query(\n", 326 | "# \"\"\"\n", 327 | "# MATCH (Napoleon_Chunk:Napoleon_Chunk) WHERE id(Napoleon_Chunk) = $id\n", 328 | "# SET Napoleon_Chunk.textEmbedding = $vector\n", 329 | "# \"\"\",\n", 330 | "# params={\"id\": chunk[\"id\"], \"vector\": vector_list}\n", 331 | "# )" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": "### 7. Finding all Chunks from the same formItem" 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 11, 342 | "metadata": {}, 343 | "outputs": [ 344 | { 345 | "name": "stdout", 346 | "output_type": "stream", 347 | "text": [ 348 | "General Information : [{'chunkItemInfo': {'text': 'Charles-Maurice de Talleyrand-Périgord (/ˌtælɪrænd ˈpɛrɪɡɔːr/, French: [ʃaʁl mɔʁis də tal(ɛ)ʁɑ̃ peʁiɡɔʁ, – moʁ-]; 2 February 1754 – 17 May 1838), 1st Prince of Benevento, then Prince of Talleyrand, was a French secularized clergyman, statesman, and leading diplomat. After studying theology, he became Agent-General of the Clergy in 1780. In 1789, just before the French Revolution, he became Bishop of Autun. He worked at the highest levels of successive French governments, most commonly as foreign minister or in some other diplomatic capacity. His career spanned the regimes of Louis XVI, the years of the French Revolution, Napoleon, Louis XVIII, and Louis Philippe I. Those Talleyrand served often distrusted him but, like Napoleon, found him extremely useful. The name \"Talleyrand\" has become a byword for crafty and cynical diplomacy.\\nHe was Napoleon\\'s chief diplomat during the years when French military victories brought one European state after another under French hegemony. However, most of the time, Talleyrand worked for peace so as to consolidate France\\'s gains. He succeeded in obtaining peace with Austria through the 1801 Treaty of Lunéville and with Britain in the 1802 Treaty of Amiens. He could not prevent the renewal of war in 1803 but by 1805 he opposed his emperor\\'s renewed wars against Austria, Prussia, and Russia. He resigned as foreign minister in August 1807, but retained the trust of Napoleon. He conspired to undermine the emperor\\'s plans through secret dealings with Tsar Alexander I of Russia and the Austrian minister Klemens von Metternich. Talleyrand sought a negotiated secure peace so as to perpetuate the gains of the French Revolution. Napoleon rejected peace; when he fell in 1814, Talleyrand supported the Bourbon Restoration decided by the Allies. He played a major role at the Congress of Vienna in 1814–1815, where he negotiated a favorable settlement for France and played a role in unwinding the Napoleonic Wars.', 'formItem': 'General Information', 'chunkId': 'Talleyrand-General Information-chunk0000', 'chunkSeqId': 0}}]\n", 349 | "Career : [{'chunkItemInfo': {'text': \"Talleyrand, along with Napoleon's younger brother, Lucien Bonaparte, was instrumental in the 1799 coup d'état of 18 Brumaire, establishing the French Consulate government, although he also made preparations for flight if necessary. He also persuaded Barras to resign as Director. Talleyrand was soon made Foreign Minister by Napoleon, although he rarely agreed with Napoleon's foreign policy. Domestically, Talleyrand used his influence to help in the repeal of the strict laws against émigrés, refractory clergy, and the royalists of the west. The Pope released him from the ban of excommunication in the Concordat of 1801, which also revoked the Civil Constitution of the Clergy. Talleyrand was instrumental in the completion of the Treaty of Amiens in 1802. He wanted Napoleon to keep peace afterwards, as he thought France had reached its maximum expansion.\\nTalleyrand was an integral player in the German mediatization. While the Treaty of Campo Formio of 1797 had, on paper, stripped German princes of their lands beyond the left bank of the Rhine, it was not enforced until the Treaty of Lunéville in 1801. As the French annexed these lands, leaders believed that rulers of states such as Baden, Bavaria, Württemberg, Prussia, Hesse and Nassau, who lost territories on the Left Bank, should receive new territories on the Right Bank through the secularization of ecclesiastical principalities. Many of these rulers gave out bribes in order to secure new lands, and Talleyrand and some of his associates amassed about 10 million francs in the process. This was the first blow in the destruction of the Holy Roman Empire.\", 'formItem': 'Career', 'chunkId': 'Talleyrand-Career-chunk0000', 'chunkSeqId': 0}}]\n", 350 | "Death : [{'chunkItemInfo': {'text': \"Charles Maurice de Talleyrand-Périgord, a prominent French diplomat, died on May 17, 1838, in Paris, at the age of 84. His death marked the end of a significant era in European diplomacy, as he had been a key political figure through the French Revolution, the Napoleonic era, and the Bourbon Restoration. In his later years, Talleyrand suffered from declining health, afflicted by gout and other ailments that left him increasingly frail. Before his death, he sought reconciliation with the Catholic Church, receiving the last rites from the Archbishop of Paris, thus signaling his return to the Church after many years of estrangement. Talleyrand's death was widely noted across Europe due to his immense influence on European politics and diplomacy. He is remembered as a master diplomat who navigated the turbulent political landscape of his time with skill and pragmatism. Born into an aristocratic family on February 2, 1754, Talleyrand initially pursued a clerical career, becoming a bishop. He played a significant role during the French Revolution, advocating for the confiscation of Church property and supporting the revolutionary government. Throughout his diplomatic career, he served under various regimes, including those of Napoleon Bonaparte and King Louis XVIII, showcasing his ability to adapt to changing political landscapes. He was instrumental in negotiating key treaties, including the Treaty of Vienna (1815), which reshaped Europe after the Napoleonic Wars. Known for his pragmatism and sometimes criticized for his opportunism, Talleyrand's actions were often driven by a desire to maintain stability and balance of power in Europe. His life and career were characterized by his extraordinary ability to survive and thrive through one of the most tumultuous periods in European history, and his death marked the end of a remarkable journey of political maneuvering and influence.\", 'formItem': 'Death', 'chunkId': 'Talleyrand-Death-chunk0000', 'chunkSeqId': 0}}]\n" 351 | ] 352 | } 353 | ], 354 | "source": [ 355 | "# Each chunk is a small part of the document. To do this, first we need to find all chunks that belong together\n", 356 | "cypher = \"\"\"\n", 357 | " MATCH (from_same_chunk_item:Talleyrand_Chunk)\n", 358 | " WHERE from_same_chunk_item.formItem = $TalleyrandParam\n", 359 | " AND from_same_chunk_item.formItem = $TalleyrandParam\n", 360 | " RETURN from_same_chunk_item {.text, .formItem, .chunkId, .chunkSeqId } as chunkItemInfo\n", 361 | " ORDER BY from_same_chunk_item.chunkSeqId ASC\n", 362 | " LIMIT 1\n", 363 | "\"\"\"\n", 364 | "\n", 365 | "items = ['General Information', 'Career', 'Death']\n", 366 | "for item in items:\n", 367 | " kg.query(cypher, params={'TalleyrandParam':item})\n", 368 | " print(f\"{item} : {kg.query(cypher, params={'TalleyrandParam':item})}\")" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": "### 8. Adding NEXT relationship to connect chunks together based on the order of their ID" 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": 13, 379 | "metadata": {}, 380 | "outputs": [ 381 | { 382 | "name": "stdout", 383 | "output_type": "stream", 384 | "text": [ 385 | "for General Information: [{'size(section_chunk_list)': 12}]\n", 386 | "for Career: [{'size(section_chunk_list)': 13}]\n", 387 | "for Death: [{'size(section_chunk_list)': 1}]\n" 388 | ] 389 | } 390 | ], 391 | "source": [ 392 | "# ordering based on chunkSeqId\n", 393 | "cypher = \"\"\"\n", 394 | " MATCH (from_same_chunk_item:Talleyrand_Chunk)\n", 395 | " WHERE from_same_chunk_item.formItem = $TalleyrandParam\n", 396 | " AND from_same_chunk_item.formItem = $TalleyrandParam\n", 397 | " WITH from_same_chunk_item\n", 398 | " ORDER BY from_same_chunk_item.chunkSeqId ASC\n", 399 | " WITH collect(from_same_chunk_item) as section_chunk_list\n", 400 | " CALL apoc.nodes.link(\n", 401 | " section_chunk_list, \n", 402 | " \"NEXT\", \n", 403 | " {avoidDuplicates: true}\n", 404 | " )\n", 405 | " RETURN size(section_chunk_list)\n", 406 | "\"\"\"\n", 407 | "\n", 408 | "items = ['General Information', 'Career', 'Death']\n", 409 | "for item in items:\n", 410 | " kg.query(cypher, params={'TalleyrandParam':item})\n", 411 | " print(f\"for {item}: {kg.query(cypher, params={'TalleyrandParam':item})}\" )" 412 | ] 413 | } 414 | ], 415 | "metadata": { 416 | "kernelspec": { 417 | "display_name": "base", 418 | "language": "python", 419 | "name": "python3" 420 | }, 421 | "language_info": { 422 | "codemirror_mode": { 423 | "name": "ipython", 424 | "version": 3 425 | }, 426 | "file_extension": ".py", 427 | "mimetype": "text/x-python", 428 | "name": "python", 429 | "nbconvert_exporter": "python", 430 | "pygments_lexer": "ipython3", 431 | "version": "3.12.1" 432 | } 433 | }, 434 | "nbformat": 4, 435 | "nbformat_minor": 2 436 | } 437 | -------------------------------------------------------------------------------- /KnowledgeGraph/__pycache__/chunking.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/KnowledgeGraph/__pycache__/chunking.cpython-312.pyc -------------------------------------------------------------------------------- /KnowledgeGraph/chunking.py: -------------------------------------------------------------------------------- 1 | import json 2 | from langchain.text_splitter import RecursiveCharacterTextSplitter 3 | 4 | 5 | text_splitter = RecursiveCharacterTextSplitter( 6 | chunk_size = 2000, 7 | chunk_overlap = 200, 8 | length_function = len, 9 | is_separator_regex = False, 10 | ) 11 | 12 | 13 | 14 | def split_data_from_file(file): 15 | #### define a variable to accumlate chunk records 16 | chunks_with_metadata = [] 17 | 18 | #### Load json file 19 | file_as_object = json.load(open(file)) 20 | keys = list(file_as_object.keys()) 21 | print(keys) 22 | #### pull these keys from the json file 23 | for item in keys: 24 | print(f'Processing {item} from {file}') 25 | 26 | #### grab the text of the item 27 | item_text = file_as_object[item] 28 | 29 | #### split the text into chunks 30 | item_text_chunks = text_splitter.split_text(item_text) 31 | 32 | chunk_seq_id = 0 33 | #### loop thtough chunks 34 | for chunk in item_text_chunks: 35 | 36 | #### extract file name from each chunk 37 | form_name = file[file.rindex('/') + 1:file.rindex('.')] 38 | 39 | #### create a record with metadata and the chunk text 40 | chunks_with_metadata.append({ 41 | #### metadata from looping... 42 | 'text': chunk, 43 | 'formItem': item, 44 | 'chunkSeqId': chunk_seq_id, 45 | #### constructed metadata... 46 | 'chunkId': f'{form_name}-{item}-chunk{chunk_seq_id:04d}', 47 | 'source': file_as_object['Source'] 48 | }) 49 | chunk_seq_id += 1 50 | print(f'\tSplit into {chunk_seq_id} chunks') 51 | return chunks_with_metadata -------------------------------------------------------------------------------- /LLM/T5.py: -------------------------------------------------------------------------------- 1 | from transformers import AutoTokenizer, pipeline, AutoModelForCausalLM 2 | from langchain_huggingface.llms import HuggingFacePipeline 3 | from transformers import T5Tokenizer, T5ForConditionalGeneration, pipeline, T5Model 4 | from langchain_huggingface import HuggingFaceEmbeddings 5 | import torch 6 | 7 | class t5_model: 8 | 9 | def __init__(self, model_id="t5-base"): 10 | self.model_id = model_id 11 | self.tokenizer = T5Tokenizer.from_pretrained(self.model_id) 12 | 13 | def load_model(self, max_new_tokens=1000): 14 | model = T5ForConditionalGeneration.from_pretrained(self.model_id) 15 | pipe = pipeline("text2text-generation", model=model, tokenizer=self.tokenizer, max_new_tokens=max_new_tokens,) 16 | hf = HuggingFacePipeline(pipeline=pipe) 17 | return hf 18 | 19 | def embed(self,text): 20 | model_name = "t5-base" 21 | tokenizer = T5Tokenizer.from_pretrained(model_name) 22 | model = T5Model.from_pretrained(model_name) 23 | # Tokenize the input text 24 | inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True) 25 | 26 | # Get the encoder's outputs 27 | with torch.no_grad(): 28 | outputs = model.encoder(**inputs) 29 | 30 | embeddings = outputs.last_hidden_state.mean(dim=1) 31 | return embeddings.squeeze().numpy() 32 | -------------------------------------------------------------------------------- /LLM/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/LLM/__init__.py -------------------------------------------------------------------------------- /LLM/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/LLM/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /LLM/__pycache__/model.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/LLM/__pycache__/model.cpython-312.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Knowledge Graph for Napoleon History Using Neo4j 2 | 3 | ## Watch demo on my linkedin: 4 | https://www.linkedin.com/posts/homayounsrp_graphrag-vectorrag-activity-7223765289434296320-5rQC?utm_source=share&utm_medium=member_ios 5 | ## Detailed Implementation Tutorial 6 | https://medium.com/@homayoun.srp/building-a-knowledge-graph-for-rag-using-neo4j-e69d3441d843 7 | 8 | 9 | This project is divided into two main parts: 10 | 11 | 1. **Designing the Knowledge Graph and Ingesting Data** 12 | 2. **Retrieving Data from the Knowledge Graph** 13 | To clone this repository, run: 14 | ``` 15 | git clone https://github.com/homayounsr/Knowledge-Graph-for-RAG-using-Neo4j 16 | ``` 17 | 18 | Install Poetry if you haven't already: 19 | ``` 20 | pip install poetry 21 | ``` 22 | 23 | Then, install the project dependencies: 24 | ``` 25 | poetry install 26 | ``` 27 | Use main.py to run the application 28 | 29 | ### Using Your Own Data 30 | If you want to use your personal data, follow these steps: 31 | 32 | 1. Place your raw data into the Data folder. 33 | 2. Clean the data using the preprocessing script. 34 | 3. Convert the cleaned text data to JSON using the text2json script. 35 | 4. Design your custom knowledge graph by modifying the Nodes_and_Relationships.ipynb notebook in the knowledge_graph folder. 36 | 5. Use the generated JSON file as input for your knowledge graph. 37 | 6. Modify Chunking.py to chunk your file and extract properties from it 38 | 39 | #### Contribution 40 | Contributions are welcome! Please submit a pull request or open an issue if you have suggestions or improvements. 41 | If you have any questions you can send an email 42 | to harrison.sohrab@gmail.com 43 | 44 | -------------------------------------------------------------------------------- /assets/Career.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/assets/Career.png -------------------------------------------------------------------------------- /assets/Death.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/assets/Death.png -------------------------------------------------------------------------------- /assets/KnowLedgeGraph_Design.txt: -------------------------------------------------------------------------------- 1 | Knowledge Graph Design 2 | 3 | Node Properties: 4 | 5 | - Person: 6 | - name: STRING 7 | 8 | - Napoleon_Chunk: 9 | - chunkId: STRING 10 | - formItem: STRING 11 | - chunkSeqId: INTEGER 12 | - text: STRING 13 | - source: STRING 14 | - textEmbeddingOpenAI: LIST 15 | 16 | - Talleyrand_Chunk: 17 | - chunkId: STRING 18 | - formItem: STRING 19 | - chunkSeqId: INTEGER 20 | - text: STRING 21 | - source: STRING 22 | - textEmbeddingOpenAI: LIST 23 | 24 | - Waterloo_Chunk: 25 | - chunkId: STRING 26 | - formItem: STRING 27 | - chunkSeqId: INTEGER 28 | - text: STRING 29 | - source: STRING 30 | - textEmbeddingOpenAI: LIST 31 | 32 | - Event: 33 | - name: STRING 34 | 35 | - Career: 36 | - period: STRING 37 | - position: STRING 38 | - chunk_info: STRING 39 | 40 | - Death: 41 | - date: STRING 42 | - location: STRING 43 | - chunk_info: STRING 44 | 45 | - General_info: 46 | - location: STRING 47 | - chunk_info: STRING 48 | - battleDate: STRING 49 | - outcome: STRING 50 | - commander: STRING 51 | - knownFor: STRING 52 | - nationality: STRING 53 | - deathDate: STRING 54 | - birthDate: STRING 55 | 56 | - Reason: 57 | - chunk_info: STRING 58 | - cause: STRING 59 | - strategicMistake: STRING 60 | - politicalImpact: STRING 61 | 62 | - Combatant: 63 | - chunk_info: STRING 64 | - frenchCommander: STRING 65 | - alliedCommander: STRING 66 | - prussianCommander: STRING 67 | - mainForces: STRING 68 | 69 | - Consequence: 70 | - chunk_info: STRING 71 | - immediateResult: STRING 72 | - longTermImpact: STRING 73 | - geopoliticalEffect: STRING 74 | 75 | Relationships: 76 | 77 | - (:Person)-[:RELATED_TO]->(:Event) 78 | - (:Person)-[:RELATED_TO]->(:Person) 79 | - (:Person)-[:HAS_Career_INFO]->(:Career) 80 | - (:Person)-[:HAS_Death_INFO]->(:Death) 81 | - (:Person)-[:HAS_General_INFO]->(:General_info) 82 | - (:Napoleon_Chunk)-[:NEXT]->(:Napoleon_Chunk) 83 | - (:Talleyrand_Chunk)-[:NEXT]->(:Talleyrand_Chunk) 84 | - (:Waterloo_Chunk)-[:NEXT]->(:Waterloo_Chunk) 85 | - (:Event)-[:HAS_Combatant_INFO]->(:Combatant) 86 | - (:Event)-[:HAS_Consequence_INFO]->(:Consequence) 87 | - (:Event)-[:HAS_Chunk_INFO]->(:General_info) 88 | - (:Career)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk) 89 | - (:Career)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk) 90 | - (:Death)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk) 91 | - (:Death)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk) 92 | - (:General_info)-[:HAS_Chunk_INFO]->(:Napoleon_Chunk) 93 | - (:General_info)-[:HAS_Chunk_INFO]->(:Talleyrand_Chunk) 94 | - (:General_info)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk) 95 | - (:Reason)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk) 96 | - (:Combatant)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk) 97 | - (:Consequence)-[:HAS_Chunk_INFO]->(:Waterloo_Chunk) 98 | -------------------------------------------------------------------------------- /assets/general_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/homayounsrp/Knowledge-Graph-for-RAG-using-Neo4j/27dcd7ca580fef351d1aae3dab21b6ffe3217b36/assets/general_info.png -------------------------------------------------------------------------------- /data/cleaned/Talleyrand.txt: -------------------------------------------------------------------------------- 1 | Main-Section: General Information 2 | 3 | 4 | Charles-Maurice de Talleyrand-Périgord (/ˌtælɪrænd ˈpɛrɪɡɔːr/,[1] French: [ʃaʁl mɔʁis də tal(ɛ)ʁɑ̃ peʁiɡɔʁ, – moʁ-]; 2 February 1754 – 17 May 1838), 1st Prince of Benevento, then Prince of Talleyrand, was a French secularized clergyman, statesman, and leading diplomat. After studying theology, he became Agent-General of the Clergy in 1780. In 1789, just before the French Revolution, he became Bishop of Autun. He worked at the highest levels of successive French governments, most commonly as foreign minister or in some other diplomatic capacity. His career spanned the regimes of Louis XVI, the years of the French Revolution, Napoleon, Louis XVIII, and Louis Philippe I. Those Talleyrand served often distrusted him but, like Napoleon, found him extremely useful. The name "Talleyrand" has become a byword for crafty and cynical diplomacy. 5 | He was Napoleon's chief diplomat during the years when French military victories brought one European state after another under French hegemony. However, most of the time, Talleyrand worked for peace so as to consolidate France's gains. He succeeded in obtaining peace with Austria through the 1801 Treaty of Lunéville and with Britain in the 1802 Treaty of Amiens. He could not prevent the renewal of war in 1803 but by 1805 he opposed his emperor's renewed wars against Austria, Prussia, and Russia. He resigned as foreign minister in August 1807, but retained the trust of Napoleon. He conspired to undermine the emperor's plans through secret dealings with Tsar Alexander I of Russia and the Austrian minister Klemens von Metternich. Talleyrand sought a negotiated secure peace so as to perpetuate the gains of the French Revolution. Napoleon rejected peace; when he fell in 1814, Talleyrand supported the Bourbon Restoration decided by the Allies. He played a major role at the Congress of Vienna in 1814–1815, where he negotiated a favorable settlement for France and played a role in unwinding the Napoleonic Wars. 6 | Talleyrand polarizes opinion. Some regard him as one of the most versatile, skilled and influential diplomats in European history, while some believe that he was a traitor, betraying in turn the ancien régime, the French Revolution, Napoleon, and the Bourbon Restoration.[2] 7 | 8 | Talleyrand was born in Paris into an aristocratic family which, though ancient and illustrious, was not particularly prosperous.[3] His father, Count Charles Daniel de Talleyrand-Périgord, was 20 years of age when Charles was born. His mother was Alexandrine de Damas d'Antigny. Both his parents held positions at court, but as the youngest children of their respective families, had no important income. Talleyrand's father had a long career in the French Royal Army, reaching the rank of lieutenant general, as did his uncle, Gabriel Marie de Périgord, despite having the same infirmity from which Talleyrand would suffer throughout his life. His father served all through the Seven Years' War.[4] 9 | From childhood, Talleyrand walked with a limp, which caused him to later be called le diable boiteux[5] (French for "the lame devil") among other nicknames. In his Memoirs, he linked this infirmity to an accident at age four, but recent research has shown that his limp was in fact congenital.[6] It might have been a congenital clubfoot.[7] In any case, his handicap made him unable to follow his father into a military career, leaving the obvious career of the Church.[7] 10 | The latter held out the hope for Charles-Maurice of succeeding his uncle, Alexandre Angélique de Talleyrand-Périgord, then Archbishop of Reims, one of the most prestigious and richest dioceses in France.[8] At eight years old,[9] Talleyrand attended the Collège d'Harcourt, the seminary of Saint-Sulpice,[10] while studying theology at the Sorbonne until the age of 21. In his free time, he read the works of Montesquieu, Voltaire, and other writers who were beginning to question the authority of the ancien régime in matters of church and state. As subdeacon he witnessed the coronation of Louis XVI at Reims in 1775.[9] 11 | He was not ordained a priest until four years later, on 19 December 1779, at the age of 25.[11] Very soon, in 1780, he attained the influential position of Agent-General of the Clergy,[12] and was instrumental in promoting the drawing up of a general inventory of Church properties in France as of 1785, along with a defense of "inalienable rights of the Church", the latter being a stance he later denied. In 1788, the influence of Talleyrand's father and family overcame the King's dislike and obtained his appointment as Bishop of Autun, with a stipend of 22,000 livres. He was consecrated a bishop on 4 January 1789 by Louis-André de Grimaldi.[11] The undoubtedly able Talleyrand, though hardly devout and even free-thinking in the Enlightenment mold, was outwardly respectful of religious observance. In the course of the Revolution, however, he was to manifest his cynicism and abandon all orthodox Catholic practice. He resigned his bishopric on 13 April 1791.[11] On 29 June 1802, Pope Pius VII laicized Talleyrand, an event most uncommon at the time in the history of the Church.[11][13] 12 | 13 | 14 | Shortly after he was consecrated as Bishop of Autun, Talleyrand attended the Estates-General of 1789, representing the clergy, the First Estate.[14] During the French Revolution, Talleyrand strongly supported the anti-clericalism of the revolutionaries. Along with Mirabeau, he promoted the appropriation of Church properties.[15] He participated in the writing of the Declaration of the Rights of Man and of the Citizen and proposed the Civil Constitution of the Clergy that nationalized the Church in preference to allegiance to the Pope, and swore in the first four constitutional bishops, even though he had himself resigned as Bishop following his excommunication by Pope Pius VI in 1791. During the Fête de la Fédération on 14 July 1790, Talleyrand celebrated Mass. Notably, he promoted public education in full spirit of the Enlightenment by preparing a 216-page Report on Public Instruction. It proposed pyramidical structure rising through local, district, and departmental schools, and parts were later adopted.[16] During his 5-month tenure in the Estates-General, Talleyrand was also involved in drawing up the police regulations of Paris, proposed the suffrage of Jews, supported a ban on the tithes, and invented a method to ensure loans.[17] Few bishops followed him in obedience to the new decree, and much of the French clergy came to view him as schismatic.[18] 15 | Just before his resignation from the bishopric, Talleyrand had been elected, with Mirabeau and the Abbé Sieyès, a member of the department of Paris. In that capacity he did useful work for some eighteen months in seeking to support the cause of order in the turbulent capital. Though he was often on strained terms with Mirabeau, his views generally coincided with those of that statesman, who before he died is said to have advised Talleyrand to develop a close understanding with England.[18] 16 | In 1792, he was sent twice, unofficially, to London to avert war, and he was cordially received by Pitt and Grenville. After his first visit, he persuaded the then foreign minister, Charles François Dumouriez, of the importance of having a fully accredited ambassador in London, and the marquis de Chauvelin was duly appointed, with Talleyrand as his deputy.[19][18] Still, after an initial British declaration of neutrality during the first campaigns of 1792, his mission ultimately failed. In September 1792, he left Paris for England having acquired a passport from Danton personally[20] just at the beginning of the September massacres. The National Convention issued a warrant for his arrest in December 1792. In March 1794, with the two countries at the brink of war, he was forced to leave Britain by Pitt's expulsion order. He then went to the neutral country of the United States. 17 | The ship he took to the US was forced by rough weather in the Channel to stop at Falmouth where Talleyrand recounts[21] an awkward chance meeting with Benedict Arnold at an inn. 18 | During Talleyrand's stay in the US, he supported himself by working as a bank agent, involved in commodity trading and real estate speculation. He was a house guest of Aaron Burr of New York and collaborated with Theophile Cazenove in Philadelphia.[22] On 19 May 1794 Matthew Clarkson, the mayor of Philadelphia, received his oath. Talleyrand swore "that I will be faithful and bear true allegiance to...the United States of America... "[23] Burr later sought similar refuge in Talleyrand's home during his self-imposed European exile (1808–12). However, Talleyrand would refuse to return the favor because Burr had killed Talleyrand's friend Alexander Hamilton in an 1804 duel.[24] Talleyrand returned to France in 1796. 19 | After 9 Thermidor, he mobilized his friends (most notably the abbé Martial Borye Desrenaudes and Germaine de Staël) to lobby in the National Convention and the newly established Directoire for his return. His name was suppressed from the émigré list and he returned to France on 25 September 1796. After gaining attention by giving addresses on the value of commercial relations with England, and of colonization as a way of renewing the nation, he became Foreign Minister in July 1797.[25] He was behind the demand for bribes in the XYZ Affair which escalated into the Quasi-War, an undeclared naval war with the United States, 1798–1800. Talleyrand saw a possible political career for Napoleon during the Italian campaigns of 1796 to 1797. He wrote many letters to Napoleon, and the two became close allies. Talleyrand was against the destruction of the Republic of Venice, but he complimented Napoleon when the Treaty of Campo Formio with Austria was concluded (Venice was given to Austria), probably because he wanted to reinforce his alliance with Napoleon. Later in 1797, Talleyrand was instrumental in assisting with the Coup of 18 Fructidor, which ousted two moderate members of the Directory in favor of the Jacobins headed by Paul Barras.[25] 20 | 21 | Talleyrand had a reputation for Promiscuity and as a voluptuary. He left no legitimate children, though he possibly fathered over two dozen illegitimate ones. Four possible children of his have been identified: Charles Joseph, comte de Flahaut, generally accepted to be an illegitimate son of Talleyrand;[42] the painter Eugène Delacroix, once rumoured to be Talleyrand's son, though this is doubted by historians who have examined the issue (for example, Léon Noël, French ambassador); the "Mysterious Charlotte", possibly his daughter by his future wife, Catherine Worlée Grand; and Pauline, ostensibly the daughter of the Duke and Duchess Dino. Of these four, only the first is given credence by historians. However, the French historian Emmanuel de Waresquiel has lately given much credibility to father-daughter link between Talleyrand and Pauline whom he referred to as "my dear Minette". Thaddeus Stevens "suffered too from the rumor that he was actually the bastard son of Count Talleyrand, who was said to have visited New England in the year before Stevens' birth.... Actually Talleyrand did not visit New England till 1794, when Stevens was already two years old."[43] 22 | Aristocratic women were a key component of Talleyrand's political tactics, both for their influence and their ability to cross borders unhindered. His presumed lover Germaine de Staël was a major influence on him, and he on her. Though their personal philosophies were most different (she a romantic, he very much unsentimental), she assisted him greatly, most notably by lobbying Barras to permit Talleyrand to return to France from his American exile, and then to have him made foreign minister. He lived with Catherine Worlée, born in India and married there to Charles Grand. She had traveled about before settling in Paris in the 1780s, where she lived as a notorious courtesan for several years before divorcing Grand to marry Talleyrand. Talleyrand was in no hurry to marry, and it was after repeated postponements that Napoleon obliged him in 1802 to formalize the relationship or risk his political career. While serving as a high level negotiator at the Congress of Vienna (1814–1815), Talleyrand entered into an arrangement with Dorothea von Biron, the wife of his nephew, the Duke of Dino. Shortly after, he separated from Catherine.[44] 23 | Talleyrand's venality was notorious; in the tradition of the ancien régime, he expected to be paid for the state duties he performed—whether these can properly be called "bribes" is open to debate. For example, during the German mediatization, the consolidation of the small German states, a number of German rulers and elites paid him to save their possessions or enlarge their territories. Less successfully, he solicited payments from the United States government to open negotiations, precipitating a diplomatic disaster (the "XYZ Affair"). The difference between his diplomatic success in Europe and failure with the United States illustrates that his diplomacy rested firmly on the power of the French army that was a terrible threat to the German states within reach, but lacked the logistics to threaten the US not the least because of the British naval domination of the seas. After Napoleon's defeat, he withdrew claims to the title "Prince of Benevento", but was created Duke of Talleyrand with the style "Prince de Talleyrand" for life, in the same manner as his estranged wife.[45] 24 | Described by biographer Philip Ziegler as a "pattern of subtlety and finesse" and a "creature of grandeur and guile",[46] Talleyrand was a great conversationalist, gourmet, and wine connoisseur. He was a frequent visitor at the salon hosted by Adèle de Bellegarde and her sister Aurore, with whom he dined regularly over a period of five years.[47] From 1801 to 1804, he owned Château Haut-Brion in Bordeaux. In 1803, Napoleon ordered Talleyrand to acquire the Château de Valençay as a place particularly appropriate for reception of foreign dignitaries,[48] and Talleyrand made it his primary place of residence until his death in 1838. There he employed the renowned French chef Marie-Antoine Carême, one of the first celebrity chefs known as the "chef of kings and king of chefs", and was said to have spent an hour every day with him.[49] His Paris residence on the Place de la Concorde, acquired in 1812 and sold to James Mayer de Rothschild in 1838, is now owned by the Embassy of the United States. 25 | Talleyrand has been regarded as a traitor because of his support for successive regimes, some of which were mutually hostile. According to French philosopher Simone Weil, criticism of his loyalty is unfounded, as Talleyrand served not every regime as had been said, but in reality "France behind every regime".[50] 26 | Near the end of his life, Talleyrand became interested in Catholicism again while teaching his young granddaughter simple prayers. The Abbé Félix Dupanloup came to Talleyrand in his last hours, and according to his account Talleyrand made confession and received extreme unction. When the abbé tried to anoint Talleyrand's palms, as prescribed by the rite, he turned his hands over to make the priest anoint him on the back of the hands, since he was a bishop. He also signed, in the abbé's presence, a solemn declaration in which he openly disavowed "the great errors which … had troubled and afflicted the Catholic, Apostolic and Roman Church, and in which he himself had had the misfortune to fall."[51] He died on 17 May 1838 and was buried in the Notre-Dame Chapel,[52] near his Château de Valençay. 27 | Today, when speaking of the art of diplomacy, the phrase "they are a Talleyrand" is variously used to describe a statesman of great resourcefulness and craft, or a cynical and conscienceless self-serving politician.[53] 28 | 29 | 30 | Main-Section: Career 31 | 32 | Talleyrand, along with Napoleon's younger brother, Lucien Bonaparte, was instrumental in the 1799 coup d'état of 18 Brumaire, establishing the French Consulate government, although he also made preparations for flight if necessary. He also persuaded Barras to resign as Director.[25] Talleyrand was soon made Foreign Minister by Napoleon, although he rarely agreed with Napoleon's foreign policy. Domestically, Talleyrand used his influence to help in the repeal of the strict laws against émigrés, refractory clergy, and the royalists of the west.[25] The Pope released him from the ban of excommunication in the Concordat of 1801, which also revoked the Civil Constitution of the Clergy. Talleyrand was instrumental in the completion of the Treaty of Amiens in 1802. He wanted Napoleon to keep peace afterwards, as he thought France had reached its maximum expansion. 33 | Talleyrand was an integral player in the German mediatization. While the Treaty of Campo Formio of 1797 had, on paper, stripped German princes of their lands beyond the left bank of the Rhine, it was not enforced until the Treaty of Lunéville in 1801. As the French annexed these lands, leaders believed that rulers of states such as Baden, Bavaria, Württemberg, Prussia, Hesse and Nassau, who lost territories on the Left Bank, should receive new territories on the Right Bank through the secularization of ecclesiastical principalities. Many of these rulers gave out bribes in order to secure new lands, and Talleyrand and some of his associates amassed about 10 million francs in the process. This was the first blow in the destruction of the Holy Roman Empire.[26] 34 | While helping to establish French supremacy in neighboring states and assisting Bonaparte in securing the title of First Consul for life, Talleyrand sought all means of securing the permanent welfare of France. He worked hard to prevent the rupture of the peace of Amiens which occurred in May 1803, and he did what he could to prevent the Louisiana Purchase earlier in the year. These events, as he saw, told against the best interests of France and endangered the gains which she had secured by war and diplomacy. Thereafter he strove to moderate Napoleon's ambition and to preserve the European system as far as possible.[25] 35 | Napoleon forced Talleyrand into marriage in September 1802 to longtime mistress Catherine Grand (née Worlée). Talleyrand purchased the Château de Valençay in May 1803, upon the urging of Napoleon. This later was used as the site of imprisonment of the Spanish royal family in 1808–1813, after Napoleon's invasion of Spain. 36 | In May 1804, Napoleon bestowed upon Talleyrand the title of Grand Chamberlain of the Empire, with almost 500,000 francs a year.[25] In 1806, he was made Sovereign Prince of Benevento (or Bénévent), a former Papal fief in southern Italy. Talleyrand held the title until 1815 and administered the principality concurrently with his other tasks.[27] 37 | Talleyrand was opposed to the harsh treatment of Austria in the 1805 Treaty of Pressburg and of Prussia in the Peace of Tilsit in 1807. In 1806, after Pressburg, he profited greatly from the reorganization of the German lands, this time into the Confederation of the Rhine. He negotiated the Treaty of Posen with Saxony, but was shut out completely from the negotiations at Tilsit. After Queen Louise of Prussia failed in her appeal to Napoleon to spare her nation, she wept and was consoled by Talleyrand. This gave him a good name among the elites of European nations outside France. 38 | 39 | 40 | Having wearied of serving a master in whom he no longer had much confidence, Talleyrand resigned as minister of foreign affairs in 1807, although the Emperor retained him in the Council of State as Vice-Grand Elector of the Empire.[28] He disapproved of Napoleon's Spanish initiative, which resulted in the Peninsular War beginning in 1808. At the Congress of Erfurt in September–October 1808, Talleyrand secretly counseled Tsar Alexander. The Tsar's attitude towards Napoleon was one of apprehensive opposition. Talleyrand repaired the confidence of the Russian monarch, who rebuked Napoleon's attempts to form a direct anti-Austrian military alliance. Napoleon had expected Talleyrand to help convince the Tsar to accept his proposals and never discovered that Talleyrand was working at cross-purposes. Talleyrand believed Napoleon would eventually destroy the empire he had worked to build across multiple rulers.[29] 41 | After his resignation in 1807 from the ministry, Talleyrand began to accept bribes from hostile powers (mainly Austria, but also Russia), to betray Napoleon's secrets.[30] Talleyrand and Joseph Fouché, who were typically enemies in both politics and the salons, had a rapprochement in late 1808 and entered into discussions over the imperial line of succession. Napoleon had yet to address this matter and the two men knew that without a legitimate heir a struggle for power would erupt in the wake of Napoleon's death. Even Talleyrand, who believed that Napoleon's policies were leading France to ruin, understood the necessity of peaceful transitions of power. Napoleon received word of their actions and deemed them treasonous. This perception caused the famous dressing down of Talleyrand in front of Napoleon's marshals, during which Napoleon famously claimed that he could "break him like a glass, but it's not worth the trouble" and added with a scatological tone that Talleyrand was "shit in a silk stocking",[31] to which the minister coldly retorted, once Napoleon had left, "Pity that so great a man should have been so badly brought up!" 42 | Talleyrand opposed the further harsh treatment of Austria in 1809 after the War of the Fifth Coalition. He was also a critic of the French invasion of Russia in 1812. He was invited to resume his former office in late 1813, but Talleyrand could see that power was slipping from Napoleon's hands. He offered to resign from the council in early 1814, but Napoleon refused the move. Talleyrand then hosted the tsar at the end of March after the fall of Paris, persuaded him that the best chance of stability lay with the House of Bourbon, and gained his support.[32] On 1 April 1814 he led the Sénat conservateur in establishing a provisional government in Paris, of which he was elected president. On 2 April the Senate officially deposed Napoleon with the Acte de déchéance de l'Empereur; by 11 April it had approved the Treaty of Fontainebleau and adopted a new constitution to re-establish the Bourbon monarchy. 43 | 44 | 45 | When Napoleon was succeeded by Louis XVIII in April 1814, Talleyrand was one of the key agents of the restoration of the House of Bourbon, although he opposed the new legislation of Louis's rule. Talleyrand was the chief French negotiator at the Congress of Vienna; earlier that same year he signed the Treaty of Paris. It was due in part to his skills that the terms of the treaty were remarkably lenient towards France. As the Congress opened, the right to make decisions was restricted to four countries: Austria, the United Kingdom, Prussia and Russia. France and other European countries were invited to attend, but were not allowed to influence the process. Talleyrand promptly became the champion of the small countries and demanded admission into the ranks of the decision-making process. The four powers admitted France and Spain to the decision-making backrooms of the conference after a good deal of diplomatic maneuvering by Talleyrand, who had the support of the Spanish representative, Pedro Gómez Labrador, Marquis of Labrador. Spain was excluded after a while (a result of both the Marquis of Labrador's incompetence as well as the quixotic nature of Spain's agenda), but France (Talleyrand) was allowed to participate until the end. Russia and Prussia sought to enlarge their territory at the Congress. Russia demanded annexation of Poland (already occupied by Russian troops); this demand was finally satisfied, despite protests by France, Austria and the United Kingdom. Austria was afraid of future conflicts with Russia or Prussia and the United Kingdom was opposed to their expansion as well—and Talleyrand managed to take advantage of these contradictions within the former anti-French coalition.[citation needed] On 3 January 1815, a secret treaty was signed by France's Talleyrand, Austria's Metternich and Britain's Castlereagh. By this tract, officially a secret treaty of defensive alliance,[33] the three powers agreed to use force if necessary to "repulse aggression" (of Russia and Prussia) and to protect the "state of security and independence." 46 | Talleyrand, having managed to establish a middle position, received some favors from the other countries in exchange for his support: France returned to its 1792 boundaries without reparations, with French control over the papal Comtat Venaissin, County of Montbéliard, and Salm, which had been independent at the start of the French Revolution in 1789. It would later be debated which outcome would have been better for France: allowing Prussia to annex all of Saxony (Talleyrand ensured that only part of the kingdom would be annexed) or the Rhine provinces. The first option would have kept Prussia farther away from France, but would have needed much more opposition as well. Some historians have argued that Talleyrand's diplomacy wound up establishing the fault lines of World War I, especially as it allowed Prussia to engulf small German states west of the Rhine. This simultaneously placed the Prussian Army at the French-German frontier, for the first time; made Prussia the largest German power in terms of territory, population and the industry of the Ruhr and Rhineland; and eventually helped pave the way to German unification under the Prussian throne. However, at the time Talleyrand's diplomacy was regarded as successful, as it removed the threat of France being partitioned by the victors. Talleyrand also managed to strengthen his own position in France (ultraroyalists had disapproved of the presence of a former "revolutionary" and "murderer of the Duke d'Enghien" in the royal cabinet). 47 | Napoleon's return to France in 1815 and his subsequent defeat, the Hundred Days, was a reverse for the diplomatic victories of Talleyrand (who remained in Vienna the whole time). The second peace settlement was markedly less lenient and it was fortunate for France that the business of the Congress had been concluded. Having been appointed foreign minister and president of the council on 9 July 1815, Talleyrand resigned in September of that year, over his objections to the second treaty. Louis XVIII appointed him as the Grand Chamberlain of France, a mostly ceremonial role which provided Talleyrand with a steady income. For the next fifteen years he restricted himself to the role of "elder statesman", criticizing and intriguing against Minister of Police Élie, duc Decazes, Prime Minister Duc de Richelieu and other political opponents from the sidelines. In celebration of the birth of the Duc de Bordeaux, Louis XVIII made Talleyrand a knight of the Order of the Holy Spirit.[34] 48 | In December 1829, Talleyrand funded the foundation of the National newspaper. The newspaper was run by his personal friend Adolphe Thiers, alongside Armand Carrel, François Mignet and Stendhal. Its first issue appeared on 3 January 1830, quickly becoming the mouthpiece of the Orléanist cause and gaining popularity among the French liberal bourgeoisie.[35] Following the ascent of Louis-Philippe I to the throne in the aftermath of the July Revolution of 1830, Talleyrand reluctantly agreed to become ambassador to the United Kingdom,[36] a post he held from 1830 to 1834. In this role, he strove to reinforce the legitimacy of Louis-Philippe's regime. He played a vital role in the London Conference of 1830, rebuking a partition plan developed by his son Charles de Flahaut and helping bring Leopold of Saxe-Coburg to the throne of the newly independent Kingdom of Belgium.[37] In April 1834 he crowned his diplomatic career by signing the treaty which brought together as allies France, Great Britain, Spain and Portugal.[32] 49 | After resigning from his position as ambassador in London in November 1834, Talleyrand stopped playing an active role in French politics. He split his time between Château de Valençay and Saint-Florentin, where he hosted frequent banquets and played whist with his visitors. His physical health began to steadily deteriorate and he began using an armchair on wheels provided to him by Louis Philippe I. He spent most of his time in the company of the Duchess Dino and concerned himself with the education of her daughter Pauline.[38] Talleyrand suffered from bouts of recurring depression which were caused by his concern over his legacy and the development of the Napoleonic myth. To that end he ordered that his autobiography, the Memoirs, be published 30 years after his death. He also sought to gain the friendship of people he believed would shape public opinion in the future, including Honoré de Balzac, Lady Granville and Alphonse de Lamartine.[39] During the last years of his life Talleyrand began planning his reconciliation with the Catholic Church. On 16 May 1838, he signed a retraction of his errors towards the church and a letter of submission to Pope Gregory XVI. He died the following day at 3:55 p.m., at Saint-Florentin.[40] 50 | By a codicil added to his will on 17 March 1838, Talleyrand left his memoirs and papers to the duchess of Dino and Adolphe de Bacourt. The latter revised them with care, and added to them other pieces emanating from Talleyrand. They fell into some question: first that Talleyrand is known to have destroyed many of his most important papers, and secondly that de Bacourt almost certainly drew up the connected narrative which we now possess from notes which were in more or less of confusion. The mémoires were later edited by the duc de Broglie and published in 1891.[41] 51 | 52 | 53 | Charles Maurice de Talleyrand-Périgord’s influence on the events leading up to the Battle of Waterloo was significant, though his role was more indirect compared to military leaders. As a prominent diplomat and statesman, Talleyrand was crucial in shaping the political and diplomatic landscape of early 19th-century Europe, particularly during the turbulent Napoleonic era. Initially, he served as Napoleon Bonaparte’s foreign minister, where his strategic acumen and diplomatic skills were instrumental in shaping France’s foreign policy and navigating the complex international alliances and rivalries of the time. 54 | 55 | Talleyrand’s political maneuvering was a cornerstone in the formation and sustainability of the Seventh Coalition, an alliance of major European powers united against Napoleon. His diplomatic efforts were essential in forging and maintaining this coalition, which included the United Kingdom, Russia, Austria, and Prussia. The cohesion and effectiveness of this alliance were pivotal in countering Napoleon’s ambitions and ultimately in the outcome of the Battle of Waterloo. 56 | 57 | The Congress of Vienna, held in 1814-1815, was another arena where Talleyrand’s influence was profound. Despite France’s defeat and Napoleon’s abdication, Talleyrand played a critical role in the negotiations, leveraging his expertise to secure relatively favorable terms for France. His diplomatic skills helped to mitigate the harshness of the proposed settlements and ensured that France retained a degree of influence in the new European order. This not only affected the immediate post-war arrangements but also set the stage for the balance of power that characterized European politics in the subsequent decades. 58 | 59 | In the context of the Battle of Waterloo itself, while Talleyrand did not engage in military strategy or tactics, his impact was felt through the broader diplomatic and political strategies that shaped the conflict. The cohesive and well-coordinated efforts of the Seventh Coalition, made possible in part by Talleyrand’s diplomatic endeavors, were crucial in countering Napoleon’s military campaigns. The unity of the coalition forces was a direct result of the successful diplomatic groundwork laid by Talleyrand and his counterparts. 60 | 61 | Additionally, Talleyrand’s influence extended to the post-Waterloo period. His role in the subsequent peace negotiations and the establishment of a new European order was instrumental in ensuring stability and preventing further widespread conflict. The relative peace that followed the Napoleonic Wars, known as the Concert of Europe, was partly a product of Talleyrand’s diplomatic work, which helped to stabilize Europe and prevent the resurgence of large-scale conflicts for several decades. 62 | 63 | In summary, while Talleyrand was not directly involved in the Battle of Waterloo, his diplomatic skills and strategic positioning were crucial in shaping the conditions leading up to the battle and its aftermath. His efforts in forging the Seventh Coalition, negotiating at the Congress of Vienna, and contributing to the post-war settlement were integral to the outcome of the conflict and the stability of Europe in the early 19th century. 64 | 65 | Main-Section: Death 66 | 67 | Charles Maurice de Talleyrand-Périgord, a prominent French diplomat, died on May 17, 1838, in Paris, at the age of 84. His death marked the end of a significant era in European diplomacy, as he had been a key political figure through the French Revolution, the Napoleonic era, and the Bourbon Restoration. In his later years, Talleyrand suffered from declining health, afflicted by gout and other ailments that left him increasingly frail. Before his death, he sought reconciliation with the Catholic Church, receiving the last rites from the Archbishop of Paris, thus signaling his return to the Church after many years of estrangement. Talleyrand's death was widely noted across Europe due to his immense influence on European politics and diplomacy. He is remembered as a master diplomat who navigated the turbulent political landscape of his time with skill and pragmatism. Born into an aristocratic family on February 2, 1754, Talleyrand initially pursued a clerical career, becoming a bishop. He played a significant role during the French Revolution, advocating for the confiscation of Church property and supporting the revolutionary government. Throughout his diplomatic career, he served under various regimes, including those of Napoleon Bonaparte and King Louis XVIII, showcasing his ability to adapt to changing political landscapes. He was instrumental in negotiating key treaties, including the Treaty of Vienna (1815), which reshaped Europe after the Napoleonic Wars. Known for his pragmatism and sometimes criticized for his opportunism, Talleyrand's actions were often driven by a desire to maintain stability and balance of power in Europe. His life and career were characterized by his extraordinary ability to survive and thrive through one of the most tumultuous periods in European history, and his death marked the end of a remarkable journey of political maneuvering and influence. -------------------------------------------------------------------------------- /data/json/Talleyrand.json: -------------------------------------------------------------------------------- 1 | { 2 | "General Information": "Charles-Maurice de Talleyrand-Périgord (/ˌtælɪrænd ˈpɛrɪɡɔːr/, French: [ʃaʁl mɔʁis də tal(ɛ)ʁɑ̃ peʁiɡɔʁ, – moʁ-]; 2 February 1754 – 17 May 1838), 1st Prince of Benevento, then Prince of Talleyrand, was a French secularized clergyman, statesman, and leading diplomat. After studying theology, he became Agent-General of the Clergy in 1780. In 1789, just before the French Revolution, he became Bishop of Autun. He worked at the highest levels of successive French governments, most commonly as foreign minister or in some other diplomatic capacity. His career spanned the regimes of Louis XVI, the years of the French Revolution, Napoleon, Louis XVIII, and Louis Philippe I. Those Talleyrand served often distrusted him but, like Napoleon, found him extremely useful. The name \"Talleyrand\" has become a byword for crafty and cynical diplomacy.\nHe was Napoleon's chief diplomat during the years when French military victories brought one European state after another under French hegemony. However, most of the time, Talleyrand worked for peace so as to consolidate France's gains. He succeeded in obtaining peace with Austria through the 1801 Treaty of Lunéville and with Britain in the 1802 Treaty of Amiens. He could not prevent the renewal of war in 1803 but by 1805 he opposed his emperor's renewed wars against Austria, Prussia, and Russia. He resigned as foreign minister in August 1807, but retained the trust of Napoleon. He conspired to undermine the emperor's plans through secret dealings with Tsar Alexander I of Russia and the Austrian minister Klemens von Metternich. Talleyrand sought a negotiated secure peace so as to perpetuate the gains of the French Revolution. Napoleon rejected peace; when he fell in 1814, Talleyrand supported the Bourbon Restoration decided by the Allies. He played a major role at the Congress of Vienna in 1814–1815, where he negotiated a favorable settlement for France and played a role in unwinding the Napoleonic Wars.\nTalleyrand polarizes opinion. Some regard him as one of the most versatile, skilled and influential diplomats in European history, while some believe that he was a traitor, betraying in turn the ancien régime, the French Revolution, Napoleon, and the Bourbon Restoration.\n\nTalleyrand was born in Paris into an aristocratic family which, though ancient and illustrious, was not particularly prosperous. His father, Count Charles Daniel de Talleyrand-Périgord, was 20 years of age when Charles was born. His mother was Alexandrine de Damas d'Antigny. Both his parents held positions at court, but as the youngest children of their respective families, had no important income. Talleyrand's father had a long career in the French Royal Army, reaching the rank of lieutenant general, as did his uncle, Gabriel Marie de Périgord, despite having the same infirmity from which Talleyrand would suffer throughout his life. His father served all through the Seven Years' War.\nFrom childhood, Talleyrand walked with a limp, which caused him to later be called le diable boiteux (French for \"the lame devil\") among other nicknames. In his Memoirs, he linked this infirmity to an accident at age four, but recent research has shown that his limp was in fact congenital. It might have been a congenital clubfoot. In any case, his handicap made him unable to follow his father into a military career, leaving the obvious career of the Church.\nThe latter held out the hope for Charles-Maurice of succeeding his uncle, Alexandre Angélique de Talleyrand-Périgord, then Archbishop of Reims, one of the most prestigious and richest dioceses in France. At eight years old, Talleyrand attended the Collège d'Harcourt, the seminary of Saint-Sulpice, while studying theology at the Sorbonne until the age of 21. In his free time, he read the works of Montesquieu, Voltaire, and other writers who were beginning to question the authority of the ancien régime in matters of church and state. As subdeacon he witnessed the coronation of Louis XVI at Reims in 1775.\nHe was not ordained a priest until four years later, on 19 December 1779, at the age of 25. Very soon, in 1780, he attained the influential position of Agent-General of the Clergy, and was instrumental in promoting the drawing up of a general inventory of Church properties in France as of 1785, along with a defense of \"inalienable rights of the Church\", the latter being a stance he later denied. In 1788, the influence of Talleyrand's father and family overcame the King's dislike and obtained his appointment as Bishop of Autun, with a stipend of 22,000 livres. He was consecrated a bishop on 4 January 1789 by Louis-André de Grimaldi. The undoubtedly able Talleyrand, though hardly devout and even free-thinking in the Enlightenment mold, was outwardly respectful of religious observance. In the course of the Revolution, however, he was to manifest his cynicism and abandon all orthodox Catholic practice. He resigned his bishopric on 13 April 1791. On 29 June 1802, Pope Pius VII laicized Talleyrand, an event most uncommon at the time in the history of the Church.\n\n\nShortly after he was consecrated as Bishop of Autun, Talleyrand attended the Estates-General of 1789, representing the clergy, the First Estate. During the French Revolution, Talleyrand strongly supported the anti-clericalism of the revolutionaries. Along with Mirabeau, he promoted the appropriation of Church properties. He participated in the writing of the Declaration of the Rights of Man and of the Citizen and proposed the Civil Constitution of the Clergy that nationalized the Church in preference to allegiance to the Pope, and swore in the first four constitutional bishops, even though he had himself resigned as Bishop following his excommunication by Pope Pius VI in 1791. During the Fête de la Fédération on 14 July 1790, Talleyrand celebrated Mass. Notably, he promoted public education in full spirit of the Enlightenment by preparing a 216-page Report on Public Instruction. It proposed pyramidical structure rising through local, district, and departmental schools, and parts were later adopted. During his 5-month tenure in the Estates-General, Talleyrand was also involved in drawing up the police regulations of Paris, proposed the suffrage of Jews, supported a ban on the tithes, and invented a method to ensure loans. Few bishops followed him in obedience to the new decree, and much of the French clergy came to view him as schismatic.\nJust before his resignation from the bishopric, Talleyrand had been elected, with Mirabeau and the Abbé Sieyès, a member of the department of Paris. In that capacity he did useful work for some eighteen months in seeking to support the cause of order in the turbulent capital. Though he was often on strained terms with Mirabeau, his views generally coincided with those of that statesman, who before he died is said to have advised Talleyrand to develop a close understanding with England.\nIn 1792, he was sent twice, unofficially, to London to avert war, and he was cordially received by Pitt and Grenville. After his first visit, he persuaded the then foreign minister, Charles François Dumouriez, of the importance of having a fully accredited ambassador in London, and the marquis de Chauvelin was duly appointed, with Talleyrand as his deputy. Still, after an initial British declaration of neutrality during the first campaigns of 1792, his mission ultimately failed. In September 1792, he left Paris for England having acquired a passport from Danton personally just at the beginning of the September massacres. The National Convention issued a warrant for his arrest in December 1792. In March 1794, with the two countries at the brink of war, he was forced to leave Britain by Pitt's expulsion order. He then went to the neutral country of the United States.\nThe ship he took to the US was forced by rough weather in the Channel to stop at Falmouth where Talleyrand recounts an awkward chance meeting with Benedict Arnold at an inn.\nDuring Talleyrand's stay in the US, he supported himself by working as a bank agent, involved in commodity trading and real estate speculation. He was a house guest of Aaron Burr of New York and collaborated with Theophile Cazenove in Philadelphia. On 19 May 1794 Matthew Clarkson, the mayor of Philadelphia, received his oath. Talleyrand swore \"that I will be faithful and bear true allegiance to...the United States of America... \" Burr later sought similar refuge in Talleyrand's home during his self-imposed European exile (1808–12). However, Talleyrand would refuse to return the favor because Burr had killed Talleyrand's friend Alexander Hamilton in an 1804 duel. Talleyrand returned to France in 1796.\nAfter 9 Thermidor, he mobilized his friends (most notably the abbé Martial Borye Desrenaudes and Germaine de Staël) to lobby in the National Convention and the newly established Directoire for his return. His name was suppressed from the émigré list and he returned to France on 25 September 1796. After gaining attention by giving addresses on the value of commercial relations with England, and of colonization as a way of renewing the nation, he became Foreign Minister in July 1797. He was behind the demand for bribes in the XYZ Affair which escalated into the Quasi-War, an undeclared naval war with the United States, 1798–1800. Talleyrand saw a possible political career for Napoleon during the Italian campaigns of 1796 to 1797. He wrote many letters to Napoleon, and the two became close allies. Talleyrand was against the destruction of the Republic of Venice, but he complimented Napoleon when the Treaty of Campo Formio with Austria was concluded (Venice was given to Austria), probably because he wanted to reinforce his alliance with Napoleon. Later in 1797, Talleyrand was instrumental in assisting with the Coup of 18 Fructidor, which ousted two moderate members of the Directory in favor of the Jacobins headed by Paul Barras.\n\nTalleyrand had a reputation for Promiscuity and as a voluptuary. He left no legitimate children, though he possibly fathered over two dozen illegitimate ones. Four possible children of his have been identified: Charles Joseph, comte de Flahaut, generally accepted to be an illegitimate son of Talleyrand; the painter Eugène Delacroix, once rumoured to be Talleyrand's son, though this is doubted by historians who have examined the issue (for example, Léon Noël, French ambassador); the \"Mysterious Charlotte\", possibly his daughter by his future wife, Catherine Worlée Grand; and Pauline, ostensibly the daughter of the Duke and Duchess Dino. Of these four, only the first is given credence by historians. However, the French historian Emmanuel de Waresquiel has lately given much credibility to father-daughter link between Talleyrand and Pauline whom he referred to as \"my dear Minette\". Thaddeus Stevens \"suffered too from the rumor that he was actually the bastard son of Count Talleyrand, who was said to have visited New England in the year before Stevens' birth.... Actually Talleyrand did not visit New England till 1794, when Stevens was already two years old.\"\nAristocratic women were a key component of Talleyrand's political tactics, both for their influence and their ability to cross borders unhindered. His presumed lover Germaine de Staël was a major influence on him, and he on her. Though their personal philosophies were most different (she a romantic, he very much unsentimental), she assisted him greatly, most notably by lobbying Barras to permit Talleyrand to return to France from his American exile, and then to have him made foreign minister. He lived with Catherine Worlée, born in India and married there to Charles Grand. She had traveled about before settling in Paris in the 1780s, where she lived as a notorious courtesan for several years before divorcing Grand to marry Talleyrand. Talleyrand was in no hurry to marry, and it was after repeated postponements that Napoleon obliged him in 1802 to formalize the relationship or risk his political career. While serving as a high level negotiator at the Congress of Vienna (1814–1815), Talleyrand entered into an arrangement with Dorothea von Biron, the wife of his nephew, the Duke of Dino. Shortly after, he separated from Catherine.\nTalleyrand's venality was notorious; in the tradition of the ancien régime, he expected to be paid for the state duties he performed—whether these can properly be called \"bribes\" is open to debate. For example, during the German mediatization, the consolidation of the small German states, a number of German rulers and elites paid him to save their possessions or enlarge their territories. Less successfully, he solicited payments from the United States government to open negotiations, precipitating a diplomatic disaster (the \"XYZ Affair\"). The difference between his diplomatic success in Europe and failure with the United States illustrates that his diplomacy rested firmly on the power of the French army that was a terrible threat to the German states within reach, but lacked the logistics to threaten the US not the least because of the British naval domination of the seas. After Napoleon's defeat, he withdrew claims to the title \"Prince of Benevento\", but was created Duke of Talleyrand with the style \"Prince de Talleyrand\" for life, in the same manner as his estranged wife.\nDescribed by biographer Philip Ziegler as a \"pattern of subtlety and finesse\" and a \"creature of grandeur and guile\", Talleyrand was a great conversationalist, gourmet, and wine connoisseur. He was a frequent visitor at the salon hosted by Adèle de Bellegarde and her sister Aurore, with whom he dined regularly over a period of five years. From 1801 to 1804, he owned Château Haut-Brion in Bordeaux. In 1803, Napoleon ordered Talleyrand to acquire the Château de Valençay as a place particularly appropriate for reception of foreign dignitaries, and Talleyrand made it his primary place of residence until his death in 1838. There he employed the renowned French chef Marie-Antoine Carême, one of the first celebrity chefs known as the \"chef of kings and king of chefs\", and was said to have spent an hour every day with him. His Paris residence on the Place de la Concorde, acquired in 1812 and sold to James Mayer de Rothschild in 1838, is now owned by the Embassy of the United States.\nTalleyrand has been regarded as a traitor because of his support for successive regimes, some of which were mutually hostile. According to French philosopher Simone Weil, criticism of his loyalty is unfounded, as Talleyrand served not every regime as had been said, but in reality \"France behind every regime\".\nNear the end of his life, Talleyrand became interested in Catholicism again while teaching his young granddaughter simple prayers. The Abbé Félix Dupanloup came to Talleyrand in his last hours, and according to his account Talleyrand made confession and received extreme unction. When the abbé tried to anoint Talleyrand's palms, as prescribed by the rite, he turned his hands over to make the priest anoint him on the back of the hands, since he was a bishop. He also signed, in the abbé's presence, a solemn declaration in which he openly disavowed \"the great errors which … had troubled and afflicted the Catholic, Apostolic and Roman Church, and in which he himself had had the misfortune to fall.\" He died on 17 May 1838 and was buried in the Notre-Dame Chapel, near his Château de Valençay.\nToday, when speaking of the art of diplomacy, the phrase \"they are a Talleyrand\" is variously used to describe a statesman of great resourcefulness and craft, or a cynical and conscienceless self-serving politician.", 3 | "Career": "Talleyrand, along with Napoleon's younger brother, Lucien Bonaparte, was instrumental in the 1799 coup d'état of 18 Brumaire, establishing the French Consulate government, although he also made preparations for flight if necessary. He also persuaded Barras to resign as Director. Talleyrand was soon made Foreign Minister by Napoleon, although he rarely agreed with Napoleon's foreign policy. Domestically, Talleyrand used his influence to help in the repeal of the strict laws against émigrés, refractory clergy, and the royalists of the west. The Pope released him from the ban of excommunication in the Concordat of 1801, which also revoked the Civil Constitution of the Clergy. Talleyrand was instrumental in the completion of the Treaty of Amiens in 1802. He wanted Napoleon to keep peace afterwards, as he thought France had reached its maximum expansion.\nTalleyrand was an integral player in the German mediatization. While the Treaty of Campo Formio of 1797 had, on paper, stripped German princes of their lands beyond the left bank of the Rhine, it was not enforced until the Treaty of Lunéville in 1801. As the French annexed these lands, leaders believed that rulers of states such as Baden, Bavaria, Württemberg, Prussia, Hesse and Nassau, who lost territories on the Left Bank, should receive new territories on the Right Bank through the secularization of ecclesiastical principalities. Many of these rulers gave out bribes in order to secure new lands, and Talleyrand and some of his associates amassed about 10 million francs in the process. This was the first blow in the destruction of the Holy Roman Empire.\nWhile helping to establish French supremacy in neighboring states and assisting Bonaparte in securing the title of First Consul for life, Talleyrand sought all means of securing the permanent welfare of France. He worked hard to prevent the rupture of the peace of Amiens which occurred in May 1803, and he did what he could to prevent the Louisiana Purchase earlier in the year. These events, as he saw, told against the best interests of France and endangered the gains which she had secured by war and diplomacy. Thereafter he strove to moderate Napoleon's ambition and to preserve the European system as far as possible.\nNapoleon forced Talleyrand into marriage in September 1802 to longtime mistress Catherine Grand (née Worlée). Talleyrand purchased the Château de Valençay in May 1803, upon the urging of Napoleon. This later was used as the site of imprisonment of the Spanish royal family in 1808–1813, after Napoleon's invasion of Spain.\nIn May 1804, Napoleon bestowed upon Talleyrand the title of Grand Chamberlain of the Empire, with almost 500,000 francs a year. In 1806, he was made Sovereign Prince of Benevento (or Bénévent), a former Papal fief in southern Italy. Talleyrand held the title until 1815 and administered the principality concurrently with his other tasks.\nTalleyrand was opposed to the harsh treatment of Austria in the 1805 Treaty of Pressburg and of Prussia in the Peace of Tilsit in 1807. In 1806, after Pressburg, he profited greatly from the reorganization of the German lands, this time into the Confederation of the Rhine. He negotiated the Treaty of Posen with Saxony, but was shut out completely from the negotiations at Tilsit. After Queen Louise of Prussia failed in her appeal to Napoleon to spare her nation, she wept and was consoled by Talleyrand. This gave him a good name among the elites of European nations outside France.\n\n\nHaving wearied of serving a master in whom he no longer had much confidence, Talleyrand resigned as minister of foreign affairs in 1807, although the Emperor retained him in the Council of State as Vice-Grand Elector of the Empire. He disapproved of Napoleon's Spanish initiative, which resulted in the Peninsular War beginning in 1808. At the Congress of Erfurt in September–October 1808, Talleyrand secretly counseled Tsar Alexander. The Tsar's attitude towards Napoleon was one of apprehensive opposition. Talleyrand repaired the confidence of the Russian monarch, who rebuked Napoleon's attempts to form a direct anti-Austrian military alliance. Napoleon had expected Talleyrand to help convince the Tsar to accept his proposals and never discovered that Talleyrand was working at cross-purposes. Talleyrand believed Napoleon would eventually destroy the empire he had worked to build across multiple rulers.\nAfter his resignation in 1807 from the ministry, Talleyrand began to accept bribes from hostile powers (mainly Austria, but also Russia), to betray Napoleon's secrets. Talleyrand and Joseph Fouché, who were typically enemies in both politics and the salons, had a rapprochement in late 1808 and entered into discussions over the imperial line of succession. Napoleon had yet to address this matter and the two men knew that without a legitimate heir a struggle for power would erupt in the wake of Napoleon's death. Even Talleyrand, who believed that Napoleon's policies were leading France to ruin, understood the necessity of peaceful transitions of power. Napoleon received word of their actions and deemed them treasonous. This perception caused the famous dressing down of Talleyrand in front of Napoleon's marshals, during which Napoleon famously claimed that he could \"break him like a glass, but it's not worth the trouble\" and added with a scatological tone that Talleyrand was \"shit in a silk stocking\", to which the minister coldly retorted, once Napoleon had left, \"Pity that so great a man should have been so badly brought up!\"\nTalleyrand opposed the further harsh treatment of Austria in 1809 after the War of the Fifth Coalition. He was also a critic of the French invasion of Russia in 1812. He was invited to resume his former office in late 1813, but Talleyrand could see that power was slipping from Napoleon's hands. He offered to resign from the council in early 1814, but Napoleon refused the move. Talleyrand then hosted the tsar at the end of March after the fall of Paris, persuaded him that the best chance of stability lay with the House of Bourbon, and gained his support. On 1 April 1814 he led the Sénat conservateur in establishing a provisional government in Paris, of which he was elected president. On 2 April the Senate officially deposed Napoleon with the Acte de déchéance de l'Empereur; by 11 April it had approved the Treaty of Fontainebleau and adopted a new constitution to re-establish the Bourbon monarchy.\n\n\nWhen Napoleon was succeeded by Louis XVIII in April 1814, Talleyrand was one of the key agents of the restoration of the House of Bourbon, although he opposed the new legislation of Louis's rule. Talleyrand was the chief French negotiator at the Congress of Vienna; earlier that same year he signed the Treaty of Paris. It was due in part to his skills that the terms of the treaty were remarkably lenient towards France. As the Congress opened, the right to make decisions was restricted to four countries: Austria, the United Kingdom, Prussia and Russia. France and other European countries were invited to attend, but were not allowed to influence the process. Talleyrand promptly became the champion of the small countries and demanded admission into the ranks of the decision-making process. The four powers admitted France and Spain to the decision-making backrooms of the conference after a good deal of diplomatic maneuvering by Talleyrand, who had the support of the Spanish representative, Pedro Gómez Labrador, Marquis of Labrador. Spain was excluded after a while (a result of both the Marquis of Labrador's incompetence as well as the quixotic nature of Spain's agenda), but France (Talleyrand) was allowed to participate until the end. Russia and Prussia sought to enlarge their territory at the Congress. Russia demanded annexation of Poland (already occupied by Russian troops); this demand was finally satisfied, despite protests by France, Austria and the United Kingdom. Austria was afraid of future conflicts with Russia or Prussia and the United Kingdom was opposed to their expansion as well—and Talleyrand managed to take advantage of these contradictions within the former anti-French coalition.[citation needed] On 3 January 1815, a secret treaty was signed by France's Talleyrand, Austria's Metternich and Britain's Castlereagh. By this tract, officially a secret treaty of defensive alliance, the three powers agreed to use force if necessary to \"repulse aggression\" (of Russia and Prussia) and to protect the \"state of security and independence.\"\nTalleyrand, having managed to establish a middle position, received some favors from the other countries in exchange for his support: France returned to its 1792 boundaries without reparations, with French control over the papal Comtat Venaissin, County of Montbéliard, and Salm, which had been independent at the start of the French Revolution in 1789. It would later be debated which outcome would have been better for France: allowing Prussia to annex all of Saxony (Talleyrand ensured that only part of the kingdom would be annexed) or the Rhine provinces. The first option would have kept Prussia farther away from France, but would have needed much more opposition as well. Some historians have argued that Talleyrand's diplomacy wound up establishing the fault lines of World War I, especially as it allowed Prussia to engulf small German states west of the Rhine. This simultaneously placed the Prussian Army at the French-German frontier, for the first time; made Prussia the largest German power in terms of territory, population and the industry of the Ruhr and Rhineland; and eventually helped pave the way to German unification under the Prussian throne. However, at the time Talleyrand's diplomacy was regarded as successful, as it removed the threat of France being partitioned by the victors. Talleyrand also managed to strengthen his own position in France (ultraroyalists had disapproved of the presence of a former \"revolutionary\" and \"murderer of the Duke d'Enghien\" in the royal cabinet).\nNapoleon's return to France in 1815 and his subsequent defeat, the Hundred Days, was a reverse for the diplomatic victories of Talleyrand (who remained in Vienna the whole time). The second peace settlement was markedly less lenient and it was fortunate for France that the business of the Congress had been concluded. Having been appointed foreign minister and president of the council on 9 July 1815, Talleyrand resigned in September of that year, over his objections to the second treaty. Louis XVIII appointed him as the Grand Chamberlain of France, a mostly ceremonial role which provided Talleyrand with a steady income. For the next fifteen years he restricted himself to the role of \"elder statesman\", criticizing and intriguing against Minister of Police Élie, duc Decazes, Prime Minister Duc de Richelieu and other political opponents from the sidelines. In celebration of the birth of the Duc de Bordeaux, Louis XVIII made Talleyrand a knight of the Order of the Holy Spirit.\nIn December 1829, Talleyrand funded the foundation of the National newspaper. The newspaper was run by his personal friend Adolphe Thiers, alongside Armand Carrel, François Mignet and Stendhal. Its first issue appeared on 3 January 1830, quickly becoming the mouthpiece of the Orléanist cause and gaining popularity among the French liberal bourgeoisie. Following the ascent of Louis-Philippe I to the throne in the aftermath of the July Revolution of 1830, Talleyrand reluctantly agreed to become ambassador to the United Kingdom, a post he held from 1830 to 1834. In this role, he strove to reinforce the legitimacy of Louis-Philippe's regime. He played a vital role in the London Conference of 1830, rebuking a partition plan developed by his son Charles de Flahaut and helping bring Leopold of Saxe-Coburg to the throne of the newly independent Kingdom of Belgium. In April 1834 he crowned his diplomatic career by signing the treaty which brought together as allies France, Great Britain, Spain and Portugal.\nAfter resigning from his position as ambassador in London in November 1834, Talleyrand stopped playing an active role in French politics. He split his time between Château de Valençay and Saint-Florentin, where he hosted frequent banquets and played whist with his visitors. His physical health began to steadily deteriorate and he began using an armchair on wheels provided to him by Louis Philippe I. He spent most of his time in the company of the Duchess Dino and concerned himself with the education of her daughter Pauline. Talleyrand suffered from bouts of recurring depression which were caused by his concern over his legacy and the development of the Napoleonic myth. To that end he ordered that his autobiography, the Memoirs, be published 30 years after his death. He also sought to gain the friendship of people he believed would shape public opinion in the future, including Honoré de Balzac, Lady Granville and Alphonse de Lamartine. During the last years of his life Talleyrand began planning his reconciliation with the Catholic Church. On 16 May 1838, he signed a retraction of his errors towards the church and a letter of submission to Pope Gregory XVI. He died the following day at 3:55 p.m., at Saint-Florentin.\nBy a codicil added to his will on 17 March 1838, Talleyrand left his memoirs and papers to the duchess of Dino and Adolphe de Bacourt. The latter revised them with care, and added to them other pieces emanating from Talleyrand. They fell into some question: first that Talleyrand is known to have destroyed many of his most important papers, and secondly that de Bacourt almost certainly drew up the connected narrative which we now possess from notes which were in more or less of confusion. The mémoires were later edited by the duc de Broglie and published in 1891.\n\n\nCharles Maurice de Talleyrand-Périgord’s influence on the events leading up to the Battle of Waterloo was significant, though his role was more indirect compared to military leaders. As a prominent diplomat and statesman, Talleyrand was crucial in shaping the political and diplomatic landscape of early 19th-century Europe, particularly during the turbulent Napoleonic era. Initially, he served as Napoleon Bonaparte’s foreign minister, where his strategic acumen and diplomatic skills were instrumental in shaping France’s foreign policy and navigating the complex international alliances and rivalries of the time.\n\nTalleyrand’s political maneuvering was a cornerstone in the formation and sustainability of the Seventh Coalition, an alliance of major European powers united against Napoleon. His diplomatic efforts were essential in forging and maintaining this coalition, which included the United Kingdom, Russia, Austria, and Prussia. The cohesion and effectiveness of this alliance were pivotal in countering Napoleon’s ambitions and ultimately in the outcome of the Battle of Waterloo.\n\nThe Congress of Vienna, held in 1814-1815, was another arena where Talleyrand’s influence was profound. Despite France’s defeat and Napoleon’s abdication, Talleyrand played a critical role in the negotiations, leveraging his expertise to secure relatively favorable terms for France. His diplomatic skills helped to mitigate the harshness of the proposed settlements and ensured that France retained a degree of influence in the new European order. This not only affected the immediate post-war arrangements but also set the stage for the balance of power that characterized European politics in the subsequent decades.\n\nIn the context of the Battle of Waterloo itself, while Talleyrand did not engage in military strategy or tactics, his impact was felt through the broader diplomatic and political strategies that shaped the conflict. The cohesive and well-coordinated efforts of the Seventh Coalition, made possible in part by Talleyrand’s diplomatic endeavors, were crucial in countering Napoleon’s military campaigns. The unity of the coalition forces was a direct result of the successful diplomatic groundwork laid by Talleyrand and his counterparts.\n\nAdditionally, Talleyrand’s influence extended to the post-Waterloo period. His role in the subsequent peace negotiations and the establishment of a new European order was instrumental in ensuring stability and preventing further widespread conflict. The relative peace that followed the Napoleonic Wars, known as the Concert of Europe, was partly a product of Talleyrand’s diplomatic work, which helped to stabilize Europe and prevent the resurgence of large-scale conflicts for several decades.\n\nIn summary, while Talleyrand was not directly involved in the Battle of Waterloo, his diplomatic skills and strategic positioning were crucial in shaping the conditions leading up to the battle and its aftermath. His efforts in forging the Seventh Coalition, negotiating at the Congress of Vienna, and contributing to the post-war settlement were integral to the outcome of the conflict and the stability of Europe in the early 19th century.", 4 | "Death": "Charles Maurice de Talleyrand-Périgord, a prominent French diplomat, died on May 17, 1838, in Paris, at the age of 84. His death marked the end of a significant era in European diplomacy, as he had been a key political figure through the French Revolution, the Napoleonic era, and the Bourbon Restoration. In his later years, Talleyrand suffered from declining health, afflicted by gout and other ailments that left him increasingly frail. Before his death, he sought reconciliation with the Catholic Church, receiving the last rites from the Archbishop of Paris, thus signaling his return to the Church after many years of estrangement. Talleyrand's death was widely noted across Europe due to his immense influence on European politics and diplomacy. He is remembered as a master diplomat who navigated the turbulent political landscape of his time with skill and pragmatism. Born into an aristocratic family on February 2, 1754, Talleyrand initially pursued a clerical career, becoming a bishop. He played a significant role during the French Revolution, advocating for the confiscation of Church property and supporting the revolutionary government. Throughout his diplomatic career, he served under various regimes, including those of Napoleon Bonaparte and King Louis XVIII, showcasing his ability to adapt to changing political landscapes. He was instrumental in negotiating key treaties, including the Treaty of Vienna (1815), which reshaped Europe after the Napoleonic Wars. Known for his pragmatism and sometimes criticized for his opportunism, Talleyrand's actions were often driven by a desire to maintain stability and balance of power in Europe. His life and career were characterized by his extraordinary ability to survive and thrive through one of the most tumultuous periods in European history, and his death marked the end of a remarkable journey of political maneuvering and influence.", 5 | "Source":"Talleyrand History" 6 | } -------------------------------------------------------------------------------- /graphRAG_generation.py: -------------------------------------------------------------------------------- 1 | from langchain_community.graphs import Neo4jGraph 2 | from langchain_openai import ChatOpenAI 3 | from langchain.prompts.prompt import PromptTemplate 4 | from langchain.chains import GraphCypherQAChain 5 | from neo4j_env import graph 6 | import textwrap 7 | 8 | retrieval_qa_chat_prompt = """ 9 | Task:Generate Cypher statement to 10 | query a graph database. 11 | Instructions: 12 | Use only the provided relationship types and properties in the 13 | schema. Do not use any other relationship types or properties that 14 | are not provided. 15 | Remember the relationships are like Schema: 16 | {schema} 17 | if question say Talleyrand it menas Charles-Maurice de Talleyrand 18 | and if say Napoleon means Napoleon Bonaparte and if say waterloo is Battle of Waterloo. 19 | 20 | Note: Do not include any explanations or apologies in your responses. 21 | Do not include any text except the generated Cypher statement. Remember to correct the typo in names 22 | 23 | Example 1: What was the story of napoleon in the battle of waterloo? 24 | MATCH (Napoleon:Person)-[:RELATED_TO]->(waterloo:Event)-[:HAS_General_INFO]->(info:General_info)-[:HAS_Chunk_INFO]->(ChunkInfo:Waterloo_Chunk) 25 | RETURN p, e, info, ChunkInfo.text 26 | 27 | Example 2: What was the story of the battle of waterloo? 28 | MATCH (waterloo:Event)-[:HAS_General_INFO]->(info:General_info)-[:HAS_Chunk_INFO]->(ChunkInfo:Waterloo_Chunk) 29 | RETURN p, e, info, ChunkInfo.text 30 | 31 | Example 3: tell me about Talleyrand and napoleon in 5 lines 32 | MATCH (Talleyrand:Person)-[:RELATED_TO]->(Napoleon:Person)-[:HAS_Career_INFO]->(info:Career_info)-[:HAS_Chunk_INFO]->(ChunkInfo:Napoleon_Chunk) 33 | RETURN Talleyrand, Napoleon 34 | 35 | The question is: 36 | {question} 37 | 38 | """ 39 | 40 | 41 | class GraphRAG: 42 | def __init__(self): 43 | self.cypher_prompt = PromptTemplate( 44 | input_variables=["schema", "question"], 45 | template= retrieval_qa_chat_prompt 46 | ) 47 | self.cypher_chain = GraphCypherQAChain.from_llm( 48 | ChatOpenAI(temperature=0), 49 | graph=graph, 50 | verbose=True, 51 | cypher_prompt=self.cypher_prompt, 52 | ) 53 | 54 | def generate_cypher_query(self, question: str) -> str: 55 | response = self.cypher_chain.run(question) 56 | return textwrap.fill(response, 60) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from graphRAG_generation import GraphRAG 2 | from vectorRAG_generation import VectorRAG 3 | 4 | 5 | def query_graph_or_vector_rag(use_graph: bool, question: str) -> str: 6 | if use_graph: 7 | # GraphRAG 8 | query_generator = GraphRAG() 9 | cypher_query = query_generator.generate_cypher_query(question) 10 | return f"Cypher Query: {cypher_query}" 11 | 12 | else: 13 | # VectorRAG 14 | retrieval_qa = VectorRAG() 15 | answer = retrieval_qa.query(question) 16 | return f"Answer: {answer}" 17 | 18 | 19 | # query 20 | question = "Who was leading the Battle of Waterloo?" 21 | 22 | # Use GraphRAG -> True 23 | # Use VectorRAG -> False 24 | result_relationship = query_graph_or_vector_rag(True, question) 25 | print(result_relationship) 26 | 27 | -------------------------------------------------------------------------------- /neo4j_env.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | import os 3 | from langchain_community.graphs import Neo4jGraph 4 | load_dotenv('.env', override=True) 5 | # Warning control 6 | import warnings 7 | warnings.filterwarnings("ignore") 8 | 9 | NEO4J_URI = os.getenv('NEO4J_URI') 10 | NEO4J_USERNAME = os.getenv('NEO4J_USERNAME') 11 | NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD') 12 | NEO4J_DATABASE = os.getenv('NEO4J_DATABASE') 13 | OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') 14 | OPENAI_ENDPOINT = os.getenv('OPENAI_BASE_URL') + '/embeddings' 15 | 16 | 17 | 18 | # Global constants 19 | VECTOR_INDEX_NAME = 'NapoleonOpenAI' 20 | VECTOR_NODE_LABEL = 'Napoleon_Chunk' 21 | VECTOR_SOURCE_PROPERTY = 'text' 22 | VECTOR_EMBEDDING_PROPERTY = 'textEmbeddingOpenAI' 23 | 24 | 25 | graph = Neo4jGraph( 26 | url=NEO4J_URI, username=NEO4J_USERNAME, password=NEO4J_PASSWORD, database=NEO4J_DATABASE 27 | ) -------------------------------------------------------------------------------- /preprocessing.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | 3 | 4 | def read_local_html(file_path): 5 | with open(file_path, 'r', encoding='utf-8') as file: 6 | return file.read() 7 | 8 | 9 | def extract_text_from_html(html): 10 | soup = BeautifulSoup(html, 'html.parser') 11 | 12 | # Extract headings and paragraphs 13 | headings = soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) 14 | paragraphs = soup.find_all('p') 15 | 16 | # Pair headings with paragraphs 17 | content = [] 18 | current_heading = None 19 | for element in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p']): 20 | if element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']: 21 | if current_heading: 22 | content.append(current_heading) 23 | # Add "Section:" before the heading text 24 | current_heading = f"Section: {element.get_text().strip()}\n" 25 | elif element.name == 'p': 26 | if current_heading: 27 | current_heading += f"{element.get_text().strip()}\n" 28 | else: 29 | content.append(f"{element.get_text().strip()}\n") 30 | if current_heading: 31 | content.append(current_heading) 32 | 33 | return ''.join(content) 34 | 35 | 36 | def save_text_to_file(text, output_path): 37 | with open(output_path, 'w', encoding='utf-8') as file: 38 | file.write(text) 39 | 40 | 41 | file_path = r'data\raw\The Indispensable Talleyrand _ Hoover Institution The Indispensable Talleyrand.html' 42 | 43 | 44 | html_content = read_local_html(file_path) 45 | 46 | 47 | extracted_text = extract_text_from_html(html_content) 48 | 49 | 50 | save_text_to_file(extracted_text, 'data/cleaned/The Indispensable Talleyrand _ Hoover Institution The Indispensable Talleyrand.txt') 51 | 52 | print(f"Text has been extracted and saved") 53 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "knowledge-graph-for-rag-using-neo4j" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Homayoun.srp@gmail.com>"] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.12" 10 | neo4j = "^5.23.1" 11 | pypdf3 = "^1.0.6" 12 | langchain = "^0.2.12" 13 | python-dotenv = "^1.0.1" 14 | langchain-community = "^0.2.11" 15 | langchain-openai = "^0.1.20" 16 | beautifulsoup4 = "^4.12.3" 17 | 18 | 19 | [build-system] 20 | requires = ["poetry-core"] 21 | build-backend = "poetry.core.masonry.api" 22 | -------------------------------------------------------------------------------- /txt2json.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | 5 | def read_text_file(file_path): 6 | with open(file_path, 'r', encoding='utf-8') as file: 7 | return file.read() 8 | 9 | 10 | def parse_text_to_json(text): 11 | # Remove citations 12 | text = re.sub(r'\[\d+\]', '', text) 13 | 14 | # Split the text into sections based on 'Main-Section:' 15 | sections = re.split(r'\n(?=Main-Section:)', text) 16 | 17 | # Initialize an empty dictionary to hold the JSON structure 18 | json_structure = {} 19 | 20 | for section in sections: 21 | if not section.strip(): 22 | continue 23 | 24 | # Match the main section header 25 | main_section_match = re.match(r'Main-Section: (.+)', section) 26 | if not main_section_match: 27 | print("No main section match:", section) 28 | continue 29 | 30 | main_section_name = main_section_match.group(1) 31 | # Extract the text following the main section header 32 | section_text = section[len(main_section_match.group(0)):].strip() 33 | 34 | # Add the main section and its content to the JSON structure 35 | json_structure[main_section_name] = section_text 36 | 37 | return json_structure 38 | 39 | 40 | def save_json_to_file(data, output_path): 41 | with open(output_path, 'w', encoding='utf-8') as file: 42 | json.dump(data, file, indent=4, ensure_ascii=False) 43 | 44 | 45 | input_file_path = r"data\cleaned\Talleyrand.txt" 46 | output_file_path = 'data/json/Talleyrand.json' 47 | 48 | 49 | text_data = read_text_file(input_file_path) 50 | print("Text data read from file:", text_data[:500]) 51 | 52 | 53 | json_data = parse_text_to_json(text_data) 54 | 55 | 56 | save_json_to_file(json_data, output_file_path) 57 | 58 | print(f"JSON data has been saved to {output_file_path}") 59 | print("JSON Data:", json_data) 60 | -------------------------------------------------------------------------------- /vectorRAG_generation.py: -------------------------------------------------------------------------------- 1 | from langchain import hub 2 | from langchain.chains.combine_documents import create_stuff_documents_chain 3 | from langchain.chains.retrieval import create_retrieval_chain 4 | from langchain_openai import OpenAIEmbeddings 5 | from langchain_community.vectorstores import Neo4jVector 6 | import textwrap 7 | from langchain_openai import ChatOpenAI 8 | from neo4j_env import * 9 | 10 | class VectorRAG: 11 | def __init__(self): 12 | self.vector_store = Neo4jVector.from_existing_graph( 13 | embedding=OpenAIEmbeddings(), 14 | url=NEO4J_URI, 15 | username=NEO4J_USERNAME, 16 | password=NEO4J_PASSWORD, 17 | index_name=VECTOR_INDEX_NAME, 18 | node_label=VECTOR_NODE_LABEL, 19 | text_node_properties=[VECTOR_SOURCE_PROPERTY], 20 | embedding_node_property=VECTOR_EMBEDDING_PROPERTY, 21 | ) 22 | 23 | self.retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat") 24 | self.combine_docs_chain = create_stuff_documents_chain(ChatOpenAI(temperature=0), self.retrieval_qa_chat_prompt) 25 | self.retrieval_chain = create_retrieval_chain( 26 | retriever=self.vector_store.as_retriever(), 27 | combine_docs_chain=self.combine_docs_chain 28 | ) 29 | 30 | def query(self, question: str) -> str: 31 | result = self.retrieval_chain.invoke(input={"input": question}) 32 | return textwrap.fill(result['answer'], 60) 33 | 34 | 35 | --------------------------------------------------------------------------------