Beyond Vector Search: Building Zero-Hallucination GraphRAG Applications with Amazon Neptune and Amazon Bedrock
Learn how to combine Amazon Neptune knowledge graphs with Amazon Bedrock to eliminate LLM relational hallucinations. This guide covers openCypher queries, multi-hop relationship traversals, and complete Python orchestration for production GraphRAG architectures.
Beyond Vector Search: Building Zero-Hallucination GraphRAG Applications with Amazon Neptune and Amazon Bedrock
Tags:
#aws-community #amazon-bedrock #amazon-neptune #graphrag #generative-aiStandard Retrieval-Augmented Generation (RAG) using vector databases has become the go-to architecture for grounding Large Language Models (LLMs) on enterprise knowledge. However, vector search has a glaring limitation: it struggles with multi-hop relationships and global structural context.
If you ask a standard vector search pipeline: "Which microservices dependent on Payment API share a database managed by Team Alpha?", vector similarity search will pull passages containing those keywords—frequently failing to connect the multi-step relationships.
Enter GraphRAG. By combining the relational connectivity of Knowledge Graphs using Amazon Neptune with the reasoning power of Amazon Bedrock, you can eliminate relational hallucinations and enable true graph-aware AI applications.
Architecture Overview
1
2
3
4
5
6
7
8
9
10
11
[User Query]
│
▼
[Amazon Bedrock (Claude 3.5 Sonnet)] ──(Generates Cypher/Gremlin Query)──► [Amazon Neptune Graph DB]
│ │
│ (Returns Graph Path)
▼ │
[Bedrock Prompt Context] ◄───────────────────────────────────────────────────────┘
│
▼
[Grounded Answer Output]- Natural Language to Graph Query: Bedrock translates user intent into an openCypher or Gremlin graph query.
- Graph Traversal: Amazon Neptune executes precise multi-hop traversals across nodes and edges.
- Contextual Synthesis: Bedrock receives the exact graph paths and relationships to synthesize a deterministic, zero-hallucination answer.
Prerequisites
- An active AWS Account.
- An Amazon Neptune Serverless cluster provisioned in your VPC.
- Model access granted for Anthropic Claude 3.5 Sonnet in Amazon Bedrock.
- A Python 3.11+ environment with
boto3andrequests-aws4authinstalled.
Step 1: Ingest Data into Amazon Neptune (openCypher)
Let's populate Neptune with a knowledge graph representing cloud infrastructure, services, databases, and teams.
Run the following openCypher query against your Neptune HTTP endpoint (
https://<your-neptune-endpoint>:8182/openCypher):1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create Microservices
CREATE (s1:Service {id: 'SVC-01', name: 'CheckoutService'})
CREATE (s2:Service {id: 'SVC-02', name: 'PaymentAPI'})
CREATE (s3:Service {id: 'SVC-03', name: 'InventoryAPI'})
// Create Databases & Teams
CREATE (db1:Database {id: 'DB-01', name: 'OrdersDB', engine: 'Aurora PostgreSQL'})
CREATE (t1:Team {id: 'TEAM-A', name: 'CoreFintech'})
// Create Relationships
CREATE (s1)-[:DEPENDS_ON]->(s2)
CREATE (s1)-[:USES_DB]->(db1)
CREATE (s2)-[:USES_DB]->(db1)
CREATE (db1)-[:MANAGED_BY]->(t1)Step 2: Build the GraphRAG Translation & Query Engine
Now we create a Python script that uses Bedrock to convert a natural language question into a Cypher query, executes it on Neptune, and synthesizes the answer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import json
import boto3
import requests
from requests_aws4auth import AWS4Auth
# Initialize Boto3 Clients
bedrock_runtime = boto3.client('bedrock-runtime', region_name='us-east-1')
# Neptune Configurations
NEPTUNE_ENDPOINT = "https://your-neptune-cluster.cluster-custom-us-east-1.neptune.amazonaws.com:8182/openCypher"
REGION = "us-east-1"
def get_aws_auth():
credentials = boto3.Session().get_credentials()
return AWS4Auth(
credentials.access_key,
credentials.secret_key,
REGION,
'neptune-db',
session_token=credentials.token
)
def generate_cypher_query(user_question: str) -> str:
"""Translates user prompt into openCypher query using Bedrock."""
prompt = f"""
You are a Cypher query generator for Amazon Neptune.
Graph Schema:
- Nodes: Service (name), Database (name, engine), Team (name)
- Relationships: DEPENDS_ON, USES_DB, MANAGED_BY
Translate this natural language question into ONLY a valid openCypher query.
Do not include markdown formatting, explanations, or backticks.
Question: {user_question}
"""
payload = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 300,
"messages": [{"role": "user", "content": prompt}]
}
response = bedrock_runtime.invoke_model(
modelId="anthropic.claude-3-5-sonnet-20240620-v1:0",
body=json.dumps(payload)
)
result = json.loads(response['body'].read())
return result['content'][0]['text'].strip()
def query_neptune(cypher_query: str) -> dict:
"""Executes openCypher query against Amazon Neptune."""
auth = get_aws_auth()
response = requests.post(
NEPTUNE_ENDPOINT,
data={'query': cypher_query},
auth=auth
)
return response.json()
def generate_final_answer(user_question: str, graph_data: dict) -> str:
"""Synthesizes the final response grounded in Neptune data."""
prompt = f"""
Answer the user's question accurately using ONLY the provided knowledge graph context.
Question: {user_question}
Graph Context: {json.dumps(graph_data)}
"""
payload = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 500,
"messages": [{"role": "user", "content": prompt}]
}
response = bedrock_runtime.invoke_model(
modelId="anthropic.claude-3-5-sonnet-20240620-v1:0",
body=json.dumps(payload)
)
result = json.loads(response['body'].read())
return result['content'][0]['text']
# Orchestration Example
if __name__ == "__main__":
question = "Which microservices share a database managed by Team CoreFintech?"
print(f"1. Translating Question: '{question}'...")
cypher = generate_cypher_query(question)
print(f"Generated Cypher: {cypher}\n")
print("2. Traversing Amazon Neptune Graph...")
graph_results = query_neptune(cypher)
print(f"Graph Output: {json.dumps(graph_results)}\n")
print("3. Generating Grounded Response...")
final_answer = generate_final_answer(question, graph_results)
print(f"\nFinal Answer:\n{final_answer}")Step 3: Comparing Results
- Standard Vector Search: Might return generic documentation on CheckoutService and CoreFintech, leaving the developer to manually inspect code repos to trace database ownership.
- GraphRAG Output:> "Both CheckoutService and PaymentAPI share the OrdersDB database, which is currently managed by Team CoreFintech."
Because the LLM is constrained strictly to paths returned by the Neptune graph query, relational hallucinations drop to near zero.
Production Best Practices
- Use Neptune Serverless: Scale capacity automatically up or down based on workload spikes without over-provisioning infrastructure.
- Implement Hybrid Search (Graph + Vector): Combine Neptune Analytics (for graph traversals) with vector indexes to fetch both unstructured textual context and structured relationship paths simultaneously.
- Query Validation Layer: Sanitize generated Cypher queries before execution to prevent unauthorized write operations (
DELETE,REMOVE) on your production graph.
Conclusion
By grounding Bedrock LLMs with Knowledge Graphs hosted on Amazon Neptune, you upgrade your RAG architecture from simple similarity matching to true structural reasoning.
Are you using GraphRAG or Hybrid RAG in your production stack? Share your thoughts and architecture approaches in the comments below!
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article