Video: GenAI and Graph Foundations: Building GraphRAG with Neo4j with Martin O’Hanlon | Duration: 7328s | Summary: GenAI and Graph Foundations: Building GraphRAG with Neo4j with Martin O’Hanlon | Chapters: Workshop Series Introduction (10.64s), Workshop Introduction Overview (207.905s), Defining Generative AI (529.72s), Introducing Graph RAG (881.365s), GraphRAG and Knowledge Graphs (1169.705s), Setting Up Environment (1445.105s), Knowledge Graph Construction (1987.455s), Extracting Knowledge Graphs (2118.92s), Defining Graph Schema (2921.065s), Graph Schema Refinement (3382.125s), Adding Structured Data (3834.475s), Graph RAG Benefits (4306.03s), Exploring Knowledge Graphs (4562.98s), Vector Retrieval Techniques (4777.96s), Enhancing Knowledge Graphs (5755.105s), TextEcipher Retriever Recommendations (6696.89s), Conclusion: Knowledge Graph Applications (6799.61s), Concluding Remarks and Resources (7050.7s)
Transcript for "GenAI and Graph Foundations: Building GraphRAG with Neo4j with Martin O’Hanlon": All right. Good morning, good evening, good afternoon, and welcome to this first Road to Nodes AI workshop series session, part one of five, basically. So I'm super excited to kick this off. We have an amazing set of workshops, and this is obviously the start of the whole show today with my dear colleague Martin, Martin O'Hanlon, who is one of the masterminds behind the Graph Academy program, where you find lots of workshops around Neo4j with all kinds of topics, starting with fundamentals on Neo4j, fundamentals with Aura, GenAI, as you see today, drivers with Java or JavaScript or other tools and usage scenarios. We like build a knowledge graph or work on a graph rack pipeline as as we'll see later in this series as well. So that's that's what Martin is doing and Martin and the team's doing, so that's that's great to have him today. So very, very warm welcome to you, Martin. Thank you, Alex. Thank you very much. Yeah. Thank you. So, Alex, are we ready to go, or do you want almost ready to go. I'd like to keep a couple of things out of the way before you dive in fully, is more or less a couple of housekeeping things. Obviously, this session is going to be recorded. So we are recording this video today, and we will share the link to the video with you afterwards. So if you missed anything or if you want to watch something again, obviously no problem. You can ask questions. We invite you to ask questions during this session. Since it's live, we can answer questions. We have another team member from the Graph Academy team in backstage, Irina, giving a login, who is there to help and answer questions. We will have some time during this session to answer questions live with Martin, so we will see how it goes, as much time as we have and how much time we can cover questions. But supposed to be interactive. It's supposed to be something where you participate as well, where you participate with the workshop, not only practical, but also with questions or comments, so keep that going. If you have questions, please use the q and a section. So as you can see on the right hand side of this window, you should see three buttons. One says chat. That's where the chat is and where people are saying hi from Germany, from Ukraine, from Belgium, from Brazil, from Croatia, from The United States, India, UK, welcome to you all. You also see a button that says Q and A. Click that button and type in your question, then it pops up in the Q and A section. If you put your question in the chat section, since you can see it here, lots of hellos, lots of messages there, we might miss your question, so please use the q and a function for that. And, I think with that, I'm off again. Obviously, I will be back, but I leave I leave the stage to to Martin. And and, yeah, I wish you all good a good time. Have fun. Enjoy. And, yeah, participate. Thank you, Alex, so much for the for the introduction. So just in case, you missed it, my name's Martin. My name's Martin O'Hanlon. I work in the developer relations team. Most of my job is creating, educational content and running workshops and things things like this. You can find me almost everywhere online with at Martin O'Hanlon. There are very few others of me. So, just a couple of things to note. So I'm gonna obviously be presenting. Please, join along, with the workshop. There is huge amounts of practical activity, and we'll talk about how how we do that. Just some, sort of housekeeping things. I'm gonna try and look into you. If I need to answer questions, I'll look over here. So if I tend tend to do this, that's what I'm doing. I'm just looking to see if there's any questions. Okay. Alright. So let's talk about what we're gonna do today. So we're gonna broadly split split this into four sections. So we're gonna talk about what is generative AI, and we're gonna spend some time talking about what is GenAI, looking at topics like RAG and GraphRAG and how Neo four j can help you improve your GenAI applications. We're gonna construct a knowledge graph, and we'll talk about knowledge graphs, but we're gonna turn some unstructured data into a graph. And then we're gonna look at retrievers. So we're gonna look at how we can can can get data out of that graph to use as part of our Gen AI application retrievals. And then I'm gonna leave you with some homework, which is, the final module about creating an agent. So that's what we're gonna do today. You are gonna need access to our Graph Academy platform. This is gonna allow me to give you access to the content. It's also gonna create a Neo four j instance for you to use as part of the as part of the workshop, and it will mean that you'll have the content forever as well. Okay. So what do I need? Well, first off, you're gonna need some links, and you're gonna need some information. So this top link here, dev.neo4j.com/workshopworkshop-genainotes. Alex, if you wouldn't mind just popping that in the chat or pinning pinning it somewhere, that would be really useful. If you go to that link, you are gonna be taken to this document. And this document has in it all of the information that you're gonna need and all of the links that you're gonna need in order to be able to complete this workshop. So the first thing, if you're not already a member, if you've not already used, GraphAcademy, if you go to graphacademy.neo4j.com and, create an account. So it's gonna take you to GraphAcademy, I hope. If GraphAcademy isn't working, this will be a short workshop. So, yeah, it'll take you to, graph GraphAcademy, to, create an account. And then finally, what I'm gonna gonna ask you to do is go to here. So this is the workshop that we are gonna be working through. It's called graphacademy.neo4j.com/courses/workshopgenai. I'm just gonna refresh my window a moment. Yeah. If you could, yeah, go to graph Graphic Academy and sign up, for the workshop, that would be the the the right place to start. Hopefully, what you'll end up with is you'll end up with this view. So if you sign up for, Graph Academy and you go to this workshop here, you'll oh, this workshop link here, you will be met with this screen. So Neo four j and generative AI workshop. Let just have a quick look. Oh, great. Okay. That's brilliant. I was slightly worried for a moment. But, yes, once you've signed up for GraphAcademy, and gone slightly, you should be able then to enroll in the course. And, again, this is gonna give you access to all of the the the content that we're gonna be using for this workshop. Okay. Great. Right. So while you're going through that process of signing up and, getting to the course content, I'm just gonna talk a little bit about, Graph Academy and how it works. So I've signed up. There are a number of modules that we're gonna go through today. This generative AI one, knowledge graph construction, retrieval, and agents, and you can follow along with me. In this first module, we're gonna talk about what is generative AI, and we're gonna look at the benefits of using of GraphRAG and retrieval augmented generation and talk a little bit about how we can use knowledge graphs to structure real world data. So let's I'm just gonna crack on with the first lesson. I'm gonna leave you guys to to to kinda get get yourself signed up because there's there's some talking I need to do. There's some information that I need to pass on to kind of get people up to speed with understanding what we mean by generative AI. Okay. So just to recap, we're on this first lesson in the first module, what is generative AI? And you have all of the content in front of you front of you to follow along. I'm just I'm gonna use a slightly different view, but it's exactly the same content. Okay. So let's start by defining some terms. So first off, what do we mean by Gen AI? So what we mean by what what I mean by Gen AI and what we're be talking about in this session is using artificial intelligence models that generate new content. Generative AI generate new content, and it typically resembles human made data. And, typically, the way these GenAI models work is they these they take a lot of training data, normally publicly accessible information, push it through a model, and then the output is new content that's based on that training data. So when we're talking about GenAI, that's what we're talking about. Let's just make sure we all understand. GenAI model is not intelligent in the way humans are. They don't understand or comprehend, the content they generate, and they are statistical models. Right? And they can produce coherent, contextually relevant output, but it lacks understanding. Right? So in this workshop, in this course, we're gonna be focusing on LLMs, large language models. Large language models are text generating, GenAI models. They generate text, human like text. And, typically, the way that we get an LLM to work and when I and you will have all used an LLM. I'm sure your Claudes, your Geminis, your GPTs, all large lang language models. And the way they work is that we provide some instruction, and the LLM provides the most likely response based on the patterns in its training data. So if given the instruction continue the sequence a b c, it could respond with d e f. That would be a typical response. However, our instructions are normally significantly longer than that, and our responses also are significantly more complex. And when we give an instruction, we talk about giving an LLM a prompt. And our prompt should specify what our requirements are and give clear instructions about how to respond. And it is through these prompts, that we get answers and answers and responses back. However, at their core, they are complex predictive text machines. They don't understand, and they also heart would find it difficult to say, how did you determine that answer? I don't know. Often referred to as black boxes. So if we understand that model of, training data into a model, allows us to generate new content. New content they they they that they generate is based on the training data that they have received. It is static. It happens at a point in time, and it may not reflect the the state of the world or have or certainly have include, private information either. So, you know, when prompted to provide a response relating to new data, so, say, example, tell me about some, new Neo four j features, it would be based on access on on the on the data that that it that the LNM has got. Here are some old features with incomplete information. So this represents one of our challenges. Accuracy is also represents a big challenge. And, quite often or often, LLMs will, provide a response which is presented as fact. Yeah? But it is made up. Right? It's not true, and and it's known as a hallucination. So I don't know if this works outside The UK, but in The UK, we call it yeah. A common saying is the moon is made of cheese. If we trained our large language model enough with enough data that suggested that the moon was made of cheese, the LLM would respond with, what's the moon made of? It's made of cheese. So we can improve the accuracy of our responses by providing context in our prompts. So, if we wanna avoid hallucination and we ask our LLM, you know, summarize this company's this company's results, its training data might not include that company's latest results. But if we provide a stock market report within our prompt, then the LLM has, additional information, I. E. The information that's gotten its prompt, the context, in order to provide a better response. And we can provide more information. So, you know, summarize this company's results. We give a stock market pull, and we give it some annual annual results. We're gonna get a better response. So we are gonna be talking a lot about supplying context. Supplying context in your prompts helps LLMs generate accurate, relevant, trustworthy data that is low in hallucination and also compensates for the lack of data. We are gonna be talking a lot about supplying context. This is the challenge that we face ourselves with. How do we provide good context to our generative AI models in order for them to provide us with good responses? Alright. So that is our first listening listening to me about what we mean by generative AI. I'm just gonna take a moment because people may have joined. If you have joined and you want to join and join, our session, you will find all of the links here in this dev.neo4j.com slash workshop notes oh, sorry. Workshop dash gen AI notes. We're gonna be using our GraphAcademy platform. If you go there, graphacademy.neo4j.com, you can sign up for an account, and you can, go to this workshop here. All of the links are in this document, and I believe it's been shared in the chat as well. Okay. So that's generative AI. Let's go on and let's talk about GraphRAG. So before we talk about GraphRAG, let's talk about RAG or retrieval augmented generation. So this is an approach that allows us to improve our responses from our LLMs by giving them better context, by giving them relevant up to date information. And, typically, RAG works like this. So, our user presents a question, gives it a prompt. Our application, our, you know, our Chatter application, uses a retriever, passes that question the user, sent sent us to a retriever. That may that may retriever may do may search against a particular document document store or a data store or a graph to retrieve some context. That additional context is then passed to the LLM. So the LLM gets typically the user's question plus some additional context that the retriever has received, and they get an and they get a better response because we have included additional information that the LLM might need in order to provide a better response. So that's how RAG works. Typically, understanding the user's the user's query, retrieving some information, and then generating a response. So that's our typical graph, rag rag process. And, you know, rag allows us to build applications, that get over some of the challenges that we were faced with. So, for example, what's the latest news on the Olympics? Our large language model on its own, based on its training data, does not include that information. However, if we create a an an application that fits that fits in front of that, we could use an an API and some real time data to gather some additional information to send to our LLM in order to gain a better response. And quite often, when we're talking about providing additional information, we talk about process of grounding, of providing additional information to ground the LLM, in fact. So RAG. Retrievers are a key component of that RAG process. The retriever is the bit that is is is responsible for turning the user's, typically, a question or a request, into some additional data that the LLM might might need. So, typically, a retriever takes unstructured input question prompt, searches for structured data that can provide context and answers, and then provides that information back. So Neo four j, various methods of building retrievers, and we're gonna look at some of those today, full text search, vector search, and TextDecipher. Yes. And we're gonna review some of those during that course during this course. And, you know, those data sources could be, you know, documents, APIs, knowledge graphs, whatever. Right? You can build a retriever, that takes that unstructured date that unstructured input and find some relevant information, from whatever source. So data sources, I've sort of enhanced the model that we looked at earlier with this idea of this news chatbot. You know, what's the latest election result in my area? You know, again, an LLM is not gonna be able to answer that question based on its own training data, but we could use a knowledge graph to understand the relationships between news topics, how how how they relate to each other, and we may build that knowledge graph based on, you know, a real time news API or previous articles. Those articles could be public or private in order to be able to sort provide us a better response. So we talked about RAG. Let's talk about graph RAG. So graph RAG was the term originally created by a research paper from Microsoft. And, essentially, what that said was you get better results from RAG solutions if you combine that in if you also use a graph in order to provide contextual information. So the benefits of of of graph rag, richer context, improved accuracy, explainability, flexible queries, and enhanced reasoning. And we're gonna, experience graph rag today, and we'll look at some of those outcomes. So a common approach to graph rag is to use a combination of vector search and graph traversal. Right? So I have expanded on the diagram that we set that that we saw before. Oh, blimey. That's a bit big. I'll leave it like this. So user asks a question. Our application, you could use a vector plus grass plus graph retriever. It could use a vector index to find similar nodes. It could use the graph to find find related nodes around that node and then provide better results back to our large language model. So classic graph rag approach, and we're gonna look at others today as well. Full text search, another really powerful tech tech technique, particularly if you are looking for specifics. So, you know, if a user is looking for a movie or an actor by name, full text search can provide a a better response than things like vector search. And also Text Decipher, Text Decipher alternative approach, in GraphRAG that allows us to take a question and turn that question into a Cypher statement, so a query that we can run against that graph. Okay. So that is enough. That's not quite enough. I was gonna say, there's just one last thing I I I wanted to talk about, and then we will move into the doing. So, I have made reference to knowledge graphs a few times. So let's just talk about what I mean by a knowledge graph, and then we can start to experience them. So a knowledge graph is an organized representation of real world entities and their relationships. That knowledge graphs provide us with a structured way to represent entities, attributes, relationships, and how they are connected, and particularly useful, when you wanna provide structured interconnected data. Yeah. Okay. Search engines have used knowledge graphs for a very long time. Right? And if we think about, you know, a knowledge graph that represents Neo four j, we could have a graph that's sort of represented like this. So there is Neo four j, the company. Neo four j, the company is located in both Malmo and London. London happens to be the capital of The United Kingdom. Neo four j is an owner of the neo four j dot com website, and it's also a blog post that is about Neo four j and was published on that website. And it happened to be written by Emil Efron, who happens to be one of the founders of Neo four j and and works there as well. So we can use this very flexible structure to define facts and information about what is essentially unstructured data. Great things about knowledge graphs is we can use them to, break down the sources of sources of information as well. So if we have two sources of sources of information, we can break them into chunks, and we can also, relate those chunks together in terms of how they, relate to each other. So, source a can relates to topic one as the source b. Source I happens to also relate to, topic j, which is referred to in a different chunk within source source b. So this very adaptable and, flexible structure allows us to display that that information. Again, moving on from defining our data within our knowledge graph, we can also describe its organizing principles as well. Okay. So gen AI in generative AI applications, knowledge graphs really do play a crucial role. They allow us to capture and organize domain specific information, and they are not strictly limited to structured data. We can use them to define, to create to understand facts within unstructured data and also integrate that structured and unstructured data together. Okay. Right. So let's just recap where we are. So we spent a little bit of time talking about what is GenAI, what is GraphRAG, what's a knowledge graph. What we're gonna do now is we are going to, do, we are gonna get into the process of building a knowledge graph ourselves and creating retrievers. Now to do that, I, I need you need to set you up, with, a development environment, and we're gonna use the GraphRag for Python package. It's called Neo four j GraphRag, and that's gonna allow us to, access these generative AI features in Neo four j. So I have created a starter repository for you, which is this one here, and it contains all of the start starter code that you are gonna need. You can also use a GitHub code space to open that repo and have a an online IDE. I would highly recommend that you do this. They are free. If you so you need a GitHub account. There's a free monthly usage, and it's gonna more than cover, cover this course. Yeah. So I'd recommend using this code What the code space is gonna do is, actually open up that repository, and set you up a dev environment. So, yeah, I'm just gonna go through that process. So I clicked on here, open in a GitHub code space, and I'm just gonna create my code space. So, again, what what this is doing is it's gonna go off, clone that repository of all the starter code and all all all of the data, and it's gonna set up our our environment. This is a good opportunity to pause, and we'll talk about questions because it'll take a minute or so to kind of, set up our environment. Alex, did we have questions we wanted to talk about? Hello? Okay. I think there was a now for the repo, I think I see Asta and Varun. What's the URL for the GitHub code space? So the git the GitHub. code space, just click on this button here, open in a GitHub code space. So if you go to we're on lesson number four, which is this graph rag for Python lesson here. Yeah. And. you can skip skip skip right through there. Think if you are just watching this now and didn't click through on GraphAcademy, maybe you show this again where where people need to click. oh, sorry. Let's go right back to the start. So if you follow this link here, this dev.neo4j.com, you're gonna end up, at my notes document, which is here, which is here. Sign up for Graph Academy. This is where you get to the course, neo4j.comgenerativeaiworkshop. This is where you'll find all of the content. You're also gonna need this document because there's an OpenAI API key in here as well that we're gonna use. So, yeah, that's where you'll find all of the information. And once you've signed up for GraphAcademy and you've and you've enrolled in the course, we are in this first module, and it's lesson number four, which is this GraphRack for Python. And that's gonna take you through the process of creating your dev environment. And like I say, I would recommend opening it in a code space. Alex. I think that should get get. everybody there, hopefully. If if not, if you're still lost, let us know, Mhmm. but I think that's that's good. Yeah. And all links and keys, again, Rina posted it in chat as well, are in this link. So if you don't know where to click and if you turned late, maybe it's still time. You can follow through on this link page that Marty created. Great. We had a couple of questions. Okay. Irina answered most of those, but one, maybe we can dive in a little bit, comes in from Don. And Don asks, can you provide a citation for the reference that documented the advantages of GraphRAG? I think Yes. before you know, nobody's talking about so then. good. Yes. Yep. I won't find it just now, but we'll make sure it gets to you. I mean, to be honest, if you search for Microsoft research paper graph rag, you will find it. Yep. Yeah. Okay. So once your GitHub code space has opened, you're gonna end up with, something that looks a little bit like this. And I I wasn't watching it, so I'm I'm assuming it's done everything. But what you need to do is so we have our codespace set up. I'm gonna make this a little bit bigger because I realize it's probably a little bit small. Hopefully, that is big enough. Let's just make my, let's just make my window a little bit smaller. But there are once you've opened your Codespaces, there are instructions to set up your environment. So you need to create a an environment file, a dot EMV file. And this information in here is the connection details to your Neo four j instance that was created when you enrolled in this GraphAcademy course. So, I'm gonna go through the process, and I'll do it. So I need to create a dot e v file. So write a little new new file. E m v. I'm gonna paste that information in there that I got from GraphAcademy. So this goes into my dot e m v file. I need an OpenAI API key, which is included in that notes document. So let's just take that and pop that in there. So now I should have everything that I need in my environment file. This, all of this is gonna allow me to connect to OpenAI and connect to my Neo four j database. There is a test environment file here, which will test to make sure you have got everything set up. So once you've set up your dot a m v file, click on run, and it's gonna run five tests. And if you get back okay, you're done. Yeah. Five tests mean that you've set up your environment file correctly, and you have everything set up. But we'll just take a pause and a breath for a moment there. Alex? Yeah. You know, generally, this this workshop today, we are going with OpenAI, but, obviously, you have the choice to do LORD, to do OLAMA, to to do whatever you prefer. It's just easier and, I think, more, you know, well charted path like with 50 OpenAI version, but this is completely. up to you afterwards how you like it. Yes. I mean, what what what what you'll see as we go through the workshop is, you know, using a different model is just about changing a class name and giving a different and giving a different API key or set set of connection credentials. Yeah. We use OpenAI for this for consistency and for ease of use. Yep, yep. And the link in the Graph Academy also gives you a pop up, basically. When you want to do it locally, you could do that. But that's also, again, like Martin said, this is the easiest way for everybody to quickly get to where we want to be. If you want to do it locally, it's totally possible, but we're not gonna show it today. But if you wanna follow along and just watch Martin do it and then you wanna do it yourself locally, know that there is this maybe you can show it quickly on the on the GraphAcademy page where you have the the possibility to do things locally. Yeah. I mean, there's a there's there's instructions little exactly of. That's what I meant. setting. Yeah. up your local. local machine as well. Okay. Yep. But. the important thing is once you have set up your environment file, and you've tested your environment, you get okay back. And just to sum up, for the credentials here, if you scroll up a little bit, Of course. Neo4j URI is individually to everybody here watching. No. As long as you enrolled to this course, this will be dynamically adjusted to your specific version. Absolutely. And the OpenAI key is where we initially linked, and I can post the link again in chat, is where all the content is in this docs page. So you can copy all this text and the URI, and the username, and the password, and the database. All of these are dynamically already updated in this snippet here. All you need to do is add the opening IP that we shared in this other document. Excellent. Thank you. Great. So I'm just gonna have a quick chat. It it it would be great to see. Couple of people wrote that they got okay, Oh, great. That's so that was all I was interested in. Brilliant. In which case, hope, I'm gonna move on. you. know, I hope everybody else is is was able to catch up. Great. Yeah. Okay. So success. All working. get started. Okay. Super. Yeah. More. and more OKs. Super. Good. Continue. Thank you very much. Alright. So first module we're gonna look at is knowledge graph construction. So we are gonna go through the process of creating a knowledge reference from some unstructured text, and we're gonna use, a class called the simple KG pipeline, simple knowledge knowledge graph pipeline, to create that knowledge graph from unstructured data. So, just to recap, we're now on module two, which is this knowledge graph construction. So let's just spend a moment, and we're gonna have a look at how do we turn unstructured data into a knowledge graph. So, ultimately, we're gonna use an LLM to do it. So when we construct a knowledge graph, we typically go through these things. So we're we're gonna have some unstructured data. We're gonna find it and gather it together. We're chunk it, split it into bits. We're gonna vectorize it. That allows us to do things like semantic search. We're gonna pass all that data to an LLM, and we're ask it to extract nodes and relationships. So it's gonna ask it to say, what are the what are the entities and what are the relationships between the entities? And then we will we will then use that output to generate the graph. So that's the construction process. So if we think about an example, so if you wanted to construct a knowledge graph based on the Neo four j Wikipedia page, that's our unstructured data, we would gather the text on the page. Looks looks a little bit like this. We would split it into bits, into smaller chunks that are more, manager but also more context aware. We would generate embeddings and vectors for each of those chunks. And then finally, we would send the text with an appropriate prompt to an LLM to ask it to parse, the entities and relationships, and we would then use that information to generate a graph. So we would get back a graph that was company, Neo four j, graph database, program language Java, and create relationships between the company Neo four j, sorry. Yeah. The company Neo four j, the graph database, Neo four j, and talk about how the Neo four j graph database was implemented in Java. So that's how we do it. So let's move on to second lesson. So what we're gonna look to do now is we are going to spend some time, and we're gonna use that near the the Graphrack for Python package to extract some data, some, notes and relationships from from some unstructured data. Okay. So first task for us to do. So we're gonna open up this file here. So, if we go to our code space, we're gonna look for the file workshop GenAI extract schema. So here it is. Here's this workshop GenAI extract schema. And what this extractor does is it creates a prompt instructing the LLM to identify those, entities and relationships, passes it, parses the information, and then creates a schema object. So if we give it the text Neo four j as a graph database, a simplified version of that schema will be this. So let's have a go. So we're gonna run the program and have a look at it. So we're in this GenAI workshop extract schema. So here here's my program. We can see my schema from TextExtractor that I got from the Neo four j GraphRAG package. Here's my text, and I'm gonna run it and see what output I get. So if I run my Python file, gonna kick it off, it's gonna go off to, in this case, OpenAI's LLM, the model GPT four, and we're gonna come back with information that's extracted from that text. So our text was Neo four j as a graph database management system developed by Neo four j, and it's come back with a company. It's also said, well, there's some name properties. It's also come back with a database management system, and it's come back with some, relationship type of developed by saying that this database management system was developed by this company. So we can see that it's turned this relatively simple piece of unstructured data into a graph schema. And it looks like that. Right? So it looks like this. Neo four j, the graph database, was developed by Neo four j, the company. So have a go. Experiment with a couple of different pieces of data, and we'll just have a quick look at any questions because I realized that we may find ourselves Yeah. A. couple of people struggling still with the ENV file. Okay. I think maybe we need to repeat that one more time. Everybody needs to create this ENV file or copy and paste the example that there is, but then you need to fill in the OpenAI key that we provided through the docs page, and then all the other information is individually to you, dynamically updated in that code snippet that is on the Graphrack course page from Graph Academy. So we cannot show you what kind of URL you need to put in and what's what's the password because that's dynamically created for you specifically when you are enrolling to the course. So it's a little different for everybody. Yeah. So the instructions are in this lesson here, graph rag graph rag for Python, and your task is this bit here, set up your environment. The important thing to note is create a copy of this file and name it dot e m v. So we need a new file called dot e m v. These are the required values that you need. This this connection is unique to you. And then you need that OpenAI API key, which you will find in this notes document here. And when you've created your EMV file, it should look like this with an OpenAI key and your connection details. And you can test everything is working by running this test environment dot py program, and you should get back okay. Okay. Cool. And, yes, we will record this session. So I see a couple of people asking for that. We have we have we are running this recording, and, obviously, we share the video to the video the link to the video with you afterwards. Yeah. Okay. Great. So hopefully, some of some of you have been able to see how we can extract how we how we can extract a graph from unstructured data. So, I will just here's this here's the program. So I'll just change the text to be, you know, the Eiffel Tower is a wrought iron lattice tower on the De Mars in Paris, France, and we'll get out a different set of nodes and a different set of relationships. So, you know, label tower. I'm trying to find another one. That's property material. Yeah. So we can see that it's created and more located on, and they'll I'm assuming there'll be a location somewhere as well. Country, France. Yeah. Okay. So we can see how we can take unstructured data and create a graph from it. So that is the sort of basis point of what we are going to do. So what we're gonna do now is we're gonna actually create a graph. So we are gonna take some time, take that unstructured data, and create create a graph. So we're gonna use, what's called the simple KG pipeline, the simple knowledge graph pipeline class. I think we missed a trick calling it simple because it can create very, very advanced, knowledge graphs. The simple comes from it's simple to use. But it basically does the five things that we talked about in terms of how you create a knowledge graph on structured data. You load some text. You split it into chunks. You create embeddings. You extract entities, and then you write that data to your database. And there's a a whole host of pipeline steps that that, that the pipeline goes that the pipeline goes through. To do that, and broadly, you can see loading, splitting, chunking, entity resolution, pruning, so get rid of things we don't need, and then writing. So what you're gonna do is you're gonna create a knowledge graph from text in a Graph Academy, course, and it's actually a Neo four j and generative AI fundamentals course. And it looks so it's what is GenAI? It is that first lesson that we went through today. So we're gonna talk take this unstructured data, this lesson, you know, all about what is generative AI and create a knowledge graph from that data. So here we go. Your task. So your task is to open up this file, the workshop Gen AI KG builder, knowledge knowledge graph builder. I want you to review the code, and then I want you to run the pipeline to create the create the graph from PDF documents. I'll I will quickly demo how to go through it, but if I just close my slide view, all of the information is contained in this lesson. So it breaks down all of the code and gives you instructions on what to do. So, I'll go through the process as well. You don't have to follow along with me. You can follow along with the instructions in here. But, yeah, first of all, our task is open up that KG Builder file, review the code, and then we'll run it. And we'll create our knowledge graph and then explore our graph. I'll just quickly go through it, and then we'll we'll look at any questions. So the file that we're looking for is this KG Builder. So here is our code. Just have a quick quick I'll just have a quick walk through. This will start to look very familiar as we see more and more examples. So our first one is we need a connection to our Neo four j database. That's what this is. So drive a connection to our database. We need a large language model. In this case, we happen to be using GPT four o. If you wanted to use Olama or a a another, you would pick a different LLM class and provide a different model name and potentially different model model parameters as well. But, yeah, we need an LLM. We also need an embedder. So we need a way of turning our text into embeddings, and that's what our embedder does for us. And here is our knowledge graph builder. So we give it the LLM we wanna use, the database we wanna the the driver to the Neo four j survey we wanna use, what database we wanna use, our embedder. And in this case, we are creating our data from PDFs. And the PDF file that we're particularly looking looking at is the one that is in workshop Gen AI data, Gen AI fundamentals one, this one here, this document here. And, actually, if we look at this PDF document, we can see that it's a text version of the of the class that we store. Generative AI or GenAI refers to artificial intelligence systems, blah blah blah blah. Yeah. So that's the text that we're gonna turn into a knowledge graph. And, yeah, and once I'm ready, I can just run it. And I I I will do that, and then may, perhaps we'll see if there's been any questions as well. Alex, I really has there been anything we wanna talk about? Yes. There are a couple of question. One is more of a of a comment. When you go code, maybe you slow down a little bit. I will do my I will thank you for the feedback. I will do my very best. I'm also conscious that we have a time limit, but yeah. Yeah. Yeah. Yeah, Yeah. yeah. We know. Exactly. Cool. And then we have a question from Surya. That's a little bit to what you asked earlier. Won't having vector search and craft traversal increase the complexity for retrieval? Only if you decide to use both. So you don't have to use both. We're gonna create embeddings of our chunks. We don't have to use them if we don't want to, but we can use them as well, particularly if we're looking to do things like similarity search. There you know, other than storage space and a little bit of runtime, there's no significant downside to creating the embeddings on, you know, what is a relatively small example as well. Cool. We had one other question, but I unfortunately clicked it away. Oh, So. Okay. Okay. Hopefully, they'll resubmit mean, it. there there are a bunch of more questions, but exactly they are yeah. We'll we'll we'll try to answer them as we as we go along. But Great. yeah. Okay. So I've run my I've run my my program, this KG Builder, and we can see that it's come back and it's created six six nodes. And, actually, if I look in my graph, I'm gonna see that it's created this structure, which says for every document, it has created a document node. For every chunk within the document, so every split them up, it's created a chunk node. And then it's created a series of entities, and those entities are the entities that have been extracted from those texture text text chunks. So let's have a look at them. So if we run the Cypher query, we can see that we have one document, which is the document we imported, and then we have a big chunk of text. Now in actual fact, we only have one chunk. At the moment, we're not chunking the text. The chunk size is bigger than the length of this document, so we only get one document. But that's fine. We're gonna look and evolve this program, throughout throughout this, this this workshop. But what we can also do is if we look at the chunks, if we look at the chunk and we go to the entities, we can see the entities that have been created by our knowledge graph builder from the unstructured data. So, yeah, if I run this this query, we can see it gets a bit let's just see if we can tidy this up a little bit. Right. So we have large language model. We have an entity model large language model. We have a prompt, you're a friendly travel agent, that responds to a large language model. We also have another prompt which responds to a large language model. We also have what is the capital of Japan, which is another prompt. And we also have another model, which is GPT, and another model, which is Dalit. So we can see based on the information that we put into this graph that it has identified prompts, models, prompts prompt prompt prompts and models from from from from this particular chunk chunk of text. One of the things that we're gonna do is we're gonna look to refine this schema to get ask it to find the things that we are looking for rather than the things that it decides that that we might want to see. But, yeah, we can see that our process has worked. We've taken unstructured data, and then we have created a knowledge graph from, you know, a relatively simple piece of code, which is this simple knowledge graph builder. Okay. So what we're gonna do is we are gonna move on now. So we've created our graph. We're gonna move on now to just looking at chunk size because what we saw was the default chunk size is quite large, and we ended up with, like, one chunk. Now the larger the chunk size, the more context the LLM has to work with, but it also may lead to less granular data. So what we're gonna do is we're gonna look to modify this to use a different chunk size. So, again, you have a task to do. So your task is to, delete the existing graph because we're gonna change it. You're gonna create a custom fixed type size splitter. You're gonna modify that simple KG pipeline. You're gonna run it again, and then you're gonna review the documents. So, again, the information the information you need is in the lesson so you can follow follow follow along. And, again, I'll go through through the process as well, and then we can talk about, what we see and explore the output that we get. So, but, yeah, you we are just to recap, we are in this lesson here, chunk size. So what we're gonna do is we're gonna modify the program that we were just working on, that KG Builder program, to, modify the chunk size. And, again, I'll go through that process as well. So, yeah, I'm just gonna let's just start off with deleting all our nodes and relationships where we can Just wondering why my query is not working. Ah, there we go. Okay. Oh, I think you've already done it. So let's go back to this KG Builder program, and we're gonna import this fixed size splitter so we can change our chunk size. Yeah. So I need to create a text splitter. The chunk size size you use is almost entirely down to, your use case. We're fit we are splitting on just the number of characters, but you can have significantly more complex chunking strategies. And it doesn't have to be on size. It could be on you know, if you have a structured document that's maybe got segments in it, could be by segments. You could also I don't know. If it's got code snippets in it, they become their own chunks. So how you chunk your document, lot of it depends on the data that you're working working with. But, again, for this example, we're gonna go really simple, and we're just gonna use it based on 500 characters. So we need to create a chunk size, and we need to modify our simple KG pipeline to use that text splitter. So here's the text splitter that we created, and I've just updated the KG simple KG pipeline to use it by by setting a parameter. So done that. Give it a run. So I'm gonna run it, and we'll look at the results that changing the chunk size has, on our graph. And, again, if you're following along, there's a complete code here if you wanna want to have have a look at it, and then we can go on and explore the changes that it's made to our graph. But okay. There we go. It's come back, and it has created our graph. So once I've gone through and I've split up the text, let's just have a look. And, actually, I can now see that I have one document, but I have multiple chunks. So I have how many chunks do I have? I have it split that that that document into eight separate chunks of sort of sizes of about 500. Right? And now if I go and look at the my knowledge graph again, I can now see that there are significantly more chunks. And, interestingly, one of the things I see is is that I see, greater granularity in terms of the entities that have been created. So I'm getting, I I get the same models that I saw before with some additional information. So we can see how changing the chunk size changes the data I'm sorry, the knowledge graph that gets that that gets that gets created. So chunking is a really important part of that of that process. Okay. Right. So let's move on. So we have, so far, we have taken out unstructured data. We've created a graph from it, and we've modified the graph as well to, to change the chunk size to to, to something that's more appropriate. But what we're gonna do now is we're gonna look at how we define a schema because the graph that we've created is unconstrained. You know? Any entity or relationship that can be created based on the based on the data and it meaning that any entity or relationship that can be created based on the data, that's, been extracted from the text. So this can lead to graphs that are nonspecific, difficult to analyze a query. And, also, it can, lead to, the LLM not finding the information that that you are looking for. So what we're gonna do in this in this lesson is we're gonna modify that pipeline to use a custom schema. So we can provide a schema in terms of giving it node labels labels to look for, relationships to look for, or patterns to look for, and any one of those three or in any combination. So you could just say, I'm a, I'm interested in these nodes, but I don't care how they're connected. Just work it out. Or I'm interested in these nodes and these relationships, but I don't care about the patterns that you put that you put between them or all three. So we are going to do that. And, again, we're gonna we're starting to see a pattern in terms of how this workshop works is, there is a file which has got some starter code in it. Your job is to review that, refine it, modify it, run it, and then review what happens. So this is what this is what your task is to open up that KG Builder schema, code, review it, define some nodes and relationships, modify it, and then run the pipeline again. So, again, just to recap where we are, we are in this lesson here, which is define a schema, and we are here. So if you wanna follow along, you can do that by following these instructions. Again, I'll go through the same process of creating a schema and adding it to my code. I'll quickly do that, and then we'll do any questions. So let's just I'm just gonna tidy up a little bit because I realize that I've got limited screen space. So, this is the program we're gonna open, which is the KG Builder schema, so adding a schema to our graph. I mean, if we look through, most of this is looking familiar. We've got our connection to our database. We've got our LLM. We've got our embedder. We've got our text splitter. But now I've defined a set of node types, and I'm passing those node types to my schema. So I'm saying, well, the things that I'm interested in is technology concepts, examples, and processes. Those are the things I mean, am, I'm I'm interested in. And that will instruct, the LLM to look specifically for those node types when it creates the graph. But we can do more than that. We can create more descriptive, note description. So we can you know, we saw a technology example, concept process. We could add in challenge, benefits. We can also include descriptions of what that is. So, for example, benefit, a benefit advantage of using a technology or or an approach so we can start start to be more specific about the data that we need, yeah, about the data that we need. So I am going to go through the process of just updating this with all of that information from the from the graph. So I'm gonna say, actually, I want all of these node types. We can also define relationships. So okay. Well, the relationships I'm interested in is related to, part of, used in, etcetera, etcetera. So I can again include them in as part of my schema. And then I can say, well, actually, I've defined some nodes. I've defined some relationships. How do I want them to work together? So I want to know things like where a particular technology is related to another technology or a concept is related to a technology, where example is used in a technology, so on and so forth. And we can be as explicit as we want about the patterns that we want to find. And, again, really, really helpful if you are looking for specific data within specific data within within your documents or any unstructured data. Okay. So I've defined a set of nodes, a set of relationships, and a set of patterns. I am going to add those to my schema. So I pass that to my schema. So I know I've my KGBuilder has got that text splitter we added before. It's also got a schema of nodes, relationships, and patterns. And I can now I can now run the program. Right? I should I should probably delete any nodes that already exist. So match n. Delete any nodes, and then I can recreate my graph. So I just run it. So, again, it's gonna run. It's gonna use the LLM. It's gonna use that schema that I've defined to find to find those nodes and relationships that we are looking for within the graph. There's And there's sort of something I wanted to wanted to pull out at this point. The the the important piece in that point to note here is this number of nodes to resolve. So six, thirteen, 36. This represents the number of nodes that have been created, that have been created by the KG pipeline. Now when we ran it completely unconstrained with a with a big chunk size, we got six. When we changed the chunk size, we got 13. When we provided a schema, we got 36. We got more. We got more entities and more nodes. So by defining what we are looking for, we get a much, essentially, a much richer but also more concise graph. So and, yeah, we can run the same queries that we saw before to review that graph. So now we have if we look at our node types, we have challenges. We have examples. We have processes. We have technologies, resources, and concepts. We have the chunks that we will the node types that we were looking for. And we also have sets of relationships that that decide, you know, process summarizing data is part of the technology large language models, which is a fact which is true, right, based on the information we've got. Also included, if we have a quick look at the doc the the counts, we can see, again, one document, nine chunks, but 31 entities. We have increased the the usefulness or the information within our graph by by changing the chunk size, by define by defining a schema. Okay. I'm gonna have a quick pause, and we'll do some questions and see if if any if there's anything we need to talk about before we move on. Yeah. I see a couple of questions. So or a cup some people are commenting that it's a bit hard to follow your talking and then doing the exercises. Don't worry. I mean, right like we said in the beginning, this is recorded. And if you if you wanna just look at what Martin is doing today and then do it, especially the code part, in your own pace, I I can understand that sometimes it's a bit quick for for people to follow you juggling between Windows and code snippets, so you can totally do that and maybe focus on what's what's being said at the moment and then answer. Ask some questions for clarification and continue the the exercise maybe afterwards to to make it a little bit easier for you. Yes. I would agree with that. All of the information, hopefully, is in front of you. What I would say is, you know, don't try and follow along with me changing the code. All of the code's in front of you where you where you can, you know, copy it so you don't have to type it out and things like that. Right? Yep. But, Cool. yes, as Alex points out, this content's going nowhere. You've signed up for GraphGalemy, you were enrolled in this workshop, but you have this content forever. Absolutely. Yeah. So if there's things you wish to go back to, please do. Yeah. Let's maybe do one or two questions here generally from here's one from Suendu. Suendu asks, how is this scalable? Suppose I have millions of documents to index, then there will be a million LLM calls, and that's gotta be expensive. I I yeah. I think it depends on depends on the LLM that you are using. There are other approaches to entity entity extraction as well. There is a blog post from a colleague of mine called Will, which talk talks about, you know, if you if you have relatively simple or relatively relatively structured structured data, yeah, you can you can use other approaches. You can also use, if cost and time is a challenge, local models as well. We don't. have to use, you know, the big OpenAI GPT models. Actually, entity relationship extraction works really well with smaller models as well. Super. And then there was one from Hai Chen earlier when you talked about graft pruning. And they asked, And do we need to prune the graft? we don't have to prune the graph, but sometimes you may want to. So, for example, let's say there are two entities which are the same person. Yeah. So we've identified the same person within within a text within a text document, but, essentially, two nodes have been created. Maybe there's one maybe there's a node that represents Martin o, but another node that represents Martin o Hanlon. We can go through a process of pruning and merging and getting rid of things we don't need. If we need to be, you don't have to. Yeah. Absolutely. Yeah. An optional step. Okay. Yeah. Okay. Yeah. Good to know. And, again, because it comes up a couple times, the Neo four j URI and all the information, please don't copy or type what Martin is sharing on the screen because this is specifically for his instance. You have all and as long as you enrolled into GraphAcademy because your own URI and your own information, and you need to copy that from there. Otherwise, it's not gonna work. And, yeah, please do that. Yeah. Yeah. So the, yeah, the the all of the instructions to set up your environment is in this GraphRack for Python lesson. That's gonna include the information that you need. Okay. Okay. So where where did I wanna leave you? I want where I wanted to leave you at the end of this lesson was that observation that by by changing our chunk size, by providing a schema, we are we are allowing ourselves to create a more more useful, more concise graph that fulfills the requirements that we have. And, you know, we can define as many nodes, relationships, and patterns as we think is appropriate, to, you know, fulfill our fulfill our use case. It's it would be very unusual scenario where you would just simply take some data, throw it at an LLM, say, did you get out? Yeah. Because most of the time, you know what you are looking for, yeah, or you have an idea of the things that are important. Okay. So, let's move on. So we've we've gone through this process of defining a schema. What we're gonna do now is we're gonna add some structured data to the knowledge graph as well. Okay. So the knowledge graph that we've created so far is entirely based on unstructured data that's extracted from documents. In a lot of use cases, it would be normal to have structured data alongside our unstructured data. So I don't know. Let's say it's a, a collection of legal case files, all as PDF documents. We we we may have the name of that document, who last edited it, where we got it, all of that information. Right? You know, we have metadata or structured data about our unstructured data. And combining our structured and unstructured data together can help enhance our knowledge graph's sort of richness and usefulness. I will point out is, like, quite often, within within knowledge graphs and graph terminology, the unstructured bit part of our graph is often known as the lexical graph, while the structured part is known as the domain graph. I tend to use the word structured and unstructured, but if you hear those terms, that's where they come from. Okay. So our repository has got a CSV file in it that's got some metadata about the lessons the documents were created from, and we're gonna use the CSV file as input and the structured data. So if we look at the CSV file, well, first of all, it's got the file name, so, you know, what what the name of the file is. It's got what course it came from. So this this, this particular lesson came from the, the GenAI fundamentals course. Got what module it's part of. In this case, it was module number one. It's got what lesson it is, which in this case, it's what is generative AI, and it's also got your URL. So we have some metadata about our, unstructured data, And we can use that to enhance our our knowledge graph. Okay? So, again, your task is, right, we are gonna open up this file. We're gonna review the code and see how it works and how it uses Cypher to create nodes from the unstructured data. Then we're gonna run the program to create the graph with that with that structured data, and then we're gonna review and explore how the structured data gives us new ways to new ways to query and explore the knowledge graph. And, again, all of the instructions are here. So here is loading from the CSV file. Here's the file you're gonna open. It's get it's getting quite long now. It's gonna talk about the differences between, this new this new program and the one you were working on before, and then we'll we'll go through and create this graph for us. Again, I'm I'm gonna walk through the process. You do not have to follow me. In fact, I would advise you not to follow me. Follow the follow the instructions because you're gonna be able to clearly see the snippets of text it refers to. Okay. So the program we are looking back is this one here, KG structured builder, knowledge graph structured builder. So, again, hopefully, it's looking familiar. We've got our connections in Neo four j. We've got our LLM. We've got our embedder. We've got our text splitter. We've a load of things that define our schema, so the things that we're looking for, and we've got our KG builder. So this is where our change starts. So the first thing is is that, we're gonna load this c s this CSV file. And our CSV file, is here, this docs dot CSV file. And we can look at it, we can see it's got file name, course, module, lesson, URL. That's our structured data. So that's the data it's gonna use. We have got a Cypher statement. So what a Cypher statement is gonna do is it's gonna match the document that we created using our, knowledge, our knowledge graph builder, and then it's gonna create a new node. It's gonna merge in a new node for the lesson, and it's gonna set the lesson name, the lesson module, the lesson course, and then it's gonna create a relationship between the document. So that's the document for the lesson, and the document is a PDF of this lesson. So this is the Cypher statement that's gonna create our structured part the structured part of our graph. And then it's a case of, for each document in the CSV file, run the KG Builder. So run the KG Builder for that particular document, create the structured graph, and then print some output. So create the structured graph where you can see it's running execute query. It's running this Cypher query, and it's passing, the parameters to it. So that's our document. I'm again, I'm gonna go through through the process of running it. But, yeah, it's gonna create this graph so we can see documents, chunks, and entities. It's gonna add in this new node, which is this document was a PDF of this lesson, and it's gonna include some structured data. So, again, let's run our graph. So just deleted everything, and I'm gonna go through the process of running this program. Now this will we're gonna we're gonna create the graph of every document now, so it's gonna go through every single document. Previously, we've only been going through one. So it might take a look. So it's gonna take a minute or so. So we can see it's just processing doc processing document number one. But once it comes back yep. There we go. So, yeah, going through creating creating the creating the documents. Alright. So this is probably a good time for questions, Alex, if there is an issue. Yeah. Yeah. There are there are many, many, many questions. Good. So I wanna start maybe with one from Colin. Colin asks, if you have a highly complex corpus of data that spans multiple domains, would it ever make sense to maintain separate graphs and then query across them? Example, one graph for companies, people, departments, organization, and a separate one for product, services, tech stacks, code, etcetera. I suppose I can see the benefit and the downside of having multiple graphs. And actually, you know, through multi DB and I've forgotten. Is it is it called composite query where we where where we query, query? yeah. Yeah. So you you using Neo four j and Cypher, you can query across multiple graphs anyway. I think a lot of those questions have probably come down to things like infrastructure. So for example, if you have one graph that's, you know, has huge memory requirements but very small storage requirements, you might wanna put that on its own machine. The other thing I would say is is that, you know, with things like InfiniGraph, which is a a huge hyper scaled graph graph platform, you could put it all in one graph as well. So that does. property sharding to create enormous graphs. So I think I think the the decision for that question probably comes down to architecture choices rather than anything to do with product particularly or constrict or constraints with the product. No. That's a very long way of saying it depends. It depends. Right? Yeah. Yeah. Yeah. Another question here from Donald is, we are working often with the problem that LLMs or anything we do with AI is not quite deterministic. It's a long, long question, Donald. Sorry for summing this up in one sentence here, maybe. And the question is, is GraphQL helping to get more deterministic kind of responses back from an LLM to avoid the whole hallucination and issue? Yeah. So so, you know, the trick to solving that problem is about, as you know, as we started off this conversation, is about providing better context. You know, if if your approach currently is to use sort of vector based rags to provide context, what you can end up with vector based RAG is huge is huge amounts of data that are not always relevant. Right? If we use one of the benefits of graph RAG is is that we can almost always, provide, a more concise, more factually relevant context. Right? That's why, that's why graph frag sees better results in terms of, outputs, output outputs from GenAI applications. We will look at some of the techniques, to do that in the in the next module around retrievers. Cool. Super. I can go on, but I think we have, some more content to cover. So I'll, Okay. yep, I'll save. some time for the end. Thank you. Alright. So I've I've reached a point where I think we are where we are at, I think it's on the last doc oh, no. It's on the last but one document, which is so it's currently processing this TextEcipher document. It's got one more to do. But what I what I'm gonna do is I'm gonna leave that running through the last document. It's now on the last document, And we can see that it's that the number of entities that it's creating is going up as it adds more data to the graph. And we can Maybe now now I'm seeing a couple of questions that are answered around the same kind of topic of people see different numbers than what you show on screen. Yeah. Yep. Yeah. I mean, that that is that is one of the benefits and one of the downsides of generative AI applications more generally. Right? They are nondeterministic. There is always an element of randomness. Right? Yeah. You that yes. I'm that is entirely expected. Yeah. Yep. Good. And, you know, even with so we are using the LLM we are using, we are setting its temperature to zero to give us the most deterministic response. But, again, yeah, seeing seeing differences is is is is entirely expected. Yep. Alright. And and, Cool. yeah, it's now finished. So, you know, let's have a quick moment. And what we're gonna do is we're just gonna have a look at this graph. So we've added this structured data. We can now do things like find lessons that cover a specific, technology. So if we look at this, at this Cypher query, what we're gonna do is we're gonna go off and find a node that has a technology label. That's gonna come from our unstructured data. And we're gonna use that technology label, which happens to be anything that contains the name knowledge graph. We're gonna go off, find knowledge graphs. We're gonna use the chunks to find the documents, the documents to find the lessons, and then we're gonna return the lessons. So in essence, what what I get back is these are the lessons within that content within within that content that relate to knowledge graphs. So, you know, what is what what is RAG, what is a knowledge graph, and then constructing a knowledge graph. Right? All of that information. So all of these lessons relate to knowledge graph technologies. So we can start to use our knowledge graph to define facts about so for example, we know that this lesson contains information about knowledge graphs. That is a fact that we can determine, and we can use our graph to to explore those those relationships and gain and gain that information. So let's look at it another way around. So if we say, okay. Well, let's find our lessons. Let's find the chunks that make up those lessons. And then for each of those chunks, tell me the technologies that that are, that are, referred to and the concepts that are referred to. So now I get back. So here are my lessons. Here's my URLs. Here's my technologies, and here's my concepts. So I'm gonna pick one. There is a lesson on providing context. There is a concept called supplying context, which you might expect. Right? There's also, in the same lesson, there's a technology that relates to retrieval. It relates to the technology retrieval augmented generation. So again, we can use this structured data the other way around and say okay, well, if I have this lesson, what, technologies in well, in this case, what technologies and what concepts are referred to within that lesson? So by adding our structured data, we, get get a, a more holistic view, allows us to link the things together. So it allows us to say, in this case, lessons tell us how, lessons tell us how they relate to the things that we're looking for, in this case, technologies, concepts. Okay. Alright. So we are gonna move on. There is a there is an open ended task here that I would love to see people do after the event, which is like creating your own knowledge graph. There is a huge data a huge collection of open datasets. Kaggle's really good for them. The resume dataset is a great dataset if you're looking for lots of unstructured data. Have a go at creating your own knowledge graph. I would go through the the the process that we've been through today, which is, you know, try and find the data that's try try and find information just on, you know, unconstrained. Don't give it a schema. Just see what comes out, and then work through and actually go, okay. Well, you know, if I based on my on my dataset, what are the things that I'm interested in? Define some nodes, define some relationships, define some patterns. It's a good exercise to go through. And, you know, with the code that you've got in front of you now, it's it doesn't take it doesn't take take long either. Okay. So, let's just recap what we have done so far. We spent some time talking about what generative AI is, what is RAG, what is graph RAG. We have built up a knowledge graph. So we have constructed a knowledge graph from structured and unstructured data. What we're gonna do now, for the last, sort of half an hour of our workshop is we're gonna look at retrieval. So we said right back at the start, retrievers are really important to help us find context. So we've spent some time, and we've created a knowledge graph from our unstructured data. That's gonna that's given us a data source to find information from. We need to create some retrievers now that allow us to access that knowledge graph and find relevant information. So we're gonna look at how vector, vector plus, Cypher, and how TextEcipher retrievers can be used. They are not the only retrievers that you can write to interact with your knowledge graph, though. Those are the ones that we're gonna explore in in this session. Okay. Alright. So let's just talk a little bit about vector RAG. So we have a look at retrieval augmented generation and that role of, retrievers in finding relative, relevant information. So one of the challenges in RAG is if you have a user who's asking a question, how do we find the correct information to pass to the LLM? We can't pass the whole knowledge graph because it would be enormous, and the LLM would probably be just as confused as passing it nothing. Right? So we we need to, we need to be concise. Right? So one of the things that we're gonna look at is how we can use semantic search and vector indexes to help us find that information, and then we're gonna expand on that to, turn our vector rag into a graph rag vector plus graph, retriever. Alright. So, again, let's just go over some terms. So when I talk about semantic search, I mean search that tries to find data, based on intent and contextual meaning rather than keywords. So if I'm looking for fruit, yeah, you know, in traditional search, I might get results about fruit. Sorry. If I'm looking for Apple, I might get results about fruit. However, in when we look at semantic search, the engine tries to gauge the context. You know, are you searching about fruit, tech company, something else? And through semantic search, we can create results that are tailored on term and perceived intent. And, typically, the way that we do that is through vectors. So, again, let's just make sure we all understand, what we mean. So we can represent data as vectors, as a list of numbers. So a point in three d space, we can represent with a vector of three numbers. And we can use vectors to represent anything, Different types of data, text, images, audio, video, whatever you the shape of a graph. Right? Whatever we think is appropriate. And the number of the number of numbers the number of numbers in our vector, yeah, describes its dimensionality. So this particular vector that represents a three d space has a dimensionality of three. And we can then create create dimension very, very high dimensions. We're gonna be using vectors with 1,500 plus dimensions. So the way that we create a a vector is through what's typically described as an embedding model. And embedding models create embeddings. Right? When we use the term embedding, we are talking about a vector that is being created for a particular purpose. Right? So if we use the word apple and we create an embedding for the word apple, it might have dimensions such as fruit, technology, color, taste, shape, and those are just, a few. Yeah? And we can compare vectors for for, for different terms. So if we compare the vector for apple to other words such as fruit, technology, color, shape, yeah, we can see, how similar they are. Right? So we create embeddings, which are vectors for all four different search terms. So let's say we have an embedding for fruit and an embedding for apple. If we look at the distance or the angle between them, we can gauge how similar they are. So words with similar meanings or context will be closer together while unrelated words are further apart. So when we use semantic search in vector based RAG, typically, this is what we do. We take our source data. Yeah? We push it through an embedding model that creates a vector representation of that data, and then we then store that embedding in a in a vector index. And the embedding model we use describes how that search works. So there are different different embedding models for different purposes. The one we are gonna use is specifically looking at semantic searches, looking at similarity, between, terms, phrases, whole sentences, whole documents. Alright. So RAG. Let's go back to this. User submits a question. The application passes that that application that question to a vector retriever. Vector retriever creates an embedding of the question. So user asks a question. We'll create an embedding of that question. We take we create an embedding for that question. We then use that embedding to search against our vector index, to then which we can then use to provide results back to our application. We can use those results to provide a better enhanced prompt to our LLM. So I think this is better demonstrated through doing it. So let's have a go. So we're gonna move on to this question here, this, lesson here on vector and cipher retriever. Alright. So the chunks in our knowledge graph include vector embeddings. So let's just go slightly off piece for a moment, and we'll go and have a look at them. So if I see match, see, chunks on I'm looking for my chunks. Return. Say, just give me the, limit 10. Just give me the first ten ten ten chunks. So these are the first 10 chunk nodes, in my graph. And if I click on them, I can see the properties. So I can see the text. That's great. I can also see the embedding. So each one of these chunks, when we when we ran it through the knowledge graph builder, it sent it to an embedding model, and it created this embedding for us. So we can use this vector to help us find relevant chunks and chunks of information. But in order to do that, we need to create a vector index. So, if you're, with me so far, again, we're on this lesson here, which is vector plus Cypher retriever. We need to create a vector index on our chunk on our chunk nodes. And this is the Cypher to do it. Correct vector index. We give it a name. We tell it what what node we wanna, create the vector index on, what property on the node, and then we give it some information. So the embeddings that we created have 1,536 dimensions. They were created by the text embedding AIDA two model, which is one of OpenAI OpenAI's model, and we're gonna tell it what similarity function we want to use. Cosine or Euclidean. Everybody tells me that if you're doing similarity search, cosine is the one to use. But, yeah, let's create our our our index. There we go. Created our index. Done. We've created that index. We can now use our index. So we can search that vendor effect index by creating embedding for a search term. So if we're gonna find if we wanna find chunks that relate to retrieval augmented generation, we need to create an embedding of this piece of piece of text. And that's what this function does here, gen a I dot vector dot encode. It encodes that using the OpenAI model, and we need to pass it our token. What we then do is we we we get our user embedding back. That's the embedding for a question. We can then use our vector index to query the nodes. This is the vector index I want. This is the number I want back, so I want the top five. And then give me back the node and the score. So if I if I if I if I run this to start with, it's gonna moan at me because oh. Excuse me while I get myself ready. Here we go. So this is the query I want to run. I need to put my OpenAI key in there. So same a API key that you use for your program. If we pop that in there, we can use that to create an embedding for this phrase. And if I run it, it's gonna use that vector index. So let's just make this a little bit bigger. So it's come back and said, okay. Well, the first node node I found is about what is rank, which makes sense. Right? And it's given us a score. The score is between zero and one, one being exactly the same, zero not being the same. And then it's come back and said, okay. Well, I found the second result, which was this piece of text retrieval augmented generation process. I found another one which was vector rag, this piece of text. And, again, it's given me a score for each one of those notes. What this has done is it's created embedding for that phrase. It's then used the embedding for that phrase to query the vector index and return the results. So I don't know. Let's change this to what is a hallucination? Again, it's gonna try and find it's gonna use that term to find the most relevant information. Oh, it's come back with a, check your understanding about this. This lesson includes hallucination, misneeded or baseless phenomenon known as hallucination. So, again, we can see that it's that it's used this phrase, to search that vector index. Okay. Right. So we are now going to use that vector index to create a, a vector and Cypher RAG, sort of simple application. So, again, same process that we've seen before. We're gonna open some a starter program. We're gonna review how that code works. We're gonna do a vector similarity search to find the most relevant chunks for a query, and then we are gonna get a response back. So, again, we are on this lesson here, which is vector plus Cypher retriever. And if you haven't already, you'll need to create create your vector index, and then we are gonna open up this program here and work through this activity. And then, yeah, all of the information is there. I'm gonna take a breath, and we'll do some questions. And then I will, again, run through the activity as well. Hello, Alex. Hello, Martin. Let's look at a couple of questions. One comes in from. They ask, is there a way that the the tool he says, but I think probably means just, you know, the the overall application, Mhmm. comes up with recommended notes and relationships themselves without providing example schema. I'm not sure I understand the question. I'm really sorry. You showed earlier, we provided a couple of, you know, entities, relationships. So I was Steve. Right. Okay. Okay. Okay. So so, yeah, so talking about the knowledge graph builder process. Yeah. Yeah. Yeah. I mean, yeah, you you, the the first activity we activity we built we did when we built the knowledge graph was we didn't provide any schema at all. No no's. No relationships. No patterns. And we just said go go go crazy. Right? Find whatever you can. Yeah. That's a, it's it's interestingly, it tends to be the approach that I take if I am, trying to learn about a piece of data because it tends to find things, and you go, oh, that's interesting. Yeah. But, yeah, almost always, it becomes more useful to refine the schema. The other thing you you can do is you is that you don't have to define a complete schema. You can just say, okay. Well, I'm only interested to say, I'm only interested in these nodes, these, node types, but I don't care what relationships. You know? Always come back with the with these nodes, but you can create whatever relationships you want. Yeah. Is yeah. Yeah. yeah. I I there was a similar question about some somebody I have this huge catalog of of of documents where I don't quite know what it's all about. Maybe use a you know, another pipeline, an LLM maybe analyzing general gist of of your content and then, you know, providing a couple of suggestions for this and then take it from there into a to the GraphQL pipeline. Yeah. I would say, you know, treat it as an iterative process. You know, start off with a small, with a small section of sample data. You know, maybe leave it unconstrained or maybe, you know, there's there's options around using strict enforcement or not, and and and build it up over time. Yep. Cool. One more question maybe from Ricardo. If we ingest documents that are similar but, let's say, different versions of the same solution, could that probably confuse the LLM when retrieving the data, like overlapping? Example, install upgrade of a product. I have version twelve and fifteen. I mean, potentially, but I think that's probably where a graph would help you, because, you know so I don't know. Let's say versions of documents are really important. Right? You may specifically instruct, you know, through your schema, always find the version, right, and, you know, give a very detailed description of the version version you that you're looking for, but also define properties such as the version number, you know, and how it and how it relates to to other documents. I the other thing that we perhaps didn't didn't touch on is, when we create our our knowledge graph, it's gonna do that merging of entities. So let's say the same document refers to, the same entity. You're only gonna end up with one entity. Yeah. And and there are as part we we do we haven't covered it in this workshop. But even just part of just using the simple, knowledge graph builder, there are, like, post processing sort of merge techniques you can use as well. You know? Yeah. And I maybe to add here, what we do is is is a bit of on the, in the inverted commas, rough side because we just use unstructured data to get data into a knowledge graph. You most likely have a bunch of structured data you could use as well to. work on and to include into your knowledge graph or to use some kind of an ontology or something else. And that will then guide as well a little bit the creation of your knowledge graph and then help it to avoid these things like, Okay, I have a version. I have certain dependencies or certain connections that are already You know them, and you can create them, and you can define them, and then that linkage is created, and then you use that to help out and to avoid these duplicate or that they didn't know, okay. There's version 12 and there's version 15, and, obviously, these are two different things. And if you are having a problem with version 12, look at the version 12 documentation, not at 15. Mhmm. Yes. Yeah. Okay. What I'm. gonna do is I'm just gonna go through this activity, and we'll talk we'll talk about about what it what it does. So let's have a look at the code. Again, it's just tidy up so I have because I've got limited screen space. So we're gonna look at this vector cipher rag. So, again, some of this is gonna look familiar to you. You know, we can see our connection to our database. We can see our embedder. We need an embedder because what we're gonna do is we're gonna turn our question into an embedding. We need to define a retrieval query. So this is the information that comes back from the graph after it's done its vector search. We create our retriever. So this is our vector cipher retriever. We tell it we tell it what database to use, what index to use, what embedded to use, and what retrieval query to use. We're gonna need an LLM. Our LLM's gonna turn our question, into gonna we're gonna use that LLM after we've done our vector retrieval to turn our question into a into a response. And then we're gonna create this Rag pipeline. Our Rag pipeline needs two things. It needs a retriever, and it needs an LLM. So our retriever is our text is our vector cipher retriever, and our LLM is this is that the LLM that we've just created. So our where can I learn more about knowledge graphs? So that's the query that we are passing through, and we're gonna use our rag graph rag pipeline to find that information. Alright. So let's just give let's give that a run. So it's gonna go through that process. It's gonna use that question. It's gonna turn that question into an embedding. It's gonna use the vector index, to find the most relevant information, and then it's gonna use that context to provide information. So you can learn more about knowledge graph in the Neo four j blog post, what is a knowledge graph available at this link. That content is taken directly from the the knowledge graph that we have that we have created. And, actually, we can see that by if we print out the context so if I just modify my program. So at the moment, I'm just printing out the response. If I print the context as well, so this is the information that the GraphRAG pipeline used in order to be able to come up with that information. We're gonna get a big context window back, but we can see. So this is the this is the answer. Here is the context. And, yeah, right at the top, the top most responses, you can learn more about more about more in this near near for j for j blog post. So we can see that this that this response is gained directly from the context that we have just used. Okay. So at the moment, our Cypher retrieval query, so this is the data that comes back after we have done a search, is relatively simple. Now we can update that retrieval query to be more sophisticated. So at the moment so if I just go back to my program, our retrieval query basically just turns returns the text that we found using the vector index and the score. That's it. But we can return more information. So let's update that. Because what we are gonna return is we're gonna return not only the text and the score, but also the the lesson URL. And we're gonna use our knowledge graph to also return things like the technologies and the concepts that are part of those lessons. I'm just gonna not print out the context because it ends up being quite quite big. But I'm gonna ask you the same question. Where where can I learn more about knowledge graphs? So now I get back a a different response. I still get back you can learn more about knowledge graphs in the in the blog post. Additionally, you can explore further lessons on knowledge graph specifically here. So it is not only giving me information that it's found just from, the vector, just from the text, but it's also giving me information that it's found from the knowledge graph itself as well. So, we can go further than that. Right? So we can do things that we can generalize that query. So, for example, if I run if I run this if I run this query I'm sorry. I'm just trying to tidy up a little bit. So this query here, this outputs concept generative AI is related to the techno to the technology. I don't wanna be able to scroll that way, but I don't oh, there we go. Related to the technology GPT. So we can use our knowledge graph to generate to generate facts. Right? So we can create a more sophisticated retrieval query that uses our knowledge graph that uses our knowledge graph to provide additional context. Again, that's I won't worry too much about what this does, but, essentially, what this is doing is is this query is creating these concept gen AI related to technology GPT. It's creating those facts. So, you know, let's ask different questions such as what is hallucination, and how can I avoid it? Hallucination in the context of gen AI models. To avoid hallucination, you can provide additional context in your prompts. Again, so I've created a a a better response by including additional information. I will let's just run that again and print out the context just so we can see the context that's that's that's that's being used. You know, it's a big wall of text, it can be quite difficult to to interpret, but let's just pick one out. The concept limitations of LLMs, yeah, has the challenge of hallucination. Concept limitations we've we've got some repeating repeating patterns here, which I suspect is my cipher rather than the graph. Concept limitations has has the challenge of access to data. So we can see that we have expressed facts within our context by using our knowledge graph. Okay. Let's I'm just looking at time. So I'm just gonna go on to the last lesson that we're gonna cover in this workshop, and then we'll do a quick round round of questions and finish on time. So, again, just to recap, we're gonna move on to this next lesson, which is this Text retriever. So Text Decipher allows you to create graph rack pipelines, that can answer natural language questions by generating Cypher statements. Right? Cypher Text to Cypher is super handy if you're looking to get precise information out of your knowledge graph, and we're gonna explore that in this session. So, again, there is a task to do, which is we're gonna open up this program. We're gonna review the code. We're gonna look at how it works, and then we're gonna experiment with different questions. I'm gonna run through it through through it through it now, and then we'll have a few questions at the end. So, again, I'm sure you've worked out the plan. We open up a program, we review it, we amend it, and then we carry on. So we're gonna open up this TextEcipher RAG Python program. So let's just have a quick look at what this program does. Again, so we can see things that we already know. We've got a connection. We've got a connection to the database. We've got an LLM. We've got some Cypher examples. Now what Cypher examples allow us to do is it allows us to provide information to the retriever that says, if you get a query that's a bit like this, find a node with the name of use this sort of cipher, so match node where two lower node name contains no two lower name. Right? Essentially, what I'm doing here is I'm saying if you are asked to search for a name, use Cypher, which is case insensitive. That's what the, the to lower is doing, and the contains. Yeah. But we build our retriever. We give it a connection. We give it a database. We give it an LLM, and we give it a set of examples. And we create our our GraphRack pipeline, and we say, how we and then we define a query. How many technologies are mentioned in the knowledge graph? So this is the query that we are that we are looking to to answer, and we're gonna put that through our GraphRag pipeline. And what our application should do is, we'll see this run. So it's come back. This is our response. There are 818 technologies mentioned in the knowledge graph. And the way that it has generated that information is it's basic is it's created this Cypher statement from the natural language query that I put in, which was how many technologies I mentioned in the in the knowledge graph. It's run the Cypher statements, come back with this context, and it's we use that context in order to be able to answer the question. And, again, Text Decipher, incredibly useful when you're looking to get defined, specific information. And I think there are so, you know, how does Neo four j relate to other technologies? So that information is contained within our within our graph. And if we run our Text Decipher retriever, hopefully, we should see that it's what it's gonna do is gonna take that natural language question, generate some Cypher, and return us a response. Okay. Oh, here we go. Right. So this is our response. Neo four j relates to other technologies through its integration, various data processing. Additionally, Neo four j can work with alongside vector retrievers because it's run this Cypher statement here. So it says match technology name Neo four j. Find out how use the related to relationship to find out how that is related to other technologies. And it this is the context it has returned. And this context has come back from our knowledge graph, hasn't come back from our, you know, vectors. It is information that we have defined, you know, from our unstructured data. So RAG relates to knowledge graph. Again, the the context is slightly difficult to interpret, but we can see the information here. So related technology generate generative AI, related technology large language model. So we can see how it has used that context to provide us with this information. Neo four j can also, integrate with technologies like GraphAg, which can can can clear, which combines graph database with retrieval augmented generation techniques to enhance information retrieval and and generate generation tasks. Okay. So I'm I'm gonna leave you to experiment with with that retriever. Maybe ask some ask some questions of your own, and we will have a last round of questions, Alex, if you don't mind. Oh, Alex is gone. Oh, there he is. Just takes a little bit of time to back back up here. So, yeah, thank you very much, Martin. And I I start with one that come up in chat now from from two different people, and I know it's probably gonna be it depends. Depends, right here. yeah, Yeah. it it's it's tricky. You know, do we have any any recommendations for, you know, embedding models or small language models for for text to cipher or for certain tasks when when running these these these, and and if we have any any recommendations, what is performing, you know, best or what is showing good good good results. I think you're right, Alex. Unfortunately, it depends. I think I I think when I look at Text Decipher, and, this gives me a chance to actually there's a small segue here. So there is a a module here about agents. And if we have a slightly longer workshop, we we go through this process where we whereby we create an agent that can use different types of retriever to answer single singular questions. But what I would say about text about Text Decipher retrievers is is, particularly when you're using them within an agent, you can make them more specific. So at the moment, our tech the Text Decipher retriever that we have created essentially looks at the whole of our knowledge graph schema to create, to understand the schema, to turn our natural language question into a cipher statement. Now if you have a larger graph or you have a more complicated graph, actually, one of the things that can be really useful is to constrain your retriever to say, what your retriever this TextEcipher retriever, your job is to just find, I don't know, customers, accounts, and products. Right? Your this TextEcipher retriever, your job is to find information about sales agents and their bonus structures. I don't know. Yeah. But but segment segment the graph to create a more, a more, specific, text aside for retriever and have many of them. So you can have as many as you want. Right? You know, you don't just. have to have one. You could have ten, twenty. Right? And when used as part of an, you know, an an agent that can pick the right one, that has that has big benefits. In terms of, like, my own personal recommendations for, like, models for doing TextDecipher, Again, based on my experience, so there is always the case of it depends, I find that the smaller models, tend to work better. So if I'm looking at OpenAI's models, you know, things like GPT five nano. You know? So where you're asking much because you're asking a much more specific question, Cool. generate me a Cypher statement rather than something that's much more vague. And one one practical question comes in from DB Endo. How long will we have access to this lab? So this lab, as long as you want. I'm I'm not gonna as long as you want. You. have access. to this to this content forever. Yeah. The only the only thing is we're gonna deactivate the OpenAI key at the end of this workshop. the OpenAI key. So. you. have to find your own or. use something else, but other than that, everything is available to you as long as you like it. Yeah. Okay, maybe we squeeze in one more question or two. This one is more of a comment coming in from David. David says information is often siloed between unstructured text, lexical graph, and structured database or APIs, domain graph. Yeah. So, yeah, a good point. I think oftentimes these things are a little bit distributed, siloed. With Neo4j and with our approach, I think we try to mitigate this issue and try to make relationships and connections between these silos to solve it. Yeah. I was gonna say exactly the same. And, you know, it's been it's a relatively simple example that we have worked through through through today. But, again, one one of the the the point I was demonstrating was by linking our structured data to our lesson to our unstructured data, I. E, our documents, our chunks, and our entities. Actually, we can answer a greater range of questions with greater clarity. Yeah. Cool. And then one question comes from Colin. He says, touching on the idea of temporal graphs and incremental updates, can this type of pipeline extract claims about entities and resolve conflicts? Example, one document says that project status is green, and another two weeks later says project status is red, or the other way around, like when you saw approval processes or something like production chain, or I could think about maybe. looking even at supply chain and say, okay. Once this pathway the recent history now, you know you know, something like yeah. Something So I. I think what I get what Colin what Colin is referring to. I think there's I think you you are not constrained. You can build your pipeline however you want. Right? And your pipeline could do things like before it creates the graph from the from you know, you've had new unstructured data, so new documents appear. Your pipeline could check the to to for the existence of old documents that refer to the same thing before it creates before it creates the graph. Or, I mean, one thing I would say is is that is, by putting your data within a knowledge graph, even if you don't identify it at, you know, knowledge graph creation time, it's gonna be very easy because of the relationships between the between the entities and the documents to know that a status has changed. Yeah. And maybe one thing to add, which has been and I posted this in chat. It's been recently quite popular is this notion of the AI memory or context graph. Mhmm. And maybe this is a solution there to think about, okay, some things have been approved. Give it a mark. Showcase that in your graph. Have previously decision trees modeled in your graph, and that makes some sense for decisions later on or for, how do we say, assessing decisions or making sure what the decision is about. And you can look at that in this way and can think about, okay, there is this whole addition of information and this addition of, yeah, context and memory, what has been done in the past and that inflicts or that influences, rather, Yeah. a current or a future decision. Yeah. And I think going back to Colin Colin Colin's usage, you know, if we have the idea of a document that's approved or not, you know, using that idea of a context graph to describe how and what has happened. You know, I like the idea of, like, you know, documents have a status node and, you know, the status move node moved from approved to not approved. Disproved. Yeah. Yeah. No. That's a that's a great example, Alex. Yep. Cool. Okay. So we have three minutes left. There there was just a couple of slides that I wanted to finish off with. And I know, Alex, you've got a couple of slides to finish off off with as well. Yeah. So I was just gonna make the point. You have access to this content forever. It's not going anywhere. It'll be on Graphical. And if you log back into Graphical, you'll be able to see your progress along this workshop. There are also a huge number of, of hands on hands on courses like this workshop. This workshop, is an abridged version of a few of our generative AI workshop. So if you're looking for, like, more detail or you wanna get have more time to explore and, you know, go into deeper use cases, there are lots and lots of courses courses on our GraphAcademy platform. And there's, you know, created learning paths and free certifications as well. And then I think I'm passing over to Alex. Yeah. Just a couple of things. We are continuing. Obviously, we've wrote to notes, but also we are doing a special stream on Neo four j, the definitive guide of the authors, Luan and Christophe, who wrote the O'Reilly book, and that's gonna happen on the March 12. So if you are maybe more interested in how Neo4j works and what how graph data modeling works, what is Cypher, how do you write Cypher queries, how do you improve your Cypher queries, then I'd like to invite you to join this stream next week. It's happening on YouTube and LinkedIn. Just look for the Neo four j accounts on these pages. And, yeah, if you want to click further, yeah, exactly wrote a note. So as you know, this is part one of five workshops we're running. These are all independent of each other, so you can jump in and out on any of those, but I obviously recommend to join all of them. But we have interesting topics, and if this this is now basically set the foundations to the other workshops that are coming. We do something on agents. We do something on graph neural networks, and with health care, for example, we do something with MCP, obviously, as this has been requested a couple of times, and we do something with memory. So at the very end, on the March 31, we talk about long term memory, agentic workflows for AI. And next slide. As you all registered for Road to Nodes, you also enrolled into Nodes AI. That's happening on the April 15. But if you like this and if you think this was interesting, please share it with friends or colleagues. Spread the word that we are continuing doing this, so I'd like to, you know, hopefully see you all in April. And yeah, one more slide, I think. If you want to meet people in person beyond the online world that we are here now, we have a couple of graph summits happening in Europe and in The United States across the next couple of months. So already next week, we are doing one in Copenhagen and then the week after in Munich. So if you are in any of these locations, come by. There's also gonna be a workshop, so you might meet somebody of the Graph Academy team like Martin and the others, and you can discuss a little bit more your learning path. And, yeah, with that, thank you all for joining today. Thank you for your many, many, many, many questions. Sorry if we weren't able to answer all of those. I'd like to invite you to the graph community that we have on community.neo4j.com. If you have further questions or our Discord server, we'll try to look there, and the community comes together to answer questions there. And, yeah, I invite you to continue the learning and with Neo4j Graph Academy. And, obviously, thank you to Arena for answering relentlessly all the questions in the back end, and thank you very much, Martin, for for presenting today and for leading this workshop. Thank you. It's been incredibly enjoyable. Cool. Well, thank you. Thank you all, and, yeah, see you next time, Bye. next lesson, Yeah. next workshop, or next stream, or somewhere else around the world. Bye. Bye bye.