PRサマリの自動生成とプロジェクト固有のルールに基づくAIレビューを実現するPR-Agent導入ガイド

こんにちは、Androidエンジニアの @syarihu です。

GiftmallのAndroidアプリプロジェクトでは、PR-Agentを活用した自動コードレビューとPRサマリ生成の仕組みを構築しています。プルリクエストが作成されると、AIが自動的にコードを分析して、レビューコメントや説明を日本語で生成してくれます。

PR-Agentは、OpenAI(GPT)、Google(Gemini)、Anthropic(Claude Sonnet)などの各種LLMを使用してコードを理解し、以下の機能を提供します。

  • 自動PRサマリ生成: 変更内容を分析し、ファイルごとの変更概要とウォークスルーを生成
  • 自動コードレビュー: セキュリティや潜在的な問題を指摘し、改善点を提案
  • コード改善提案: 具体的なコード例とともに、より良い実装方法を提示

本記事では、GiftmallのAndroidアプリプロジェクトで使用しているPR-Agentのワークフロー設定について、その構成と各機能の詳細、実際の使い方を解説します。

なお、本記事の執筆時点では PR-Agent v0.31 を使用しています。

PRサマリの例 コード改善提案の例

※ ここで利用しているのはサンプルコードで、実際のプロジェクトのコードではありません

AIコードレビューツールの導入背景

GiftmallのAndroidアプリチームでは、経験の浅いエンジニアのコードレビューに関して次のような課題を抱えていました。

  • コードレビューに時間がかかっており、レビュアーの負担が大きくなっていた
  • 静的解析ツール(Lint)では拾えないようなロジックの問題点や設計の改善点を指摘する必要があり、それらを丁寧に説明することに多くの時間を費やしていた
  • PRの説明が不足しているケースもあり、レビュアーがコードの意図を理解するのに余計な時間がかかることもあった

このような状況を改善するため、AIコードレビューツールの導入を検討しました。AIレビューには次のような期待がありました。

  • 静的解析では拾えないロジックの問題点や設計の改善点を指摘してくれること
  • AIが自動生成するサマリを活用することで、説明が不足しているケースを補助できること
  • レビュアーの負担を軽減し、より重要な案件や設計の議論に時間を使えるようになること

PR-Agentの選定理由

PR-Agentを導入した時点(2025年3月ごろ)では、GitHub Copilotのレビュー機能もKotlinに未対応で、実用的なAIコードレビューの選択肢が限られていました。 このような状況の中で、GiftmallのAndroidアプリプロジェクトではPR-Agentを採用しました。選定理由は以下の通りです。

  • OSSで開発されている: オープンソースで開発されており、コミュニティによる活発なメンテナンスが行われています。大手企業での導入事例も多く、Web上には豊富な知見が蓄積されているため、問題が発生した際の解決策を見つけやすく、安心して導入できる環境が整っていました
  • 導入コストが低い: 導入に必要なのは基本的にLLMプロバイダーのAPIキーだけで、複雑な設定や追加のインフラストラクチャを用意する必要がなく、すぐに効果を検証できる点が魅力的でした
  • 柔軟なLLM選択: PR-Agent自体の利用は無料であり、OpenAI、Anthropic、Googleなど、好きなLLMプロバイダーを選択できます。これにより、プロジェクトの要件や予算に応じて最適なモデルを選択でき、将来的により優れたモデルが登場した際にも柔軟に対応できます
  • PR Bodyへの自動サマリ埋め込み: PR-AgentにはAIが自動生成したサマリをPR Body(本文)に直接埋め込めるオプションがあります。マーカーを設定した任意の場所にサマリを埋め込めるため、人間が書いたPR Bodyの補助として使いやすく、情報がコメント欄に埋もれにくいという利点があります。この機能により、PRの説明が一箇所にまとまり、レビュアーが必要な情報をすぐに見つけられます

GiftmallのAndroidアプリプロジェクトにおけるPR-Agentワークフローの構成

主な機能

1. 自動PRサマリ生成 (AUTO_DESCRIBE)

PRが作成されると、AIが変更内容を分析して、次の情報を含む詳細な説明を生成します。

  • 変更の概要
  • ファイルごとの変更サマリ(テーブル形式)
  • ファイルタイプ別の分類
  • ウォークスルー形式の詳細説明

設定のポイント:

PR_DESCRIPTION.INLINE_FILE_SUMMARY: 'table'  # ファイルごとの変更をテーブル形式で表示
PR_DESCRIPTION.ENABLE_SEMANTIC_FILES_TYPES: 'true'  # ファイルの種類を意味的に分類
PR_DESCRIPTION.COLLAPSIBLE_FILE_LIST: "adaptive"  # ファイルリストを折りたたみ可能に
PR_DESCRIPTION.USE_DESCRIPTION_MARKERS: 'true'  # マーカーで囲んで後から更新可能に
PR_DESCRIPTION.EXTRA_INSTRUCTIONS: 'PRの説明は必ず日本語で記述してください。'

※ここで設定している環境変数は、PR-Agent の公式ドキュメントでは小文字で記載されています。しかし GitHub Actions のenvによる環境変数の設定では大文字・小文字が区別されないため、本プロジェクトでは大文字で設定しています。

PR本文への統合:

USE_DESCRIPTION_MARKERS: 'true'を設定することで、AIが生成した説明はコメントとして投稿されるのではなく、PR本文(body)に直接埋め込めます

これにより、PRの説明が一箇所にまとまり、レビュアーが情報を探しやすくなります。

この機能を実現するために、pr_agent:typepr_agent:summaryなどのマーカーをPull requestのbodyに入れておく必要があります。 AIが生成した説明であることが分かりやすいように、.github/PULL_REQUEST_TEMPLATE.mdに専用のセクションを用意しています。

### :wrench: Type of this change

- pr_agent:type

### :robot: AI generated details

<!--ai_details_start-->

pr_agent:summary

pr_agent:walkthrough

<!--ai_details_end-->
  • <!--ai_details_start--><!--ai_details_end-->のHTMLコメントマーカーで囲まれた領域に、AIが生成した内容が挿入されます
    • PR-Agentのマーカーは一度置き換えると再生成ができないため、これは自前でPR-Agentのマーカーに置き換えて再度PR-Agentに説明を生成させるためのコメントマーカーです
  • pr_agent:typepr_agent:summarypr_agent:walkthroughは、PR-Agentによって自動的に実際の内容に置き換えられます
  • /describeコマンドで再生成する前に、<!--ai_details_start--><!--ai_details_end-->のHTMLコメントマーカーの中をpr_agent:typepr_agent:summaryなどにリセットしておけば、PR-Agentに再度生成させられます

🤖 Generated by PR Agent のヘッダーの自動挿入について:

USE_DESCRIPTION_MARKERS: 'true'でマーカーを埋め込んで自動生成させると、マーカーで置き換えられた箇所の上に次のようなヘッダーが自動で挿入されます。

### 🤖 Generated by PR Agent at COMMIT_ID

これを非表示にするには、次のように設定をオフにすることで自動生成されないようにできます。

PR_DESCRIPTION.INCLUDE_GENERATED_BY_HEADER: 'false'

2. 自動コードレビュー (AUTO_REVIEW)

AIがコードをレビューして、以下の観点から分析します。

  • セキュリティの問題点
  • コードの潜在的なバグ
  • ベストプラクティスからの逸脱

設定のポイント:

PR_REVIEWER.REQUIRE_SECURITY_REVIEW: 'true'  # セキュリティレビューを有効化
PR_REVIEWER.PERSISTENT_COMMENT: 'true'  # コメントを永続化
PR_REVIEWER.ENABLE_REVIEW_LABELS_SECURITY: 'true'  # セキュリティラベルを追加
PR_REVIEWER.EXTRA_INSTRUCTIONS: 'レビューのコメントはすべて必ず日本語で記述してください。'

コメントの永続化:

PERSISTENT_COMMENT: 'true'を設定することで、レビューコメントが永続化されます。

この設定により次のようなメリットがあります。

  • PR-Agentを再実行した際、既存のコメントが更新される(新しいコメントが追加されない)
  • PRのコメント欄が重複したレビューで溢れることを防止
  • レビュー履歴が一箇所にまとまり、変更の経緯を追いやすくなる

/reviewコマンドで再レビューを実行しても、コメントが増殖せず、ノイズになりにくい設計となっています。

3. コード改善提案 (AUTO_IMPROVE)

次のような具体的なコード改善案を提示してくれます。

  • 問題のあるコードに焦点を当てた提案
  • 具体的なコード例を提示

設定のポイント:

PR_CODE_SUGGESTIONS.FOCUS_ONLY_ON_PROBLEMS: 'true'  # 問題点に集中
PR_CODE_SUGGESTIONS.SUGGESTIONS_SCORE_THRESHOLD: '0'  # すべての提案を表示
PR_CODE_SUGGESTIONS.COMMITABLE_CODE_SUGGESTIONS: 'true'  # コミット可能な提案を生成
PR_CODE_SUGGESTIONS.EXTRA_INSTRUCTIONS: ${{ vars.PR_AGENT_CUSTOM_PROMPT }}  # カスタムプロンプト

コミット可能な提案:

COMMITABLE_CODE_SUGGESTIONS: 'true'を設定することで、AIが提案したコード変更を直接コミットできる形式で提供します。

この機能により次のようなメリットがあります。

  • Suggested changeで表示されるため、PR作成者が、良い提案を簡単に適用可能
  • 細かい修正を手動で行う手間を削減

これを有効にしない場合は提案がサマリ形式で表示されます。その場合、 PR_REVIEWERと同じように PR_REVIEWER.PERSISTENT_COMMENT: 'true' の設定を入れることで1つのコメントに対して上書きしてくためノイズが少なくなります。

コミット可能な提案の場合、再度レビューをしたときに同じ箇所への指摘でも別の指摘としてコメントをするため、レビューを何度か行うとノイズになることがありますが、サマリで表示されるよりはSuggested changeで表示されたほうがどこに何の指摘をされているのかが分かりやすいため、有効にしています。

この設定はプロジェクトの要件に合わせて決めるとよいでしょう。

カスタムプロンプト:

EXTRA_INSTRUCTIONSにカスタムプロンプトをとしてプロジェクト固有のコーディング規約やガイドラインを設定することで、ガイドラインに沿ったコードの提案をさせられます。

カスタムプロンプトには次のような指示を含めています。

  • Jetpack Composeのガイドライン(アプリのカラー定義をまとめたGmColorの参照など)
  • 文字列リソースの使用規則
  • コミットメッセージのガイドライン
  • A/Bテストフラグの実装パターン
  • モジュール参照ルール

Claude Code向けに用意しているCLAUDE.mdをそのまま渡すのではなく、CLAUDE.mdをベースにしてレビューで特に見てほしい観点を抽出したレビュー用に最適化したプロンプトを作成しています。

これにより、AIはプロジェクト固有のルールに従った提案を生成し、より実用的なフィードバックを提供できます。後述するモデル比較でも見られるように、Claudeは特にこのカスタムプロンプトを正確に理解し、プロジェクト固有の情報(:cross_domain:composeモジュールのGmColorなど)を明確に参照した指摘を行います。

トリガー条件

ワークフローは以下のイベントで起動します:

  1. プルリクエストイベント: opened, reopened, ready_for_review,review_requested
  2. コメント投稿イベント: /describeなどのコマンドに反応

ただし、以下の場合は実行されません。

  • Botからのイベント
    • renovateなどbotが作成するPRはサマリの生成やレビューの必要が無いため、botからのイベントは無効化しています
  • release/またはfeature/で始まるブランチからのPR(issue_commentイベント以外)
    • releaseやfeatureはレビュー済のPRがマージされるため改めてPR-Agentを動かす必要が無いので無効化しています

synchronizeイベントについて

PR-Agentは、デフォルトではopened, reopened, ready_for_review, review_requestedのイベントでのみ動作します。GitHub Actionsのトリガーにsynchronize(PRへの新しいコミットのプッシュ)を追加しても、PR-Agent側でフィルタされるため動作しません

もし、PRに新しいコミットがプッシュされるたびにPR-Agentを実行したい場合は、次の設定を追加する必要があります。

GITHUB_ACTION_CONFIG.PR_ACTIONS: '["opened", "reopened", "ready_for_review", "review_requested", "synchronize"]'

注意点:

synchronizeイベントを有効にすると、次のような影響があります。

  • API呼び出し回数の増加: コミットのたびにLLM APIが呼び出されるため、使用量とコストが増加します
  • コメントの増加: 更新のたびに新しいレビューコメントや提案が追加され、PRのコメント欄が煩雑になる可能性があります
  • 通知の増加: チームメンバーへの通知が頻繁に発生し、ノイズが多くなる可能性があります

このため、GiftmallのAndroidアプリプロジェクトではsynchronizeイベントを意図的に無効化しています。

必要に応じて、/review/improveなどのコマンドを手動で実行することで、柔軟にレビューをトリガーできます。

