NODES AI

NODES AI 2026 - Agentic AI with Knowledge Graphs on the JVM: A Music Graph in Practice

29:05 · 15 Apr 2026 · YouTube

About this talk

This talk explores the implementation of Agentic AI within the Java Virtual Machine (JVM) to enhance music discovery through a personalized graph of music owned by the speaker. The speaker explains how they created a graph model using Neo4j to organize their music collection into artists, albums, genres, and tags, enabling intelligent recommendations. Two agents are introduced: a music discovery agent that interacts with users to suggest albums from the collection and a more autonomous agent that finds listening paths through album transitions. The framework used for building these agents is Embable, which incorporates various planning strategies and maintains a fully type-safe environment. The speaker emphasizes the importance of structure in graph similarity and the careful design of guardrails to ensure the AI's recommendations align with the user's tastes.

Full transcript

[music] Welcome everyone to uh this session. It's probably the only session uh today that talks about Agentic AI but on on the JVM. Uh I have a lot to show you. So let's let's just get into it. Um so what's behind the session right there is a story behind this session. Um, I I realized that all of the streaming platforms that I was using, they o over

time I began to miss the artists that I really like listening to. And I think that's just the way the the algorithms um optimize for you to be more engaged and not to be uh you know surprised or uh bring up artists from the past, right? So once uh once this sort of started happening to me, I thought, okay, I do have a physical music collection and

and that's sitting on the shelf gathering a fair bit of dust. So what can I do to start playing that and also being able to focus on my music more and really getting back to the music that I I intentionally buy, right, and like to listen to. So what I did is I took my record collection and I put it into Neo forj and then I built

an agent. well, two of them to help me use it and that's what we're going to go through today. So, the graph is really really represents my record collection, right? This is not a generic uh music catalog. It is very very specifically the music uh that I own, the physical uh records that I own. So, I'm going to take you very quickly through the graph model. It

is very very simple and very intuitive. You have artists, you have albums, and you have genres and you have tags. uh your usual properties on on artists and albums. Uh a couple of things I want to point out. We so the whole concept of of having an agent uh recommend music to you is really based on the shape of that graph and how artists are connected to

to other artists, right? That's why you see that we have um a lot of similarity uh relationships. So I use um I use GDS to act to calculate the similarity a node similarity for tags to tags genres to genres um summaries. So you'll see that there's a summary on every artist and album. Now this summary is a string which is is gathered from or rather it's an

LLM right now because it's a music graph. Uh the knowledge the world knowledge it has about music is fairly fairly reliable good enough to to write you a very nice summary. So that's what I went with and then there are embeddings on those summaries which is really important because uh these these summaries give you that stylistic semantic meaning and uh the relationships tell you you know how

well connected uh your your graph is and you know clusters and so on right so all the similarity relations actually roll up into a similar relation and that's what we are going to uh use today now um the data in the graph uh I if you collect records Discs is a fairly fairly well-known platform for record collectors. What I did is I exported I exported my catalog.

I imported it into Neo forj but because it has um it doesn't have all the information I need. I leaned on music brains uh which is a super good collection of u music data. I actually downloaded their um their Postgress database. Um it's pretty massive but uh you can do it overnight. And what I do is uh I look I look into music brains to fetch things

like you know the um the music brains ID start year end year and and really factual information about the artist and the album that I don't yet trust an LLM for. Then you have your similarity signals as we spoke about and the embeddings on top. So if you have a look at the graph below, this is really just the um artist force directed graph and you can

already see without me doing anything um some very very definite patterns I have in my um in my graph. So to the uh to the right you will see fairly interconnected clusters that is hard rock, heavy metal um uh classic American British rock. Then you have this little vague cluster in the middle, a bridge in the middle with some classical and then it kind of shoots off

to to a bit of Iberian and and Portuguese. Right? So this collection is what really serves to ground the LLM to give me answers based on what I really like listening to and not pick you know a random genre that I absolutely do not listen to and maybe don't have any interest in listening to. Right? So, so this is the purpose between uh behind having a graph

