neo4j-contrib/neo4j-skills

neo4j-graphrag-skill

Build GraphRAG retrieval pipelines on Neo4j using the neo4j-graphrag Python package (v1.16.0+). Covers retriever selection (VectorRetriever, HybridRetriever, VectorCypherRetriever, HybridCypherRetriever, Text2CypherRetriever, ToolsRetriever), external vector DB retrievers (Weaviate, Pinecone, Qdrant), retrieval_query Cypher fragments, query_params, filters, GraphRAG pipeline wiring (GraphRAG + LLM + prompt), all LLM providers (OpenAI, Anthropic, VertexAI, Bedrock, Cohere, Mistral, Ollama), embe…

Trending #6209 First seen Apr 28, 2026

Installation

$ npx skills add neo4j-contrib/neo4j-skills --skill neo4j-graphrag-skill

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from neo4j-contrib/neo4j-skills · top by installs.

npx skills add neo4j-contrib/neo4j-skills

Browse all from neo4j-contrib/neo4j-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 108
License LICENSE
Default branch main
Open issues 2
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.11
Allowed toolsBash WebFetch
Declared agents claude-code gemini

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 18,662 B
  • docs README.md 1,549 B
  • docs SUMMARY.md 896 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 754 installs

SKILL.md

Neo4j GraphRAG Skill

When to Use

  • Building GraphRAG retrieval pipelines with neo4j-graphrag Python package
  • Choosing between VectorRetriever, HybridRetriever, VectorCypherRetriever, HybridCypherRetriever
  • Writing retrieval_query Cypher fragments for graph-augmented context
  • Wiring retriever + LLM into a GraphRAG pipeline
  • Using LLM-routed multi-retriever with ToolsRetriever
  • Debugging low retrieval quality
  • Integrating Neo4j with LangChain, LlamaIndex, or Haystack

When NOT to Use

  • KG construction from documentsneo4j-document-import-skill
  • Plain vector/semantic search without graph traversalneo4j-vector-index-skill
  • Hybrid search that combines vector with fulltext or other ranked sourcesneo4j-vector-index-skill
  • GDS algorithms (PageRank, Louvain, node embeddings)neo4j-gds-skill
  • Agent long-term memoryneo4j-agent-memory-skill
  • Writing raw Cypher queriesneo4j-cypher-skill

Retriever Selection

Has fulltext index?
  YES → Hybrid variants (HybridRetriever / HybridCypherRetriever)
  NO  → Vector variants (VectorRetriever / VectorCypherRetriever)

Need graph traversal after vector lookup?
  YES → Cypher variants (VectorCypherRetriever / HybridCypherRetriever)
  NO  → plain variants

Natural-language-to-Cypher?        → Text2CypherRetriever (no embedder needed)
LLM should route between retrievers? → ToolsRetriever
Vectors stored in external DB?      → WeaviateNeo4jRetriever / PineconeNeo4jRetriever / QdrantNeo4jRetriever
Retriever Vector Fulltext Graph Best For
VectorRetriever Baseline semantic search
HybridRetriever Better recall, no graph expansion
VectorCypherRetriever GraphRAG without fulltext
HybridCypherRetriever Production GraphRAG — default
Text2CypherRetriever NL→Cypher, no embedder
ToolsRetriever varies varies varies LLM-routed multi-retriever
WeaviateNeo4jRetriever Vectors in Weaviate
PineconeNeo4jRetriever Vectors in Pinecone
QdrantNeo4jRetriever Vectors in Qdrant

Install

pip install neo4j-graphrag[openai]        # OpenAI LLM + embeddings
pip install neo4j-graphrag[anthropic]     # Anthropic Claude
pip install neo4j-graphrag[google]        # Vertex AI / Gemini
pip install neo4j-graphrag[bedrock]       # Amazon Bedrock (boto3)
pip install neo4j-graphrag[cohere]        # Cohere
pip install neo4j-graphrag[mistralai]     # MistralAI
pip install neo4j-graphrag[ollama]        # Ollama (local)
pip install neo4j-graphrag[weaviate]      # Weaviate external retriever
pip install neo4j-graphrag[pinecone]      # Pinecone external retriever
pip install neo4j-graphrag[qdrant]        # Qdrant external retriever

Requires: Python >= 3.10, neo4j >= 5.17.0 (driver 6.x supported).


Step 2 — Choose Retriever

Has fulltext index? YES → Hybrid variants (better recall)
                   NO  → Vector variants (baseline)

Needs graph context after vector lookup? YES → Cypher variants
                                         NO  → plain variants

