NODES AI

NODES AI 2026 - Agentic AI Governance: Data Foundations & Real-Time Policy Enforcement

32:27 · 15 Apr 2026 · YouTube

About this talk

This talk covers the challenges and solutions involved in migrating the Policy Enforcement Service (PAS) from Cosmos DB to Neo4j. The speakers, William Liang and Murthy Channer Party, discuss the improvements realized through this migration, including enhanced data governance and real-time policy enforcement capabilities. They explain graph complexity and how it impacts performance and scalability. Key paradigm shifts facilitated by Neo4j involve transforming the data model, optimizing query performance by intelligently pruning the graph, and addressing concerns regarding write amplification and latency. Additionally, the session highlights an innovative AI workflow for user interaction, leveraging schema awareness to ensure accurate query generation without hallucinations.

Full transcript

[music] >> Uh yeah, hello again. Um everyone. Welcome to a gentle AI governance, data foundations, and real-time policy enforcement. So, uh today Murthy Channer Party and myself, William Liang, we're going to be basing to basically going to do a quick overview of our service, PAS, the policy enforcement service. We'll discuss how we migrated it from Cosmo DB uh to Neo4j, the paradigm shift we made along the

way, and how migrating to Neo4j allowed us to identify PAS and enabled um current and future AI workflows. Oh, before I start, a quick shout-out to um our colleague Prasidhya Paramita, who was instru- instrumental in helping us wire up uh a lot of uh the presentation today. Uh anyway, so what is [clears throat] data governance? Uh if you go to Adobe Experience League, it's defined as data

governance is an essential concept that's intertwined with every uh capability in Experience Platform. Uh what what exactly does that mean? Well, AEP, Adobe Experience Platform, is a platform that drives Adobe Experience Cloud applications, uh Journey Optimizer, Real-Time CDP, Customer Journey Analytics, and a core feature of AEP is data governance. Uh this is the framework uh that customers use to label what data can be used and define

policy for how that data can be used. And data governance is the framework that assures that that data and usage is compliant throughout the whole process. As data flows into a platform on the left-hand side of this diagram, through the platform, and down to the applications on the on the right-hand side of this diagram. Uh one of the uh core services of data governance is PAS, the

policy Uh this is the service um where various platform entities are registered and arranged in a label inheritance graph like this one you see here where the the uh properties of um the resources, the labels, and marking actions are represented by these rectangles. So, previously when PAS was uh implemented on top of Cosmo DB, uh that led us down the path where the business logic was one

where we did a a brute-force traversal of this graph to discover labels and marking actions. Uh that obviously doesn't perform or scale very well. Uh that led to high CPU utilization, high memory, high latency. Obviously, performance is a function of graph complexity. Um so, what is graph complexity? Well, graph complexity is actually a function of three things: graph size, graph density, graph connectivity. Uh graph size is

the number of nodes and the number of edges or relationship. Uh graph density is a function of local density and global density where local density are these supernodes kind of represented by this uh dark blue rectangle in the middle of the screen on this diagram where there's a lot of relationships fanning in from the left-hand side and relationships fanning out on the right-hand side. Uh graph density

is also a function of global density, which are the connectivity of subnodes uh subgraphs. So, on the right-hand side of this diagram, the orange nodes, you can see they're highly connected amongst them where the relationships are very dense. Then there's graph connectivity, which is the sheer number of paths and the depth of the path. And when you um the interesting thing is for highly connected sub graphs,

every time uh a path makes a hop, that actually multiplies the number of paths. Um, so migrating to Neo4j actually allowed us to uh address a bunch of these uh basically address all of these things through a number of paradigm shifts. Uh the first one being a structural shift where previously the data model was one where we had these fat property heavy nodes. And Neo4j allowed us

to essentially explode the data model out so that the properties are factored out into first class citizens into their own nodes. Uh that allowed us to essentially query the nodes directly and do forward and back backwards traversal uh straight from the nodes. Um, the other two paradigm shifts are essentially logical shifts and workload shifts where we logically evolve from a brute force traversal to essentially intelligently pruning

away large portions of the graph, so making us ourselves less sensitive to the complexity of the graph. And the workload shift is moving the for from being uh having expensive reads to expensive writes, so reads are now much more performant. But we we do have to be more conscious about how we manage our writes and the scope of the writes. Um, so with respect to logical shift,

like I said, previously we would have a graph like this. We would have to do a brute force traversal along this in throughout this graph to discover a specific label, say one of these brown nodes, and then a specific marking action, say a pink node, and to determine if there's a path between those. Um, now that we exploded out our data models and we can query the

the the label the properties directly, we can essentially query for the specific uh label and the specific marking action. So, in this example, if you can imagine if we need to look for say the first brown node and the first pink node, and then just establish if there's a path between them on this left-hand side, we are we can effectively prune away all the complexity on the

right-hand side. So, regardless how complex, how large and complex the graph is, we we can throw it out away cuz it that's irrelevant to what we're looking for. So, essentially, we eliminate proof for traversal and simply prune away all the irrelevant parts of the graph. Um so, having done all this, that allows us to make our reads super fast, but we do have to be careful about

how we manage the writes. So, we have to put the graph in the correct state um in order to to facilitate these writes. Now, when we talk about writes, there's three elements uh three facets we have to be conscious about. One is write amplification, then there's write serialization, and write latency. Write amplification is the fan out effect. So, in this diagram, if you imagine an update to

this pink node will fan out writes to the green nodes, which which will fan out writes to red nodes, which fan out writes to the blue nodes. So, we have to make sure we manage these fan outs to limit the scope of the Write serialization is is imagine there's uh concurrent updates to several green nodes. They're effectively going to serialize around this pink node. So, again, we

have to make sure that we minimize the scope of the writes so we we're not serializing a bunch of these things around certain nodes. And write latency is is the fact that all writes go to the the leader node in Neo4j Aura. So, we want to make sure we we minimize the workload that's going to leader node. So, how do we address all these? Uh well, basically,

to address the fan out, we need to basically remove any unnecessarily unnecessary relationship. Don't be too verbose. So, in this example, if there's an update to a a yellow node and it fans out to the pink nodes, we want to eliminate any relationship to essentially eliminate any fan out from the pink nodes. Um right serialization is um is this again, uh if there's concurrent updates to the

yellow nodes and they all fan out to these pink nodes, we want to limit the scope so the left-hand side yellow node fans out to uh write to the to the pink node, but the scope is really the left pink node. We want to limit that scope so it doesn't uh block and serialize updates to the other uh yellow nodes on the And then again, to address

latency, uh these rights all all go to the leader node in Neo4j Aura. We want to make sure we clearly delineate reads and writes so we can direct them to the leaders or the followers so that we essentially minimize the workload on the followers. Uh so, having done all this, this allowed us to essentially migrate to really reduce, increase our performance, reduce latency, allowed us to scale,

and that allowed us to identify PAS, the Policy Enforcement Service. So, at this point, I'll hand it off to Murthy and I'll let him talk about uh how we uh PAS. Uh you're muted, Murthy. >> Thank you. Thanks, William. Um Hello, everybody. I'm going to talk about, like William just showed us, how we solved the hard problem, which is the graph problem, um turning into lean nodes,

intelligent pruning, and a surgical uh search. That's the foundation. Now, I want to talk about what we build on top of it. Now that governance is solved, uh because once you have a particular governance graph, the next question is how do we let engineers or data stewards actually talk to it? Writing Cypher by hand to debug a policy violation at 2:00 a.m. is not the answer. So,

we built an agentic AI workflow that sits on top of this foundational graph. So, the architecture is pretty straightforward, but the details matters. So, a user types in a planning question in natural language. The question goes through a schema aware prompting, and then I'll explain what that is in a bit, and Claude generates a production grade Cypher. We execute against the and then the results are back

translated into an English natural language. The whole thing runs on LangGraph with MCP tools connected, backed by Claude on on our data centers. So, user types in the question, and we get the results in natural language. So, why out of the box LLMs decipher fails because the schema awareness is the difference between a demo and a production system. If you just send a natural language question to