uh with with similarity that structures might taste right. So, two agents today. Uh the first is a music discovery agent. Now, this one is is a more of an interactive chatty kind of agent and it recommends what to play from my uh collection, right? So, it can recommend an album to me. it can uh suggest maybe an album that I should have in my collection um and

it can actually add that album to my collection. So this agent also helps me to expand my underlying underlying graph. The second agent is also very interesting. This is a more autonomous agent and what it does is it finds listening parts uh through uh through my collection. So what's a listening part? Right? I I think this is important to uh to make to grasp before we get

through, you know, what the what the stages look like. So, so if you if you take an example, right, if you if you take the Beatles for example, right, they they didn't pop out of nowhere and and start singing. They were they were influenced by uh Chuck Berry and Moto and some early skiffl and and what you see in the Beatles in their early stage is they

had these these very very catchy tunes and a bit of rock and roll energy but then as as they developed their own style uh later as you know as they progressed they lean more into psychedelia and um non-western influences like uh Indian instruments right and why is this important because they they were one of the artists that influenced a a whole number of artists around them at

that time and even to today. And why this is important and and this tells you now why we have the similarities is if you look at two examples uh that you I'm sure at least you relate to one of them right two examples where you can you can actually listen and pick up that Beatles stone immediately is is Oasis right so they they based they were influenced

by the early stage Beatles it was you know big singable hooks and um but they added of course their own swagger so they developed later and then you electric light orchestra which based itself on the later Beatles. So some lush harmonies, a lot of strings, a lot of studio music production and and this is important because when we build when we build a journey across albums, what

you want your year to pick up is some thread holding them together. You don't want an abrupt uh change between between genres, right? So this agent really does that. um it uses Neo forj heavily to actually select the albums and build the parts. But where it steps in is to evaluate the quality of the path it builds. It identifies okay you know you have a gap and

you can have a gap and I do have many gaps because my record collection is quite limited. Uh it isn't it isn't very large. So there will be gaps where I where I don't transition well between artists and then it will auton autonomously decide okay I'm going to go go out and find the best artist that will bridge this gap um go fetch it from music brains

add it to the graph repeat till it reaches either a suitable path um or it gives up because you know can't can't do better with with my collection. Now before we get into the code we uh this is based on the embable uh agent framework right it's a it's a framework built on the JVM for agendic flows uh what's cool about this framework is I think primarily

uh the planning strategies so so the one that started off with which is the one I actually use in this talk today is gulp which is goal oriented action uh uh programming and it's a deterministic form of reaching a goal through a set of parts uh through a set of actions Right. So, Embable works on the concepts of steps and these are these are called actions and

then there are conditions which determine you know the prerequisites and the post condition for a particular step and what it does is it basically with without an LLM to plan it will look at what's the best way that I can get to this goal right and and actually behind the covers that's that's also a graph graph it uses the AAR method so we're going to look at

that today uh fairly Recently there were two more um planning strategies that were introduced. One is utility more for chatlike situations and then the supervisor which is a non-deterministic uh sort of planning which we're not going to touch today. Uh other important uh other important things to note about the embable framework is you use your domain right uh fully type safe. There's no free form text. Uh

if you if you refactor your agents aren't going to break your logic isn't going to break. if you have a typo, you aren't going to break. So, it's designed with uh testability in mind from the get- go. And it's built on Spring. So, for enterprise applications, uh you can leverage, you know, the already existing mature capabilities like dependency injection or observability and security. So, we're going to

jump into the first agent, um a more simple one. And before I get into the code, I want to show you I want to explain to you what it's doing so it's easier to relate to the code. Now this is the flow for the music discovery agent. There are three possible things it can do. One is it can recommend albums from my collection. The second one is

can suggest new artists to explore. And this is purely a suggestion. It doesn't modify my collection. Um and then if I if I actually wanted to save that artist, then I've got to tell it, okay, go and save the artist. So the way it's set up is the user input which is my prompt to to the agent. This triggers off everything. Um we have an LLM that

