本記事は AI が記事を作成しております。
何か表現がわかりづらい部分もあるかもしれませんがご了承ください。
はじめに
「GitHub Copilot で複数のAIに役割を持たせて、チームみたいに協調させたら面白いのでは?」
そんな発想から、4人の専門家AIエージェント + 1人のオーケストレーターによるコードレビューチームを VS Code 上に構築してみました。本記事では、セットアップから実際のレビュー実行、指摘に基づく修正、再レビューまでの一連の流れを紹介します。
本記事で紹介した Agent は下記で公開しています。
この記事でやること
- AI エージェント定義ファイル(
.agent.md)を作成してレビューチームを編成 - レビュー対象のサンプルアプリ(子供向け算数クイズゲーム)を用意
-
@Orchestratorでマルチエージェントレビューを実行 - 指摘事項を修正してA評価を目指す
前提環境
- VS Code + GitHub Copilot(Chat 拡張含む)
- Node.js + TypeScript
- カスタムエージェント機能(
.github/agents/ディレクトリ)
カスタムエージェントは .github/agents/ にエージェント定義ファイル(.agent.md)を配置することで利用できます。Copilot Chat で @エージェント名 と呼び出すだけで使えます。
1. エージェントチームの設計
今回は以下の5人体制を組みました。
| エージェント | 役割 | 観点 |
|---|---|---|
| Architect | 設計レビュアー | SOLID原則、レイヤー構造、依存関係 |
| SecurityReviewer | セキュリティ専門家 | OWASP Top 10、認証認可、入力検証 |
| Tester | QAエンジニア | テストカバレッジ、エッジケース、テスト設計 |
| TechWriter | テクニカルライター | 命名規則、ドキュメント、可読性 |
| Orchestrator | チームリーダー | 上記4人を順に呼び出し結果を統合 |
2. エージェント定義ファイルの書き方
各エージェントは .github/agents/ に Markdown ファイルとして配置します。YAML フロントマターで名前・説明・使用可能ツールを、本文でロール・レビュー観点・出力フォーマットを定義します。
Orchestrator(統括役)の例
---
name: Orchestrator
description: 全エージェントを統括し、コードレビューを実行するオーケストレーター
tools:
- read_file
- grep_search
- semantic_search
- list_dir
- file_search
- runSubagent
---
# Orchestrator(オーケストレーター)
あなたはコードレビューチームのリーダーです。
4人の専門家エージェントを順に呼び出し、包括的なコードレビューを実施します。
## 実行手順
1. プロジェクト全体の構成を把握する
2. 以下の順序で各エージェントを `runSubagent` で呼び出す:
- **Architect** に設計レビューを依頼
- **SecurityReviewer** にセキュリティレビューを依頼
- **Tester** にテストレビューを依頼
- **TechWriter** にドキュメントレビューを依頼
3. 全エージェントの結果を統合し、最終レポートを作成する
Orchestrator には runSubagent ツールを必ず含めてください。これがないと他のエージェントを呼び出せません。
SecurityReviewer(セキュリティ専門家)の例
---
name: SecurityReviewer
description: セキュリティ脆弱性を専門にレビューするエージェント
tools:
- read_file
- grep_search
- semantic_search
---
# Security Reviewer
あなたはセキュリティエンジニアです。
OWASP Top 10 の観点でコードの脆弱性を徹底的にレビューします。
## レビュー観点
1. A01: アクセス制御の不備
2. A02: 暗号化の失敗
3. A03: インジェクション
...
他の Architect、Tester、TechWriter も同様の形式で、それぞれの専門分野に特化した指示を記述します。
3. レビュー対象アプリの作成
レビュー対象として、子供向け算数クイズゲームの REST API を TypeScript + Express で作成しました。意図的にいくつかの問題を仕込んでいます。
プロジェクト構造
src/
app.ts # Express アプリケーション構成
config.ts # 設定の一元管理
index.ts # サーバー起動エントリポイント
middleware/
auth.ts # JWT認証ミドルウェア
errorHandler.ts # グローバルエラーハンドラ
models/
store.ts # データモデル・インメモリストア
routes/
auth.ts # ユーザー登録・ログインAPI
todos.ts # Todo CRUD API
game.ts # 算数クイズゲームAPI
game/
engine.ts # クイズ生成・スコア計算ロジック
types.ts # 型定義・セッション管理
__tests__/
api.test.ts # 統合テスト
engine.test.ts # エンジンユニットテスト
store.test.ts # ストアユニットテスト
仕込んだ問題の例
レビューで見つけてもらうために、以下のような問題をわざと入れておきました。
// ❌ JWTシークレットがハードコード
const JWT_SECRET = 'super-secret-key-change-me';
// ❌ 所有者チェックなし — 他人のTodoを更新・削除できる
router.put('/:id', (req: AuthRequest, res: Response) => {
const todo = todos.find(t => t.id === req.params.id);
// ↑ userId のチェックがない!
});
// ❌ Math.random() で予測可能なセッションID
const sessionId = Math.random().toString(36).substring(2, 10);
// ❌ デバッグ用エンドポイントが本番に残っている(回答漏洩)
router.get('/session/:id', (req, res) => {
res.json(session); // 答えが丸見え!
});
// ❌ リーダーボードが無限に成長(メモリリーク)
leaderboard.push(entry);
4. マルチエージェントレビューの実行
Copilot Chat で以下のように入力するだけです:
@Orchestrator src/ のコードを全体レビューしてください
すると Orchestrator が自動的に4つの専門エージェントを runSubagent で順に呼び出し、それぞれの観点でレビューを実施。最後に統合レポートが出力されます。
5. レビュー結果(修正前)
総合評価
| 観点 | 評価 | 主な指摘数 |
|---|---|---|
| アーキテクチャ | B | 9件 |
| セキュリティ | C | 11件 |
| テスト | C | 9件 |
| ドキュメント | B | 9件 |
最優先指摘 Top 5
-
[Critical] JWT シークレットのハードコード —
'default-secret-key'がフォールバック値として存在し、環境変数未設定でトークン偽造可能 -
[Critical] ゲームセッションの IDOR —
sessionIdを知れば他ユーザーのセッションにアクセス可能 - [High] レート制限なし — ブルートフォース攻撃に対して無防備
- [重大] MathQuizEngine のユニットテストなし — 中核ロジックにテストが存在しない
- [重大] セッションのメモリリーク — TTL/クリーンアップ機構なし
意図的に仕込んだ問題をしっかり検出してくれました。それ以外にも、ボディサイズ制限やCORS設定など、自分では気づかなかった指摘もありました。
6. 指摘に基づく修正
レビュー結果をもとに、すべての指摘に対応しました。主な修正内容:
アーキテクチャ改善
app/server 分離 + 設定一元管理
+ const JWT_SECRET = process.env.JWT_SECRET;
+ if (!JWT_SECRET) {
+ throw new Error('JWT_SECRET environment variable is required.');
+ }
+
+ export const config = {
+ jwtSecret: JWT_SECRET,
+ port: Number(process.env.PORT) || 3000,
+ bcryptRounds: 10,
+ tokenExpiry: '24h',
+ bodyLimit: '10kb',
+ allowedOrigins: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
+ } as const;
セキュリティ修正
レート制限・セキュリティヘッダー・CORS 導入
+ import helmet from 'helmet';
+ import cors from 'cors';
+
const app = express();
+ app.use(helmet());
+ app.use(cors({ origin: config.allowedOrigins, credentials: true }));
+ app.use(express.json({ limit: config.bodyLimit }));
Todo の所有者チェック追加
router.put('/:id', (req: AuthRequest, res: Response) => {
- const todo = todos.find(t => t.id === req.params.id);
+ const todo = todos.find(t => t.id === req.params.id && t.userId === req.userId);
ゲームセッション:IDOR修正 + TTL管理 + 暗号論的ID生成
// TTL付きセッション管理クラス
class GameSessionManager {
private sessions = new Map<string, { session: GameSession; createdAt: number }>();
get(id: string): GameSession | undefined {
const entry = this.sessions.get(id);
if (!entry) return undefined;
if (Date.now() - entry.createdAt > SESSION_TTL_MS) {
this.sessions.delete(id);
return undefined;
}
return entry.session;
}
// ... cleanup, countByPlayer など
}
// crypto.randomBytes で安全なセッションID生成
export function generateSessionId(): string {
return crypto.randomBytes(16).toString('hex');
}
テスト大幅強化
7件 → 63件(9倍!)
PASS src/__tests__/api.test.ts — 35 tests(統合テスト)
PASS src/__tests__/engine.test.ts — 18 tests(エンジンユニットテスト)
PASS src/__tests__/store.test.ts — 10 tests(ストアユニットテスト)
Tests: 63 passed, 63 total
追加したテストの例:
describe('generateQuestions', () => {
it.each(['easy', 'normal', 'hard'] as const)(
'should generate valid %s questions',
(difficulty) => {
const questions = generateQuestions(difficulty, 10);
for (const q of questions) {
expect(q.text).toContain('= ?');
expect(q.choices).toContain(q.answer);
expect(q.choices).toHaveLength(4);
}
}
);
});
describe('calculateStars', () => {
it('should return 3 stars for 90%+ score', () => {
expect(calculateStars(9, 10)).toBe(3);
expect(calculateStars(10, 10)).toBe(3);
});
it('should return 0 stars for total=0', () => {
expect(calculateStars(0, 0)).toBe(0);
});
});
7. 修正後の改善サマリ
| 観点 | Before | After | 主な改善 |
|---|---|---|---|
| アーキテクチャ | B | A | config一元管理、app/server分離、エラーハンドラ、TTLセッション管理 |
| セキュリティ | C | A | JWT必須化、helmet/cors/rate-limit、IDOR修正、入力バリデーション |
| テスト | C | A | 7→63件、エンジンUT、ストアUT、CRUD全テスト、認可テスト |
| ドキュメント | B | A | API仕様、環境変数、プロジェクト構造の文書化 |
やってみた感想
良かった点
- 多角的な視点: 1人のAIに「全部レビューして」と頼むより、専門特化したエージェントに分けた方が指摘の深さが増す
- 再現性: エージェント定義がファイルとして残るので、チーム内で共有・改善できる
- 修正→再レビューのサイクル: 指摘を直して再レビューすると評価が上がるのが分かりやすい
注意点
- エージェント定義の プロンプト品質 がレビュー品質に直結する。出力フォーマットを明確に指定するのが重要
- Orchestrator で
runSubagentを呼ぶため、レスポンスにやや時間がかかる(4エージェント分) - あくまでAIの判断なので、最終的な妥当性は人間が確認する必要がある
まとめ
GitHub Copilot のカスタムエージェント機能を使えば、「AI同士にチームを組ませる」ことが手軽にできます。今回はコードレビューチームを作りましたが、同じ仕組みで以下のような応用も考えられます:
- デバッグチーム: 再現係 → 原因調査 → 修正担当 → 検証係
- ドキュメントチーム: リサーチャー → ライター → エディター
- 設計チーム: PM → アーキテクト → 開発者 → QA
エージェント定義ファイルはテキストベースなので、Git で管理・改善サイクルを回しやすいのも魅力です。
ぜひ試してみてください!
リポジトリ構成(参考)
.github/
agents/
architect.agent.md
security-reviewer.agent.md
tester.agent.md
tech-writer.agent.md
orchestrator.agent.md
copilot-instructions.md
src/
app.ts, config.ts, index.ts
middleware/ auth.ts, errorHandler.ts
models/ store.ts
routes/ auth.ts, todos.ts, game.ts
game/ engine.ts, types.ts
__tests__/ api.test.ts, engine.test.ts, store.test.ts
public/
index.html