技術的なポイント

1. PR本文の自動的な更新

AIによる説明を再生成する際、既存のAI生成セクション(<!--ai_details_start--><!--ai_details_end--> で囲まれた部分)をリセットする仕組みを実装しています。

この処理は、ワークフロー内の「Prepare PR body with reset AI details」ステップで実行されます。

処理の流れ

  1. PR情報の取得

     let prNumber;
     if (context.eventName === 'pull_request') {
       prNumber = context.payload.pull_request.number;
     } else if (context.eventName === 'issue_comment') {
       prNumber = context.payload.issue.number;
     }
    
     const { data: pr } = await github.rest.pulls.get({
       owner: context.repo.owner,
       repo: context.repo.repo,
       pull_number: prNumber
     });
    
     const body = pr.body || '';
    

    PRの本文を取得します。イベントタイプに応じてPR番号を特定し、GitHub APIを使用してPR情報を取得します。

  2. マーカーの検索

     const aiDetailsStart = '<!--ai_details_start-->';
     const aiDetailsEnd = '<!--ai_details_end-->';
    
     const startIndex = body.indexOf(aiDetailsStart);
     const endIndex = body.indexOf(aiDetailsEnd);
    
     if (startIndex === -1 || endIndex === -1) {
       core.setOutput('new-body', JSON.stringify(body));
       return;
     }
    

    PR本文内から<!--ai_details_start--><!--ai_details_end--> のマーカーを検索します。どちらかのマーカーが見つからない場合は、元の本文をそのまま使用します。

  3. セクションの分割とリセット

     const beforeSection = body.substring(0, startIndex + aiDetailsStart.length);
     const afterSection = body.substring(endIndex);
     const newAiDetails = '\\\\n\\\\npr_agent:summary\\\\n\\\\npr_agent:walkthrough\\\\n\\\\n';
    
     const newBody = beforeSection + newAiDetails + afterSection;
     core.setOutput('new-body', JSON.stringify(newBody));
    

    PR本文を3つのセクションに分割します:

    • beforeSection: マーカー開始位置まで(マーカー自体を含む)
    • newAiDetails: リセットされたAI生成セクション(pr_agent:summarypr_agent:walkthroughのプレースホルダー)
    • afterSection: マーカー終了位置から最後まで(マーカー自体を含む)

    これらを結合することで、AI生成セクションだけが初期状態にリセットされた新しいPR本文を作成します。

  4. PR本文の更新

     const newBody = ${{ steps.prepare-pr-body.outputs.new-body }};
     await github.rest.pulls.update({
       owner: context.repo.owner,
       repo: context.repo.repo,
       pull_number: prNumber,
       body: newBody
     });
    

    準備した新しい本文でPRを更新します。この後、PR-Agentが実行され、pr_agent:summarypr_agent:walkthroughのプレースホルダーが実際のAI生成コンテンツに置き換えられます。

動作例

元のPR本文:

### :robot: AI generated details

<!--ai_details_start-->

## 📝 Summary

以前に生成された古い説明...

## 📂 Files walkthrough

古いファイル一覧...

<!--ai_details_end-->

リセット後(PR-Agent実行前):

### :robot: AI generated details

<!--ai_details_start-->

pr_agent:summary

pr_agent:walkthrough

<!--ai_details_end-->

PR-Agent実行後:

### :robot: AI generated details

<!--ai_details_start-->

## 📝 Summary

新しく生成された説明...

## 📂 Files walkthrough

新しいファイル一覧...

<!--ai_details_end-->

この仕組みにより、/describeコマンドで何度でも説明を再生成でき、常に最新の変更内容を反映したPR説明を維持できます。

2. AIモデルの選択

CONFIG.MODEL: 'anthropic/claude-sonnet-4-5-20250929'

PR-Agentでは、各プロバイダーの様々なモデルを選択して使用できます。利用可能なモデルの一覧は、PR-Agentのソースコードで確認できます。

モデル選択の経緯

GiftmallのAndroidアプリプロジェクトでは、次のモデルを比較検証しました。

  • o1-2024-12-17: OpenAI o1シリーズモデル(以前使用)
  • gpt-5: OpenAI GPT-5モデル
  • anthropic/claude-sonnet-4-5-20250929: Anthropic Claude Sonnet 4.5モデル(現在使用)

導入当時は o1-2024-12-17 を使っていましたが、比較検証の結果、現時点(2025年11月)では anthropic/claude-sonnet-4-5-20250929が良さそうと判断し、現在採用しています。

モデル比較:Claude Sonnet 4.5 vs GPT-5

動作検証としてコードレビューで指摘させるためにわざとルール違反をするようなコードをClaude Codeに作ってもらい、Claude Sonnet 4.5とGPT-5で生成されたPR説明とレビューを比較しました。 (修正前のコードも適当なサンプルコードで、実際のプロジェクトのコードではありません)

PR説明の比較

Claude Sonnet 4.5の出力例:

  • より具体的で詳細な説明(「ハードコーディングされた文字列を多数追加(リソース化が必要)」など)
  • 問題点を明確に指摘(リソース化が必要、など)

GPT-5の出力例:

  • より抽象的な説明(「トースト文言とログ出力の追加」「直接色コード設定の追加」など)
  • 問題点の指摘が不明確

レビューコメントの比較

Claude Sonnet 4.5の出力例:

  • プロジェクト固有のガイドライン(:cross_domain:composeモジュールのGmColor)を明確に参照
  • 具体的な修正方法を提示
  • より実用的で「人間に優しい」指摘

GPT-5の出力例:

  • やや一般的で、具体性に欠ける
  • プロジェクト固有の情報が不明確

Claude Sonnet 4.5の特徴

anthropic/claude-sonnet-4-5-20250929モデルは、次の点で優れた性能を発揮しています。

  • コンテキストの理解: プロジェクト固有のガイドラインやモジュール構造を正確に理解
  • 具体的な指摘: 抽象的ではなく、具体的なモジュール名やクラス名を参照した提案
  • 日本語の品質: 自然で分かりやすい日本語での説明
  • 問題点の明確化: レビュアーが見落としがちな問題(リソース化の必要性など)を明示的に指摘

モデルは要件に応じて変更可能ですが、精度と品質のバランス、そしてプロジェクトのコンテキストをどれだけ正確に理解できるかを考慮して選択することをおすすめします。

GiftmallのAndroidアプリプロジェクトでのPR-Agentの使い方

1. ドラフトPRの作成

プルリクエストをドラフトとして作成します。

2. レビュー準備完了(In Reviewへ移行)

開発が完了したら、ドラフトをReady for reviewに変更します。この時点でPR-Agentが自動的に以下を生成します。

  • PRサマリ(変更内容の概要とファイル一覧)
  • コードレビュー(セキュリティや潜在的な問題の指摘)
  • 改善提案

AIからの指摘を確認し、明らかな問題があれば修正します。これによりコードの品質を向上できます。

3. レビュー対応と更新

In Reviewにする前やレビュアーからのフィードバックやAIの指摘に対応した後、必要に応じて以下のコマンドを使用します。

  • /review: In review前にAIレビューを確認したり、修正後に再レビューを依頼したい場合
    • コードを大きく変更した際に、AIに再度レビューしてもらう
    • 既存のレビューコメントが更新される(PERSISTENT_COMMENTにより)
  • /describe: PR説明を更新したい場合
    • 重要な修正を加えた際に、説明を最新の状態に更新
    • 実装の経緯や意図などを追記する必要がある場合は、手動でテンプレートの:technologist: Detailsセクションに記載
  • /improve: 改善提案を更新したい場合

利用可能なコマンド

コマンド 用途
/describe PR説明の再生成
/review レビューの再実行
/improve 改善提案の再実行

PR-Agentを使うことによるメリット

開発者の負担軽減

  • 説明作成の手間削減: 実装内容についてはAIが自動で詳細な説明を生成するため、開発者は実装意図や経緯など、AIでは補えない部分の説明に集中できます
  • レビュー前の品質向上: 必要に応じてドラフト段階でAIのフィードバックを受けることで、人間のレビュアーに依頼する前にコードの品質を高められます

レビューの質と効率の向上

  • 細かい指摘の自動化: 変数名や関数名のtypo、コメント内の誤字など、静的解析ツール(Lint)では検出しにくい問題をAIが指摘します。コーディングスタイルはLintで担保できますが、意味的な誤りや文脈に依存する問題はAIの得意分野です
  • 人間が見落としやすい問題の検出: セキュリティの脆弱性や潜在的なバグなど、人間のレビューでは見落としがちな問題を指摘
  • 一貫性のあるレビュー: 常に同じ基準でコードをチェックし、レビューの品質を均一化
  • 日本語対応: カスタムプロンプトによってすべてのフィードバックが日本語で提供されるため、チーム内のコミュニケーションがスムーズ

