How to Query Code Using Natural Language with Code-Graph-RAG
Code-Graph-RAG enables developers to ask questions about any codebase using plain English and receive precise, deterministic answers derived from a live knowledge graph stored in Memgraph.
Code-Graph-RAG bridges the gap between human language and code analysis by combining Tree-sitter parsing with graph database technology. The open-source tool constructs a language-agnostic representation of your source code in Memgraph, then exposes both natural language interfaces and deterministic query APIs to explore relationships between symbols. Whether you need to find callers of a function or trace implementation hierarchies, the system translates your intent into optimized Cypher queries against the graph.
Architecture of the Natural Language Query System
The foundation for querying code using natural language rests on a three-tier architecture that transforms raw source into a traversable graph. First, the Tree-sitter-powered parser in codebase_rag/graph_loader.py extracts every function, class, module, and their relationships from your repository. These entities populate a Memgraph database following a strict schema defined in codebase_rag/constants.py, creating nodes for symbols and edges for relationships like CALLS, IMPLEMENTS, and IMPORTS.
When you submit a natural language question, the RAG (Retrieval-Augmented Generation) layer either routes complex intent to an LLM for Cypher generation or executes pre-written deterministic queries for reproducible operations. The deterministic path guarantees identical results for questions like "find definition" or "list callers" by using optimized Cypher statements stored in codebase_rag/cypher_queries.py.
Core Query Functions in graph_query.py
The deterministic query engine lives in codebase_rag/graph_query.py, implementing direct interactions with the Memgraph backend. These functions provide the building blocks for both CLI usage and programmatic integration.
The resolve function (lines 41-90) maps a symbol name or path:line reference to its definition(s), handling ambiguous matches across the codebase. When you need complete source context, the definition function (lines 19-62) retrieves the file location, source snippet, docstring, and existence flag for any qualified name.
For relationship traversal, callers and callee (lines 21-41) walk CALLS edges to enumerate direct call sites, supporting depth parameters for transitive analysis. Additional utilities like implementors, overrides, importers, and tests_reaching (lines 43-85) expose inheritance hierarchies and test coverage paths through specialized Cypher patterns.
Querying via the Command Line Interface
The cgr graph command group in codebase_rag/graph_cli.py exposes deterministic queries as JSON-encoded CLI tools. Each subcommand wires Click options directly to the corresponding function in graph_query.py: lines 63-77 handle resolve, lines 75-84 manage definition, and lines 97-108 process callers.
Resolve a symbol to its definitions:
cgr graph resolve "my_module:42"
Retrieve full source and documentation:
cgr graph definition my_pkg.utils.Helper
Find immediate callers with optional depth traversal:
cgr graph callers my_pkg.service.process_data --depth 3
Discover interface implementations:
cgr graph implementors my_pkg.interfaces.IWorker
Identify test files that reach specific symbols:
cgr graph tests-reaching my_pkg.core.CoreEngine
Programmatic Python Integration
Embed queries directly into development tools by importing from codebase_rag.graph_query. This bypasses CLI overhead and allows custom result processing within Python scripts.
from codebase_rag.graph_query import definition, callers
# fetch_all represents a Memgraph QueryFn obtained from connect_memgraph
def analyze_symbol(fetch_all, project, qualified_name, repo_root):
# Get definition details including source and docstring
def_info = definition(fetch_all, project, qualified_name, repo_root)
# Find all callers up to depth 2
call_sites = callers(fetch_all, project, qualified_name, depth=2)
return def_info, call_sites
Natural Language to Cypher Translation
While deterministic queries handle structured lookups, the natural language interface routes complex questions through an LLM that generates Cypher queries against the graph schema. This hybrid approach ensures that simple navigation commands remain reproducible while open-ended questions like "How does the authentication flow work?" leverage semantic understanding to compose multi-hop graph traversals.
The LLM receives context about the graph schema—node labels like Function and Class, relationship types such as CALLS and IMPLEMENTS—and produces parameterized Cypher that executes against Memgraph. Results return as structured JSON that the system renders as human-readable answers, maintaining traceability back to specific source locations in graph_loader.py.
Summary
- Code-Graph-RAG combines Tree-sitter parsing with Memgraph to create a queryable knowledge graph of your codebase.
- Deterministic queries in
graph_query.pyprovide reproducible results for navigation tasks without LLM variance. - CLI tools via
cgr graphexpose resolution, definition lookup, caller analysis, and relationship traversal as JSON commands. - Python API allows direct integration of graph queries into custom development tools and scripts.
- Natural language interface translates English questions into Cypher for complex exploratory analysis while maintaining precision for simple lookups.
Frequently Asked Questions
What database does Code-Graph-RAG use to store the code graph?
Code-Graph-RAG uses Memgraph as its backend graph database. The system stores parsed symbols as nodes and their relationships (calls, implementations, imports) as typed edges, enabling efficient traversal queries through Cypher according to the schema defined in codebase_rag/constants.py.
How does the system handle ambiguous symbol names?
The resolve function in codebase_rag/graph_query.py handles ambiguity by accepting either a qualified name or a path:line coordinate. When multiple definitions exist, it returns all candidates with their respective file locations and existence flags, allowing downstream tools to disambiguate based on context.
Can I use Code-Graph-RAG without an LLM for deterministic results?
Yes. The deterministic query layer in graph_query.py executes pre-written Cypher statements directly against Memgraph for operations like finding definitions, callers, or implementations. These functions bypass the LLM entirely, ensuring consistent, reproducible results ideal for CI/CD pipelines and automated analysis.
What languages does the parser support?
The parser utilizes Tree-sitter, which supports multiple programming languages including Python, JavaScript, TypeScript, Java, C, and C++. The graph schema in codebase_rag/constants.py remains language-agnostic, representing concepts like functions, classes, and modules uniformly regardless of source language.
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 →