Claude Context
为 AI 编码助手提供语义化代码检索能力的 MCP 工具
1. 项目简介
Claude Context 是一个为 Claude Code、Cursor、Gemini CLI 等 AI 编码助手(以及 VSCode、Chrome 浏览器)提供代码库语义检索能力的工具集。它用向量数据库(Milvus / Zilliz Cloud)把代码切分后的片段索引起来,再通过 MCP(Model Context Protocol) 协议暴露 index_codebase / search_code 等工具,让 AI Agent 可以用自然语言快速定位到代码库中相关的代码片段,显著降低 token 消耗。
核心功能列表
- 索引代码库:将整个仓库按语言切块、向量化后写入 Milvus。
- 语义搜索(Hybrid Search):结合 BM25 稀疏检索 + Dense Vector(稠密向量)检索,支持 RRF 重排。
- 增量同步:基于 Merkle DAG + 文件哈希检测变更,只重建动过的文件(后台定时 5 分钟跑一次)。
- 多端接入:MCP Server(stdio)、VSCode 扩展(Sidebar + 命令)、Chrome 扩展(在 GitHub 等页面直接搜索)。
- 多嵌入提供商:OpenAI、VoyageAI、Gemini、Ollama(本地)、OpenRouter。
- 两种切分策略:基于
tree-sitter的 AST 切分(首选)+ LangChain 字符切分(兜底)。
技术栈总览
| 类别 | 选型 | 备注 |
|---|---|---|
| 语言 | TypeScript 5.8 | 全栈 TS,严格模式 |
| 运行时 | Node.js ≥ 20 | 见根 package.json > engines |
| 包管理 | pnpm ≥ 10(workspace) | pnpm-workspace.yaml 配置 |
| MCP SDK | @modelcontextprotocol/sdk ^1.12.1 | stdio 传输 |
| 向量库 | @zilliz/milvus2-sdk-node ^2.5.10(gRPC)+ 自研 REST 客户端 | packages/core/src/vectordb |
| AST 切分 | tree-sitter + 11 种语言 grammar | JS/TS/PY/Java/Cpp/Go/Rust/C#/Scala |
| 字符切分 | langchain ^0.3.27 的 RecursiveCharacterTextSplitter | 兜底方案 |
| Embedding | openai ^5.1.1、voyageai、@google/genai、ollama | 统一抽象在 base-embedding.ts |
| 哈希/差异 | 自实现 MerkleDAG + sha256 | packages/core/src/sync |
| VSCode 扩展 | vscode ^1.74、webpack 5、web-tree-sitter | 浏览器化打包 |
| Chrome 扩展 | webpack + manifest v3 | gRPC stub 掉 Milvus,走 REST |
| 评测 | Python + uv + 自研 server/retrieval | evaluation/ 目录 |
2. 目录结构说明
claude-context/
├── packages/ # monorepo 核心:4 个子包
│ ├── core/ # ⭐️ @zilliz/claude-context-core:核心引擎,被其它包依赖
│ │ └── src/
│ │ ├── context.ts # 【门面类 Context】索引/搜索主入口,1300+ 行,最重要的文件
│ │ ├── types.ts # SemanticSearchResult 等顶层类型
│ │ ├── index.ts # 统一 re-export 入口
│ │ ├── embedding/ # Embedding 抽象与多提供商实现
│ │ │ ├── base-embedding.ts # 抽象基类 Embedding(embed/embedBatch/detectDimension)
│ │ │ ├── openai-embedding.ts # OpenAI 实现
│ │ │ ├── voyageai-embedding.ts # VoyageAI 实现(voyage-code-3 默认)
│ │ │ ├── gemini-embedding.ts # Gemini 实现(支持 Matryoshka 维度)
│ │ │ └── ollama-embedding.ts # 本地 Ollama 实现
│ │ ├── vectordb/ # 向量数据库抽象
│ │ │ ├── types.ts # VectorDatabase 接口 + HybridSearch 类型
│ │ │ ├── milvus-vectordb.ts # gRPC 实现(生产首选)
│ │ │ ├── milvus-restful-vectordb.ts # REST 实现(VSCode Web / Chrome 用)
│ │ │ └── zilliz-utils.ts # Personal API Key → endpoint 解析
│ │ ├── splitter/ # 代码切块
│ │ │ ├── ast-splitter.ts # tree-sitter AST 切分 + 自动降级
│ │ │ └── langchain-splitter.ts # 字符级兜底
│ │ ├── sync/ # 增量同步
│ │ │ ├── merkle.ts # MerkleDAG:用 sha256 做内容指纹的 DAG
│ │ │ └── synchronizer.ts # FileSynchronizer:快照 + 变更检测
│ │ └── utils/env-manager.ts # 统一 env 读取 + ~/.context/.env 加载
│ │
│ ├── mcp/ # ⭐️ @zilliz/claude-context-mcp:MCP stdio server
│ │ └── src/
│ │ ├── index.ts # MCP 服务入口,注册 4 个 tools
│ │ ├── config.ts # 从 env 构建 ContextMcpConfig;CodebaseSnapshot v1/v2
│ │ ├── handlers.ts # 4 个工具的实现(最重量级,1000+ 行)
│ │ ├── snapshot.ts # ~/.context/mcp-codebase-snapshot.json 读写
│ │ ├── sync.ts # SyncManager:5 分钟定时增量同步
│ │ ├── embedding.ts # createEmbeddingInstance 工厂
│ │ └── utils.ts # 路径归一化、内容截断
│ │
│ ├── vscode-extension/ # VSCode 扩展(商店名 semanticcodesearch)
│ │ └── src/
│ │ ├── extension.ts # 激活入口,注册命令 + webview
│ │ ├── config/configManager.ts # 读取 workspace 配置,生产 Context 实例
│ │ ├── commands/{index,search,sync}Command.ts
│ │ ├── webview/ # Sidebar 面板(HTML + JS + CSS 三件套)
│ │ └── stubs/ # gRPC 桩,强制走 REST
│ │
│ └── chrome-extension/ # 浏览器里对 GitHub 等仓库做搜索
│ └── src/
│ ├── background.ts / content.ts / options.ts
│ ├── manifest.json # MV3
│ ├── milvus/chromeMilvusAdapter.ts # 浏览器端 Milvus 适配
│ └── stubs/ # 同样用 stub 屏蔽 Node gRPC
│
├── examples/basic-usage/ # 最小可运行示例(直接用 core 包)
│ └── index.ts # Context/Milvus/Embedding 组合调用样板
│
├── evaluation/ # Python 离线评测套件
│ ├── run_evaluation.py # 主入口:对比原始检索 vs Claude Context
│ ├── retrieval/ # 基类 + 自定义检索器
│ ├── servers/ # grep/read/edit 三个 MCP 评测 server
│ ├── analyze_and_plot_mcp_efficiency.py # 生成 token 节省图表
│ └── pyproject.toml
│
├── docs/ # 用户文档
│ ├── getting-started/{prerequisites,quick-start,environment-variables}.md
│ ├── dive-deep/{asynchronous-indexing-workflow,file-inclusion-rules}.md
│ └── troubleshooting/{faq,troubleshooting-guide}.md
│
├── scripts/build-benchmark.js # 构建性能统计
├── .env.example # 环境变量模板(要复制到 ~/.context/.env)
├── pnpm-workspace.yaml # workspace 声明:packages/*、examples/*
├── package.json # 根脚本(pnpm -r build 等)
└── README.md # 项目总纲(250 行+,含所有客户端接入方式)
3. 架构设计
3.1 整体架构模式
Claude Context 采用 Monorepo + 分层(Hexagonal 风格) 的设计:
- 领域核心(core):把「索引一个代码库」「对代码库做语义搜索」抽象成
Context这个门面类,只依赖 3 个接口:Embedding、VectorDatabase、Splitter。 - 适配层(mcp / vscode-extension / chrome-extension):各自实现一种 UI/协议壳子,调用 core 提供的 API,不关心向量化和切分细节。
- 驱动实现(multiple providers):
OpenAI/Voyage/Gemini/Ollama各自是Embedding的实现;MilvusVectorDatabase(gRPC)与MilvusRestfulVectorDatabase(REST)是VectorDatabase的两种实现。
这让「把 Milvus 换成别的向量库」或「把 OpenAI 换成本地 Ollama」这类替换只改一处实例化代码。
3.2 核心模块划分
| 模块 | 职责 | 关键类/文件 |
|---|---|---|
| 门面 | 协调切分→向量化→入库;暴露 indexCodebase/semanticSearch | Context (packages/core/src/context.ts) |
| 切分 | 把源代码文件切成可向量化的 chunk | AstCodeSplitter / LangChainCodeSplitter |
| 向量化 | 文本 → 向量 | Embedding 抽象 + 4 个实现 |
| 存储 | Collection 管理、Dense + Sparse 双字段、Hybrid Search | VectorDatabase 接口 + MilvusVectorDatabase |
| 增量 | 检测文件增/删/改 | MerkleDAG + FileSynchronizer |
| 快照 | 记录”哪些 codebase 已索引/正在索引” | SnapshotManager(MCP 独有) |
| 适配层 | MCP / VSCode / Chrome 各自的 UI 壳 | 3 个独立包 |
3.3 数据流向
索引阶段(index):
codebasePath
→ getCodeFiles() // 按扩展名 + ignore 过滤
→ codeSplitter.split() // AST 或 LangChain,产出 CodeChunk[]
→ 按 EMBEDDING_BATCH_SIZE(100) 累积 chunk
→ embedding.embedBatch() // 得到 Dense vector
→ 构造 VectorDocument(含 BM25 文本 + dense vector + metadata)
→ vectorDatabase.insertHybrid(collectionName, documents)
→ FileSynchronizer 写入 ~/.context/merkle/<md5>.json(文件哈希 + Merkle DAG)
搜索阶段(search):
query(string)
→ embedding.embed(query) // 得到 query dense vector
→ 组装 2 个 HybridSearchRequest:
- { anns_field: 'vector', data: queryVec } // Dense
- { anns_field: 'sparse_vector', data: query } // BM25
→ vectorDatabase.hybridSearch(..., { rerank: { strategy: 'rrf' } })
→ 归一化为 SemanticSearchResult[]
→ 返回给 AI Agent
增量同步(5 分钟一次):
FileSynchronizer.initialize()
→ 从 ~/.context/merkle/<hash>.json 加载旧快照
→ 重新扫描目录生成新的文件哈希 + 新 MerkleDAG
→ MerkleDAG.compare(old, new) 发现根哈希变化 → 逐文件对比
→ 输出 { added, removed, modified }
→ context.reindexByChange() 删除旧 chunks、重新 embed+insert 增量部分
4. 核心流程解析
4.1 MCP Server 启动流程
入口:packages/mcp/src/index.ts
// packages/mcp/src/index.ts(节选)
async function main() {
const config = createMcpConfig() // ① 从 env 读配置,支持 ~/.context/.env
logConfigurationSummary(config)
const server = new ContextMcpServer(config) // ② 实例化服务
await server.start() // ③ 启动 stdio
}
ContextMcpServer 构造函数做了四件事(见上面 index.ts 节选):
- 新建
@modelcontextprotocol/sdk的Server,声明capabilities.tools。 - 用
createEmbeddingInstance(config)生产 Embedding 实例(根据EMBEDDING_PROVIDER)。 new MilvusVectorDatabase({ address, token })建立向量库连接。- 把二者注入
new Context({...}),然后初始化SnapshotManager(读~/.context/mcp-codebase-snapshot.json)、SyncManager、ToolHandlers。
server.start() 中有一个细节值得留意:
// 修复 Issue #295:老版本 MCP 会留下 "0 files / 0 chunks + completed" 这样的伪记录
// 会让客户端误判为"未索引",进而触发强制重索引 → 删数据 → 循环。所以启动时做一次愈合。
await this.toolHandlers.validateLegacyZeroEntries()
const transport = new StdioServerTransport()
await this.server.connect(transport)
this.syncManager.startBackgroundSync() // 5 秒后启动首轮同步,之后每 5 分钟一次
关键点:MCP 协议只能用 stdout 传 JSON-RPC,所以文件顶端把 console.log/console.warn 重定向到了 stderr(packages/mcp/src/index.ts 前 15 行),避免污染协议流。
4.2 index_codebase 工具执行链路
- Agent 调用 MCP
index_codebase→setRequestHandler(CallToolRequestSchema, ...)派发到toolHandlers.handleIndexCodebase。 handleIndexCodebase做参数校验(必须是绝对路径)、检查快照是否已索引、决定 force/非 force,然后立即返回一个”indexing started”响应(不阻塞 Agent)。- 真正的重活由后台 Promise 跑:
Context.prepareCollection(codebasePath):创建 Milvus collection(含 densevector+sparse_vector两个字段)。getCodeFiles()扫描全部符合扩展名且不匹配 ignore 规则的文件。processFileList()流式读文件 →codeSplitter.split()→ 按 100 条/批累积 → 调用processChunkBatch()。
processChunkBatch()核心逻辑:
// packages/core/src/context.ts
const chunkContents = chunks.map((c) => c.content)
const embeddings = await this.embedding.embedBatch(chunkContents) // 批量向量化
const documents: VectorDocument[] = chunks.map((chunk, index) => ({
id: this.generateId(relativePath, start, end, chunk.content), // sha256 前 16 位
content: chunk.content, // 作为 BM25 原文存储
vector: embeddings[index].vector, // Dense
relativePath,
startLine,
endLine,
fileExtension,
metadata: { ...rest, codebasePath, language, chunkIndex: index },
}))
await this.vectorDatabase.insertHybrid(collectionName, documents)
- 过程中
SnapshotManager把百分比进度写入~/.context/mcp-codebase-snapshot.json,Agent 随时可以get_indexing_status查。
4.3 search_code 工具执行链路
// 简化自 context.ts → semanticSearch()
const queryEmbedding = await this.embedding.embed(query);
const requests: HybridSearchRequest[] = [
{ data: queryEmbedding.vector, anns_field: 'vector', param: {...}, limit: topK * 2 },
{ data: query, anns_field: 'sparse_vector', param: {...}, limit: topK * 2 },
];
const hits = await this.vectorDatabase.hybridSearch(
collectionName,
requests,
{ rerank: { strategy: 'rrf' }, limit: topK, filterExpr }
);
return hits.map(h => ({ content, relativePath, startLine, endLine, language, score }));
为什么是 Hybrid? Dense 向量擅长语义相似(“用户登录逻辑” → authenticateUser),BM25 擅长关键字命中(“UserService.login”),RRF 重排能兼得两者。
4.4 增量同步流程(5 分钟/次)
SyncManager.startBackgroundSync:
- 启动 5 秒后跑首次 → 然后
setInterval(handleSyncIndex, 5 * 60 * 1000)。 handleSyncIndex遍历 snapshot 里所有indexed状态的 codebase,调context.reindexByChange(path)。reindexByChange内部用FileSynchronizer.checkForChanges():
// packages/core/src/sync/synchronizer.ts
public async checkForChanges() {
const newFileHashes = await this.generateFileHashes(this.rootDir); // 全量扫 + sha256
const newMerkleDAG = this.buildMerkleDAG(newFileHashes);
const changes = MerkleDAG.compare(this.merkleDAG, newMerkleDAG); // 先比根
if (changes.added.length || changes.removed.length) {
const fileChanges = this.compareStates(oldHashes, newHashes); // 再比文件
await this.saveSnapshot();
return fileChanges;
}
return { added: [], removed: [], modified: [] }; // 大多数情况走这里
}
精妙处:MerkleDAG 的根节点数据是”所有文件哈希拼接”再 sha256,根节点没变意味着一个字节都没动,整个 O(N) 的文件对比就可以跳过。
4.5 VSCode 扩展启动流程
packages/vscode-extension/src/extension.ts 的 activate():
configManager = new ConfigManager(context) // 读 workspace 配置
codeContext = createContextWithConfig(configManager) // 用 REST 版 Milvus
searchCommand = new SearchCommand(codeContext) /* index/sync 同理 */
// 注册 Webview 视图 + 3 个命令 + 状态栏
setupAutoSync() // 默认每 5 分钟
runInitialSync() // 启动时跑一次
VSCode 扩展强制使用 REST 版 MilvusRestfulVectorDatabase,因为 @zilliz/milvus2-sdk-node 的 gRPC 依赖无法在 webpack 打包的扩展环境中工作(src/stubs/ 里也能看出:把 milvus-vectordb.js、ast-splitter.js 全替换成了 stub)。
5. 关键设计与实现
5.1 使用的设计模式
- 门面(Facade):
Context把切分/向量化/入库/同步四个子系统封装成indexCodebase、semanticSearch、reindexByChange三个方法。 - 策略(Strategy):
Splitter接口有 AST / LangChain 两种策略;AstCodeSplitter内部还会根据语言是否支持自动降级到 LangChain。 - 抽象工厂:
createEmbeddingInstance(config)(MCP)、ConfigManager.createEmbeddingInstance(VSCode)根据字符串生产实现。 - 模板方法:
Embedding抽象类提供preprocessText、preprocessTexts,子类只填embed/embedBatch。 - 观察者/回调:
indexCodebase(path, onProgress)的进度回调。 - 适配器:
chromeMilvusAdapter.ts把浏览器 fetch 适配成 Milvus REST 调用。
5.2 数据模型
Milvus Collection Schema(Hybrid): 见 packages/core/src/vectordb/milvus-vectordb.ts,关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | VarChar PK | chunk_<sha256[:16]>,由 relativePath + lines + content 生成 |
vector | FloatVector(dim) | Dense embedding |
sparse_vector | SparseFloatVector | 由 Milvus 内置 BM25 function 从 content 生成 |
content | VarChar(65535) | 原文,BM25 的输入 & 搜索返回字段 |
relativePath | VarChar | 相对 codebase 根 |
startLine / endLine | Int64 | 便于定位 |
fileExtension | VarChar | 过滤用 |
metadata | JSON | codebasePath、language、chunkIndex 等 |
Collection 命名:由 Context.getCollectionName(codebasePath) 生成,默认 hybrid_code_chunks_<md5(codebasePath)>,长度受限于 MAX_COLLECTION_NAME_LENGTH = 255。可通过 CODE_CHUNKS_COLLECTION_NAME_OVERRIDE 环境变量覆盖。
快照结构(V2):~/.context/mcp-codebase-snapshot.json
// packages/mcp/src/config.ts
type CodebaseInfo =
| { status: 'indexing'; indexingPercentage: number; lastUpdated: string }
| {
status: 'indexed'
indexedFiles: number
totalChunks: number
indexStatus: 'completed' | 'limit_reached'
lastUpdated: string
}
| { status: 'indexfailed'; errorMessage: string; lastAttemptedPercentage?: number; lastUpdated: string }
Merkle 快照:~/.context/merkle/<md5(codebasePath)>.json —— 文件哈希 Map + 序列化的 DAG。
5.3 错误处理与日志策略
- MCP 服务内:把
console.log/warn改写到 stderr(保证 stdout 纯净),大量带前缀的结构化日志如[Context]、[SYNC-DEBUG]、[SNAPSHOT-VALIDATE]。 - Hard-fail vs Soft-fail:
- 单个文件读不到 →
console.warn跳过,不中断整体索引(见processFileList的 try/catch)。 - VectorDB 瞬态错误 → 清 buffer 继续下一批;集群无响应 → 抛出由上层 handler 捕获并写入
indexfailed状态。
- 单个文件读不到 →
- 集合数量超限:用特殊常量
COLLECTION_LIMIT_MESSAGE做 message 匹配,避免平台差异导致误判(见vectordb/types.ts)。 - 快照自愈:启动时
validateLegacyZeroEntries()会用 MilvusgetCollectionRowCount()校正历史遗留的0/0+completed条目(详见 Issue #295 的注释,保留了很高的可读性)。
5.4 安全机制
- API Key 与 Token:全部走环境变量 (
OPENAI_API_KEY、VOYAGEAI_API_KEY、GEMINI_API_KEY、MILVUS_TOKEN),envManager支持~/.context/.env,避免把密钥写入项目根。 - 路径隔离:
Context要求绝对路径,避免”相对路径在不同 CWD 下定位到不同 collection”引起数据污染(文档asynchronous-indexing-workflow.md明确说明)。 - 隐藏文件永远排除:
matchesIgnorePattern和FileSynchronizer.shouldIgnore都硬编码了「任何以.开头的路径段都忽略」,保护.env、.ssh等不被索引。 - Chunk 数硬上限:
CHUNK_LIMIT = 450000,超大仓库会触发limit_reached状态,防止无限膨胀。
6. 环境搭建与运行
6.1 前置条件
- Node.js ≥ 20.x(22.x 也支持)
- pnpm ≥ 10(
npm install -g pnpm) - Milvus / Zilliz Cloud 账号(或本地
docker run -p 19530:19530 milvusdb/milvus:latest) - 一个 Embedding 服务(最简单是 OpenAI API Key;想纯本地就装 Ollama +
nomic-embed-text)
6.2 安装与构建
# 1. 克隆
git clone https://github.com/zilliztech/claude-context.git
cd claude-context
# 2. 装依赖(会为所有 workspace 子包装依赖)
pnpm install
# 3. 构建全部包
pnpm build
# 也可以只构建某一个
pnpm build:core # @zilliz/claude-context-core
pnpm build:mcp # @zilliz/claude-context-mcp
pnpm build:vscode # semanticcodesearch(VSCode 扩展)
# 4. 开发模式(多包 watch)
pnpm dev
6.3 运行方式
A. 直接跑示例(最快看到效果)
# 1. 配置全局 env(一次即可)
cp .env.example ~/.context/.env
# 编辑 ~/.context/.env,至少填:
# OPENAI_API_KEY=sk-...
# MILVUS_ADDRESS=your-zilliz-endpoint
# MILVUS_TOKEN=your-zilliz-key
# 2. 运行 basic example(会索引整个项目并做几次搜索)
pnpm example:basic
B. 作为 MCP Server(给 Claude Code/Cursor/Gemini 用)
# 直接用已发布的包
npx @zilliz/claude-context-mcp@latest
# 或者用本地构建版本
node packages/mcp/dist/index.js
Cursor 里在 ~/.cursor/mcp.json 加:
{
"mcpServers": {
"claude-context": {
"command": "npx",
"args": ["-y", "@zilliz/claude-context-mcp@latest"],
"env": {
"OPENAI_API_KEY": "sk-xxx",
"MILVUS_ADDRESS": "https://xxx.zilliz.com",
"MILVUS_TOKEN": "xxx"
}
}
}
}
C. VSCode 扩展
pnpm build:vscode
cd packages/vscode-extension
pnpm package # 产出 .vsix
# 或者:F5 启动 Extension Development Host 调试
6.4 关键环境变量(完整列表见 .env.example)
| 变量 | 作用 | 默认 |
|---|---|---|
EMBEDDING_PROVIDER | OpenAI / VoyageAI / Gemini / Ollama | OpenAI |
EMBEDDING_MODEL | 对应 provider 的模型名 | text-embedding-3-small |
EMBEDDING_BATCH_SIZE | 一次嵌入多少 chunk | 100 |
OPENAI_API_KEY | OpenAI key | — |
MILVUS_ADDRESS | Milvus/Zilliz endpoint | — |
MILVUS_TOKEN | Zilliz personal key | — |
SPLITTER_TYPE | ast / langchain | ast |
HYBRID_MODE | true 开启 BM25+Dense | true |
CUSTOM_EXTENSIONS | 追加后缀名(.vue,.svelte) | 空 |
CUSTOM_IGNORE_PATTERNS | 追加忽略规则(temp/**) | 空 |
CODE_CHUNKS_COLLECTION_NAME_OVERRIDE | 覆盖 collection 命名 | 空 |
7. 推荐学习路线
第一阶段 · 入门(0.5 ~ 1 天)
目标:搞懂”什么是语义代码检索”,能跑起 basic example。
- 读
README.md前半部分(到 Architecture 图为止),理解整体定位。 - 读
docs/getting-started/quick-start.md+docs/dive-deep/asynchronous-indexing-workflow.md,理解”异步索引 + 后台同步”的用户视角状态机。 - 照 6.3 A 节跑通
pnpm example:basic,打开 Zilliz Cloud 控制台肉眼看到生成的 collection。 - 读
examples/basic-usage/index.ts:这是Context最小用法样板,30 行左右就能看完。
第二阶段 · 进阶(2 ~ 3 天)
目标:掌握核心引擎的每个环节。
- 门面 - 精读
packages/core/src/context.ts(1300 行,分 4 次读):- 构造函数 + 默认 ignore/extension 表
indexCodebase()→prepareCollection()→processFileList()→processChunkBatch()semanticSearch()的 Hybrid 请求构造reindexByChange()如何调FileSynchronizer
- 切分 - 精读
packages/core/src/splitter/ast-splitter.ts,重点看SPLITTABLE_NODE_TYPES、extractChunks、refineChunks(大块二次切分)+ 兜底到 LangChain 的条件。 - 向量化 - 通读
packages/core/src/embedding/base-embedding.ts+openai-embedding.ts,理解detectDimension、preprocessText的”按字符截断”近似。 - 存储 - 挑
packages/core/src/vectordb/milvus-vectordb.ts中三个方法精读:createHybridCollection、insertHybrid、hybridSearch;对照vectordb/types.ts接口。 - 增量 - 精读
sync/merkle.ts(只有 89 行)+sync/synchronizer.ts的checkForChanges/buildMerkleDAG。画一张小图:一次文件改动如何传导到 DAG 根 → 触发context.reindexByChange。 - MCP 层 - 读
packages/mcp/src/index.ts+handlers.ts的handleIndexCodebase/handleSearchCode。重点看”立即返回 + 后台 Promise”的模式,和SnapshotManager的 v1→v2 迁移。
第三阶段 · 实践(验证理解)
挑 1~2 个小任务练手:
- 加一种语言:在
SPLITTABLE_NODE_TYPES和getLanguageConfig里接入tree-sitter-ruby(仓库当前没有 Ruby grammar,Ruby 当前只能走 LangChain)。 - 加一个 Embedding 提供商:模仿
openai-embedding.ts写一个 Cohere / Jina 实现,注册到packages/mcp/src/embedding.ts的工厂里。 - 加一个 MCP 工具:实现
list_indexed_codebases,读SnapshotManager返回当前已索引的 codebase 列表(目前需要看日志才能知道)。 - 换一种向量库:写一个
QdrantVectorDatabase implements VectorDatabase,实测indexCodebase是否完全不用改(验证抽象是否真的干净)。 - 做一次评测:进
evaluation/目录,按README.md跑run_evaluation.py,观察 token 节省效果。
8. 常见问题与注意事项
8.1 容易踩的坑
- MCP 必须用 stdout 传协议:任何
console.log都会污染,所以packages/mcp/src/index.ts顶部做了重定向。如果你自己加新的依赖库打印到 stdout,整个 MCP 会话会直接崩。 .env要放~/.context/.env,不要放在被索引的仓库里:.env.example开头明确警告,否则和被索引项目的 env 冲突,且.env又会被默认忽略规则排除。- 绝对路径强约束:Agent 传相对路径给 MCP 会被拒。索引”同一仓库的不同软链路径”会被当成两个 codebase 创建两份 collection,浪费额度。
- Chunk 上限 450000:超级大仓库会看到
status: 'limit_reached';这是保护措施,不要以为是 bug。 - VSCode 扩展被迫走 REST:因为
@zilliz/milvus2-sdk-node含 Node 原生模块,webpack 无法打入浏览器化的扩展;如果你想在扩展里直连 gRPC Milvus,不可行。 - Hybrid Search 要求 Milvus ≥ 2.4:老版本 Milvus 没有
sparse_vector+ BM25 function,会建 collection 失败。 - Merkle 快照位于
~/.context/merkle/<md5>.json:如果你手动删了 Milvus collection 但忘了删这个文件,下次 sync 会误以为”所有文件都没变”什么也不做。SyncManager里有一段启发式恢复:error.message.includes('Failed to query Milvus')就FileSynchronizer.deleteSnapshot。 - Ollama 走本地时
EMBEDDING_MODEL与OLLAMA_MODEL优先级不同:config.ts里 Ollama 优先读OLLAMA_MODEL,其他 provider 优先读EMBEDDING_MODEL,写配置时要注意。
8.2 代码中的特殊约定
- 日志前缀即模块名:
[Context]、[Synchronizer]、[SYNC-DEBUG]、[SNAPSHOT-VALIDATE]、[EMBEDDING]等,方便 grep 定位;新加模块时请沿用。 - 路径用斜杠归一化:
ignorePattern匹配前一律把\\换成/,跨平台一致。 - ID 生成规则:chunk id =
chunk_<sha256(relativePath:startLine:endLine:content)[:16]>,因此只要内容和行号没变,重复索引也不会重复入库(Milvus PK 去重)。 - 生产/VSCode/Chrome 三套 VectorDatabase 实现,但共用一个接口 (
packages/core/src/vectordb/types.ts):新增方法时务必同步三个实现,否则某个适配层会编译挂。
8.3 值得注意的 TODO / 技术债务
roadmap中未完成项(README.md末尾):Agent-based interactive search mode、Search result ranking optimization、Robust Chrome Extension 尚未实现。- V1 快照兼容代码:
packages/mcp/src/snapshot.ts的loadV1Format把历史 v1 数据里的indexedFiles/totalChunks填成0,加之 Issue #295 的自愈逻辑,说明这块曾踩过坑;后续可能会彻底废弃 v1 格式,自定义扩展快照结构时需要小心。 rowCount被同时用作indexedFiles和totalChunks(handlers.ts注释明确承认”imprecise”):目前没有便宜的元数据查询能拿到真实 file count,后续如果 Milvus 支持 group-by count 应该替换。AstCodeSplitter未覆盖 Ruby/Swift/Kotlin/PHP 等常用语言,只在 LangChain 分支兜底,切分质量有改进空间。- Chrome 扩展比 VSCode 扩展粗糙一些(
README.md明说”Robust Chrome Extension”还在 roadmap),依赖 stub 较多,阅读时别把它当生产级参考。 CHUNK_LIMIT = 450000是硬编码,对超大 monorepo 不友好,未来可能会改成配置项。