跳到主要内容
代码理解引擎

让我们的智能体看见整个代码仓库的引擎

这一个是给我们自己做的。以前,一个被要求做代码评审或重构的智能体只能看到打开的那个文件,别的所剩无几,于是只能一路猜着读下去。这个引擎把代码库解析成语法树和使用关系图,为一次改动排出真正相关的上下文,并回答同时依靠精确匹配与语义的检索请求。如今我们做的每一次评审和每一次重构,起点都是整个仓库的结构,而不是打开的那个文件。

  • 行业开发者工具
  • 合作方式内部引擎,为我们自己的智能体而建
  • 状态在自有工作中使用,并持续扩展
  • 代码理解
  • 混合检索
  • 智能体基础设施
  • 开发者工具

先服务我们自己的智能体

这不是客户项目。它是我们自己工具链里的一环,决定智能体在动手之前能看到什么。它把一个项目索引一次,随着文件保存持续更新索引,并回答关于代码结构的问题:什么定义在哪里、谁在使用它、一个文件引入了什么、又有谁引入了这个文件。

决定一切的约束是推理预算。一个技术上完整却大到读不完的回答,不如一个装得下的小回答。所以每个回答都会按固定预算裁剪,同时始终保持结构化输出的有效性;文件返回的是签名而不是完整函数体。目的是把预算花在工作上,而不是花在阅读上。

挑战

问题出在我们自己身上

没有客户提出这个需求。它来自我们看着自己的智能体,带着对大型代码库的错误认识去干活。

打开的文件不等于代码库

被要求改一个函数的智能体,能看到这个函数和它所在的文件。谁调用了它、它又调用了什么、项目的哪些部分会因为这次改动而受影响,全都在画面之外。改动是孤立地想出来的,也是同样孤立地被评审的。

为了找一处而读完整个项目

没有地图,找到正确位置就意味着不断打开文件再丢掉。每个被丢掉的文件,代价都已经从真正工作所需的同一份预算里付掉了;在大仓库里,这份代价在任何有用的思考开始之前就发生了。

只靠一种搜索从来不够

精确的名称搜索会漏掉那些换了名字却在做同一件事的代码。基于语义的搜索又会漏掉刚刚输入的那个确切标识符。二选一的结果,是这个工具每次都以不同的方式出错。

解决方案

我们做了什么

一个理解结构的索引,一层融合三种观察方式的检索,以及对任何一次回答成本的硬性上限。

用结构地图取代一堆文件

引擎把源代码解析成语法树,并取出每个文件里真正声明了什么,于是一个项目可以用它的结构而不是它的文本来描述。

  • 在十种语言中提取函数、类、方法、接口、类型、枚举、结构体、特征与实现
  • 一个文件可以只返回签名而不带函数体,让智能体看见一个自己并没有读过的文件的形状
  • 项目地图带着每个文件的语言、行数与符号数,当目录树大到失去意义时会收拢到顶层

三种观察方式,合成一份排序

关键词搜索、字面名称匹配与语义相似度各自回答不同类型的问题,所以引擎让三者跑在同一个索引上并把结果融合,而不是从中挑一个。

  • 全文关键词搜索、子串名称匹配与向量相似度在每次查询中一起运行
  • 三份排序通过倒数排名融合合并,它比较的是名次而不是彼此无法比较的分数,因此不需要手工调权重
  • 一个符号在整个项目中被引用的广度会轻微影响排序,让真正承重的代码排在无人调用的代码前面

什么依赖它,而不只是它在哪里

除了定义,引擎还记录名称在哪里被使用,包括调用和类型引用,以及哪些文件引入了哪些文件。这把一次查找变成了一个关于后果的问题。

  • 使用关系图在回答这个定义在哪里之外,也回答什么依赖它
  • 引入关系是双向的:一个文件引入了什么,以及项目中大致有哪些文件引入了它
  • 地图旁边还有一扇最近变更的窗口,于是近期改了什么是答案的一部分,而不是另一次查询

有上限的回答,就在代码本来所在的机器上

交付这一侧由两条规则塑造:回答必须装进调用方为它留出的空间,而代码不应该为了被理解而离开这台机器。

  • 每个回答都按固定预算裁剪,同时保持结构化输出有效,因此一次宽泛的查询无法冲垮调用方的上下文
  • 整个索引就是开发者自己机器上的单个数据库文件,向量检索在同一进程内运行,没有任何代码被上传到任何地方
  • 一次保存会被文件监视捕捉并在半秒内重新索引,而内容没有变化的文件会凭哈希被识别并直接跳过
成效

我们的工作发生了什么变化

这是一个内部工具,所以诚实的说法只能是它改变了我们的工作方式。它背后没有基准测试,我们也不会拿出一个。

整个仓库

评审从哪里开始

一次评审或重构从项目的结构开始:这次改动碰到了什么、谁在使用它、谁把它引了进来。打开的那个文件,不再是可以思考的边界。

预算花在工作上

推理用在哪里

用签名代替函数体,把回答裁剪到装得下,意味着更少的可用上下文被花在最终无关的文件上。收益在于那些根本不必读的部分。

不离开这台机器

我们为什么敢把它对准客户代码

开发者自己机器上的一个索引文件,检索在同一进程内运行。凭据文件、私钥和证书相关文件不会被索引,索引本身也只有它的属主能读。

最后审阅:

你们的工具看见的是整个代码库,还是只有打开的那个文件?

告诉我们你们的团队今天如何面对一个大型仓库,上下文又在哪里用尽。我们会告诉你什么值得做,什么不值得。

聊聊你的项目