☃️

GitHub Copilotにおける Agent / Instructions / Prompt の整理と活用方法

に公開

皆さん、GitHub Copilotは利用されていますでしょうか?

私は、コスパが良いのと複数のモデルを利用できる点に魅力を感じたため、仕事だけでなくプライベートの開発でも利用しています。

しかし、使い込んでいく中で一つ壁に当たることがありました。それは、Agent・Instructions・Promptといった複数のファイルタイプが存在し、それぞれがどのように影響し合い、どのように使い分けるべきかが分かりにくいという点です。

そこで本記事では、私自身の備忘録も兼ねて、これらの概念と実際の検証結果を踏まえた活用方法について解説します。

GitHub Copilotとは

GitHub Copilotは、AIを活用したコーディング支援ツールとして登場しました。当初はVSCodeエディタ上でのコード補完が主要な機能でしたが、現在では GitHub Web上でのチャット機能、プルリクエスト(PR)のレビュー、そして Coding Agent と呼ばれるWeb上で動作する自律的なタスク実行機能までを備えています。

このGitHub Copilotを効果的に制御し、期待通りの成果を引き出すために重要となるのが、今回紹介する3つのファイルタイプです。

https://docs.github.com/en/copilot

3つのファイルタイプ:Agent、Instructions、Prompt

GitHub Copilot(特にVS Code環境やCoding Agent機能)は、現在以下の3種類のファイルを特別な意味を持つものとして認識します。

  1. Agent (*.agent.md): 特定の役割や知識を持つカスタム AI エージェントを定義する
  2. Instructions (*.instructions.md): AIが遵守すべき「ルール」や「制約」を定義する
  3. Prompt (*.prompt.md): AIに依頼する具体的な「タスク」の手順を定義する

なお、VS Code環境においては、これらのファイルを「プロジェクト固有」と「グローバル」の2つのスコープで定義可能です。
プロジェクト固有のファイルはそのプロジェクト内でのみ有効ですが、グローバルファイルはすべてのプロジェクトで横断的に利用できます。

全体像は以下の通りです。
Copilotのファイルタイプ間の関係図

Agentの使い方

Agentは、特定の役割や専門知識を持つ「カスタムAI」を定義する機能です。

なぜAgentを定義するのか?

単一の汎用的なエージェントですべてのタスクをこなそうとすると、AIが混乱したり、意図しない挙動を示したりすることがあります。専用のAgentファイルを定義することには、主に以下のメリットがあります。

  1. コンテキスト汚染の回避:
    フロントエンド、バックエンド、インフラなど、異なる領域の知識やルールをすべて一つのエージェントに詰め込むと、AIが混乱し、無関係な知識に基づいた回答をしてしまう「コンテキスト汚染」のリスクが高まります。特定の作業に特化したAgentを用意することで、必要なコンテキストのみを読み込ませ、精度の高い回答を引き出すことができます。

  2. 役割と振る舞いの統一:
    「厳格なコードレビュアー」や「親切な教育係」など、タスクに応じたペルソナを定義することで、チーム全体で統一された品質やトーンの支援を受けることができます。

  3. 使用ツールの最適化:
    例えば「仕様検討」を行うAgentにはファイル編集権限を与えず、「実装」を行うAgentには与えるといったように、タスクに必要なツールだけを割り当てることで、誤操作のリスクを減らし、安全性を高めることができます。

定義例

以下は、フロントエンド開発に特化したエージェントの定義例です。

---
name: frontend-dev
description: フロントエンド開発(React/TypeScript)に特化したエージェント
tools: ['edit', 'runCommands']
---

# フロントエンド開発エージェント

## 役割
あなたはReactとTypeScriptのエキスパートです。
モダンなフロントエンド開発のベストプラクティスに従い、コードの生成やリファクタリングを行います。

## 責任範囲
- コンポーネント設計と実装
- パフォーマンス最適化(再レンダリングの抑制など)
- アクセシビリティ(a11y)への配慮

## 振る舞い
- コードは常にTypeScriptの型安全性を重視してください。
- 説明は簡潔に、コード例を多用してください。

このように定義すると、GitHub Copilot Chatの入力欄左下にあるエージェント選択メニュー(標準の Agent, Plan, Ask, Edit などが並ぶ場所)に、追加したエージェントが表示されるようになります。これを選択することで、特定のペルソナを持ったAIと対話が可能になります。

フロントエンド開発エージェントの選択画面

Agentの活用例

例えば、プロジェクトの特性やタスク内容に応じて、以下のような専用 Agent を用意する活用例が考えられます。

  • 領域別コーディング: フロントエンド、バックエンド、インフラ(IaC)など、技術スタックごとに分割し、専門性を高める。
  • 品質保証(QA): テストコードの生成や、エッジケースの洗い出しに特化させる。
  • ドキュメント作成: API仕様書や技術記事、READMEの更新を専門に行う。
  • コードレビュー: セキュリティ脆弱性のチェックや、コーディング規約の遵守を確認する。
  • オンボーディング: 新規参画者向けに、プロジェクト構成や開発フローを解説するメンター役を担わせる。

また、Agentは後述するサブエージェントとして組み込むこともできるため、活用の幅はより広がると思います。

他のテクニックとして、エージェント間の引き継ぎ(Handoffs)を行う記事も参考に貼っておきます。
https://dev.classmethod.jp/articles/vscode-copilot-plan-mode-as-custom-agents/

Instructionsの使い方

Instructionsは、AIに対する「行動指針」や「制約事項」を定義する機能です。Promptが「個別のタスク」に対する指示であるのに対し、Instructionsは「プロジェクト全体」や「特定のファイル群」に対して永続的に適用されるルールです。

なぜInstructionsを定義するのか?

プロジェクトの規模が大きくなると、開発者間やAIとの対話において認識のズレが生じやすくなります。Instructionsを定義することには、以下のメリットがあります。

  1. ルールの統一:
    「回答は日本語で」「コードコメントは英語で」といった基本的なルールを明文化することで、AIの出力品質を均一化できます。
  2. 入力の手間削減:
    毎回プロンプトで「エラーハンドリングはanyhowを使って」と指示する必要がなくなり、本質的なタスクの指示に集中できます。
  3. コンテキストに応じた制御:
    ファイルの種類(拡張子やパス)に応じて適用するルールを切り替えることで、適切な文脈での支援が可能になります。

定義例:プロジェクト全体への適用

以下は、プロジェクト全体に適用する general.instructions.md の例です。

---
applyTo: "**"
---

# 全体指示

- チャットの返答は、日本語で簡潔かつ丁寧に行ってください。
- すべてのドキュメント(設計書、ソースファイル、コメントなど)は、特に指示がない限り英語で記述してください。

applyTo: "**" と記述することで、このルールはプロジェクト内のすべてのファイルに対する操作やチャットに適用されます。ここでは「チャットの返答は日本語」「ドキュメントは英語」という基本方針を徹底させています。

定義例:特定のファイルへの適用

特定の言語やディレクトリにのみルールを適用したい場合は、globパターンを使用します。
以下は、Rustのソースコード(*.rs)にのみ適用される rust.instructions.md の例です。

---
applyTo: "**/*.rs"
---

# Rustコーディングガイドライン

## 全般
- Rustらしい(idiomaticな)コードを使用してください。
- エラーハンドリングには `panic!` ではなく `Result` を優先してください。
- アプリケーションレベルのエラーハンドリングには `anyhow` を使用してください。

## ドキュメント
- すべてのパブリックな構造体と関数にはドキュメントコメント(`///`)を記述してください。

このように applyTo: "**/*.rs" と指定することで、Rustファイルを開いている時や編集している時のみ、この「Rust特有のコーディング規約」がAIに読み込まれます。これにより、無関係なファイル(例:MarkdownやJSON)を編集する際に、不要なRustの知識が干渉することを防げます。

Instructions適用の注意点

Instructionsが適用される条件について、重要な注意点があります。applyTo で指定したルールが読み込まれるのは、チャット欄で明示的にファイル参照を指定した場合、または現在エディタで開いているファイルに対してです。

  • 参照ファイルとして指定されている場合: 対象のInstructionsファイルが読み込まれます。
    Instructionsが参照ファイルとして指定された場合の読み込み例

  • 単にファイルパスをテキストで入力した場合: 対象のInstructionsファイルは読み込まれません。
    Instructionsがファイルパスをテキストで入力された場合の読み込み失敗例

つまり、Copilotに特定のファイルの文脈を理解させたい場合は、VSCodeのCopilot Chat欄に、単にファイル名を書くのではなく、# を使ってファイル参照を行うか、そのファイルを開いた状態で対話する必要がありそうです。

applyToの使い方は、以下の記事も参考になります。
https://zenn.dev/m10maeda/articles/copilot-multi-instruction-files

Instructionsの活用例