an LLM and ask it can generate a Cypher, but there's a likelihood of hallucination. I'll write a match query when your label is entity. Silent wrong results are worse than errors. So, it will there's a possibility of generating a silent wrong queries and translate into a wrong results, too. Our solution is every prompt contains the exact AB schema, the schema that we have, so that we we

inject the context, and the LLM cannot hallucinate what logged in in the prompt. So, every query automatically scoped to the right tenant, the IMS are getting in sandbox. So, zero cross-tenant data leakage as well. So, that's the multi-tenancy safety here. The react loop, the other thing that separates this from query tool is the react loop, which actually does the reasoning and acting on and that's not a

single Cypher query. So, I'll show you in a bit the demo. The agent decomposes that into multiple targeted queries, and LangGraph manages that loop using making calls to the tools. The context injection, and we did not train Claude on AEP data. the because the training is so expensive, and it can go stale. Instead, we did is runtime context injection. Static schemas context is baked into the prompt.

Dynamic tenant context is injected at query time. Where this is heading in the future is towards the skills. The MD files that we do the skills, that's the future that we are heading towards. Your schema So, the the advantage here moving towards the future your schema evolves over a period of You update a file, no retraining or no redeployment. That's the direction the industry is moving. So,

yeah, with that said, we can jump into the demo. This is agent that we building as we see um it um actually we're injecting the context um and domain awareness to this. So in a sandbox this is a large this is a a demo graph that you can actually see what kind of labels it gives you a summary of how many schemas data sets and breakdown of

that. We can actually talk to the agent now it's actually making a call to prod and see that how many labels exist on this and it actually executes successfully and gives you that the core label of C10 has been applied. And it translated into a natural language which says the sandbox contains a single governance label. This appears to be a core classification label likely part of all

the details about governance data within the best graph. >> [sighs] >> We can also do an inspect graph. Now this is where I was trying to do um explain earlier um this breaks down into multiple queries and gives you the um see how the reasoning and translation of that into eight different queries behind the screens and it gives you how many entities count of this graph. Um

where what are the relationships for each um query it actually reasons acts on it and translate the results as well. Like the graph contains 65 relationships and what types. We can go through the um count of individual breakdown of the entities across different types. Um what it actually gives you is also um the out degrees like how deep and wide is the graph. This gives you a

visibility of which entity ID is like kind of um having the um out degree the max and have insights on that. So um engineers or um data stewards can act on it. Um, or we also have, uh, other queries including the labels, how many labels it generated. Um, and then what options are option labels we So, this is all, um, a context, um, driven, um, specific to

this particular, um, this is this is the thing that we built on top of it and we got a eight pre-built or queries that we did, um, on and then we have the summary coming up like this at the end. That's what we built on top of it. Um, right now, um, now I'm Uh, yeah, I'll pass it on to William now. That's the demo that I

had, um, for what we built. Um, Hey, William. >> There's a question in chat. I think you might be able to answer better than I can. Um, are there universal Cypher skills you're using, um, starting point to generate the queries? We're actually passing on, um, there's no generic queries, but we are actually passing on the context. We're injecting the context and the schema, like, um, for example,

governance label, um, we're making the LLM, um, prompts via our injecting at the runtime. So, um, we're we're not doing a generic or universal queries, um, that we are using. Um, more towards we're just, um, injecting, um, the graph, um, the schematics of the graph. For example, governance label or policy to make this LLM enable or the agent enable to answer the questions in the context. Um,

any other questions on there, William? Do you see any other >> I think that's the only one I see right >> Okay. >> Oh, here's one more. >> Yes, another one. Are you generating the schema using an LLM or an algorithm? We are using We are um actually generating via our own algorithm. Um so, the LS So, it allows us to keep the context intact and un-

not um depending on LLM's um hallucination. So, we do it with an algorithm. Good good question. >> [snorts] >> Oh, maybe there's another question, um William. You want to take it? I dropped this in Q&A. Can you provide some practical examples of the movement to lean nodes? properties you converted to nodes. >> Yeah, let me Yeah, I'll share again. Hang on, let me find that slide. Give

