[已开源]: 使用 ChatGpt 自动 Review 代码并生成 Github 评论

1,988 阅读5分钟

大家好,我是韩数,最近业余时间写了一个开源小工具 ChatGPT CodeReview, 目前已经开源至Github。

项目地址: github.com/hanshuaikan… (跪求各位客官姥爷Star一下)

image.png

由于我自己也基本上是 ChatGPT 的重度用户,因此自己在 github 上去开源一些代码的时候也不免会发给 AI 帮忙提出一些评审建议,有的评审建议还是比较有用的,于是就想到能不能使用 ChatGPT 去自动生成 github 的 review 评论呢,我期望的效果大概是这样:

image.png

说干就干,当然,今天这篇文章并不是主要推广我的这个小工具, 这个小工具更多的是验证了自己想法的可行性。

ChatGPT CodeReview 介绍

ChatGPT Code Review 是一个用Golang 编写的 AI Code Review 工具,它可以利用 AI 自动完成某个Github PR 的 Review 并 comment 到对应的代码段下。

目前主要提供了命令行和代码集成两个使用, 开发者可以通过实现对应的接口,自定义相关AI(文心一言) 和 集成(gitlab, 码云) 的实现。 默认集成了 ChatGPT 3.5 和 github 的实现。

package main

import (
    "context"
    "github.com/hanshuaikang/chatgpt-codereview/pkg"
    "github.com/hanshuaikang/chatgpt-codereview/pkg/chatgpt"
    "github.com/hanshuaikang/chatgpt-codereview/pkg/github"
    "os"
)

func main() {
    config := pkg.Config{
       Owner:  "",
       Repo:   "",
       Pr:     0,
       Prompt: "",
       ApiKey: "",
       Token:  "",
    }
    
    defaultGptCli := chatgpt.NewGptClient(config)
    githubCli := github.NewGithubCli(config.Token, config.Owner, config.Repo, config.Pr)
    runner := pkg.NewCodeReviewRunner(&config, githubCli, defaultGptCli)

    ctx := context.Background()
    err := runner.RunCodeReview(ctx)
    if err != nil {
       os.Exit(1)
    }
}

详细的实现方案

流程图

image.png

其实用 AI 进行 CodeReview 的思路并不复杂,网上的方案基本上都是通过 githu b的接口获取到某个 PR 的Diff, 并解析 AI 返回的结果,但是经过实操,发现这样准确率实在堪忧, 原因主要是因为 diff 拿到的是并不完整的代码段, AI 很难基于一个不完整的代码段给出什么精确的建议, 基于这个方案我发现 AI 很容易胡说八道, 比如提醒你的代码少了个括号什么的。

于是我换了一种思路,整个文件进行CodeReview, 只截取变动部分的评论,接下来是主要实现思路

取一个好的 prompt 很重要

在我的开源项目 chatgpt-codereview 中, 这个prompt 是这样的:

角色:你是一个非常超级高级的Golang工程师。

任务:请帮我review以下代码,重点审查是否存在非常严重的bug或代码逻辑错误。
只有在遇到[代码漏洞,逻辑漏洞,拼写错误,性能问题]等关键问题时才提出修改建议。请忽略补充日志、注释、优化错误,缺少结构体定义信息等常规性建议。

要求:

审查标准:严格遵循以下几点:
只提出关键性问题包括,代码漏洞、逻辑漏洞、拼写错误、性能问题。
建议格式:所有建议应按照“[行号] 建议内容”的格式提出,以便清晰地识别问题所在的具体位置。
返回内容限制:只返回有问题的行,不要返回任何标记为“无需修改”的行。

当然有这些还远远不够, 此时我们需要强制 AI 返回固定格式的数据, 最好能告诉我们哪行代码有需要改进的地方, 我们需要在末尾强制指定返回格式, 如果使用 chatgpt-codereview, 会自动在你自定义的 prompt 后添加该 prompt 进行补充。

You must return it in this format, like [25] if err ! = nil { . instead of [Line 25] if err ! = nil

获取 github 某个 pr 的 commit 信息

这一步主要需要解析 github 的 pr 下每个 commit 的 git diff 信息, 大概长这样:

@@ -34,24 +34,14 @@ func (e syntaxExecutor) parseOutPut(path string, output string) (int, map[string
 
 func (e syntaxExecutor) runVet(path string) (int, map[string]interface{}) {
 
-       isDir, err := isDirectory(path)
-       if err != nil {
-               return 0, nil
-       }
-
-       pathArgs := "./..."
-       if !isDir {
-               pathArgs = path
-       }
-
        var out bytes.Buffer
        var errOut bytes.Buffer
 
-       cmd := exec.Command("go", "vet", pathArgs)
+       cmd := exec.Command("go", "vet", "./...")
        cmd.Stdout = &out
        cmd.Stderr = &errOut
 
-       err = cmd.Run()
+       err := cmd.Run()
        // go vet 命令如果 err, 则说明有语法错误
        count, detail := e.parseOutPut(path, errOut.String())
        if err != nil {

解析该信息,我们可以知道最终变化的部分在本次 commit 的 34 - 47 行, 也就是我们本次 commit 的范围,但是这样还不够。 把这段代码直接交给 AI 去 Review 是完全行不通的。 因为如果我们想要生成 comment 信息,我们必须知道两个信息 文件和要comment的行号

获取 commit 该文件的全部内容,并按照行号编号。

由于给代码段会导致 AI 胡说八道, 因此会将需要 review 的文件的全部内容,编号发给 AI, 以获取更准确的结果。 为文件生成编号:

1 package engine
2 
3 import (
4       "bytes"
5       "os"
6       "os/exec"
7       "path/filepath"
8       "regexp"
9       "strings"
10 )
11 
12 type syntaxExecutor struct {
13 }
.....

将 prompt 和 处理过的文件 发送给 AI 进行 CodeReview。

剩下的工作就十分简单了,将 prompt 和 处理过的文件 发送给 AI 进行 CodeReview, 并获取到对应的结果, 进行解析, 解析完毕之后将内容转换成 github 的 comment。 调用 github 的 API 把 code reivew 结果提交。

总结

到这里, 已经把整个思路讲清楚了,由于 golang 并没有很好的实现, 市面上的开源项目也很少把详细的思路写出来, 所以写了今天这篇文章, 最后跪求各位客官点个小小的 star, 就好这口。

项目地址: github.com/hanshuaikan…

image.png

万水千山总是情,给个star行不行

全部笔记已经开源至GitHub

欢迎点赞,关注我,有你好果子吃(滑稽)