はじめに
開発プロセスにおいてコードレビューは非常に重要ですが、レビュー待ちの時間の発生や、レビュアーの負担増加が課題になることがあります。
そこで今回は、AWS CodeCommit、Amazon EventBridge、AWS Lambda、そして Amazon Bedrock を組み合わせて、プルリクエスト(PR)作成時や手動トリガー時に自動でAIがコードレビューをしてくれる仕組みを構築しました。
自動レビューだけでなく、「mainブランチへの直接push時」や「特定の差分だけを狙ってレビューしたい時」に対応できるよう、手動発火の仕組みも取り入れています。
全体構成図
本システムのアーキテクチャは以下の通りです。
処理の流れ
- トリガーの検知: CodeCommitでのPR作成・更新、あるいは手動イベント(AWSコンソールやテスト実行等)を Amazon EventBridge が検知し、AWS Lambda を起動します。
- コンテキストの準備: Lambda関数は、Amazon S3 からレビュー用のプロンプト(指示書)を取得し、AWS CodeCommit から対象コミットのコード差分(diff)を取得します。
- AIによるレビュー: プロンプトと差分コードを組み合わせて Amazon Bedrock(Anthropic Claudeなど)へリクエストを投げます。
- フィードバック: Bedrockから返ってきたレビュー結果を、Lambdaが CodeCommit のPR(またはコミット)のコメントとして直接書き込みます。
各コンポーネントの設定と実装
-
Amazon S3(プロンプトの管理)
レビューの品質や評価軸を柔軟に変更できるよう、システムプロンプトは外部(S3)に切り出しています。
配置ファイル: s3://[バケット名]/prompts/review_prompt.txt
あなたは優秀なシニアエンジニアです。提供されたソースコードの差分(diff)をレビューしてください。
# 評価軸
1. バグや潜在的な脆弱性がないか
2. 可読性やメンテナビリティは適切か
3. パフォーマンス上の懸念はないか
# 出力フォーマット
指摘事項がある場合は、具体的な修正案を含めてMarkdown形式かつ日本語で簡潔に出力してください。
特に問題がない場合は「Good Job!」とだけ返してください。
2. Amazon EventBridge(イベントルールの設定)
PRの作成・更新イベントを検知するためのイベントパターンを設定します。
{
"source": ["aws.codecommit"],
"detail-type": ["CodeCommit Pull Request State Change"],
"resources": ["arn:aws:codecommit:[リージョン]:[アカウントID]:[リポジトリ名]"],
"detail": {
"event": ["pullRequestCreated", "pullRequestSourceBranchUpdated"]
}
}
※手動実行時は、このEventBridgeのパターンに依存しない形で、後述するLambdaへ特定のJSONイベントを直接渡すことで発火させます。
3. AWS Lambda(実装コード)
Python(boto3)を使用したLambda関数のサンプルコードです。
※ランタイム:Python 3.14 / タイムアウト:3分以上に設定(Bedrockの応答時間を考慮)
import json
import boto3
import os
codecommit = boto3.client('codecommit')
s3 = boto3.client('s3')
bedrock = boto3.client('bedrock-runtime')
PROMPT_BUCKET = os.environ['PROMPT_BUCKET']
PROMPT_KEY = os.environ['PROMPT_KEY']
def get_prompt_from_s3():
obj = s3.get_object(Bucket=PROMPT_BUCKET, Key=PROMPT_KEY)
return obj['Body'].read().decode('utf-8')
def lambda_handler(event, context):
print("Received event:", json.dumps(event))
# 1. トリガーの判別(PR自動 or 手動実行)
is_manual = event.get('manual', False)
if is_manual:
# 手動実行時のパラメータ取得(例)
repository_name = event['repository_name']
before_commit = event['before_commit_id']
after_commit = event['after_commit_id']
pr_id = event.get('pull_request_id') # 任意
else:
# EventBridgeからの自動検知時のパラメータ取得
detail = event['detail']
repository_name = detail['repositoryNames'][0]
pr_id = detail['pullRequestId']
# PRの最新のコミット情報を取得
pr_info = codecommit.get_pull_request(pullRequestId=pr_id)['pullRequest']
# ターゲットとソースのコミットIDを取得
# (※環境に合わせて適切なコミットIDの取得ロジックに調整してください)
before_commit = pr_info['pullRequestTargets'][0]['destinationCommit']
after_commit = pr_info['pullRequestTargets'][0]['sourceCommit']
# 2. CodeCommitから差分(diff)の取得
differences = codecommit.get_differences(
repositoryName=repository_name,
beforeCommitId=before_commit,
afterCommitId=after_commit
)
# 簡易的に差分のテキストを組み立て(※ファイル数やサイズが大きい場合は考慮が必要)
code_diff_text = ""
for diff in differences.get('differences', []):
file_path = diff.get('afterBlob', {}).get('path', 'unknown')
code_diff_text += f"\n--- File: {file_path} ---\n"
# 実際の運用では blob の内容比較や差分詳細をテキスト化します
# ここではデモ用に差分メタデータを付与(適宜カスタマイズしてください)
if not code_diff_text:
print("No meaningful code changes found.")
return {'statusCode': 200, 'body': 'No diff to review'}
# 3. S3からプロンプトの取得
system_prompt = get_prompt_from_s3()
# 4. Amazon Bedrock (Claude 3.5 Sonnet 等) への問い合わせ
# ※お使いのモデルIDを指定してください
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
body = json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 2048,
"system": system_prompt,
"messages": [
{
"role": "user",
"content": f"以下のコード差分をレビューしてください:\n\n{code_diff_text}"
}
]
})
response = bedrock.invoke_model(body=body, modelId=model_id)
response_body = json.loads(response.get('body').read())
review_comment = response_body['content'][0]['text']
# 5. レビュー結果をCodeCommitにフィードバック
if pr_id:
# PRが存在する場合はPRコメントとして投稿
codecommit.post_comment_for_pull_request(
pullRequestId=pr_id,
repositoryName=repository_name,
beforeCommitId=before_commit,
afterCommitId=after_commit,
content=review_comment
)
else:
# main直pushや特定差分の手動実行(PRなし)の場合はコミットに対してコメント
# (※リポジトリやブランチ運用に合わせて調整)
print(f"Review Comment for Commit {after_commit}:\n{review_comment}")
# 必要に応じて post_comment_for_compared_commit などを実行
return {
'statusCode': 200,
'body': json.dumps('Review completed successfully!')
}
工夫したポイント・こだわり
-
プロンプトの外部管理(S3)
レビューのルール(言語設定、セキュリティ重視、命名規則のチェックなど)を変更したくなった際、Lambdaのコードを書き換えることなく、S3上のテキストファイルを修正するだけで即座に反映できるようにしました。 -
「自動」と「手動」のハイブリッド運用
EventBridgeによるPR検知だけでなく、Lambdaのペイロード内に manual: true フラグとターゲットのコミットID群を渡すことで、「PRを作らない main ブランチへの直接push」や「特定の2つのコミット間のピンポイントな差分レビュー」にも柔軟に対応できるロジックにしました。これにより、開発フェーズやブランチ戦略に合わせた柔軟な運用が可能になりました。
まとめと今後の展望
この仕組みを導入したことで、開発者が手動でAIを呼び出したいシーンにも応えつつ、レビューの初動を大幅に高速化することができました。人間による本レビューの前に、軽微なバグやフォーマットの指摘をAIが済ませてくれるため、チーム全体の生産性向上を実感しています。
今後は、ファイルサイズが大きすぎる場合のチャンク分割処理や、特定のファイル拡張子(.md などを除外する)のフィルタリング機能などを拡張していきたいと考えています。
最後までお読みいただきありがとうございました!