執筆時現在(2026/1/8)、世間はClaude OpusやSkillsの登場で圧倒的にClaude一強を感じています。
我が(?)GitHub Copilotも近日中にリリース予定のVSCode January 2026アップデートでGitHub Copilot ChatのSkillsに対応予定(既にInsidersではリリース済み)ですが、今後もしばらくClaude一強の状況はあまり変わらないんじゃないかなあと俯瞰しています。
リリースされました
December 2025 (version 1.108)
ですがGitHub CopilotもVSCode November 2025アップデートでrunSubagentツールを使用しカスタムエージェント(.github/agents/*.agent.md)を明示的に呼び出し可能になり、飛躍的に便利になっています。
サブエージェント
GitHub Copilotのサブエージェントはメインエージェント(我々ユーザーがチャットを打って返答を行うエージェント)と独立しメインエージェントから指示を受けて作業を行い結果をメインエージェントに返すという機能です。
サブエージェントの嬉しいところはメインエージェントから完全独立した環境であるということです。
つまりメインエージェントに入力される我々ユーザーからの大量のインプットコンテキスト、MCPサーバー等から渡されるコンテキストから完全に分離し、必要最小のコンテキストのみを持って作業を行うことができます。
サブエージェントの特徴
- メインエージェントとは独立したコンテキストで稼働する
- メインエージェントから渡されたコンテキスト以外は共有しない
- メインエージェントのinputに対してoutputを返すのみ
- そのためサブエージェントがチャット出力を行うことはできない
- コンテキストが分離するので単一作業の精度は上昇するが、その分最終的にメインエージェントがチャット応答して返答というトータルでは時間がかかる
サブエージェントの実行とトータルの時間はトレードオフです、当然サブエージェントの呼び出しが増えれば増えるだけ時間がかかります。
そのため時間がかかっても正確性を担保したい、複雑なワークフローを的確に実行してほしい場面ではサブエージェント化が非常に有用です。
反対に現在カスタムプロンプトで一連のワークフローに満足している場合サブエージェント導入の恩恵は薄いと思います。
参考記事情報
サブエージェントについてはJunya YamaguchiさんがZennで公開してくださってる記事が大変参考になりますのでぜひご一読ください。
サブエージェント導入以前
今まで私は複雑なワークフローを一つのカスタムプロンプト(.github/prompts/*.prompt.md)に定義し、スラッシュコマンドで呼び出していました。
- レビューを行いファイルを生成するreviewコマンド(
.github/prompts/review.prompt.md) - それを修正するためのreview-fixコマンド(
.github/commands/review-fix.prompt.md)
理想の挙動に近づけようとしたら私の場合どんどん内容が追加されてしまい、最も安定して稼働するのが下記のようなカスタムプロンプトでした。
.github/prompts/review.prompt.md
---
agent: "agent"
tools: ['vscode/vscodeAPI', 'read/problems', 'read/readFile', 'edit', 'search', 'web/fetch', 'context7/*', 'git-mcp-server/git_branch', 'git-mcp-server/git_diff', 'git-mcp-server/git_log', 'git-mcp-server/git_set_working_dir', 'git-mcp-server/git_status', 'github-mcp/issue_read', 'github-mcp/list_branches', 'github-mcp/pull_request_read', 'github-mcp/request_copilot_review', 'github-mcp/search_code', 'github-mcp/search_issues', 'github-mcp/search_pull_requests', 'github-mcp/search_repositories', 'datetime/*', 'todo']
description: "コードレビュー用プロンプト (MCP Server 用)"
---
# ルール
- **重要: `/review` が呼ばれた際は直前の会話を一度リセットし、必ず指示に従って作業を行う**
- すべて日本語で記述する
- ただし一般的な固有名詞、プログラミングに関する用語、ライブラリ/フレームワーク/関数/変数/クラス/ファイル/ディレクトリ名等は英語表記を許可する
- ですます調は禁止
- 曖昧な表現は避け、具体的かつ明確に記述する
- なぜその修正が必要なのか、理由を明確に説明する
- 手順を省略せず実行する
- レビューするべきコードの差分が存在しない場合、レビューは不要であるためその旨を出力して終了する
- ファイル名、フロントマター等で日時にプレースホルダを使用しない
- 必ず指示時点での現在時刻を明示的に取得し設定する
## レビューで行うこと
レビューでは良い点の指摘は不要、問題点の指摘と改善提案に集中する
- issue の要件を満たしているかどうか
- コードの品質(コーディング規約、DRY 原則違反、冗長な表現や短縮変数名の使用など)
- セキュリティ上の懸念点
- パフォーマンスの問題
もしコードの改善を提案する場合、必ず根拠となる実装を確認してからレビューを行うこと
特に関数呼び出し等の場合、関数の実装を確認せずにレビューを行うことは禁止
## レビューで行わないこと
- 型チェック、リントチェックの実施提案
- テストコードの有無確認、E2Eテストやユニットテストの実施提案
- ドキュメントの有無の確認
- ただしプロジェクト固有のルールを遵守するために必要なドキュメントが存在しない場合、ドキュメントの作成や追記の提案は許可される
- レビューはあくまでカレントブランチと main ブランチの差分に対して行い、main ブランチに存在するコード全体のレビューを行うことは禁止
### 型注釈
- プロジェクトの基本方針は型推論で十分な場合は型推論を使用し、明示的な型注釈は最小限に抑える
- そのため不必要な型注釈、`as` 型アサーションの使用、過度なジェネリクス指定等を推奨しない
- ただし明示的に型注釈を追加するべき理由がある場合、合理的な型アサーションや適度なジェネリクス指定は許容される
- `as const` の使用は推奨されるため上記には該当しない
## 事前準備
1. `git_set_working_dir`に`${workspaceRoot}`をセットする
2. カレントブランチ名を取得する
3. issue 番号はブランチ名に含まれているので、`/issue[-_/]?(\d+)/` の正規表現を用いて、ブランチ名から issue 番号を抽出する
4. 該当の issue 番号の issue 及び issue コメントを取得する
- 必ず issue コメントも取得し、最終的な issue の要件を正確に把握すること
5. 変数 `${currentDatetime}` と `${type}` を初期化する
- `${currentDatetime}`
- `mcp_datetime_get_current_time` を使用し現在の日時を取得する
- 以下のパラメーターを使用する
- `{format: "custom", customFormat: "YYYY-MM-DD_HH-mm-ss", timezone: "Asia/Tokyo"}`
- `${type}`
- オプション `-r` が指定または指定がない場合、 `local` をセットする
- オプション `-p` が指定された場合、 `pr_copilot` をセットする
## オプション
コマンドに以下のオプションが指定されている場合、オプションに従う
オプション指定がない場合、デフォルトの`-r`として動作する
- `-r` `--review`: カレントブランチと main ブランチの差分に対するコードレビューを実行(デフォルト)
- `-f <file_name>` `--file <file_name>`: 指定されたファイルに対して`-r`オプション同等のコードレビューを実行
- `-p` `--pr-review`: 指定された Pull Request に対して行われた GitHub Copilot Code Review のレビューのレビューファイルを作成
## オプション -r またはオプション無しの場合のレビュー手順
1. カレントブランチと main ブランチの差分を取得する
- カレントブランチが main ブランチの場合、レビューは不要なので終了する
2. 差分に対してコードレビューを実行する
- コードレビュー時は `./.github/copilot-instructions.md` や `./.github/instructions/*` 、`./document/*` の内容を参照すること
- コンテキストに含めた `./.plans/issue<issue_number>/review/*.md` の内容をチェックし、重複した指摘や矛盾のある実装を避ける
- `${workspaceRoot}/.plans/issue<issue_number>/review/*.md` が存在するか確認し、存在する場合既に同じ指摘を過去に行っていないか確認する
- このディレクトリ内のファイルには過去のレビューと修正結果や判断が記載されており、重複した指摘や矛盾のある実装を避けるために必須
- `.plans/` ディレクトリは**必ず list_dir ツールを使用して存在確認を行うこと**
- ファイルが複数ある場合全てチェックする
- 過去既に解決済みのレビューはこの時点で除外し、同様のレビューを繰り返さないこと
- 1ID1レビューを厳守する
- 同一ファイルへの指摘であっても、複数の指摘がある場合はIDごとに分割する
- 例: 「変数名が不適切、useEffectのルール違反等2箇所の修正が推奨される場合別々のIDで指摘する」は2つのルール違反なので2IDに分割する
3. レビュー結果を出力する
- レビューの結果1件以上の指摘が存在する場合
- チャットウインドウにレビュー結果を出力する
- `${workspaceRoot}/.plans/issue<issue_number>/review/${currentDatetime}.md` にレビュー結果を保存する
- レビューの結果指摘が存在しない場合
- チャットウインドウにレビューは不要である旨を出力する
- マークダウンファイルの作成は行わずに終了する
必ず
- チャットウインドウによる出力(必須)
- マークダウンファイルによる出力(1件以上の指摘が存在する場合のみ)
の両方を実施すること
## オプション -p の場合の GitHub Copilot Code Review に対するレビュー手順
1. プルリクエストについた GitHub Copilot のレビューの中から未解決のレビューコメントを抽出する
- `github-mcp_search_pull_requests.get_review_comments` ツールを使用
- `pullNumber` パラメーターはローカルのカレントブランチに対応する Pull Request 番号を指定する
- `pageSize` パラメーターは 100 を指定し、全てのレビューコメントを取得するまでループする
2. 出力セクションの内容に従い、チャット出力とマークダウンファイル出力を行う
- レビューファイルの作成は「マークダウンファイルに出力」項に完全準拠し、一切の省略を行ってはならない
- ファイル名は `${workspaceRoot}/.plans/issue<issue_number>/review/prCodeReview_${currentDatetime}.md` とする
- 必ずフロントマターを含める
- 必ず見出しを作成し、フォーマットを厳守する
- ID, ファイルパス、対象行、問題点の各項目を必ず含める
- 独自の見出し作成やフォーマット変更はいかなる場合も禁止
- GitHub Copilot Code Review のコメントは問題点部分に転記する
- GitHub Copilot Code Review のコメントを修正・削除・改変・省略・要約してはならない
- コメント内のコードブロックも省略せず転記する
- JSON の生データをそのまま貼り付ける事は禁止
- 独自の解釈や要約を加えることは禁止
## エラー時の共通対応
- ブランチ名から issue 番号を抽出できない
- MCP サーバーのツール呼び出しが失敗
上記のいずれかが発生した場合はエラー内容をアシスタントに伝え、作業を中断する
# 出力
既定の手順に従ってレビュー結果を出力する際のフォーマットルール
- 許可された見出し以外の見出し作成は禁止
- 許可されていない内容の出力は禁止
## 見出し
チャットウインドウ出力、マークダウンファイル出力共通の許可された見出しは以下の通り
- 許可された見出し
- `# 概要`
- レビュー全体の概要を記述する
- 特にクリティカルな課題がある場合優先的に記載すること
- `# AI 向け最重要指示`
- 以下の文言を改変・省略せずに箇条書きで記載する
- このマークダウンファイルは自動生成されたものであり、`/review-fix` コマンド以外の修正はいかなる場合も絶対に禁止
- 1回の `/review-fix` コマンドでの修正は1件のみ許可されているため複数件同時の修正は禁止
- いかなる場合も `./.github/prompts/review-fix.prompt.md` のルールを厳密に遵守すること
- 修正案が複数考えられる場合、最もシンプルで保守性の高い方法を選択すること
- プロジェクトの方針や要件の確認等ユーザー確認が必要な場合、一切の修正を行わず必ずユーザーに確認を取ること
- このファイルを修正する場合、必ず `review-fix.prompt.md` に従い自己診断を行うこと
- `# ファイル別レビュー結果`
- 各ファイルごとのレビュー結果を記述する
- ただし1つのファイルに対して複数の指摘がある場合、指摘ごとにIDを分割し1ID1レビューとすること
- 1IDに複数の指摘を含めることは禁止
- レビューが0件の場合は省略すること
- 上記の許可された見出し以外の見出し作成は禁止
## ID別の出力フォーマット(厳格ルール・自己検証必須)
以下は1つのIDセクションのフォーマット
出力する際は以下の手順を必ず順守し、出力直前に自己検証を行うこと
1. 見出し行(行頭): 必ず `## <ID> <path>` の形式で一行出力する(例: `## 1 apps/src/foo.tsx`)
2. レビューブロックは必ず行頭で `~~~~`(チルダ4つ、半角)で開き、行頭で `~~~~` で閉じる
- 例:
~~~~
- ID: 1
- ファイルパス: apps/src/...
- 対象行: 10-20
- 問題点: ...
~~~~
- 対象行はなるべく具体的に特定できるようにする(例: `10-20`、`35-40, 50-55` など、最大20行を目安にする)
3. レビューブロック内の各項目は必ず行頭に `- ` を付与し箇条書き形式で出力する
- 項目名と値の間は必ず `: `(コロン+半角スペース)で区切ること
- 項目名と値の間に不可視文字や余分な空白を入れないこと
- 項目名は必ず以下の通りに記載し、それ以外の項目を追加してはならない
- ID
- ファイルパス
- 対象行
- 問題点
- 詳細
- 修正案
- 問題点はレビュー内容を1行で要約した内容を記載する
- 詳細は問題点の具体的な説明を記載する
- 必要に応じてコードスニペットを含めることができる
- 具体的にどんな問題があるのか、なぜ問題なのかを明確に説明すること
- 修正案は具体的な修正方法を記載する
- 可能な限り具体的なコード例を含めること
- すべてのフェンス行は行頭に置くこと(前にスペース/タブ/不可視文字を入れない)
4. ブロック内にコードスニペットを含める場合は、ブロック内で必ず行頭 `~~~`(チルダ3つ)で開閉する
- チルダ4つ、バッククォートによるコードスニペットの囲みは禁止
- コードスニペットの例:
~~~
const x = 10;
const userAge = 10;
~~~
- コードスニペットを含める場合、必ず先頭のチルダ3つの直前と最後の直後に空行を入れる
5. `<REVIEW_FIX_SLOT:ID=${id} />` をレビューブロックの直後に必ず一行で出力する
- 必ず行頭に置くこと(前にスペース/タブ/不可視文字を入れない)
- 上下一行ずつ空行を入れること
6. 半角チルダ `~` のみ使用する(全角や類似文字禁止)
7. 出力直前に次を自己検証すること
- 見出しが行頭にあるか
- 見出しの直下はレビューブロックの開始行 `~~~~` であるか
- 外側フェンスは `~~~~`、内側コードフェンスは `~~~` が使われているか
- フェンスの開閉が対になっているか
- フェンス行に不可視文字や余分な空白がないか
- `<REVIEW_FIX_SLOT:ID=${id} />` がレビューブロック直後に正しく設置されているか
- 不可視文字や余分な空白がないか
- 直前と直後に空行があるか
- レビューブロックの最後が `<REVIEW_FIX_SLOT:ID=${id} />` +空行で終わっているか
- その他許可されていないフォーマットが使用されていないか
### チャットウインドウに出力
チャットウインドウに出力する際は以下のルールに従う
- チャットウインドウへの出力は必須
- 内容を要約したり省略する事は禁止
- 特に「ファイル別レビュー結果」を抜粋したり省略する事は禁止
- 必ずチャット末尾に作成したマークダウンファイルのパスを記載する
- パスはマークダウン形式で出力を行い、確実にリンクをクリックできるようにすること
### マークダウンファイルに出力
マークダウンファイルに出力する際は以下のルールに従う
- 必ず `edit/create_file` ツールを使用し、`${workspaceRoot}/.plans/issue<issue_number>/review/${currentDatetime}.md` に出力する
- フロントマターフォーマットの項目に従い作成し必ずファイル冒頭に記述する
#### フロントマターフォーマット
---
reviewID: `${currentDatetime}`
issueNumber: <issue番号>
prNumber: <PR番号 / PRが存在しない場合は空欄>
reviewDate: `${currentDatetime}`
lastUpdate: `${currentDatetime}`
type: `${type}`
status: in_progress
progress:
total: <総レビュー件数>
fixed: 0
skipped: 0
untouched: <総レビュー件数と同じ値>
summaries:
- id: 1
status: open
summary: <レビュー1のサマリー>
filePath: <レビュー1のファイルパス>
---
- フロントマターの value 値内では以下の記号の使用を避ける
- バッククオート(`)
- コロン(:)
## 自己検証手順
レビューファイルを作成した後、以下の自己検証を行うこと
自己検証の結果、1つでも不備があった場合は修正を行い、再度自己検証を行う
- レビューファイルが確実に存在しているか
- `./.plans/issue<issue_number>/review/` ディレクトリ内にレビューファイルが存在しているか
- この自己診断は `read/readFile` ツールを使用し必ずファイルの存在を保証すること
- フロントマターが正しく設置されているか
- ファイルの1行目はフロントマターの開始行 `---` であるか
- フロントマターの終了行 `---` が正しく設置されているか
- 各フィールドが正しく設置されているか
- 概要見出しが存在するか
- 概要の中には特に優先度の高いレビュー要約が含まれているか
- AI 向け最重要指示見出しが存在するか
- 指示文が正しく記載されているか
- ファイル別レビュー結果見出しが存在するか
- 各レビュー ID セクションが正しいフォーマットで記載されているか
- 各レビュー ID セクションの見出し直下はブロックの始まり(`~~~~`)であるか
- 各レビュー ID セクションのレビューブロックが正しいフォーマットで記載されているか
- 各レビューブロック内の各項目が正しく記載されているか
- フェンス行が正しく記載されているか
- 概要、AI 向け最重要指示、ファイル別レビュー結果以外の見出しが存在していないか
- 上記3つの見出しレベルがトップレベル(`#`)であるか
.github/prompts/review-fix.prompt.md
---
agent: "agent"
tools: ['vscode/vscodeAPI', 'read/problems', 'read/readFile', 'edit', 'search', 'web/fetch', 'context7/*', 'git-mcp-server/git_branch', 'git-mcp-server/git_diff', 'git-mcp-server/git_log', 'git-mcp-server/git_set_working_dir', 'git-mcp-server/git_status', 'github-mcp/issue_read', 'github-mcp/list_branches', 'github-mcp/pull_request_read', 'github-mcp/request_copilot_review', 'github-mcp/search_code', 'github-mcp/search_issues', 'github-mcp/search_pull_requests', 'github-mcp/search_repositories', 'datetime/*', 'todo']
description: "コード修正用プロンプト"
---
# ルール
この `/review-fix` は `/review` で指摘された問題点に対して、具体的なコードの検証・修正・提案を行うためのプロンプト。
## 前提条件
- 必ずコンテキストとして `./.plans/issue<issue_number>/review/*.md` を参照すること
- 受け取ったレビュー Markdown ファイルの内容を厳密に遵守し、指摘された問題点に対してのみ修正を行うこと
- レビューで指摘されていない箇所の修正は一切禁止
- ただしレビューの指摘箇所の修正の過程で他の箇所の修正が必要な場合、その理由を明確に説明した上で修正を行うこと
## 厳守するルール
- すべて日本語で記述すること
- 常に常体を使用し、ですます調での記述は禁止
- 特に「レビューレスポンス」項はレビュワーに対するレスポンスなので敬語を選択肢がちだが必ず常体を使用する
- 修正案が複数考えられる場合、最もシンプルで保守性の高い方法を選択すること
- ライブラリに関連する修正は必ず `context7` ツールや `search` ツールを使用して最新のドキュメントを参照し、最適な方法を選択すること
- この時必ずライブラリのバージョンを確認し、バージョンに適した方法を選択すること
- 該当バージョンで非推奨となっている方法を選択してはならない
- 修正前に必ずレビューの妥当性を検証し、妥当ではないと判断した場合は修正を行わず、その理由を明確に説明すること
- 必ずプロジェクトルールを確認し、プロジェクトルールに反した修正案ではないことを確認の上妥当性を判断すること
- プロジェクトルールとは以下のファイル群を確認することを指す
- `./.github/copilot-instructions.md`
- `./.github/instructions/*`
- `./document/*`
- レビューのID、ファイルパス、対象行、問題点、詳細等チルダ4つでマークアップされた内容は絶対に変更しないこと
- この prompt では妥当性の判断、理由の記載、具体的な修正内容の記載のみを行うこと
- レビューファイル(`./.plans/issue<issue_number>/review/*.md`)に対して、全文の再生成・再出力・構造変更・冒頭への追記を一切行ってはならない
- レビューファイルへの修正結果は、**必ず unified diff(```diff```)のみで出力すること**
- diff 以外の Markdown 本文・説明文・箇条書きをレビューファイルに出力してはならない
- diff で変更してよいのは次の2点のみとする
- フロントマターのうち、後述する「フロントマターの更新ルール」で許可されているフィールド
- ID 別セクションの `<REVIEW_FIX_SLOT:ID=${id} />` 部分
- 上記以外(概要、見出し、他ID、既存レビュー本文、順序、区切り線など)の変更は禁止
- コード修正後、必ず `read/problems` ツールを使用してコード上にエラーが発生していないか確認すること
## 注意点
- CI やテスト、型チェックやリンター実行の実施依頼を行わないこと
- git コマンドの実行提案・チャットアシスタントによる実行は行わないこと
- git 操作はユーザーが確認しユーザー自身が行うため一切の操作を禁止
- ただし過去の差分確認のため許可された git-mcp-server ツール、 github-mcp-server ツールの使用は可能
# 修正の手順
- 以下は実際の修正手順やフォーマットについての詳細な説明
- 必ず手順やフォーマットを遵守し、勝手な判断で許可されていないことを行わない
## 事前準備
1. コンテキストとして受け取ったレビューファイルを確認し、正しいレビューファイルかどうか検証する
- 以下が1つでも `false` の場合、修正を行わずに正しいレビューファイルではない旨を出力して終了する
- フロントマターが存在するか
- フロントマターの各種フィールドが正しく存在しているか
- ファイル別レビュー結果セクションが存在するか
2. 変数 `${currentDatetime}` と `${id}` を初期化する
- `${currentDatetime}` は `mcp_datetime_get_current_time` を使用し現在の日時を取得する
- 以下のパラメーターを使用する
- `{format: "custom", customFormat: "YYYY-MM-DD_HH-mm-ss", timezone: "Asia/Tokyo"}`
- `${id}` には修正対象のレビュー ID をセットする
- `summaries.${id}.status` を確認し、最上位の `open` のレビュー ID をセットする
- `summaries.${id}.status` が `open` のレビューが存在しない場合、修正を行わずにその旨を出力して終了する
## 修正手順
1. `${id}` のレビュー内容の妥当性を検証し、修正するべきかどうか判断する
- 修正の検証には必要に応じて `context7/*` ツールや `search` ツールを使用して過去のコードやドキュメントを参照することが可能
- 修正の妥当性についてユーザーに確認するべきと判断した場合は以降の手順を全てスキップし、確認事項を明確に説明する
- レビューファイルの更新は禁止
- チャット出力のみ許可
- 妥当ではないと判断した場合、ステップ2をスキップしコード修正を行わない
2. レビューが妥当な場合、修正案を提示し必要なコード修正を行う
3. 「レビューファイルの更新」セクションに従い更新する
4. フロントマターを「フロントマターの更新ルール」に従い更新する
5. 「チャット出力フォーマット」に従ってチャットに出力する
### フロントマターの更新ルール
レビューファイルにはフロントマターが含まれており、作業において以下のルールを遵守すること
- このプロンプトで変更が許容されるフィールドは以下の通り
- `lastUpdate`: `${currentDatetime}` を使用し更新
- `status`: `progress.untouched === 0 ? "completed" : "in_progress"` を評価して設定
- `progress.fixed`: 修正完了の場合だけ `1` インクリメント
- `progress.skipped`: 対応不要の場合だけ `1` インクリメント
- `progress.untouched`: 修正または対応不要にした分だけ `1` デクリメント
- `summaries.${id}.status`: デフォルト `open`、修正完了は `fixed`、対応不要は `closed` を設定
- 使用できるステータスは「許可されるステータス一覧」を参照
- 上記以外のフィールドは一切変更してはならない
### 許可されるステータス一覧
- `summaries.${id}.status` に設定可能な値は以下の通り
| 値 | 説明 |
| --- | --- |
| `open` | レビューに対してまだ未対応であることを示す(初期値) |
| `fixed` | レビューに対して修正が完了したことを示す |
| `closed` | レビューに対して対応が不要であることを示す / レビューが不適切である場合や修正が不要と判断された場合に使用 |
## レビューファイルの更新
レビューファイルは以下のルールを遵守し更新を行うこと
- レビューファイルの更新は必ず妥当性検証、コード修正が必要な場合コード修正が終わってから更新を行うこと
- 実際のコード修正を行っていないのに「行った」という前提でレビューファイルを更新してはならない
- ただし妥当性検証の結果、修正が不要と判断した場合はコード修正を行わずにレビューファイルを更新してもよい
- フロントマターの許可されたフィールド、`${id}` セクション内の `<REVIEW_FIX_SLOT:ID=${id} />` セクション以外の変更は禁止
- 「review-fix セクションの追加手順」に従って `### review-fix` セクションを追加する
- 「具体的な修正内容」と「対応不要の詳細」セクションが同時に存在してはならない
- 当該 ID セクションの最終行と次のセクションのタイトルの間に一行の空行を挿入すること
### フロントマターの更新手順
1. `lastUpdate` を `${currentDatetime}` を使用し更新する
2. `progress` の各フィールドを以下のルールで更新する
- `fixed`: `summaries.${id}.status` が `fixed` の場合、`1` インクリメントする
- `skipped`: `summaries.${id}.status` が `closed` の場合、`1` インクリメントする
- `untouched`: `summaries.${id}.status` が `fixed` または `closed` の場合、`1` デクリメントする
3. `summaries.${id}.status` を以下のルールで更新する
- 「修正完了」の場合、`fixed` に設定
- 「対応不要」の場合、`closed` に設定
4. `status` を以下のルールで更新する
- `progress.untouched === 0 ? "completed" : "in_progress"`
### review-fix セクションの追加手順
1. `<REVIEW_FIX_SLOT:ID=${id} />` の ID が `${id}` と同じタグを特定する
2. `### review-fix` セクションを作成し、以下の内容を記載する
- 妥当性: <妥当 / 妥当ではない / ユーザー確認必要 のいずれか>
- 理由: <妥当性の理由を具体的に記載>
- 具体的な修正内容: <修正した場合のみ、具体的な修正内容を記載>
- コードスニペットがある場合はコードスニペットで囲む
- コードスニペットが差分の場合、マークダウンの言語指定に `diff` を使用する
- 対応不要の詳細: <対応不要の場合のみ、対応不要の理由を具体的に記載>
- 注意点:
- 基本的に注意点セクションは作成禁止、どうしても注意点セクションでしか記述できない内容がある場合のみ作成する
- 未知の前提を元に注意点を作成することは禁止
- 例: 「将来的に xxx が yyy になる可能性があるため注意」など
- レビューレスポンス: <フロントマターの `type === pr_copilot` の場合のみ>
- レビューレスポンスは GitHub Copilot Code Review のレビューレスポンスとして使用される
- `type === pr_copilot` の場合、レビューレスポンスは必須
- 箇条書き禁止
- 常体を使用する(ですます調は禁止)
- 1〜3行程度の簡潔な文章で、修正内容を要約して記載する
3. `<REVIEW_FIX_SLOT:ID=${id} />` タグを作成した `### review-fix` セクションに置換する
- 必ず追記ではなくタグを置換すること
- `### review-fix` セクションの各行の行頭にスペース/タブ/不可視文字を入れてはならない
- ただしインデントが必要な部分にのみインデントを入れることは許可される
#### 最終的に期待する `${id}` セクションの差分
```diff
- <REVIEW_FIX_SLOT:ID=${id} />
+ ### review-fix
+ // 内容は上記「review-fix セクションの追加手順」に従う
```
#### review-fix 自己検証手順
`<REVIEW_FIX_SLOT:ID=${id} />` タグを作成した `### review-fix` セクションに置換した後、以下の自己検証を行うこと
1. `<REVIEW_FIX_SLOT:ID=${id} />` タグが `### review-fix` セクションがに正しく置換されているか
- `${id}` セクション内に追加されているか
- `~~~~` 行の直下に空行を挟んで `### review-fix` セクションが追加されているか
- `<REVIEW_FIX_SLOT:ID=${id} />` タグが削除されているか
2. `### review-fix` セクション内に必須項目がすべて記載されているか
- 妥当性
- 理由
- 具体的な修正内容 または 対応不要の詳細
- 「具体的な修正内容」と「対応不要の詳細」セクションが同時に存在していないか
- (`type === pr_copilot` の場合のみ)レビューレスポンスが記載されているか
- レビューレスポンスの文体が常体になっているか
3. `### review-fix` 最終行と `${id + 1}` セクションの見出し行(またはファイル末尾)の間に1行の空行が挿入されているか
4. `<REVIEW_FIX_SLOT:ID=${id} />` タグが `${id}` セクション内に残っていないか
5. `### review-fix` セクション部分の差分が「最終的に期待する `${id}` セクションの差分」と一致しているか
##### 自己検証の結果、不備があった場合の対応
- 再度パッチを当てるのではなく、一度レビューファイルを修正前の状態に戻し、再度レビューファイルの更新手順を最初からやり直すこと
- 自己検証の不備を放置してはならない
- 修正後再度自己検証を行い、すべての不備が解消されるまで繰り返すこと
## チャット出力フォーマット
- 修正内容を以下のテンプレートでチャットに出力する
- このテンプレートはあくまでチャット出力用のため、レビューファイルの更新には使用しないこと
```
## `${id}` <ファイルパス>
<既存のセクションをそのまま出力する、削除や修正は一切禁止>
### review-fix
- 妥当性: <妥当 / 妥当ではない / ユーザー確認必要 のいずれかを記載>
- 理由: <妥当性の理由を具体的に記載>
### 具体的な修正内容
<修正した場合のみ>
<コードスニペットがある場合はコードスニペットで囲む>
<コードスニペットが差分の場合、マークダウンの言語指定に `diff` を使用する>
### 対応不要の詳細
<対応不要の場合のみ>
### 注意点
<必要な場合のみ>
```
ざっくりこの2つをご紹介すると、/reviewコマンドでレビューファイルを生成し、/review-fixコマンドでレビューファイルを元に修正を行うというものです。
まず/reviewコマンドを実行すると次のようなマークダウンファイルがプロジェクトの.plans/<issueNumber>/review/review_YYYY-MM-DD_HH-mm-ss.mdに生成されます。
---
reviewID: 2025-12-31_02-11-48
issueNumber: 195
prNumber:
reviewDate: 2025-12-31_02-11-48
lastUpdate: 2025-12-31_02-21-04
type: local
status: in_progress
progress:
total: 1
fixed: 0
skipped: 0
untouched: 1
summaries:
- id: 1
status: open
summary: 型推論が可能な箇所に冗長な型注釈が記述
filePath: app/src/features/settings/AboutAppContentScreen.tsx
---
## 1 app/src/features/settings/AboutAppContentScreen.tsx
~~~~
- ID: 1
- ファイルパス: app/src/features/settings/AboutAppContentScreen.tsx
- 対象行: 33
- 問題点: useState において型推論が可能な箇所に冗長な型注釈が記述されている
- 詳細: `useState<string>('')` は初期値から型が明白であるため、明示的な型注釈は不要である。プロジェクトの方針として、型推論で十分な場合は型注釈を最小限に抑えることになっている。
- 修正案: `useState('')` に変更する。
~~~~
<REVIEW_FIX_SLOT:ID=1 />
次に、/review-fixコマンドでこのレビューの妥当性を検証し、妥当だと判断した場合はフロントマターを更新します。
最後に<REVIEW_FIX_SLOT:ID=1 />部分に/review-fixの妥当性判断、修正内容の差分を追加します。
- <REVIEW_FIX_SLOT:ID=1 />
+ ### review-fix
+ - 妥当性: 妥当
+ - 理由: 初期値から型が明白な場合、TypeScript の型推論を利用することでコードが簡潔になり、プロジェクトの「型注釈を最小限に抑える」という方針にも合致する。
+ - 具体的な修正内容:
+ ~~~diff
+ - const [content, setContent] = useState<string>('');
+ + const [content, setContent] = useState('');
+ ~~~
この形式を採用したのは次のような理由です。
- 修正履歴が残し、次のレビュー時に参照させることで重複レビューや同じ修正を繰り返し提案させるループを軽減したかった
- ただしgitやCLIを一切触らせない方針なので、コミットで管理する形式は避けたかった
- フロントマターを導入することでAIも人間もファイル冒頭でステータスを確認しやすくした
- フロントマター未導入前はマークダウンをとにかくぶっ壊してしまうことが多かった
- 明示的に
<REVIEW_FIX_SLOT:ID=${id} />を置換させる- 導入前は「修正するIDの見出しを見つけ、次の見出しの直前に挿入」等の指示を色々試したがどれもマークダウンを破壊してしまう
- 明示的に置換部分を提示することで修正範囲をできるだけ絞った結果、マークダウンを破壊してしまうことが少なくなった
しかしここまでやってもまだ完璧ではありませんでした。
-
<REVIEW_FIX_SLOT=${id} />を残したままreview-fixセクションを追加してしまう - 極稀にレビューファイル全体を上書きしてしまう
かなりプロンプトの改善を重ねてもこの辺りは100%にならなかったのは事実です。
ただし当然ですが高性能モデルを採用すれば話は別です、例えばClaude Haiku4.5以上のClaudeやGemini 3 Flash/Pro等プレミアムリクエストが×0以外のモデルならほぼ指示通りにこなしてくれます。
ですが私の場合予算が限られる個人開発者でありかつ何度も何度も「レビューファイル生成->レビューの妥当性検証と修正は1回ずつ」を繰り返すので、小さな作業ではなるべくプレミアムリクエスト×0のモデルを使用したいです。
根本的な欲求として、「なるべくプレミアムリクエスト消費しないモデルで小さなレビュー&修正を回したい」というものがありました。
そのためレビューは高性能モデルを使用、修正は対象をなるべく小さくして小さな修正をプレミアムリクエスト消費なしのモデルで行いたいというのが基本的な考え方です。
一つのカスタムプロンプトに詰め込むのは厳しい
上記のカスタムプロンプトの問題点は主に次の通りです。
- 使用するMCPツールが多すぎる(過剰コンテキストの原因)
- 複数の作業(コードレビュー、ファイル作成のフォーマット定義、引数オプションを使用することによる分岐、フォーマットやレビューに関するルール制定等)
- 重複した指示
MCPツールの詰め込み
SNS等で散々話題になっているので皆さんご存知だと思いますが、MCPツールは多ければ多いほどコンテキストを圧迫します。
GitHub Copilotはトークンベースではないのでコンテキスト圧迫が金銭面で影響を及ぼすことはありませんが、過剰コンテキストはそもそも生成AIの能力低下やそもそもコンテキスト上限に達すると作業を行ってくれない等の問題もあります。
複数の作業により指示を忘れる/性能低下問題
2に関してはドキュメントに分離しても良いのですがこれだけの指示が記載してあるとドキュメントの参照を行ってくれないことが多々発生します。
例えば型定義の指示がreview.prompt.mdに記述してありますが、これはReactのコンポーネントの戻り値がJSXなのは推論で十分なのに「明示するべき」とレビューを繰り返すため泣く泣く追加したものです。
明示的に各種ファイルを参照して欲しいと記述を行っても、コンテキスト過多になるとそれが必ず行われる保証はありません。
これは3の重複した指示にも共通していて、例えばreview.prompt.mdに関してはフォーマットに関する指示が何度も登場しています。
良くないとは思いつつ、最終的に理想とする形でレビューファイルの作成まで行ってくれたのがこの形でした。
しかし繰り返しですが最終形態でも問題が完全に0になることはありませんでした。
体感上記のプロンプトだと/reviewは30回に1回程度、/review-fixは20回に1回程度レビューファイルの生成・修正が完璧ではないことがありました。
カスタムエージェント対応
いよいよ本題です、ここで重要なのがカスタムエージェントです。
カスタムエージェント2025年10月末にGAリリースされた機能で、ざっくりいうと「Agentモード」や「Planモード」のような振る舞いを自分で定義して使えるよ、というような機能です。
カスタムエージェント登場以前はGitHub Copilotでは主に.github/copilot-instructions.md(Claudeで言うAGENTS.md、GitHub Copilotでも使用可)に様々定義して自動参照させるような使い方がメインでした。
それをエージェントとして切り出し、特定の振る舞いに特化させることができるようになったのがカスタムエージェントです。
それ以前も特定のディレクトリに対する指示を.github/instructions/*.instructions.mdに記述して「*.tsファイルを触る時は参照してね!」「backend/schema/のファイルを触る時は参照してね!」という個別の指示は書けたのですが、カスタムエージェントが登場したことによって以下のような嬉しいことがあります。
- 使用するMCPツールをエージェントごとに許可・制限できる
- やることやできること、できないこと等を細かく制御できる
今まで各種instructionsを参照していちいち「こうしてね」という指示をチャットで入力していたものを、全てファイルに定義できる形になりました。
| 種類 | できること |
|---|---|
instructions |
どう振る舞うかは定義できたが道具(MCPサーバー)の種類と何をするかはユーザーが別途指示する |
| カスタムエージェント | どう振る舞いどの道具を使えて何をするかを1ファイルに定義できる |
これまでの「振る舞い」と「MCPツール」を別々に管理していたものが、同じファイル内で定義することで「あなたはこの道具を使えるこういう人だよ」を一箇所に定義できるようになりました。
分かりやすく言えばカスタムエージェント登場以前はトンカチとノコギリとドライバーとチェーンソーを持っている人に「ネジ締めといて(道具はどれ使うか考えてね)」と指示をしていたイメージ。
カスタムエージェントを使えば「(ドライバーを持っている人に)ネジ締めといて」と指示するイメージでしょうか。
道具の選択を明示的に狭めることができるので、その分AIが判断に迷うことが格段に減る事は想像に難くありません。
しかし実はこれだけでは今回の記事の趣旨に達しませんでした、私はカスタムエージェントではなく先述したプロンプト達をカスタムプロンプト(.github/prompts/*.prompt.md)に定義して使っていたからです。
カスタムエージェントが登場しただけでは結局1ファイルに多数のMCPツールを定義して膨大な複数の指示を記述することに変わりありません。
状況が変わったのが組み込みMCPツールであるrunSubagentツールが拡充されたからです。
runSubagentツール
先述したZennの記事が非常に詳しく記載してくださっているので合わせてご覧いただくことをおすすめしますが、それまでrunSubagentツールはあくまで自然言語でサブエージェントを起動させるツールでした。
// サブエージェントを起動するよう指示
サブエージェントを起動して○○の処理をして結果を返してください。
// サブエージェントの結果を利用
サブエージェントの戻り値をチャットに出力してください。
例えばパっと思いつく使い方だと、複数の株価の情報を取得している時に「個別株価の情報はrunSubagentを使って取得する」のような使い方でしょうか。
サブエージェントを使うことでメインエージェントのコンテキストの肥大化を防ぎ、メインエージェントは「複数の株価の比較に専念する」のようなイメージ。
あくまで自然言語で指示を行う必要があったので、正直登場当時は私も「うーん・・・使わんなあ」という印象でした。
runSubagentツールがカスタムエージェントを呼び出し可能に
それがVSCode November 2025アップデートのRun agents as subagents (Experimental)(エージェントをサブエージェントとして実行する)で大きく進化しました。
今までは自然言語ベースで「これをサブエージェントでやってね」という使い方に留まっていたrunSubagentツールが、カスタムエージェント(.github/agents/*.agent.md)をサブエージェントとして実行できるようになりました。
// 振る舞い(やることや戻り値)はカスタムエージェントで定義するので親側で指定不要
サブエージェントで<*.agent.mdのname>を起動してください。
// サブエージェントの結果を利用
サブエージェントの戻り値をチャットに出力してください。
パっと見る限りさっきと変わりないように見えますがこれは単純な指示だからです。
「メインエージェントから受け取ったコンテキストを指定のフォーマットに整形して重複値を除去した上で既存の値を呼び出してマージしその内容をメインエージェントに返す」のような内容だと指示が何行にも及びますよね。
その自然言語での指示を一つのファイルに事前定義しておけるようになったことで、プロジェクト全体で同じ指示を複数の場所で再利用できるようになりました。
旧runSubagentツール |
新runSubagentツール |
|
|---|---|---|
| MCPツール | 一連のタスクで使用する全てのツールを許可しておく必要があった | そのエージェントで使用するツールだけを許可すればよくなった |
| 振る舞い | 自然言語でなにを行うか指示する必要があった |
*.agent.mdに定義した振る舞いを呼び出せるようになった |
私のケースで言えば今までreview.prompt.mdに全てを定義して1ファイルで実行していたことが、カスタムエージェント+runSubagentツールを組み合わせることで
- カスタムプロンプト(
review.prompt.md)は各種エージェントをサブエージェントとして呼び出すだけのオーケストレーター兼スラッシュコマンドを呼び出すエントリーポイント - カスタムエージェント(
*.agent.md)はメインエージェントから呼び出され事前定義された作業だけを行う専門家
として明確に分離することが可能となりました。
なぜカスタムエージェントなのか?
一度ここでなぜカスタムエージェントを使用するのかを整理しておきます。
特にカスタムプロンプトとカスタムエージェントはかなり似た概念です(私も果たして本当に今の使い方が最善なのかの答えはまだ出てないです…笑)。
| カスタムプロンプト | カスタムエージェント | |
|---|---|---|
| ファイル | .github/prompts/*.prompt.md |
.github/agents/*.agent.md |
| 使用MCPツール定義 | できる | できる |
| 振る舞いの定義 | できる | できる |
| スラッシュコマンド | できる | できない |
| モード選択で指定 | できない | できる |
runSubagentでの呼び出し |
できない | できる |
サブエージェントとして呼び出し可能なのはカスタムエージェント(*.agent.md)だけです。
逆にカスタムエージェントはスラッシュコマンドとして呼び出せません。
複雑なワークフローの部品の一つとして定義する、この時サブエージェントとして呼び出し可能なカスタムエージェントが適役です。
カスタムエージェント+runSubagent導入後の構成
先述したreview.prompt.mdは以下のようになりました。
---
agent: "agent"
tools: ['agent/runSubagent']
description: "コードレビュー用プロンプト"
---
# ルール
- すべて日本語で記述し、手順を省略せず実行すること
- 手順に定められていないエージェントの実行は禁止
- 各エージェントの出力値は必ず正確に受け取り、一切改変すること無く次の手順に渡すこと
- 会話内容は `${context}` として参照する
## オプション
コマンドに以下のオプションが指定されている場合、オプションに従う
オプション指定がない場合、デフォルトの`-r`として動作する
- `-r` `--review`: カレントブランチと main ブランチの差分に対するコードレビューを実行(デフォルト)
- `-f <file_name>` `--file <file_name>`: 指定されたファイルに対して`-r`オプション同等のコードレビューを実行
- `-p` `--pr-review`: 指定された Pull Request に対して行われた GitHub Copilot Code Review のレビューのレビューファイルを作成
### 共通準備
- `${issueNumber} = ${context}.issueNumber ?? runSubagent(_GetIssueNumberAgent())`
- `${prNumber} = ${context}.prNumber ?? runSubagent(_GetPullRequestNumberAgent())`
- `${fileName} = ${context}.fileName ?? null`
### 共通対応
- 各セクションの `${review}` が `null` の場合、レビュー対象が無い旨をチャット出力し以降の手順を全てスキップの上終了する
### エラー時の共通対応
- サブエージェントはエラー検知時に `{ "error": true, "agentName": "<エージェント名>", "result": "<詳細なエラー内容>" }` の形式でエラー内容を返す
- `error` フィールドが含まれている場合エラーと判断する
- エラーであると判断した場合、以降の手順を中断し エラーJSONをそのまま出力し終了する
- サブエージェントの呼び出しに失敗した場合も同様にエラーと判断し、以降の手順を中断し呼び出しに失敗したサブエージェント名とエラー内容をそのまま出力し終了する
## オプション -r またはオプション無しの場合のレビュー手順
1. `${review} = runSubagent(_DiffReviewAgent({ "issueNumber": "${issueNumber}" })).review`
- `${review}` が `null` の場合、差分がない旨をチャット出力し以降の手順を全てスキップの上終了する
2. `${formattedReview} = runSubagent(_WriteReviewFileAgent({ "type": "local", "issueNumber": "${issueNumber}", "review": "${review}" }))`
## オプション -p の場合の GitHub Copilot Code Review に対するレビュー手順
1. `${prReview} = runSubagent(_GetGitHubCopilotCodeReviewAgent({ "prNumber": "${prNumber}" }))`
2. `${formattedReview} = runSubagent(_WriteReviewFileAgent({ "type": "pr_copilot", "issueNumber": "${issueNumber}", "prNumber": "${prNumber}", "review": "${prReview}" }))`
## オプション -f <file_name> の場合のレビュー手順
1. `${fileName}` が `null` の場合、ファイル名を指定するようにチャット出力し終了する
2. `${review} = runSubagent(_DiffReviewAgent({ "issueNumber": "${issueNumber}", "fileName": "${fileName}" })).review`
3. `${formattedReview} = runSubagent(_WriteReviewFileAgent({ "type": "local", "issueNumber": "${issueNumber}", "review": "${review}" }))`
280行近くあったプロンプトが50行程度に抑えられています。
MCPツールの呼び出しはagent/runSubagent1つのみになりました。
以前はあった大量のルール、レビューでやること/やらないこと、オプション内部の詳細な手順が全て無くなっています。
その代わり、各オプション内にはどのような手順でサブエージェントを呼び出すのか、その際なにをコンテキストとして渡すのかだけを記述しています。
review.prompt.mdは以下のカスタムエージェントをサブエージェントとして呼び出しています。
| カスタムエージェント | 目的 |
|---|---|
| _DiffReviewAgent | main.HEADの比較を行い、差分がある場合にのみコードレビューを実施する |
| _GetGitHubCopilotCodeReviewAgent | Pull Requestに対してついたGitHub Copilot Code Reviewのレビューを取得する |
| _WriteReviewFileAgent | レビュー結果をファイルに出力する |
| _GetIssueNumberAgent | Issue Numberを取得する |
| _GetPullRequestNumberAgent | Pull Request Numberを取得する |
指示形式について
各オプション内部でプログラミング言語のような形式で記述してあります。
これは当初以下のように記述していました。
1. _DiffReviewAgentをrunSubagentで実行し、以下の引数を渡す
- `{ "issueNumber": "${issueNumber}" }`
- 戻り値を `${review}` として参照する
で、どうせ引数で渡すなら関数っぽく書いた方が分かりやすいんじゃ…?という発想から試しにそのように記述してみたところいい感じに動いてくれてるのでこのような記述形式にしてあります。
もちろん自然言語のままでも十分目的の動作を果たしてくれるので、この辺りは好みで良いと思います。
(自分はこうしてるよ!ってのがあったらぜひ教えて下さい…笑。)
runSubagentにはカスタムエージェントを指定しています。
runSubagent(カスタムエージェント(カスタムエージェントに渡すコンテキスト))
コンテキストはJSON形式で渡しています、これはMCPサーバーがJSON形式でやり取りを行うので親和性が高いんじゃないかなと言う発想です。
また、エラー時の共通対応項に記述してありますが全てのカスタムエージェントがエラー時に同じ構造のエラーJSONを返す実装にしてあります。
更にカスタムエージェントのI/Oは全てJSONで統一してあります。
カスタムエージェントはJSON以外を扱えないという訳ではないですし、そもそもエージェント同士のやり取りはテキスト形式です。
ですがJSONっぽい形式で渡すことで最低限の型の定義が行えますし、その辺りのパースはAI側がよしなにやってくれることを期待して最も効率が良さそうなJSONを選択した、という形です。
全エージェントがJSONだけをI/Oとする設計にすることで、ユーザーが「あれ、このサブエージェントは何を返すんだ…?」と考えることを排除できます。
もちろん自然言語でも良いんですが、ブレが出てきてしまうので個人的にはサブエージェント設計を行うならI/OはJSONで統一することをおすすめします。
カスタムエージェント
次に呼び出されてサブエージェントとして機能するカスタムエージェントです。
例えば先程のreview.prompt.mdから呼び出されるカスタムエージェントの一つ、_GetGitHubCopilotCodeReviewAgentです。
---
name: _GetGitHubCopilotCodeReviewAgent
description: Pull Request に対して行われた GitHub Copilot Code Review のレビューを取得するサブエージェント
tools: ['read/readFile', 'github-mcp/pull_request_read']
target: vscode
---
# このエージェントの目的
GitHub 上の指定された Pull Request に対して行われた GitHub Copilot Code Review のレビューを取得し、正規化して出力する
## エラー時の対応
- このエージェントは単独で動作することは想定しておらず、必ず親エージェントから呼び出されることを前提としている
- 以下のような場合には、エラー内容を含む JSON を返すこと
- 親エージェントから渡されるコンテキストが存在しない
- ツールの呼び出しが失敗した
- `project.yaml` の取得に失敗した
- 詳細なエラー内容を必ず `result` フィールドに含めること
```json
{
"error": true,
"agentName": "_GetGitHubCopilotCodeReviewAgent",
"result": "<詳細なエラー内容>"
}
```
## input
- このエージェントは親エージェントから以下の入力値を受け取る
```json
{
"prNumber": "<pull request の番号>"
}
```
```typescript
type input = {
prNumber: string,
}
```
## 手順
1. `.github/metadata/project.yaml` ファイルを読み込み、 `owner` と `repository` の値を取得し、 `${metadata}` として設定する
2. `pull_request_read` ツールを使用し、以下のパラメーターを使用し Pull Request のレビュー情報を取得し、 `${reviewComments}` として設定する
- `{ "method": "get_review_comments", "owner": ${metadata.owner}, "repo": ${metadata.repository}, "pullNumber": "${input.prNumber}" }`
- 取得した値の `pageInfo.hasNextPage` が `true` の場合、全てのレビューコメントを取得するまでループする
- output が以下の場合は、`${reviews}` を `null` として設定し以降の手順を全てスキップする
- 404 エラー等で Pull Request が存在しない場合
- Pull Request に対してレビューコメントが一切存在しない場合
3. 取得した `${reviewComments}` の中から `reviewThreads[].IsResolved === false` のスレッドオブジェクトを抽出し `${openReviewThreads}` として設定する
- オブジェクトが0件の場合、`${reviews}` を `null` として設定し以降の手順を全てスキップする
4. `${openReviewThreads}` を以下の形式でマッピングし `${reviews}` として設定する
### reviews のマッピング構造
```json
[
{
"id": "<generated auto increment id>",
"reviewId": "<reviewThreads[n].Comments.Nodes[0].URL 欄の値の末尾 discussion_r 以降の数字>",
"filePath": "<reviewThreads[n].Comments.Nodes[0].Path>",
"lineNumber": "<reviewThreads[n].Comments.Nodes[0].Line>",
"body": "<reviewThreads[n].Comments.Nodes[0].Body>",
"author": "<reviewThreads[n].Comments.Nodes[0].Author.Login>",
"isOutdated": "<reviewThreads[n].IsOutdated>"
}
]
```
## output
- 出力は必ず JSON フォーマットで行う
- 出力する JSON には余計な説明文やコメントを含めない
- JSON の前後に説明文や注釈等一切追加しない
```json
{
"reviews": "${reviews}"
}
```
```typescript
type ReviewItem = {
id: number;
reviewId: string;
filePath: string;
lineNumber: number;
body: string;
author: string;
isOutdated: boolean;
};
type Output = {
reviews: ReviewItem[] | null;
};
```
カスタムエージェントはほぼ全て同じフォーマットで統一してあります。
- このエージェントの目的項
- エラー時の対応項
- input項
- 手順項
- output項
これらをテンプレートとしておくことで必要事項を埋めるだけで簡単にサブエージェントを定義できるようにしました。
エラー時の対応
## エラー時の対応
- このエージェントは単独で動作することは想定しておらず、必ず親エージェントから呼び出されることを前提としている
- 以下のような場合には、エラー内容を含む JSON を返すこと
- 親エージェントから渡されるコンテキストが存在しない
- ツールの呼び出しが失敗した
- `project.yaml` の取得に失敗した
- 詳細なエラー内容を必ず `result` フィールドに含めること
```json
{
"error": true,
"agentName": "_GetGitHubCopilotCodeReviewAgent",
"result": "<詳細なエラー内容>"
}
```
エラー時の対応項は全カスタムエージェント共通です。
共通化することで親エージェント(review.prompt.md)はどのツールからもエラー時は必ず同じJSON形式でエラーを検知することが可能です。
「サブエージェントがエラーの時はこういうエラーが返ってくるから共通でこう対応してね」と書いておくだけでOK。
複数のサブエージェントをオーケストレーション形式で扱う場合、エラーJSONにエージェント名を入れておくとどこでエラーになったのか分かりやすくて嬉しいです。
input
## input
- このエージェントは親エージェントから以下の入力値を受け取る
```json
{
"prNumber": "<pull request の番号>"
}
```
```typescript
type Input = {
prNumber: string,
}
```
inputは親エージェントから来る引数を定義しており、JSON形式で記述した後に型を明示するためにTypeScript形式で型を指定しています。
正直型定義なんて書かなくてもサブエージェントの実行に一切問題無かったんですが、中長期でメンテナンスを行う私が理解しやすいように入れておきました。
たまたま現在TypeScriptのプロジェクトを開発しているのでなにも考えずにTypeScript形式なんですが、これは型を定義できればRustでもJavaでもPythonのType Hintでもなんなら自然言語でも良いと思います(そもそも無くても動きますし)。
「もしかしたらJSONに対してTSで型を表現したらAIが混乱するかもな…」と少しだけ思ったんですが今のところこれでトラブルになった事はないのでひとまずこの運用で稼働しています。
output
## output
- 出力は必ず JSON フォーマットで行う
- 出力する JSON には余計な説明文やコメントを含めない
- JSON の前後に説明文や注釈等一切追加しない
```json
{
"reviews": "${reviews}"
}
```
```typescript
type ReviewItem = {
id: number;
reviewId: string;
filePath: string;
lineNumber: number;
body: string;
author: string;
isOutdated: boolean;
};
type Output = {
reviews: ReviewItem[] | null;
};
```
outputも冒頭の文言、JSONとTypeScript形式の型定義は全カスタムエージェント共通です。
とにかくJSONだけを返すことを意識し、カスタムエージェントがAIによる自然言語のブレを返さないようにしています。
カスタムエージェント切り出しのメリット
ここまで散々カスタムエージェント化によるメリットを語ってきましたが、最大の恩恵はやはり「やるべきこととMCPツールを一つのファイルに閉じ込められる」ことです。
これによって冒頭で挙げた「レビューファイルを壊す」等の挙動がほぼ0になりました、カスタムエージェント+runSubagentを導入して既に100回以上実行していますが今のところこの形式で以前あったトラブルは0です。
やはりエージェント内で有効なMCPツールが大幅に減少し特定の作業に特化させることでコンテキストが大幅に減少したことが非常に大きく、特に現在複雑なワークフローを1つのプロンプトで実行している方にはメリットが非常に大きいと思います。
更にメンテナンス性も向上しました。
以前は確かに1ファイルを見れば良かったのですが、あくまでマークダウンファイルなのでプログラミング言語のようにIDEによる支援が皆無です。
ファイル冒頭で定義した内容を「あれ、なんだっけ」と何度もファイルを上下に往復することがなくなり細かい修正がめちゃくちゃやりやすくなりました。
カスタムエージェント+runSubagentのデメリット
ただし呼び出すサブエージェントが多ければ多いほどかなり時間がかかるという点は考慮する必要があるかもしれません。
サブエージェントの呼び出しはどうしても時間がかかってしまい、カスタムプロンプト(.github/prompts/*.prompt.md)単体でやっていた時よりも時間がかかるようになりました。
特に推論モデルや元から応答が遅いモデルと使用する場合は顕著です、サブエージェントの呼び出しは今のところ速度とトレードオフなのはデメリットかもしれません。
ですがこの点は作業が正確になりミスを人間がフォローすることが無くなったので今のところトータルの時間は変わっていないように思います。
私のようにAIにCLIを触らせない、スピードより信頼性重視という考え方ならこのトレードオフも大きなデメリットには感じていません。
また、カスタムエージェントが大量に増えると管理が大変という問題もあります。
例えばこれは現在の私のカスタムエージェントの一覧ですが、今後さらに現在のようにオーケストレーターから呼び出すカスタムエージェントを定義し続けるとどれがどれだかよく分からない状況に陥りそうです。
残念ながら執筆時現在、.github/agents/*.agent.mdという形式でしかカスタムエージェントとして認識されません。
なので.github/agents/subDirectory/*.agent.mdのようにディレクトリ分けできないのが最大のネックかなと思っています。
これを解決するためにファイル名にはプレフィックスを付けています。
- Issue関連は
issue. - Pull Request関連は
pr.
このぐらいのファイル数であればまだこれで管理できますが、将来的にはできればディレクトリ分けできるようにして欲しいなあと思っています。
参考程度に、この後リリースされるSkillsはディレクトリ分けが可能なようです…prompts/とagents/もディレクトリ分けさせてくれ…!
この問題に共通して、カスタムエージェントはVSCodeのGitHub Copilot ChatのUI上でAgentモードやPlanモードと同じように選択が可能です。
私の使い方の場合、カスタムエージェントはあくまでオーケストレーターから呼び出される前提で直接指定して使うことは想定していません。
そのため、このようにUI上表示されるのは正直邪魔だし将来的に直接指定して使うことを前提に作る場合大量の部品として定義してあるカスタムエージェントは邪魔です。
その対策として直接呼び出すことを想定していないカスタムエージェントはフロントマターのnameにプレフィックスとしてアンダーバーを付与しています。
発想はプログラミング言語でよくある未使用変数やプライベートプロパティなんかにアンダーバーをつけるのと一緒ですね。
ちなみにこの並び順は名前でソートされるわけではなく追加順なので、冒頭に記号や数字をつけて並び替えるみたいなのは今のところできません。
また画像下部の「カスタムエージェントの構成」からこのリスト上から非表示にすることもできるんですが、残念ながら非表示にしてしまうとそもそもGitHub Copilot上でカスタムエージェントとして呼び出すこと自体が不可になります。
当然私のようにrunSubagentから呼び出す場合も呼び出し不可になるので、この辺りは将来的に改善されると嬉しいですね…!
Agent Skillsはどうなる?
まさにこれを書いている今日、VSCodeの最新アップデートがリリースされ正式にVSCode上のGitHub Copilot ChatでもAgent Skillsが利用可能になりました。
カスタムプロンプトやカスタムエージェントが明示的な呼び出しが必要な一方、Skillsは暗黙的な呼び出しが可能です。
今現在の私の付き合い方だと明示的タイプのカスタムプロンプトやカスタムエージェントの方が相性が良いかなあと感じている反面、Claudeユーザー方面から聞こえてくるSkillsの利便性は決して無視できないのも事実です。
ここまで書いたカスタムエージェントをサブエージェントとして呼び出すような使い方もSkillsの普及で完全に無くなる可能性もありますが、少なくとも明示的に振る舞いとツールをセットにサブエージェントとして独立したコンテキストで動かせるというのは現行方式の強みです。
ただし途中登場したような「レビューファイルを参照させる」や「該当ディレクトリ配下に特化した指示(今までのAGENTS.md)」のようなものはSkillsに分離することで必要な時に動的ロードができるようになります。
Skillsで定義しておくことで振る舞いと同時にMCPツールの指定とドキュメントを同じ場所で扱えることになるので、今後はある程度Skillsへの移行が進むのかなと感じています。
まだまだ検証・実証が日々必要な段階ですが、GitHub CopilotもClaudeに追従する形で日々進化しているので積極的に新機能を試して日々開発環境をアップデートしていきたいですね!

