DEV Community

Nikhil Agrawal
Nikhil Agrawal

Posted on

Your RAG is Basic. Here's the KG-RAG Pattern We Used to Build a Real AI Agent.

Let's be honest. Slapping a vector search on top of an LLM is the "hello world" of GenAI. It's a great start, but it breaks down fast when faced with real-world, interconnected data. You can't answer multi-hop questions, and you're constantly fighting to give the LLM enough context.

We hit that wall. So we re-architected. Here's the pattern we implemented: Knowledge Graph RAG (KG-RAG). It's less about a single tool and more about orchestrating specialized data stores.

The Stack:

  • Source of Truth: MongoDB
  • Vector Store (Semantic Search): Weaviate
  • Graph Store (Context & Relationships): Neo4j
  • LLM/Embeddings: Google Gemini

The Problem with Vector-Only RAG:
A query like "What did team A conclude about the feature B launch?" is hard. Vector search might find docs about team A and docs about feature B, but it struggles to guarantee the retrieved context contains team A's conclusions about feature B.

The KG-RAG Solution:
We built a ChatService orchestrator that follows a "Graph-First" retrieval pattern.

Here's the pseudo-code for the backend logic:

`async function handleQuery(userQuery) {
// 1. Identify entities in the query (e.g., "team A", "feature B")
const entities = extractEntities(userQuery);

// 2. Query the Knowledge Graph for context and specific chunk IDs
// CYPHER: MATCH (t:Team {name:"team A"})-[]->(d:Doc)-[]->(c:Chunk)
// WHERE (d)-[:DISCUSSES]->(:Feature {name:"feature B"}) RETURN c.id
const contextualChunkIds = await neo4j.run(cypherQuery, { entities });

// 3. Perform a filtered vector search in Weaviate
// This is way more accurate than a broad search.
const relevantChunks = await weaviate.search({
vector: embed(userQuery),
filters: { chunkId: { $in: contextualChunkIds } }
});

// 4. Synthesize prompt and generate with LLM
const prompt = createPrompt(userQuery, relevantChunks);
const answer = await gemini.generate(prompt);

// 5. Also, return the graph data from step 2 for UI visualization
return { chatAnswer: answer, graphData: getGraphFrom(contextualChunkIds) };
}`

This pattern is a game-changer. It grounds the LLM in structured, factual relationships before feeding it semantic context, dramatically improving accuracy.
The best part? The UI is now a dual-pane view: a standard chatbot on the left and an interactive 3D graph on the right, both powered by the same API call. Clicking a node on the graph fires a new query. It's the interactive dev loop we've always wanted for data.
Stop building basic RAG toys. Start thinking about orchestration.

Top comments (0)