Code RAG Chatbot
RAG for code search and understanding, chatbot for code queries, and MCP for Claude Code RAG skills.
Code RAG
Test it by asking questions about my portfolio at paulxie.com/demos/code-rag/
A RAG chatbot that answers questions about code repositories. Ingests all sibling project directories, parses Rust, Python, TypeScript, and Go codebases with tree-sitter, extracts docstrings and a persistent AST call graph, generates embeddings, and responds via Google Gemini. Intent classification routes queries to optimized retrieval strategies — including graph augmentation for relationship queries and per-comparator decomposition for comparison queries. Retrieval traces surface all sources with relevance scores — the system shows its work.
Use as MCP server with Claude Code
The retrieval brain ships as code-rag-mcp, a single-binary MCP server you can drop into any of your own repos to give Claude Code intent-routed retrieval, call-graph traversal, architecture overviews, and topology insight — emergent-module maps with folder-drift comparison, “read these first” rankings, dependency-cycle detection, and call-path tracing with Mermaid output (nine tools). No API keys, no cloud.
Install is three steps, no terminal commands once the exe is on disk:
- Download the zip for your platform from the GitHub Release page and extract it. You get one binary plus a
code-rag-mcp.config.yamltemplate. - Edit the YAML — set
target_pathto your repo (or a parent folder withworkspace: truefor many sub-projects). - Run the exe (double-click works). It writes
.claude/skills/code-rag.md,.mcp.json, and a.gitignoreentry into your target dir, then exits.
Open Claude Code in the target dir. The bundled skill instructs the agent to run code_rag_reindex mode=full for the initial ingest automatically.
Full walkthrough: crates/code-rag-mcp/README.md. Release process: docs/release.md.
Usage
Place this repo alongside the projects you want to index:
any-parent-folder/
├── code-rag/ ← this repo
├── project-a/
├── project-b/
└── ...
Ingestion walks all sibling directories — each becomes a separate project. The parent folder name doesn’t matter.
docker-compose -f docker-compose-ingest.yaml updocker-compose up
To clean, run sh clean_docker.sh.
Development Roadmap
| Version | Date | Focus |
|---|---|---|
| V0.1 | 2025-12-23 | MVP - Core engine |
| V0.2 | 2026-01-01 | Docker deployment |
| V0.3 | 2026-01-31 | Workspace restructuring |
| V1.1 | 2026-02-04 | Schema foundation (UUID, content_hash, delete API) |
| V1.2 | 2026-02-06 | LanguageHandler trait refactor |
| V1.3 | 2026-02-06 | Incremental ingestion (SHA256, three-layer architecture) |
| V1.4 | 2026-02-07 | TypeScript support (TSX grammar, JSDoc) |
| V1.5 | 2026-02-07 | Docstring extraction (Rust, Python, TypeScript) |
| V2.1 | 2026-02-07 | Inline call context (AST-based call extraction) |
| V2.2 | 2026-02-08 | Intent classification + query routing (cosine similarity) |
| V2.3 | 2026-02-08 | Retrieval traces (scored sources, cross-type ranking) |
| Leptos Migration | 2026-03-25 | WASM frontend (replace htmx/Askama with Leptos) |
| GitHub Pages Demo | 2026-03-25 | Shared engine crate + standalone WASM deployment |
| V3.1 | 2026-04-02 | Retrieval test dataset (43 queries, 4 intent categories) |
| V3.2 | 2026-04-02 | Recall measurement harness (recall@K, MRR, intent accuracy) |
| V3.3 | 2026-04-03 | Baseline quality metrics (dual-run, per-intent breakdown) |
| B1 | 2026-04-04 | Cross-encoder reranking (ms-marco-MiniLM-L-6-v2) |
| B2 | 2026-04-04 | Hybrid BM25+semantic search infrastructure (disabled pending B3) |
| B3 | 2026-04-05 | Declaration signatures + searchable_text + per-intent gating (recall@5 0.70→0.75) |
| B4 | 2026-04-05 | Intent classifier 58%→74% (prototypes + k-NN + keyword pre-filter) |
| B5 | 2026-04-06 | Dual-vector schema + per-intent ArmPolicy (bm25/rerank gating) |
| C1 | 2026-04-09 | Graph RAG — call graph edges + 3-tier resolution + traversal (relationship 0.50→0.57) |
| C2 | 2026-04-09 | Graph result protection — SOTA routing + soft reserve (relationship 0.57→0.60) |
| C3 | 2026-04-09 | Comparison query decomposition — per-comparator RRF + project filter (comparison 0.62→0.65, aggregate 0.71→0.72) |
| A1 | 2026-04-17 | Text module consolidation — single code-rag-engine::text for tokenize/IDF/BM25/searchable_text (WASM/native single source) |
| A2 | 2026-04-17 | Folder-level embeddings — FolderChunk (5-line template, 118 chunks) shipped dark for A3 activation |
| A3 | 2026-04-17 | Collapsed-tree routing — folder arm activated for Overview/Implementation/Comparison (comparison 0.31→0.67 +36pp) |
| A4 | 2026-04-18 | File-level embeddings — FileChunk (4-line template, 247 chunks) + stratified relationship retrieval (recall@pool metric introduced) |
| MCP | 2026-04-24 | Claude Code MCP server (code-rag-mcp) — five tools (search, graph, overview, neighbors, reindex) on rmcp 1.5; bundled Skill; single-binary install (download → edit code-rag-mcp.config.yaml → run exe); subsumes code-rag-ingest via internal ingest subcommand; manually-triggered cross-platform release pipeline; agent-driven first ingest |
| Caravan | 2026-05-21 | Caravan adoption (B0p → M5). Four #[wagon] seams (Embedder / Reranker / VectorReader / LlmClient); RigGeminiImpl extracted to code-rag-llm; chat-side core (AppState, retrieve) extracted to code-rag-core (MCP no longer transitively depends on chat binary); four mix-and-match caravan.yaml targets (dev-monolith / dev-split-light / dev-split-mixed / dev-split-heavy) exercise per-seam independent dispatch. caravan-rpc 0.1.0 published to crates.io |
| R0 | 2026-06-08 | Crate split — parser/ingester renamed code-rag-ingest; code-raptor reclaimed as the topology engine (Track R home) |
| R1 | 2026-06-08 | RelationGraph + typed relation edges (graph_edges: imports/re_exports/contains/implements/extends/embeds/references/rationale_for) with per-language anchored resolution. Hardened 2026-08: project-scoped identifier index (graph never links projects) + documented per-language import matching |
| R2 | 2026-06-10 | Emergent communities — deterministic Louvain (no RNG) + hub exclusion + cohesion scoring, persisted to additive community_assignments table; folder→file contains excluded from partition input by design |
| R3 | 2026-06-10 | ClusterChunk summaries (template tier, cluster_chunks table, retrieval arm) — arm gated OFF by measurement (displaced code/folder chunks on Overview; machinery stays wired for a slot-protection/LLM-tier revisit) |
| R4 | 2026-08-06 | Structural analytics + per-project architecture report — degree centrality (wasm-safe), Brandes edge-betweenness bridges, surprise ranking, import-cycle detection (Tarjan SCC + bounded canonical DFS), byte-deterministic markdown artifact |
| R5 | 2026-08-10 | Track R capstone — emergent-vs-folder drift comparison (report section), interactive topology view in the demo (d3-force, click-a-node → chat query), GraphML + viz JSON exports, four MCP topology tools (communities / central_nodes / cycles / path with Mermaid call-flow) via revived find_path |
Purpose
- To practice and demonstrate familiarity with
- Rust
- RAG
- Chatbot Application
- To function as meta-project
- To answer questions about sibling code repositories
Tech Stack
- Backend: Rust + Axum
- LLM: Google Gemini (via rig-core)
- Vector DB: LanceDB
- Embeddings: FastEmbed
- Frontend: Leptos (Rust WASM, CSR)
- Deployment topology: Caravan — one yaml flips per-seam dispatch (inproc / container / lambda) without source edits
Deployment flexibility via Caravan
code-rag adopts Caravan, an application-definition compiler. The four inter-component boundaries (Embedder, Reranker, VectorReader, LlmClient) are wrapped as caravan-rpc seams; a single yaml projects code-rag onto multiple deployment topologies with zero source-code edits between them.
# caravan.yaml (excerpt)
targets:
dev-monolith: # all 4 seams inproc — baseline single binary
runtime: docker-compose
entries: { code-rag-chat: container }
dev-split-mixed: # 2 seams flipped to separate compose services
runtime: docker-compose
entries: { code-rag-chat: container }
seams:
Embedder: container
Reranker: container
# VectorReader + LlmClient default to inproc
caravan compile --target=dev-split-mixed
docker compose \
-f docker-compose.yaml \
-f infra/dev-split-mixed/generated/docker-compose.override.generated.yaml \
up
Caravan emits a compose override that layers atop the hand-authored docker-compose.yaml — adding peer services for split seams + injecting the per-deploy-unit CARAVAN_RPC_PEERS table the SDK reads at runtime. Same chat image, same binary, same source tree; only the env var changes.
SDK adoption surface is closed at four touchpoints in user code:
#[wagon]on each seam trait (declares the wire surface, crates/code-rag-store/src/seams.rs)caravan_rpc::provide::<dyn I>(Arc::new(impl_))at startup (crates/code-rag-core/src/state.rs)caravan_rpc::client::<dyn I>().method(...)at call sitescaravan_rpc::run_or_serve(user_main)once in src/main.rs so the same binary can detour into peer-server mode based onCARAVAN_RPC_ROLE
No-config inertness: when CARAVAN_RPC_PEERS is unset, client::<dyn T>() returns the registered Arc<dyn T> directly with zero overhead. All existing deployment surfaces — compose chat, compose ingest, local cargo run, WASM gh-pages, single-binary MCP release — keep working unchanged.
See caravan.yaml for the full seam declarations and four mix-and-match targets (dev-monolith / dev-split-light / dev-split-mixed / dev-split-heavy).
Guiding Principles
“Vertical slices, retrieval quality, code understanding”
| Principle | Meaning |
|---|---|
| Vertical | Build working end-to-end first, then deepen |
| Retrieval | Quality of retrieved context determines answer quality |
| Understanding | Goal is semantic code understanding, not just text search |
Architecture
| Crate | Single Responsibility |
|---|---|
code-rag-ingest | Ingestion CLI — parsing, chunk extraction, data export |
code-raptor | Topology engine (Track R) — derives architectural structure from persisted relation edges |
code-rag-engine | Shared algorithms — intent, context, scoring (pure, no I/O) |
code-rag-store | Storage seams + LanceDB-backed impls; fastembed Embedder + Reranker |
code-rag-llm | LLM-provider seam impls (rig-core-backed Gemini today; extracted at M5 so synthetic Caravan peer services can build against the impl) |
code-rag-core | Chat-side core shared between the chat binary and the MCP server — AppState, retrieve(), EngineError, SourceInfo. Extracted at M5 to break code-rag-mcp’s transitive dep on code-rag-chat |
code-rag-types | Shared types — no logic |
code-rag-chat | HTTP routing + Askama templates + binary main (2 binaries: chat server + harness). Core logic moved to code-rag-core |
code-rag-mcp | MCP stdio server (single-binary release) — depends on code-rag-core directly, no chat-binary transitive |
code-rag-ui | Leptos WASM SPA — chat interface (backend or standalone mode) |
Current State
- Function-level chunking: 1 function/class → 1 vector (BGE-small, 384 dim)
- Supports Rust, Python, TypeScript, and Go via tree-sitter AST parsing
- Docstrings extracted:
///(Rust),"""(Python),/** */(TypeScript JSDoc),//(Go) - Declaration signatures extracted: functions + structs/enums/traits/interfaces/classes
- Hierarchy chunks (Track A):
FolderChunk(1 per directory, 5-line template — folder/files+languages/key types/key functions/subfolders) andFileChunk(1 per source file, 4-line template — file/exports/imports/purpose). Built deterministically at ingest from existing CodeChunk metadata + C1 imports map — no LLM. Both render through purecode-rag-engine::{folder,file}functions, so server-embedded bytes and browser BM25 bytes are byte-identical - Persistent call graph (Graph RAG): LanceDB scalar-only
call_edgestable, 3-tier resolver (same-file → import-based → unique-global), AST scoped-identifier (module::function()) extraction - Typed relation topology (Track R):
graph_edgestable persists imports / re_exports / contains / implements / extends / embeds / references / rationale_for edges with per-language anchored resolution, project-scoped so the graph never links sibling projects (retrieval stays corpus-wide) - Emergent communities (Code Raptor): deterministic Louvain (fixed order, no RNG — identical partition every run) over calls ∪ imports ∪ type relations ∪ file-level containment, with hub exclusion + cohesion scoring; persisted per-chunk in
community_assignments. Folder→file edges are deliberately excluded from the partition input so the emergent-vs-folder comparison is earned, not self-fulfilling - Architecture report + drift: per-project byte-deterministic markdown artifact — “read these first” degree centrality, cross-community bridges (Brandes edge-betweenness) with relation provenance, surprising-connection ranking, import cycles (Tarjan SCC + bounded canonical DFS), and an emergent-vs-folder drift section (community purity vs directory layout — e.g. it flags the
vector_store.rsmonolith splitting into 15 communities) - Topology exports: per-project
graph_viz_<project>.json(browser artifact, capped 5000 nodes / 15 000 edges) + full-graph GraphML for Gephi/yEd — both byte-deterministic across runs - Test code exclusion at ingest (3-level): directory
tests/, filenametest_*.py/*.test.ts, AST-walked#[cfg(test)]enclosing-mod detection (~24% chunk reduction) - Intent classification: cosine similarity against prototype query embeddings
- Query routing: declarative routing table maps intent → retrieval limits across all six chunk types (code, folder, file, readme, crate, module_doc)
- Two-stage retrieval: hybrid (BM25 on
searchable_text+ vector) → cross-encoder reranking (ms-marco-MiniLM-L-6-v2), fused via N-ary RRF in sharedcode-rag-engine::fusion - Per-intent
ArmPolicy: per-intent{body_vec, sig_vec, bm25, rerank, folder_vec, file_vec}gating (single source of truth, server + browser). Overview = hybrid+rerank; Implementation = rerank-only; Relationship = hybrid+rerank; Comparison = vector-only. Folder arm gated off for Relationship after empirical regression (folder chunks of X displaced consumers of X — stratified retrieval, file arm stays on) - Graph-augmented retrieval: shared
code-rag-engine::graph(graph_augment,merge_graph_chunks,reserve_graph_slots,detect_direction). Two protection paths: SOTA routing for explicit-direction queries (“what calls X / called by”) partitions graph chunks out of the reranker entirely; soft reserve for ambiguous-direction over-retains the code arm by+5and rescues demoted graph chunks. Mirrored line-for-line in WASM standalone - Comparison query decomposition (
code-rag-engine::comparison): regex extracts ≥2 comparators → per-comparator body-vec sub-searches (comparator name prepended to original query) → vote-based dominant-project filter → RRF fusion → max-of-natural rescoring so RRF outputs compete with non-code arms. Mirrored in WASM standalone - Single text module (A1):
code-rag-engine::textis the sole home fortokenize,IdfTable, BM25 kernel,build_searchable_text,split_camel_case, and intent prototype texts. Compiles to native + wasm32 — no duplication across server, store, raptor, or UI - Dual-vector schema: nullable
signature_vectorcolumn populated at ingest (shipped OFF after 8-config space sweep; column retained for future experiments) - Intent classifier: prototype cosine similarity + k-NN (k=3) weighted voting + Comparison keyword pre-filter with adversarial guards — 74% accuracy (was 58%)
- Retrieval traces: all 6 chunk types surfaced with relevance scores, sorted by relevance
- Quality harness: 87-query test dataset (79 recall-scoreable), recall@K,
recall@pool(introduced in A4 — recall over every chunk reachingbuild_context, no top-k truncation), MRR, intent accuracy, latency — dual-run mode - Current baseline (post_rationale_anchor, 2026-08-07, 6-project corpus, classifier routing): recall@5 = 0.60 aggregate · overview 0.70 · implementation 0.61 · relationship 0.47 · comparison 0.62 · recall@10 = 0.69 · recall@pool = 0.72. Not comparable to the earlier post-A4 0.72@5: two projects were purged from the corpus (source repos deleted), the cross-project resolution leak was fixed (edges that once inflated recall were wrong answers), and the corpus now includes code-rag’s own Track R code
- Incremental ingestion: SHA256 file hashing, skips unchanged files
- Shared
code-rag-enginecrate: pure algorithms compile to native + wasm32 - GitHub Pages demo:
standalonefeature runs the full RAG pipeline in-browser (LLM generation optional), plus an interactive topology tab — d3-force canvas of each project’s emergent communities (community-colored, degree-sized, relation-dashed edges; theme-aware palette), click a node to run a code-rag query about it
Known Limitations
- Granularity: File and folder levels covered by Track A (
FileChunk,FolderChunk). Repo-level summary considered and retired by measurement (existing READMEs + project-root FolderChunks already covered the 3 hero queries at recall@10=1.0) - Comparison short-identifier ceiling: Two stubborn pre-C3 failures (
comp-retriever-generator,b4-comp-retriever-api) remain — BGE-small produces noisy vectors for bare hyphenated identifiers (retriever,generator), and the C3 regex extracts comparators only from explicit “compare X and Y / X vs Y” phrasings. Gated on a future embedder upgrade (BGE-base / jina-code) or MMR fallback - Classifier: No longer the dominant bottleneck post-B4 (+2pp classifier→GT gap on recall@5). Implementation and Relationship classification still below targets (70% / 53%)
- Incremental reconcile under multi-file change: surfaced during A4 calibration — wipe-and-reinsert across many simultaneously-changed files corrupted BM25 IDF / row ordering and degraded hybrid retrieval. Workaround: fresh full ingest. Filed as a follow-up; not Track A-specific
Planned Features
See project-vision.md and development_plan.md for roadmap.
Keywords
- Language:
Rust - Architecture & Patterns:
Layered Architecture (API/Store/Ingestion)·Trait-Based Abstraction (LanguageHandler)·Registry Pattern (OnceLock)·Three-Layer Pipeline (Parse→Reconcile→Orchestrate)·Router Pattern·Handler Pattern·Shared State (Arc)·Repository Pattern·DTO Pattern·Modular Design·Pipeline Pattern (Ingest→Embed→Store)·Visitor Pattern (WalkDir)·Error Propagation (thiserror)·Ephemeral Side-Channel Pattern·Declarative Routing Table·Scored Search API·ScoredChunk<T> (Generic Wrapper)·Retrieval Traces·Multi-Binary Crate (lib.rs extraction)·FlatChunk Centralization - LLM & RAG:
RAG (Retrieval-Augmented Generation)·Graph RAG·Call Graph Augmentation·Graph-Augmented Retrieval·SOTA Routing (Reranker Bypass)·Soft Reserve·Comparison Query Decomposition·Per-Comparator RRF Fusion·Sub-Query Expansion·Vote-Based Project Filter·Max-of-Natural Rescoring·LLM Integration·Google Gemini API·rig-core·Semantic Search·Chatbot·Intent Classification (Cosine Similarity)·Prototype Query Embeddings·k-NN Prototype Voting·Keyword Pre-Filter (adversarial-guarded)·Intent-Aware Retrieval·Per-Intent Gating (ArmPolicy)·Two-Stage Retrieval·Cross-Encoder Reranking·Hybrid Search (BM25 + Dense)·RRF Fusion·Dual-Vector Schema·Declaration Signatures·searchable_text (IR field boosting)·camelCase Splitting (index-time)·Cross-Type Source Ranking·Distance-to-Relevance Scoring·Retrieval Transparency - Quality & Evaluation:
Recall@K·MRR (Mean Reciprocal Rank)·Intent Accuracy·Latency Percentiles (p50/p95)·Dual-Run Evaluation (Classifier vs Ground-Truth)·Per-Intent Breakdown·Declarative Test Dataset·Substring File Matching·Dataset Freeze Policy·Baseline Regression Tracking·Space Search (per-intent ArmPolicy sweep)·Adversarial Test Cases·Held-out Classifier Eval - Vector Database:
LanceDB·LanceDB FTS·Scalar-Only LanceDB Table (call_edges)·BM25·FastEmbed·BGE Embeddings·ms-marco-MiniLM-L-6-v2 (ONNX) - Code Analysis:
Tree-sitter·AST Parsing·Code Chunking·Docstring Extraction·JSDoc Parsing·Multi-Language (Rust, Python, TypeScript, Go)·Incremental Ingestion (SHA256)·Call Graph Extraction (AST-based)·Function Call Detection (Direct + Method)·Call Edge Resolution (3-tier)·Import-Based Symbol Resolution·Scoped Identifier Extraction·Test Code Exclusion (cfg(test) AST walk) - Graph & Topology (Track R):
Community Detection (Louvain)·Modularity Maximization·Cohesion Scoring·Edge Betweenness (Brandes)·Tarjan SCC·Dependency Cycle Detection·Emergent Architecture·Architecture Drift (Emergent vs Folder)·Typed Relation Edges·Degree Centrality·GraphML Export·Mermaid Call-Flow·Force-Directed Layout (d3-force)·Deterministic Graph Algorithms - Web Framework:
Axum·Leptos (WASM CSR)·Tower HTTP·CORS - Async & Runtime:
Tokio Runtime·Async Programming - DevOps:
Docker·Docker Compose·GitHub Pages (WASM)·Google OAuth2 (GIS) - Rust Ecosystem:
tracing·Error Handling (anyhow/thiserror)·Serde·clap (CLI)·chrono