[控场AI]
· 7 分钟阅读· 3,679 字

RepoPilot:用AST与依赖图让AI真正读懂代码库

RepoPilot:用AST与依赖图让AI真正读懂代码库

RepoPilot 先用 AST 和依赖图构建仓库结构认知,再让 LLM 基于定向上下文生成问答与测试。

RepoPilot 是一款面向开发者的 AI 工具,旨在解决理解陌生大型代码库的困境。它不把整个仓库塞进 LLM,而是先用确定性代码分析理解仓库架构:通过 AST 解析提取导入导出、函数签名等结构化信息,再构建文件级依赖图,最后从选中文件出发遍历依赖、组装定向上下文,交由 LLM 完成代码库问答与测试生成。系统将代码分析管线与 AI 生成管线彻底分离,上下文构建器与提示词构建器各司其职。作者强调 AI 生成的测试仍需人工审查,私有仓库的凭据管理是产品化关键难点。

面对一个陌生仓库,数百个文件、层层嵌套的依赖、几乎为零的文档——这是每个开发者都熟悉的困境。AI编程助手虽然缓解了部分痛点,但直接把整个仓库塞进LLM提示词既不现实也不高效。RepoPilot 尝试了一条更结构化的路径:先用确定性代码分析理解仓库架构,再让AI基于这份"地图"回答问题、生成测试。

RepoPilot 项目概览

RepoPilot 解决什么问题

RepoPilot 是一款面向开发者的AI工具,核心能力围绕"仓库感知"展开。用户可以直接连接 GitHub、GitLab 或 Bitbucket 仓库(包括通过 OAuth 接入的私有仓库),让系统完成索引与依赖分析,然后进行代码库对话或测试生成。

具体功能包括五项:仓库集成、代码库索引与依赖分析、AI驱动的代码库问答、上下文感知的单元与集成测试生成,以及用自然语言描述业务场景来生成测试用例。

作者明确表示,目标不是取代工程师的判断,而是减少理解代码库和搭建测试基础所需的重复劳动。这一定位值得注意——它把AI放在"降低起步成本"的位置,而非"自动交付正确代码"。

核心工程挑战:上下文不是越多越好

AI辅助开发最大的瓶颈之一是上下文。大型仓库无法整体传入LLM提示词;即便勉强塞进上下文窗口,把每个文件都提供给模型也往往低效——token消耗上升、无关信息涌入,反而让模型难以聚焦于当前讨论的具体实现。

RepoPilot 的解法是让检索基于代码的真实结构关系,而非文本相似度。这引出了三个关键步骤:AST解析、依赖图构建、定向上下文组装。

第一步:用AST解析理解源码结构

RepoPilot 的索引管线针对不同语言采用不同策略:JavaScript 和 TypeScript 使用 TypeScript 编译器生态(ts-morph),Python 使用 Python AST,Java 则采用注释感知的源码扫描。

解析器提取的不是纯文本,而是结构化信息:导入与导出、函数与类、方法签名、源文件之间的关系、相关代码片段。

以一段 TypeScript 代码为例:checkout.ts 从 ./payment.service 导入 PaymentService,并在 checkout 函数中实例化后调用其 process 方法。基于文本的搜索只能识别出类名,而AST方法能可靠地确认导入关系,以及包含该导入的函数结构。这个差别在决定"哪些相关文件应该进入AI请求"时至关重要。

AST(抽象语法树) 是源代码语法结构的树状表示,每个节点对应代码中的一个构造(如函数声明、类、导入语句)。与正则或文本搜索不同,AST 由解析器根据语言文法生成,能准确区分“导入了一个类”和“仅提及了类名”这类语义差别。在 RepoPilot 中,ts-morph 封装了 TypeScript 编译器 API,让开发者能以编程方式遍历和查询 AST;Python 内置的 ast 模块同样将源码转为可检查的节点树。正是借助 AST,系统才能稳定提取导入导出关系、函数签名和文件间引用,为后续构建依赖图提供可靠数据基础。

第二步:构建仓库依赖图

解析完成后,RepoPilot 构建一张表示代码库内部关系的知识图谱。概念上,checkout.ts 通过 import 指向 payment.service.ts,后者又指向 payment.gateway.ts。

有了这张图,当用户请求为 checkout.ts 生成测试时,系统不必提供整个仓库,而是从选中文件出发,遍历相关依赖,收集最可能影响实现的上下文。这张图也为未来的影响面分析和架构探索能力打下基础。

依赖图(Dependency Graph)是一种有向图,节点表示文件或模块,边表示它们之间的引用关系(如 import、require)。与简单的调用图不同,依赖图关注的是源码组织层面的耦合:A 文件导入了 B 文件,即形成一条 A→B 的边。在 RepoPilot 中,这张图由 AST 解析结果自动生成,使得系统可以从任意选中文件出发,沿边遍历其直接和间接依赖,从而圈定“最可能影响当前实现”的文件集合。这种基于图结构的检索比文本相似度更精确,也为后续的影响面分析(修改一个文件会影响哪些模块)提供了可计算的基础。