Instructionsは、以下のような場面で特に効果を発揮すると考えられます。

  • 言語ごとのコーディング規約: *.ts にはTypeScriptの、*.py にはPythonの規約(PEP8など)を適用する。
  • テストコードのルール: *.test.ts に対して、使用するテストフレームワーク(Jest/Vitest)やモックの書き方を指示する。
  • ドキュメント作成ルール: docs/**/*.md に対して、見出しの構成や用語集の遵守を指示する。
  • 特定ディレクトリの制約: legacy/**/*.js に対して、「リファクタリングは行わず、バグ修正のみを行うこと」といった制約を課す。

ただし、条件によってはチャット実行時に常に呼び出されてしまうため、applyTo を使った参照範囲の制御や、条件付きでコンテキストに読み込むなどの工夫が必要になります。コンテキスト汚染を避けるためにも、これらの仕組みを前提に活用していくことが大切と思います。

Promptの使い方

Promptは、頻繁に行うタスクをテンプレート化し、再利用可能にする機能です。対話形式で必要な変数を確認するよう記述すれば、動的な入力に対応した定型処理を自動化できます。

なぜPromptを定義するのか?

毎回ゼロから指示を書くのではなく、Promptとして定義しておくことで、以下のメリットがあります。

  1. 品質の均一化:
    「テストコードの生成」や「リファクタリング」など、誰が実行しても同じ手順・品質でタスクを完了できるようになります。チーム内でのベストプラクティスを共有する手段としても有効です。

  2. 入力漏れの防止:
    プロンプト内に変数を定義することで、実行時に必要な情報の入力を強制できます。これにより、「エラーログを貼り忘れた」「対象ファイルを指定し忘れた」といったミスを防げます。

  3. 作業効率の向上:
    複雑な指示や背景情報を毎回入力する手間が省け、数クリックで高度なタスクを開始できます。

定義例

以下は、対話的に情報を収集しながら実装を進める new-command.prompt.md の例です。

---
agent: agent
---

# 新規コマンド実装(対話モード)

あなたはRustのエキスパート開発者です。あなたのタスクは、タスク管理CLIのための新しいコマンドを実装することです。

## コンテキスト
このプロジェクトはRustと`clap`を使用したCLIタスクマネージャーです。

## 指示

### ステップ1: 情報収集
まず、以下の情報がまだ提供されていない場合は、ユーザーに尋ねてください:
- **コマンド名**: 追加するサブコマンドの名前。
- **説明**: コマンドの機能についての短い説明。
- **引数**: コマンドが受け取るべき引数やフラグ。

**ここで停止し、ユーザーの応答を待ってください。**

### ステップ2: 実装
ステップ1の情報が得られたら、以下を進めてください:

1.  コマンドロジック用の新しいモジュールまたは関数を作成する。
2.  `src/main.rs`(または引数が定義されている場所)の `Cli` enumを更新し、新しいサブコマンドを含める。
3.  ハンドラー関数を実装する。
4.  エラーハンドリングは `anyhow::Result` を使用して行うこと。

## 出力
このコマンドを実装するために必要なコード変更を提供してください。

この例では、単に変数を埋めるだけでなく、対話的なステップを定義している点が重要です。

  • ステップ実行と待機: ステップ1 で情報を聞き出し、**ここで停止し、ユーザーの応答を待ってください。** と指示することで、Copilotは勝手に実装を始めず、ユーザーからの入力を待つようになります。

このようにプロンプトを設計することで、複雑なタスクでもAIと協調しながら着実に進めることが可能になります。

Promptの活用例

定型的な作業はすべてPrompt化の候補となります。

  • 単体テスト生成: テストフレームワークや網羅すべきケース(正常系・異常系)を指定したテンプレート。
  • リファクタリング: 「可読性向上」「パフォーマンス改善」など、特定の観点に基づいた修正を依頼する。
  • エラー解析: エラーログと関連コードを入力させ、原因と修正案を提示させる。
  • 仕様書からのコード生成: 設計書のテキストを入力として、ボイラープレートコードを生成する。
  • コミットメッセージ作成: 変更内容から、プロジェクトの規約(Conventional Commitsなど)に沿ったメッセージを生成する。

GitHub Copilot Code ReviewにおけるInstructionsの活用

GitHub Copilotが提供する「プルリクエストの自動レビュー機能」においても、Instructionsによる制御が可能です。これにより、プロジェクト固有の観点に基づいたレビューを自動化できます。

仕組みと注意点

