AI Engineer World's Fair 2025
Intro to GraphRAG
Read the talk
Intro to GraphRAG: Build a Talent Assistant with Explicit Retrieval Logic
Build a skills graph in Neo4j, combine exact overlap with semantic similarity, extract people from biographies, and expose the resulting retrieval patterns to a LangGraph agent.
From a talk by Zach Blumenfeld
Before you start: Basic Python and familiarity with embeddings and LLM tool calling will help; the article introduces the Cypher patterns needed to follow the workshop.
From employee data to an assistant
How do you turn employee data into an assistant that can find skills, compare colleagues, and help assemble a team? The workshop begins with a prepared environment: participants receive numbered Post-it notes, connect to one of two Jupyter servers, and clone the workshop repository. Numbers at or below 160 use the first server; numbers at or above 201 use the second. The temporary login convention is lowercase attendee followed by the assigned number for both username and password. The slides and links are shared in the workshop GraphRAG intro Slack channel. These hosted environments last only for the session; the code remains available afterward.
Zach Blumenfeld develops the assistant in three stages: graph basics and Cypher in Neo4j, entity extraction from unstructured text, and a simple LangGraph agent with retrieval tools. This is an introductory workshop; advanced MCP integrations sit outside the main walkthrough. The prepared Jupyter servers already have the dependencies installed, while Neo4j Browser provides a separate view of the graph as it grows.
To follow the notebook setup:
- Open a terminal from Jupyter’s plus button and use the README’s clone command for the workshop repository.
- Copy
ws.envinto the Talent workshop directory. It supplies the database connection settings and the workshop OpenAI key. - Connect Neo4j Browser using the URI, username, and password from that environment file.
The notebooks execute queries; Browser makes the returned nodes and relationships easier to inspect.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Put domain logic in the graph
A knowledge assistant already has a user interface, models, an agent, and tools that can reach data sources. Adding a knowledge graph introduces a shared model for information arriving from both documents and PDFs and structured sources such as CSVs or relational tables. The reason to do this is practical: if you know the kinds of questions the assistant must answer, you can encode the relevant domain relationships before retrieval begins.
For the talent assistant, the starting relationship is a person knows a skill. Expose that schema and suitable tools to the agent, and questions about talent search, skills alignment, staffing, team formation, and substitutions can become explicit retrieval operations. The graph makes the definition of a match inspectable. This becomes especially useful when a request is broken into several steps: the agent can select operations whose retrieval logic is already defined.
Neo4j represents this information as a property graph. Nodes identify people, places, or things; relationships connect them; properties describe either. A person’s name is a property, but so could be a number, an array, or an embedding vector. Cypher expresses the connections with patterns that resemble the graph itself:
cypher
MATCH (p:Person)-[:KNOWS]->(s:Skill)
RETURN p.name AS person, s.name AS skill;
Person and Skill are node labels, KNOWS is a relationship type, and p and s are variables referring to the matched nodes. The arrow makes the relationship direction explicit. This is enough syntax to read the workshop’s examples without treating the session as a complete Cypher course.
Graph traversal also works alongside search and analytics. Embeddings represent inputs numerically so that related meanings can be retrieved even when the words differ. Neo4j supports range indexes, uniqueness constraints, text search, Lucene full-text search, and approximate nearest-neighbor vector search. Vector search can provide an entry point into a graph, after which Cypher follows relationships. Graph analytics adds centrality, community detection, paths, and graph embeddings; later, the assistant will use skill communities written back into the database.
How large should that graph be? Blumenfeld recommends keeping the data model small where possible, especially for dynamic query generation. At the same time, data points that need low-latency traversal between them belong in the same graph. Graph boundaries and schema complexity are therefore separate decisions: colocate connected information, then choose labels and properties that keep its model understandable.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Load people, skills, and stable identities
The first notebook is in the Talent workshop directory, rather than the repository’s other workshop. After loading ws.env, it reads a table with three fields: an email address, a name, and a list of skills. The dataframe is organized into batches for loading. A fresh participant database should be empty; Blumenfeld’s already contains data from an earlier run.
Before loading, establish the identities used to match existing nodes: email for a person and name for a skill. The supporting constraints matter both for correctness and for indexed matching during repeated merges. There is one important distinction in Neo4j’s constraint semantics: uniqueness alone permits a missing property; a node key combines uniqueness and existence, while a separate existence constraint can also require the property. The intended person identity here is an email that is both present and unique.
The load operation follows the same pattern for every input row. Merge the person by email, update the name, unpack the skills list, merge each skill by name, and merge the connection:
cypher
UNWIND $rows AS row
MERGE (p:Person {email: row.email})
SET p.name = row.name
WITH p, row
UNWIND row.skills AS skill_name
MERGE (s:Skill {name: skill_name})
MERGE (p)-[:KNOWS]->(s);
$rows is a batch of records with email, name, and skills fields. Matching the person on email rather than on every attribute allows a name update to affect the existing identity. Matching skills by name lets many people connect to the same skill node.
Browser can now inspect people, skills, or complete paths. Returning a path exposes the connections directly:
cypher
MATCH path = (:Person)-[:KNOWS]->(:Skill)
RETURN path
LIMIT 25;
The demonstration shows skills including API design, Tableau, and Flask attached to people. KNOWS is not a built-in Neo4j concept: it is the domain’s chosen relationship name, and HAS could have served the same purpose. That choice becomes part of the model’s vocabulary when an LLM reads the schema and generates queries.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Expand from Lucy to a local skills neighborhood
Start with an organization-wide aggregation: how many distinct people know each skill?
cypher
MATCH (p:Person)-[:KNOWS]->(s:Skill)
RETURN s.name AS skill, count(DISTINCT p) AS people
ORDER BY people DESC;
This ranks skills by the people connected to them. It answers a different question from semantic search: popularity comes from recorded relationships, not proximity between embeddings.
Next, choose Lucy and ask who shares her skills. The first traversal goes from Lucy to a skill and then back to another person who knows it. Extend that path once more to retrieve every skill those other people know:
cypher
MATCH path =
(lucy:Person {name: $person_name})-[:KNOWS]->(:Skill)
<-[:KNOWS]-(other:Person)-[:KNOWS]->(:Skill)
WHERE other <> lucy
RETURN path;
Set $person_name to Lucy’s stored name. The progression is Lucy → her skills → people sharing those skills → those people’s skills. It produces a larger neighborhood than the initial shared-skill query, because the final skills need not be ones Lucy knows.
The distinction is useful for team formation: a similar colleague can also bring capabilities beyond the original person’s skill set. In the displayed neighborhood, Scrum appears central because many of the connected people know it. The graph gives a concrete, symbolic definition of the group being examined, and that definition can be changed one traversal at a time.
The result shape controls how Browser can display it. Return nodes or a path to obtain a graph view; return only scalar values such as names and the result is a table. Browser’s table and graph tabs cannot reconstruct a path that the query did not return.
DISTINCT is a separate concern. It removes duplicate returned values, which can arise when several traversal routes reach the same result. It may be unnecessary for the particular path query shown, but becomes important when a multi-hop query projects repeated people or skill names into a list.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Decide how much Cypher the model should write
An audience question introduces the next design decision: how well do LLMs write these queries? Blumenfeld’s answer depends on schema complexity. Simple aggregations and carefully prompted paths over small schemas can work well. For more complicated traversal logic, he recommends limiting what the model must invent:
- Expert tools: Put a tested traversal in a Python function or an MCP tool and let the model choose it.
- Restricted patterns: Let the model select or fill in a small set of permitted query patterns.
- Dynamic generation: Supply enough schema and prompt context for the model to compose the query when the task warrants that flexibility.
He also mentions Gemma-derived Text2Cypher models available on Hugging Face. Neo4j’s April 2025 announcement describes that release family; the workshop itself uses OpenAI models.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Count shared skills, then decide whether to store the result
The Lucy traversal becomes a reusable similarity query when the starting person is parameterized and the shared skills are counted. DISTINCT now ensures that repeated routes do not produce repeated projected values:
cypher
MATCH (person:Person {email: $email})-[:KNOWS]->(skill:Skill)
<-[:KNOWS]-(other:Person)
WHERE other <> person
RETURN other.email AS email,
other.name AS name,
count(DISTINCT skill) AS shared_skills,
collect(DISTINCT skill.name) AS skills
ORDER BY shared_skills DESC;
The notebook also examines shared-skill counts across pairs of people. This similarity measure is grounded in exact common skills: its explanation is the list of shared nodes.
If the assistant repeatedly asks for people with similar skill sets, the overlap results can be stored as person-to-person relationships. The notebook uses the locally retrieved dataframe to merge these derived edges and attach overlap information. The graph can then follow one similarity edge instead of recomputing the original person–skill–person traversal on every request.
That shortcut introduces a refresh obligation. An audience member points out that the stored overlap is static, and Blumenfeld confirms that the query must run again to update it.
| Approach | Retrieval | Maintenance |
|---|---|---|
| Live shared-skill traversal | Compute overlap from current connections | No stored overlap to refresh |
| Stored similarity relationship | Read a derived person-to-person edge | Recompute after relevant updates |
A frequently changing graph may be better served by the live traversal. The database is designed to traverse relationships; materialization is an option, not a prerequisite.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Use Leiden to make skill communities explicit
The next enrichment operates over the person-to-person similarity graph. A Graph Data Science client creates a projection, and Leiden community detection finds groups of connected people. The objective is to identify communities with relatively dense internal connections and sparse connections between groups. Blumenfeld introduces the result as a hierarchy; algorithmically, Leiden refines communities and recursively aggregates them into condensed graphs. Here, the similarity edges give that clustering a skills-based interpretation.
The notebook writes community IDs back to the graph, checks the communities, and plots a heat map of skill frequency by community. The data is randomly generated, so the displayed groupings should not be read as real organizational structure. With realistic employee data, the same analysis could reveal concentrations of data-engineering, front-end, or machine-learning skills. Running the enrichment cells through G.drop completes this part of the notebook while retaining the written community properties needed by later examples.
Whether this work is worth doing depends on the questions users ask. Recurring questions about employee segments or performance within groups can justify persistent communities; similar reasoning applies to customer segmentation and recommendations. A request to find one person with skills similar to another may need only pairwise matching. The benefit of clustering is an inspectable grouping method that the assistant can describe, rather than asking the model to invent groups on demand.
The heat map explains the composition of those groups. Its cells show how often a skill appears within a community, not a separate set of secondary skills. An audience member suggests using the distribution to name communities, which is exactly the kind of interpretation it supports. Tableau or Swift appear in the discussion of one displayed group; product-manager, front-end, and DevOps communities are examples of what more realistic data might reveal.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Make the schema readable to an agent
A schema for an agent benefits from ordinary graph modeling discipline and language that expresses the domain clearly. Person → KNOWS → Skill reads naturally and translates directly into a traversal. Blumenfeld favors simpler models for dynamic query generation, while noting that retriever design, data size, and category cardinality still affect the right representation. Avoid turning every category into a label: hundreds or thousands of labels can make the schema harder to work with when properties would suffice.
Schema context can be assembled by retrieving node labels and relationship types into a JSON-like representation. For a reasonably stable model, enrich that representation with descriptions of properties, labels, and relationships, including guidance on where information belongs and how to retrieve it. Include actual patterns such as (p:Person)-[:KNOWS]->(s:Skill) alongside the descriptions. The model then receives both a vocabulary and examples of how its terms connect.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Embed descriptions, then make similarity traversable
Exact overlap is only one way skills can be related. The next dataset contains skill names, descriptions, and precomputed embeddings. The descriptions are the useful embedding input: short names such as R and AWS provide less context than an explanation of the capability they represent. Blumenfeld describes the workshop’s text-embedding-ada vectors as having 1,536 dimensions. He then loads the vectors and descriptions onto skill nodes and creates the skills vector index.
Searching from the stored Python skill retrieves ten relevant skills. The displayed results include Ruby, Java, pandas, Django, and PyTorch, with Blumenfeld acknowledging that some are more useful matches than others. The next example starts with wording absent from the database: LangChain embeds API coding, and a thresholded vector search returns API design and JavaScript. Semantic search therefore connects a user’s vocabulary to the graph’s existing skill entities.
The notebook then stores semantic similarity as scored relationships between skills. This makes the embedding-derived connections visible and traversable. The displayed groups include Azure, AWS, and cloud architecture; Flask and Django; Tableau, Power BI, and data visualization; Java, Scala, and Kotlin; and Python with pandas. These connections can support clustering and customized retrieval scores as well as visualization.
Persisted edges also allow explicit curation. Blumenfeld proposes removing a Java–Python connection if it is inappropriate for the application. Subsequent retrieval that follows those stored relationships can respect that decision. This changes the graph’s traversal policy; it does not require treating the embedding’s original notion of similarity as the final authority.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Combine exact evidence with semantic expansion
Semantic edges become more useful when combined with exact shared-skill evidence. A custom person-similarity query can weight these contributions separately: recorded overlap provides direct matches, while semantic connections recognize related capabilities. The application can expose this as several operations—find similar skills, then find people—or couple the logic in one query when a particular similarity policy should remain fixed.
An audience member asks how a natural-language request chooses the right entities and retrieval path within a larger ontology. Blumenfeld defers the concrete answer to the agent module, where the tools define those entry points. For now, the notebook’s larger query demonstrates the retrieval policy itself: balance semantic proximity against hard skill overlap to control which people count as similar.
The vector index does not have to live in Neo4j. An existing Postgres deployment or specialized vector database can remain part of the architecture.
| Vector location | Main consideration |
|---|---|
| Neo4j alongside the graph | Avoid cross-store synchronization |
| Existing external store | Avoid an unnecessary migration |
Blumenfeld suggests that colocation could theoretically reduce query latency, but supplies no measured comparison. The deployment decision depends on infrastructure-specific cost per performance.
The final graph-basics demonstration controls how far semantic expansion may go. A variable-length Cypher pattern permits zero to two semantic hops between skills, and the query also unions in the direct shared-skill pattern. Matthew knows React; John knows HTML; JavaScript provides a semantic bridge between those skills. Other connections need only one hop. Bounding the traversal makes the breadth of similarity explicit: a person can match through the same skill, a directly related skill, or a related skill reached through an intermediate node.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Turn biographies into the same graph model
The second module replaces the structured input table with two biographies. It reconnects through the same environment file and checks the database before extraction. At the start of module two, the workshop connection check expects 154 nodes after the preceding setup. The extraction target remains the same simple domain: people with names, emails, and named skills.
Pydantic expresses that target as nested Python objects:
python
from pydantic import BaseModel, Field
class Skill(BaseModel):
name: str
class Person(BaseModel):
name: str
email: str
skills: list[Skill] = Field(default_factory=list)
class PersonList(BaseModel):
people: list[Person] = Field(default_factory=list)
A richer model could put proficiency on the KNOWS relationship. In that case, the person would contain relationship objects, each holding both the skill and the relationship’s attributes. The workshop keeps only the list of skills.
A system prompt and GPT-4.1 turn the two documents into JSON for the two people. Once those objects exist, loading them is almost the same operation as before: merge each person on the indexed email, set the name, merge the skills, and merge the KNOWS relationships. Browser then confirms that the Neo4j employee examples and their extracted skills are present in the graph.
For more elaborate ingestion, Blumenfeld points to Neo4j GraphRAG for Python and the Knowledge Graph Builder reference application. These provide directions for workflows involving overlapping document chunks and concurrent or asynchronous processing, beyond a serial loop over short biographies.
Adding the biographies exposes the maintenance cost of earlier enrichment. The newly inserted KNOWS relationships are unweighted, but they can change person-to-person overlaps and community assignments. New skills also need semantic enrichment and corresponding semantic relationships. Blumenfeld confirms that these derived structures should be refreshed as data arrives, and acknowledges that ingestion would ideally precede clustering in the pipeline.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep document structure when context crosses sections
Extracting people and skills is one way to build a graph from text. Another is to preserve the document’s structure. Catalogs and RFPs often contain predictable sections: an introduction, objectives, a proposal, and subsections. Those structural pieces and their metadata can become graph objects, with embeddings attached to the relevant content. Retrieval can then move from an entity in a chunk up through the document hierarchy and down to another section.
This gives the system two sources of grouping. Leiden derives communities from connectivity; a document already supplies a natural hierarchy. That hierarchy can support summaries across sections or documents without requiring every grouping to be discovered algorithmically.
Why put entities, chunks, and documents in the same graph? Consider a legal clause whose expiry date appears elsewhere in the contract. Finding the clause is only the first retrieval step; following the document’s connections can bring back the date needed to interpret it. The accompanying domain-knowledge slide shows a graph and a query connecting chunks to definitions, another way to retrieve context through explicit relationships.
The audience extends this example to comparing data-protection language across vendors. Once the matching clauses are found, a traversal can also recover dates or perpetuity provisions elsewhere in the documents. Combining the entity and document structures preserves a route from a relevant passage to the surrounding facts that qualify its meaning.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Package the graph patterns as four expert tools
The third module reconnects to Neo4j and assembles four capabilities: retrieve a person’s skills, find similar skills, find similar people, and recommend people from a requested skill set. It begins with explicit graph patterns; schema-guided text-to-Cypher comes later. A direct person-to-skill match supplies the simplest tool.
The next patterns handle user wording that does not exactly match stored skill names. Vector search resolves phrases to skill nodes, and semantic relationships expand the candidates, with score thresholds controlling what is retained. Continuous delivery, cloud native, and security illustrate inputs that can be mapped into the graph’s vocabulary. This answers part of the earlier entry-point question: the tool’s retrieval pattern determines how the user’s words become graph entities.
Person similarity can use several kinds of evidence:
- Community membership: Retrieve people in the same Leiden skills community and inspect their skills.
- Exact overlap: Follow the stored similar-skill-set relationship. John Garcia is the example used to inspect overlap counts against other people.
- Exact plus semantic similarity: Combine shared-skill evidence with related skills, independently weighting the contributions. This yields a floating-point score and can change the ranking.
For recommendations from a requested skill set, the larger query is shown in two parts: search for skills, expand to semantically related skills, then find people who know them and count their matches.
A skills object helps define function arguments and return values. The final tool boundaries preserve the retrieval decisions:
| Tool | Graph operation |
|---|---|
| Retrieve a person’s skills | Match the person’s KNOWS connections |
| Find similar skills | Vector search, then one semantic hop |
| Find similar people | Weight exact and semantic skill similarity |
| Find people from skills | Resolve skills, expand, traverse to people, rank |
The last tool counts who knows the most matching skills. The model can choose the capability without having to reconstruct its traversal and scoring policy each time.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Let the agent choose and chain the tools
After testing the LLM and collecting the tools, the notebook first shows that tools can be bound directly to a model. It then creates a ReAct agent with LangGraph’s historical create_react_agent entry point, supplying the LLM and the four tools. LangGraph v1 later deprecated this helper in favor of LangChain’s create_agent; the recorded walkthrough uses the earlier API.
A greeting checks that the agent responds, and a notebook utility makes streamed interactions easier to inspect. Asking for Christophe’s skills selects the retrieve-person-skills tool. Asking which skills are similar to Power BI and data visualization selects the similar-skills tool. A question about a person with similar skills selects person similarity. These examples show the division of responsibility: the agent interprets the request and selects a capability; the tool supplies the defined graph retrieval.
A small Gradio chatbot then turns the isolated questions into a conversation. The sequence begins with skills related to Power BI, follows with who knows those skills, and then asks who is similar to those people. Blumenfeld checks the notebook trace as he proceeds: it shows a similar-skills call, a people-from-skills call, and finally person-similarity calls for each returned person. The follow-ups move between retrieval patterns while retaining the subject established by earlier answers.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Extend the assistant with annotated-schema queries
The final implementation example adds dynamic query generation to the expert-tool approach. An annotated schema supplies descriptions to an aggregation-query function containing an LLM. For a request to describe communities, the shown example generates Cypher that combines the person–skill pattern with the Leiden community property. The schema tells the model both what information exists and how the relevant parts connect. This receives a brief walkthrough, with additional examples left in the notebook.
The closing access question returns to the environment’s lifetime. The Jupyter servers are scheduled to shut down quickly, but the code and data remain in GitHub, linked from the deck shared through Slack. Blumenfeld suggests creating a cloud database through the Aura console’s free trial and loading the workshop data there to continue experimenting.
For participants at the event, the meetup announcement is corrected from tomorrow to that evening at 5:00. A separate 1:00 workshop goes deeper into graph analytics and communities, while booth follow-ups cover Neo4j MCP servers, ADK examples, and further knowledge-graph construction. Those extensions build on the working assistant demonstrated here: a small domain graph, explicit retrieval patterns, and an agent that can select among them.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Three notebooks, data and slides covering graph basics, unstructured extraction and a talent retrieval agent.
Neo4j’s Leiden algorithm, configuration options and examples for writing community assignments.
Announcement of Neo4j’s Gemma 3 4B and 27B query-generation models, with links to the models and training dataset.
Python tools and examples for knowledge graph construction, vector retrieval and grounded generation.
An application for extracting graphs from unstructured sources, inspecting them and asking questions over the results.
Further reading
Examples explaining uniqueness, mandatory properties and node keys.
Original announcement describing text-embedding-ada-002 and its embedding dimensions.
Updates since the talk
Explains the move from the prebuilt create_react_agent function to LangChain’s create_agent.
Read the complete timestamped transcript
- 0:00
[upbeat music] So as you come in, we have here a server set up with everything you'll need if you wanna follow along.
- 0:23
You should have gotten a Post-it note. If you don't, just raise your hand and my colleague Alex over here will come find you and we'll provide you with one.
- 0:31
Uh, basically what you're gonna do is you're just gonna go, if you have a number one sixty or below, you go to this link here, the QR code on top as well.
- 0:41
Um, and if you have a number that's two oh one or above, you go to the second link or the QR code. From there, I'll give you some directions.
- 0:48
You're gonna have to clone a repo, um, and then you're just gonna have to move an environments file over really quick. Also, for everything in this deck, I did create a workshop GraphRAG intro Slack channel, so if you're part of the AIE, uh, Slack group, you can also go there and just grab this deck, um, and, you
- 1:05
know, get the links or however, however you'd like to do that. So we'll get started here, I think in just a couple of minutes.
- 1:11
How about the-
- 1:12
Oh, right. So your... Sorry. Your nu- your number will give you your username and your password. Basically, it's attendee, all lowercase, then your number. That'll be both your username and your password when you sign in.
- 1:34
The other link that you should try to open as well is the, um, browser preview, but I'll walk you through that here in a second. I'll give it just another minute here for everyone, uh, to file in and get situated.
- 1:50
Sorry, what is the username and password?
- 1:52
The username and password is gonna be attendee, all lowercase, and then the number that you have. Both the username and the password are the same.
- 2:01
Is that a great question if these are gonna be live, um, after the session, the notebooks?
- 2:07
So the notebooks, uh, the servers will be down, um, after the session, but we-- you have the GitHub link you can go back to. Um, so the code is, is there for you to use.
- 2:18
Um, it's just that the environment won't be available afterward.
- 2:20
Okay.
- 2:27
All righty, so I'm gonna go ahead and get started here. I'll leave this screen for a second, so if you wanna grab the QR codes here, now would be the time to do it.
- 2:36
Um, obviously, you can go to the Slack channel and also pick up this deck.
- 2:41
All righty. So we're going to do an intro to GraphRAG workshop today. Um, I was debating what to actually put in this workshop since everything's changing so quickly, um, and some of my colleagues convinced me not to make it too complicated.
- 3:00
So this course is gonna be very much an introductory level course. Um, if you wanna look at sort of more advanced GraphRAG techniques, integrations with things like MCP, we have that at our booth.
- 3:13
Um, and we have some other things that we're doing, other events, um, tomorrow that will go over some of that stuff, and I'll have links to all of that, uh, as we go through.
- 3:21
But basically, what we're gonna do is we're gonna get everything set up. Hopefully, that should only take a few more minutes, and then we have three modules. We're gonna go over some graph basics.
- 3:31
We'll be using Neo4j today, so it's a graph database. Just how to query that, kind of how to, you know, construct, um, your logic to retrieve data. We'll go over, um, another module on unstructured data, um, and how to do entity extraction.
- 3:52
People already know about Neo4j? Can we get a quick show of hands real quick for that?
- 3:55
How many people know about Neo4j? All righty. How many people have, by the way, used Neo4j, like have written Cypher queries?
- 4:07
Okay, so some folks in here. And then how, how many people have used LangChain before?
- 4:13
Okay. So, so a fair number of you. Okay. That's good to know. Um, and then in our, in our third module, we'll actually go over, we'll use LaneGraph. We'll build a very simple agent that will use some retrieval tools, and you'll get to see how some of that works, and we'll wrap up after that with some resources.
- 4:29
So make sure to ask questions straight away. Raise your hand. I'll stop intermittently. Um, we only have eighty minutes, though, so I wanna make sure if you do have a question, um, go ahead and raise it and we'll get that answered 'cause we're gonna be moving through the material, um, a little bit quickly.
- 4:44
Um, as I said before, we have two Jupyter server set up, so you don't need to pip install anything. Um, you can go ahead and connect to these notebooks.
- 4:53
Attendees, I already explained, you should have a number. If you don't, go ahead and raise your hand. Um, the username and password is just gonna be attendee followed by your number.
- 5:02
One sixty and less, you go to that first link. Two oh one and larger, you go to the second link. Um, there's also, I-- if you can, go ahead and open browser.neo4j.io/preview.
- 5:14
Um, I'll show you in a little bit. You're gonna log into that as well. That will let you visualize the graph a little bit better as we start putting data inside of it.
- 5:23
You said to raise your hand if you don't have a number?
- 5:25
Yes.
- 5:26
Okay.
- 5:27
All right. So Alex, um, if we can get a number over here.
- 5:36
All righty. Once you're inside of your environment, what I want you to do is these two commands. So you're gonna open up a terminal window in Jupyter. You can do that by pressing the little plus sign.
- 5:50
I want you to Git clone. The command should actually be in the README. It should say Git clone, and it will give you the link to the repository that you need to clone.
- 5:58
And once you do that, I want you to copy the workshop file over into that GenAI workshop talent folder. That environment file, ws.env, is gonna have information to a database that's already set up.
- 6:11
It'll also have an OpenAI key inside of it that we'll be able to use for the workshop.
- 6:18
So just to show you what this looks like, um,
- 6:24
if I go over here and I look at my, my terminal, right? I've already done this.
- 6:31
Make this a little bit bigger. You just go ahead and Git clone.
- 6:36
Um, if you go to the README that was, um, in the main folder, this README here, it has the, uh,
- 6:45
the, um, the GitHub URL. So you just basically go Git clone, um, and then... Oops. And then after that, you just copy that workshop file into that GenAI workshop talent directory.
- 7:00
So you'll get this GenAI workshop talent. It's like a subdirectory in here, and you just have to copy that file, and that will have, um, resources that you'll need to log into, uh, the browser and for you to connect to your notebooks.
- 7:14
The other link, um, that is inside of that deck is this browser.neo4j.preview link. Um, basically that will give you a way to visualize the graph. So what you will do, if I go ahead and disconnect, um, and maybe I,
- 7:32
um, go to connect to an instance. So you should get a screen that looks like this, and then that workshop file will have... And it should actually be the same thing for you guys.
- 7:41
It should be the attendee, the number, and then the same thing for the password. So go ahead and make sure you do that because then you'll be able to visualize the graph a little bit better.
- 7:54
Any questions so far?
- 7:56
Yeah. What's the login [keyboard clacking] for the Jupyter environment?
- 7:59
So for the Jupyter environment, if you got your number, it's attendee, all lowercase,
- 8:05
and then your number for both the username and the password.
- 8:12
Sorry, where's the connection URL for this?
- 8:17
Um.
- 8:18
It's in the WS, WS.env or dot one. Okay.
- 8:22
Yeah. WS.env for the, for the Neo4j browser.
- 8:26
Thank you. Sorry, JupyterHub is what? Jupyter Notebook?
- 8:32
So it's, um, right here. So it's basically going to be your username... Or sorry. It's going to be the attendee, all lowercase, so then the number that you receive for both the username and the password.
- 8:47
That's it?
- 8:47
Yeah. And if, if you, um, wanna come back to this, if you have, if you're connected through Slack, the workshop GraphRAG intro, you can go there and you can pick up the slides as we move on, so then that way you just have a constant reference back to it.
- 9:05
Um, so while everyone gets set up here, I'll talk a little bit about just what GraphRAG is in general to kinda motivate what we're doing here. So this is, um, an architecture actually, um, that represents what some of our customers do.
- 9:20
It's a very common, um, architecture for GraphRAG users. It's generalized. And basically, the idea is that you have your agent over there, you have your AI models and your, your UI.
- 9:32
So like all the normal things that you might think of if you're putting together a knowledge assistant. Um, but then there's this knowledge graph thing in the middle, and that knowledge graph thing you can ingest both unstructured and structured data into that.
- 9:44
So unstructured being things like documents and PDFs and that sort of stuff, and then structured being tables like CSVs or stuff from a relational database or what have you.
- 9:54
Um, and so there's a big question of like, "Well, why in, you know, the heck do we need this like knowledge graph thing in the middle?" Right? Like we have agents, we can have tools, and we can go pick stuff from data sources.
- 10:07
Um, and so the idea with this is that if you have a use case and you kind of know the types of questions that you wanna answer with your agents, by taking your data and decomposing even a very simple knowledge graph to start, um, you're gonna be able to expose, um, a lot of the sort of domain
- 10:24
logic that you'd want to apply through the model of your data. Um, so the idea is like we'll see when we build a skills graph, we'll make some relationships about people knowing skills, and by making that schema available to the agent and making tools available to the agent, um, you're gonna be able to have a lot more
- 10:40
control over how data is retrieved, more accurately explain the retrieval logic better. Um, and we see this as especially important as we start moving more and more into this agentic world, 'cause it's not like a one-shot vector search anymore, right?
- 10:54
We're starting to see that now when we get questions or prompts handed to, um, an agentic workflow, those start to get broken down in various ways. Um, and when you have a knowledge graph, it just lets you, um, offer retrieval logic to complement that in, in a, in a much more, uh, simple and, and in my opinion,
- 11:11
a better manner. And today we're gonna be looking at a skills and employee graph. Uh, so basically what we'll-- The use case we'll be looking at is you're building a knowledge assistant to help with things like searching for talent, aligning and analyzing skills within an organization, um, and doing things like staffing and team formation and substitutions and
- 11:33
things of that nature. And so I'm gonna present a little bit about what we're gonna go through in these modules first. So I'll do some stuff inside of ADK.
- 11:44
Hopefully it won't take too long. I just wanna kinda talk to you about Cypher and some of the things that you'll be seeing, and then we'll go ahead and get hands-on here pretty quickly.
- 11:56
Um, so we're gonna talk about creating a graph. We'll start with some structured data here just to keep things simple. I'll introduce unstructured data a little bit later. Some basic Cypher queries, some algorithms, and we'll get into some vector search and semantic stuff.
- 12:10
Um, so a knowledge graph basically, um, when we think about it, a knowledge graph generally is devi- is defined as some, as design patterns to organize and access interrelated data.
- 12:21
And at Neo4j, we model the data inside of the databases, what's called a property graph, and this consists of three primary elements. So the first are nodes. These are like your nouns.
- 12:32
These are your people, places, and things. Next are your relationships. These are how things are related together, hence the name. Um, and often will be like verbs. So person knows person, person lives with person, person drives or owns a car.
- 12:49
And both of the, um, nodes and relationships can have properties, which are just attributes. They can be strings, they can be numbers, they can be arrays of things, and they can be vectors as well.
- 13:02
So we can store vectors for-- We've had for a long time inside of Neo4j, um, and you can do search over these things.
- 13:10
Now, the, uh, query language that we're going to use to access the database is called Cypher, and I know a lot of you raised your hands in the beginning, so you already have some familiarity with this.
- 13:22
Um, but Cypher k-kind of looks like ASCII text. So the idea, right, is that, um, it has this SQL-esque kind of feel to it, but you get to write these statements like if you see match person KNOWS skill, um, basically you're connecting a person node to a skills node through that knows relationship.
- 13:41
So it reads kind of very, um, literally in the way that it's written. Um, nodes have what's called labels, uh, which is sort of like it would be the equivalent of a type of table within a SQL database of basically what type of entity it is.
- 13:57
Um, and then as I said before, they have properties. So for example, you can identify by a property like name, um, and you can have s-variables like P and S, which refer to the actual, um, entity as you start to write your query more.
- 14:12
So this is not gonna be a course on writing Cypher, right? 'Cause we can make a, an eighty-minute course just on, like, how are we gonna make Cypher queries.
- 14:20
Um, but we, we'll be walking through these queries. So don't expect to, like, be, if you haven't seen Cypher before, to be a super expert in the Cypher query language when we're done.
- 14:30
Um, but just know that this is kind of how it works, and then as you go through, hopefully you'll get a better understanding and a feel for how these queries, uh, work and, and the types of data that can be returned as you run them.
- 14:42
Um, and so I'm sure r-- Is everyone pretty familiar with vector searching here at this point? Yeah, I, I have a feeling this audience probably would be. So I won't spend too long on this, right?
- 14:53
I think we all kinda know what embeddings are. It's basically a type of data compression. You can apply them to all sorts of things, right? Text, audio, you can even apply them to graphs.
- 15:04
Um, oftentimes it's just gonna be a vector of numbers, and then you can use that to find similar things within that domain space. So find texts that are similar, uh, semantically, not just lex-lexically, like b- actually based on, um, the types of things that they're talking about.
- 15:20
Um, and within Neo4j, you have search indices, including vector. So there's range indices, you have uniqueness constraints, you're able to search text, you're able to do full text with Lucene.
- 15:31
Um, and then we also have approximate nearest neighbor vector search as well that we'll be leveraging, um, as we go through in combination with the Cypher queries that we were just looking at to do graph traversals.
- 15:43
Um, the next thing to know about is that in addition to being able to query the database, we also have analytics. So we have graph analytics powered on the database that lets you do different types of data enrichment, um, and do more graph global type of analytics.
- 16:00
So finding which nodes are most central according to different algorithms, doing things like community detection, how do you cluster the graph, finding paths between nodes, doing different types of embeddings.
- 16:11
Um, so we have a lot of those algorithms, and we'll be touching on them very, very briefly today in the first module just to show that, you know, once you have a knowledge graph, you can start enriching that data and then actually using things like, we'll see, in our case, we'll be using community detection, um, where we'll
- 16:28
be summarizing skills inside of our graph, and then we'll be able to pass that on to an agent to actually use that to explain, um, some parts of our graph, uh, for our use case.
- 16:41
All righty. So with that in mind, we'll go ahead here and jump into the first notebook. Um, are there any questions before we dive in?
- 16:50
Is anyone still-- Okay. Yes, over here.
- 16:54
Can you give us the link, please?
- 16:55
Um, yes. Do you have, uh... Let me just go back here
- 17:00
to the... So, and this is available in the Slack channel too. If you don't have a number, um, my colleague Alex over there can go ahead and grab one for you.
- 17:22
Um, we're in the workshop GraphRAG intro Slack channel, so you can go there to grab the deck and all the links. But basically, if your number is one sixty or below, you go to that first Jupyter server.
- 17:33
If it's two oh one, um, or above, you go to the second one. Um, you use attendee, all lowercase, and then your number as both your username and your password.
- 17:42
You'll do that for the Jupyter notebook and then also for the Neo4j browser if you wanna follow along with visualizing, um, the graph as we go through.
- 17:52
Any other... Yes?
- 17:54
I know this is a intro-- introduction to, uh, GraphRAG. So, um, but maybe, like, you know, when you're building these, these graphs, uh, I see you have, you have like a small, small, uh, graph.
- 18:06
Uh, have you, like, you know, prioritized whether you should like big, big graphs, like, you know, uh, one that contains more than skill or, or like make smaller graphs?
- 18:17
Is there any, uh, this thing-
- 18:18
So your question is about data modeling and whether-- how do you prioritize making one graph versus multiple graphs? Um- I mean, it's a good question. I think in general, for a lot of what we're seeing with agents, I find it's helpful to have a smaller data model if possible, um, especially if you're doing different types of dynamic
- 18:38
query generation. Um, so to keep that in mind. But as things are getting better, we can pull back the graph schema and, and offer it to agents, and I'm-- we're noticing that as agents sort of keep iter...
- 18:49
or as language models really keep iterating, they're starting to get better and better at interpreting. So whenever you wanna do traversals in a low latency way between two data points, those things really should go in the same graph, and then it's a question as far as what you make a label versus a property, um, i-in that scenario.
- 19:07
So we'll go through some of it, and then if you wanna talk after and come by our booth, we can have a more sort of use case-focused conversation. Anything else?
- 19:16
All righty. So I'm gonna go ahead here and then dive into the notebook.
- 19:22
So for our first notebook... Can go ahead and restart. That's fine.
- 19:34
All righty. So you're just gonna come down here and start, um... and remember we're in the, uh, talent subfolder. So there's two workshops in here. The one we'll be doing is called Talent.
- 19:49
Um, if you're in the other one, it's-- there's also some interesting stuff in there, but, uh, you won't be able to follow along. All righty, so looks like I'm running now.
- 19:57
So basically, what I'm gonna do is I'm gonna get my environments file here, and I'm just gonna load it. If you, um, don't have the environments file, just go ahead and move it.
- 20:08
It's in the root directory. Just go ahead and move it into this subdirectory. It's this ws.env file.
- 20:16
Um, and basically what we're gonna do first is we're just going to load our skills datasets. It's gonna be a table.
- 20:24
And if we look at that table, um, we're going to have, uh, basically three fields.
- 20:32
There's the, um, an email field, a name field, and then just a list of skills for the person. And as I said before, I'll-- we'll go into a little bit of detail here around how you might extract this from documents like resumes, um, in a second.
- 20:47
Um, but basically for now, because we're interested in sort of this skills, um, mapping and team formation and staffing kind of use case, we're starting with this sort of very simple dataset to get us started.
- 21:01
Um, and so there's a couple steps here that just go through basically organizing the data to make it easy to load, and then we're gonna start to create our graph.
- 21:13
And so a lot of this is just what we'd call, like, basic kind of Neo4j data loading. We're gonna create chunks out of our data frame. You're gonna, um, basically check to make sure you've got nothing in your database.
- 21:27
I do have stuff in my database 'cause I was just running this before, um, but that's on me because I was just running the course before. Yours should say zero.
- 21:35
Now, the first thing we do is set a constraint. So basically, inside of Neo4j, whenever you create nodes, um, if you have what's called a node key constraint or a uniqueness constraint, it's basically saying, in this case, that the email has to be unique and non-null for all your, um, for all your people, and that will make
- 21:56
it so that it's very fast to match on people and do merging operations. So a lot of times people will say, "Well, Neo4j is really slow," um, and that's often because of simple mistakes like not setting a constraint and then you're going to have to do very complex searches in the database every time you search on a
- 22:13
user rather than having it, um, in an index that's unique.
- 22:17
Um, and then you also do the same for skill because our data model is gonna be person and skill, so we have two types of nodes.
- 22:25
Um, and when we do that, we'll go ahead and have two constraints here inside of the database. You'll see for skill and for person. Um, after that, we'll go ahead and start loading our nodes, um, and our relationships.
- 22:40
So the way that this query works, and I, I guess I won't run it even though it, it won't actually change anything in my database. But what we're doing here is we're looping through chunks of our data frame and we're saying, "Hey, merge a person on email, set their name," and then for that list of skills, basically,
- 22:59
you're going to merge a skill on a skill name, and then you're gonna merge here that the person knows that skill. So it's gonna create this graph pattern of person knows skill in the database.
- 23:10
Once you run that, what you can do is if you have that browser window open that we were going over before, is I can go ahead and copy one of these, or maybe I'll just take, I'll take this one.
- 23:23
Well, I'll go ahead and take this one first.
- 23:26
What this will show you inside of the database
- 23:30
is if I just match people, I'll get my people back here,
- 23:35
and I may have lost... Oh, cool, I still have my internet connection. All right. So I can go ahead and see that I have my people. They have their names and their email addresses.
- 23:44
Um, you can do the same thing for matching skills,
- 23:48
and then, um, you can also look for relationships. So this gets into that pattern matching that we were talking about before with Cypher. This is a very simple version of matching a path.
- 23:58
So I'm saying P, which is path, is equal to node, connect to knows, connects to another node, and I'm saying limit twenty-five, and that will return a graph where I get to see all these different relationships.
- 24:12
Looks like my internet connection is still somewhat slow, but I get it back here. So you'll see I'll get my people. I'll get that knows relationship. Then in this case, this person knows API design, Tableau, Flask, um, and you'll see different skills pop up here inside of your graph.
- 24:31
Um, and there's, you know, you can, you can go ahead and run these through what, through our driver here as well to look at the data, um, pull back the different people that are in there, um, and find out what skills they have and such.
- 24:45
We do here.
- 24:49
Question.
- 24:49
Yeah.
- 24:50
When you say knows, is that... You're just making up a term. Like, that could be any string, or is knows, like, a specific Neo4j concept?
- 24:57
Knows is a relationship type. We are making it up. So our, our domain model that we have, um, I can actually call it here, and mine is gonna show more than yours if you run the same command 'cause it has the, um, some other later stuff that we do in the course.
- 25:15
But basically, you have person KNOWS skill. That's our data model. So you can say person HAS skill, would be another way to put it, right? Um-
- 25:26
But the word knows is just something that you, you could change that word and-
- 25:29
Exactly
- 25:30
... there's an edge there.
- 25:31
Exactly, yeah. And it's, it's actually funny because this is becoming even more important, um, now that we're using, uh, LLMs to design queries because, like, the language that you use is sort of like an annotation for the model, right?
- 25:47
So that starts to become very interesting. All righty. So there's some Cypher queries here that I'll go ahead and run through really quick, and I may, uh, depending on time, need to, need to kinda speed things up through this notebook 'cause I wanna make sure that we actually get to the agent at the end.
- 26:08
Um-
- 26:08
How did you say we can visualize the graph again?
- 26:11
So, um, if you go into the, uh, the deck, there's this link browser.neo4j.io/preview, and then I think it's just your username and your password. Um, but you can also look inside of your, um, workshop environment file, and it will have that information there.
- 26:31
You just use your username and your password and your URI information, um, which you get here. So you get your URI, then your username and your password.
- 26:43
All righty, so like I said, we'll go through some of these. So for example, we can count in Cypher, so we can say MATCH person KNOWS skill. We can get back the name, and we can count the distinct people, uh, basically here for, for each skill.
- 26:58
So basically what we're doing here is you can think of it as, like, okay, I've got all my skills and I'm gonna count the distinct people that know that skill.
- 27:05
It's very simply what we're doing. When we get that back, we'll see kind of what our most popular skills are here, um, going down, and they're all kind of tech-focused.
- 27:16
We can also ask different types of multi-hop questions, which is very interesting. So for example, I'll take this and I'll copy it over to my browser 'cause it's, it's interesting to see these visually.
- 27:27
Um, but what we're asking here is we're gonna, we're gonna take this person, um, named Lucy, and I'm just gonna ask, you know, what people are kind of similar to Lucy in terms of knowing the same skills, right?
- 27:44
So I can go ahead and run that, and then what I'll get is I'll get Lucy here. I'll get all of her skills, and then I'll get all the other people that know those skills here, right?
- 27:55
Um, and you can build on that iteratively. So I can, if I go back here, I can also say, well, now I wanna know all of those skills or all of those people, and I wanna know basically,
- 28:08
I'm gonna add at the end of that query, um, I get they know a certain skill, and then I wanna get all of those people, and then I wanna get all the skills they know.
- 28:17
So I'm basically adding this and what skills do these other people know to the query. And then I'll get a very large graph back. But the idea with this is that once we have this logic extracted from whatever our original data source is, we can now control at a much more fine-tuned level how we define what a
- 28:36
similar person is or what a similar skill is because we have this ability to traverse over the graph, um, and apply, um, concrete logic. It's basically like having your information in a symbolic versus just a sub-symbolic vector.
- 28:50
Um, and so, you know, you'll get a lot of stuff back because now we're looking at people and all the other skills that they know, and I can go in here and find the most central skills among these people, right?
- 29:00
Like for example, Scrum is very central among this group, um, because there's, there's a lot of people that know that skill. So I'm figuring out about this local community that sort of, uh, knows the similar skills to Lucy.
- 29:14
Um, and, and in here, it's just some examples of running that, uh, same logic, um, basically inside of the, inside of the notebook. Yes.
- 29:25
Um, at the end do I get only the table and the row? I don't see the graph.
- 29:29
So, um, to get to the graph, basically, uh, what you're going to do is you're gonna go to this Neo4j browser link.
- 29:35
I'm over there. I'm over there. I mean, on the, on the UI.
- 29:39
Oh, I see. Um-
- 29:40
Here. Oh, all right.
- 29:41
So-
- 29:41
See the graph now, yeah
- 29:42
... you table, then graph, and then sometimes if you're not returning nodes, it will only return a table. Like, if I said, you know, return, um-
- 29:50
I think I re- I just put the same query as you.
- 29:54
Yeah. Okay.
- 29:55
You did. Oh.
- 29:58
Okay. Yeah, so if you, if you just say return P, um, in that case it should return, it should return the path. Sometimes if you don't see it, it's because you're returning, like, just a name or something.
- 30:09
Um, and then in that case it'll, like, just show you a list of names.
- 30:13
Oh. Thank you. That's helpful.
- 30:14
Follow-up question. What is a distinct doing in this case? Um,
- 30:20
and I don't have a distinct here. You just delete, you just delete it. Oh yeah, I did. Yeah. Um,
- 30:28
I don't know if it's completely necessary for this one actually. Um-
- 30:34
Yeah, I don't think it is completely necessary for this one. Um, there are times when you do very complicated, especially we'll see that there are a couple other examples where we do, like, multi-hop paths, and there's a chance with some of those that you'll get basically two paths that are the same, um, in which case having the
- 30:51
distinct there just allows you to, um, filter it better.
- 30:56
Question.
- 30:56
Yep.
- 30:56
Um, so how, how good are any of the LLMs is in writing those, uh, Cypher, uh, queries?
- 31:05
So that's hard to say. They're getting better. Um, a lot of it depends on the complexity of your schema. Um, and basically, you know, we see for simpler aggregation queries or when you have a lot of prompt engineering around doing different types of path queries that are very specific on a smaller model, they can do well.
- 31:23
Um, we do often recommend that you have your own expert tools if there's, like, a really complicated type of traversal that you wanna do. So right, you can write your own Python functions or you can have your own MCP server that will just have, like, your, you know, set of functions for your more complicated traversals.
- 31:39
Uh, we also see, too, you can sort of restrict the options for LLM. So instead of writing a complete query, you can say, "Hey, like, there's, you know, these, you know, three types of general patterns," you know, and, and write that part of the pattern and then it will go into this other query.
- 31:54
So you can do stuff like that to help it. Um, we've also done, we've had fine-tuned models that we just released, um, I think back in April. Um, they're on, they're fine-tuned from Gemma.
- 32:04
They're on Hugging Face. Um, so you can try using those as well. They can do a little bit better. We're not gonna use them here though, unfortunately, 'cause we're using a bunch of OpenAI, uh, OpenAI models for this.
- 32:18
Um, all righty. So a lot of this, um, as, as I was just going over, is basically just running these queries. Um, returning, in this case the distinct is important to return a distinct name because you might actually get to the same person multiple times.
- 32:35
Um, so if you're just returning a name and a, an, um, of a skill, right? Then it's important to, um, to, to use distinct in that case. So we get all the distinct skills that basically showed up in the graph we were just looking at.
- 32:50
Um, another thing that might be important for our use case is finding similar people. Um, so this is, again, using that, uh, query that we were just going over to find.
- 33:00
We used Lucy before, but now we can actually, uh, parameterize that here, um, and then basically go no skill, and then we can match, um, basically from that skill going to another person, and we can sort of count number of shared skills between people.
- 33:19
Um, and so when we do that, right,
- 33:23
um, we can go ahead and see, in this case, um, like, the number of shared skills between, um, between different individuals.
- 33:32
Um, and we do it again here, I think, for, um, a different, uh, set of people. I think this just counts most skills shared between any two people. Um, so you can kind of see that here.
- 33:45
Again, just another way to measure similarity beyond just semantic similarity, measuring actually, um, what we have from our model in terms of exact shared skills.
- 33:57
Um, and as we go through this, some of the things that we can do to help speed up our queries, and this is, this is sort of optional, but if we know that we're going to, um, sort of look for similar skill sets a lot, we can create a similar skill relationship inside of our graph.
- 34:12
Um, so basically we can match two different people, um, and then we can merge, uh, a similar skill set basically bet- based on, um, an overlap of the, um, of a skill count.
- 34:26
So we have our data frame locally that we can use for that, that we, that we were just looking, we basically just pulled it back when we were looking at those similar skills, and what that's gonna do is it's just gonna create, again, this, uh, relationship that has similar skill set between people.
- 34:42
Um, and if I were to look at that, it will, um...
- 34:47
Go over to my browser. We'll go ahead and see.
- 34:57
You know, I can have a similar skill. Some of these are overlap one, others will be greater, um, inside of here. I think they go up to three. Um, so it's, all this is doing is basically saying, "Hey, if two people, like, what is their overlap?"
- 35:10
So we don't have to do that full traversal over and over again if we don't want to, um, with that similar skill set relationship.
- 35:18
Um, the next thing I wanted to show you and it-
- 35:21
Question.
- 35:21
Yes.
- 35:21
So, but if you do this, you need to... Like, this is static, and if you want to update what's the overlap, you need to run this query again.
- 35:28
You would need to run that query over again, yeah. So I mean, it, it depends on how often your data gets updated. There's nothing actually wrong with doing the multi-hop query over and over again.
- 35:40
Um, the graph database is designed to handle that. So if you had a situation where you had a graph that was, um, constantly getting updated, you a- you might not even need to create this relationship.
- 35:52
Um, the next thing that I wanted to show you, um, was how our graph analytics works, um, inside of Neo4j and basically using that to, uh, enrich the graph.
- 36:03
So this is, um, basically creating what we call a, a GDS or a graph data science client. Uh, and what's going, what we're basically doing here is we're creating something called a projection, and then we're running a l- algorithm called Liden.
- 36:19
Um, for the GraphRAG community, are you all have, have, how many people here have heard of Liden as an algorithm?
- 36:26
Okay, so just, just a few people. How many people have heard of Louvain as a graph algorithm?
- 36:32
Okay, so we got a couple people. Basically what this algorithm does is it breaks the graph down into a hierarchy. So it will start by, um, basically- Breaking the graph into a few big communities and then going into smaller communities, and what it's trying to do is optimize what we call modularity, and it's basically this metric that
- 36:52
says, "Hey, I wanna create these clusters in my graph where the connections within the cluster are very high and connections across clusters are very low." So I'm creating these modules, and basically what I do by creating these, um, and I'm using that similar skill set relationship.
- 37:09
So this is another important reason to create it, 'cause if you do, um, analytics on your graph, um, it can help with those analytics running a little bit better.
- 37:16
'Cause I have person connects to another person with a similar skill set, um, and by running this Leiden algorithm, basically what we get is, uh, a bunch of communities that reflect people within the communities knowing similar skills.
- 37:33
Um, so this is all simulated data, but basically if I go down to, um... And I'll skip over some of this. We do some checks around, like, how good the communities are.
- 37:46
I'd encourage you to run this just so that the agent works well at the end. Um, but the-- what I wanted to show you here is this, uh, graphic right here.
- 37:56
So what this is, this graphic is looking at, because basically we wrote this community ID property back to the graph, and you get these different community IDs, and then you get to see which communities in a heat map have the most skills in a certain, in a certain area.
- 38:11
So this data is randomly generated, so a lot of these patterns are gonna look maybe a little bit funky if you were to really dig into them. But the idea is that, um, as you have very-- if-- as you have more relevant data and realistic data, this can actually show you, like, your data engineers are here, right?
- 38:29
And your front end guys and front end folks are over here, and then your ML people are over here. So you can start to see that within the graph, um, and really break that down.
- 38:40
Um, but do go ahead and run everything here, um, through the G.drop so that you have that property. Uh, another way that we can break down,
- 38:50
uh, sort of different groups inside of the graph is to look at... Go ahead, sorry.
- 38:55
Uh, I just had, uh, one question regarding, like, when, when do you, uh, customize your graph? Like, for example, the community detection algorithm that you're running-
- 39:02
Yeah
- 39:03
... and when do you just let the agents just-- Is there any,
- 39:08
you know, again, uh, heuristics where you, where you say, "Okay, well, actually it's better to invest time in figuring out whether we should, uh, you know, improve our graph rather than just-"
- 39:19
Well, I think it depends on your use case, right? Like, if you're very interested in, you know, saying, "Hey, I wanna understand, like, the skill communities inside of my company," right?
- 39:29
If that's, like, a question that's gonna come up frequently, then using something like graph analytics can be very beneficial, right? Because you can do basically, like, employee segmentation, and you can understand performance with inside of different groups and stuff.
- 39:42
We see it oftentimes used for customer segmentation and recommendation systems, and that sort of thing too. Um, at the same time, if you're just like, "Hey, I just want to, like, look for matches of different people with similar skills," then maybe you don't need community detection for that, 'cause that's just, like, a pairing exercise, right?
- 39:59
Um, so I'd say you use it whenever you wanna do some sort of clustering analysis and persist that and then sort of even have visibility and I guess the, the confidence in knowing that there was some way that you did that, right?
- 40:12
And it's not just up to the model that's just making stuff up around how that works, right?
- 40:17
So basically you look at, uh, what the users are doing and then try to see if you need to build-
- 40:23
Yeah. [crosstalking] Yeah. Yeah.
- 40:25
Thanks.
- 40:29
Yes.
- 40:31
S- since the communities are based on their skills, right? What is the heat map showing us? Secondary skills?
- 40:40
The heat map is showing you how often different skills show up with inside of each community.
- 40:48
Aren't the communities based on what skills they have?
- 40:52
Yeah. So, like, the first community, for example, is it looks like either Tableau or Swift, right?
- 40:58
So basically, like, we could use this to name those communities for understanding.
- 41:02
Yeah. To understand, like, the skill breakdown within each community. And again, this is generated data, so this is a little bit random, right? Um, but you can imagine that in a non-random scenario, what you're probably gonna end up seeing is, like, if you have a lot of product managers versus a lot of, you know, um, like, front
- 41:21
end developers versus, you know, like, DevOps folks, like, you'll see that grouping start to emerge.
- 41:28
Yes.
- 41:28
Yeah. Two connected questions. One, do you have any different, uh, best practices for data modeling for an agent to understand the data model or just general graph best practices around creating data models?
- 41:41
Um, yeah, I mean, I'd say a lot of the agent stuff is evolving super, super quickly, um, you know, as LLMs keep changing and getting better. Um, we've had for a long time guides on how to do, like, data migration from relational systems to graph and how to think about that.
- 41:58
There's a certain way in graph how you think about, again, like, nodes being nouns, relationships being verbs, and how to connect those together. For agents, I think it's really nice when the data model reflects a natural language, right?
- 42:10
So person knows skill. It's a very natural language way of, you know, saying something that dir- that translates directly to a data model. Um, and as I was saying before, simpler data models seem to work better when you do, like, dynamic query generation.
- 42:24
Um, so there's stuff like that, and the rest of it is I know, like, the it depends answer is, like, you know, such a cop-out. But it is true that, like, depending on the type of retrievers that you have, the size of your data, um, the cardinality of different categories of things in your data, right?
- 42:42
Like, you know, you generally don't want, if you can avoid it, to have hundreds or thousands of no labels, 'cause it's just a lot. So then you make them properties.
- 42:50
So there's a lot of stuff like that to consider. So I don't know if that answers your question, um, but-
- 43:02
About the data model. Like, what sort of schema file would you give it, or what sort of, um, what do you generate within WMTA? Or what does it seem effective-
- 43:11
So, yeah, we'll see at the end of module three, which we might not have time to get to, but you'll see it in the code, and I'll, I'll show it really quick, is that there's...
- 43:19
You can, from the graph schema, there's, um, functions that we have to pull back the node labels and the relationship types. So you can create a sort of JSON representation, right, of what the graph schema looks like, um, and then combine it with specific prompts.
- 43:32
So then it's like, "Okay, I follow that." Another, um, thing to do that helps even more is if you have a graph data model that's not gonna change a lot over time, where you know you can just pull it and it will be the same for a while, is you can annotate that schema.
- 43:45
So you can say, like, for specific properties or node labels or relationship types, "Hey, this thing does this." And when you ingest data, make sure you, you know, put it here, and when you pull data, make sure you can, you know, go on different paths.
- 43:58
The other thing is putting in, like we had person, know, skill. Putting those actual query patterns into the schema as well helps a lot because the model can read that and then understand how to do that traversal better.
- 44:11
All right. Anything else? All right, cool. Um, so we're actually getting close on time, so I'm gonna go pretty quickly through the rest of this. Um, but hopefully, um,
- 44:25
it, it'll be pretty understandable. So there's another way that we can start, um, thinking about skills and relationships between skills, is how they're semantically similar. Um, so basically what we can do is actually make embeddings on our skills.
- 44:40
So there's another file in here, um, that basically has a CSV file that you read into this notebook that has skills and descriptions and an embedding. So which, which field here do you think we embedded, the skills or the description, and why, right?
- 44:58
So one of the things is when you have really short names, like R is a... technically a programming language, although a lot of people don't love it. I love R.
- 45:08
Um-
- 45:09
AWS.
- 45:11
AWS. Like, they're very short, right? So having descriptions about those, uh, skill names. If you embed those, it provides a more informative embedding, right? So that's the whole idea there.
- 45:23
So basically, we give each skill a description, and then we embed that description. And what we're seeing inside of this is we're actually going to, um... And these are all text embedding ADA, so they're, so they're 1536.
- 45:36
Um, we're gonna go ahead and create a vector property. Um, this is just loading those up in chunks. Um, and then we're gonna set the description as well. And after we do that, um, we'll basically have, I think we create our vector index down here that we call a skills embedding.
- 45:56
Once that's set up, basically what we're able to do, and you'll see it show up here, we'll get that skills embedding index, is we're now going to be able to do vector search on skills inside of the graph.
- 46:09
So if I have Python as a skill, and I go ahead and I'll use this command in Cypher to search the skills embedding, pull back the ten most relevant skills.
- 46:18
Um, and you'll see here it'll, it'll bring some skills back. Um, here it's, like, Ruby and Java. We got Pandas at least. That's good. Django, PyTorch. Um, so some of these are better than others, but the point is that we can go ahead and apply these vectors, um, and then pull information back with vector search.
- 46:39
And another interesting thing that we can do, um, as well
- 46:45
i- is we can, if I had something that wasn't in the database, like say I'm just looking for API coding, right, and I searched that as a term. Basically what I'm doing here is I'm using the OpenAI, uh, client here to just embed this model, or I might be using actually LangChain up here.
- 47:02
Looks like it's LangChain. Um, and then I'm doing a search on the database to pull back relevant skills with a certain similarity threshold, and I'll get back API design and JavaScript for that API coding example.
- 47:15
Um, and what I can actually do in this case is I can say, "Well, I have this ability to do semantic similarity in the database." I can actually write a relationship that's just similar semantic, and I can attach a score to that.
- 47:32
Um, so there's some advantages to doing this, but a big one is visualization and also clustering. So if I were to take this command, which takes a semantal-- similar semantic relationship, and I go into my graph,
- 47:49
and I just put that in here, and I just return all basically the skills that are semantically similar.
- 47:57
This internet speeds up hopefully. And I zoom in. I'll start to see sort of interesting groupings here. So
- 48:09
I'll start to see, for example, that I get my cloud skills here, Azure, AWS, cloud architecture, all in one place. Similarly, like I have Flask and Django here connected.
- 48:20
I've got my data analytics groups. There's like Tableau, Power BI, data visualization. Um, and then I've got a big grouping over here. So you see like you have your JVM languages, like your Java and Scala and Kotlin here.
- 48:33
Um, and then I've got, you know, my Python stuff here with Pandas. And then if I go up in this group that's connected, right, I've got my Java, and then I've got like all this front-end, you know, frameworks and stuff up here.
- 48:46
Um, so don't underestimate the power of being able to visualize similarities. Very important because I can create communities from these. I can use this for customized scoring in my retrieval queries, which we'll see.
- 49:00
Um, but the other really cool thing is that, um, if for some reason, like maybe I don't think- Java should be connected to Python, I can control that. I can remove that relationship.
- 49:12
And then every time I do similarity relationships, I have control over that and I can filter that, right? Um, so that's just some important things to keep in mind about how you can, uh, sort of use vectors and graphs together.
- 49:27
Uh-
- 49:27
Yes. Quick question. Uh, is it only when you do semant- uh, semantic similarity, is it only to visualize or are there any other use cases?
- 49:35
We'll see in a bit here because basically what, what we can do, and I'll answer this actually as I, as I go down, um, I can pull back this semantic similarity relationship here, but what I can start to do, um, which is actually pretty cool, is I can start creating these, um, sort of customized scorings between things
- 49:57
that kind of balance the, like, the semantic similarity versus hard rela- like, skill matches, and I can weight that if I wanted to in a, in a custom way.
- 50:07
So you can use it in your retrieval patterns as well to improve things.
- 50:12
Um, yep.
- 50:14
For a typical retrieval pattern, would you go directly in Neo4j or would you do vector search first, look at the entities, and then find similar entities, and then do maybe vector search again?
- 50:25
Do you use both typically or?
- 50:27
You ... So you can use both. Um, so there's a lot of workflows, 'cause now you can compose things together into multi- multiple steps, right? So you can definitely do something where you can pull similar skills and then look for people, right?
- 50:39
And it just depends on how you break down those functions for the agents. Sometimes if you know that, like, there's a very specific pattern that you want to follow, like here this is a very ...
- 50:49
This looks like a really big query. It's somewhat intimidating, but it's actually not that complicated. Like, what you're doing here is you're just sort of doing a weighting between the semantic similarity and a hard, like, s- overlap with similar skill sets.
- 51:03
Like, that might be a case where it act- coupling that logic together might make sense. Like, if there's a very specific type of metric that you want for similarity.
- 51:12
But what, what if you're coming from, like ... When you're coming from, you have a query and let's say you have an assistant and you have a much larger, uh, ontology.
- 51:19
Mm-hmm.
- 51:20
Um, that's like, "Tell me about people with certain skills." Um, how do you know that these are even the entities that you're looking at? Uh, w- w- what's the first step when you go from query to retrieval?
- 51:32
How do you break it into the entities to know that this is the type of query it should be or this is the query that you should be doing anyway?
- 51:38
Does that make sense?
- 51:38
Yeah. I mean, w- why don't we revisit that when we get to the third module? The second module s- should be really quick, and then we c- you can see some of the functions in the third module, and then that might help me answer that question a little bit better.
- 51:50
I think I know where you're going, but maybe seeing that will help.
- 51:55
Um, so as I said before, this is doing kind of like a, a balance between the semantic similarity and sort of a, a hard overlap of skills, and then you can use that to kind of weight, you know, how you want to find similar people inside of the graph.
- 52:13
Um, so you can, you can start balancing both sort of the vector search similarity and the, uh, similarity that, uh, just happens with inside of the hard matches. And another cool thing about a graph database specifically is ...
- 52:27
I'll go ahead and take this query here,
- 52:30
um, just so you can see what this looks like.
- 52:33
Oh, go ahead.
- 52:35
Feel free to push this question, but are you gonna talk a little bit about, um, I guess the trade-offs between doing the vector embedding within Neo4j versus maintaining a separate, like Postgres database for that research?
- 52:47
Yeah. I mean, you could go either way, right? So some of, a lot of this, to be honest, will come down to cost considerations. Like, how expensive is it in Neo4j versus how expensive is it inside of Postgres, and that varies a lot depending on the type of infrastructure you have.
- 53:02
Having everything in one place means that you ... It's a little bit ... You don't have to, like, sync your data, right? And then you also, the query latency is, at least in theory, gonna be lower because you're just querying from the same database.
- 53:15
Um, but if you already have data in Postgres maybe or you already have a specialized vector database, you also don't have to migrate your data necessarily to Neo4j to make that work.
- 53:25
So yeah, I'd say a lot of it actually, it's performance, but it's really, like, cost per performance, right, is kind of what you're thinking about in terms of what d- what does each deployment cost.
- 53:38
Um, so this query here, um, is actually, I'm taking, I'm looking for similarity between two people, and then you see I have this like star dot dot thing here.
- 53:48
And basically, with a graph, you're allowed to do what's called variable length queries. So I'm saying, "Hey, go out on similar semantic, but you're allowed to go out anywhere from zero to two hops between these skill sets before you find a connection between John and Matthew," um, here.
- 54:05
And then I can also union it against just the plain, you know, person knows same skill. Um, and when we get that back, we'll see, right, like you get Matthew over here.
- 54:16
Matthew knows React. John knows HTML. And then those are sort of similar because they both have a semantic similarity to JavaScript. Same thing here. You see we have this, uh, semantic similarity, but this is only one hop.
- 54:30
This is where the variable hop comes in. So you can start to control, like, these, you know, sort of how far out you can go on, on either of these paths to be able to pull back similarities between people.
- 54:42
Um, so it's just an advantage of a graph database.
- 54:47
Then I think might wanna finish it off for this notebook. Um, I would take a break, except that, uh, we only have 23 more minutes.
- 55:00
So what do you say? Should we just power through the last 20 minutes, you think? Yeah. Let's, let's, let's do that. So- Now I, we, we looked at some of the advantages of using the graph and the semantic similarity inside of the graph, and now we'll talk a little bit about our second module here.
- 55:17
And I won't go over to the slides, 'cause I think for you guys I can probably just hop right into the notebook, uh, around, well, what if we have just resumes, right?
- 55:26
We don't have a CSV file. So this is gonna be a simple example that will show you, um, basically how to take the data, um, from text and turn it into useful data for the graph.
- 55:37
So again, like if you're, if you're going through and running this live, you're just connecting to your same workshop file, which you should have from before. Testing the connection, making sure you can count.
- 55:49
Now you should actually get 154 nodes. So here, and if you come by our booth, we can show you much more, uh, sort of exciting examples than, than the two text, uh, blobs that we have here.
- 56:02
Um, but here we have two different bios. Um, and basically the way that you can do this, and if you've already done some entity extraction, you're probably already familiar with this workflow, is we can define our domain model, um, in terms of, uh, Pydantic classes.
- 56:17
Uh, so here, basically, I'm gonna define my person with a name and an email, and then a list of skills, um, and then I have the skills field here.
- 56:29
Um, if you add relationship properties, you would have like a nose. Maybe like in a more complicated model, nose as a relationship would also have like a proficiency property, in which case this would be a list of, you know, nose skills, and then you would have...
- 56:46
A nose skill would have a class, um, would have a skill property inside of it. But you can see this is a very simple example. So all we're doing here is we're defining a list of skills that someone can have.
- 56:56
Skills just has the name property, and then we can create this person list. And then once we have our, uh, Pydantic, uh, class defined, uh, we can create our system message, uh, to basically be a prompt for our model.
- 57:10
Um, and then we can use, in this case, I used, uh, four one here, um, and we gave it the documents to, uh, to ingest, and then it will spit out at the end of this some JSON, uh, with those two people.
- 57:25
So we had two documents, each one, um, corresponded to one person, and then we got our emails and skills, um, with all their different names and such. Um, and once we have that, right, um, it's, it's from there it's pretty trivial to load in, and it's very similar to what we did last time.
- 57:44
Um, in fact, if we go down to our graph creation here, we'll see, uh, this isn't exactly the query that we had, but, uh, it's very similar where we've basically...
- 57:53
We're ingesting one person at a time, merging on that, uh, email address which we have indexed, sending the name, and then for each of the skills, that's sort of a list inside of there, um, we're gonna go ahead and merge, uh, the skill name and then that nose property connecting them together.
- 58:12
Um, and then of course I could go back to the graph and I can say, um, "These are Neo4j employees that I loaded." Um, but I can go ahead and look for one of them in the graph, and I should get them back here, um, where I have them and I have the different skills, uh, that they
- 58:30
went ahead and, uh, picked up, um, that I can put inside of the database. So, um, very, very simple. Uh, we have, um, our own, um, GraphRAG Python package as well, which is very good for...
- 58:46
And, and also our Knowledge Graph Builder, if you look at some of the code that we used, um, to implement that, which is kind of like a reference UI, um, which has more sort of examples around if you wanted to do things like document chunking with overlap.
- 58:58
Um, you know, and, and of course also doing like multi-threading with Async and stuff like that, so you're not just, you know, doing a four loop, you know, over, over a bunch of, over a bunch of bios.
- 59:10
So we have all of that. If you stop by the booth, we'll, we can, we can give you more around that. Um, but like I said, especially for this crowd, because you guys are already familiar with this, um, you know, this is, this is a very short module.
- 59:22
And yes?
- 59:22
So we've just gone through two resumes, added them to the graph. Do we now need to go recluster our data and re-embed it and, and do that, or?
- 59:32
So, so we would have to do that, yes. So ideally, I would have done this in an, in an order where I would have done this first and then we've, we've got...
- 59:40
We would have gone through like the clustering.
- 59:42
You're materializing new relationships in with weights on them.
- 59:46
We're, we're putting new relationships in. These don't have weights on them, but if we wanted to reformulate our communities, um-
- 59:54
These are-
- 59:54
Here you go, sir. Then everyone can hear you.
- 59:56
Thank you. So, so the, the communities... Kind of following what you're doing. You're adding extra links in between the nodes that have weights on them, whether it's for semantic distance for skills, or whether it's for pr- you know, you're, you are X hops away from somebody else in terms of some other distance computation that you've materialized.
- 1:00:19
Yeah. So we would, we would ideally rerun that in a recurring way a- as we upload data, right? So because I created-- I did, in this case, create, I think, some new skills in addition to the people.
- 1:00:32
So, like, has same, has similar skill set that we, we, we would redo that, and then we would get a couple more relationships there. Um, the semantic one, if we created new skills, we would create new semantic relationships between the skills.
- 1:00:47
Yes.
- 1:00:47
Okay. Thank you.
- 1:00:50
Cheers.
- 1:00:56
All right. Any other questions before we move on?
- 1:01:01
I'm ready.
- 1:01:02
All right. Well, then that was a, uh, a very quick module. So I'll go over and cover, um, just some other topics around this very, very quickly.
- 1:01:14
Inside of my, inside of my slides here.
- 1:01:18
Um, so what we saw was an example where I'd call it entity extraction or named entity recognition, where we were taking a document and we were literally breaking out people, places, and things, and relationships from within that document.
- 1:01:34
Um, there's other things that we can do, like for example, if we have certain types of documents, like from a catalog, or in this case RFPs, um, we can start to break things out by actual document structure.
- 1:01:47
So I'm only gonna walk through this just so you understand that there's different types of extraction that we can do to create graphs. Um, for example, if you know what the anatomy of a document is, like in this case if we have an RFP, um, we know that this RFP is gonna be designed in a way where
- 1:02:03
there'll be different sections, that we have an intro, objective, proposal, and subsections within that. We can actually create a graph out of those things too. So this is another way that we can do, um ...
- 1:02:15
I would call it more like document extraction, um, where we're actually putting the, the metadata of the document and modeling it as a graph. Uh, and the advantage of doing things this way is that basically as you start to embed these different pieces, um, and put them into a knowledge graph, um, you can basically do these patterns
- 1:02:37
where you can do these searches on either entities that come from different chunks, um, and you can sort of go up and down, uh, these, uh, document hierarchies to find things, which can be very helpful if you have documents that always have repeated structure, um, so you know that entity sometime connects between those, the structures of those
- 1:02:57
documents. You can start to incorporate that inside of your, um, graph retrieval queries. Um, and then it also gives you a way to do community summaries, 'cause we saw Leiden before.
- 1:03:10
Um, but also if you have documents that give you a natural, uh, hierarchy, um, you have a way of also summarizing information, um, across those documents as well. Um, yep.
- 1:03:21
Why do you have entities and, um, documents and chunks in the same ontology, as opposed to extracting entities and, um, and just creating a separate ontology separate from documents and chunks?
- 1:03:33
Why do you combine the two?
- 1:03:35
Well, I think when they're combined, you, you can just do traversals between them. So-
- 1:03:40
What do you have, like, what's an example of a, of a traversal you, traversal that you wanna do between entities and chunks?
- 1:03:45
An example that you wanna do between a traversal of entities and chunks? Like, I say, like, legal contracts is a good example, where if you know, like, you wanna search for different legal clauses, but then, like the expiry date, that might be somewhere else.
- 1:03:57
So, like, that, that would be one example.
- 1:04:00
Yeah. Thank you.
- 1:04:02
Yeah. Yes.
- 1:04:03
So just making sure I understand. So, like-
- 1:04:05
There's one.
- 1:04:05
Yeah, sure. So just wanna make sure I'm, uh, clear on what you're talking about traversing the document. So in a legal document, say, like a data protection clause across multiple vendors or something like that, and comparing the language?
- 1:04:18
Like, is that a use case?
- 1:04:21
Yeah.
- 1:04:22
Yeah. Okay.
- 1:04:23
Yeah, so that and then, like, you know, so there might be like, um, like a perpetuity piece or like, you know-
- 1:04:30
Whatever
- 1:04:30
... dates and different things, and then being able to kind of traverse over that document to find that in addition to the entities.
- 1:04:40
Anything else? All right. All righty, so I just wanted to introduce that as, as another example of how to do things, um, for the third module, because we're already at 10:07.
- 1:04:53
So let me just go ahead and jump into the thing so you'll get to see it.
- 1:04:59
Uh, we'll go over to module three. And this is gonna be very simple. So has, has that... Who here, and we probably asked this in the beginning, I think we already asked how many people have experience building agents.
- 1:05:10
This is gonna be very simple. It's just gonna be a LaneGraph agent that we're gonna, that we're gonna make here. Um, basically what we're going to do is, again, a similar setup with our environments file.
- 1:05:22
We're gonna connect to Neo4j, test our connection. Um, there's gonna be four tools that we wanna build. We wanna be able to retrieve the skills of a person. We wanna be able to retrieve similar skills to other skills.
- 1:05:38
Similar people, like if we wanted to find out who's another good person to work on a thing. Um, and then retrieve people based on a set of skills. And in this example, um, we're basically going to, um, do a lot of tools first.
- 1:05:54
So at the end of this notebook, there's gonna be like that text to Cypher stuff where you get the schema back. But here what we're gonna go over first is actually going to be putting these different tools together.
- 1:06:05
Um, and we do that by graph patterns, and it's the same graph patterns that we've been going over. Uh, so for example here, right, if we just wanna find, um, the skills that someone knows, it's very simple, right?
- 1:06:15
Just person matching to their skills. Um, and as you go down this notebook, basically what you're seeing is all the different patterns. So the second is retrieving people with similar skills.
- 1:06:27
And here we're actually going to use the, um, the vector index and that similar semantic relationship. Um, so we're basically going to pull, um... Actually, this one is searching for people with skills.
- 1:06:40
I apologize. So in this one, the, this is, um,
- 1:06:45
you're gonna look for skills. So for example, you, if a user puts in different skills, those might not match the skills we have inside of the database exactly word for word.
- 1:06:55
So you're gonna use vector search to pull out, um, the specific skills and what's semantically similar to those skills, and we can do some scoring thresholds in here to pull back exactly what we want.
- 1:07:07
Um, and then that will go ahead and return some skills. So if, if we had, for example, right, like this continuous delivery, cloud native, and security, um, this would be like the types of skills that we pull back from that.
- 1:07:20
The person similarity. Um, there's a few different ways that we can do that, and we've talked about that a lot towards the beginning. Um, we can do it by community.
- 1:07:31
So we can look for people that know different skills. We can get all of their names, and then we can look for, um, that Leiden community that we created.
- 1:07:41
We can look for all the skills that those people know, and basically what we're doing at that point is we're looking for people inside of the same, uh, skills, uh, community.
- 1:07:51
Um, but the other way that you can do that is, um, you can look for similar skill sets, um, using the, uh, similar skill set relationship, so the hard-coded relationship that we've made from before, uh, which basically looks at, hey, how many-- how-- what's the actual skill overlap if you just looked at, um, who knows what inside
- 1:08:11
of the graph? Um, and that will bring back, um, some answers here between-- So we were looking at John Garcia, and we're saying, "Hey, find similar people," and then we can get like a score count of overlap, um, to the, to the different people here.
- 1:08:26
Um, and then we can start adding in that semantic similarity. So this is where we get this big query, right? But what this query is actually doing is it's sort of balancing between the, um, similar skill set and semantically similar skill set.
- 1:08:41
So it's kind of taking both those scores and adding them together. Um, and then from there we get, uh, a floating number score, um, and a little bit of a different answer that's not just based on hard skill connections, but also skills that are kinda close together.
- 1:08:56
Um, and we can weight those independently as well. Um, and we can also recommend people given a set of skills. So if we have a set of skills here, um, we can just do, uh, a vector search on, um, on those skills.
- 1:09:12
Um, and then-- Actually, this one... Combination of person given skills. Skills.
- 1:09:22
Yeah, here it is. Um, so basically, um, the query was broken out into two parts just because this is, this is kind of a big thing to look at.
- 1:09:31
But the idea with this is we can get, um... We can basically do vector search on skills, get semantically similar skills, um, and then a find person who knows those skills.
- 1:09:41
So very similar to some of the last ones. Um, and then we can get a skill count for all of those groups and get people back. Um, when we actually define the functions for our agent, um, we're going to create here, um, a skills object, which basically is just gonna help us, um, with some of our function
- 1:10:00
arguments and returns. Uh, but basically first tool, retrieve skills of person, very simple query. Um, and then we'll have, uh, down here for tool two, for tool two, when we say find similar skills, um, what we're gonna look at here is again that query where we're gonna do that semantic similarity between skills.
- 1:10:22
So we're gonna do a vector search to find skills, and then we're gonna go out one hop on semantic similarity. Um, and then we're basically gonna collect everything and return it.
- 1:10:32
And then for the third one, we're gonna do that weighting, um, because this tool three is gonna be for person similarity. We're gonna do that weighting between the similar semantic and the similar skill set with that larger query.
- 1:10:46
And then I know I'm going through this kinda quickly. For the fourth one where we say find person based on skills, here again our entry point is gonna be a vector search on skills, um, going out to match those semantically similar skills.
- 1:11:00
But then kind of at the end of that, uh, we'll add on a traversal that will attach the person to knowing those skills, count who knows the most, and then effectively return that.
- 1:11:11
So those are the four tools that we're gonna end up using for this agent. Um, when we set up the agent here, if you're familiar with, uh, how LangGraph works, basically, uh, we, we get our LLM, we test that it's alive, we define our list of tools.
- 1:11:28
We can, if we didn't wanna do this in an agentic way, right, we can just bind our tools to our LLM, um, and we can, we can invoke our LLM with tools.
- 1:11:38
Um, but what we're going to do instead, because this is just showing invoking the different, the different tools, um, is we are gonna go and run it with an agent.
- 1:11:49
So we're gonna use create React agent, which comes from LangGraph. Um, it's one of their pre-built agent, um, that uses the, the React, um, I don't know if you'd call it a framework, but, um, sort of that methodology, uh, to build an agent.
- 1:12:05
And effectively, um, once we do that, we give it the LLM, we give it the four tools, uh, that we had, and then, um, we can see that here we're just testing, we're saying hi, and we're just making sure we get some response back.
- 1:12:21
There's a utility function here just to make it easier running in the notebook, um, which will basically just, you know, do this, some of the, um, some of the streaming methodology.
- 1:12:31
So I can just say, "Hey, what skills, um, does Christophe have?" And then if I run that, and I don't know if I need to rerun my agent here.
- 1:12:40
Looks like not. Everything's running. Um, so you'll see it says, uh, when I ran that, and actually I ran it for the wrong question.
- 1:12:52
Here, what skills does Christophe have? Person named Christophe, and then it will bring back his skills. So you see there it will choose to use the, uh, retrieve skills of person.
- 1:13:04
Um, and similarly, if I went down and I said, you know, "What skills are similar to Power BI and data visualization?" Um, it'll go through and choose the appropriate, um,
- 1:13:17
you know, the, the appropriate tool for the job. So in this case- Uh, find similar skills. It'll pull those back, and you'll see going down, right, if I said, "Well, what person has similar skills to, you know, this other person here?"
- 1:13:32
Um, then it will know, oh, I need person similarity. So it will go ahead and use that specific tool. So in this case, what we're doing is we're providing a bunch of tools that are presumably expert tools that we can give to the model, and then it will know that, okay, I have to go ahead and pull
- 1:13:46
those, um, those specific tools to be able to, uh, provide a response.
- 1:13:52
And then there's a little app down here as well if you wanted to run the, uh, chatbot. So,
- 1:13:58
um, it's a little Gradio app here. Um, but basically if I ran that, I can go ahead and come in here, and then I can have a little conversation with it.
- 1:14:11
So this is very small, but what skills are similar? You know, I can go ahead and ask it in here, and then provided everything's working, it'll go ahead and choose the appropriate tool, and then I can say, "Well, who knows," um, you know, um, maybe I'll just say those skills, and it should,
- 1:14:34
um, go ahead and pull the appropriate tool to be able to find out who knows all these different skills, right? And if I go back to my, uh, to my example here, I should see the query logic that it used.
- 1:14:48
So first, you know, it said find similar skills to what I just mentioned, 'cause I asked about Power BI, and then after that, I asked about, uh, people who know skills, so it said, "Find persons based on similar skills."
- 1:15:01
And likewise, I can say, you know, "Who is, um, similar to those people
- 1:15:09
in the graph?" And it will likewise go through, and it should, you know, understand that it needs to use the find other similar persons tools, um, to be able to do that.
- 1:15:23
So you'll see if I, if I was to keep going down,
- 1:15:27
I should get calls, um, here to find persons with similar... We have it here. Yeah, person similarity. So it just called the person similarity for each person.
- 1:15:40
Um, and I know we only have a few minutes left. Uh, there is, if you wanted to run this further, basically, um, I have a text-to-Cypher example. So this is where I'll have an example of passing it the annotated schema.
- 1:15:53
So this is getting to kind of what you were asking about, right? Um, where I've provided these descriptions. Um, so it's sort of like annotations for the schema as well.
- 1:16:04
And then I can go ahead and give that to an aggregation query function that will have... There's also an LLM inside of here that will create the Cypher. Um, but you can see in here I asked it some questions like describe communities.
- 1:16:17
It was able to understand that it needed to grab, you know, the, the match person knows skill, and then it needed to grab the lighting community. So it, it knew from the schema, right, that it needed to generate this Cypher.
- 1:16:30
Um, and there's a couple more examples of that in the notebook. Um, are there any quest- I know I just went over a lot. Um,
- 1:16:37
are there any questions from that that are worth answering now while we have just a couple minutes left?
- 1:16:43
How long will the- Got it. Got you. Sorry, so slow. Oh, I didn't even know you were here. Yeah. How long will the, uh, Jupyter server be up if we wanna play with this?
- 1:16:53
Jupyter server is gonna go down very quickly. But if you look at the deck, at the end of the deck, I have a link to the code, and the data's all in GitHub too.
- 1:17:04
So basically, if, if you go here, that's the GitHub repository, so you can play with it. I-
- 1:17:11
How do we get the deck?
- 1:17:12
What's that? The, the deck is in the s- Do you have access to the Slack channel?
- 1:17:16
Yes.
- 1:17:17
So the deck is in the Slack channel. Um, and I, I'll go ahead and, and jump to that in a second. But there's the GitHub repository. You can use Aura, um, console.
- 1:17:29
We have a free trial that you can use. You can just set up a cloud database, and you can load the data into there. Um, also before you guys leave, there's a meetup happening tomorrow.
- 1:17:40
Um-
- 1:17:41
It's today. It's tonight.
- 1:17:42
It's tonight?
- 1:17:42
Tomorrow's tonight, yeah.
- 1:17:43
Oh, sorry. Tonight at 5:00. Um, that... And there's a link there for more information on that. Um, and then we also have another workshop at, uh, 1:00 where today was very simple, like we're gonna go over more graph analytics type of stuff in that workshop.
- 1:18:01
So like the community stuff that I was doing, we're gonna dive more into depth on that in that workshop. Um, other than that, uh, come by our booth if you have more questions.
- 1:18:13
We're gonna be wherever right there is. I don't think we have a big expo hall, but if you wanna see Neo4j MCP servers, ADK examples, more knowledge graph construction, um, all a great, uh, you know, place to come to ask all those types of questions. [outro music]