idea_world_labDEV JOURNAL
2026年6月25日木曜日

Qwen PR Review Workflow 構造と改善記録

作成日: 2026年 6月 25日

目的

この文書は qwen-code ベースの PR レビュー workflow を取り込み、現在のリポジトリに合わせてカスタマイズする過程を記録するものです。

今回の作業の核心は、単に workflow が成功で終わるだけでなく、Qwen が生成したレビュー結果を実際の GitHub PR コメントとして残すことです。

背景

このリポジトリは CodeRabbit、Sourcery、Qodo などの外部レビュー Bot も併用しています。ただし別途 qwen-code workflow を取り込み OpenRouter 経路で動作させ、PR ごとに Qwen ベースのレビューを残す流れも実験しています。

当初はコスト問題で AI PR レビュー API の流れを停止しました。その後 OpenRouter の free tier で openai/gpt-oss-120b:free を活用できるようになり、この経路で再挑戦する方針に README を修正しました。

元の workflow 構造

対象ファイル:

.github/workflows/qwen-code-pr-review.yml

元のワークフロー名:

🧐 Qwen Pull Request Review

主要トリガー:

on:
  pull_request_target:
    types: ['opened']
  pull_request_review_comment:
    types: ['created']
  pull_request_review:
    types: ['submitted']
  workflow_dispatch:
    inputs:
      pr_number:
        required: true
        type: 'number'

job 条件は次の場合に実行されるように設定されていました。

  • workflow_dispatch 手動実行
  • pull_request_targetopened イベントで、PR 作成者が OWNERMEMBERCOLLABORATOR のいずれかの場合
  • PR レビューコメントに @qwen /review が含まれている場合
  • PR レビュー本文に @qwen /review が含まれている場合

workflow の主要なステップは次のとおりです。

  1. PR コードを checkout
  2. PR のタイトル、本文、変更ファイル、diff 情報を収集
  3. QwenLM/qwen-code-action を実行
  4. プロンプト内で Qwen にレビューを作成させ、gh pr comment でコメントを投稿するよう指示

観察された問題

PR #5 で workflow を実際に観察しました。

PR:

https://github.com/yyeongjin/idea_world/pull/5

PRは正常に作成され、次のボットが反応しました。

  • CodeRabbit
  • Sourcery
  • Qodo

Qwen workflowも pull_request_target イベントで実行されました。

観測された実行:

workflow: 🧐 Qwen Pull Request Review
event: pull_request_target
conclusion: success

しかし、実際の PR には Qwen のレビューコメントが残っていませんでした。

workflow log を見ると、Qwen はレビュー内容を書いた後、実際のツール呼び出しを実行せず、次のような形式の JSON スタイル指示をテキストとして出力しました。

{"tool":"write_file", ...}
{"tool":"run_shell_command", ...}

workflow自体は成功でしたが、ユーザーが期待した結果である「PRコメント作成」は完了しませんでした。

原因整理

問題はQwenにコメント作成まで任せた構造だった。

元のプロンプトは Qwen に次のことを直接実行するよう指示しました。

1. write_file("review.md", "<your detailed review feedback here>")
2. gh pr comment $PR_NUMBER --body-file review.md --repo $REPOSITORY

しかし実際の実行では、Qwenはこの作業をツール呼び出しで実行せず、やるべき作業をテキストとしてのみ出力しました。

そのため、モデルがツールを正しく呼び出さないと、workflow が success でも PR にはコメントが残りません。

追加で確認した構造上の問題

workflow の job 条件には issue_comment イベントが含まれていました。

(github.event_name == 'issue_comment' && ... contains(github.event.comment.body, '@qwen /review') ...)

しかし、on: トリガーには issue_comment がありませんでした。

つまり、PR の会話で @qwen /review とコメントしても、ワークフローはまったく開始されない構造でした。

また、pull_request_target トリガーは opened のみ反応しました。

pull_request_target:
  types: ['opened']

そのため、PRブランチに新しいコミットがプッシュされる synchronize イベントでは自動的に再実行されませんでした。

改善方向

モデルにGitHubコメントの作成を任せず、GitHub Actionsのステップがコメント作成を担当するように変更する。

改善後の責任分担は以下の通りです。

Qwen
  -> レビュー Markdown テキストのみ生成

GitHub Actions
  -> Qwen の出力値をファイルに保存
  -> gh pr commentで PR にコメントを作成

このようにすれば Qwen がツール呼び出しをテキストで出力したとしても、少なくとも workflow step で qwen_result を受け取りコメントを残すことができる。

変更内容

1. trigger 強化

既存:

pull_request_target:
  types: ['opened']

変更:

pull_request_target:
  types: ['opened', 'reopened', 'synchronize']