この機能は、リポジトリ内の .github/copilot-instructions.md を参照して動作します。運用にあたっては、以下の2点に留意する必要があります。

  1. 参照されるブランチ:
    レビュー実行時に参照されるのは、PRの作成元(Compare)ブランチに含まれるファイルです。ベースブランチ(mainなど)にルールを追加しても、作業ブランチ側にその変更が反映されていない場合、ルールは適用されません。

  2. VS Code Chatとの競合:
    .github/copilot-instructions.md は、VS Code上のCopilot Chatでも自動的にコンテキストとして読み込まれます。そのため、このファイルに「あなたは厳格なコードレビュアーです」といった強い役割定義を直接記述してしまうと、日常のコーディング支援チャットにおいてもその振る舞いが適用され、使い勝手を損なう可能性があります。

推奨される運用パターン

前述の競合を避けるため、レビューの詳細な基準は別ファイル(例:docs/code-review-guidelines.md)に切り出し、copilot-instructions.md からは条件付きで参照する構成が良いと考えられます。

Copilot Code ReviewにおけるInstructionsの推奨運用パターン

定義例:.github/copilot-instructions.md

When asked to perform a code review or when assigned as a reviewer on a GitHub PR, you **MUST** strictly follow the instructions in `docs/code-review-guidelines.md`.

このように記述することで、「コードレビューを依頼された場合」という条件でのみ特定のガイドラインを適用させることが可能となります。これにより、普段のチャット体験を損なうことなく、PRレビューの品質を標準化できそうです。

サブエージェントの活用

Claude Code ではすでに実装されていた仕組みですが、Copilot にもサブエージェント機能が新たに追加されました。エージェントが別のエージェントを呼び出して処理を委譲できるため、複雑なタスクを分割し、専門化されたエージェントに任せることで全体の精度と効率を高められそうです。

設定と挙動

この機能を利用するには、事前にVS Codeの設定で実験的な機能を有効にする必要があります。

VS Code設定で実験的機能を有効にする画面

検証の結果、Copilot Chat で最初に対話を行う上位エージェント(親)がサブエージェント(子)を呼び出し、その処理結果を受け取れることを確認しました。

copilot-sub-agent

ただし、この挙動には重要な特性があります。上位エージェントは、サブエージェントの「思考プロセス」や「中間生成物」を直接覗き見ることはできませんでした。 上位エージェントが受け取れるのは、サブエージェントが最終的に返した「完了報告」や「出力結果」のみです。

この挙動については、以下の記事で紹介されているClaude Codeのサブエージェントの挙動と同様であり、GitHub Copilotにおいても同じ設計思想に基づいていると考えられます。

https://zenn.dev/trust_delta/articles/claude-code-subagent-001

したがって、サブエージェントを活用する際は、「必要な情報はすべて最終的な出力に含める」 か、あるいは 「出力を別ファイルに保存し、上位エージェントから参照させる」 といった工夫が必要そうです。そうしなければ、親エージェントは「処理が完了しました」という事実しか知ることができず、具体的な成果物を利用できない可能性があります。

サブエージェントの活用例

先行してサブエージェント機能が導入されているClaude Codeなどの事例を参考にすると、以下のような活用パターンが考えられます。

  1. 独立した検証(Double Check):
    実装を行うエージェントとは別に、「検証専用」のサブエージェントを呼び出し、生成されたコードのテストやレビューを行わせます。コンテキストが分離されているため、実装時のバイアス(思い込み)に影響されず、客観的な検証が可能になります。

  2. 専門的な調査(Deep Dive):
    特定のエラーやライブラリの仕様について、サブエージェントに調査を依頼します。親エージェントのコンテキストを汚染することなく、調査結果の要約だけを受け取ることで、メインの対話をクリーンに保つことができます。

  3. 大規模なリファクタリング:
    親エージェントが全体のアーキテクチャを管理し、個別のモジュールや関数のリファクタリングをサブエージェントに委譲します。各サブエージェントは担当範囲に集中できるため、複雑な依存関係に惑わされることなく作業を完遂できます。

現状ではサブエージェント機能を十分に使いこなせていないため、活用方法を検証でき次第、改めて記事として整理したいと考えています。🙏

まとめ

GitHub Copilot は、適切に設定すれば強力な「チームメンバー」として機能する可能性があります。
そのためには、今回紹介した AgentInstructionsPrompt の 3 つを活用し、役割・ルール・タスクを明確に分けながら丁寧に検証していくことが重要だと思います。

ご自身のプロジェクトでも、最適な Copilot の構成をぜひ検討してみてください。

Thinkingsテックブログ

Discussion