主题
09 - 基础概念解析:从 0 到 1 看懂前 8 章
这一章面向完全没接触过代码索引 / LLM Agent / 知识图谱的读者。每个概念会用两段式讲解:
- 比喻/生活实例:先在脑中建立直觉
- 技术解释:再补充准确定义
看完这一章后,你重新读 01–08 章会顺畅很多。
0. 全局比喻:把"AI 编程助手"看作一个新来的实习生
整本调研讨论的事情其实可以用一个非常具体的场景描述:
想象你公司新来了一个很会写代码的实习生(这就是 Claude Code / Codex / Cursor 这种 AI 编程助手)。
他每天接到任务的工作流程是:
- 不熟悉项目,先翻 README
- 用
grep找一下相关的关键字- 打开几个文件读一读
- 然后开始写代码
这个流程在小项目上还行。但如果是一个 几百万行代码的大项目:
- 实习生光是"先翻一遍 README + grep + 读文件" 就要花掉半天
- 因为不熟悉调用关系,改一行代码可能导致另外 5 个文件出 bug
- 每次新任务都要重新走一遍这个流程
整个调研讨论的问题就是:
如何不让这个实习生每次都从零开始?
各种方案就像给他配不同的辅助工具:
| 方案 | 类比 |
|---|---|
| 知识图谱派 | 给他一份项目结构图("A 函数调用 B 函数,B 在 X 文件里") |
| 向量 RAG 派 | 给他一个项目专属搜索引擎("按意思找代码") |
| Grep 加速派 | 把他手里的老式手电筒换成激光雷达(grep 变快 36 倍) |
| LSP 派 | 直接把 IDE 的能力借给他(鼠标右键就能跳定义) |
| AST 结构派 | 给他一套代码规则巡查表(自动找出所有不规范的代码) |
下面开始详细讲每个概念。
1. Token:大模型的"计费单位"
生活类比
想象寄快递按"重量"算钱。Token 就相当于"快递的克重"。
你给 AI 发一段话 = 寄一个包裹。AI 看这段话的"克重"(token 数)来收费。
技术解释
Token 是大语言模型把文字切成的"最小理解单位"。例如:
"我爱编程" → ["我", "爱", "编", "程"] → 4 个 token
"Hello World" → ["Hello", " World"] → 2 个 token不同模型切法略有不同。一般 1 个中文字 ≈ 1–2 token,1 个英文单词 ≈ 1 token。
为什么 token 这么重要
LLM 是按 token 计费的。调用 Claude Sonnet 4 大约每 100 万 token 收 3 美元。
举个具体例子:
你让 AI 读完整个 fastapi 仓库(44,751 token)然后回答一个问题
↓
单次成本约 = 44,751 / 1,000,000 × 3 美元 ≈ 0.13 美元
一天问 50 次 = 6.5 美元
一个团队 10 人 = 65 美元/天 ≈ 2 万美元/年这就是为什么调研里所有项目都在讲"省 token":
- code-review-graph 省 8.2 倍 → 0.13 美元变 0.016 美元
- Semble 省 98% → 0.13 美元变 0.0026 美元
省的全是真金白银。
2. MCP(Model Context Protocol):AI 工具的"USB 标准"
生活类比
想象 USB 出现前,每个鼠标、键盘、打印机都有自己的接口(PS/2 圆口、串口、并口、各种奇怪的小针)。买一台新打印机就要重新装一套驱动。
USB 出现后,所有外设都用同一种插口,电脑通过统一协议识别它们。
MCP 就是 AI 工具世界的 USB。
技术解释
MCP (Model Context Protocol) 是 Anthropic 在 2024 年推出的开放协议,让 AI 助手(Claude / Codex / Cursor 等)能用同一套接口接入任意"外挂工具"。
AI 助手(Claude Code)
│
│ MCP 协议(统一插口)
│
┌────────┴────────────────────┐
▼ ▼ ▼ ▼
知识图谱 搜索引擎 数据库 浏览器
(MCP) (MCP) (MCP) (MCP)每个工具(叫 MCP Server)暴露一些"方法",比如:
gitnexus 暴露:
- query("关键词") → 搜代码
- context("函数名") → 看函数关系
- impact("函数名") → 看爆炸半径
- rename("旧名", "新名") → 跨文件重命名AI 助手通过 MCP 协议自动发现并调用这些方法。
为什么 MCP 重要
在 MCP 出现前,要让 Claude 接入 GitHub,需要专门为 Claude 写一个 GitHub 插件;让 ChatGPT 接入要再写一个;Cursor 也要再写一个。MCP 让一份代码(MCP Server)能被所有支持 MCP 的 AI 同时用。
调研中的所有项目都是做成 MCP Server:
| 项目 | 暴露的 MCP 工具数 |
|---|---|
| code-review-graph | 28 + 5 prompt 模板 |
| GitNexus | 7 工具 + 多个资源 |
| code-graph-rag-mcp | 26 方法 |
| Semble | 少(专注 search) |
| Serena | 38+ |
安装一个 MCP Server 长什么样
bash
# 给 Claude Code 加一个 MCP Server
claude mcp add semble -s user -- uvx --from "semble[mcp]" semble
# 给 Codex 加同一个
codex mcp add semble -- uvx --from "semble[mcp]" semble一行命令搞定,AI 启动后就能调用这个工具。
3. AST(抽象语法树):把代码"翻译"成树结构
生活类比
拿到一句话:"小明吃了一个红苹果"。
在小学语文课上,老师会教你画句子成分分析图:
[整句] / \ [主语] [谓语部分] 小明 / \ [动词] [宾语] 吃了 / \ [定语] [中心词] 红的 苹果这棵树就是这句话的 "AST"。它把人类的自然语言变成机器能精确处理的结构。
技术解释
AST (Abstract Syntax Tree, 抽象语法树) 是把代码解析后得到的树形结构。
例如这段 Python 代码:
python
def validate_user(name, password):
if password == "admin":
return True
return False它的 AST 大致长这样:
FunctionDef (validate_user)
├── Args
│ ├── name
│ └── password
└── Body
├── If
│ ├── Condition: password == "admin"
│ └── Body: Return(True)
└── Return(False)为什么调研里 90% 的项目都用 AST
grep 把代码当字符串搜索:
- 想找"所有函数定义" → 写正则
def \w+\(,但只能找到def形式的,匹配不到 lambda、装饰器函数 - 想找"所有 if 里嵌套 if" → 几乎做不到
AST 把代码当结构搜索:
- "所有函数定义" = 找树里所有
FunctionDef节点 - "if 嵌套 if" = 找
If节点下还有If节点的
调研中:
- code-review-graph、GitNexus、code-graph-rag-mcp:解析 AST → 抽出节点和边 → 建图
- Semble:解析 AST → 按函数/类粒度切代码 chunk → 做嵌入
- ast-grep:直接用 AST 模式匹配代码
4. Tree-sitter:业界最常用的"AST 解析器"
生活类比
如果 AST 是"句子成分分析图",Tree-sitter 就是那位画分析图的语文老师——而且这位老师同时会 50 多种语言(Python、Java、Rust……)。
技术解释
Tree-sitter 是 GitHub 在 2018 年开源的一款通用 AST 解析器:
- 极快:用 C 实现,毫秒级解析千行代码
- 支持几十种语言:每种语言只需写一份 grammar 文件
- 增量解析:改一行只重新解析这一行附近,不重做整个文件
- 容错:代码有语法错误也能解析出"尽量正确的树"(IDE 里语法报错时不影响补全就是靠它)
调研里几乎每个图谱派项目都用 Tree-sitter
| 项目 | Tree-sitter 用途 |
|---|---|
| code-review-graph | 23 种语言的解析全靠它 |
| GitNexus | 11 种语言解析 |
| code-graph-rag-mcp | 多 Agent 共享一个 Tree-sitter parser |
| Semble | 按 AST 边界切代码 chunk |
| Probe | AST 感知搜索 |
它已经是事实标准。
5. LSP(Language Server Protocol):复用 IDE 的智能
生活类比
每个语言(中文 / 英文 / 日文)都有一个专门的"翻译官",懂这个语言的所有细节。
你的 VS Code 编辑器不直接懂代码,它通过和翻译官对话来获得智能:
- VS Code 问翻译官:"
validate_user定义在哪?"- 翻译官(Python 的 Pyright)回答:"在
auth.py第 42 行"- VS Code 在屏幕上画一个跳转链接
这个"问答协议"就是 LSP。每种语言的"翻译官"叫一个 Language Server。
技术解释
LSP (Language Server Protocol) 是微软为 VS Code 设计、后来成为业界标准的协议。
你的编辑器 Language Server
(VS Code / Cursor) (按语言不同:
Python → Pyright
│ LSP TypeScript → tsserver
│ ←─→ Rust → rust-analyzer
│ Java → jdtls
……)
编辑器只管 UI,
所有"懂代码"的事
交给 Language ServerLSP 提供的能力:
- 跳到定义(Go to Definition)
- 查找所有引用(Find References)
- 自动补全(Completion)
- 类型推断与悬浮提示(Hover)
- 错误诊断(Diagnostics)
- 跨文件重命名(Rename)
- 调用层次(Call Hierarchy)
Serena 的核心创新就是"借用 LSP"
调研中的其他项目都在自己重新发明轮子:
- code-review-graph:自己用 Tree-sitter 解析每种语言
- GitNexus:同样
Serena 的思路不一样:
既然每种语言的 Language Server 已经存在十几年了(VS Code/Vim/Emacs 都用),而且做得比我自己写的好得多——那我直接用 LSP 不就完了?
这让 Serena 一次性支持 52 种语言,且每种语言的 rename / find references 质量与 VS Code 一致。
6. 向量嵌入(Embedding):把"意思"变成数字
生活类比
想象给每个词分配一个 GPS 坐标:
苹果 → (39.9, 116.4) ← 跟"水果"和"手机"都近 水果 → (40.0, 116.5) ← 跟"苹果"很近 手机 → (39.8, 116.3) ← 跟"苹果"也近(科技语境) 汽车 → (22.5, 113.9) ← 离"苹果"远意思接近的词,坐标也接近。 这种"坐标"就是向量嵌入。
技术解释
Embedding(向量嵌入) 是把一段文字(单词、句子、代码块)映射到一个高维向量(通常是 384、768、1536 维的浮点数数组)。
python
embed("用户登录") → [0.12, -0.45, 0.78, ..., 0.33] (1536 维)
embed("authenticate") → [0.11, -0.43, 0.80, ..., 0.31] ← 跟"用户登录"很接近
embed("calculate price") → [-0.55, 0.22, ..., 0.18] ← 离"用户登录"远衡量"离得近不近"用余弦相似度:
similarity(A, B) = (A · B) / (|A| × |B|)值在 -1 到 1 之间,越接近 1 越像。
为什么这能"按意思搜代码"
用户搜: "用户认证逻辑"
↓ embed
向量 V_query
代码库每个函数都已经嵌入过:
validateUser: V_1 ← 与 V_query 余弦相似度 0.89
loginHandler: V_2 ← 与 V_query 余弦相似度 0.85
calculatePrice: V_3 ← 与 V_query 余弦相似度 0.12
返回相似度最高的 K 个 → 命中正确答案即使函数名叫 verifySignature(和"用户认证"字面上没关系),向量空间里它们仍然相近——这就是语义搜索的魔力。
调研里的嵌入模型
| 项目 | 默认嵌入 | 备注 |
|---|---|---|
| Semble | Model2Vec(potion-code-16M) | CPU 静态查表,超快 |
| code-review-graph | all-MiniLM-L6-v2 | 通用小模型 |
| Claude Context | OpenAI / VoyageAI / Gemini / Ollama | 云 API 为主 |
| GitNexus | transformers.js | 浏览器内跑 |
7. Model2Vec / 静态嵌入:让 CPU 也能跑得飞快
生活类比
传统嵌入 = 每次找一个词的 GPS 都重新算一遍卫星定位(要 GPU、要几秒)
静态嵌入 = 预先做好一份"全国地址电话本",每次查只是翻字典(CPU 毫秒级)
技术解释
传统嵌入模型(如 BERT、CodeBERT):
- 是个 Transformer 神经网络
- 每生成一条嵌入要跑一次完整推理
- CPU 上几百毫秒到几秒一条
Model2Vec / 静态嵌入:
- 离线训练一次:用 Transformer 算所有 token 的嵌入
- 蒸馏成一张大查找表(token → 向量)
- 推理时只做"查表 + 平均池化",CPU 上每秒上万条
精度损失:
- 传统 BERT:NDCG@10 = 0.862
- Semble (Model2Vec):NDCG@10 = 0.854
- 仅差 0.008,但速度快 10–200 倍
这是 Semble 能在普通笔记本上 250ms 索引整个项目的核心原因。
8. BM25:经典的"按关键词找文章"
生活类比
BM25 就像图书馆的目录卡:你说"我要找包含'爆炸半径'这个词的书",图书管理员去翻目录,按"这本书出现这个词多少次 + 这本书有多厚"算个评分,把最相关的几本给你。
它不懂"爆炸半径"和"影响分析"的语义关系,但找精确字符串特别快。
技术解释
BM25 (Best Matching 25) 是 1994 年提出的经典词法检索算法,至今仍是搜索引擎主力。核心思想:
score(doc, query) = Σ over terms in query:
IDF(term) × (term_freq × (k+1)) / (term_freq + k × (1 - b + b × |doc|/avgdoc))简单来说:
- 文档里出现查询词次数越多 → 分数越高
- 但同时文档不能太长(长文档天然命中多)
- 查询词在整个语料库越罕见 → 分数加成越多
调研里 BM25 的角色
几乎所有混合检索方案都把 BM25 当"词法路":
| 项目 | 双路 |
|---|---|
| Semble | 语义(Model2Vec)+ 词法(BM25) |
| Claude Context | 语义(向量)+ 词法(BM25) |
| GitNexus | 语义(嵌入)+ 词法(BM25) |
| code-memory | 语义(密集向量)+ 词法(BM25) |
为什么不能只用语义检索?
因为语义检索找不到"精确名字":
- 用户搜
MAX_RETRY_COUNT→ BM25 一发就中;向量绕远 - 用户搜
validateUser→ BM25 直接命中;向量可能输给"做认证的其他函数"
两路结合才能既"找意图"又"找名字"。
9. RRF(Reciprocal Rank Fusion):怎么合并两个搜索结果?
生活类比
你同时让两个朋友推荐餐厅:
- 朋友 A 列出前 5 名:[海底捞、外婆家、必胜客、汉堡王、麦当劳]
- 朋友 B 列出前 5 名:[海底捞、肯德基、外婆家、星巴克、必胜客]
你怎么决定最终去哪?最简单的方法:
海底捞:A 排第 1,B 排第 1 → 综合分 (1/1 + 1/1) = 2.0 外婆家:A 排第 2,B 排第 3 → (1/2 + 1/3) = 0.83 必胜客:A 排第 3,B 排第 5 → (1/3 + 1/5) = 0.53 肯德基:A 没排上,B 排第 2 → (0 + 1/2) = 0.50 麦当劳:A 排第 5 → (1/5 + 0) = 0.20最终结果:海底捞 > 外婆家 > 必胜客 > 肯德基 > ……
这就是 RRF 的核心:每个排名贡献 1/(k+rank) 分,加起来排序。
技术解释
RRF (Reciprocal Rank Fusion) 公式:
RRF_score(doc) = Σ over runs (1 / (k + rank_in_run(doc)))其中 k 通常取 60(一个经验值,平滑用)。
为什么 RRF 比"加权平均"好
直接对两路分数加权平均的问题:
- 向量分数范围 0–1
- BM25 分数范围 0–几十
- 量纲不同,加权很难调
RRF 只看排名,不看分数,天然解决了"量纲对不齐"的问题。
调研里 Semble、Claude Context、code-graph-rag-mcp 都用了 RRF 思路。
10. Trigram 倒排索引:flashgrep 加速 36 倍的秘密
生活类比
想象你在新华字典里找"葡萄"这个词。
笨办法:从第一页翻到最后一页,看每一页有没有"葡萄"。100 万字的字典要翻好几小时。
聪明办法:字典背面有索引表——
"葡" → 第 128, 567, 891 页 "萄" → 第 128, 234, 891 页"葡萄"的页 = "葡"的页 ∩ "萄"的页 = {128, 891}。直接翻这两页就好。
技术解释
Trigram(三字组) = 文件里所有连续 3 字节的窗口:
"fetchRates" 的 trigram = {fet, etc, tch, chR, hRa, Rat, ate, tes}倒排索引 = 反过来:trigram → 包含它的文件列表
"fet" → [file_12, file_47, file_88, file_92, ...]
"etc" → [file_12, file_23, file_47, file_88, ...]
"tch" → [file_12, file_47, file_88, file_135, ...]搜索 fetchRates = 把每个 trigram 的文件列表取交集 → 候选文件(少则几个,多则几十个)→ 在候选文件里跑精确匹配。
为什么提速 36 倍
普通 grep 在 Chromium(4GB 代码)上要顺序扫所有文件 = 137 秒。
flashgrep 用 trigram 索引:
- 候选文件可能只剩几十个
- 在这几十个文件里跑精确匹配 = 7.82 秒
用空间换时间:索引占 2.5GB,但单次查询时间从分钟级降到秒级。
为什么是 trigram,不是 bigram 或 4-gram
| 选择 | 问题 |
|---|---|
| Unigram(1 字节) | posting list 太长(几乎每个文件都有每个字节) |
| Bigram(2 字节) | 区分度不够 |
| Trigram(3 字节) | 平衡点:区分度好 + 索引体积可控 |
| 4-gram+ | posting list 太短易丢命中 + 索引膨胀 |
这套方法是 Google Code Search、Zoekt、csearch 等业界产品的通用方案。
11. 知识图谱:把代码变成"人际关系网"
生活类比
把一个公司的关系画成网:
[张总] / \ 汇报 汇报 / \ [李经理] [王经理] / \ \ 汇报 汇报 汇报 / \ \ [小明] [小红] [小刚] | 合作 | [外包]节点 = 人,边 = 关系(汇报、合作、协同)。
想知道"如果张总辞职会影响谁?"——从张总出发 BFS 遍历整张图,结果一目了然。
技术解释
知识图谱(Knowledge Graph) 是一种 节点 + 边 的数据结构。
对代码而言:
| 节点(Node) | 边(Edge) |
|---|---|
| 函数 | 调用关系(A 调用 B) |
| 类 | 继承关系(A extends B) |
| 文件 | 引用关系(A imports B) |
| 测试 | 覆盖关系(test_X 覆盖 X) |
| 变量 | 引用关系 |
图数据库 = 专为图设计的存储
调研里:
- code-review-graph:用 SQLite 模拟图存储
- GitNexus:用 KuzuDB(原生图数据库,支持 Cypher 语言)
- code-graph-rag-mcp:用 better-sqlite3 + sqlite-vec
Cypher 是图数据库的"SQL",例如:
cypher
// 找出所有调用 validateUser 的函数
MATCH (caller:Function)-[:CALLS]->(target:Function {name: "validateUser"})
RETURN caller.name, caller.file比用 SQL 写多次 JOIN 简洁得多。
12. 爆炸半径(Blast Radius):改一个函数会炸到哪?
生活类比
想象你要换办公室门口的咖啡机。
- 直接影响(半径 1):每天用这台机器的所有人(约 50 人)
- 间接影响(半径 2):用这些人冲的咖啡的人(来访客户、隔壁部门)
- 三度影响(半径 3):依赖这些人工作产出的下游团队
半径越小,改动越安全;半径越大,越要小心。
技术解释
代码里的爆炸半径分析:
你要改 calculatePrice 函数
↓ 在图上找它
图节点:calculatePrice
↓ BFS 上游(谁调用它)
深度 1:checkoutHandler, invoiceGenerator ← 直接会崩溃
深度 2:reportService, taxCalculator ← 可能受影响
深度 3:dashboardService, exportApi ← 传递性影响
↓ 同时找测试覆盖
覆盖测试:test_checkout, test_invoice
↓ 汇总
"改这个函数有 8 个文件可能受影响,但只有 2 个测试覆盖"风险评分矩阵(GitNexus 用的):
| 影响范围 | 风险 |
|---|---|
| <5 个符号,少量执行流 | LOW |
| 5–15 个符号,2–5 个执行流 | MEDIUM |
| >15 个符号或大量执行流 | HIGH |
| 关键路径(认证、支付) | CRITICAL |
code-review-graph 和 GitNexus 都把这个能力作为核心卖点——传统 grep 完全做不到。
13. 社区检测(Community Detection):自动发现"功能模块"
生活类比
看一张 100 人的关系图:
张三 ── 李四 ── 王五 (这三个常聚餐 = 朋友群 A) │ │ │ │ 赵六 ── 钱七 ── 孙八 (这三个常打球 = 朋友群 B) │ │(弱连接) 周九 ── 吴十 (这两个是夫妻 = 家庭群 C)算法自动把"内部紧密连接"的人圈一起 → 三个社群。
技术解释
社区检测算法(如 Leiden、Louvain) 在图上自动找"内部连接稠密、外部连接稀疏"的子图。
应用到代码:
项目 ── 自动检测 ──→
社区 A:所有认证相关函数
社区 B:所有支付相关函数
社区 C:所有数据库 ORM 函数
……为什么这很有用:
- 帮 AI 理解项目的自然边界(即使你没明确分文件夹)
- 重构时,"把这个函数从社区 A 搬到社区 B"是个明确信号
- 文档自动生成(每个社区一个章节)
- code-review-graph 的
generate_wiki_tool就是基于社区结构
中心度(Centrality)和"桥"节点
继续上面的关系图比喻:
- Hub 节点(中心度高)= 在派对里认识所有人的"社交达人"。代码里就是"被调用次数最多的函数",往往是架构热点
- Bridge 节点(介数中心性高)= 连接两个不同朋友群的"中间人"。代码里就是"两个模块间的桥梁函数",改它风险最高
code-review-graph 的 get_hub_nodes_tool 和 get_bridge_nodes_tool 就是干这个的。
14. RAG(检索增强生成):让 AI 现学现卖
生活类比
闭卷考试:你只能用脑子里记得的知识。
开卷考试:你可以查教科书,先查到相关章节,再回答问题。
RAG = 给 AI 开卷考试。
流程:
- AI 收到问题
- 系统先从你的资料库里找出几段最相关的内容
- 把这几段塞进 AI 的提示词
- AI 基于这些内容回答
技术解释
RAG (Retrieval-Augmented Generation, 检索增强生成):
用户问:"这个项目的认证逻辑怎么走?"
↓
┌───────────────┐
│ Retrieval │ ← 从代码库检索相关 chunk
│ (BM25/向量/ │
│ 图查询) │
└───────┬───────┘
↓
Top-K 相关代码片段
↓
┌───────────────┐
│ Augmented │ ← 把这些片段拼到提示词
│ Prompt │
└───────┬───────┘
↓
┌───────────────┐
│ Generation │ ← LLM 基于片段回答
│ (LLM) │
└───────┬───────┘
↓
最终答案调研里所有"语义检索 + 喂给 LLM"的项目都是 RAG
- Semble = 代码 RAG(极简版)
- Claude Context = 企业级代码 RAG(Milvus 后端)
- code-graph-rag-mcp = 图 + RAG 混合
RAG 的核心优势:LLM 不需要把整个仓库塞进上下文,只需要相关的几段就够了 → 省 token、降延迟、不爆 context window。
15. Chunking:怎么把长代码切成"喂得下"的小块
生活类比
写读书笔记:你不会把一本 500 页的书一句话总结。会按章节切,每章一段笔记。
切得太碎(每页一段)→ 上下文丢失 切得太粗(整本一段)→ 不利于检索
Chunking 就是找平衡点。
技术解释
Chunking(分块) 是 RAG 的关键预处理步骤。把代码切成 chunk 的策略:
| 策略 | 说明 | 优劣 |
|---|---|---|
| 按行数固定切 | 每 200 行一段 | 简单,但可能在函数中间切断 |
| 按字符数切 | 每 1500 字符一段 | 同上 |
| AST 感知切(推荐) | 沿函数 / 类边界切 | 保留语义单元完整性 |
| Overlap(重叠) | 相邻 chunk 有 100 行重叠 | 避免"刚好切在关键点" |
调研里:
- Semble:AST 感知(按函数)
- Claude Context:默认 2500 行 chunk + 300 行 overlap
- code-graph-rag-mcp:AST 感知(按节点)
16. Merkle DAG:Claude Context 的"智能增量"
生活类比
快递公司每天清点仓库。
笨办法:逐个货架数所有箱子,10 万箱要数一整天。
聪明办法:
- 每个货架贴一个总签收码(汇总该货架所有箱子的编号)
- 每个区域贴一个区域签收码(汇总该区域所有货架)
- 整个仓库贴一个顶层签收码
清点:只对比顶层签收码与昨天的。
- 一样 → 整个仓库没动,结束
- 不一样 → 看哪个区域签收码变了 → 再看哪个货架 → 最后定位到具体箱子
几秒钟就能完成全仓盘点。
技术解释
Merkle DAG (有向无环图) 把目录树每一级都做 hash:
顶层 hash = hash(目录 A 的 hash + 目录 B 的 hash + ...)
目录 A 的 hash = hash(文件 a1 的 hash + 文件 a2 的 hash + ...)
文件 a1 的 hash = hash(文件内容)任何一个文件改了,所有它的祖先节点 hash 都会变。
增量同步只需:
比对:当前顶层 hash vs 上次顶层 hash
↓ 不同
比对每个子目录 hash
↓ 锁定变化的子目录
逐级下钻
↓
找到所有变更的文件复杂度从 O(N)(逐文件比)降到 O(log N)。
Claude Context 把它存在 ~/.context/merkle/ 里,所以索引几十万文件的项目也能秒级判断"哪些文件变了"。
这与简单的 git status 比对的优势是:
- 即使不在 git 仓库也能用
- 跨多个目录联合也能用
- 对
.gitignore不感知的场景也能用
17. mmap:让磁盘文件"伪装成"内存
生活类比
图书馆借书 vs 自己买书:
借书:每次要看就跑去图书馆找(等同于每次 read 文件),耗时。
买书:摆在自己书架上(等同于读进内存),但占空间。
mmap = 图书馆开了个"租用书架"在你家:书还在图书馆(磁盘),但你查阅时操作系统假装它就在你的书架上(虚拟内存)。
技术解释
mmap (Memory-Mapped File) 是操作系统提供的能力,让程序"假装"一个磁盘文件就在内存里访问:
传统 IO:
程序 → read() → 内核拷贝磁盘数据 → 程序缓冲区
(每次 IO 都要拷贝)
mmap:
程序 → 直接访问内存地址 X
↓
内核透明地把 X 对应的磁盘页加载进 PageCache
↓
返回数据优势:
- 启动 0 拷贝(指针直接指向磁盘)
- 多进程共享同一份缓存(操作系统级 PageCache)
- 只加载实际访问的页(懒加载)
flashgrep 用 mmap 的好处
flashgrep 的 trigram 索引有 2.5GB(Chromium 场景):
- 不 mmap:每次启动加载 2.5GB → 几秒
- mmap:启动 0 延迟,访问到哪页才加载哪页
- 多个 flashgrep 进程并行查询时,共享同一份 PageCache,互不重复加载
18. Hooks:在 AI 工具调用前后"插一脚"
生活类比
门铃 = 普通调用:客人按门铃 → 你开门,结束。
门铃 + 提前接待 + 事后送客 = Hooks:
- 客人按门铃前,你的"前台"先扫一眼监控(PreToolUse)
- 客人离开门铃后,"前台"做最后的安全检查(PostToolUse)
技术解释
Claude Code 的 Hooks 系统允许在 AI 调用工具的前后自动执行自定义脚本:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Grep|Glob|Bash",
"hooks": [
{
"type": "command",
"command": "node /path/to/gitnexus-hook.cjs",
"timeout": 10
}
]
}
]
}
}- PreToolUse:AI 调用
Grep/Glob/Bash之前,先跑gitnexus-hook.cjs - PostToolUse:AI 调用完 之后,再跑一次
GitNexus 用 Hooks 干什么
AI: "我要 grep 一下 calculatePrice"
↓ 触发 PreToolUse
GitNexus hook:
- 注意到 AI 在搜索代码
- 从图谱里取出 calculatePrice 的相关上下文
- 把这些信息**注入**到 Grep 工具的结果里
↓
AI 收到的是: grep 结果 + 图谱补充的"这个函数的 callers/callees/测试覆盖"
↓
AI 直接拿到更丰富的上下文,省去再次 MCP 调用这是 GitNexus 与 code-review-graph 的关键差异:前者用 Hooks 自动增强 AI 工具,后者主要靠 AI 主动调 MCP。
19. Skills:Claude Code 的"工作流模板"
生活类比
新同事入职拿到的"操作手册":
- 接客户电话怎么做(按 SOP 一步步)
- PR 审查怎么做
- 部署上线怎么做
Skills 就是给 AI 装的一套 SOP,每个 Skill 是一个具体场景的工作流模板。
技术解释
Claude Code 的 Skills 是放在 ~/.claude/skills/ 目录的 markdown 文件,每个 Skill 描述:
- 触发场景("调试 bug"、"审查 PR")
- 推荐工作流(先调哪个 MCP 工具,再调哪个)
- 输出格式
例如 GitNexus 安装的 7 个 Skill 之一 gitnexus-pr-review:
markdown
触发: "审查这个 PR" / "PR #42 改了什么?"
工作流:
1. gh pr diff <number> → 获取 diff
2. gitnexus_detect_changes → 映射到流
3. 对每个变更符号: gitnexus_impact → 爆炸半径
4. 汇总发现并评估风险
输出模板:
## PR Review: <标题>
**风险: LOW / MEDIUM / HIGH / CRITICAL**
...Claude Code 看到用户说"审查这个 PR"就自动加载这个 Skill 走流程。
20. NDCG@10:怎么衡量"搜索结果好不好"
生活类比
百度搜"附近的火锅店",给你 10 个结果。
- 第 1 名是真好的店 → 加分多
- 第 10 名是真好的店 → 加分少(用户根本不会翻到)
- 完全无关的店排在前面 → 减分多
NDCG = "搜索结果的综合考分",分数越高越好。
技术解释
NDCG@10 (Normalized Discounted Cumulative Gain at 10) 是信息检索领域的标准指标:
- 看前 10 个结果
- 第 K 个位置的"贡献" = (相关度) / log(K+1)
- 把这 10 个加起来得到 DCG
- 再除以"理想排序下的 DCG"(IDCG)得到 NDCG,范围 0–1
典型值参考:
| NDCG@10 | 含义 |
|---|---|
| 0.95+ | 接近完美 |
| 0.85–0.95 | 商业级搜索引擎水平(Semble: 0.854) |
| 0.70–0.85 | 良好 |
| <0.70 | 还不够好 |
调研里:
- Semble: NDCG@10 = 0.854 ← 跟专业 Transformer 模型相当
- 专业 Transformer: NDCG@10 = 0.862
仅差 0.008,但 Semble 速度快 10–200 倍。
21. F1 / Precision / Recall:影响分析的准确性指标
生活类比
机场安检:找出所有真正带违禁品的人。
- Recall (召回率):所有真正带违禁品的 100 人里,安检抓到了几个?(抓到 100 个 = 100% 召回)
- Precision (精确率):所有被安检拦下的 200 人里,真正带违禁品的有几个?(50 个 = 25% 精确率)
- F1:召回率和精确率的"折中分数"
技术解释
真实情况
是 否
预测 是 [TP] [FP]
否 [FN] [TN]- Precision = TP / (TP + FP):预测为"是"的里面,真正"是"的比例
- Recall = TP / (TP + FN):真正"是"的里面,被预测为"是"的比例
- F1 = 2 × Precision × Recall / (Precision + Recall)
code-review-graph 的影响分析为什么"故意保守"
官方 benchmark:
- Recall = 100%(不会漏报)
- Precision = 0.38(多报很多)
- F1 = 0.54
意思是:它会报很多"可能受影响"的文件(一部分其实不会受影响),但不会漏掉任何真正受影响的文件。
设计哲学:改代码时宁多报勿漏报。多检查几个文件无所谓,漏掉一个调用方导致线上 bug 才严重。
22. 调用图 vs. 依赖图 vs. 模块图:别搞混
很多调研项目里都提到"图",但其实不止一种:
| 图的类型 | 节点 | 边 | 用途 |
|---|---|---|---|
| 调用图(Call Graph) | 函数 | A 调用 B | "改 A 影响谁" |
| 依赖图(Dependency Graph) | 模块 / 包 | A 依赖 B | "升级这个包会影响哪些项目" |
| 继承图(Inheritance Graph) | 类 | A 继承 B | "这个父类有哪些子类" |
| 数据流图(Data Flow Graph) | 变量 | A 的值流向 B | "敏感数据有没有泄露" |
| 执行流(Execution Flow) | 入口函数 | A 经过 B 最后到 C | "登录请求走过哪些函数" |
调研里:
- code-review-graph:所有都覆盖
- GitNexus:调用图 + 模块图 + 执行流
- code-graph-rag-mcp:调用图 + 模块图 + 跨语言依赖
23. 概念全景速查表
| 概念 | 一句话理解 | 对应章节 / 项目 |
|---|---|---|
| Token | LLM 的计费单位 | 全部 |
| MCP | AI 工具的 USB 标准 | 全部(核心协议) |
| AST | 代码的"句子成分分析图" | 02 / 03 / 04 / 05 / 08 |
| Tree-sitter | 通用 AST 解析器 | 同上 |
| LSP | IDE 的"语言翻译官协议" | 08 (Serena) |
| Embedding | 把意思变 GPS 坐标 | 04 / 05 / 08 |
| Model2Vec | CPU 上跑得飞快的嵌入 | 05 (Semble) |
| BM25 | 经典关键词搜索 | 04 / 05 / 08 |
| RRF | 合并多路检索的方法 | 05 / 08 |
| Trigram 倒排索引 | flashgrep 的核心 | 06 |
| 知识图谱 | 代码的"人际关系网" | 02 / 03 / 04 |
| 爆炸半径 | "改这个会炸到哪" | 02 / 03 |
| 社区检测 | 自动发现功能模块 | 02 |
| RAG | 让 AI 开卷考试 | 04 / 05 / 08 |
| Chunking | 切代码喂给 LLM | 04 / 05 / 08 |
| Merkle DAG | 智能增量同步 | 08 (Claude Context) |
| mmap | 磁盘文件伪装成内存 | 06 (flashgrep) |
| Hooks | AI 工具调用前后插一脚 | 03 (GitNexus) |
| Skills | AI 的工作流 SOP 模板 | 03 (GitNexus) |
| NDCG@10 | 搜索结果好不好的考分 | 05 |
| F1 / Precision / Recall | 准确性指标 | 02 |
24. 从 0 到 1 的推荐学习路径
如果你完全零基础,建议这样读:
第一阶段(理解大图景)
本章 §0 全局比喻
↓
本章 §1 Token + §2 MCP ← 看完知道为什么需要本地索引
↓
README + 01 章 ← 全景图 + 三大流派
第二阶段(理解核心技术)
本章 §3 AST + §4 Tree-sitter ← 解析代码的基础
↓
本章 §6 Embedding + §8 BM25 ← 检索的两条路
↓
本章 §14 RAG ← 整个 RAG 思想
第三阶段(按兴趣深入)
对图谱感兴趣 → §11 知识图谱 + §12 爆炸半径 + §13 社区检测 → 02 / 03 章
对检索感兴趣 → §7 Model2Vec + §9 RRF + §15 Chunking → 05 / 08 章
对速度感兴趣 → §10 Trigram + §17 mmap → 06 章
对 IDE 集成感兴趣 → §5 LSP → 08 章 (Serena)
第四阶段(动手实战)
选 02–08 中任一项目按教程跑起来
↓
07 章对比与选型 → 决定团队最终方案25. 常见问题(FAQ)
Q: "MCP" 和 "API" 有什么区别?
A:API 是泛指任何"程序对外暴露的接口"。MCP 是 API 的一种特定形式——专门为 AI 助手设计,规定了如何描述工具能力、如何调用、如何返回。可以理解为"AI 助手用的 REST API"。
Q: "向量数据库"和"普通数据库"有什么区别?
A:普通数据库(MySQL)擅长"精确查找"(user_id = 123)。向量数据库(Milvus)擅长"相似度查找"("找跟这个向量最像的 10 个")。前者用 B-tree 索引,后者用 HNSW / IVF 等向量索引。
Q: 为什么"语义检索"不能取代"关键字检索"?
A:因为语义检索擅长"按意思找",但找精确名字时反而绕远。例如搜常量 MAX_RETRY_COUNT,BM25 一发命中,向量可能输给"做重试的其他函数"。所以业界普遍 BM25 + 向量混合。
Q: 我要装哪些工具才能在 Claude Code 里用上代码索引?
A:最简单的:
bash
# 一个语义搜索
claude mcp add semble -s user -- uvx --from "semble[mcp]" semble
# 一个图谱分析
pip install code-review-graph && code-review-graph install
# 重启 Claude Code然后随便问:"找一下处理认证的代码" / "改这个函数会影响什么?",AI 会自动调用合适的工具。
Q: 这些索引数据会上传到云端吗?
A:默认全部本地。但有几个项目可选用云嵌入:
- code-graph-rag-mcp、code-review-graph 默认本地嵌入(sentence-transformers),可切到 OpenAI/Gemini
- Claude Context 默认 OpenAI 云嵌入,可切到本地 Ollama
- Semble、BitFun、Serena 完全本地
数据敏感的话,记得选全本地路径。
Q: 索引会占多大空间?
A:粗略对照:
| 项目 | 索引体积(相对源码) |
|---|---|
| Semble | <5% |
| code-review-graph | ~10% |
| GitNexus | ~10–15% |
| code-graph-rag-mcp | ~15–20%(含向量) |
| BitFun (flashgrep) | ~58%(用空间换速度) |
26. 推荐扩展阅读
- MCP 官方文档:https://modelcontextprotocol.io/
- Tree-sitter 介绍:https://tree-sitter.github.io/tree-sitter/
- LSP 规范:https://microsoft.github.io/language-server-protocol/
- RAG 入门 (LangChain):https://python.langchain.com/docs/tutorials/rag/
- 图算法入门:《Algorithms on Graphs》Coursera 课程
- BM25 / IR 入门:《Introduction to Information Retrieval》(Manning et al.,免费)
- 向量数据库选型:https://github.com/erikbern/ann-benchmarks
读完本章 + 推荐阅读,你基本就能加入业界关于"AI 代码助手"的所有技术讨论了。