issue_comment:
  types: ['created']

意図:

  • PRが新しく開かれたときに実行
  • PRが再度開かれたときに実行
  • PRブランチに新しいコミットが上がったときに実行
  • PRの会話に @qwen /review を残したときに実行

追加観察:

on: トリガーだけを強化しても十分ではなかった。実際に PR ブランチに新しいコミットを push したとき、pull_request_target の実行は生成されたが、ジョブ条件がまだ github.event.action == 'opened' のみを許可しているため、実行は skipped で終了した。

したがって、ジョブ条件も次のように修正する必要がある。

(github.event.action == 'opened' ||
 github.event.action == 'reopened' ||
 github.event.action == 'synchronize')

2. QwenステップにID付与

変更:

- name: 'Run Qwen PR Review'
  id: 'qwen_review'

意図:

次のステップで次の出力を読むためです。

steps.qwen_review.outputs.qwen_result

3. prompt の責任変更

従来のプロンプトは Qwen にファイル作成と PR コメント作成を直接指示していました。

変更後のプロンプトは Qwen にレビュー用の Markdown テキストのみを最終回答として返すよう指示します。

Return the review markdown text as your final answer.
Do not call write_file.
Do not call gh pr comment.
Do not output JSON tool-call instructions.
A later GitHub Actions step will post your final answer as a PR comment.

意図:

  • Qwenがツール呼び出しJSONを出力する問題を減らす。
  • コメント作成の責任をモデルではなくワークフローステップに移す。

4. Qwen 出力値を PR コメントとして投稿するステップを追加

追加されたステップ:

- name: 'Post Qwen PR Review'
  if: |-
    ${{ always() && steps.qwen_review.outputs.summary != '' }}
  env:
    GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
    PR_NUMBER: '${{ steps.get_pr.outputs.pr_number || steps.get_pr_comment.outputs.pr_number }}'
    REPOSITORY: '${{ github.repository }}'
    QWEN_RESULT: '${{ steps.qwen_review.outputs.summary }}'
  run: |-
    {
      echo "## Qwen PR Review"
      echo
      printf '%s\n' "$QWEN_RESULT"
    } > qwen-review.md
    gh pr comment "$PR_NUMBER" --body-file qwen-review.md --repo "$REPOSITORY"

目的:

  • Qwen の出力が空でない場合は必ず PR コメントとして残す。
  • コメント作成は GITHUB_TOKENgh pr comment を使用する決定的なステップが担当する。

追加観察:

最初は出力名を steps.qwen_review.outputs.qwen_result と誤って読んだ。実際に QwenLM/qwen-code-actionaction.yml を確認すると、コンポジット内部のステップは qwen_result を作成するが、アクションの外部に公開される出力名は summary だった。

outputs:
  summary:
    value: ${{ steps.qwen_run.outputs.qwen_result }}

なので、次のコメントステップでは steps.qwen_review.outputs.summary を読む必要があります。 qwen_result を読むと、Qwen がレビュー本文を正常に生成しても post ステップがスキップされます。

注意点

pull_request_target ワークフローは PR ブランチのワークフローファイルではなく、ベースブランチのワークフローファイルを基準に実行されます。

したがって、PR ブランチにワークフローの修正をプッシュするだけでは、現在の PR の pull_request_target の動作は変わりません。

実際に適用するには、次のいずれかが必要です。

  1. workflow の修正自体をまず main に反映させる。
  2. その後 PR に @qwen /review を残すか、新しいコミットを push して変更された workflow を実行する。

今回の作業では workflow の修正を main に先に反映し、PR #5 で再度 Qwen のレビューをトリガーして実際にコメントが作成されるかを確認する流れで進める。

セキュリティ基準

workflow ドキュメントには API キー、エンドポイント URL、プロバイダーのベース URL などの機密情報を記録しない。

GitHub Actions のログでも、次の値はシークレットマスクの対象である必要がある。

OPENAI_API_KEY
OPENAI_BASE_URL
OPENAI_MODEL
GITHUB_TOKEN

文書にはモデル変更の事実と workflow の構造だけを残し、実際の secret 値は記録しない。

現在の結論

今回の観察で確認したことは次のとおりです。

  • PR の作成自体は正常に動作した。
  • CodeRabbit、Sourcery、Qodo は正常に PR に反応した。
  • Qwen workflow も pull_request_target では success で終了した。
  • しかし従来の構造では Qwen が実際にコメントを書かず、ツール呼び出し指示をテキストとして出力できていた。
  • したがって「workflow 成功」と「PR コメント作成成功」は別々に見る必要がある。
  • Qwen の出力値を GitHub Actions の後続ステップが直接コメントとして投稿するように変更する方が安全である。