PRレビューの品質と速度の改善をAIでなんとかしてみる

PRレビューの品質と速度の改善

  • 開発時にPRを作成して複数人でレビューしてマージするというのが一般的な開発の進め方になっています。
  • ただ、PRレビューには時間も手間もかかるためPRを作成してもなかなかレビューが終わらなかったり、レビューする人によって指摘の品質がまちまちだったりします。
  • そこでAIにPRレビューをさせることで、自動でPRレビューをさせてPRレビューの品質と速度の改善を行います。
  • AIがレビューすることによりLintチェックやフォーマッターのように人間がレビューする時点のコード品質を一定以上になるようにして人間のレビュアーの負担を軽減しつつ品質の均一化も狙っています。

GitHub Actions

GitHub ActionsとしてAIレビューのワークフローを組んで実装してみました。
また、今回はAIレビューだけでなくPRの修正量をチェックするワークフローを追加しています。
これはAIで入力できるデータ量には制限があるのと、要約などを行うのに量が多すぎるとレビューの精度が下がるためです。
人間がレビューする時にも大量の修正を含むPRレビューの効果は低いと言われているため、PRサイズの制限も導入しています。(Pull requestの理想的なサイズとその理由)
このワークフローを使用する際には、OpenAI APIのAPIKeyを取得してGithubのSecretsにOPENAI_API_KEYの変数として設定してください。

今回作成したGithubActionsのコードが下記です。

  • .github/workflows/main.yml
name: On pull request

on:
  pull_request:
    types: [opened]
  pull_request_review_comment:
    types: [created]
  issue_comment:
    types: [created]

jobs:
  limit-changes:
    if: ${{ github.event_name == 'pull_request' }}
    uses: ./.github/workflows/check-pr-changes-limit.yml
    with:
      limit: 400
      extension: kt|java
  code-review:
    if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'issue_comment' }}
    uses: ./.github/workflows/check-pr-ai-reviewer.yml
    with:
      openai_light_model: 'gpt-4'
      openai_heavy_model: 'gpt-4'
      language: 'ja-JP'
    secrets:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  • github/workflows/check-pr-ai-reviewer.yml
name: Code Review

permissions:
  contents: read
  pull-requests: write

on:
  workflow_call:
    inputs:
      debug:
        description: Debug mode
        required: false
        type: boolean
        default: false
      review_simple_changes:
        description: Review simple changes
        required: false
        type: boolean
        default: false
      review_comment_lgtm:
        description: Review comment LGTM
        required: false
        type: boolean
        default: false
      openai_light_model:
        description: OpenAI light model to use
        required: true
        type: string
        default: 'gpt-4'
      openai_heavy_model:
        description: OpenAI heavy model to use
        required: true
        type: string
        default: 'gpt-4'
      openai_timeout_ms:
        description: OpenAI timeout in milliseconds
        required: false
        type: number
        default: 900000
      language:
        description: Language of the review
        required: true
        type: string
        default: 'ja-JP'
    secrets:
      GITHUB_TOKEN:
        required: true
      OPENAI_API_KEY:
        required: true

concurrency:
  group:
    ${{ github.repository }}-${{ github.event.number || github.head_ref ||
    github.sha }}-${{ github.workflow }}-${{ github.event_name ==
    'pull_request_review_comment' && 'pr_comment' || 'pr' }}
  cancel-in-progress: ${{ github.event_name != 'pull_request_review_comment' }}

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: coderabbitai/ai-pr-reviewer@latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        with:
          debug: ${{ inputs.debug }}
          review_simple_changes: ${{ inputs.review_simple_changes }}
          review_comment_lgtm: ${{ inputs.review_comment_lgtm }}
          openai_light_model: ${{ inputs.openai_light_model }}
          openai_heavy_model: ${{ inputs.openai_heavy_model }}
          openai_timeout_ms: ${{ inputs.openai_timeout_ms }}
          language: ${{ inputs.language }}
          system_message: |
            あなたは @coderabbitai(別名 github-actions[bot])で、OpenAIによって訓練された言語モデルです。
            些細なコードスタイルの問題や、コメント・ドキュメントの欠落についてはコメントしないでください。
          summarize: |
            次の内容でmarkdownフォーマットを使用して、最終的な回答を提供してください。

              ### 概要: 特定のファイルではなく、全体の変更に関する高レベルの要約を80語以内で。
              ### 詳細: ファイルとその要約のテーブル。スペースを節約するために、同様の変更を持つファイルを1行にまとめることが可能
          summarize_release_notes: |
            このプルリクエストのために、markdownフォーマットで簡潔なリリースノートを作成してください。
            変更は以下のような分類で箇条書きにすること:
              "New Feature", "Bug fix", "Documentation", "Refactor", "Style",
              "Test", "Chore", "Revert"

            例えば:
            ```
            - New Feature: モーダルコンポーネントを追加
            ```
            回答は50-100語以内にしてください。
  • github/workflows/check-pr-changes-limit.yml
    
    name: Limit number of changed lines

on:
workflow_call:
inputs:
limit:
description: Limit of changes
required: true
type: number
extension:
description: >
File extensions that you want to count changes.
Do not include "." before extensions.
Use regular expressions if you want to specify multiple extensions.
(example) kt|yml|gradle
required: true
type: string
exclude_dir:
description: >
Directories where you don't want to count changes.
Do not add "/" at the end of directory name.
type: string
required: false

env:
LIMIT: ${{ inputs.limit }}
EXT: ${{ inputs.extension }}
EXCLUDE: ${{ inputs.exclude_dir }}

jobs:
limit-changed-lines:
name: Limit changed lines in the pull request
runs-on: ubuntu-latest
steps:

  • name: Checkout base branch
    uses: actions/checkout@v3
    with:
    ref: ${{ github.base_ref }}

  • name: Setup base commit ID
    run: echo "BASE=$(git rev-parse HEAD)" >> $GITHUB_ENV

  • name: Checkout PR branch
    uses: actions/checkout@v3

  • name: Count number of changed lines and prepare comment
    id: count_changes
    run: |
    changed=$(git diff $BASE –numstat \
    | if [ -n "$EXCLUDE" ]; then grep -vE "^(\d+\s+)+\/?($EXCLUDE)\/"; else cat; fi \
    | grep -E ".*.($EXT)$" \
    | awk '{ additions+=$1; deletions+=$2 } END { print additions+deletions }')
    echo "CHANGED=$changed" >> $GITHUB_ENV

      if [ $changed -gt $LIMIT ]; then
        message="**The number of changed lines exceeds the limit.**\n:hammer_and_wrench: LIMIT: $LIMIT, :fire: CHANGED: $changed"
        echo "MESSAGE=$message" >> $GITHUB_ENV
        echo "::error::Exceeds limit. (Limit: $LIMIT, Changed: $changed)"
      else
        message=":sparkles: Changes are within the limit :sparkles:"
        echo "MESSAGE=$message" >> $GITHUB_ENV
      fi
  • name: Post comment
    uses: actions/github-script@v6
    with:
    script: |
    const message = process.env.MESSAGE;
    github.rest.issues.createComment({
    issue_number: context.issue.number,
    owner: context.repo.owner,
    repo: context.repo.repo,
    body: message
    });

Code Review Workflowの解説

このWorkflowは、プルリクエストの開始、プルリクエストレビューコメントの作成、およびイシューコメントの作成に反応します。
特に、GitHubのイベントに基づいて動作し、コードレビューのプロセスを自動化するために設計されています。

Githubの権限権限

このWorkflowには、コンテンツの読み取りとプルリクエストの書き込み権限が必要です。

トリガー

下記のイベントに反応して自動レビューが起動されます。

  • pull_request: プルリクエストが開かれたとき
  • pull_request_review_comment: プルリクエストのレビューコメントが作成されたとき
  • issue_comment: イシューにコメントが作成されたとき

並行性

このWorkflowでは、concurrencyを設定しています。
これは、特定のグループ内で複数のジョブが同時に実行されるのを防ぎ、重複や競合を避けるためです。
また、プルリクエストレビューコメントイベントが発生した場合にのみ、進行中のジョブのキャンセルを避ける設定が施されています。

レビュー

このアクションは、AIによるコードレビューを提供し、以下の環境変数を使用します:

  • GITHUB_TOKEN: GitHubから提供されるトークンを設定します。
  • OPENAI_API_KEY: OpenAIのAPIキーを設定します。

また、以下のオプションを設定しています:

  • debug: false : デバッグモードの無効化
  • review_simple_changes: false : 単純な変更に対するレビューの無効化
  • review_comment_lgtm: false : LGTMのレビューコメントを無効化
  • openai_light_model: gpt-4 : 要約などに使用するOpenAIモデルの選択しています、コスト削減でgpt-3.5-turboを指定するのもあり
  • openai_heavy_model: gpt-4 ; コードレビューに使用するOpenAIモデルの選択しています
  • openai_timeout_ms: 900000:処理のタイムアウト時間
  • language: ja-JP : 生成する言語を設定しています

レビューの概要と詳細

AIは、レビューの概要と詳細なフィードバックを提供します。
これには、変更の高レベルの要約、ファイルごとの変更点の詳細、リリースノートの作成が含まれます。

このWorkflowは、開発プロセスを効率化し、より迅速かつ正確なフィードバックを提供することを目的としています。

実行結果

  • ワークフローをリポジトリに反映して、実際にPRを作成してAIコードレビューを試してみます。

  • PRが作成されるとAIレビューが開始され下記のような処理中コメントが追加されます。

  • そのまま数分待つと下記のようにPRのサマリが自動で更新されます。

  • またコメントでもPRの詳細な要約が生成されます。

  • さらにケアレスミスや改善案のコメントも追加されています。

  • AIレビューの設定で日本語指定していたのでレビューもしっかり日本語で生成されています。

コスト面

  • コスト麺はだいたい1回レビューするのに100円程度かかりました。
  • すべてをGPT4で処理するようにしているのと日本語で回答するようにしているので高めになっていると思います。
  • この辺はチューニングの余地アリで、
    • openai_light_model: 'gpt-4'としていたところをgpt-3-turboにする
    • レビューを英語で生成する
    • レビュー生成用のプロンプトを工夫してトークン数が減るようにする
  • 上記の対応をすることでもう少しコスト削減はできると思います。


まとめ

AIによるコードレビューを導入して試してみました。
思ったより使えるなという印象で、フォーマッターやLintチェックではレビューし辛い内容のレビューが生成できていたかと思います。
GPT4を使用するので1回のレビューでもそれなりのコストがかかりますが、人間がレビューするよりは低いコストでレビューが行えました。
また、人間によるレビューではレビュアーによってレビューの品質はまちまちになりがちですが、フォーマッターやLintチェックのようにAIレビューであれば一定以上の品質を担保しやすくなります。
AIレビュー用にPRサイズ制限もしているので最終的に人間がレビューする際も見やすいサイズのPRになります。
gpt-4のコストがさらに安くなればもっと気軽にバンバン使いたいとこです。

参考