はじめに
皆様ChatGPTに実装したコードを渡してレビューさせたことはありますでしょうか。
意外と実装の間違いや改善点を提案してくれて重宝しますよね。
今回はPull Request(以降PR)を作成した際に、ChatGPTがレビューした結果がコメントされるようになる手順を紹介します。
またせっかくなのでChatGPTは最新のGPT-4 Turbo
を使います。
結果
対応手順の前にまず結果になります。
わかりやすい間違いを含んだSwiftクラスを用意しPRを作成しました。
class SampleSwiftClass {
let baseNumber = 5
/// baseNumberとnumberを加算して返却する
/// - parameter number: 加算する数値
/// - Returns: 加算後の数値
func plus(number: Int) -> Int {
return self.baseNumber + number
}
func minus(number: Int) -> Int {
return self.baseNumber + number
}
}
PR作成をトリガーにしてレビューが実施され、このように結果がコメントされます。
手順
こちらを実現するための手順を説明します。
1. OpenAI の API Keyを発行する
以下でCreate new secret key
を選択してAPI Keyを発行します。
https://platform.openai.com/api-keys
※発行後はkeyを表示できないので注意
2. OpenAI APIの支払い設定
以下で支払い設定します。初回最低5ドルの支払いが必要です。
https://platform.openai.com/account/billing/overview
また以下で使いすぎや不正利用防止で上限を決めましょう。
https://platform.openai.com/account/limits
3. GitHubリポジトリにOpenAI API keyを設定する
リポジトリのSettings > secrets and variables > ActionsでNew repository secret
を押下して1のAPI keyをOPENAI_API_KEY
という名称で設定します。
4. GitHub Actionsの設定ファイルを作成し配置する
以下を作成しリポジトリの.github/workflows/
に配置します。
name: PR Review Workflow
on:
# PRに対しActionsを動作させる
pull_request:
types: [opened, synchronize, reopened]
jobs:
review:
runs-on: ubuntu-latest
# ActionsからPRに対しコメントするために必要
permissions:
pull-requests: write
steps:
# レビュースクリプト実行のためコードをチェックアウトする
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
pip install openai
pip install PyGithub
- name: Run script
run: python scripts/code_review_script.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
REPOSITORY: "your-username/your-repository-name"
PR_NUMBER: ${{ github.event.number }}
※REPOSITORY: "your-username/your-repository-name"
はリポジトリに合わせて修正してください。
5. コードレビュー依頼スクリプトを作成する
Pythonで作成してます。やっていることは以下になります。詳細はコードを見てください。
- GitHub APIでPRのdiffを取得
- コードレビューを依頼するプロンプトを作成
- diffに改善点があればコメントする
- 重要度に応じて"MUST:","IMO:","NITS:"のラベルを付ける
- 結果はjson形式でフォーマットを指定
- PRにすでにコメントされている内容と重複するコメントはしない、など
※GitHub APIでPRのコメントを取得して指定
- OpenAI APIを利用、modelは
gpt-4-1106-preview
(GPT-4 Turbo)を指定 - レビュー結果jsonを元にGitHub APIを使ってPRにレビューコメントを投稿
import requests
import os
import json
from openai import OpenAI
# 各環境変数を定数化
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
REPOSITORY = os.getenv("REPOSITORY")
PR_NUMBER = int(os.getenv("PR_NUMBER"))
# GitHubのPull Request API URL
PR_API_URL = f'https://api.github.com/repos/{REPOSITORY}/pulls/{PR_NUMBER}'
# PRのdiffを取得する
def get_pr_diff():
headers = {
'Authorization': f'token {GITHUB_TOKEN}',
'Accept': 'application/vnd.github.v3.diff'
}
diff_response = requests.get(PR_API_URL, headers=headers)
return diff_response.text
# Open AI APIでコードレビューを行い結果をjsonで返却する
def get_openai_review(prompt):
client = OpenAI(api_key=OPENAI_API_KEY)
# レスポンスをjson、modelにGPT-4 Turboを指定
chat_completion = client.chat.completions.create(
messages=[
{
"role": "user",
"content": prompt,
}
],
response_format={"type":"json_object"},
model="gpt-4-1106-preview",
)
review_result = chat_completion.choices[0].message.content
return review_result
# コードレビューを依頼するプロンプトを作成
def create_prompt(code_diff):
prompt = (f'Review the following code:\n\n{code_diff}\n\n'
'- Be sure to comment on areas for improvement.\n'
'- Please make review comments in Japanese.\n'
'- Ignore the use of "self." when using variables and functions.\n'
'- Please prefix your review comments with one of the following labels "MUST:","IMO:","NITS:".\n'
' - MUST: must be modified\n'
' - IMO: personal opinion or minor proposal\n'
' - NITS: Proposals that do not require modification\n'
'- The following json format should be followed.\n'
'{"files":[{"fileName":"<file_name>","reviews": [{"lineNumber":<line_number>,"reviewComment":"<review comment>"}]}]}\n'
'- If there is no review comment, please answer {"files":[]}\n')
prompt += create_ignore_pr_reviews_prompt()
return prompt
# 既にコメントされている場合は同じコメントをしないように依頼するプロンプトを作成
def create_ignore_pr_reviews_prompt():
url = f'{PR_API_URL}/comments'
headers = {'Authorization': f'token {GITHUB_TOKEN}'}
response = requests.get(url, headers=headers)
comments = response.json()
if len(comments) == 0:
return ""
ignore_prompt = '- However, please ensure the content does not duplicate the following existing comments:\n'
for comment in comments:
body = comment['body']
path = comment.get('path')
line = comment.get('line') or comment.get('original_line')
ignore_prompt += f' - file "{path}", line {line}: {body}\n'
return ignore_prompt
# レビューコメントを投稿する
def post_review_comments(review_files):
url = f'{PR_API_URL}/commits'
headers = {
'Authorization': f'token {GITHUB_TOKEN}',
'Accept': 'application/vnd.github.v3+json'
}
pr_commits_response = requests.get(url, headers=headers)
pr_commits = pr_commits_response.json()
last_commit = pr_commits[-1]['sha']
for file in review_files["files"]:
for review in file["reviews"]:
comment_url = f'{PR_API_URL}/comments'
comment_data = {
'body': review["reviewComment"],
'commit_id': last_commit,
'path': file["fileName"],
'position': review["lineNumber"]
}
requests.post(comment_url, headers=headers, data=json.dumps(comment_data))
code_diff = get_pr_diff()
prompt = create_prompt(code_diff)
review_json = get_openai_review(prompt)
post_review_comments(json.loads(review_json))
※ymlに記載してますが配置箇所はリポジトリのscripts/
です。
6. PRを作成/PUSHする
これでPRを作成すれば自動でレビューが行われ、PRにコメントされます。
PRのブランチにPUSHした場合も実施されます。
※かかる時間は1~2分です。
課題
ある程度形になってはいますが以下の課題があります。
- PRのタイトルや詳細をレビュー内容に含めたい。
- レビューの参考情報としてPRにコメントされることがあるのでレビュー内容に含めたい。
- 特定のプレフィックスをつけるようにすればできそう。
- エラーハンドリングをまったくしてないので対応したい。
- 指摘修正しても出来る限りの改善点を指摘してくれるので終わらない。
- 上限を決める等、プロンプトの方で改善したい。
- PRにコメントが多いとプロンプトが肥大化するので料金がかかる。
- pythonではなくjsの方がメンテナンスできる人が多いかもしれない。
- PUSHする度にレビューしてほしいわけではないので、レビュー実施のON/OFFが出来るようにしたい。
最後に
AIの良い所の一つとして細かく実装しなくても判断してくれるので、このような自動化は楽に作成することができました。
Copilot Workspace
のようなものが浸透していけば、これもあっという間に時代遅れになってしまうかもしれませんが、うまく使いこなし簡単なレビューはAIに任せて、効率良く開発を進めましょう。
ちなみに今回作成する過程で結果に記載した小さいSwiftクラスのPRを、計60回程度レビューしてもらいましたが、OpenAI APIにかかった料金は0.63ドル=約92円でした。