For natural-language-to-Cypher? → Text2CypherRetriever (no embedder needed)
For multi-tool LLM routing?     → ToolsRetriever
Using external vector DB?       → WeaviateNeo4jRetriever / PineconeNeo4jRetriever / QdrantNeo4jRetriever
Retriever Vector Fulltext Graph When to use
VectorRetriever Baseline; quick start
HybridRetriever Better recall; no graph context
VectorCypherRetriever GraphRAG without fulltext
HybridCypherRetriever Production GraphRAG — default choice
Text2CypherRetriever LLM generates Cypher; no embedder
ToolsRetriever varies varies varies Multi-retriever LLM routing

For custom Cypher hybrid search outside the neo4j-graphrag retriever APIs, use neo4j-vector-index-skill.

Vector backend selection [v1.16+, auto]: on Neo4j 2026.01+ all four vector/hybrid retrievers auto-route through the Cypher 25 SEARCH ... WHERE clause when filters are SEARCH-compatible (simple AND comparisons) and all filter props are declared in the index WITH [n.prop] list. $or, $in, $like, or undeclared props → automatic fallback to db.index.vector.queryNodes() procedure path (with warning log). Declare filterable properties via filterableproperties=[...] on createvector_index().


Step 3 — Create Indexes (run once)

// Vector index (all retrievers need this)
CREATE VECTOR INDEX chunk_embedding IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
OPTIONS { indexConfig: {
  `vector.dimensions`: 1536,
  `vector.similarity_function`: 'cosine'
} };

// Fulltext index (Hybrid retrievers only)
CREATE FULLTEXT INDEX chunk_fulltext IF NOT EXISTS
FOR (c:Chunk) ON EACH [c.text];

// Confirm ONLINE before ingesting:
SHOW INDEXES YIELD name, state
WHERE name IN ['chunk_embedding', 'chunk_fulltext']
RETURN name, state;
// Both must show state = 'ONLINE'

If index not ONLINE: wait, poll every 5s. Do NOT start ingestion until ONLINE.


Step 4 — Core Pattern (HybridCypherRetriever)

from neo4j import GraphDatabase
from neo4j_graphrag.embeddings import OpenAIEmbeddings
from neo4j_graphrag.generation import GraphRAG
from neo4j_graphrag.llm import OpenAILLM
from neo4j_graphrag.retrievers import HybridCypherRetriever

driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD))
embedder = OpenAIEmbeddings(model="text-embedding-3-large")  # OPENAI_API_KEY from env

# retrieval_query: Cypher fragment executed after the vector/fulltext lookup.
# Auto-injected variables:  node  (matched node)   score  (similarity float)
# MUST include a RETURN clause.  score must appear in RETURN.
retrieval_query = """
MATCH (node)<-[:HAS_CHUNK]-(article:Article)
OPTIONAL MATCH (article)-[:MENTIONS]->(org:Organization)
RETURN node.text AS chunk_text,
       article.title AS article_title,
       collect(DISTINCT org.name) AS mentioned_organizations,
       score
"""

retriever = HybridCypherRetriever(
    driver=driver,
    vector_index_name="chunk_embedding",
    fulltext_index_name="chunk_fulltext",
    retrieval_query=retrieval_query,
    embedder=embedder,
)

llm = OpenAILLM(model_name="gpt-4.1", model_params={"temperature": 0})

rag = GraphRAG(
    retriever=retriever,
    llm=llm,
)

response = rag.search(
    query_text="Who does Alice work for?",
    retriever_config={"top_k": 5},
)
print(response.answer)
driver.close()

VectorCypherRetriever

from neo4j_graphrag.retrievers import VectorCypherRetriever

retriever = VectorCypherRetriever(
    driver=driver,
    index_name="chunk_embedding",
    retrieval_query=retrieval_query,
    embedder=embedder,
)

response = rag.search(
    query_text="What happened at Apple?",
    retriever_config={"top_k": 10},
)

Text2CypherRetriever

Translates natural language to Cypher using an LLM. No embedder required.

Security (v1.16.0+): Every LLM-generated Cypher is run through EXPLAIN first.
Any statement classified as write/destructive raises Text2CypherRetrievalError instead
of executing — prevents prompt-injection attacks.

from neo4j_graphrag.retrievers import Text2CypherRetriever

retriever = Text2CypherRetriever(
    driver=driver,
    llm=OpenAILLM(model_name="gpt-4.1"),
    neo4j_schema=None,       # None = auto-fetch schema from DB; pass string to trim
    examples=[
        "Q: Who works at Neo4j? A: MATCH (p:Person)-[:WORKS_AT]->(c:Company {name:'Neo4j'}) RETURN p.name"
    ],
)
results = retriever.search(query_text="Which people work at Neo4j?")