me 1 second. So, I I think basically uh I'll try to explain it uh briefly, and this might be a little redundant from what I said earlier, but if you imagine previously these brown, pink, and green nodes were all self-contained in these entities. So, what we previously had to do was in order to discover uh say a specific property for a brown node and a pink node,

we would have to traverse the graph by entities, or if we had to search for it, we had to search by entity first, and then introspect into the specific properties. So, we couldn't um we we effectively didn't have a straight way to get at the properties without uh going through an entity entity node. So, it's a very roundabout way. So, we didn't couldn't directly get at it.

We couldn't traverse by it. So, by exploding it out, it gave us a lot of flexibility. So, we can query these properties directly without having to have to know these specific entity it belongs to. And then we can essentially uh identify paths between specific properties without having to go through an entity. Uh I don't know if that answers your question, but that's sort of like a a

really quick way to explain to explain it. >> Hey William, there's a follow-up. Could you want to take that? >> I'm sorry, I can't see the uh chat while I'm sharing. >> Can you briefly talk about um the challenges you had or thought about while trying to make this robust? >> Oh, uh so, uh good question. So, there's actually quite a few. So, um there's probably a

couple ways to define robustness. Uh robustness for us was primarily in making sure um the system can scale up. So, our clients um as they scale up their workload and uh we have an increasing number of load. So, just to support that, we basically need to be able to scale up and scale out. Uh this is where again by exploding our data model, we can make our

APIs more refined and more precise. Uh so, we can get at the answers more quickly. And that's why it was important for us to essentially offload offload reads to make them less expensive. But again, the trade-off we is right became more expensive. So, uh we did solve one problem and introduce another one. But to solve the right problem, to minimize the cost of it, we really had

to minimize the scope of the rights. And that's essentially what I was getting to back here. Where once we made our reads super performant, so getting back to here, there's a lot of ways you can make reads super performant by actually introducing more relationships, making it more verbose. So, you can do shortcuts between certain properties. But of course, that creates a lot of fan out. Um which

is what we kind of found at some point. So, we actually had to limit the figure out ways to limit the scope of the the rights so that uh rights don't become prohibitively expensive. Um hopefully that answered it answered the question. >> Yeah, I think uh Yes, thank you. I think there was one more question. How do How does this correlate to the hierarchical knowledge graph? >>

Um I'm not quite sure what the question pertains to. Um can you clarify? >> Uh hierarchical knowledge graphs related to the explosion. >> Oh, oh, I think what you're trying to get at is going back to this. Oh, so that's right. Um So, basically >> in other words, so yeah, I think this was the question. Yeah, sorry we didn't >> Yeah, yeah, yeah, exactly. So, previously when

we didn't when they all the nodes were self-contained, the graph would look something like this where the properties weren't really uh surfaced. Um so, yes, you do get an explosion of the graph where it ends up like this. So, previously, we would have simply these dark purple nodes, but now the dark purple nodes are exploded out with properties that results in these orange, brown, green, pink nodes

and things like that. The interesting thing is we found that um with Neo4j, since they're highly optimized around this data model where you explode things out, and you have all these relationship, it's actually not very sensitive actually explode this out. So, it it can actually traverse these relationships super fast. So, actually, the more of a verbose you have, the the better. Uh and it actually makes the

API a little more uh human readable and refined. Again, the only challenge is So, obviously, if if this was super verbose, uh reads are super fast. We can shortcut things say between this pink node and this green node super easily. Again, the challenge becomes like how do you maintain all these different relationships? So, say I want to maintain a relation between this pink node and the various

green nodes, an update here will fan out to these purple nodes, fan out to the green nodes, which may fan out writes from the green nodes to brown nodes. So, that's what I mean by the fan out effect. So, uh you have to kind of find a balance between managing uh how much the the sort of minimum set of relationships you need to make your read super

