PR-Agentは、GitLabのマージリクエスト(MR。GitHubでいうプルリクエスト)に、AIによる説明文・レビュー・改善提案を書き込むオープンソースのツールです。2026年4月にQodoからコミュニティへ移管され、現在のリポジトリはThe-PR-Agent/pr-agent、ライセンスはMITです。GitLabで動かす方法はCI/CDパイプライン、Webhookサーバー、AWS Lambdaの3つあり、CI/CDパイプラインだけはMRのコメントから/reviewを呼べません。この記事は2026年10月25日公開のv0.49.0を基準に、方式の選び方、各手順、トークンと権限、自動実行の制御、つまずく箇所を順に説明します。
まとめ:試すならCI、コメントで呼ぶならWebhookサーバー
- まず1プロジェクトで試す:
.gitlab-ci.ymlにジョブを1つ足すCI/CDパイプライン方式。サーバー不要だが、動くのはMRパイプラインの起動時だけで、コメントの/reviewや/askには反応しない。 - コメントで呼びたい・複数プロジェクトに効かせたい:Webhookサーバー方式。配布済みの
pragent/pr-agent:0.49.0-gitlab_webhookイメージを立てればビルドは要らない。 - 常駐サーバーを持ちたくない:同じWebhookサーバーをAWS Lambdaで動かすか、別製品のQodoかGitLab Duoのコードレビューを使う。
- 共通の前提:GitLab 15.7以降、
apiスコープのトークン(/describeで説明文を書き換えるならDeveloperロール)、LLMのAPIキー。CIのCI_JOB_TOKENではコメントを投稿できない。 - 日本語化:リポジトリの
.pr_agent.tomlにresponse_language = "ja-JP"。ただしURL・トークン・シークレットなど接続系の設定はリポジトリ側に書いても無視される。
GitLabで動かす3方式とコメントコマンドの対応
方式ごとの違いは、どのGitLabイベントを受け取れるかに尽きます。公式のGitLab Integrationの手順書は、パイプライン方式について「GitLabのコメントイベントを受け取らない」と明記し、コメントでコマンドを使いたいならWebhookサーバーを立ててCommentsトリガーを有効にするよう案内しています。
| 方式 | 常駐 | コメントの/review | 自動で動く契機 | 向く場面 |
|---|---|---|---|---|
| CI/CDパイプライン | 不要 | 不可 | MRパイプラインの起動 | 1プロジェクトで試す |
| Webhookサーバー | 必要 | 可 | MR作成・再オープン・下書き解除など | 複数プロジェクトで常用 |
| AWS Lambda | 不要 | 可 | Webhookと同じ | サーバー管理を避けたい |
パイプライン方式のMRパイプラインは、MRを作ったときだけでなくソースブランチへコミットをプッシュするたびに起動します。describe・review・improveを3つ並べた公式例のままだと、プッシュのたびにLLMを3回以上呼ぶ点に注意してください(improveは提案が出ると自己評価の呼び出しが加わります)。PR-Agent全体の機能やGitHubでの導入はPR-Agentとは?使い方・GitHub Actions/Docker導入と料金で扱っています。
前提条件:GitLabのバージョン・トークン・権限
GitLab 15.7以降と /diffs エンドポイント
PR-AgentはGitLab 15.7以降を前提に、MRの/diffsエンドポイントを全ページ取得して差分を読みます。旧来の/changesエンドポイントへは切り替えません。GitLab側が大きすぎるファイルの差分を省略した場合(too_large)は、2つのリビジョンからローカルで差分を作り直します。変更ファイル数がGitLabの返す件数と食い違うなど差分の上限に当たったときはエラーで止まり、MRに「PR-Agent command was not run」という通知を出そうとします。CIのサンプルが使う$CI_SERVER_FQDNはGitLab 16.10から使える変数で、それより古い版では$CI_SERVER_HOSTと$CI_SERVER_PORTを組み合わせます。
投稿に使うトークンの選び方
PR-AgentはMRへコメントを書き込むので、書き込みのできるトークンが要ります。CIジョブに自動で渡されるCI_JOB_TOKENは使えません。GitLabのCI/CDジョブトークンの説明では、Notes APIで許されているのはMRノートの取得(GET)だけで、投稿はできないためです。
- 公式手順の構成:PR-Agent専用のGitLabユーザーを作り、そのユーザーで
apiスコープのパーソナルアクセストークンを発行する。レビューの書き込みだけならReporterロールで足りるが、/describeでMRの説明文を更新するにはDeveloperロールが要る。 - 人のアカウントに紐付けたくない場合:プロジェクトアクセストークンはプロジェクト専用のボットユーザーとして発行される。GitLab.comではPremium以上の契約が必要。PR-Agentの手順書には載っていない構成なので、
apiスコープとDeveloperロールを付けて動作を確かめてから広げる。 - 有効期限:GitLabのアクセストークンは期限が必須で、指定しなければ発行日から365日後に切れる。期限切れはレビューが黙って止まる典型的な原因になる。
トークンの送り方はgitlab.auth_typeで選べます。既定のoauth_tokenはAuthorization: Bearerヘッダー、private_tokenはPRIVATE-TOKENヘッダーで送り、どちらもGitLab.comとセルフマネージドの両方で使えます。
CI/CDパイプラインで動かす手順
.gitlab-ci.ymlにPR-Agentのジョブを追加する
公式例をもとに、イメージの版を固定し、下書きMRとRenovateなどボットのMRを除外し、回答を日本語にした構成です。既存のCI設定へ組み込む場合は、既存のstagesを残してpr_agentを追加し、以下のジョブを統合してください。$CI_MERGE_REQUEST_DRAFTはGitLab 17.10で追加された変数なので、それより古い版では1つ目のルールを外します。
stages:
- pr_agent
pr_agent_job:
stage: pr_agent
image:
name: pragent/pr-agent:0.49.0
entrypoint: [""]
script:
- cd /app
- export MR_URL="$CI_MERGE_REQUEST_PROJECT_URL/merge_requests/$CI_MERGE_REQUEST_IID"
- export gitlab__url=$CI_SERVER_PROTOCOL://$CI_SERVER_FQDN
- export gitlab__PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN
- export config__git_provider="gitlab"
- export config__response_language="ja-JP"
- export openai__key=$OPENAI_KEY
- python -m pr_agent.cli --pr_url="$MR_URL" describe
- python -m pr_agent.cli --pr_url="$MR_URL" review
- python -m pr_agent.cli --pr_url="$MR_URL" improve
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_DRAFT == "true"'
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^(dependabot|renovate)\//'
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
imageのlatestタグは毎リリースで中身が変わり、v0.47.0〜v0.49.0のように既定値や挙動が変わる版も続いています。Docker Hubのpragent/pr-agentのタグ一覧には0.49.0のような版付きタグがあるので、版を固定して上げるときにリリースノートを読むほうが安全です。旧名前空間のcodiumai/pr-agentは更新が止まっています。ボットの除外はブランチ名で判定しています。公式手順書は、GITLAB_USER_LOGINはパイプラインを手動で動かした人に変わるので、ブランチ名で絞るほうが確実だと説明しています。
プッシュのたびにLLMを呼ぶのを避けたいなら、最後のルールにwhen: manualとallow_failure: trueを付けると、MRのパイプライン画面からボタンで実行する運用になります。rulesの中でwhen: manualにしたジョブはallow_failureの既定がfalseなので、付け忘れるとパイプラインが手動ジョブ待ちのまま止まります。
CI/CD変数の登録とprotectedの落とし穴
YAMLが参照するGITLAB_PERSONAL_ACCESS_TOKENとOPENAI_KEYは、プロジェクトの「設定 > CI/CD > 変数」にマスクした変数として登録します。ここで「保護(protected)」を付けると、保護ブランチ以外のパイプラインには変数が渡りません。MRのソースブランチは通常保護されていないので、保護を付けたままだとトークンが空のままジョブが失敗します。GitLab 18.1以降でMRパイプラインに保護変数を渡すには、同一プロジェクト内でソース・ターゲットの両ブランチが保護され、実行者がターゲットブランチへのプッシュかマージの権限を持ち、保護リソースへのアクセス設定を有効にしている必要があります。保護を外して使う場合は、CI設定を変更できる人を信頼できる範囲に限ってください。マスクはログへの表示を防ぐだけで、悪意あるジョブによるトークンの持ち出しは防げません。
社内CAの証明書を使うセルフマネージドGitLabでは、gitlab__SSL_VERIFY=$CI_SERVER_TLS_CA_FILEでGitLab APIの検証に使うCAを指定できます。この設定が効くのはGitLab APIの通信だけで、LLMへの通信やgit cloneには効きません。
テスト結果などのCI成果物をレビューに渡す
テストレポートやSASTの出力を、/review・/describe・/improve・/askの追加コンテキストとして渡せます。前のジョブで成果物(artifacts)として保存し、PR-Agentのジョブをneeds:でそのジョブに依存させ、ARTIFACT_PATHにファイルの場所を指定します。ジョブは/appに移動して動くので、パスは$CI_PROJECT_DIRから書きます。
pr_agent_job:
stage: pr_agent
needs: ["test_job"]
image:
name: pragent/pr-agent:0.49.0
entrypoint: [""]
script:
- cd /app
- export ARTIFACT_PATH="$CI_PROJECT_DIR/reports/pytest.xml"
- export ARTIFACT_INSTRUCTIONS="失敗しているテストを直さない提案があれば指摘してください。"
- python -m pr_agent.cli --pr_url="$MR_URL" review
この抜粋ではMR_URLなどのexportを省いています。実際には前節のジョブのscriptに2行を足す形で使います。/askでCIの成果物を参照できるようになったのはv0.49.0からです。
Webhookサーバーで動かす手順
配布済みイメージでWebhookサーバーを起動する
公式手順書はリポジトリをクローンしてdocker build . -t pr-agent:gitlab_webhook --target gitlab_webhook -f docker/Dockerfileでビルドする流れですが、Docker Hubには同じターゲットのビルド済みイメージpragent/pr-agent:gitlab_webhookと版付きの0.49.0-gitlab_webhookが公開されています。まず共有シークレットを作ります。
python3 -c "import secrets; print(secrets.token_hex(20))"
トークン類はシェルの履歴に残さないよう、環境変数ファイル(ここではpr-agent.env)にまとめて--env-fileで渡します。このファイルはGitへコミットせず、ホスト上ではchmod 600 pr-agent.envなどで所有者以外からの読み取りを制限します。
CONFIG__GIT_PROVIDER=gitlab
GITLAB__URL=https://gitlab.example.com
GITLAB__PERSONAL_ACCESS_TOKEN=<専用ユーザーのトークン>
GITLAB__SHARED_SECRET=<上で生成した文字列>
OPENAI__KEY=<OpenAIのAPIキー>
CONFIG__RESPONSE_LANGUAGE=ja-JP
docker run -d --name pr-agent-gitlab --env-file pr-agent.env \
-p 3000:3000 pragent/pr-agent:0.49.0-gitlab_webhook
コンテナはgunicornの下でpr_agent.servers.gitlab_webhookを動かし、3000番ポート(PORTで変更可)の/webhookでGitLabからの通知を受けます。v0.48.0からWebhook系のイメージは非rootのUID・GID 10001で動くので、秘密情報をファイルでマウントするならこのユーザーが読める権限にします。ワーカー数はCPU数から2〜4の範囲で自動で決まり(上限はGUNICORN_MAX_WORKERS、固定するならGUNICORN_WORKERS)、公式の目安は既定の4ワーカーでメモリ約1Gi、起動時にOOMで落ちるならGUNICORN_WORKERS=2に下げます。
GitLabからコンテナへHTTPSで届く経路(リバースプロキシや社内ロードバランサー)は別途用意します。セルフマネージドGitLabで社内ネットワークに置く場合、GitLabの管理設定でローカルネットワーク宛てのWebhookを許可していないと登録できません。
GitLabにWebhookとトリガーを登録する
プロジェクト(またはグループ)の「設定 > Webhook」で次を登録します。
- URL:
https://<PR-Agentのホスト名>/webhook - シークレットトークン:
GITLAB__SHARED_SECRETと同じ文字列。GitLabはX-Gitlab-Tokenヘッダーで送り、サーバーは一致しなければHTTP 401で拒否する - トリガー:公式手順はプッシュイベント・コメント・マージリクエストイベントの3つを有効にする。コメントを外すと
/reviewなどのコマンドが届かず、マージリクエストイベントを外すと自動レビューが動かない
登録したら、MRを作るか、MRに/reviewとコメントして反応を確かめます。v0.47.0から、GitLabではコメントの先頭がスラッシュで始まるときだけコマンドとして扱います。文中にreviewと書いただけでは動きません。
Webhookで自動実行するコマンドと契機を設定する
Webhookサーバーが自動でコマンドを実行する契機は、gitlab_webhook.pyの分岐で決まっています。MRの作成と再オープン、下書きから準備完了への変更では[gitlab] pr_commandsを実行します。既定は/describe(最終更新メッセージなし)・/review・/improveの3つです。コミットの追加で動かすにはhandle_push_triggerを有効にします。
[gitlab]
pr_commands = ["/describe", "/review"]
feedback_on_draft_pr = false
handle_push_trigger = true
push_commands = ["/review"]
handle_reviewer_assignment = true
reviewer_commands = ["/review"]
| 設定 | 既定 | 効果 |
|---|---|---|
| pr_commands | describe・review・improve | MR作成・再オープン時に実行 |
| feedback_on_draft_pr | false | 下書きMRは実行しない |
| handle_push_trigger | false | コミット追加でpush_commandsを実行 |
| push_commands | describe・review | コミット追加時のコマンド |
| handle_reviewer_assignment | false | ボットをレビュアーに追加したら実行 |
| reviewer_commands | review | レビュアー追加時のコマンド |
レビュアー追加の契機は、gitlab.personal_access_tokenの持ち主をボットとみなして判定します。全MRで自動実行するとLLM費用がかさむチームでは、pr_commands = []とhandle_push_trigger = falseでMR作成時とプッシュ時の自動実行を止め、handle_reviewer_assignment = trueだけを残す方法があります。レビューしてほしいMRにだけボットをレビュアーとして追加すれば、そのMRにだけ費用がかかります。表示名にbot-・-bot・bot_・_bot・codiumを含むユーザーが起こしたイベントは無視されるので(config.bot_user_indicators)、ボットのMRで動かないのは既定の挙動です。ブランチ名で除外するなら[config] ignore_pr_source_branches = ["^(dependabot|renovate)/"]を使います。
AWS LambdaでWebhookを受ける
Webhookサーバーと同じ処理をLambdaの関数URLで受ける構成も公式に用意されています。--target gitlab_lambda -f docker/Dockerfile.lambdaでイメージを作り(Docker Hubにも0.49.0-gitlab_lambdaがあります)、ECRに置いて関数を作ります。タイムアウトは3分以上にし、関数URLの末尾に/webhookを付けてGitLabに登録します。Lambdaの環境変数名にはドットが使えないので、GITLAB.PERSONAL_ACCESS_TOKENはGITLAB__PERSONAL_ACCESS_TOKENと書きます。
トークンをAWS Secrets Managerに置く場合、v0.48.0からWebhookのシークレットトークンは<シークレット名>:<webhook_token>の形式が必須になりました。シークレット名だけの旧形式はHTTP 401で拒否されるので、サーバーとWebhookの設定を同時に切り替えます。プロジェクトごとのシークレットにはgitlab_token・webhook_token・token_nameを入れ、実行ロールにsecretsmanager:GetSecretValueを許可します。さらにLambdaの環境変数にCONFIG__SECRET_PROVIDER=aws_secrets_managerを設定します。LLMのキーなど共通設定もSecrets Managerから読む場合は、共通設定用シークレットを作成し、そのARNをAWS_SECRETS_MANAGER__SECRET_ARNに設定します。
.pr_agent.tomlで日本語化とレビュー方針を設定する
レビューの言語や観点は、リポジトリのデフォルトブランチ直下に置く.pr_agent.tomlで変えられます。日本語にするには、extra_instructionsで頼むより、出力言語そのものを指定するresponse_languageが確実です。
[config]
response_language = "ja-JP"
[pr_reviewer]
extra_instructions = """\
- SQLを文字列連結で組み立てている箇所を指摘する
- 例外を握りつぶしているcatchを指摘する
"""
[pr_description]
generate_ai_title = false
リポジトリ側に書いても無視される設定があります。config_security.pyは、名前がurl・token・secret・keyなどで終わる接続系の設定と、gitlab.auth_type・gitlab.ssl_verifyをホスト専用として扱います。[gitlab] urlやトークン、LLMのapi_baseは、サーバーの環境変数かCIの変数で渡してください。
グループ全体に共通設定を配るpr-agent-settingsリポジトリは、v0.48.0から既定で読まれなくなりました。使い続けるにはサーバー側でconfig.global_settings_repo = "pr-agent-settings"を設定します。v0.49.0では、最も近いサブグループの設定を継承するconfig.use_group_settings_fileがオプトインで加わりました。どちらもサーバー側の設定で、リポジトリの.pr_agent.tomlからは有効にできません。
動かないときの症状別チェック
| 症状 | 原因 | 対処 |
|---|---|---|
| CIでコメントの/reviewが無反応 | パイプライン方式の仕様 | Webhookサーバーへ移る |
| CIジョブでトークンが空 | 変数に保護を付けた | MRでの保護変数の利用条件を確認 |
| Webhookが401 | シークレット不一致 | GITLAB__SHARED_SECRETと揃える |
| Secrets Manager構成で401 | v0.48の新形式に未対応 | シークレット名:webhook_token |
| Webhookが413 | 本文が5MiB超 | max_webhook_request_body_bytes |
| 説明文だけ更新されない | Reporterロール | Developerロールにする |
| 下書きMRで動かない | 既定でスキップ | feedback_on_draft_pr = true |
| コメントしても動かない | 先頭がスラッシュでない | /review で書き始める |
| 証明書エラー | 社内CA | gitlab__SSL_VERIFYにCAのパス |
GitLabの「設定 > Webhook」では、Webhookごとに最近のイベントの応答コードと本文を確認できます。ただし応答が200でも、コマンドが実行されたとは限りません。pragent/pr-agent:0.49.0-gitlab_webhookを起動して試すと、シークレットの不一致は401、6MBの本文は413を返しました。一方、先頭にスラッシュの無いコメントと、表示名がrenovate-botのユーザーの/reviewは、どちらも200のsuccessを返したうえで処理されませんでした。理由はサーバーのログ(docker logs pr-agent-gitlab)にIgnoring comment not starting with /やSkipping GitLab bot userと出るので、200なのに動かないときはログを見ます。
GitLab DuoやQodoとの選び分け
GitLab上のAIコードレビューは、PR-Agentのほかに2つの選択肢があります。判断の軸は、費用をどこに払うかと、LLMの選択権を誰が持つかです。
- GitLab Duo Agent PlatformのCode Review Flow:GitLab標準の機能で、サーバーもLLMのキーも要らない。GitLab Creditsの料金表では1クレジットで4回実行でき、オンデマンドの定価は1クレジット1ドルなので、1回あたり0.25ドル。GitLab.comではFreeプランでもクレジットで使える(18.10以降)。製品の全体像はGitLab Duo Agent Platformとはを参照。
- Qodo(旧Qodo Merge):PR-Agentの元開発元による別製品。現在はQodoのポータルにあるウィザードでGitLabを接続する。GitLab.comの場合、トップレベルグループのOwnerが
apiスコープのトークンを1回だけ渡すと、Developer権限のサービスアカウントが作られる(トークンは保存されない)。個人の名前空間のリポジトリは対象外。違いはQodo Mergeとは?現Qodo 3.xの使い方・料金とPR-Agentとの違いで整理している。
PR-Agentを選ぶべきなのは、LLMの契約を自社で持ちたい場合です。コードを社外のSaaSに渡せないという要件は、GitLab Duo Self-Hosted(Self-ManagedのPremium・UltimateにGitLab Duo Enterpriseアドオンを追加)やQodoのオンプレミス版でも満たせますが、どちらも上位の契約が前提です。OSSのまま、Azure OpenAIやBedrock、社内のOllamaといった自社のLLMにつなげられるのはPR-Agentです。逆に、レビュー対象が少なく運用担当もいないチームがWebhookサーバーを常駐させても、保守の手間に見合いません。その場合はCode Review Flowの1回0.25ドルと、自前のLLM費用およびサーバーの手間を比べてから決めてください。GitHubとGitLabのCI機能の違いはGitHubとGitLabの違い、他のCI/CD製品はCI/CDツール比較にまとめています。
よくある質問
GitLabのPR(プルリクエスト)とMRは同じものですか?
同じ役割です。GitLabではプルリクエストを「マージリクエスト(MR)」と呼び、PR-Agentのコマンドや設定もGitLabではMRに対して動きます。CIの例で使う--pr_urlにはMRのURLを渡します。
PR-Agentは無料で使えますか?
PR-Agent本体はMITライセンスのOSSで無料です。かかるのはLLMのAPI利用料と、Webhookサーバーを立てる場合のサーバー代です。費用はMRの大きさと実行回数で決まるので、プッシュごとの自動実行を絞るのが最も効きます。
CI/CDパイプライン方式でコメントから/reviewを実行できますか?
できません。パイプライン方式はMRパイプラインの起動時に決めたコマンドを流すだけで、GitLabのコメントイベントを受け取りません。コメントで呼びたい場合はWebhookサーバーかLambdaの方式に切り替え、Webhookのトリガーでコメントを有効にします。
セルフマネージドのGitLabでも使えますか?
GitLab 15.7以降なら使えます。CIではgitlab__urlに$CI_SERVER_PROTOCOL://$CI_SERVER_FQDNを、WebhookサーバーではGITLAB__URLに自社のURLを設定します。社内CAを使っているならgitlab__SSL_VERIFYにCA証明書のパスを渡します。
OpenAI以外のLLMやローカルのモデルは使えますか?
使えます。PR-AgentはLiteLLM経由でモデルを切り替えられ、[config] modelにanthropic/やollama/で始まるモデル名を書き、対応するキーやエンドポイントをサーバー側に設定します。v0.49.0の既定モデルはconfiguration.tomlのgpt-6.1-solです。エンドポイントのapi_baseはホスト専用の設定なので、リポジトリの.pr_agent.tomlには書けません。