will uh so we have an LLM that will basically look at look at my query and produce a strongly typed object out of this. Right now that object is uh is a is a kind of discovery request. It can be one of these three. So what I'm what the framework does is it says okay you know I' I've got a discovery request in hand which one is

it and can I achieve the goal and then it will route you through one of these three branches. Uh you can vary as you can see in here you can vary the the models that you that you use for every every action um depending on you know cost or creativity and so on so forth and you can have a mix of everything doesn't have to be an

LLM. Uh you can see that the ad is really just service logic where we uh we we fetch the artist from music brains. This is uh JDBC driver to postgress but we put in a little LLM at the end because remember there are those summaries which don't really exist in in music brains. So here we do a quick hop out to an LLM to uh fetch the

the summary uh run the embeddings uh refresh the similarity the projections and then we're off. Yep. Okay. So, let's let's have a look at the first one. So, I'm going to flip between a couple of of demos, the code, and the explanation so that you don't have to watch uh the code keep running. Okay. So, the first question is it's going to pick something from my collection.

Now, before I show you the code and and that's going to go off and do its thing, uh here's what what this is really doing uh behind the scenes. So, first of all, the framework has picked the music discovery agent with a 95% confidence. Remember, we have two and it's picked it based on um what goal it thinks I want to achieve. Then it gets into the

planning phase. So, it uses a blackboard kind of like a heap to store the objects, the current state, your requests, and so on. And it uses this to discover you know or rather to determine what's the current state of the world and how can I get from the current state of the world to the goal right and every time an action gets executed uh the state of

the world obviously has changed there are new objects maybe uh created there are new results it will re-evaluate its plan to get to the goal right so uh it goes through the discovery request recommend for collection and then you have the goal achieved so let's have a quick look and see if it finished. It actually did. You can see in here. Um, so so that that uh

that snarky uh tone is really from is really from the framework. I haven't figured out how to change the tone of of the uh of the assistant talking back, but you can see it came back uh with these suggestions. And um I'd also note that we could have run this as an MCP server in Claude. Uh, I have the results here, but I chose to run it

from the uh, terminal because that's that's really fast and I don't want to keep you uh, waiting in the demo. If you w were to run it in cloud, you expose the the MCP server. Very simple. Just drop in the dependency and you're done. Um, and you can connect it uh to to any, you know, to any tool that uh, uses an MCP server. So, let's quickly

um, get into a bit of the code. So we have the music discovery agent annotated with agent. This makes it as well an injectable um a discoverable spring uh bean. The description is important because that's what the agent uses to uh determine which uh that's what the framework uses to determine which agent it should call. And then you have I've collapsed the implementation of the methods because

that's not so interesting. But I want to show you what each of the actions look like. Right? Remember each of the actions represent a potential step in the path to achieve a goal. Now in this case we have three goals because uh because a user could have could accomplish what they want in one of three ways in a single agent. So uh you have the recommend we

have an achieves goal which basically tells the uh framework that okay the goal is accomplished we can terminate. Now um we have an action which tells you what it what it does but more importantly because we're using go and we're not using um we're not using an LLM to decide what the path is. The way embel works is it looks really at um how to get to

this goal from these right your method parameters. So, so to be in to be able to recommend something from my collection, it needs a recommend from collection intent, right? How does it get a recommend from collection and intent? The only way it can actually get it is by um is from here. So, we know that I've asked a question. The user input is on the blackboard. So

it can definitely start from here and then it produces a discovery intent out of which um the recommendation from collection intent is an implementation of that one and that's really how embable decides how I'm going to chain uh these things uh together. Yep. So that's how this agent worked inside it is um whatever business logic you need. So let let me open uh so this suggest collection

essentially has a prompt. You can put whatever you like in it and then you specify if you wish to change the model which one you want to use and then the result of that will be a strongly typed uh domain object right so so so it's easy to test it's easy to predict because everything works through through your domain objects and not through strings which is which

is quite important okay let's let's head back here and let's get to uh the next one which is suggesting a collection expansion. Now, because I'm using this chat mode in in um in the shell uh it uses the utility AI, I think behind the behind the scenes. So, it's actually maintaining our conversation uh history. Okay, that that was that was pretty fast. Okay, so it's gone and

