How vitali87/code-graph-rag Generates Cypher Queries from Natural Language
code-graph-rag translates plain English questions into executable Cypher queries through a five-step pipeline that combines few-shot prompting, LLM inference, and post-processing validation.
The open-source code-graph-rag project provides a retrieval-augmented generation (RAG) system for codebases stored in graph databases. At its core lies a natural language to Cypher translation mechanism that lets developers query their code's knowledge graph using everyday English rather than graph query syntax.
The 5-Step Query Generation Pipeline
1. Capture the Request via CLI or SDK
User input enters the system through either the command-line interface or programmatic SDK. In codebase_rag/cli.py, the CLI receives raw text such as "Show me all functions that call UserService.create_user" and forwards it to the translation layer.
# Example usage pattern from the SDK documentation
from cgr import CypherGenerator
generator = CypherGenerator()
cypher = generator.generate(
"Find all functions that call UserService.create_user"
)
2. Build a Few-Shot Prompt
The CypherGenerator class relies on PromptBuilder (defined in codebase_rag/prompts.py) to assemble a structured chat prompt containing:
- System instruction: Explains the NL→Cypher translation task
- Few-shot examples: Concrete question/query pairs from
CYPHER_EXAMPLE_PAIRSincodebase_rag/cypher_queries.py - User question: The actual natural language input
These examples anchor the LLM to the project's specific graph schema and query patterns.
3. Invoke the LLM
The prompt passes to the generic LLM abstraction exposed in cgr/__init__.py. The default implementation calls self.llm.chat_completion() with the constructed messages, routing to whichever provider is configured (OpenAI, Anthropic, etc.).
# Simplified structure from the implementation
class CypherGenerator:
def __init__(self, llm=None):
self.llm = llm or DefaultLLM()
self.prompt_builder = PromptBuilder()
def generate(self, natural_language: str) -> str:
prompt = self.prompt_builder.build(
user_question=natural_language,
examples=CYPHER_EXAMPLE_PAIRS # few-shot anchors
)
response = self.llm.chat_completion(messages=prompt)
return self._post_process(response.content)
4. Post-Process and Validate
Raw LLM output undergoes sanitization in codebase_rag/cypher_queries.py:
- Markdown fences (
cypher ...) are stripped - Output is validated against whitelisted Cypher patterns defined in constants like
CYPHER_FIND_BY_QUALIFIED_NAME - Optional formatting (line-break normalization) is applied
This validation layer prevents malformed or potentially unsafe queries from reaching the database.
5. Execute Against Memgraph
The validated Cypher query ships to codebase_rag/services/graph_service.py, which interfaces with the Memgraph backend. Results return to the user as structured code intelligence.
Core Implementation Files
| File | Responsibility |
|---|---|
codebase_rag/cypher_queries.py |
Cypher templates, example pairs (CYPHER_EXAMPLE_PAIRS), and validation logic |
codebase_rag/prompts.py |
Prompt assembly templates and instruction formatting |
cgr/__init__.py |
Public API exposing CypherGenerator and LLM abstraction |
codebase_rag/cli.py |
CLI entry point for natural language input |
codebase_rag/services/graph_service.py |
Query execution against Memgraph |
Why Few-Shot Examples Matter
The CYPHER_EXAMPLE_PAIRS constant is the project's secret sauce. By embedding concrete English→Cypher mappings directly into each prompt, code-graph-rag achieves schema-constrained generation without fine-tuning. The LLM sees patterns like:
# Conceptual example from cypher_queries.py structure
CYPHER_EXAMPLE_PAIRS = [
{
"question": "Find all functions that call UserService.create_user",
"cypher": "MATCH (c:Function)-[:CALLS]->(t:Function {name: 'create_user', class: 'UserService'}) RETURN c"
},
# ... additional domain-specific examples
]
This approach outperforms zero-shot prompting on specialized graph schemas while remaining portable across LLM providers.
Summary
CypherGeneratoris the primary interface for natural language to Cypher translation- The pipeline follows capture → prompt → LLM → validate → execute stages
- Few-shot examples from
codebase_rag/cypher_queries.pyground the LLM in the codebase's graph schema - Post-processing validation ensures only safe, pattern-matched queries reach Memgraph
- The architecture abstracts LLM providers through
cgr/__init__.pyfor flexibility
Frequently Asked Questions
What LLM providers does code-graph-rag support?
The project uses a generic LLM abstraction layer in cgr/__init__.py that can wrap any provider implementing the chat_completion interface. OpenAI and Anthropic Claude are commonly configured, though the exact supported list depends on your installation's dependencies.
How does the system prevent invalid Cypher queries?
Generated queries pass through a whitelist validator defined in codebase_rag/cypher_queries.py. The system checks against allowed patterns (such as CYPHER_FIND_BY_QUALIFIED_NAME) and strips extraneous formatting before execution. Queries failing validation are rejected before reaching the Memgraph backend.
Can I customize the few-shot examples for my codebase?
Yes. The CYPHER_EXAMPLE_PAIRS constant in codebase_rag/cypher_queries.py is the designated extension point. Adding question→query pairs specific to your graph's node labels, relationship types, and property conventions improves translation accuracy without modifying core logic.
Where is the Cypher query actually executed?
Execution happens in codebase_rag/services/graph_service.py, which manages connections to Memgraph. This service receives the validated Cypher string, runs it against the knowledge graph, and formats results for the CLI or calling application.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →