【勉強】生成 AI 協働者編③GitHub Actions でエージェントを動かす

生成 AI

生成 AI は毎日使っているのに、知識そのものは数年前で止まっている自覚がありました。Gemini や Claude を私生活で触っているのですが、どちらも「だいたい分かっている」つもりになっていて、実際には用語の意味も課金の仕組みも説明できない、という状態です。そこで、キャッチアップすべき項目を洗い出したラーニングパスを自分用に作り、端から潰していくことにしました。

構成は 4 レイヤー・16 ステップで、項目数は 90 ほどです。利用者編から始めて、協働者編・開発者編・運用者編へ進みます。進め方は「まず自分で手を動かして調べる・検証する」「詰まったところや調べきれなかったところを AI に補ってもらう」「ワークログに残す」の 3 つを項目ごとに繰り返します。

前回の協働者編②では、GitHub の Issue を入口、プルリクエスト(PR)を出口にして、手元のエージェントに作業を任せました。今回の協働者編③では、そのエージェントを GitHub Actions 上で動かします。練習用のリポジトリ(麻雀の点数計算をする小さな Web API)は、前回から引き続き使っています。題材は前回と同じく Claude Code です。この記事は、そのワークログを清書したものです。個人的に勉強して、個人的にまとめただけのものなので、網羅的な解説になっていない可能性が大いにあります。また、記載の内容はすべて 2026-11 時点のものになりますが、生成 AI まわりは動きが速いので最新の情報とは異なる場合があります。

協働者編では、次のことができる前提で進めます。どれも生成 AI とは関係のない知識なので、ラーニングパスの項目には含めていません。

  • ターミナルでコマンドを実行し、ファイルやディレクトリを操作できる
  • Git で branch・commit・merge を自分で実行できる(Pro Git の最初の 3 章程度)
  • GitHub でリポジトリを作り、プルリクエストを作ってマージしたことがある(GitHub の Hello World 程度)
  • GitHub Actions の workflow を読み、どのイベントで何が動くかが分かる(GitHub Actions のクイックスタート 程度)
  • Python などで書かれたコードを読み、テストを実行できる

下の表が今回のやることリストです。私と同じような状況にある人は、ぜひこの順番で自分でもタスクをこなしてみることをオススメします。

項目タスク
ヘッドレス実行【調べる】claude -p と対話セッションの違いを説明できるようにする
【動かす】claude -p でテストを実行し、結果を JSON で受け取る
Issue から PR までを Actions で任せる【調べる】手元での実行と GitHub Actions での実行の違いを説明できるようにする
【動かす】Issue へのコメントでエージェントを起動し、PR を作らせる
AI レビューの自動化【調べる】レビューの基準をどこに置くかを説明できるようにする
【動かす】PR を作ったときに自動でレビューされるようにする
エージェントの権限を絞る【調べる】エージェントの PR を誰がどこで止めるかを説明できるようにする
【動かす】手元のエージェントが使う GitHub の認証情報を、1 つのリポジトリに絞る

ヘッドレス実行

  • 【調べる】claude -p と対話セッションの違いを説明できるようにする
  • 【動かす】claude -p でテストを実行し、結果を JSON で受け取る

手元以外の場所でエージェントを動かすにあたって、まずは対話せずに動かす方法を押さえておきます。

対話せずに 1 回動かす

Claude Code は、-p フラグを付けると非対話モードで動きます(Claude Code をプログラムで実行する)。プロンプトを渡すと 1 回だけ作業して結果を出力し、終了します。スクリプトから呼び出したり、CI で動かしたりするときに使います。出力の形式は --output-format で、text(デフォルト)・json・stream-json から選べます。

対話セッションでは、承認が必要な操作のたびに確認を求められますが、-p ではその対話ができません。そこで、使ってよいツールを --allowedTools で個別に指定するか、--permission-mode で全体の方針を決めます(権限モードを選択する)。主なモードは次の 3 つです。

  • auto:実行してよいかの判断を、分類器(別のモデル)に任せる
  • dontAsk:確認が必要な操作はすべて拒否する(ファイルの読み取りなど、確認が不要な操作は実行される)
  • acceptEdits:ファイルの書き込みと、mv・cp などの一般的なファイル操作を確認なしで許可する

読み込まれるものは対話セッションと同じで、CLAUDE.md や Skill に加えて、hooks や MCP サーバーも読み込まれます。前回記事の GitHub 連携の節(協働者編②)で触れたとおり、-p は信頼の確認を出さないので、他人のリポジトリで実行すると、そこに置かれた hooks がそのまま走ります。これを避けたいときや、どのマシンでも同じように動かしたいときは、--bare を付けるとそれらの読み込みをスキップできます(bare モードで高速に開始する)。ただし、bare モードはサブスクリプションの認証情報を使わないので、API キーが必要です。

テストを実行して JSON で受け取る

練習用のリポジトリでテストを実行させ、結果を JSON で受け取ってみました。

$ claude -p --output-format json "pytest を実行して" | jq
{
  ...,
  "result": "`uv run pytest` を実行しました。177 件すべて通り、失敗はありません(5.91 秒)。...",
  ...
}

result に、テストの実行結果の要約が入って返ってきます。一方、--bare を付けて同じように実行すると、Not logged in · Please run /login と表示されて止まりました。私の現状の環境ではサブスクリプションの認証情報しか設定していないためです。

なお、テストが実行されたのは、私が ~/.claude/settings.json で権限モードのデフォルトを auto にしていたからです。当然、GitHub Actions のランナーのような、ローカル以外の環境で Claude Code を動かすには、前述したように適切に権限を与えておく必要があります。権限モードの指定を忘れてしまうと Manual モード(default)で始まるので、承認が要る操作は拒否されてしまいます(ツールを自動承認する)。

Issue から PR までを Actions で任せる

  • 【調べる】手元での実行と GitHub Actions での実行の違いを説明できるようにする
  • 【動かす】Issue へのコメントでエージェントを起動し、PR を作らせる

前回記事では、手元の Claude Code に「Issue #N に取り組んで」と頼むと、Skill からサブエージェントが起動して、ブランチの作成から PR の作成まで進める形を作りました。今回は、この作業を GitHub Actions 上で実行させます。

セットアップ

GitHub Actions 上で Claude Code を動かす仕組みが Claude Code GitHub Actions です。デフォルトのテンプレートを使用すると、Issue や PR のコメントで @claude とメンションしたときに、Claude が変更を実装してコミットを push するようになります。プロンプトを与えて、任意の振る舞いをさせることもできます。

セットアップは、手元の Claude Code で /install-github-app を実行すれば OK です。概ね、以下のような流れでした。

  • Claude GitHub App を GitHub のアカウントにインストールする
  • workflow のテンプレートを選ぶ(今回は Issue と PR のコメントの @claude に反応するもの)
  • 認証に使うトークンを選ぶ。サブスクリプションで長期トークンを作ると、リポジトリの secret に CLAUDE_CODE_OAUTH_TOKEN として保存される
  • workflow のファイルを追加する PR が用意されるので、作成してマージする

App のインストール範囲は、最初はすべてのリポジトリにしていましたが、権限を絞ったほうがいいと考え直し、GitHub のアカウントの設定から練習用のリポジトリだけに変更しました。

workflow を書き換える

テンプレートの workflow は、@claude のメンションに Claude が応えるだけのものです。これを、前回作ったサブエージェントを起動し、PR の作成まで進める形に書き換えました。workflow の変更も、Claude Code に方針を相談して Issue を立て、PR を作って進めました。

Claude Code GitHub Actions の動き方には、2 つのモードがあります(インタラクティブモードと自動化モード)。

  • インタラクティブモード:workflow に prompt を書かない。Claude はコメントの @claude を待って、その依頼に応える。進捗や結果は Issue や PR のコメントに表示される
  • 自動化モード:workflow に prompt を書く。Claude はそのプロンプトに従って動く。結果はデフォルトでは workflow の実行ログに残る

今回の workflow は自動化モードとし、「Issue 番号を受け取ってサブエージェントに渡す」という手順を prompt で固定しました。また、発火のタイミングは、Issue のコメントに @claude がつくこととしました。なお、デフォルトで実行を起動できるのは、リポジトリへの書き込み権限を持つ人間のユーザーだけです(実行をトリガーできるユーザー)。

認証と権限

Claude を呼び出す認証には、API キーか、サブスクリプションの OAuth トークンを使います。今回は後者を使用したので、GitHub Actions 上での実行でも普段と同じサブスクリプションの枠から消費されることになります。このトークンはモデルを呼び出すことしかできず、トークン自体に権限を絞る機能は付属していません(長期トークンを生成する)。そのため、使用できるツールを --allowedTools で、1 回の実行の量を --max-turns と workflow のタイムアウトで抑えるなどの設定が必要になります。また、詳しくは後述しますが、インストールした Claude GitHub App に払い出してもらった GitHub の一時的な認証情報を使用して GitHub の操作をさせる場合は、操作可能なリポジトリの範囲を App の設定で絞っておくことができます。

今回は、workflow 中の --allowedTools に、ファイルの編集・git・gh コマンドの一部・テストの実行などを並べました。

動作確認

workflow を書き換えたあとに動作確認をしてみたところ、Issue にコメントで @claude と書いたことをトリガーとして、Actions 上でサブエージェントが起動し、手元のときと同じようにブランチを作ってテストを書き、PR を作るところまで進みました。

手元と同じ作法で動いたのは、リポジトリに置いた .claude/ のサブエージェントや hooks、CLAUDE.md を、checkout でそのままランナーに展開し、それを Actions 上の Claude Code が読み取ったからです。一方で、手元の ~/.claude にある設定はランナーには展開されないので、一部の内容については workflow 側で書き直す必要がありました。また、Claude の GitHub App はセキュリティ上の理由で workflow を書き換えられないようなので(FAQ)、.github/workflows/ を変える Issue は手元で進めることを促すように prompt を書き換えました。

作法を Skill やサブエージェントとしてリポジトリに置いておいたことで、実行する場所が変わっても、ほとんど書き直さずに済みました。ただ、一部の設定については手元と Actions で二重に用意することになったり、CLAUDE.md にもどちらで進めるかの使い分けを書く必要が出てきたりして、だんだん複雑になってきたとも感じています。

AI レビューの自動化

  • 【調べる】レビューの基準をどこに置くかを説明できるようにする
  • 【動かす】PR を作ったときに自動でレビューされるようにする

前回記事の AI コードレビューの節では、手元の /code-review でレビューを頼み、GitHub 上での自動レビューは後で試すことにしていました。ここでそれを試します。Claude の Code Review は Team / Enterprise プランでしか使えないので、前の項目と同じ Claude Code GitHub Actions で組みます。なお、GitHub 上で使える AI レビューには、GitHub Copilot のコードレビューや Gemini Code Assist などもあるようですが、今回は触っていません。

レビューの基準をどこに置くか

まず、レビューの基準をどこに置くかを Claude Code と相談しました。候補は 3 つです。

置き場所良い点気をつける点
workflow の prompt1 か所で完結する観点を変えるたびに workflow の変更になる。手元で使い回せない
CLAUDE.mdどこから実行しても読まれる。実装する側とレビューする側が同じ決まりを見るレビューの手順まで書くと、実装の会話にも毎回混ざる
.claude/ の下の Skill手順を分けて置ける。ふつうの PR で直せて、手元でも使えるprompt から確実に呼ぶ必要がある

最後の行について補足すると、claude-code-action は、.claude/ や CLAUDE.md を PR の版ではなく、マージ先のブランチ(main)の版に戻してから Claude を動かします(Which files come from the base branch on pull requests)。そのため、.claude/ の下にレビュー基準を置いておけば、レビューされる PR 自身が書き換えてもマージされるまでは効かないので、Claude Code が勝手にレビュー基準を緩めてズルをする、といったことを防げます。

選んだのは Skill に置くやり方です。レビューの手順や出力の形などを Skill(/review-pr)にまとめておいて、workflow の prompt では PR の番号をこの Skill に与えて実行させるようにしました。こうしておけば、手元で作業させた場合でも同じ観点でレビューできるようになります。

最終的な workflow の構成

レビュー用の workflow は、次のようになりました。

  • 発火は、main 向けの PR が作成されたとき(pull_request の opened)
  • Actions 上のエージェントが作った PR でも起動するよう、allowed_bots に claude[bot] を入れる(デフォルトでは、ボットが起こしたイベントでは動かない)
  • prompt は /review-pr <PR 番号> だけ
  • --allowedTools には、ファイルの読み取りと gh pr view・gh pr diff・gh pr comment などを入れる

動作確認では、手元で作った PR(作成者は私)と、前の項目の Actions 上のエージェントが作った PR(作成者は claude[bot])の両方で、作成してから 1 分ほどでレビューのコメントが付きました。なお、発火は opened だけにしたので、指摘を受けて push し直しても、レビューはもう一度は走りません。このあたりは変更の余地がありそうです。

また、このレビューの job は、指摘の有無にかかわらず成功で終わるので、マージを止める関門にはなりません。人がマージのボタンを押す前に読む材料、という位置づけとしています。

エージェントの権限を絞る

  • 【調べる】エージェントの PR を誰がどこで止めるかを説明できるようにする
  • 【動かす】手元のエージェントが使う GitHub の認証情報を、1 つのリポジトリに絞る

マージのボタンはいつも自分で押していますが、手元のエージェントは gh pr merge を実行できてしまう状態なので、これをどうにかします。

手元のエージェントの認証情報

手元の Claude Code は、Bash ツールで gh を実行して GitHub を操作しています。gh は gh auth login で得たトークンで動きますが、このトークンは自分のすべてのリポジトリに手が届き、期限もありません。gh auth token で平文のまま取り出せることも含めて、エージェントに持たせるには危ういという指摘もあります(gh CLIの認証トークンの扱いが怖すぎるので、暫定的な対応手段をまとめた)。

MCP サーバーに切り替える案も考えましたが、git の push は gh の認証情報で動くので、どちらにしても手元の認証情報は絞る必要があります。そこで、gh のまま fine-grained PAT(リポジトリ単位・権限単位で絞れる個人用アクセストークン)に切り替えることにしました。具体的には、gh は環境変数 GH_TOKEN のトークンを、保存済みの認証情報より優先して使います(gh help environment)。そのため、生成した PAT をこの環境変数にセットする部分を ~/.claude/settings.json の env に書けば(env)、Claude Code のセッションの中だけ PAT に切り替わり、自分のシェルの gh や git はこれまでどおりの認証情報で動く環境が手に入ります。

ただし、人間用の認証情報はマシン上に残っているので、エージェントがその気になれば、強い権限を取り戻せてしまいます(たとえば、GH_TOKEN を unset するだけで、Claude Code も人間用の認証情報を使い始める)。そのため、本来は PC 全体の GitHub の認証の方針から見直すべきですが、今回はそこまでは立ち入らないことにしました。

PAT の権限

GitHub の Web 画面から PAT を作り、対象は練習用のリポジトリだけにしました。権限は、Contents・Issues・Pull requests を読み書き、Actions・Commit statuses・Metadata を読み取りのみにしています。

Workflows の権限は付けないこととしました。Claude Code に相談したところ、workflow を push できる権限があると、push のたびに動く workflow を書いて、レビューの前にリポジトリの secret を使った任意の処理を実行できてしまう、と指摘されたためです。AWS で、IAM ユーザー自身には許可されていない操作を、CloudFormation を経由して実行できてしまう話を思い出しました(いわゆる権限昇格)。これにより、Claude Code は workflow をいじる Issue を自分だけでは完遂できなくなったので、その場合はコミットまで進めて止まり、push は人間に依頼する、という手順を新たな運用ルールとして CLAUDE.md に足すことにしました。

一方、マージを PAT で止めることはできません。マージに必要なのは Contents の書き込み権限で(詳細に制御された個人用アクセス トークンに必要なアクセス許可)、これは push にも必要だからです。そこで、~/.claude/settings.json の permissions.deny に、gh pr merge・強制 push・main への push を止めるルールを足しました。

ここまでの設定を済ませ、Claude Code を再起動して gh auth status を実行すると、GH_TOKEN のトークンが使われていることを確認できました。さらに、ほかの勉強用の private リポジトリを gh repo view で見ようとすると、Claude Code の中からは Could not resolve to a Repository と返り、別のターミナルからは普通に表示されることを確認しました。

Actions 側の認証情報

GitHub Actions には、job ごとに発行される GITHUB_TOKEN があり、workflow の permissions: で権限を与えます。ところが、GITHUB_TOKEN によって起きたイベントでは、新しい workflow の実行が作られません(GITHUB_TOKEN)。無限ループを防ぐための仕様ですが、これだとエージェントが push しても、テストの workflow が動きません。

そこで Claude Code GitHub Actions には、workflow に id-token: write の権限を与えて GitHub の OIDC トークンを発行させ、それを Anthropic に送り、Claude GitHub App としての一時的なトークンと引き換えるという機能があります。この App トークンを使用して GitHub を操作すれば、それに起因したイベントで問題なく新しい workflow が実行されます。

この App のトークンも Contents の書き込み権限を持つので、手元の PAT と同じく PR のマージができてしまいます。そこで Actions 側でも --disallowedTools で、手元と同じルールを並べました。ちなみに、どちらもコマンドの文字列で照合するので、書き方を変えればすり抜けられ、100% は防げません。それを承知のうえで、この粒度で妥協しています。

ブランチ保護

今回使用している GitHub の認証情報(PAT、App のトークン)では PR のマージをピンポイントで止めることができないので、手元でも Actions でも、Claude Code 側の設定(permissions.deny や --disallowedTools)で止めるようにしています。ただ、GitHub Pro 以上のプランなら、ブランチ保護(保護されたブランチについて)により、GitHub 側の設定だけでもう少しスマートに制御することができます。具体的には、強制 push の禁止やステータスチェックの必須化を GitHub 側で強制でき、テストが落ちている PR をうっかりマージすることもなくなります。ただ、それでもマージそのものを止めるには承認数 1 以上の必須レビューが要り、個人開発では使いにくいので、今回はブランチ保護は扱わない方針としています。

おわりに

今回は、Issue のコメントから PR までを Actions 上で任せ、PR を開いたら自動でレビューさせ、最後にエージェントの手が届く範囲を絞りました。AI にやってもらうだけなら簡単ですが、その仕組みを理解するのは大変でした。特に権限まわりは、Claude を呼ぶトークン、GITHUB_TOKEN、App のトークン、PAT と認証情報が次々に出てきて、何がどこに効いているのかを整理するのに時間がかかりました。項目ごとに「調べる」と「動かす」を分けて進めたことで、動かしたあとに仕組みを調べ直す流れが自然に作れたと思います。

また、実装までリモートで任せると、権限も CLAUDE.md も手元と Actions の二重になり、思った以上に複雑でした。リモートでの実行は CI 関連のもの(テストやレビュー)に留めて、実装をリモートで任せるのは後に回してもよいのかもしれません。一方で、Issue だけ立てておけば後は PC に張り付いている必要がなく、外出先からでも少しずつ実装を任せられる、という使い方には手応えがありました。実装する環境を手元から持ち出せるようにしておくことは、どこかで改めて取り組みたいテーマです。

次は協働者編④として、クラウドの環境やスケジュールでエージェントを動かすなど、手元を離れて任せるところへ進む予定です。

コメント

タイトルとURLをコピーしました