最近我们团队接手了一个祖传项目,代码质量之差令人发指。

一个函数写了八百行,变量名全是a、b、c,注释比代码还少,复制粘贴的代码到处都是。更可怕的是,这个项目还在运行,不能随便动,一动就出bug。

我们尝试用AI来辅助重构,但直接让大模型改代码效果很差,因为它不了解项目的上下文,改出来的代码经常和其他部分冲突。后来我们引入了RAG(检索增强生成)技术,让AI在改代码的时候能检索到项目的相关代码和文档,效果好了很多。

这篇文章,我想分享一下用RAG辅助代码重构的实战经验。

什么是RAG

先简单介绍一下RAG。

RAG全称是Retrieval-Augmented Generation,检索增强生成。它的核心思想是:在让大模型生成回答之前,先从知识库中检索相关的信息,然后把这些信息作为上下文一起传给大模型,让大模型基于这些信息来生成回答。

为什么要用RAG?因为大模型有几个局限:

第一,知识有截止日期。大模型的训练数据有截止时间,它不知道截止之后的事情。比如我们项目最新的代码、最新的文档,大模型是不知道的。

第二,不了解特定项目。大模型学的是通用知识,它不知道我们项目的代码规范、架构设计、业务逻辑。直接让它改代码,它只能凭通用经验来改,很容易不符合项目的实际情况。

第三,容易产生幻觉。大模型有时候会一本正经地胡说八道,编造不存在的函数、不存在的API。在代码重构中,这是非常危险的。

RAG就是用来解决这些问题的。我们把项目的代码、文档、规范都放进知识库,大模型在改代码的时候,先检索相关的内容,然后基于这些内容来生成。这样改出来的代码,就会更符合项目的实际情况,也更准确。

我们的代码重构痛点

在介绍RAG方案之前,先说说我们这个项目的痛点。

第一个痛点是,代码量大。整个项目有几十万行代码,几十个模块,几百个文件。要全部重构,靠人工几乎不可能,时间和人力都不够。

第二个痛点是,缺乏文档。这个项目换了好几拨人,文档早就跟不上代码了。很多业务逻辑,只有代码里有,而且代码写得很烂,要看懂都要花很长时间。

第三个痛点是,测试缺失。项目几乎没有单元测试,重构之后有没有改坏,全靠人工测试。每次改代码都提心吊胆,生怕改出bug。

第四个痛点是,规范不统一。不同时期、不同人写的代码,风格完全不一样。有的用面向对象,有的用面向过程;有的有注释,有的没注释;变量命名有的用英文,有的用拼音。

这些痛点加在一起,导致重构工作进展非常缓慢。我们一开始尝试人工重构,改了一个星期,才改了两个文件,效率太低了。

于是我们想到了用AI来辅助。但直接用大模型效果不好,就引入了RAG。

RAG代码重构方案

我们的RAG代码重构方案,分为几个步骤。

第一步是,构建代码知识库。我们把整个项目的代码都处理一遍,建立索引。

具体做法是:

  1. 把代码按文件、按类、按函数切分成小块。每个小块包含完整的上下文(比如函数的完整代码、所属的类和文件)。
  2. 给每个代码块生成摘要和向量。摘要用大模型生成,描述这个代码块的功能;向量用embedding模型生成,用于语义检索。
  3. 把代码块、摘要、向量存入向量数据库。我们用的是Milvus,也可以用Chroma、Pinecone等。

除了代码,我们还把项目的文档、代码规范、历史commit信息、issue记录等也放进了知识库。这些信息对代码重构也很有帮助。

第二步是,检索相关代码。当我们要重构某个函数或者某个文件的时候,先从知识库中检索相关的代码。

检索的方式有几种:

  1. 语义检索:用要重构的代码的描述作为query,检索语义相似的代码。比如要重构用户登录功能,就检索和用户、登录、认证相关的代码。
  2. 结构检索:根据代码的调用关系、继承关系,检索相关的代码。比如要重构某个函数,就检索调用这个函数的地方、这个函数调用的其他函数。
  3. 关键词检索:用关键词检索相关的代码和文档。

我们把这几种检索方式结合起来,取并集,然后去重、排序,取最相关的前N个代码块。

第三步是,构建Prompt。把检索到的相关代码、要重构的代码、重构要求,一起组装成Prompt,传给大模型。

Prompt的结构大概是这样的:

  • 角色设定:你是一个资深的代码重构专家,熟悉我们项目的技术栈和代码规范。
  • 项目上下文:检索到的相关代码和文档。
  • 重构目标:要重构的代码,以及重构的要求(比如提高可读性、消除重复、遵循项目规范等)。
  • 输出要求:输出重构后的完整代码,以及重构说明(改了什么、为什么这么改)。

第四步是,生成和验证。大模型生成重构后的代码,然后我们做验证。

验证包括:

  1. 语法检查:确保代码没有语法错误。
  2. 静态检查:用lint工具检查代码风格和潜在问题。
  3. 人工审查:开发人员审查代码,确保逻辑正确、符合项目规范。
  4. 测试:运行项目的测试(虽然少,但还是有一些),确保没有改坏。

验证通过的代码,才会提交。不通过的,反馈给大模型,让它重新改。

实战效果