evaluated that. I'm going to show you because it's easier to read on the screen than in in the um in the logs. Here's what it did. By now, you can start to recognize this pattern. It chose the music discovery agent. It interpreted my question and it decided that it needs to get to the suggest expansion goal. It has the select uh suggest collection expansion object which it

derived from my uh prompt. And then it used a couple of tools. So the tools I have is um fetching artists or fetching related artists. These are just cipher queries under the hood right um I use NEOJ OGM for this. So basically makes a query out to Neo forj and comes back with whatever data and then because it also has the shape of the graph. Um it

does a bit of reasoning to say okay um you know is this in line with any of the clusters I actually have and it does find that you know it is in line with clusters three and four and then it goes ahead and says okay here are three potential artists that you can um you can add to the Right. So now I'm actually going to take that

first artist and add it. Oops, not there. Should be here. Yep. Right. So let's see what this one is doing. So very similar pattern into how it figures out which goal, which path it wants to evaluate. But what it does in this case and what you're not seeing here is it it invokes a standard service which is um JDBC over to Postgress. It goes and it um

it fetches the artist and the album that I've asked it to fetch from music brains. Then it comes back it makes uh the LLM calls to get the summary. And then we have uh some business logic in there which is again uh triggering various cipher statements to update the similarity edges. Rerun the projections and and this helps because we need we need to make sure that when

we next want a recommendation that the new artist and the new album is very much part of the graph and it's possibly as well maybe shifted slightly uh the shape of your graph. Okay. Did this work? Yeah. Add it to your add it to your collection. Okay, let's let's go to the next one. So, this was a really really simple one. Um, but it shows you how

how embable actually plans. Now, we're going to get to the more complex agent. Now, for this one, we are still going to use uh the gulp method of uh planning, but we are going to use states. So, states are really useful when you want to do branching and looping and so on. So, before I get into the code, I want to show you what we are going

to do in this. So there are a couple of uh stages in here. The first stage is to extract uh entities, right? Um and create parts, right? So what is what is the user asking for? Are we where are we going from from this artist to this artist or this genre to this genre? And the the goal of the of stage zero is to create parts. Now

this is done uh through GDS yenk shortest parts. The next stage the next two stages are actually closely coupled because they can uh they can call each other and keep looping. So the first one is evaluating is evaluating the path. So now we give it we give these parts to an LLM and say what's the best path right? Um not only what's the best path but is

the path acceptable and by acceptable it will actually look at the transition of artists and then say okay good or no this this pair is pretty weak um not so good. If it was good, we skip over the expanding collection stage, go straight into, you know, picking up that path, pulling out the albums, uh, for each artist, and then, you know, returning building building a bit of

summary and returning to the user. But if it wasn't acceptable, then the LLM is going to basically say, okay, here's my weak pair. Let me go and find an artist that I think would stylistically bridge these two maintaining some form of continuity. Go to music brains, fetch the artist, add it to the graph, update all the similarities, re-evaluate the parts, right? So, so when you have these

stages, which I think is pretty neat. Um, the end of this stage is to return um an object of type evaluating part stage. clear the blackboard and this is remember this is kind of like the the memory that the framework is operating on uh because you're really starting from scratch right we don't want any of the old stuff we wanted to freshly evaluate new parts and repeat

now this can and does go on endlessly depending on you know how how helpful the LLM wants to be how stubborn it wants to be so we have a cut off here we wanted to try only for two attempts and then stop I think this is really important because when you're building things like this You also have to think about what makes sense. Does it make sense

for it to to loop endlessly, keep adding artists to your graph? For this particular use case, the answer is no. Because what it would do is it would dramatically change the shape of my collection and possibly even no longer align with my tastes. Right? So, so if my if my collection is sparse, so be it. We can't build a we can't build a path. I need to

get out there, buy some more albums, improve my listening taste and come back. Yeah. So, so these sorts of guardrails are really important, but they need to be thought of deliberately and what you how you want to influence uh the the end result. Yeah. Okay. So, um what are we going to do? I am going to let this run. Now this one is is a very challenging

