PRを開くたびにCodeRabbitやCopilotがコメントを投稿してくる光景は、もはや珍しくない。では、同じ仕組みを自前で構築するとどれほど複雑なのか。実は、Pythonスクリプト数十行とGemini APIの組み合わせで、GitHub Actions上に動作するAIコードレビュアーを作れる。本記事ではその仕組みを業務システムの観点から分解する。
仕組みの全体像:4ステップで回るループ
この自作レビュアーは、次の4段階で動作する。まずPRのオープン・更新・再オープンをトリガーとしてワークフローが起動する。次にgit diffで変更差分を抽出し、LLM(大規模言語モデル)のコンテキスト制約に収まるよう800,000バイトに切り詰める。その差分をGemini APIへ送り、ファイル名・行番号・重大度を含む構造化JSONを返させる。最後にGitHub Scriptがそのペイロードを読み取り、PRの対象行に直接インラインコメントを投稿する。
各ステップの役割が明確に分離されているため、どこかを差し替えやすい構造になっている。たとえばGeminiをAnthropicのClaudeに変えたい場合、APIを呼び出すPythonスクリプト部分だけを修正すればよく、GitHub Actions側のyaml定義には手を加えずに済む。
構造化出力とコメント可能行の絞り込み
AIに自由形式のテキストを返させると、行番号の取得や重大度の判定を後から正規表現で解析する必要が生じ、壊れやすい実装になりがちだ。このアーキテクチャでは、Gemini APIの「構造化出力(Structured Output)」機能を使い、あらかじめJSONスキーマを指定してレスポンスの形を固定している。
response_schema = {
"type": "OBJECT",
"properties": {
"status": {"type": "STRING", "enum": ["clean", "issues_found"]},
"summary": {"type": "STRING"},
"comments": {
"type": "ARRAY",
"items": {
"type": "OBJECT",
"properties": {
"file": {"type": "STRING"},
"line": {"type": "INTEGER"},
"severity": {"type": "STRING", "enum": ["high", "medium", "low"]},
"comment": {"type": "STRING"}
}
}
}
}
}これにより、パース処理を別途書く必要がなく、受け取ったJSONをそのままGitHub APIに渡せる。業務システムの自動化では「出力形式の揺れ」が障害の温床になりやすいため、スキーマで型を強制するこのアプローチは保守性の面で優れている。
もう一点、見落とされがちだが重要な処理がある。差分テキストを解析して「コメント可能な行」だけを事前に特定するステップだ。GitHub APIのインラインコメントは、差分に含まれる追加・変更行にしか投稿できない。削除行や変更前の行を指定するとAPIがエラーを返す。スクリプト内のparse_commentable_lines関数はdiffのhunk(変更のかたまりを表すdiffの単位)ヘッダー(@@で始まる行)を読み取り、有効な行番号のセットを構築している。AIが返してきたコメント対象行がこのセットに含まれない場合は、そのコメントを黙って捨てる設計になっている。
既存パイプラインへの組み込みと運用上の注意点
この仕組みを既存のCI/CDパイプラインに追加する際、いくつか考慮すべき点がある。
- 権限設定:ワークフローに`pull-requests: write`パーミッションが必要。最小権限の原則から、レビュー専用ジョブに分離しておくことを推奨する
- APIコストの管理:差分が大きいPRでは毎回Gemini APIを呼び出すためコストが積み上がる。draft PRをトリガー対象から外す、差分サイズに上限を設けて超過時はスキップするなどの制御を加えるとよい
- フォールスポジティブの扱い:AIのコメントはあくまで参考情報であり、マージブロッカーとして設定しない運用が現実的だ。`status: clean`か`issues_found`かを後続ステップで参照し、サマリーをPR説明欄に追記する程度の使い方から始めるとチームの抵抗感が少ない
- シークレット管理:`GEMINI_API_KEY`はGitHubのRepository SecretsまたはEnvironment Secretsに格納する。forkされたリポジトリからのPRでシークレットが漏洩しないよう、`pull_request_target`ではなく`pull_request`イベントを使う点も確認しておく
CodeRabbitやCopilot for PRといった既製品サービスとの比較でいえば、既製品はセットアップが数分で済む反面、プロンプトのカスタマイズ余地が限られる。自作の場合、レビュー観点(アーキテクチャ・セキュリティ・UX・保守性など)をプロンプトに自由に記述できる。たとえば社内コーディング規約やドメイン固有の禁止パターンをプロンプトに埋め込めば、汎用ツールでは検出できないルール違反を指摘させることも可能だ。
大規模なモノレポ(単一リポジトリに複数サービスを格納する構成)では、変更対象のパスによってレビューモデルやプロンプトを切り替えるといった応用も考えられる。フロントエンドの変更にはUI観点のプロンプトを、バックエンドの変更にはSQL・API設計観点のプロンプトを、といった分岐はyamlのpathsフィルターとジョブ分割で実現できる。
AIによる自動レビューは、人間のレビュアーを置き換えるものではない。定型的なミス(nullチェック漏れ、エラーハンドリングの欠落など)を事前に機械が拾うことで、人間のレビューをより本質的な設計議論に集中させる、という位置づけが適切だ。この役割分担を明文化してチームに共有しておくと、導入後の混乱を避けやすい。