Graphify: Parallel LLM Agents Turn Your Codebase Into a Knowledge Graph
Hook
Most code indexing tools process files sequentially. Graphify spawns a separate LLM agent for every file in your codebase simultaneously, then merges the chaos into a unified knowledge graph.
Context
Onboarding to a large, unfamiliar codebase is archaeological work. You grep through thousands of files, chase import chains across directories, and try to reconstruct mental models of how database schemas connect to API endpoints. Traditional code search tools like Sourcegraph excel at "find this function" but fail at "explain how authentication flows through this system." Documentation is outdated. READMEs lie.
Vector databases solved part of this with semantic search—embed code chunks, find similar patterns—but they discard structural relationships. You lose the graph. GraphRAG approaches like Microsoft's framework promised queryable knowledge graphs with Cypher support, but demanded Azure infrastructure and enterprise budgets. Graphify emerged as the local-first, polyglot alternative: point it at any folder containing code, SQL schemas, research papers, or even videos, and it constructs a knowledge graph by orchestrating parallel LLM extraction workers. More importantly, it hijacks your AI coding assistant's control flow through platform-specific hooks, making the graph ambient context rather than a tool you remember to invoke.
Technical Insight
Graphify's architecture splits graph construction into three distinct phases: file discovery and preprocessing, parallel entity extraction via multi-agent orchestration, and graph assembly with community detection.
The preprocessing stage uses tree-sitter parsers for 40+ programming languages to generate Abstract Syntax Trees (ASTs), not raw text. When you point Graphify at a Python file, it doesn't see strings—it sees function definitions, class hierarchies, import graphs as first-class entities. SQL schemas get dedicated extractors for table relationships. PDFs flow through text extraction pipelines. Videos hit faster-whisper for transcription. This format normalization happens before any LLM sees the data.
The parallel extraction phase is where Graphify diverges from sequential tools like Aider's repository maps. Each preprocessed file gets dispatched to a separate LLM agent worker—Claude Code's Agent API, OpenAI's Assistants API, or platform equivalents—that identifies entities (classes, functions, concepts) and relationships (calls, imports, depends_on). This isn't batched prompting. These are concurrent, independent agent instances. Here's the simplified extraction logic:
# Conceptual pseudo-code showing parallel dispatch
from concurrent.futures import ThreadPoolExecutor
import anthropic
def extract_entities(file_path, content, ast_data):
client = anthropic.Anthropic()
prompt = f"""
Analyze this code and extract:
- Entities: classes, functions, key variables
- Relationships: function calls, imports, inheritance
File: {file_path}
AST: {ast_data}
Content: {content}
Return JSON: {{"entities": [...], "relationships": [...]}}
"""
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": prompt}]
)
return parse_json_response(response.content)
files = discover_and_preprocess(target_directory)
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(
lambda f: extract_entities(f.path, f.content, f.ast),
files
))
Each worker returns a fragment: entities it found, edges it identified. The graph assembly phase merges these fragments into a unified JSON structure, deduplicating entities by exact string match (not fuzzy matching—a known limitation). When the optional igraph dependency is available (Python <3.13 only, due to compatibility issues), Graphify runs Leiden community detection to cluster the graph into conceptual modules. These clusters often reveal architectural boundaries that don't match directory structure—say, all authentication-related code across backend, frontend, and database layers grouped together.
The integration model is what makes this ambient rather than invoked. Graphify doesn't just build a graph and wait for you to query it. It injects itself into your AI assistant's decision-making process through platform-specific hooks:
# Example PreToolUse callback for Claude Code
def on_pre_tool_use(tool_name, arguments):
if tool_name in ['read_file', 'search_files']:
if graph_exists():
return {
"suggestion": "Consider using 'graphify query' instead for architectural context",
"command": f"graphify query {arguments['pattern']}"
}
return None
For platforms without callback APIs (Aider, OpenClaw), Graphify writes persistent instruction files like AGENTS.md or .cursor/rules/ that tell the assistant "always check graph.json before grepping." The assistant consumes GRAPH_REPORT.md—a markdown summary of entities, relationships, and community clusters—as architectural context on startup.
The output is deliberately multi-format: graph.json for programmatic queries, graph.html with an interactive D3.js visualization for human exploration, and GRAPH_REPORT.md for LLM consumption. Three representations for three audiences. The query interface is basic—substring matching in JSON—but sufficient for "show me all database table references" or "find authentication-related entities."
The double-subprocess model solves a subtle problem: AI assistant environments have unpredictable Python installations. The skill (running in the assistant's context) shells out to a graphify CLI, which persists the correct interpreter path to graphify-out/.graphify_python at build time. This indirection ensures consistent execution across heterogeneous environments without virtualenv collisions.
Gotcha
Parallel extraction sounds elegant until you point Graphify at a 5,000-file monorepo. Each file triggers an LLM API call, so you immediately hit rate limits (Anthropic: 50 requests/minute on tier 1, OpenAI: 500 requests/minute on pay-as-you-go). There's no batching strategy, no incremental update logic beyond file-level memoization. Re-running Graphify after changing ten files in a 2,000-file codebase means re-extracting everything. For active development, this is prohibitively expensive in both time and API costs.
Graph quality is entirely LLM-dependent with zero validation. Hallucinated entities pollute the graph—an LLM might invent a "UserAuthenticationManager" class that doesn't exist. Relationships are probabilistic: sometimes it catches a subtle dependency between modules, sometimes it misses an obvious import. Entity deduplication is exact string matching only, so "DatabaseConnection," "DBConnection," and "db_connection" become three separate nodes. There's no schema enforcement, no confidence scoring on edges, no way to trust the graph for compliance or audit purposes. The Leiden clustering is optional and breaks on Python 3.13+ due to igraph incompatibility—not Graphify's fault, but it means the most useful feature (community detection) is unavailable on modern Python installations.
The "queryable knowledge graph" claim oversells the query capabilities. You get substring search in JSON, not Cypher or SPARQL. Multi-hop graph traversals require writing custom Python to walk the JSON structure or exporting to Neo4j (an optional extra that itself requires database setup). If you need "find all code paths from API endpoint X to database table Y," you're writing traversal logic manually.
Verdict
Use if: You're inheriting a large, unfamiliar codebase (especially polyglot stacks with mixed SQL schemas, documentation, and code) and need rapid architectural orientation without reading thousands of files. You have LLM API budgets for parallel extraction and work on codebases that change infrequently enough that full re-indexing is tolerable. You want ambient integration where your AI assistant automatically consults the graph without explicit invocation. Skip if: You need incremental updates on actively developed codebases (no change tracking means prohibitively expensive re-extraction), require strict schema validation or auditable lineage tracking (LLM extraction is probabilistic), or want actual graph query languages beyond substring matching. Also skip if you're on Python 3.13+ and need community detection, or if your codebase exceeds a few hundred files and you're on rate-limited LLM tiers. For those cases, choose Sourcegraph for proven enterprise search, Microsoft's GraphRAG for production-grade Cypher queries, or vector databases for pure semantic similarity without graph structure.