path and I will tell you why the moment I set this Okay. So I'm going to get out of chat mode a bit and just go into standard um execution mode. I'm going to capture the prompts and the results in case we need to come back. So, this one is a challenging path because we're really we're really asking the agent to find us a collection of albums

that goes from uh you know this arena rock guitar heavy bonjovi all the way to a very um very emotionally intense um voice vocalled um Portuguese traditional father performance, right? uh they they are literally a night and day right so it's it's not going to be an easy path uh to find but I think this also exhibits how how the agent loops and where where it should

it should give up so this is while while that's going on um this is really the path that it actually uh took and I'm going to show you a better view um here so uh what this agent did is or what it's actually doing is it extracted the parts. Okay, this is all cool. And then these are the five parts that were created out of uh Neo

forj GDS Yens algorithm, right? Uh and you you see that it has found these five paths. So it did find a path, but when it went through the evaluation phase uh phase, it rightly rejected this pair. Every path actually contains this pair of of Linda Ronstat to uh Gordi Dana. And what it has in this is it basically gives you a reason as well to say like

look this is a really big listening gap and and any of any fan will know that it's it's a very jarring uh kind of transition. So now we start to enter our loops. we get into the first exam expansion loop where the LLM looks at this pair and says, "Okay, um I think I'm going to the best artist to bridge these two is uh Joan Buyers and

that one she brings in this rock um forky style with some traditional world music. Fair fair bridge, right? So the artist is fetched, put into the graph, parts are recalculated, retransition, and now we are back to the evaluation phase. So now we're in evaluation phase two. You can see that she has made it to path five, right? Sometimes it doesn't depending on you know how the similarity

gets calculated. But here she did make it. However, the LLM is going to reject it once again because now while while it probably likes the path, it really doesn't like sting to uh sting to Triana. And that that is a bit of bit of a weird a weird shift. So, it's going to go back and try to now bridge the gap between string uh Sting and Triana.

Here is where I mean it couldn't have planned this. Here is where it hallucinated and it actually picked an artist Triana which was already in our graph. You saw me add it um you know during this demo. It was there when I recorded this path. Um and that's really that's important because uh we have guardrails in there to say let's check if the artist already exists. If

not, we're going to reject and we're going to add it to a block list. So, so when we reject it and we evaluate again, we are going to instruct the LLM to not consider it. Um, I cannot tell you how many times it ignored the block list. So, these kinds of guardrails are really important. So, we say, okay, you know, go back and expand. But at this

point, right, we've exhausted our budget, right? We have no more budget. Um, we're going to exit out of here. We are going to select the best path which is this one. Uh build a playlist. So we're going to select albums. Then we're going to give it back to the LLM to say, "Hey, can you check these albums?" It's going to say, "Um, okay, I don't really like

um the 1987 album because the transition isn't very strong." It's going to look at into my own graph, swap the album for a better Whit Snake album, and then build your playlist with an explanation that I will not create. It's still a bit of stretch but um but not a bad attempt and this time it honestly gave up. Right. So I don't want to go through all

the code here because it is quite large but the main things I want to point out and I'm conscious of time is states right conditions that you can use for for branching logic and then the rest is pretty much the same. So the only thing you're doing is you're taking your actions and you are wrapping them into various stages. This was stage one. Um then you have

stage two and what embable enables you to do is really navigate between stages by simply deciding to uh return an object that moves you from stage one to stage two. Okay. So um things that really matter is first of all you should always consider whether you need an agent. Sometimes you just have a very normal boring old style um nonLM flow right service methods. Do you really

need an agent? Do you really need the um do you really need to build so many guardrails when it could have been straightforward code? Uh deterministic flows are still really important. They're useful for testability. Everything doesn't have to be non-deterministic and uh real systems have gaps and limits and these should be respected. So where you put in your cuto offs where you put in your uh budget

expiration and so on uh where you decide to retry where you decide to fail. And with that uh I think I'm almost at time. Thank you very much for attending and I hope you enjoyed the session.