soumya-ai/Knowledge-Graph
GraphRAG — Neo4j Aura + OpenAI + LangChain
Query a Neo4j Aura knowledge graph with OpenAI + LangChain GraphRAG (vector + multi-hop retrieval).
Query Space: Knowledge-Graph · Ingest Space: Knowledge-Graph-Ingest
A GraphRAG implementation using:
- Neo4j Aura — knowledge graph + vector indexes (cloud)
- OpenAI — entity/relation extraction, embeddings, and answers
- LangChain LCEL — ingestion and query pipelines
Hugging Face Spaces
Add secrets on both Spaces: NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD, NEO4J_DATABASE, OPENAI_API_KEY — then Restart.
Architecture
Documents
│
▼ [INDEXING — runs once]
Text Chunking (LangChain splitter)
│
▼
OpenAI GPT — entity & relation extraction (JSON)
│
▼
OpenAI text-embedding-3-small — node & chunk embeddings
│
▼
Neo4j Aura — nodes, edges, vector indexes
User Query
│
▼ [QUERYING — runs per question]
OpenAI embeddings — embed query
│
▼
Neo4j vector search — find similar chunks & entities
│
▼
Neo4j Cypher — multi-hop graph traversal (1-2 hops)
│
▼
Context assembly (entities + paths + chunks)
│
▼
OpenAI GPT-4o-mini — generate grounded answerLocal development
Prerequisites
Setup
1. Neo4j database
Option A — Neo4j Aura (recommended for cloud)
- Create an instance at console.neo4j.io.
- Wait ~60 seconds until the instance is Running.
- Copy connection details into
.env(see.env.example):
NEO4J_URI=neo4j+s://YOUR_INSTANCE.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=<from Aura console>
NEO4J_DATABASE=neo4jUse the neo4j+s:// URI from Aura (TLS).
2. Configure environment
cp .env.example .envSet Neo4j Aura credentials and OPENAI_API_KEY in .env:
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
OPENAI_EXTRACT_MODEL=gpt-4o-mini
OPENAI_EMBED_MODEL=text-embedding-3-small
OPENAI_EMBED_DIMENSIONS=15363. Install Python dependencies
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtUsage
- Ingest — open Knowledge-Graph-Ingest → Connect → Ingest sample corpus
- Query — open Knowledge-Graph → ask a question
Local Gradio: python ingest_app.py or python app.py
Project structure
├── README.md # Query Space (app.py)
├── README.ingest.md # Ingest Space (ingest_app.py) — copy as README there
├── requirements.txt
├── app.py # Query Gradio app
├── ingest_app.py # Ingest Gradio app
└── src/
├── config.py
├── neo4j_client.py
├── embeddings.py
├── graph_builder.py
├── ingest_service.py
├── sample_corpus.py
├── retriever.py
└── rag_chain.pyHow the LCEL chain works
# rag_chain.py — simplified view
chain = (
RunnableLambda(retrieve) # OpenAI embed + Neo4j vector + Cypher
| RunnableLambda(format_context) # Build prompt dict
| RAG_PROMPT # System + user template
| ChatOpenAI(model="gpt-4o-mini") # Generate answer
| StrOutputParser() # Return plain string
)
answer = chain.invoke({"question": "...", "top_k": 5})Tuning tips
Neo4j graph schema
(:Chunk) -[:MENTIONS]-> (:Entity)
(:Entity {type}) -[:REL_TYPE]-> (:Entity)
Node properties:
Entity: id, name, type, description, embedding (1536-dim default)
Chunk: id, text, embedding (1536-dim default)
Vector indexes:
entity_embedding — cosine, dims = OPENAI_EMBED_DIMENSIONS
chunk_embedding — cosine, dims = OPENAI_EMBED_DIMENSIONSTroubleshooting
Neo4j not accepting connections — check Aura instance is Running at console.neo4j.io
OpenAI API errors
- Confirm
OPENAI_API_KEYis set in.env - Check billing and rate limits at platform.openai.com
Wrong embedding dimensions If you changed OPENAI_EMBED_DIMENSIONS after ingesting, drop old vector indexes in Aura and re-ingest.
Empty retrieval results
- Check Neo4j browser: run
MATCH (n) RETURN count(n)— should be > 0 after ingestion - Lower
min_scorethresholds inretriever.py - Confirm embeddings exist:
size(e.embedding)should equalOPENAI_EMBED_DIMENSIONS
Check that indexes exist and are ONLINE SHOW VECTOR INDEXES;
-- Peek at a few entity embeddings (first 5 dimensions shown)
MATCH (e:Entity) WHERE e.embedding IS NOT NULL
RETURN e.name, e.type, size(e.embedding) AS dims,
e.embedding[0..5] AS first5
LIMIT 5;-- Same for chunks
MATCH (c:Chunk) WHERE c.embedding IS NOT NULL
RETURN c.id, size(c.embedding) AS dims,
c.embedding[0..5] AS first5
LIMIT 5;-- Count nodes missing embeddings (a sign of silent embed failures)
MATCH (e:Entity) WHERE e.embedding IS NULL OR size(e.embedding) = 0
RETURN count(e) AS entities_without_embedding;