code-rag
No description
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 26, 2026
- Updated
- Aug 29, 2026
Introduction
code-rag — 本地代码库语义索引与检索(DSH 模式)
对本地代码库(C++ / Java 为主,支持中英文注释混合)做声明感知分块 → Ollama 向量化 → Qdrant 存储, 为 DSH 会话提供五个 MCP 工具 + 一个可视化索引面板:
| 工具(模型视角) | 作用 |
|---|---|
mcp__code_rag__search_code | 自然语言检索代码,返回文件路径 / 函数名 / 行号 / 相似度 / 片段(按 root 隔离) |
mcp__code_rag__index_codebase | 异步索引:立即返回 taskId,后台执行,不受调用超时限制;full: true 只重建当前根 |
mcp__code_rag__index_status | 索引状态 + 运行中任务的实时进度(百分比/文件/分块/错误明细) |
mcp__code_rag__index_cancel | 取消当前根的运行中任务 |
mcp__code_rag__set_root | 切换当前代码库根(换工作区/项目后的第一步) |
工具名前缀
mcp__code_rag__由 DSH 的 MCP 桥接插件(@deepseek-ai/dsh-mcp-client)按mcp__<serverName>__<rawName>规则生成,search_code等是 MCP 服务器侧的原始名。
索引面板(Roo Code 风格)
MCP 服务器内置本地 HTTP 端点(127.0.0.1:8756,占用自动上探),会话输入框上方的
Code RAG 索引状态条(动态 Cordis 插件)直接读取它:
- 显示:当前根、索引状态、进行中任务的进度条(% / 文件 / 分块)、错误明细;
- 按钮:开始索引(增量)/ 全量重建 / 取消;
- 数据与按钮操作与 MCP 工具完全同源(同一任务管理器);
- 目标根 = 当前会话工作区(面板默认对工作区操作;服务器根不同会提示);
- 端口发现:探测 8756-8786 全部端口并选择 startedAt 最新(避免连到残留旧服务器);
- 所有请求带 8s 超时,服务器无响应显示离线而非永久挂起。
设置与诊断页(插件 v1.1.0+):DSH 设置 →「Web 插件」区新增 Code RAG 卡片,
一键检测索引服务器 / Qdrant / Ollama / 模式预设 / 配置文件并列出问题清单;可在线修改
<root>/.code-rag/config.json(Qdrant/Ollama 地址、嵌入模型、维度、集合、端口、
分块行数)、启动/取消索引、一键重新生成预设(npm run setup)。数据经插件 host
半部的 /api/code-rag/* 环回端点(仅本机来源可访问,详见插件 README)。
插件源码(动态版):plugins/index-panel.client.js,可交给 cordis_define 临时启用。
已固化:plugins/dsh-code-rag-index-panel/ 是常驻版 npm 插件(ModuleLoader 格式),
由 npm run setup 以 file: 依赖装入 web profile 的 bundles 列表(~/.dsh/profiles/web/package.json),
重启 DSH 后所有会话生效;此后修改 lib/client.js 由 client-hmr 轮询自动热更新(无需刷新页面)。
没有写死的路径:仓库内所有机器相关路径都是占位符(
{{CODE_RAG_DIR}}等), 由npm run setup在安装时替换为实际绝对路径。换机器/挪目录后重新运行一次即可。
目录结构
code-rag/
├── package.json # npm 脚本:setup / build / serve / index / search / status / smoke
├── tsconfig.json
├── src/
│ ├── main.ts # CLI 入口(serve | index | search | status)
│ ├── server.ts # MCP 服务器:五工具(异步任务 + HTTP 面板端点)+ stdio
│ ├── config.ts # 配置解析:默认值 < 配置文件 < 环境变量 < CLI
│ ├── taskfile.ts # 任务状态共享文件(index-task.json / panel.json)
│ ├── walk.ts # 文件扫描:ignore 规则、白名单、二进制/超大文件过滤
│ ├── chunker.ts # 声明感知分块(花括号深度 + 签名识别,150/20 兜底)
│ ├── embedder.ts # Ollama /api/embed 批量嵌入客户端(支持取消)
│ ├── qdrant.ts # Qdrant REST 客户端(集合/upsert/检索/删除,UUIDv5 确定性 ID)
│ ├── indexer.ts # 增量索引编排(进度回调 + 可取消 + 原子状态文件)
│ ├── searcher.ts # 语义检索 + 结果格式化
│ └── state.ts # 状态文件(<root>/.code-rag/state.json)读写
├── preset/ # Code RAG 模式预设模板(占位符,由 setup 生成到 ~/.dsh)
│ ├── agent.cordis.yml
│ ├── preset.yml
│ └── skills/code-rag/SKILL.md
├── plugins/index-panel.client.js # 索引面板动态版(cordis_define 用,Client 半部)
│ └── dsh-code-rag-index-panel/ # 索引面板常驻版(npm 插件:package.json + lib/;plugins/ 为市场 CI 认可的子包目录)
├── scripts/setup.mjs # ★ 一键接入:生成预设 + 同步 web profile(幂等,零依赖)
├── scripts/smoke.mjs # 端到端冒烟测试(真实 Qdrant/Ollama)
├── scripts/verify-async.mjs # 异步索引/进度/取消/HTTP 端点验证
├── testdata/sample-repo/ # 演示代码库(Java + C++,中英注释)
├── testdata/big-repo/ # 压力测试代码库(1200 个生成文件)
├── code-rag.config.example.json
└── cordis.patch.example.yml # 全局接入 DSH 的补丁片段(方案 B,占位符示例)
一、安装与构建
前置依赖(本地免费,需先就绪)
索引依赖两个本机免费组件,缺一不可(设置卡片「重新检测」会把未就绪项列入问题清单): Qdrant(向量库,端口 6333)与 Ollama(本地嵌入,端口 11434)。
Qdrant —— Docker 一条命令启动:
docker run -d \
--name qdrant \
--restart unless-stopped \
-p 6333:6333 \
-v qdrant_data:/qdrant/storage \
qdrant/qdrant
或用 Docker Compose(docker-compose.yml):
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
volumes:
- qdrant_storage:/qdrant/storage
volumes:
qdrant_storage:
Ollama —— 安装 Ollama 后拉取默认嵌入模型 (bge-m3:多语言 + 代码,1024 维,中文/英文查询均可靠):
ollama pull bge-m3
然后安装构建:
cd code-rag
npm install
npm run build # tsc → dist/
若想用回 nomic-embed-text(英文/标识符查询可用,中文语义查询效果差), 改配置
embedModel: nomic-embed-text+dimensions: 768,然后全量重建索引。
二、接入 DSH(两种方案)
方案 A:code-rag 模式(推荐,一键完成)
cd code-rag
npm install && npm run build
npm run setup # 生成预设 + 同步面板插件(幂等,可重复执行)
npm run setup(scripts/setup.mjs,零依赖)会根据自身实际位置自动完成:
- 生成模式预设
~/.dsh/.agent-presets/code-rag/(模板见preset/,其中的{{CODE_RAG_DIR}}/{{CODE_ROOT}}占位符被替换为绝对路径); - 把索引面板插件
dsh-code-rag-index-panel以file:依赖同步进~/.dsh/profiles/web/package.json(含 bundle 条目),并同步已安装副本; - 可选参数:
--root <代码库根目录>(服务器默认根,仅兜底)、--no-panel。
换机器 / 挪目录后只需重新运行
npm run setup,无需手工改任何路径。
然后:
- 重启 DSH,在模式选择器里选 Code RAG 模式,新建会话(保持其运行——索引服务器随之启动);
- 换工作区时先让模型调用
mcp__code_rag__set_root(root 传当前工作目录绝对路径), 再执行mcp__code_rag__index_codebase建立索引(persona/技能会引导模型自动完成); - 之后提问“哪里实现了 X / 找一下处理超时的代码”,模型自动调用
mcp__code_rag__search_code。
多根隔离:每个仓库的向量按 root 命名空间隔离(检索/删除/全量重建都只作用于 当前根),多个项目可共存于一个 Qdrant 集合;
full: true不会误删其他仓库。 集合未建立索引时工具返回友好提示(“请先调用 index_codebase”),不会 404 报错。
生成的 agent.cordis.yml 里的 mcp-code-rag 行(等价模板,占位符由 setup 替换):
- id: mcp-code-rag
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: code_rag # 工具名前缀
transport: stdio
command: node
args: ['<CODE_RAG_DIR>/dist/main.js', 'serve', '--root', '<CODE_ROOT>']
cwd: '<CODE_ROOT>' # = 默认代码库根目录
toolCallTimeoutMs: 300000 # 索引已异步化,此超时只影响 index_status/search_code 等轻量调用
failOnStartupError: false
方案 B:全局接入(所有会话可见)
把 cordis.patch.example.yml 里的 insert 片段合并进
<DSH_HOME>/profiles/web/cordis.patch.yml(其中的 <CODE_RAG_DIR> / <CODE_ROOT>
占位符换成你的实际绝对路径),重启 DSH。
三、索引命令
# 增量索引(默认代码库根目录 = 进程 cwd,可 --root 指定;多个仓库互不干扰)
node dist/main.js index --root <代码库根目录>
# 全量重建(只清空该 root 的向量后重新索引,不影响其他仓库)
node dist/main.js index --full --root <代码库根目录>
# 只统计不写入(演练)
node dist/main.js index --dry-run
# 查看状态
node dist/main.js status --root <代码库根目录>
增量原理:<root>/.code-rag/state.json 记录每个文件的 (mtime, size) 指纹。
未变化跳过;变化/新增 → 删除旧点 → 重嵌(确定性的 UUIDv5(root:file:startLine) 保证幂等覆盖);
磁盘上消失 → 按 payload.file + payload.root 精确清理。进程中断不损坏状态(临时文件 + 原子 rename)。
CLI 索引进度实时打印到 stderr 并写入 <root>/.code-rag/index-task.json(索引面板同源读取)。
四、异步索引(会话内)
index_codebase 默认异步:立即返回 taskId,后台执行——大项目不再超时。
进度经 index_status 轮询(模型会每 30-60 秒汇报一次),index_cancel 可取消;
会话输入框上方的索引面板显示同样的进度并提供按钮。
五、检索(混合检索)
search_code 采用混合检索:查询文本同时生成稠密语义向量(bge-m3)+ BM25
稀疏向量(标识符拆分/CJK bigram tokenizer),Qdrant Query API 以 RRF 融合两条
通道——自然语言语义("蓝牙重连逻辑")与精确标识符/错误串/数字(reconnectDevice、
6333、C3861)一次调用同时覆盖,精确命中分数通常为 1.000。
node dist/main.js search "命令行参数解析" --limit 5
node dist/main.js search "reconnectDevice" --dir src --limit 5
六、分块策略(chunker.ts)
“拟 AST”——不依赖 tree-sitter(无解析器的轻量启发式近似):
- 轻量词法扫描逐行统计花括号深度(忽略字符串/注释内的括号);
- 逐行维护作用域栈,识别声明签名(支持
public/private修饰符、template<>、 类/接口/struct/namespace、多行签名回溯 ≤4 行——回溯不越过已打开作用域, 排除if/for/while等控制流与 lambda); - 每个命名声明(函数/类)是一个独立块,其前导文档注释(Javadoc/Doxygen)自动 并入块内,保证“文件 + 函数名”级别的检索精度;
- 超大声明(> 150 行)与无结构区域(文件头、命名空间外代码)内部用滑动窗口 150 行 / 重叠 20 行——即需求里的兜底策略;
- 纯结构碎块(孤立的右括号等)自动丢弃,避免污染检索;
- 每个块携带
file / function / class / start_line / end_line,嵌入文本带定位头 (// src/foo.cpp :: bar (lines 12-34)),提升检索精度。
分块回归测试:npm test(node:test,12 个用例固化历史边界——行尾注释回溯、
} else { 撕裂、多行签名、窗口切分、稀疏 tokenizer 等)。
七、配置
优先级:内置默认 < <root>/.code-rag/config.json < 环境变量 CODE_RAG_* < CLI。
把 code-rag.config.example.json 复制到 <root>/.code-rag/config.json 后修改。
常用项:qdrantUrl、ollamaUrl、embedModel、dimensions、
chunkSize/chunkOverlap、ignore(目录忽略)、extensions(语言白名单)、
httpPort(索引面板 HTTP 端点端口,默认 8756,占用自动上探;也可用环境变量
CODE_RAG_HTTP_PORT 覆盖。注意索引面板探测 8756-8786,改到区间外时需同步修改
面板插件 lib/client.js 里的 PORT_BASE)、
embedConcurrency/embedRetries(嵌入并发与失败重试,默认 4/3)。
(root 不在此配置:由启动参数 --root / 当前工作目录决定,多仓库用 set_root 切换。)
安全:面板 HTTP 端点只接受本机来源(127.0.0.1/localhost 的 Origin),
其他网页来源一律 403。
换嵌入模型:改 embedModel 与 dimensions 后 index(增量即可)会自动
检测到状态指纹与模型不匹配并全量重建。默认 bge-m3 = 1024 维;
nomic-embed-text = 768 维。bge-m3 对中英混合注释的检索质量显著更好。
八、冒烟测试
npm run build && npm run smoke
依次验证:CLI 全量索引 → 中文查询「命令行参数解析」命中 App.parseArgs →
中文查询「指数退避重试算法」命中 engine.retryWithBackoff →
MCP 握手(initialize / tools/list 四工具 / search_code / set_root / 空根 index_status 友好输出)。
九、工作原理速览
会话提问 ──> DSH 模型 ──> mcp__code_rag__search_code(MCP stdio)
│
code-rag 服务器(node dist/main.js serve)
├─ 查询嵌入:Ollama /api/embed(bge-m3, 1024 维)
└─ 相似检索:Qdrant REST /points/search(Cosine)
│
返回 file + function + 行号 + 片段
索引路径:walk 扫描 → chunker 分块 → embedder 批量嵌入 → qdrant upsert(UUIDv5 幂等)→ state.json 指纹回写。删除路径:状态里有、磁盘上没有 → deleteByFile(payload.file 过滤)。
十、常见问题
| 现象 | 处理 |
|---|---|
search_code 提示“尚未建立索引” | 调用 index_codebase(可加 root 指定仓库)后重试 |
| 点“开始索引”没反应/卡住 | 旧版服务器残留会占端口:重启 DSH 让新服务器(stdin 关闭即退出)接管;面板已选最新实例 + 8s 超时兜底 |
| 索引显示“0 个文件” | 服务器已加 0 文件防护:root 配错或 ignore 把源码目录排除了会明确报错;点击面板根名可切换 |
| 大项目索引超时 | 已解决:index_codebase 默认异步立即返回,后台执行;用 index_status 看进度 |
| 看不到索引进度 | index_status(模型会汇报);或启用索引面板(输入框上方状态条 + 按钮) |
| 换工作区后检索的还是旧项目 | 先 set_root 切到当前工作目录,再 index_codebase |
| 检索结果混入别的项目 | 多根已按 root 隔离;确认 index_status 的当前根是否正确 |
| 索引任务卡住想停 | index_cancel 或面板的“取消”按钮 |
| Ollama 找不到模型 | ollama pull bge-m3(默认)或 ollama pull nomic-embed-text |
| 向量维度不匹配报错 | 模型换过 → 同步改 dimensions + index --full |
| 中文检索不准 | 确认默认模型为 bge-m3(1024 维);nomic-embed-text 中文语义弱 |
| 端口不通 | docker ps 看 Qdrant;curl localhost:11434/api/tags 看 Ollama |
| MCP 工具没出现 | 检查 DSH 日志中 mcp-client(code_rag) 行;确认 dist/main.js 路径存在 |
| 大仓库索引慢 | 先看是不是把生成/构建目录扫进去了:node dist/main.js index --dry-run --root <仓库> 看 scanned 数。VS C++ 项目默认已忽略 Temperat.*(MSBuild 中间目录)、x64、installer;其他项目的生成目录加到配置 ignore。真慢时调大 embedBatchSize / embedConcurrency(默认 32/4) |
| 生成文件进了索引 | 在 <root>/.code-rag/config.json 的 ignore 里加目录模式(支持 *),然后 index --full |