AI News HubLIVE
站内改写4 分钟阅读

对比AI代码审查中的上下文检索方法

Compare The Market公司比较了两种上下文检索方法(GKG和RAG)用于AI代码审查。通过严格的评估,发现基于知识图谱的GKG方法在代码审查质量指标上优于RAG,能够更准确地理解代码结构和关系,减少误报。

来源Hacker News AI作者: adzicg

在Compare The Market公司,我们有一个内部AI工具,用于自动审查合并请求(MR),目标是加快开发人员接收代码反馈的速度,并提高整个组织的MR吞吐量。该工具在MR打开后的几分钟内提供智能的初轮审查,效果良好,但我们希望它更好。早期的审查者倾向于保守,由于孤立地审查代码更改,常常出现误报。我们希望让审查者了解更改在更广泛系统中的位置,例如,删除的函数可能看起来是死代码,除非知道它被另一个服务动态调用。

为此,我们面临一个基本架构决策:代理应如何检索代码库的上下文?我们有两个主要选项:

  1. GitLab知识图谱(GKG):一种代码分析引擎,使用Tree-sitter AST解析构建结构化的代码实体和关系知识图谱,存储在Kuzu图数据库中。支持精确查询,如“查找此函数的所有调用者”或“显示类层次结构”。
  1. 检索增强生成(RAG):一种向量相似性搜索方法,将代码分块,创建嵌入,并检索语义相似的代码片段。

我们基于直觉选择了GKG——我们的假设是,代码审查需要代码关系的结构理解,而不仅仅是语义相似性。审查函数更改时,需要知道什么调用它、它调用什么,以及它如何融入整体架构。RAG擅长查找“相似”代码,但相似性不等于代码审查的相关性。

本文验证了这一直觉。通过使用Databricks上的MLflow进行严格评估,我们比较了四种方法,发现GKG在代码审查质量最重要的指标上优于RAG。数据证实我们的架构决策是正确的。

四种方法

我们评估了四种配置:仅基线、GKG、RAG、GKG+RAG。

GKG集成

去年,GitLab推出了GitLab知识图谱(GKG)的测试版,这是一个模型上下文协议(MCP)服务器。API索引仓库并构建代码库的结构化可查询表示,将依赖关系映射到图中的节点,理解函数定义及其用法,跟踪继承层次结构,并捕获模块之间的交叉引用。结果形成代码的语义映射——不仅是一列文件,而是一个关系网络。通过MCP服务器提供的工具,AI代理可以实时查询此图,例如:“这个函数在哪里被调用?”或“如果我改变此方法签名,会有什么影响?”

由于GKG仍处于测试阶段且未作为原生GitLab CI/CD功能提供,我们构建了一个单独的侧车服务——一个轻量级Docker容器,包装官方GKG二进制文件,在CI管道中与审查者一起运行。工作流程包括:当MR管道启动时,侧车容器挂载项目源代码并索引整个代码库,从头构建知识图谱;索引完成后,它在本地端口启动GKG MCP服务器,暴露一组工具调用;我们的AI审查者连接到MCP服务器并在审查工作流中使用这些工具。

GKG构建一个符号图,其中节点表示代码实体(类、函数、变量),边表示关系(调用、继承、导入)。当GKG索引仓库时,它创建交互式图可视化,显示整个代码库结构。每个节点类型代表不同的代码实体:橙色(目录)、绿色(文件)、紫色(定义,即代码中定义的类、函数和方法)、蓝色(导入符号,即外部依赖和导入)。边显示关系:哪些文件包含哪些定义,哪些函数调用其他函数,哪些模块导入哪些符号。

例如,考虑一个简单的UserService类。GKG将其映射为一个图,显示类、其方法以及调用这些方法的所有文件。当AI审查者需要了解更改的影响时,它会查询GKG。例如,如果有人修改validate_input(),代理会问:“谁调用这个函数?”这种精确信息使审查者能够评估对validate_input()的更改是否会破坏任何调用者——仅凭差异本身无法做到这一点。

RAG集成

我们的RAG实现使用LlamaIndex进行智能代码分块,并使用OpenAI嵌入进行向量相似性搜索。索引管道扫描仓库中的代码文件,使用LlamaIndex的CodeSplitter(基于AST感知的分块)将代码分割成语义单元(函数、类),然后通过OpenAI text-embedding-3-small生成嵌入,并存储在FAISS向量存储中。检索管道根据查询嵌入检索最相似的代码块。

CodeSplitter采用图优先方法:首先构建代码的AST图,然后利用结构理解创建语义上有意义的分块。与朴素文本分块不同,它识别语义边界:函数边界(每个函数成为一个块)、类边界(类定义及其方法)、逻辑分组(相关代码保持在一起)。

然而,RAG在代码审查中可能遇到根本限制:它依赖向量空间中的语义相似性,这对于自然语言效果良好,但对于代码有以下固有局限:

  • 无法进行符号解析:无法区分同名的函数定义和函数调用。
  • 无法跟踪引用:审查函数更改时,无法可靠地找到所有调用者。
  • 分块边界问题:即使有AST感知分块,重要上下文可能被分割到不同块中。
  • 精确度与召回率的权衡:语义相似性优化可能返回“相关”但不相关的代码,给LLM带来噪音。

评估设置

评估生成式AI审查者不同于评估分类器或搜索引擎。代码审查没有规范的正确答案——两位专家工程师审查同一MR会写出不同但同样有效的反馈。因此,标准准确率指标不适用。质量是多维度的:审查可能识别正确的风险但夸大严重性,可能完美校准但错过关键问题,或发现正确问题但指向错误代码。

我们构建了一个包含79个真实MR的金标准数据集,每个条目由专家注释了多维度地面真相:预期摘要点、预期问题、预期内联评论(与特定文件和代码行绑定)、预期分数范围。这些MR代表真实的生产复杂性,涵盖不同代码库、更改大小和类型。

所有四种方法在同一79个条目上进行评估,每个生成的审查在五个核心质量维度上评分:

  • 覆盖率:输出中包含多少预期摘要点、问题和内联评论?
  • 精确度:审查者发现的内容中有多少是真正必要的?这惩罚过度生成。
  • 内联评论位置准确性:代码审查本质上是空间性的;问题需要附加到正确的文件和相关的代码更改。
  • 分数校准:审查者分配的严重性分数(0-10)是否在人类认为合理的范围内?
  • 结构有效性:模式检查确保必需字段存在且格式良好。所有方法在此维度上均通过。

对于细微标准,如摘要是否“覆盖相同点”,文本匹配失败。我们使用LLM作为自动法官,根据参考期望和生成输出计数预期点的命中数。

评估在MLflow和Databricks上运行,包括数据准备、比较和迭代优化。

结果与结论

GKG在覆盖率和精确度上明显优于RAG。GKG集成使审查者能够查询知识图谱,验证初步关注点,产生更准确、一致的反馈,并显著减少误报。RAG在语义相似性任务上表现良好,但不足以满足代码审查的结构化需求。结合GKG和RAG并未显著优于单独使用GKG,表明GKG提供了审查所需的核心上下文。

这一架构决策使我们的AI代码审查工具从依赖表面语义转变为理解代码库的真实结构,从而为开发人员提供更可靠、更值得信赖的审查反馈。