用了RAG之后,代码重构的效率和质量都有了明显提升。

效率方面,之前人工重构一个文件要一天,现在用RAG辅助,半天就能改好两三个文件。大模型负责初稿,开发人员负责审查和调整,效率提高了好几倍。

质量方面,重构后的代码可读性明显提高,重复代码减少了,命名更规范了,注释也补上了。而且因为有项目上下文,大模型改出来的代码更符合项目的实际情况,不会出现和其他模块冲突的情况。

举个例子,我们重构了一个八百行的函数。大模型先检索了这个函数调用的其他函数、调用这个函数的地方、相关的业务文档,然后把这个函数拆成了八个小函数,每个函数职责单一,命名清晰,还加了注释。开发人员审查之后,只做了少量调整就通过了。如果是人工重构,这个函数至少要改两天。

还有一个例子,项目里有很多复制粘贴的代码,人工找起来很费劲。用RAG的语义检索,很快就找到了所有相似的代码块,然后让大模型统一重构,消除了大量重复代码。

遇到的问题和解决方法

当然,RAG代码重构也不是一帆风顺的,我们遇到了不少问题。

第一个问题是,代码切分的粒度。切得太细,上下文不完整,大模型看不懂;切得太粗,检索不精准,还会超出上下文长度。

我们的解决方法是,按函数和类来切分,每个函数或类作为一个块,同时保留所属文件和模块的信息。对于特别长的函数,再按逻辑块切分,但每个块都包含函数签名和上下文说明。

第二个问题是,检索的准确率。有时候检索出来的代码不相关,或者漏掉了重要的相关代码。

我们的解决方法是,混合检索。同时用语义检索、结构检索、关键词检索,取并集。然后用一个重排序模型,对检索结果重新排序,提高准确率。还可以根据反馈不断优化检索策略。

第三个问题是,大模型的幻觉。即使有了RAG,大模型有时候还是会编造不存在的函数或API。

我们的解决方法是,在Prompt里明确要求"只能使用项目中已有的函数和API,不要编造"。生成之后,用静态检查工具验证代码中引用的函数和变量是否存在。如果有不存在的,就反馈给大模型重新生成。

第四个问题是,上下文长度。大模型的上下文长度有限,检索到的代码太多就放不下。

我们的解决方法是,控制检索结果的数量,只取最相关的前10到20个代码块。对于特别大的重构任务,分多次进行,每次只重构一部分。还可以用支持更长上下文的大模型。

第五个问题是,代码的安全性。重构过程中,大模型可能会引入安全漏洞,或者不小心改坏了关键逻辑。

我们的解决方法是,人工审查是必须的,不能完全依赖AI。重要的代码,必须由资深开发人员仔细审查。还要有完善的测试和回滚机制,出了问题能快速回滚。

RAG和纯人工、纯AI的对比

我们对比了三种重构方式:纯人工、纯AI(不用RAG)、RAG+AI。

纯人工的优点是质量高、可控,缺点是效率低、成本高。适合小范围、高风险的重构。

纯AI的优点是效率高,缺点是质量不稳定、容易脱离项目实际、容易产生幻觉。适合简单的、低风险的重构。

RAG+AI的优点是效率较高、质量较好、符合项目实际,缺点是需要搭建RAG系统、有一定的技术门槛。适合中大型项目的批量重构。

我们的经验是,把这三种方式结合起来用。简单的、重复的代码重构,用RAG+AI批量处理;复杂的、核心的代码,人工重构或者RAG+AI辅助人工;AI生成的所有代码,都必须经过人工审查。

这样,既保证了效率,又保证了质量。

一些经验总结

用RAG做代码重构,我总结了一些经验。

第一,知识库的质量决定了RAG的效果。知识库要全面、准确、及时更新。代码变了,知识库也要跟着更新。否则,检索到的是过时的信息,反而会误导大模型。

第二,检索的准确率很重要。检索不准,大模型就会基于错误的信息生成代码。要花时间优化检索策略,混合多种检索方式,用重排序提高准确率。

第三,Prompt工程不能忽视。好的Prompt能让大模型生成更好的代码。要明确角色、上下文、目标、输出要求,还要给出项目的代码规范和示例。

第四,人工审查是底线。AI再强,也不能完全替代人。尤其是代码重构,涉及到业务逻辑和系统稳定性,必须有人工审查。AI是助手,不是替代者。

第五,循序渐进。不要一开始就想重构整个项目。先从一个模块、一个文件开始,验证方案的可行性,积累经验,然后再逐步扩大范围。

第六,建立反馈闭环。AI生成的代码,人工审查之后,把审查意见反馈回去,不断优化Prompt和检索策略。用得越多,效果越好。

写在最后

代码重构是每个程序员都会遇到的事情。祖传代码、烂代码,是很多人的噩梦。

RAG+AI给代码重构带来了新的可能。它不能完全替代人工,但能大大提高重构的效率和质量。让我们从繁琐的重复劳动中解放出来,把精力放在更有价值的架构设计和业务逻辑上。

当然,RAG代码重构还在发展中,还有很多问题需要解决。但我相信,随着技术的进步,AI辅助代码重构会越来越成熟,成为程序员的得力助手。

如果你也在为烂代码头疼,不妨试试RAG+AI的方式。也许会给你带来惊喜。