GraphCypherQAChain
GraphCypherQAChain is the core of Graph RAG. It takes a natural-language question, asks an LLM to write Cypher against your schema, runs the Cypher against Neo4j, and asks the LLM again to turn the raw result into a human-readable answer. Two LLM calls bookending one database query, wrapped in one chain.
GraphCypherQAChain lifecycle
The path from a natural-language question to a grounded answer.
from langchain_neo4j import GraphCypherQAChain
class GraphRAG:
def __init__(self, graph_store, vector_store=None):
self.graph_store = graph_store
self.vector_store = vector_store
self._chain = None # lazy GraphCypherQAChain
def _chain_or_raise(self):
if self._chain is not None:
return self._chain
graph = self.graph_store.connect()
self._chain = GraphCypherQAChain.from_llm(
graph=graph,
llm=_build_chat_model(),
verbose=False,
return_intermediate_steps=True,
allow_dangerous_requests=True,
)
return self._chainreturn_intermediate_steps=True is essential. Without it, you get only the final answer and have no way to see or debug the Cypher. allow_dangerous_requests=True acknowledges that generated Cypher can DELETE; in production you would sandbox or restrict the chain to read-only queries.
def ask(self, question):
chain = self._chain_or_raise()
result = chain.invoke({"query": question})
cypher, graph_context = self._extract_steps(result)
return {
"question": question,
"answer": result.get("result", ""),
"cypher": cypher,
"graph_context": graph_context,
}
def _extract_steps(self, chain_result):
cypher = None
graph_context = None
for step in chain_result.get("intermediate_steps", []) or []:
if not isinstance(step, dict):
continue
if "query" in step and cypher is None:
cypher = step["query"]
if "context" in step:
graph_context = step["context"]
return cypher, graph_contextPulling the generated Cypher and the raw context out of intermediate_steps is what makes the /ask response debuggable. When an answer looks wrong, you can paste the Cypher into the Neo4j browser and see exactly what the chain retrieved.
It tells LangChain that you accept the risk of the chain running Cypher that mutates or deletes data. By default the chain refuses to run queries it judges potentially destructive. For a workshop against a dev database this is fine. In production you either constrain the LLM with a tighter prompt, use a read-only Neo4j user, or both. The flag is an informed opt-in, not a magic switch.
Quiz: Quiz
Loading practice…