当前主流 AI 编程助手在面对大规模、非主流语言、自研框架的代码仓库时效果显著下降。其根本原因不是模型能力不足,而是模型缺少足够的代码库上下文。
本项目参考 NVIDIA 与 EA 在大规模 C++ 代码库上的实践思路,构建一个全本地、零云端依赖的代码库索引与检索工具,以 MCP 协议对外暴露服务,使任意支持 MCP 的 AI 编程助手都能获得代码库级别的上下文理解能力。
- 让 AI 编程助手"读懂"你的代码库:通过 RAG 将代码库知识注入 LLM 上下文
- 开箱即用:一条命令完成索引,一条命令启动服务
- 完全本地:不依赖任何云端服务,代码不出本机
- 仓库无关:适用于任意语言、任意框架的新代码仓库
| 用户类型 | 典型特征 |
|---|---|
| 新加入大型项目的开发者 | 需要快速理解项目架构与约定 |
| 使用自研框架/引擎的团队 | 框架文档少,AI 无法从公开数据学到相关知识 |
| 对代码安全有要求的团队 | 代码不允许上传到第三方云端 |
| 维护遗留代码库的开发者 | 代码库历史悠久,隐含大量领域知识 |
- 代码问答:"这个项目的网络层是怎么做错误重试的?"
- 代码搜索:"找到所有处理用户权限校验的代码"
- 上下文补全:AI 在生成代码时自动检索相关的项目代码作为参考
- 架构理解:"这个仓库的目录结构是什么?各模块职责是什么?"
- 文档检索:检索仓库中的 Markdown 文档、注释、README 等
# 用户的全部操作 —— 仅此而已
code-rag init /path/to/repo # 指向仓库,自动完成索引
code-rag serve # 启动 MCP 服务
之后在 Claude Code 等工具中即可自动获得代码库上下文能力,无需其他配置。
| 原则 | 说明 |
|---|---|
| 全本地运行 | 索引、嵌入、检索、服务全部在本机完成,不依赖任何外部 API |
| 零配置启动 | 对于绝大多数仓库,用户不需要写任何配置文件 |
| 语言无关 | 不绑定特定编程语言,通过 Tree-sitter 实现多语言 AST 解析 |
| 增量更新 | 文件变更时只重建受影响部分的索引,而非全量重建 |
| MCP 优先 | 以 MCP 协议作为唯一对外接口,保证与主流 AI 工具的兼容性 |
| 轻量依赖 | 尽量减少外部依赖,避免用户环境配置困难 |
┌──────────────────────────────────────────────────────┐
│ 用户的 AI Agent │
│ (Claude Code / Cursor / Copilot 等) │
└───────────────────────────┬──────────────────────────┘
│ MCP Protocol (stdio/SSE)
▼
┌──────────────────────────────────────────────────────┐
│ MCP Server 层 │
│ │
│ 对外暴露的 Tools: │
│ • search_code —— 语义/关键词搜索代码 │
│ • search_docs —— 搜索项目文档 │
│ • get_file_symbols —— 获取指定文件的符号映射 │
│ • get_repo_structure —— 获取仓库结构概览 │
│ • get_symbol_info —— 查询符号定义/引用 │
└───────────────────────────┬──────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌────────────────────┐ ┌─────────────────────────┐
│ Retriever 模块 │ │ Indexer 模块 │
│ │ │ │
│ • 语义搜索 │ │ • 文件发现与过滤 │
│ • 关键词搜索 │ │ • Tree-sitter AST 解析 │
│ • 混合排序 (RRF) │ │ • 语义感知分块 │
│ • 结果去重/聚合 │ │ • 本地嵌入生成 │
└─────────┬──────────┘ │ • 文件指纹/增量更新 │
│ └────────────┬────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────┐
│ 本地存储层 │
│ │
│ • 向量索引 —— Dense Vectors (语义搜索) │
│ • 倒排索引 —— BM25 (关键词搜索) │
│ • 元数据存储 —— 文件路径、符号信息、分块映射 │
│ • 全部持久化到本地磁盘 │
└──────────────────────────────────────────────────────┘
| 模块 | 选型 | 选择理由 |
|---|---|---|
| 对外协议 | MCP (Model Context Protocol) | 行业标准,Claude Code / Cursor 等原生支持 |
| MCP 框架 | FastMCP (Python) | 官方推荐,API 简洁,社区活跃 |
| AST 解析 | Tree-sitter | 支持 40+ 语言,增量解析,性能优异 |
| 本地嵌入模型 | ONNX Runtime + bge-small / CUDA + bge-large 系列 | 纯本地推理,前者CPU,后者GPU |
| 向量数据库 | ChromaDB | 嵌入式模式,跨平台,HNSW 索引,零运维 |
| 关键词检索 | BM25 (内置实现或 rank_bm25) | 补充语义搜索的精确匹配能力 |
| 开发语言 | Python | 生态丰富,MCP SDK 成熟,降低维护门槛 |
为什么不做 IDE 插件或 CLI 工具?
- IDE 插件需要为每种 IDE 单独开发,维护成本高
- CLI 工具无法与 AI Agent 的对话流程集成
- MCP 是一层标准协议,只需实现一次,即可被所有支持 MCP 的 AI 工具调用
- 未来 MCP 生态只会越来越完善,选择 MCP 是面向未来的决策
为什么需要 AST 解析,而不是简单的按行/按字符分块?
- 简单分块会把一个函数从中间截断,破坏语义完整性
- AST 解析能以函数、类、方法、结构体为单位进行分块,保证每个代码块是一个语义完整的单元
- Tree-sitter 的增量解析能力天然适合文件变更时的快速重建
为什么选 Tree-sitter 而非 LSP?
- LSP 需要为每种语言配置对应的 Language Server,部署复杂
- Tree-sitter 是纯解析器,不需要编译环境,启动即用
- 对于索引场景,Tree-sitter 提供的信息粒度已经足够
为什么不用 Milvus Lite / Faiss / SQLite-VSS?
| 方案 | 优点 | 缺点 |
|---|---|---|
| Faiss | 性能极好 | 不是数据库,无持久化/元数据管理,需自行封装 |
| Milvus Lite | 嵌入式运行、原生混合搜索 | 不支持 Windows,索引类型仅支持 FLAT |
| SQLite-VSS | 极轻量 | 向量检索能力弱,不支持 HNSW 等高效索引 |
| ChromaDB ✅ | 嵌入式运行、跨平台(Windows/Linux/macOS)、HNSW 索引、持久化到本地目录 | 在超大规模数据下性能略弱于 Faiss(可接受) |
ChromaDB 的关键优势:
pip install chromadb即可使用,无需单独部署服务- 原生支持 HNSW 索引,检索质量好
- 跨平台:Windows、Linux、macOS 均可运行
- 数据持久化为本地目录,重启后无需重建索引
- API 简洁,测试中无需 mock(嵌入式直接使用)
为什么需要混合检索?
- 纯语义搜索的问题:用户搜索具体的函数名
handleAuthCallback时,语义搜索可能返回语义相似但名称不同的代码 - 纯关键词搜索的问题:用户提问 "这个项目怎么处理认证回调" 时,关键词无法匹配到
handleAuthCallback - 混合检索通过 RRF (Reciprocal Rank Fusion) 将两路结果合并排序,兼顾精确匹配与语义理解
用户可以自行设置白名单或黑名单,可以通过 --include / --exclude 选项覆盖。或使用过滤规则(.coderagfilter 文件)
代码仓库
├── 代码文件 (.py, .cpp, .java, .ts, ...)
│ → Tree-sitter 解析 → 按 AST 节点分块
│ → 每个块 = 一个函数/类/方法 + 其文档注释
│ → 附带元数据:文件路径、起止行号、符号名、语言
│
├── 文档文件 (.md, .rst, .txt)
│ → 按标题层级分块(# / ## / ### 为分割边界)
│ → 附带元数据:文件路径、标题层级
│
└── 配置文件 (.json, .yaml, .toml, Makefile, Dockerfile, ...)
→ 整文件作为一个块(通常较小)
→ 附带元数据:文件路径、文件类型
输入: AST 节点
│
├── token_count ≤ max_tokens?
│ └── YES → ✅ 直接作为一个 chunk
│
├── 有可拆分的子节点?(函数/类/方法)
│ └── YES → 递归处理每个子节点
│ → 给每个子 chunk 加父级上下文前缀
│
└── 叶子节点仍超限?
└── 滑动窗口分割
→ overlap = 10% 的 max_tokens
→ 每个窗口加上下文前缀
- 为每个已索引文件记录 文件内容哈希(SHA-256)
- 执行更新时,遍历仓库文件,比对哈希:
- 哈希未变 → 跳过
- 哈希变化 → 删除旧分块,重新解析并入库
- 文件已删除 → 删除对应分块
- 可监听文件系统事件(可选),实现后台自动增量更新
代码支持Python, JavaScript, TypeScript, C++, C, Java, C#, Lua, Rust, Go.
也支持用户自定义文件类型,比如.glsl, .fx设置为C模式。
| Tool 名称 | 输入参数 | 返回内容 | 典型调用场景 |
|---|---|---|---|
search_code |
query: str, top_k: int, language?: str |
相关代码块列表(含文件路径、行号、代码内容) | AI 需要查找相关实现 |
search_docs |
query: str, top_k: int |
相关文档片段列表 | AI 需要了解项目文档 |
get_file_symbols |
file_path: str |
该文件中所有符号的索引信息 | AI 正在编辑某文件,或想了解文件内容 |
get_repo_structure |
depth?: int |
仓库目录树 + 各目录/文件的简要描述 | AI 需要了解项目整体结构 |
get_symbol_info |
symbol_name: str |
符号的定义位置、引用位置、相关代码块 | AI 需要理解某个具体符号 |
- 每个返回结果都包含文件路径 + 行号范围,方便 AI 引用出处
- 代码块返回时附带上下文窗口(前后各 N 行),避免信息截断
- 搜索结果按相关性分数降序排列,附带分数供 AI 判断可信度
| 不做的事 | 理由 |
|---|---|
| 模型微调 | 成本高、复杂度高,与"轻量本地"定位冲突 |
| 云端部署 | 违反全本地原则 |
| IDE 插件 | 通过 MCP 协议已能覆盖主流工具,不必为特定 IDE 开发插件 |
| 代码生成/补全 | 本工具只负责"检索与提供上下文",生成由 AI Agent 完成 |
| 实时编译/类型分析 | 过重,Tree-sitter 层级的 AST 解析已满足索引需求 |
| 多用户/团队协作 | 第一阶段只面向单机单用户场景 |
| 维度 | 目标 |
|---|---|
| 易用性 | 用户从下载到服务可用,不超过 3 条命令 |
| 索引速度 | 10 万行代码仓库,首次索引 < 5 分钟(普通笔记本 CPU) |
| 检索质量 | 对于明确的代码搜索意图,Top-5 结果中至少有 1 条命中相关代码 |
| 资源占用 | 服务运行时常驻内存 < 500 MB(中型仓库) |
| 启动速度 | 已有索引的情况下,服务启动 < 3 秒 |
| 兼容性 | 至少支持 Claude Code, OpenCode 和 Cursor 三个主流 MCP 客户端 |
采用 uv 进行 Python 环境管理,首次运行时自动完成依赖安装,并根据硬件环境自动适配 CPU/GPU 模型及所需的运行时组件(CUDA、OMNI 等),无需手动配置。