如何将代码库转化为知识图谱

如果你正在为软件工程构建AI工具,不要仅仅索引你的仓库。映射它。结果可能会出奇地强大。

如何将代码库转化为知识图谱
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

六个月前,我们的AI编码助手遇到了一个问题。

问它:

"如果我修改支付工作流,哪些服务会受到影响?"

它会自信地返回一个只有部分正确的答案。

问题不在于LLM。

问题在于上下文。

我们的仓库包含超过50万行代码,分布在API、微服务、事件消费者、定时任务、共享库和基础设施代码中。

传统的向量搜索可以找到相关文件。

但它难以理解所有内容是如何连接的。

所以我们将代码库从文档集合转变为知识图谱。

结果是一个AI系统,可以回答架构问题、执行影响分析、识别隐藏依赖关系,并帮助工程师更准确地导航大型代码库。

以下是我们构建它的方法。

1、为什么AI在大型代码库中挣扎

大多数AI编码工具严重依赖语义搜索。

过程大致如下:

用户问题
      ↓
向量搜索
      ↓
相关文件
      ↓
    LLM
      ↓
    答案

这对于小型项目效果出奇地好。

然而,大型企业系统是不同的。

想象以下依赖链:

UserService
    ↓
BillingService
    ↓
PaymentGateway
    ↓
StripeAdapter

现在问:

如果StripeAdapter更改会破坏什么?

向量搜索可能会检索:

StripeAdapter.ts
PaymentGateway.ts

但它可能会完全错过:

BillingService
UserService
OrderProcessing
RefundWorkflow

问题是向量搜索理解相似性。

它不自然地理解关系。

软件系统是由关系构建的。

函数调用函数。

类继承类。

服务发布事件。

消费者订阅事件。

模块导入模块。

没有这些关系,AI看到的是代码片段而不是架构。

2、将代码视为图

一旦我们退后一步,解决方案就变得显而易见。

代码库自然是一个图。

Function
    │
 calls
    ▼
Function

Class
    │
inherits
    ▼
Class

Service
    │
publishes
    ▼
Event

Consumer
    │
subscribes
    ▼
Event

每个节点代表有意义的内容。

示例:

File
Class
Method
Function
Service
Event
Database Table
API Endpoint
Queue

每条边代表一种关系。

示例:

CALLS
IMPORTS
INHERITS
PUBLISHES
SUBSCRIBES
READS
WRITES
DEPENDS_ON

一旦你以这种方式对仓库建模,回答架构问题就变得容易得多。

3、从仓库中提取结构

第一步是构建一个解析器。

对于TypeScript服务,我们使用ts-morph遍历抽象语法树(AST)。

import { Project } from "ts-morph";

const project = new Project();
project.addSourceFilesAtPaths("src/**/*.ts");
for (const file of project.getSourceFiles()) {
  const imports = file.getImportDeclarations();
  imports.forEach((imp) => {
    console.log({
      source: file.getBaseName(),
      target: imp.getModuleSpecifierValue(),
      relation: "IMPORTS"
    });
  });
}

这给了我们这样的关系:

UserService
    IMPORTS
BillingService
    IMPORTS
PaymentGateway

接下来,我们提取:

  • 函数调用
  • 类继承
  • 接口实现
  • 事件发布者
  • 事件订阅者
  • API路由
  • 数据库交互

目标不是索引代码。

目标是映射关系。

4、构建知识图谱

提取关系后,我们将它们存储在Neo4j中。

示例:

CREATE
(a:Service {name: "UserService"})
-[:DEPENDS_ON]->
(b:Service {name: "BillingService"})

另一个示例:

CREATE
(a:Service {name: "BillingService"})
-[:CALLS]->
(b:Service {name: "PaymentGateway"})

图很快开始增长。

我们不再有数千个孤立的文件,而是拥有:

20,000+ 节点
85,000+ 关系

这就是事情变得有趣的地方。

5、影响分析变得简单

以前,回答这个问题很痛苦:

如果我修改PaymentGateway会破坏什么?

工程师会手动检查:

  • 导入
  • 引用
  • 服务调用
  • 事件消费者
  • 文档

有时需要几个小时。

有了图:

MATCH p=(n)-[*]->(m)
WHERE n.name = "PaymentGateway"
RETURN p

答案立即出现。

图显示了每个下游依赖关系。

这成为我们构建的最有价值的能力之一。

6、将图搜索与LLM结合

图本身很有用。

真正的突破发生在我们将它连接到LLM时。

架构:

用户问题
       ↓
图查询
       ↓
相关子图
       ↓
代码检索
       ↓
LLM
       ↓
答案

假设开发者问:

哪些服务受支付重试影响?

工作流程变为:

步骤1:识别实体。

payment retries
PaymentService
RetryProcessor

步骤2:查询图关系。

PaymentService
    ↓
RetryProcessor
    ↓
NotificationService
    ↓
BillingService

步骤3:检索实际代码。

payment.service.ts
retry.processor.ts
billing.service.ts

步骤4:仅将相关上下文发送给LLM。

我们不是将数百个文件转储到上下文窗口中,而是提供仓库的高度连接子集。

答案质量显著提高。

7、最让我们惊讶的是

我们最初构建图是为了帮助AI。

出乎意料的是,人类开始比AI更频繁地使用它。

工程师开始问这样的问题:

  • 哪些服务依赖于此API?
  • 什么事件触发此工作流?
  • 什么消费此队列?
  • 哪些模块紧密耦合?
  • 如果我们删除此服务会破坏什么?

以前这些答案需要部落知识。

现在可以查询它们。

图成为了一个活的架构图。

8、超越RAG

大多数试图改进AI编码助手的团队专注于更好的检索。

更多的嵌入。

更多的向量数据库。

更多的分块策略。

这些改进有所帮助。

但它们没有解决核心问题。

软件系统不是文档集合。

它们是关系网络。

知识图谱明确地捕获了这些关系。

这允许AI系统推理架构,而不仅仅是搜索文本。

9、实际应用

一旦图存在,我们发现了数十个用例。

  • 影响分析。识别代码更改影响的所有内容。
  • AI代码审查。为审查者提供依赖感知的上下文。
  • 迁移规划。了解跨服务的升级影响。
  • 开发者入职。帮助新工程师理解系统架构。
  • 技术债务发现。识别紧密耦合的组件。
  • 架构文档。直接从图生成图表。

10、结束语

今天大多数AI编码助手擅长理解文件。

很少有真正理解系统的。

这是一个重要的区别。

仓库不仅仅是源代码。

它是依赖关系、工作流、服务、事件和业务逻辑的网络。

一旦我们将这些关系建模为知识图谱,工程师和AI系统都显著提高了导航代码库的能力。

最大的教训很简单:

AI不仅需要更多上下文。它需要更好的上下文。

在大型代码库中,关系通常比代码本身更有价值。

如果你正在为软件工程构建AI工具,不要仅仅索引你的仓库。映射它。结果可能会出奇地强大。


原文链接: How We Turned a 500K-Line Codebase Into an AI Knowledge Graph

汇智网翻译整理,转载请标明出处