継続的な品質保証

  • レビュー時間の短縮: 基本的なチェックはAIが実施するため、レビュアーは設計や実装方針などの重要な観点に集中できます
  • 学習機会: AIの指摘から、ベストプラクティスやセキュリティの知識を学べます

注意点

  • AIの提案は必ずしも正しいとは限らないため、人間によるレビューも重要です
  • 使用するLLMプロバイダーのAPIキーが必要です
  • 大きなPRでは処理に時間がかかることがあります
  • APIの使用量に応じて課金が発生する可能性があります
    • 料金の目安(o1-2024-12-17使用時): 2025年10月の実績では、1PR当たり1回実行の計算で33PRに対して約30ドルのコストが発生しました。ただし、PRの規模(変更ファイル数、コード行数など)によって大きく変動するため、これはあくまで参考値です

まとめ

GiftmallのAndroidアプリチームでは、経験の浅いエンジニアのコードレビューに時間がかかるという課題を抱えていました。PR-Agentを導入した結果、期待通りレビュアーの負担を軽減することができました。

AIが自動的にPR説明を生成してくれるため、説明が不足しているケースでもレビュアーがコードの意図を理解しやすくなりました。また、静的解析では拾えないロジックの問題点や設計の改善点をAIが指摘してくれることで、レビュアーはより重要な案件や設計の議論に時間を使えるようになりました。

PR-Agentを使えば、チーム全体のコードレビューの質と効率が大幅に向上します。特に日本語での詳細な説明とレビューコメントは、チーム内のコミュニケーションをスムーズにしてくれます。

本記事ではGiftmallのAndroidアプリプロジェクトでの運用事例を紹介しましたが、実際の運用ではチームの要件に応じて設定をカスタマイズすることをおすすめします。

参考情報: 今回紹介したPR-Agentのワークフロー

name: Code review by pr-agent

on:
  pull_request:
    types: [ opened, reopened, ready_for_review ]
  issue_comment:
    types: [ created ]

permissions:
  pull-requests: write
  issues: write

jobs:
  pr_agent:
    runs-on: ubuntu-latest
    name: Run PR Agent
    if: |
      (github.event_name == 'pull_request' || github.event.issue.pull_request) &&
      github.event.sender.type != 'Bot' &&
      (
        github.event_name == 'issue_comment' ||
        (
          !startsWith(github.event.pull_request.head.ref, 'release/') &&
          !startsWith(github.event.pull_request.head.ref, 'feature/') &&
          github.event.pull_request.draft == false
        )
      )
    steps:
      - name: Prepare PR body with reset AI details
        id: prepare-pr-body
        if: |
          github.event_name == 'pull_request' ||
          (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/describe'))
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            let prNumber;
            if (context.eventName === 'pull_request') {
              prNumber = context.payload.pull_request.number;
            } else if (context.eventName === 'issue_comment') {
              prNumber = context.payload.issue.number;
            }

            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prNumber
            });

            const body = pr.body || '';
            const aiDetailsStart = '<!--ai_details_start-->';
            const aiDetailsEnd = '<!--ai_details_end-->';

            const startIndex = body.indexOf(aiDetailsStart);
            const endIndex = body.indexOf(aiDetailsEnd);

            if (startIndex === -1 || endIndex === -1) {
              core.setOutput('new-body', JSON.stringify(body));
              return;
            }

            const beforeSection = body.substring(0, startIndex + aiDetailsStart.length);
            const afterSection = body.substring(endIndex);
            const newAiDetails = '\\n\\npr_agent:summary\\n\\npr_agent:walkthrough\\n\\n';

            const newBody = beforeSection + newAiDetails + afterSection;
            core.setOutput('new-body', JSON.stringify(newBody));
      - name: Update PR body
        if: |
          github.event_name == 'pull_request' ||
          (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/describe'))
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            let prNumber;
            if (context.eventName === 'pull_request') {
              prNumber = context.payload.pull_request.number;
            } else if (context.eventName === 'issue_comment') {
              prNumber = context.payload.issue.number;
            }

            const newBody = ${{ steps.prepare-pr-body.outputs.new-body }};
            await github.rest.pulls.update({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prNumber,
              body: newBody
            });
      - id: pr-agent
        uses: qodo-ai/pr-agent@d36ad319f7fb0049205017405a71275c41599587 # v0.31
        env:
          OPENAI_KEY: ${{ vars.PR_AGENT_MODEL_PROVIDER == 'openai' && secrets.OPENAI_API_KEY || '' }}
          ANTHROPIC.KEY: ${{ vars.PR_AGENT_MODEL_PROVIDER == 'anthropic' && secrets.ANTHROPIC_KEY || '' }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # [CONFIG]
          CONFIG.MODEL: '${{ vars.PR_AGENT_MODEL }}'
          # [GITHUB_ACTION_CONFIG]
          GITHUB_ACTION_CONFIG.AUTO_REVIEW: 'true'
          GITHUB_ACTION_CONFIG.AUTO_DESCRIBE: 'true'
          GITHUB_ACTION_CONFIG.AUTO_IMPROVE: 'true'
          # [PR_DESCRIPTION]
          PR_DESCRIPTION.INLINE_FILE_SUMMARY: 'table'
          PR_DESCRIPTION.INCLUDE_GENERATED_BY_HEADER: 'false'
          PR_DESCRIPTION.ENABLE_HELP_TEXT: 'false'
          PR_DESCRIPTION.ENABLE_HELP_COMMENT: 'false'
          PR_DESCRIPTION.EXTRA_INSTRUCTIONS: 'PRの説明は必ず日本語で記述してください。'
          PR_DESCRIPTION.ENABLE_SEMANTIC_FILES_TYPES: 'true'
          PR_DESCRIPTION.COLLAPSIBLE_FILE_LIST: "adaptive"
          PR_DESCRIPTION.COLLAPSIBLE_FILE_LIST_THRESHOLD: "3"
          PR_DESCRIPTION.USE_DESCRIPTION_MARKERS: 'true'
          # [PR_REVIEWER]
          PR_REVIEWER.REQUIRE_SCORE_REVIEW: 'false'
          PR_REVIEWER.REQUIRE_TESTS_REVIEW: 'false'
          PR_REVIEWER.REQUIRE_ESTIMATE_EFFORT_TO_REVIEW: 'false'
          PR_REVIEWER.REQUIRE_SECURITY_REVIEW: 'true'
          PR_REVIEWER.REQUIRE_TICKET_ANALYSIS_REVIEW: 'false'
          PR_REVIEWER.PERSISTENT_COMMENT: 'true'
          PR_REVIEWER.INLINE_CODE_COMMENTS: 'true'
          PR_REVIEWER.EXTRA_INSTRUCTIONS: 'レビューのコメントはすべて必ず日本語で記述してください。'
          PR_REVIEWER.FINAL_UPDATE_MESSAGE: 'false'
          PR_REVIEWER.ENABLE_REVIEW_LABELS_SECURITY: 'true'
          PR_REVIEWER.ENABLE_REVIEW_LABELS_EFFORT: 'false'
          PR_REVIEWER.ENABLE_INTRO_TEXT: 'true'
          PR_REVIEWER.ENABLE_HELP_TEXT: 'false'
          # [PR_CODE_SUGGESTIONS]
          PR_CODE_SUGGESTIONS.MAX_CONTEXT_TOKENS: '16000'
          PR_CODE_SUGGESTIONS.FOCUS_ONLY_ON_PROBLEMS: 'true'
          PR_CODE_SUGGESTIONS.PERSISTENT_COMMENT: 'true'
          PR_CODE_SUGGESTIONS.APPLY_SUGGESTIONS_CHECKBOX: 'true'
          PR_CODE_SUGGESTIONS.COMMITABLE_CODE_SUGGESTIONS: 'true'
          PR_CODE_SUGGESTIONS.SUGGESTIONS_SCORE_THRESHOLD: '0'
          PR_CODE_SUGGESTIONS.NEW_SCORE_MECHANISM: 'true'
          PR_CODE_SUGGESTIONS.ENABLE_INTRO_TEXT: 'true'
          PR_CODE_SUGGESTIONS.EXTRA_INSTRUCTIONS: ${{ vars.PR_AGENT_CUSTOM_PROMPT }}
          # [PR_QUESTIONS]
          PR_QUESTIONS.EXTRA_INSTRUCTIONS: "必ず日本語で回答してください。"
          # [PR_UPDATE_CHANGELOG]
          PR_UPDATE_CHANGELOG.EXTRA_INSTRUCTIONS: "必ず日本語で回答してください。"