performant without making your writes uh basically uh cost prohibitive. >> All right, I I there was another question. Uh how do you evaluate the effectiveness of the graph schema? How do you test if the entity and relationship relationships extracted are correct and relevant to the context? Yeah, that's a great question. So, I'll I'll take this William. So, we evaluate schema effectiveness through performance and parity. We look

for index free adjacency ensuring or making sure traversals don't turn into a full scans. We use structural equivalence testing during the migrations that we ran. And you know, for the EIA agents, we use schema awareness. If the model consists can consist and generate 100% accuracy, that's how we test it. So, correctness is basically a cipher without hallucinating about the labels. The schema is contextually relevant. So, basically,

we also if you've seen for debugging, we have enabled and we have a feedback loop as well where we we enable the logging of the query, what the query was generated, and how relevant is it to the question to get the feedback on. >> I think there is a question, William. What is your comment on creating in this with respect to having external properties and entity self-contained?

>> Okay, let me share again. this is a I think you were trying to ask what I mean by exploding out the um So, previously, all these the brown, pink, and green, those were completely self-contained in the entity itself. So, if we go back to this earlier diagram, uh these rectangular nodes that represent the properties were actually contained within as properties in this node. So, imagine in

Cosmos DB in a document DB, these are all just single documents where all these properties are in there, and that's what I mean by self-contained. So, we now explode out the properties. So, all the properties are distinct nodes. So, this entity actually doesn't contain any property in itself. It points to properties. So, >> Uh I think there is follow up a couple of follow ups. Are you

doing any schema prunings? Um about that, and then the follow up is now how to allocate in indexes of the two different approaches to property attachments. This affect the rights expensive expenses trade-off. >> Okay. Let me address the first one first. I I think the question around schema pruning is it's a graph schema Um so, the graph schema itself is defined and is a prescribed workflow. it's

not something that changes over time. So, we basically set that in stone, and we use that as sort of the the uh model for which how the workflow the API workflow should work. Uh so, it's essentially set in stone. So, that So, that that doesn't change. The question, the second question, let me see. How to >> I'll I'll read it out for you again, um uh it

was it was a follow up uh from the previous. How do How to allocate indexes property attachment? Does affect the rights expensive trade-off? >> Uh uh yeah, exactly. So, that's where we actually leverage um Neo4j's help in identifying uh how to um create uh the So, we actually started with our initial attempt at creating the data model and then indexes, and we iterated over that with Neo4j

directly. Um fortunately, we were mostly on the right track, but again, it was just through uh you know, um iteration working with Neo4j directly on that. And I think that's probably the best path. Starting with an initial data model uh and initial set of query establishes the um your specific um criteria and requirements, cuz Neo4j won't know that, but they can certainly help refine that once they

know what you're trying to do. >> There's one other question we are on top. I'll quickly answer. You talked about exposing the schema and graph to um to ensure coherent reasoning based on the graph. In order to properly lead the agents to reason over graph context, what other info about the graph did you have to expose? Community summaries, example questions you can answer. This is a great

question. Um we call this as a agent's context map. To move from a normal query generator to a reasoning agent, how to build the agent, we pass three additional layers of graph's metadata. The topological statistics, um the cardinality and the depth, um the schema index, the golden path. We provide curated Cypher templates like skills. Um there are example query uh patterns for complex domains or like calculating

effective permissions and a domain guardrails. Basically, we define the intent of the relationships. For example, telling the agent that inherits is transitive and directional. This prevents the agent from traversing the graph backwards or making logical errors in policy enforcement. That's what we call it as a domain guard. >> Oops. We lost you. Go ahead. >> Um finally, we expose we we expose the capability sampling. So, in

short, we don't just give it a structure. We give it the topological map and the domain playbook, too. That's how we give for um Thank you for this great question. I think we're >> Yeah, I think we're out of time. I think Hannah's giving us the uh the signal that we're running out. >> Varun, yes, we are storing we we are the long-term memory and on the

chat history. And we also built a to see how it is performing for our analytics. Thank you, Varun, for that great I think we're running a little over, so feel free to hit us up or further questions we'll be happy to answer. Thank you so much for the active participant. >> [music]