ToolsRetriever (LLM-routed multi-retriever)

from neo4j_graphrag.retrievers import ToolsRetriever

tools_retriever = ToolsRetriever(
    llm=llm,
    retrievers=[vector_retriever, text2cypher_retriever],
)
# LLM decides which retriever(s) to invoke per query

# Convert any retriever to a standalone Tool:
tool = vector_retriever.convert_to_tool()

Filters (pre-filter before vector search)

results = retriever.search(
    query_text="quarterly earnings",
    top_k=5,
    filters={
        "date": {"$gte": "2024-01-01"},
        "source": {"$eq": "10-K"},
    },
)
# Operators: $eq  $ne  $lt  $lte  $gt  $gte  $between  $in  $like  $ilike

queryparams (parameterized retrievalquery)

retrieval_query = """
MATCH (node)<-[:HAS_CHUNK]-(a:Article)-[:MENTIONS]->(org:Organization {name: $entity_name})
RETURN node.text, a.title, score
"""

# Pass via retriever.search directly:
results = retriever.search(
    query_text="What happened at Apple?",
    top_k=10,
    query_params={"entity_name": "Apple"},
)

# Or via GraphRAG.search:
response = rag.search(
    query_text="What happened at Apple?",
    retriever_config={"top_k": 10, "query_params": {"entity_name": "Apple"}},
)

Cypher 25 SEARCH Clause (v1.16.0, Neo4j 2026.x+)

# Enable SEARCH clause syntax in vector/hybrid retrievers (requires Neo4j 2026+)
retriever = VectorRetriever(
    driver=driver,
    index_name="chunk_embedding",
    embedder=embedder,
    use_search_clause=True,
)

ORDER BY on Cypher Retrievers (v1.16.0)

results = retriever.search(
    query_text="...",
    top_k=10,
    order_by="score DESC",
)

If neo4j_schema=None: retriever fetches schema automatically. For large schemas, pass a trimmed string to reduce LLM prompt size.

Destructive-query guard [v1.16+]: Text2CypherRetriever runs EXPLAIN on the generated Cypher before execution and rejects queries that produce writes (CREATE, MERGE, DELETE, SET, REMOVE, etc.). LLM-generated writes are never executed against the graph.


Custom Prompt Template

from neo4j_graphrag.generation.prompts import RagTemplate

template = RagTemplate(
    template="""Answer using ONLY the context below.
Context: {context}
Question: {query_text}
Answer:""",
    expected_inputs=["context", "query_text"],
)

rag = GraphRAG(retriever=retriever, llm=llm, prompt_template=template)

returncontext and responsefallback

response = rag.search(
    query_text="...",
    retriever_config={"top_k": 5},
    return_context=True,                        # include raw retrieved chunks
    response_fallback="No relevant context.",   # skip LLM call if retriever returns nothing
)
print(response.answer)
print(response.retriever_result)    # RawSearchResult when return_context=True

Message History (multi-turn)

from neo4j_graphrag.message_history import InMemoryMessageHistory

history = InMemoryMessageHistory()
r1 = rag.search(query_text="Who is Alice?", message_history=history)
r2 = rag.search(query_text="Where does she work?", message_history=history)

External Retrievers

# --- Weaviate ---
from neo4j_graphrag.retrievers import WeaviateNeo4jRetriever
import weaviate

weaviate_client = weaviate.connect_to_local()
retriever = WeaviateNeo4jRetriever(
    driver=driver,
    client=weaviate_client,
    collection="Chunk",
    id_property_external="neo4j_id",
    id_property_neo4j="id",
    retrieval_query=retrieval_query,
    node_label_neo4j="Chunk",       # optional: speeds up Neo4j lookup
)

# --- Pinecone ---
from neo4j_graphrag.retrievers import PineconeNeo4jRetriever
from pinecone import Pinecone

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
retriever = PineconeNeo4jRetriever(
    driver=driver,
    client=pc,
    index_name="my-index",
    id_property_neo4j="id",
    retrieval_query=retrieval_query,
)

# --- Qdrant ---
from neo4j_graphrag.retrievers import QdrantNeo4jRetriever
from qdrant_client import QdrantClient

retriever = QdrantNeo4jRetriever(
    driver=driver,
    client=QdrantClient(url="http://localhost:6333"),
    collection_name="Chunk",
    id_property_external="neo4j_id",
    id_property_neo4j="id",
    id_property_getter=lambda hit: hit.payload["neo4j_id"],  # custom ID extraction
    retrieval_query=retrieval_query,
)

LLM Providers

All implement LLMBase. All support sync + async, tool calling, and automatic rate limiting.