第三步:为LLM组装上下文

这是作者认为较有意思的架构决策:将代码分析管线与AI生成管线彻底分离。整体流程是:Git仓库 → 仓库解析器 → 依赖图 → 上下文构建器 → 提示词构建器 → LLM提供商 → 生成的测试或AI回复。

上下文构建器负责识别和收集相关代码,提示词构建器则将其转化为任务特定的指令。生成单元测试和回答架构问题所需的提示词结构完全不同,职责分离让系统更易扩展、调试和维护,也让AI提供商可以独立于仓库分析管线演进。

上下文构建器与提示词构建器的分离,本质上是对“检索”与“生成”两个阶段的解耦。上下文构建器只负责根据依赖图和任务类型,从仓库中挑选并裁剪相关代码片段,输出一份结构化的上下文对象;提示词构建器则接收该对象,套用针对具体任务(如生成单元测试、回答架构问题)的模板,转化为最终发给 LLM 的指令。这样做的好处是:更换 LLM 提供商时只需调整提示词构建器,而改进仓库分析逻辑时只需修改上下文构建器,两者互不干扰。同时,不同任务可以复用同一份上下文,只需更换提示词模板,降低了重复检索的成本。

第四步:从真实代码上下文生成测试

测试生成是RepoPilot的主要用例之一。以一个负责校验订单、检查库存、处理支付的函数为例,有用的测试套件需要覆盖的远不止正常路径:有效订单、非法输入、库存不足、支付失败、依赖异常、边界条件。

通过提供相关实现与依赖上下文,RepoPilot 试图生成比"仅含函数名的通用提示词"更有意义的测试建议。作者也坦承,生成的测试仍需开发者审查和执行——AI生成的测试并不自动等于正确性证明,但能显著降低建立初始测试基线的成本。

第五步:用业务语言描述测试场景

RepoPilot 还支持用业务需求的语言描述测试。例如,输入"为支付成功但库存预留失败的结账流程生成测试",系统会结合场景描述与仓库上下文生成相应测试代码。

这对已经明确要验证的行为、但尚未定位所有实现细节的开发者尤其有用,也在业务层需求与源码实际行为之间搭了一座桥。

业务语言驱动的测试生成与传统的“指定文件生成测试”在输入形态上有本质区别:前者输入的是自然语言描述的行为场景(如“支付成功但库存预留失败”),系统需要先将场景中的关键词映射到仓库中的相关实体(支付服务、库存服务、结账流程),再结合依赖图定位实现代码,最后生成覆盖该场景的测试。这要求系统具备一定的语义理解与实体链接能力,而不仅仅是代码结构分析。对开发者而言,这种方式降低了从需求文档到测试代码的转换成本,尤其适合在实现细节尚未完全确定时先搭出测试骨架。

技术栈与架构取舍

前端采用 Vue 3 与 TypeScript,后端为 Python 与 FastAPI,代码分析依赖 TypeScript Compiler API / ts-morph,仓库智能由依赖图与上下文构建器承担,AI集成做了LLM服务的提供商抽象,认证使用 Auth0,源码集成覆盖 GitHub、GitLab、Bitbucket,后台处理采用异步任务与worker。

架构上的核心诉求是模块化——作者不希望仓库解析器、上下文构建器和LLM集成合并成一个难以维护的大服务。

构建过程中的五条经验

第一,更好的上下文与更好的提示词同等重要。提示词工程有用,但准确定位正确的源文件与关系同样关键。

第二,确定性代码分析与生成式AI是互补关系。AST提供结构信息,LLM帮助解释信息并生成有用输出,两者无需互相替代。

第三,AI生成的测试必须验证。生成代码可能包含错误假设、不完整的mock或无效断言,人工审查与执行不可或缺。

第四,私有仓库让安全问题更复杂。仓库凭据需要谨慎处理:范围受限的访问、安全存储,以及防止在日志或提示词中意外泄露。

第五,做产品不同于做原型。认证、配额、故障处理、部署、可观测性和用户引导,很快会变得与AI功能本身同样重要。

后续方向

RepoPilot 仍在演进。作者列出的探索方向包括:面向大型仓库的更好语义检索、更高级的依赖与影响面分析、测试质量评估、对更多语言与框架的支持,以及更智能的仓库感知开发工作流。

生产环境的预览执行被刻意排除在初始公开版本之外,作者希望先验证安全、基础设施与运维要求。当前的焦点仍是仓库智能与AI辅助测试。

#RepoPilot#AI代码库理解#AST解析#依赖图#AI测试生成#上下文工程#LLM代码分析#开发者工具
分享:

相关推荐