Codebase Understanding - Indexing or not
关于 AI Coding 领域中代码库处理方式的思考:索引与非索引的对比
如何处理 codebase 是 AI Coding 领域非常重要的问题之一,各类产品有各自的做法,大致可以分厂两派:以
Cursor
为代表的对代码库构建索引并使用 RAG、以 Cline 为代表的不做索引。
Cursor
Cursor
并没有公开的信息透露他们是如何处理 codebase 的,但是从他们的 Security - Codebase Indexing 可以大概看到
Cursor
的核心思路:
- 本地用 AST 分割代码 -> 构建 Merkle 树
- 只同步变更文件、增量更新
- 代码块 -> Embedding 向量 -> Turbopuffer 向量数据库
- 用户提问 -> 语义搜索 -> 本地读取源码 -> LLM 生成答案
所以简单来说,就是将代码分块存入向量数据库,然后使用 RAG。
Cursor allows you to semantically index your codebase, which allows it to answer questions with the context of all of your code as well as write better code by referencing existing implementations. Codebase indexing is enabled by default, but can be turned off in settings.
Our codebase indexing feature works as follows: when enabled, it scans the folder that you open in
Cursor and computes a Merkle tree of hashes of all files. Files and subdirectories specified by
'.gitignore'
or'.cursorignore'
are ignored. The Merkle tree is then synced to the server. Every 10 minutes, we check for hash mismatches, and use the Merkle tree to figure out which files have changed and only upload those.At our server, we chunk and embed the files, and store the embeddings in Turbopuffer. To allow filtering vector search results by file path, we store with every vector an obfuscated relative file path, as well as the line range the chunk corresponds to. We also store the embedding in a cache in AWS, indexed by the hash of the chunk, to ensure that indexing the same codebase a second time is much faster (which is particularly useful for teams).
At inference time, we compute an embedding, let Turbopuffer do the nearest neighbor search, send back the obfuscated file path and line range to the client, and read those file chunks on the client locally. We then send those chunks back up to the server to answer the user’s question. This means that for privacy mode users, no plaintext code is stored on our servers or in Turbopuffer.
一些注意事项:
- 要阻止代码库中的特定文件被发送到
Cursor 服务器并包含在 AI 请求中,可以在代码库中添加
'.cursorignore'
文件 - 文件路径混淆详情:路径通过
'/'
和'.'
分割,每个段都使用存储在客户端的密钥和确定性的 6 字节 nonce 进行加密 - 嵌入反转:学术研究表明在某些情况下可以反转嵌入向量
- 当在 Git 仓库中启用代码库索引时,也会索引 Git 历史记录
Cline
Cline 在最近的博客中明确指出不对代码库构建索引,并且阐述了为什么他们认为不应该构建索引、使用 RAG 来处理代码库。
文中提到了 3 点构建索引和 RAG 的弊端:
- 构建索引的第一步是需要对代码进行分块,Cline 认为分块会导致撕裂代码逻辑
- 随着代码更新,代码与向量数据库中的数据同步会存在问题
- 虽然只是将向量存储到线上数据库,但其毕竟是代码的中间表示,存在一定的安全性问题
前两个问题在
Cursor
中可能有解决方案(但只是推测)。
而 Cline 所做的事情,与真人开发者类似,通过 AST 抽取代码的高级表征如类、函数、方法及其之间的关系,以这种更 old school 的方式,找到相关的代码片段,最终构建上下文。
孰优孰劣?
个人两者各有优劣,应该结合:
RAG 适用于语义搜索,语义搜索是新时代 IDE 可以做到的事情了,这在 Building a better code search experience 其实也有提到,除了能给 Coding Agent 使用以外,对于提升人类搜索代码的体验也至关重要。
而 Cline 的做法更符合人类的认知,通过 IDE 提供的「定义」和「引用」的功能,能够快速了解实现和使用方式。
如果在做商业化 Coding Agent,还需要考虑成本,RAG 不失为一种成本与效果上的折中选择,而 Cline 被人诟病的也是其大上下文占用,但毕竟他不是一款商业化产品,以「提升 Coding Agent 写码效果」为核心。
也许两者结合,根据特定任务让 Coding Agent 自行决断,是个不错的选择。