代码仓库索引调研--Cursor Repo Index 与基于 graph 的索引新方案
最近刷到一篇 paper,是关于代码定位(Code Localization)的,具体来说,是做“自然语言” -> “代码文件” 的映射。我正好在做基于 LLM 的代码生成功能,上个月把仿真脚本语言的 LLM 训练做完了,下一步想基于 RAG 技术提升代码生成的质量,正琢磨如何更高效地做代码索引和召回,感觉这篇文章在技术上有可借鉴之处,记录一下其中的技术细节,以及 Cursor 做代码 index 的方式。
paper:LocAgent: Graph-Guided LLM Agents for Code Localization
论文核心内容
LocAgent 是一个解决代码定位问题的框架,通过基于图的表示方法来处理这一挑战。将代码库解析为有向异构图,创建轻量级表示,捕获代码结构(文件、类、函数)及其依赖关系(导入、调用、继承),使 LLM 智能体能够通过多跳推理有效搜索和定位相关实体。
主要贡献
- 图表示方法:将代码库转换为有向异构图的新方法
- 多跳推理:LLM 智能体通过图结构进行有效的多跳推理
- 成本效率:使用微调的 Qwen-2.5-Coder-Instruct-32B 实现了与 SOTA 专有模型相当的结果
- 实际效果:在文件级定位上达到 92.7% 的准确率,下游 GitHub issue 解决成功率提高 12%(Pass@10)
图表示方法详解
异构图构造
LocAgent 将代码库解析为有向异构图(Directed Heterogeneous Graph),包含四类核心节点:
节点类型
- Directory 节点:代表目录/文件夹,反映代码的层次组织结构
- File 节点:代表代码文件
- Class 节点:代表类定义
- Function 节点:代表函数定义
边类型及构造方式
-
Contains 关系
- 表示包含关系(目录包含子目录和文件、文件包含类、类包含方法等)
- 建立层次结构
- 示例:
src/目录包含auth/子目录,auth/包含login.py文件
-
Import 关系
- 通过解析
import语句和from ... import ...语句构建 - 建立文件间的依赖关系
- 示例:
from utils import helper创建 current_file → utils.py 的 import 边
- 通过解析
-
Invoke 关系
- 通过 AST 分析识别函数调用和方法调用
- 建立函数间的调用关系
- 包括直接调用和间接调用
-
Inheritance 关系
- 解析类继承关系
- 建立父类和子类之间的连接
- 示例:
class Child(Parent):创建 Child → Parent 的继承边
代码到图的转换流程
1. 静态分析阶段
# 伪代码示例
def parse_codebase(codebase_path):
graph = DirectedGraph()
# 遍历所有文件
for file_path in get_all_files(codebase_path):
# 解析 AST
ast_tree = parse_ast(file_path)
# 创建文件节点
file_node = create_file_node(file_path)
graph.add_node(file_node)
# 提取类和函数
classes = extract_classes(ast_tree)
functions = extract_functions(ast_tree)
# 创建节点和边
for cls in classes:
cls_node = create_class_node(cls)
graph.add_node(cls_node)
graph.add_edge(file_node, cls_node, "contains")
for func in functions:
func_node = create_function_node(func)
graph.add_node(func_node)
graph.add_edge(parent_node, func_node, "contains")
2. 依赖关系提取
- Import 分析:使用 AST 解析器识别所有导入语句
- 调用关系分析:通过 AST 遍历识别函数调用和方法调用
- 继承关系分析:解析类定义中的基类信息
3. 图优化
- 噪声过滤:移除不相关的临时变量和内部函数
- 权重赋值:根据调用频率和重要性为边分配权重
- 层次压缩:合并过于细粒度的节点以提高效率
Qwen-2.5-Coder-Instruct-32B 微调详解
微调策略
LocAgent 采用了针对代码定位任务的专门微调方法:
1. 任务特化训练
- 图遍历指令:训练模型理解图结构和遍历策略
- 多跳推理:学习在图中进行多步推理
- 代码理解:增强对代码语义和结构的理解
实验效果
主要指标
- 文件级准确率:92.7%(在 SWE-bench 数据集上)
- 成本降低:相比专有模型降低约 86%
- 下游任务提升:GitHub issue 解决成功率提高 12%(Pass@10)
技术优势
- 轻量级表示:图表示相比完整代码更加紧凑
- 多跳推理能力:能够跨文件和模块进行推理
- 可扩展性:适用于不同规模的代码库
- 成本效率:显著降低了推理成本
对比 Cursor 的 Code RAG 技术
以现在风头正劲的 Cursor 为例,Cursor 的 repo index 技术是基于常规的 RAG 流程,通过以下步骤实现:
- Code Chunking and Processing
- Embedding Generation
- Storage and Indexing
Cursor 的实现细节参考了这篇文章,官方没有发布完整细节,有部分是推测的内容: How Cursor Indexes Codebases Fast
其中,Code Chunking 是最关键的,整个代码库索引的质量很大程度依赖于 chunk 的质量,不能简单根据 token 的数量来切分 chunk,需要使用能够理解代码结构的智能分割器,例如使用高级分隔符(例如,类和函数定义)在适当的语义边界处进行分割的递归文本分割器;另外,可以使用抽象语法树(AST)来实现 DFS 的代码遍历,从而实现代码的智能分割,一个好用的库是 tree-sitter,LocAgent 也使用了这个库来做 Class/Function 等节点的解析。
记录一个 tree-sitter 来实现代码的智能分割的文章:An attempt to build cursor's @codebase feature - RAG on codebases
Chunk 阶段还有一个小细节,使用了 merkle tree,通过计算 hash 值来实现代码的增量更新。
如何在推理时使用 embedding
上面的步骤中获得了整个 repo 的 embedding,在推理时,有以下几个步骤:
- query embedding
- Vector Similarity Search:使用向量检索来找到与问题最相关的 chunk,由于 cursor 的 embedding 是保存在官方服务器上的,所以这里查询的结果只用相关文件路径
- Local File Access:从上一步中获得相关代码的文件路径和行号,然后从本地文件中读取代码
- Context Assembly:将上一步中获得的代码作为 context 发送给 LLM
- Informed Response:LLM 根据 context 生成回答
embedding 模型
有开源的 embedding 模型,例如 sentence-transformers/all-MiniLM-L6-v2,看 HF 社区反馈,貌似中文支持一般。Cursor 很可能使用了 OpenAI 的 embedding 模型或者微调过的模型。
记录一个专门为 code 设计的 embedding 模型:microsoft/unixcoder-base

浙公网安备 33010602011771号