Class Extra Notes
OpenAILLM openai Structured output; tool calling
AzureOpenAILLM openai Azure-hosted OpenAI
AnthropicLLM anthropic Tool calling
VertexAILLM google Structured output; tool calling
MistralAILLM mistralai Tool calling
CohereLLM cohere
OllamaLLM ollama Local; tool calling
BedrockLLM bedrock Boto3 Converse API; added v1.15.0
from neo4j_graphrag.llm import (
    OpenAILLM, AzureOpenAILLM, AnthropicLLM, VertexAILLM,
    MistralAILLM, CohereLLM, OllamaLLM, BedrockLLM,
)

llm = OpenAILLM(model_name="gpt-4.1", model_params={"temperature": 0})
llm = AnthropicLLM(model_name="claude-3-5-sonnet-20241022")
llm = VertexAILLM(model_name="gemini-2.0-flash")
llm = OllamaLLM(model_name="llama3")           # no API key needed
llm = BedrockLLM(model_id="anthropic.claude-3-5-sonnet-20241022-v2:0")

# Token usage tracking (v1.15.0+)
response = llm.invoke("Hello")
# response.usage → LLMUsage(request_tokens=N, response_tokens=M, total_tokens=T)

# Graceful resource cleanup (v1.16.0+)
llm.close()         # sync
await llm.aclose()  # async

Embedder Providers

All include automatic rate limiting with tenacity exponential backoff.

Class Extra Dims
OpenAIEmbeddings openai 3072 / 1536
AzureOpenAIEmbeddings openai varies
VertexAIEmbeddings google 768
MistralAIEmbeddings mistralai 1024
CohereEmbeddings cohere 1024
OllamaEmbeddings ollama varies
SentenceTransformerEmbeddings sentence-transformers 384+
BedrockEmbeddings bedrock varies; added v1.15.0
from neo4j_graphrag.embeddings import (
    OpenAIEmbeddings, VertexAIEmbeddings, CohereEmbeddings,
    OllamaEmbeddings, SentenceTransformerEmbeddings, BedrockEmbeddings,
)

embedder = OpenAIEmbeddings(model="text-embedding-3-large")   # 3072 dims
embedder = OpenAIEmbeddings(model="text-embedding-3-small")   # 1536 dims
embedder = SentenceTransformerEmbeddings(model="all-MiniLM-L6-v2")  # 384 dims, local
embedder = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0")

Index Setup

from neo4j_graphrag.indexes import create_vector_index

# Vector index — adjust dimensions to match your embedding model
create_vector_index(
    driver,
    name="chunk_embedding",
    label="Chunk",
    embedding_property="embedding",
    dimensions=1536,
    similarity_fn="cosine",       # or "euclidean"
)

# Fulltext index (run as Cypher)
# CREATE FULLTEXT INDEX chunk_fulltext IF NOT EXISTS
#   FOR (c:Chunk) ON EACH [c.text]

Schema Inspection

from neo4j_graphrag.schema import get_schema, get_structured_schema

schema_str = get_schema(driver, sample=1000)           # human-readable string
schema_dict = get_structured_schema(driver, sample=1000)  # dict with labels/rels/props

Common Errors

Error Cause Fix
ModuleNotFoundError: neo4j_genai Old package name pip uninstall neo4j-genai && pip install neo4j-graphrag
retrieval_query returns 0 rows Missing MATCH or wrong rel direction EXPLAIN the fragment; check CALL db.schema.visualization()
KeyError: 'score' in results retrieval_query RETURN missing score Add score to every retrieval_query RETURN clause
score variable not found score re-declared in retrieval_query Do not re-declare score — it is auto-injected
Text2CypherRetrievalError LLM generated a write statement Expected security behavior (v1.16.0+); refine prompt or schema
TypeError: coroutine Missing await / asyncio.run() Wrap async calls: asyncio.run(pipeline.run_async(...))
Empty results from HybridRetriever Fulltext index not ONLINE SHOW INDEXES YIELD name, state WHERE state <> 'ONLINE'
Embedding dimension mismatch Index dims ≠ model dims Recreate index with correct dimensions= value

Verification Checklist

  • neo4j-graphrag (not neo4j-genai) installed; neo4j >= 5.17.0 driver
  • Vector index ONLINE before ingesting embeddings or running retriever
  • Fulltext index ONLINE if using Hybrid variants
  • Embedding dims in createvectorindex match the embedder output
  • retrieval_query returns node and score in RETURN (not re-declared)
  • queryparams passed via retrieverconfig on rag.search() (not on retriever constructor)
  • API keys in env vars; never hardcoded
  • llm.close() called when done to release resources

References

Load on demand: