次世代システム研究室の Y.I です。 OpenAI API を活用してちょっと便利なコマンドを作成したのでご紹介します。作成したものは、「自動Git Commitメッセージ生成」と「コードレビュー」機能です。LangChainやVectorDBなどを利用しなくても、発想次第で便利な機能を作れますので1例としてご覧ください。
機能紹介
Pythonで以下の機能を実現しています。
- Git commitメッセージの自動生成
Gitの変更履歴に基づき、適切な日本語のcommitメッセージを生成します。
- コードレビューの自動化
Gitの変更履歴に基づき、コードに問題がないかやパフォーマンス改善の提案を行います。
- openai apiのtokenを環境変数から取り込み
簡易的ですがtokenをScriptに直書きして漏洩しないように外から渡せるようにします。
- 生成結果をクリップボードへコピー
コピーする一手間を省略します。この一手間省略が使い勝手向上に役立ちます。
スクリプトの説明
このスクリプトは、主に以下のステップで構成されています。
1. コマンドライン引数の解析
スクリプトはargparse
を使い、実行時に--review
や--commit
のオプションを受け取り、どちらの機能を実行するかを判定します。
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
def parse_arguments():
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
def parse_arguments():
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
2. Gitの差分取得
git diff
コマンドを実行し、Gitの変更内容を取得します。この結果が、後にOpenAI APIに送信され、commitメッセージやコードレビューに使用されます。
result = subprocess.run(['git', 'diff', 'HEAD'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
def get_git_diff():
result = subprocess.run(['git', 'diff', 'HEAD'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
def get_git_diff():
result = subprocess.run(['git', 'diff', 'HEAD'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
3. OpenAI APIの呼び出し
OpenAI APIを使って、取得したGit差分に基づいて適切なメッセージを生成します。API呼び出しには、あらかじめ用意されたプロンプトを使い、モデルにはgpt-4o-mini
を使用しています。git diff レベルの解析ならば、十分な回答を得られるのでコストを考慮して 4o-mini を利用しています。必要に応じて API を変更します。コマンドライン引数で設定するようにしても良いですね。
def exec_openai_api(prompt_system, prompt_user, diff_text):
response = openai.chat.completions.create(
{"role": "system", "content": prompt_system},
{"role": "user", "content": prompt_user + f"#diff: {diff_text}"}
def exec_openai_api(prompt_system, prompt_user, diff_text):
openai.api_key = api_key
response = openai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": prompt_system},
{"role": "user", "content": prompt_user + f"#diff: {diff_text}"}
],
max_tokens=1000
)
return response
def exec_openai_api(prompt_system, prompt_user, diff_text):
openai.api_key = api_key
response = openai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": prompt_system},
{"role": "user", "content": prompt_user + f"#diff: {diff_text}"}
],
max_tokens=1000
)
return response
4. commitメッセージ生成のプロンプト
commitメッセージを生成するためのプロンプトでは、AIに「コードレビューの専門家」として振る舞うように指示し、わかりやすい日本語でメッセージを生成します。特に「修正によりどのように変わるのか」を生成してもらうことで、commit messageに作成したコードの効果が明記され、将来にコード解析する際の手がかかりやレビューアーへ有益な情報を伝えることができます。
<
def generate_commit_message():
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。"
"メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。"
"回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
def generate_commit_message():
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。"
"メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。"
"修正内容は特に簡潔に回答してください。"
"回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
def generate_commit_message():
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。"
"メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。"
"修正内容は特に簡潔に回答してください。"
"回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
5. コードレビュー用のプロンプト
コードレビューを行う際には、詳細なチェック項目をAIに提供し、バグやパフォーマンスの改善点を指摘させる仕組みになっています。
def generate_codereview():
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。"
"また指摘があれば改善するサンプルコードも提示してください"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください"
def generate_codereview():
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。"
"また指摘があれば改善するサンプルコードも提示してください"
+ "バグがないかチェック"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "無駄な処理を追加していないか"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "脆弱性につながるコードがないか"
+ "パフォーマンス改善につながる提案があるか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください"
)
def generate_codereview():
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。"
"また指摘があれば改善するサンプルコードも提示してください"
+ "バグがないかチェック"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "無駄な処理を追加していないか"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "脆弱性につながるコードがないか"
+ "パフォーマンス改善につながる提案があるか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください"
)
6. クリップボードへのコピー
回答結果を一手間少なく扱えるように、クリップボードにコピーします。 api利用token数は表示のみでコピー対象から外しています。
pyperclip.copy(commit_message)
# clipboard へ copy
pyperclip.copy(commit_message)
# clipboard へ copy
pyperclip.copy(commit_message)
7. script全体
python scriptの全体です。
api_key = os.getenv('OPENAI_API_KEY')
print("環境変数 OPENAI_API_KEYを登録してください。 OPENAI_API_KEY='xxx...'")
"""Parse command-line args"""
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
result = subprocess.run(['git', 'diff', 'HEAD~3'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
def exec_openai_api(prompt_system, prompt_user, diff_text):
"""openai apiでgit commitメッセージを作成する"""
response = openai.chat.completions.create(
# model="gpt-4o-2024-08-06",
{"role": "system", "content": prompt_system},
{"role": "user", "content":
prompt_user + f"#diff: {diff_text}"}
def generate_commit_message():
"""openai apiでgit commitメッセージを作成する"""
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。修正内容は特に簡潔に回答してください。回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
def generate_codereview():
"""openai apiで codereview する"""
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。また指摘があれば改善するサンプルコードも提示してください"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
diff_text = get_git_diff()
if not diff_text.strip():
print("No changes to commit.")
prompt_system, prompt_user = generate_codereview()
prompt_system, prompt_user = generate_commit_message()
prompt_system, prompt_user = generate_commit_message()
response = exec_openai_api(prompt_system, prompt_user, diff_text)
commit_message = response.choices[0].message.content.strip()
print(f"commit message: {commit_message}")
print(f"token: {response.usage}")
pyperclip.copy(commit_message)
if __name__ == "__main__":
import subprocess
import pyperclip
import os
import openai
import argparse
api_key = os.getenv('OPENAI_API_KEY')
if not api_key:
print("環境変数 OPENAI_API_KEYを登録してください。 OPENAI_API_KEY='xxx...'")
exit(1)
def parse_arguments():
"""Parse command-line args"""
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
def get_git_diff():
"""git diff HEADを出力"""
result = subprocess.run(['git', 'diff', 'HEAD~3'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
def exec_openai_api(prompt_system, prompt_user, diff_text):
"""openai apiでgit commitメッセージを作成する"""
openai.api_key = api_key
response = openai.chat.completions.create(
# model="gpt-4o",
# model="gpt-4o-2024-08-06",
model="gpt-4o-mini",
# model="o1-preview",
messages=[
{"role": "system", "content": prompt_system},
{"role": "user", "content":
prompt_user + f"#diff: {diff_text}"}
],
max_tokens=1000
)
return response
def generate_commit_message():
"""openai apiでgit commitメッセージを作成する"""
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。修正内容は特に簡潔に回答してください。回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
def generate_codereview():
"""openai apiで codereview する"""
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。また指摘があれば改善するサンプルコードも提示してください"
+ "バグがないかチェック"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "無駄な処理を追加していないか"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "脆弱性につながるコードがないか"
+ "パフォーマンス改善につながる提案があるか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
def main():
"""メイン関数"""
diff_text = get_git_diff()
if not diff_text.strip():
print("No changes to commit.")
return
args = parse_arguments()
if args.review:
prompt_system, prompt_user = generate_codereview()
elif args.commit:
prompt_system, prompt_user = generate_commit_message()
else:
prompt_system, prompt_user = generate_commit_message()
response = exec_openai_api(prompt_system, prompt_user, diff_text)
commit_message = response.choices[0].message.content.strip()
print(f"commit message: {commit_message}")
print(f"token: {response.usage}")
# clipboard へ copy
pyperclip.copy(commit_message)
if __name__ == "__main__":
main()
import subprocess
import pyperclip
import os
import openai
import argparse
api_key = os.getenv('OPENAI_API_KEY')
if not api_key:
print("環境変数 OPENAI_API_KEYを登録してください。 OPENAI_API_KEY='xxx...'")
exit(1)
def parse_arguments():
"""Parse command-line args"""
parser = argparse.ArgumentParser(description='openai apiに問い合わせる内容')
parser.add_argument('--review', '-r', action='store_true', help='code review')
parser.add_argument('--commit', '-c', action='store_true', help='git commit message')
return parser.parse_args()
def get_git_diff():
"""git diff HEADを出力"""
result = subprocess.run(['git', 'diff', 'HEAD~3'], stdout=subprocess.PIPE)
return result.stdout.decode('utf-8')
def exec_openai_api(prompt_system, prompt_user, diff_text):
"""openai apiでgit commitメッセージを作成する"""
openai.api_key = api_key
response = openai.chat.completions.create(
# model="gpt-4o",
# model="gpt-4o-2024-08-06",
model="gpt-4o-mini",
# model="o1-preview",
messages=[
{"role": "system", "content": prompt_system},
{"role": "user", "content":
prompt_user + f"#diff: {diff_text}"}
],
max_tokens=1000
)
return response
def generate_commit_message():
"""openai apiでgit commitメッセージを作成する"""
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージをmarkdownは使わずにplaintext形式の日本語で作ってください。メッセージは、要約、修正内容、修正によりどのように変わるのかを回答して。修正内容は特に簡潔に回答してください。回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
def generate_codereview():
"""openai apiで codereview する"""
return (
"あなたはプログラムコードレビューの専門家です。",
"git commitメッセージを元に、以下の観点でコードレビューしてください。また指摘があれば改善するサンプルコードも提示してください"
+ "バグがないかチェック"
+ "よりコード量が少なくなる効率が良いcodeの提案"
+ "無駄な処理を追加していないか"
+ "変数やメソッドパラメーターの値がNULLや0やマイナス値でも問題がないか"
+ "脆弱性につながるコードがないか"
+ "パフォーマンス改善につながる提案があるか"
+ "修正の目的を推測して、目的を満たしているか、より良い実現方法があれば提案してください"
+ "回答がmax_tokensを超えてしまう場合は文字数が収まるように要約してください。"
)
def main():
"""メイン関数"""
diff_text = get_git_diff()
if not diff_text.strip():
print("No changes to commit.")
return
args = parse_arguments()
if args.review:
prompt_system, prompt_user = generate_codereview()
elif args.commit:
prompt_system, prompt_user = generate_commit_message()
else:
prompt_system, prompt_user = generate_commit_message()
response = exec_openai_api(prompt_system, prompt_user, diff_text)
commit_message = response.choices[0].message.content.strip()
print(f"commit message: {commit_message}")
print(f"token: {response.usage}")
# clipboard へ copy
pyperclip.copy(commit_message)
if __name__ == "__main__":
main()
実行結果
docker compose.yaml の volume に関する設定を変更した内容に対して、git commit message を生成してみた結果になります。
・git diff 内容
diff --git a/compose.yaml b/compose.yaml
index 7a99870..79e881e 100644
@@ -39,7 +39,7 @@ networks:
diff --git a/compose.yaml b/compose.yaml
index 7a99870..79e881e 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -39,7 +39,7 @@ networks:
volumes:
db1vol:
- external: false
+ external: true
db2vol:
- external: false
+ external: true
diff --git a/compose.yaml b/compose.yaml
index 7a99870..79e881e 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -39,7 +39,7 @@ networks:
volumes:
db1vol:
- external: false
+ external: true
db2vol:
- external: false
+ external: true
・生成結果
commit message: コミットメッセージ:
要約: Docker Composeのボリューム設定を修正
修正内容: db1volおよびdb2volのexternal属性をfalseからtrueに変更
修正により: ボリュームが外部ボリュームとして扱われ、他のプロジェクトやサービスからもアクセス可能になる。
token: CompletionUsage(completion_tokens=78, prompt_tokens=202, total_tokens=280, completion_tokens_details={'reasoning_tokens': 0})
commit message: コミットメッセージ:
要約: Docker Composeのボリューム設定を修正
修正内容: db1volおよびdb2volのexternal属性をfalseからtrueに変更
修正により: ボリュームが外部ボリュームとして扱われ、他のプロジェクトやサービスからもアクセス可能になる。
token: CompletionUsage(completion_tokens=78, prompt_tokens=202, total_tokens=280, completion_tokens_details={'reasoning_tokens': 0})
commit message: コミットメッセージ:
要約: Docker Composeのボリューム設定を修正
修正内容: db1volおよびdb2volのexternal属性をfalseからtrueに変更
修正により: ボリュームが外部ボリュームとして扱われ、他のプロジェクトやサービスからもアクセス可能になる。
token: CompletionUsage(completion_tokens=78, prompt_tokens=202, total_tokens=280, completion_tokens_details={'reasoning_tokens': 0})
終わりに
このスクリプトを使えば、Gitのcommitメッセージやコードレビューの作業をAIに任せることができ、開発者の作業効率が向上します。OpenAI APIを活用したこのアプローチは、日々の開発作業をよりスマートに進めるための強力なツールです。ちょっとした script ですが、あまりの便利さにgit commit message を自分で記述する気になれません。。AI を利用してちょっとしたことから改善していきましょう!
グループ研究開発本部では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。アプリケーション開発の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。