コンテキスト管理とプロンプト設計の統合で、AIエージェントの真の力を引き出す
📚 この記事の位置づけ
本記事は、コンテキストエンジニアリングシリーズの続編です。
前提知識として、以下の記事を先にお読みいただくことをおすすめします:
- コーディングエージェント時代のコンテキストエンジニアリング実践ガイド - 個人利用の基礎
- コンテキストウィンドウの処理フローと動作メカニズム 完全解説 - 技術的詳細
本記事では、コンテキストとプロンプトの統合により、AIエージェントから最高の出力を引き出す方法を解説します。
目次
- プロンプトエンジニアリング2.0とは
- 従来のプロンプトエンジニアリングの限界
- コンテキスト駆動プロンプトの基礎
- 実践的プロンプトパターン集
- Few-shot学習とコンテキストの相乗効果
- Chain-of-Thoughtとコンテキスト管理
- エージェント別の最適化戦略
- メタプロンプティング実践
- トラブルシューティング
- ケーススタディ
プロンプトエンジニアリング2.0とは
プロンプトエンジニアリング2.0は、従来のプロンプト最適化にコンテキスト管理を統合した新しいアプローチです。単発のプロンプト改善ではなく、プロジェクト全体の知識基盤を構築し、それを活用することで、AIエージェントから一貫した高品質な出力を引き出すことができます。
1.0から2.0への進化
プロンプトエンジニアリングは、単発の「質問テクニック」から、継続的な「知識システム」へと進化しています。 1.0では毎回ゼロから指示を考える必要がありましたが、2.0ではプロジェクトの知識を蓄積し、それを基盤として一貫性のある高品質な出力を得られます。この進化により、個人の属人的スキルから、チーム全体で共有できる組織的資産へと変わりました。
1.0の特徴(従来型)
従来のプロンプトエンジニアリングは、毎回ゼロから詳細な指示を書く必要がありました。これは小規模なタスクには有効でしたが、プロジェクトが成長するにつれて、指示の再現性や一貫性の欠如が大きな問題となりました。
プロンプト例:
「Reactでログイン画面を作って」
問題点:
❌ コンテキストが毎回リセット
❌ 過去の学習が活かせない
❌ 出力の一貫性がない
❌ チーム内で知見が共有できない
2.0の特徴(コンテキスト駆動型)
プロンプトエンジニアリング2.0では、プロジェクトの知識をコンテキストファイルとして体系化し、エージェントが自動的に参照できるようにします。これにより、プロンプトはシンプルになり、出力の品質と一貫性が劇的に向上します。
システム:
1. プロジェクトコンテキストを読み込み
2. 関連する過去の決定(ADR)を参照
3. コーディング規約を適用
4. Few-shotの例を含める
プロンプト例:
「@context.md @conventions.md を読んで、
既存のAuthFormコンポーネントと同じパターンで
ログイン画面を作成してください。
バリデーションは@validation-patterns.md の
パターン3を使用」
結果:
✅ 一貫したコード生成
✅ 規約準拠
✅ 過去の知見を活用
✅ チーム全体で再現可能
なぜ2.0が必要なのか
AIエージェントを本格的に業務で活用するには、単発のプロンプト最適化だけでは不十分です。プロジェクトの成長とともに知識が蓄積され、チーム全体で共有できる仕組みが必要です。以下の表は、1.0から2.0への進化がもたらす具体的な改善を示しています。
観点 | 1.0の課題 | 2.0の解決 |
---|---|---|
再現性 | 同じプロンプトでも出力がバラバラ | コンテキストで固定化 |
効率性 | 毎回詳細な指示が必要 | コンテキストで省略可能 |
品質 | 出力品質が不安定 | 一貫した高品質を維持 |
スケール | 個人知識に依存 | チーム知識として共有 |
進化 | 学習が蓄積されない | 継続的に改善 |
従来のプロンプトエンジニアリングの限界
従来のプロンプトエンジニアリングは、個々のプロンプトの最適化に焦点を当てていましたが、実際のプロジェクト開発では様々な限界に直面します。ここでは、コンテキストの消失、暗黙の前提の不一致、プロンプトの巨大化という3つの主要な課題について解説します。
限界1: コンテキストの消失
// 1日目
プロンプト: "Reactのカスタムフックでユーザー管理を作って"
エージェント: [実装A]
// 3日後(新しいチャット)
プロンプト: "さっきのユーザー管理に、権限チェックを追加して"
エージェント: "どのユーザー管理ですか?実装を見せてください"
// 😱 コンテキストが失われた!
問題: セッションやチャット間で知識が共有されない
2.0での解決:
<!-- コンテキストファイルに記録 -->
<!-- .ai/domains/users/context.md -->
## ユーザー管理の実装
### カスタムフック: useUser
場所: `src/hooks/useUser.ts`
実装パターン: React Query + Zustand
最終更新: 2024-10-10
### 設計判断
- 状態管理: Zustand(軽量性重視)
- キャッシング: React Query(5分)
- 認証トークン: localStorage(セキュリティはADR-023参照)
プロンプト: "@domains/users/context.md を読んで、権限チェックを追加"
エージェント: "useUserフックに、hasPermission関数を追加します"
限界2: 暗黙の前提の不一致
// プログラマーの頭の中
「当然、TypeScriptで型安全に書く」
「当然、エラーハンドリングは統一パターン」
「当然、既存のutilsを再利用する」
// エージェントの出力
// → JavaScriptで、エラーはconsole.error、新規実装
// 😱 前提が伝わっていない!
2.0での解決:
# .ai/team/conventions.md
## 暗黙の前提を明示化
### 必須ルール
1. すべてのコードはTypeScript(strict mode)
2. エラーハンドリングはカスタムエラークラス必須
3. 既存のユーティリティ関数を優先的に利用
### エラーハンドリングパターン
```typescript
// ✅ 必ずこのパターン
try {
await riskyOperation();
} catch (error) {
throw new CustomError('操作に失敗しました', {
cause: error,
contextId,
userId
});
}
// ❌ これは禁止
catch (error) {
console.error(error);
}
```
限界3: プロンプトの巨大化
// プロンプトが長くなりすぎる問題
プロンプト:
「TypeScriptのstrictモードで、
既存のUserServiceと同じパターンで、
エラーハンドリングはCustomErrorクラスを使って、
バリデーションはzodで、
ログ出力はWinstonで、
テストはJestとsupertestで、
ドキュメントはTSDocで、
命名規則はcamelCaseで、
...(以下100行)...
ユーザー登録機能を作って」
// 😱 読みづらい、書きづらい、メンテできない!
2.0での解決:
// コンテキストファイルで一括管理
// .ai/team/context.md に全て記載
// プロンプトはシンプルに
プロンプト: "@context.md を読んで、ユーザー登録機能を作って"
// エージェントが自動的に:
// ✅ TypeScript strictモード適用
// ✅ UserServiceパターン踏襲
// ✅ CustomError使用
// ✅ zodでバリデーション
// ...すべて自動的に適用
コンテキスト駆動プロンプトの基礎
コンテキスト駆動プロンプトは、3層構造のモデルに基づいています。システムコンテキスト(常時有効な基本情報)、タスク固有プロンプト(具体的な指示)、補足情報(参考資料)を適切に組み合わせることで、効率的かつ高品質なコード生成を実現します。
3層構造モデル
Layer 1: システムコンテキスト(自動読み込み)
システムコンテキストは、プロジェクトの「憲法」です。 すべてのAIエージェントの動作の基盤となる、変更頻度の低い重要な情報を集約します。一度設定すれば、毎回のプロンプトで自動的に参照されるため、開発者は個別のタスクに集中できます。
# .claudefiles または .cursorrules
# 常に読み込まれる基本コンテキスト
.ai/team/context.md
.ai/team/architecture.md
.ai/team/conventions.md
# これらは毎回のプロンプトで自動的に考慮される
役割: プロジェクトの「憲法」
含むべき内容:
- 技術スタック
- アーキテクチャ原則
- コーディング規約
- 禁止事項
Layer 2: タスク固有プロンプト(明示的指示)
タスク固有プロンプトは、「今、何をしてほしいか」を明確に伝える層です。 システムコンテキストの基盤の上に、具体的な要件、制約、参考情報を加えることで、エージェントは正確に意図を理解し、期待通りの成果物を生成できます。
// 具体的な作業指示
プロンプト:
"""
@domains/auth/context.md を参照して、
2要素認証(TOTP)機能を実装してください。
要件:
1. QRコード生成(speakeasy + qrcode)
2. 検証エンドポイント(POST /api/v1/auth/2fa/verify)
3. バックアップコード生成(8桁 x 10個)
制約:
- 既存のAuthServiceを拡張
- シークレットキーは暗号化してDB保存
- ADR-025の決定事項に従う
"""
役割: 「何を作るか」の明確化
Layer 3: 補足情報(参考資料)
補足情報は、エージェントの判断を助ける「ヒント集」です。 必須ではありませんが、既存の実装例、エラーコード体系、類似機能の参考情報などを提供することで、より洗練された実装が可能になります。特に複雑なタスクや、新しい技術領域での実装時に威力を発揮します。
プロンプト:
"""
// Layer 2の指示に加えて
参考実装:
@src/modules/auth/strategies/jwt.strategy.ts
(既存の認証パターンを踏襲)
エラーメッセージ:
@docs/error-codes.md
(エラーコード体系に従う)
類似機能:
GitHub 2FA実装の考え方を参考に
(ただし、コピペではなく、我々のアーキテクチャに適合)
"""
役割: 「どう作るか」のヒント提供
効果的なプロンプト構造
良いプロンプトには型があります。 毎回ゼロから考えるのではなく、実績のあるテンプレートに沿って構成することで、品質を安定させながら作成時間を大幅に短縮できます。以下のテンプレートは、数千のプロンプトから抽出されたベストプラクティスの集大成です。この構造を使うことで、エージェントは必要な情報を漏れなく受け取り、期待通りの成果物を生成できるようになります。
## プロンプトのテンプレート
### 1. コンテキスト参照(必須)
@{関連するコンテキストファイル}
### 2. 目的(必須)
何を達成したいのか、1-2文で明確に
### 3. 要件(必須)
- 機能要件
- 非機能要件(パフォーマンス、セキュリティ等)
### 4. 制約(推奨)
- 使用すべきパターン
- 使用してはいけないもの
- 既存コードとの整合性
### 5. 参考情報(オプション)
- 類似実装
- 外部ドキュメント
- 設計判断の理由
### 6. 出力形式(オプション)
- コード + テスト
- ドキュメント込み
- ファイル分割の方針
実践的プロンプトパターン集
実際のプロジェクトで使える5つの実践的プロンプトパターンを紹介します。段階的詳細化、制約駆動、例示駆動、反復改善、デバッグ支援の各パターンは、それぞれ異なる状況で効果を発揮します。これらのパターンを状況に応じて使い分けることで、開発の生産性と品質を大幅に向上させることができます。
パターン1: 段階的詳細化プロンプト
大きなタスクは一度に完成を目指すのではなく、段階的に詳細化していく方が成功率が高まります。 このパターンでは、まず概要レベルの設計を複数提案させ、その中から最適なものを選び、徐々に詳細化していきます。各ステップで方向性を確認できるため、後戻りのリスクを最小化できます。
ステップ1: 概要設計
まずは全体像を掴むことから始めます。 いきなり詳細な実装に入るのではなく、複数の選択肢を比較検討することで、後戻りのリスクを最小化できます。エージェントに設計案を提案させることで、見落としていた観点や、より良いアプローチに気づくことができます。
プロンプト:
"""
@context.md を読んで、
通知システムの設計案を3つ提案してください。
要件:
- リアルタイム通知(新着メッセージ、システムアラート)
- 通知履歴の保存
- ユーザーごとの通知設定
各案について、以下を含めてください:
- 技術スタック
- メリット・デメリット
- 実装コスト(工数見積もり)
- 既存システムとの統合容易性
"""
エージェントの出力:
"""
案1: WebSocket + Redis Pub/Sub
メリット: リアルタイム性最高、スケール容易
デメリット: 接続管理の複雑性
工数: 3週間
案2: Server-Sent Events (SSE) + PostgreSQL
メリット: シンプル、既存DBを利用
デメリット: 双方向通信不可
工数: 2週間
案3: Webhook + 外部サービス(Firebase Cloud Messaging)
メリット: インフラ管理不要
デメリット: 外部依存、コスト
工数: 1週間
"""
ステップ2: 詳細化
選択した設計案を具体化します。 ここでは、実装に必要なすべての要素(データベーススキーマ、API設計、エラーハンドリング等)を明確にします。この段階で十分に詳細化しておくことで、実装フェーズでの迷いや手戻りを防げます。
プロンプト:
"""
案2(SSE + PostgreSQL)を詳細化してください。
以下を含めてください:
- DBスキーマ設計
- API設計(エンドポイント一覧)
- クライアント側の実装方針
- エラーハンドリング戦略
- パフォーマンス考慮事項
"""
ステップ3: 実装
いよいよコードを生成します。 ただし、一度に全てを実装しようとせず、段階的に進めることが重要です。まずは基盤となる部分から始め、動作を確認しながら機能を追加していきます。この漸進的アプローチにより、問題の早期発見と修正が可能になります。
プロンプト:
"""
上記の詳細設計に基づいて、
Notificationモジュールを実装してください。
まず以下から始めてください:
1. DBマイグレーションファイル
2. Prismaスキーマ更新
3. DTOとエンティティ定義
実装後、次のステップを提案してください。
"""
利点:
- ✅ 大きなタスクを段階的に進められる
- ✅ 各ステップで方向性を確認・修正できる
- ✅ エージェントの負荷を分散
- ✅ 思考プロセスが可視化される
パターン2: 制約駆動プロンプト
制約を明確にすることで、品質の高い実装を保証します。 「やってほしいこと」だけでなく、「やってはいけないこと」や「満たすべき基準」を事前に示すことで、エージェントは最初から正しい方向で実装を進められます。特にパフォーマンスやセキュリティが重要な機能では、この制約駆動アプローチが威力を発揮します。
プロンプト:
"""
@context.md @conventions.md を読んで、
ユーザー検索APIを実装してください。
【必須制約】
✓ パフォーマンス: 95%ile < 100ms
✓ セキュリティ: SQLインジェクション対策必須
✓ スケーラビリティ: 10万ユーザーまで対応
【禁止事項】
✗ 全件取得してフィルタ(N+1問題)
✗ LIKE '%keyword%'(フルスキャン)
✗ 生のSQL文字列結合
【推奨アプローチ】
→ PostgreSQLのフルテキスト検索
→ インデックス設計から始める
→ ページネーション必須
既存の実装例:
@src/modules/posts/post.repository.ts
(同様のフルテキスト検索実装あり)
"""
エージェントの出力:
"""
以下の実装を提案します:
1. インデックス設計:
```sql
-- フルテキスト検索用のGINインデックス
CREATE INDEX idx_users_search ON users
USING GIN (to_tsvector('english',
name || ' ' || email || ' ' || bio));
```
2. リポジトリ実装:
```typescript
async searchUsers(
keyword: string,
{ page, limit }: PaginationDto
): Promise<PaginatedResult<User>> {
// パラメータ化クエリでSQLインジェクション対策
const query = this.prisma.$queryRaw`
SELECT * FROM users
WHERE to_tsvector('english',
name || ' ' || email || ' ' || bio
) @@ plainto_tsquery('english', ${keyword})
ORDER BY ts_rank(...) DESC
LIMIT ${limit} OFFSET ${(page - 1) * limit}
`;
// パフォーマンス計測ログ
const startTime = Date.now();
const results = await query;
const duration = Date.now() - startTime;
this.logger.log(`Search completed in ${duration}ms`, {
keyword,
resultCount: results.length
});
return {
items: results,
total: await this.countSearchResults(keyword),
page,
limit
};
}
```
3. パフォーマンステスト:
```typescript
describe('User Search Performance', () => {
it('95%ile should be under 100ms', async () => {
const measurements = [];
// 100回実行して95%ileを計測
for (let i = 0; i < 100; i++) {
const start = Date.now();
await repository.searchUsers('test');
measurements.push(Date.now() - start);
}
const p95 = percentile(measurements, 95);
expect(p95).toBeLessThan(100);
});
});
```
"""
利点:
- ✅ 非機能要件を確実に満たす実装
- ✅ アンチパターンを事前に防止
- ✅ パフォーマンス要件を組み込み
- ✅ テスタビリティを考慮
パターン3: 例示駆動プロンプト
「見て学ぶ」は、AIにとっても最も効果的な学習方法です。 既存の優れた実装を参考として示すことで、エージェントはコーディングスタイル、設計パターン、命名規則などを自然に踏襲します。結果として、プロジェクト全体で一貫性のある、予測可能なコードベースを維持できます。
プロンプト:
"""
@conventions.md を読んで、
Productモジュールを実装してください。
【既存の良い例】
@src/modules/users/users.module.ts
@src/modules/users/users.service.ts
@src/modules/users/users.controller.ts
これらと同じ構造・パターンで実装してください。
特に以下を踏襲:
- レイヤー分離(Controller → Service → Repository)
- DIパターン
- エラーハンドリング方式
- バリデーションの配置
- テストの構造
【違いがある部分】
Productは画像アップロード機能が必要です。
これは既存のPostモジュールの画像処理を参考に:
@src/modules/posts/post-image.service.ts
"""
エージェントの出力:
"""
UserモジュールとPostモジュールのパターンを組み合わせて実装します。
```typescript
// product.module.ts
// Userモジュールと同じDI構造
@Module({
imports: [PrismaModule, S3Module],
controllers: [ProductController],
providers: [
ProductService,
ProductRepository,
ProductImageService, // Postモジュールのパターン
],
exports: [ProductService],
})
export class ProductModule {}
// product.controller.ts
// Userモジュールのエラーハンドリングパターン
@Controller('products')
export class ProductController {
constructor(private readonly productService: ProductService) {}
@Post()
async create(@Body() dto: CreateProductDto) {
try {
return await this.productService.create(dto);
} catch (error) {
if (error instanceof ValidationError) {
throw new BadRequestException(error.message);
}
throw new InternalServerErrorException('商品作成に失敗');
}
}
// 画像アップロード(Postモジュールのパターン)
@Post(':id/images')
@UseInterceptors(FileInterceptor('image'))
async uploadImage(
@Param('id') id: string,
@UploadedFile() file: Express.Multer.File
) {
return this.productService.uploadImage(id, file);
}
}
// product-image.service.ts
// PostImageServiceと同じパターンを踏襲
@Injectable()
export class ProductImageService {
// 同じS3設定、同じ画像処理ロジック
// ...
}
```
すべてのファイルをUserモジュールと同じテストカバレッジで
実装しますか?
"""
利点:
- ✅ 既存パターンとの一貫性
- ✅ 車輪の再発明を防ぐ
- ✅ チーム全体で統一された構造
- ✅ レビューが容易
パターン4: 反復改善プロンプト
完璧を一度に目指すのではなく、段階的に品質を高めていきます。 まずは基本機能を実装し、それをベースに、パフォーマンス、セキュリティ、エラーハンドリングといった観点で順次改善していきます。この反復的アプローチにより、各段階で学習しながら、より洗練された実装へと進化させることができます。
// ラウンド1: 初期実装
プロンプト:
"""
@context.md を読んで、
商品在庫管理機能を実装してください。
基本機能:
- 在庫数の取得・更新
- 在庫切れ検知
- 在庫予約(注文時)
"""
// エージェント: 実装を提供
// ラウンド2: パフォーマンス改善
プロンプト:
"""
上記の実装を分析して、パフォーマンスボトルネックを指摘してください。
特に以下の観点で:
- N+1問題
- 不要なDB クエリ
- キャッシュ可能な部分
問題を指摘した後、最適化版を提案してください。
"""
// ラウンド3: 同時実行制御
プロンプト:
"""
複数ユーザーが同時に在庫を更新した場合の
競合状態を考慮してください。
以下のシナリオでテストケースを書き、
実装を改善してください:
シナリオ:
1. 在庫が10個
2. ユーザーA、Bが同時に5個ずつ予約
3. 期待結果: 片方は成功、片方は在庫不足エラー
4. 実際の在庫数: 5個(整合性が保たれる)
"""
// ラウンド4: エラーリカバリ
プロンプト:
"""
在庫予約後、決済処理が失敗した場合の
ロールバック処理を追加してください。
要件:
- トランザクション管理
- 補償トランザクション(Saga Pattern)
- リトライ機構(指数バックオフ)
- 失敗時の通知
"""
利点:
- ✅ 段階的に品質を向上
- ✅ 各ラウンドで学習を蓄積
- ✅ 複雑な問題を分割して解決
- ✅ エージェントの思考を深化
パターン5: デバッグ支援プロンプト
バグは必ず発生します。重要なのは、いかに素早く原因を特定し、根本的な解決策を見つけるかです。 エージェントをデバッグパートナーとして活用することで、複数の視点から問題を分析し、再発防止策まで含めた包括的な解決が可能になります。
プロンプト:
"""
以下のコードでバグが発生しています。
【現象】
APIレスポンスが時々500エラーになる
エラーログ: "Cannot read property 'id' of undefined"
【コード】
(コードを提示)
【調査してほしいこと】
1. このバグの根本原因を特定
2. 再現手順を推測
3. 修正方法を提案(複数案)
4. 同様のバグを防ぐための対策
@conventions.md のエラーハンドリング規約に従って修正してください。
"""
エージェントの出力例:
根本原因
userRepository.findById()
がnull
を返すケースで、user.id
にアクセスしてエラーになっています。
再現手順
- 存在しないuserIdでAPIを呼び出す
- DBから削除されたユーザーのIDで呼び出す
- 不正なuserIdフォーマットで呼び出す
修正案
案1: 早期リターン(推奨)
async getUserProfile(userId: string) {
const user = await this.userRepository.findById(userId);
// ✅ nullチェックを追加
if (!user) {
throw new UserNotFoundError(`User ${userId} not found`);
}
const posts = await this.postRepository.findByUserId(user.id);
const comments = await this.commentRepository.findByUserId(user.id);
return {
user,
stats: {
postCount: posts.length,
commentCount: comments.length
}
};
}
案2: Optional Chaining(部分的)
// ❌ この場合は不適切
// 問題を隠蔽してしまう
const posts = await this.postRepository.findByUserId(user?.id);
予防策
1. Repositoryレベルで保証
// user.repository.ts
async findByIdOrFail(userId: string): Promise<User> {
const user = await this.findById(userId);
if (!user) {
throw new UserNotFoundError(`User ${userId} not found`);
}
return user;
}
2. 型レベルで保証
// NonNullable型を活用
async getUserProfile(userId: string): Promise<UserProfile> {
const user: NonNullable<User> =
await this.userRepository.findByIdOrFail(userId);
// ...
}
3. テストケース追加
describe('getUserProfile', () => {
it('存在しないユーザーでUserNotFoundErrorをthrow', async () => {
await expect(
service.getUserProfile('non-existent')
).rejects.toThrow(UserNotFoundError);
});
});
4. Lintルール追加
// .eslintrc.json
{
"rules": {
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/strict-boolean-expressions": "warn"
}
}
利点:
- ✅ 体系的なデバッグアプローチ
- ✅ 根本原因の特定
- ✅ 複数の解決策の比較
- ✅ 再発防止策の提案
Few-shot学習とコンテキストの相乗効果
Few-shot学習とは、少数の具体例からAIがパターンを学習する手法です。コンテキストファイルに良い実装例(そして悪い例も)を蓄積することで、エージェントは一貫したパターンでコードを生成できるようになります。本セクションでは、Few-shot学習を最大限に活用するためのパターンとベストプラクティスを解説します。
Few-shot学習とは
Few-shot学習 = 少数の例から学習するAIの能力
Zero-shot: 例なし
One-shot: 1つの例
Few-shot: 2-5個の例
Many-shot: 10個以上の例
コンテキストと組み合わせる理由
理論と実例の組み合わせが、最強の学習環境を作ります。 コンテキストファイルでプロジェクトの原則を示し、Few-shot例で具体的な実装パターンを見せることで、エージェントは「なぜそうするのか」と「どうやるのか」の両方を理解できます。
相乗効果:
- コンテキスト: 「何を守るべきか」(原則)
- Few-shot: 「どう実装するか」(パターン)
パターン1: プログレッシブ Few-shot
シンプルから複雑へ、段階的に例を示すことで理解を深めます。 基本パターンから始めて、徐々に高度な技術を導入することで、エージェントは各レベルの実装パターンを確実に習得できます。この構造化されたアプローチにより、初心者でも高度な実装が可能になります。
// .ai/domains/validation/patterns.md
## バリデーションパターン集
### パターン1: 基本的なDTO
```typescript
// Example 1
import { IsString, IsEmail, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail({}, { message: 'メールアドレスの形式が不正です' })
email: string;
@IsString()
@MinLength(8, { message: 'パスワードは8文字以上必要です' })
password: string;
}
```
### パターン2: ネストしたオブジェクト
```typescript
// Example 2
import { IsObject, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
export class CreateOrderDto {
@IsObject()
@ValidateNested()
@Type(() => ShippingAddressDto)
shippingAddress: ShippingAddressDto;
@IsArray()
@ValidateNested({ each: true })
@Type(() => OrderItemDto)
items: OrderItemDto[];
}
```
### パターン3: カスタムバリデーター
```typescript
// Example 3
import {
registerDecorator,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface
} from 'class-validator';
@ValidatorConstraint({ async: true })
export class IsUniqueEmailConstraint implements ValidatorConstraintInterface {
constructor(private userRepository: UserRepository) {}
async validate(email: string) {
const user = await this.userRepository.findByEmail(email);
return !user;
}
defaultMessage() {
return 'このメールアドレスは既に使用されています';
}
}
export function IsUniqueEmail(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [],
validator: IsUniqueEmailConstraint,
});
};
}
// 使用例
export class CreateUserDto {
@IsEmail()
@IsUniqueEmail() // カスタムバリデーター
email: string;
}
```
// プロンプト例
プロンプト:
"""
@domains/validation/patterns.md を読んで、
ProductのDTOを作成してください。
以下のフィールドが必要:
- name: 必須、2-100文字
- price: 必須、正の数値、小数点2桁
- category: 必須、既存カテゴリのIDのみ許可(カスタムバリデーター)
- tags: オプション、文字列配列
パターン1-3を組み合わせて実装してください。
"""
パターン2: コンテキスト付きFew-shot
単なる「例」を示すだけでは、エージェントは表面的なパターンマッチングしかできません。 例だけを見せるのではなく、その背景にある「なぜその実装を選んだのか」「どんな制約があったのか」を明確にすることが重要です。各実装例の前にコンテキスト(前提条件、設計判断、制約事項)を示すことで、エージェントは単なるコピーではなく、状況に応じた適切な応用が可能になります。
# .ai/domains/auth/examples.md
## 認証実装パターン
> 必ず @domains/auth/context.md を先に読んでください
> JWT設定、トークン有効期限などの前提はそちらに記載
### Example 1: 基本的なログイン
【コンテキスト】
- ユーザー: email + password
- トークン: JWT (15分)
- リフレッシュトークン: 7日
【実装】
```typescript
@Post('login')
async login(@Body() dto: LoginDto): Promise<LoginResponse> {
// 1. ユーザー認証
const user = await this.authService.validateUser(
dto.email,
dto.password
);
if (!user) {
throw new UnauthorizedException('認証に失敗しました');
}
// 2. トークン生成
const accessToken = this.jwtService.sign(
{ sub: user.id, email: user.email },
{ expiresIn: '15m' } // context.mdの設定に従う
);
const refreshToken = this.jwtService.sign(
{ sub: user.id, type: 'refresh' },
{ expiresIn: '7d' }
);
// 3. リフレッシュトークンをDB保存
await this.authService.saveRefreshToken(user.id, refreshToken);
return { accessToken, refreshToken, user };
}
```
### Example 2: 2要素認証付きログイン
【コンテキスト】
- 上記に加えて、2FAが有効なユーザーは追加認証が必要
- TOTPコード: 6桁、30秒有効
【実装】
```typescript
@Post('login')
async login(@Body() dto: LoginDto): Promise<LoginResponse> {
const user = await this.authService.validateUser(
dto.email,
dto.password
);
if (!user) {
throw new UnauthorizedException('認証に失敗しました');
}
// 2FA有効チェック
if (user.is2faEnabled) {
if (!dto.totpCode) {
return {
requires2fa: true,
tempToken: this.generateTempToken(user.id)
};
}
// TOTPコード検証
const isValid = await this.authService.verify2fa(
user.id,
dto.totpCode
);
if (!isValid) {
throw new UnauthorizedException('2FAコードが不正です');
}
}
// トークン生成(Example 1と同じ)
// ...
}
```
### Example 3: OAuth統合(Google)
【コンテキスト】
- Google OAuthを併用
- 既存ユーザーは自動リンク
- 新規ユーザーは自動作成
【実装】
```typescript
@Get('google/callback')
async googleCallback(@Query('code') code: string): Promise<LoginResponse> {
// 1. Googleトークン取得
const googleUser = await this.googleService.getUserInfo(code);
// 2. 既存ユーザー検索
let user = await this.userRepository.findByEmail(googleUser.email);
// 3. 新規ユーザー作成
if (!user) {
user = await this.userRepository.create({
email: googleUser.email,
name: googleUser.name,
avatar: googleUser.picture,
authProvider: 'google',
emailVerified: true, // Googleで検証済み
});
}
// 4. OAuth連携情報を保存
await this.oauthRepository.upsert({
userId: user.id,
provider: 'google',
providerId: googleUser.id,
accessToken: googleUser.accessToken,
});
// 5. JWTトークン生成(Example 1と同じロジック)
const accessToken = this.jwtService.sign(
{ sub: user.id, email: user.email },
{ expiresIn: '15m' }
);
// ...
return { accessToken, refreshToken, user };
}
```
Few-shotの効果を最大化するコツ
良い例を示すだけでは不十分です。 エージェントが確実に学習し、適切に応用できるようにするための、実証済みのテクニックを紹介します。これらのテクニックは、教育心理学の「認知負荷理論」に基づいており、人間の学習と同様、AIエージェントにも効果的です。
1. 段階的な複雑性
学習の基本は「易しいものから難しいものへ」です。 急に複雑な例を見せるのではなく、基礎から積み上げることで、エージェントは各ステップの意図と実装パターンを確実に理解できます。この「足場かけ(Scaffolding)」のアプローチにより、エージェントは段階的に高度な実装能力を獲得していきます。
Example 1: 最もシンプル(基本パターン)
Example 2: 少し複雑(条件分岐追加)
Example 3: より複雑(外部連携)
Example 4: 最も複雑(エッジケース処理)
2. コメントで意図を明示
コードは「何をするか」を示し、コメントは「なぜそうするか」を説明します。 エージェントにとって、実装の背景にある判断基準や制約を理解することは、適切な応用のために不可欠です。
// ❌ コメントなし
const token = jwt.sign(payload, secret, { expiresIn: '15m' });
// ✅ 意図を説明
// context.mdのJWT有効期限(15分)に従う
// セキュリティとUXのバランスを考慮した設定
const token = jwt.sign(payload, secret, { expiresIn: '15m' });
3. アンチパターンも含める
「良い例」だけでなく「悪い例」も示すことで、学習効果は飛躍的に向上します。 何が間違っているのか、なぜそれを避けるべきなのかを明示することで、エージェントは同じ過ちを繰り返さなくなります。
# .ai/domains/validation/patterns.md
## ❌ アンチパターン: やってはいけないこと
### Bad Example 1: バリデーションをスキップ
```typescript
// ❌ これは禁止
@Post()
async create(@Body() dto: any) { // anyは使わない
return this.service.create(dto);
}
// ✅ 正しい実装
@Post()
async create(@Body() dto: CreateUserDto) { // DTOで型とバリデーション
return this.service.create(dto);
}
```
### Bad Example 2: エラーを握りつぶす
```typescript
// ❌ これは禁止
try {
await riskyOperation();
} catch (error) {
console.log('エラーが発生しました'); // ログだけ
return null; // エラーを隠蔽
}
// ✅ 正しい実装
try {
await riskyOperation();
} catch (error) {
throw new CustomError('操作に失敗', {
cause: error,
context: { userId, operation: 'riskyOperation' }
});
}
```
Chain-of-Thoughtとコンテキスト管理
Chain-of-Thought(思考の連鎖)は、AIに思考プロセスを段階的に出力させることで、より高品質な結果を得る技法です。複雑な設計判断やデバッグが必要な場面で、エージェントに思考過程を明示させることで、判断根拠が明確になり、レビューや改善が容易になります。
Chain-of-Thought (CoT) とは
Chain-of-Thoughtは、AIに「どう考えたか」を言語化させることで、推論の質を飛躍的に向上させる技法です。 人間が複雑な問題を解く際に紙に書き出して考えを整理するように、AIも思考プロセスを明示的に出力することで、より論理的で正確な結論に到達できます。
Chain-of-Thought = AIに「思考過程」を明示的に出力させる技法
従来:
問題 → 答え
(ブラックボックス)
CoT:
問題 → 思考ステップ → 推論 → 答え
(透明で検証可能)
コンテキストと組み合わせる効果
思考プロセスの可視化は、AIの能力を引き出す鍵です。 コンテキストがプロジェクトの知識基盤を提供し、Chain-of-Thoughtがその知識を使って論理的に推論する方法を示すことで、エージェントは単なるパターンマッチングを超えた、真の問題解決能力を発揮します。
パターン1: 明示的思考プロンプト
複雑な問題こそ、段階的な思考が重要です。 エージェントに対して、いきなり答えを求めるのではなく、問題分析から始めて、選択肢の検討、最適解の選択、実装計画の立案という明確なステップを踏ませることで、より質の高い解決策が得られます。
プロンプト:
"""
@context.md @architecture.md を読んで、
大量データのエクスポート機能を設計してください。
要件:
- 10万件以上のデータを対象
- CSV/Excel形式
- バックグラウンドジョブで処理
- 完了時にメール通知
【重要】
思考過程を以下のステップで明示的に出力してください:
1. 問題分析
- どんな課題があるか?
- 制約は何か?
2. 設計の選択肢
- 3つの実装方法を検討
- それぞれのメリット・デメリット
3. 最適解の選択
- なぜこれを選ぶのか?
- トレードオフは何か?
4. 実装計画
- 実装の順序
- テスト戦略
5. 最終実装
- コード
"""
エージェントの出力例:
1. 問題分析
課題:
- メモリ制限: 10万件を一度にメモリ展開すると、Node.jsのヒープサイズ(デフォルト1.4GB)を超える可能性
- タイムアウト: APIのタイムアウト(30秒)に収まらない
- 並行性: 複数ユーザーが同時にエクスポートすると負荷急増
制約:
- @context.md より: Clean Architecture準拠
- @architecture.md より: バックグラウンドジョブはBullMQ使用
- インフラ: ECS Fargate(2GB メモリ)
2. 設計の選択肢
案1: ストリーミング処理 + 即時ダウンロード
User → API → DBストリーム → CSV変換 → Response
メリット:
- シンプル
- 追加インフラ不要
デメリット:
- タイムアウトリスク
- ユーザーはブラウザを開きっぱなし必要
- リトライ困難
案2: バックグラウンドジョブ + S3
User → API → Job Queue
Job Worker → DBストリーム → CSV → S3 → メール通知
メリット:
- タイムアウト回避
- リトライ可能
- ユーザーは他の作業可能
デメリット:
- 複雑性増加
- S3コスト
案3: バッチ処理(定期実行)
Cron → 全データをエクスポート → S3 → 通知
メリット:
- サーバー負荷を分散
デメリット:
- リアルタイム性なし
- 要件に合わない
3. 最適解の選択
案2(バックグラウンドジョブ + S3)を選択
理由:
- 要件の「バックグラウンドジョブ」に明示的に合致
- 10万件の処理時間(推定3-5分)はAPIタイムアウトを超える
- @architecture.md でBullMQが既に導入済み
- S3も既存インフラ(追加コストほぼなし)
トレードオフ:
- 実装の複雑性は増すが、スケーラビリティと信頼性を優先
4. 実装計画
Phase 1: ジョブキュー設定(Day 1)
- BullMQ の export-queue 作成
- ジョブプロセッサ骨組み
Phase 2: ストリーミング処理(Day 2)
- Prismaのストリームクエリ
- CSV変換パイプライン
- S3アップロード
Phase 3: 通知機能(Day 3)
- メール送信
- ダウンロードURL生成(署名付き)
- URL有効期限(24時間)
Phase 4: エラーハンドリング(Day 4)
- リトライ設定
- 失敗時の通知
- ログ記録
Phase 5: テスト(Day 5)
- ユニットテスト
- 統合テスト
- 負荷テスト(10万件、100万件)
5. 最終実装
5.1 APIエンドポイント
@Post('exports')
async createExport(
@Body() dto: CreateExportDto,
@CurrentUser() user: User
): Promise<ExportJobResponse> {
// ジョブをキューに追加
const job = await this.exportQueue.add('generate-export', {
userId: user.id,
format: dto.format,
filters: dto.filters,
});
return {
jobId: job.id,
status: 'queued',
estimatedTime: '3-5分',
};
}
@Get('exports/:jobId')
async getExportStatus(
@Param('jobId') jobId: string
): Promise<ExportJobStatus> {
const job = await this.exportQueue.getJob(jobId);
if (!job) {
throw new NotFoundException('ジョブが見つかりません');
}
return {
jobId: job.id,
status: await job.getState(),
progress: job.progress(),
result: job.returnvalue, // S3 URLなど
};
}
5.2 ジョブプロセッサ
@Processor('export-queue')
export class ExportProcessor {
@Process('generate-export')
async handleExport(job: Job<ExportJobData>) {
const { userId, format, filters } = job.data;
try {
// ステップ1: データをストリーミング取得
const stream = await this.dataRepository.streamData(filters);
// ステップ2: CSV変換パイプライン
const csvStream = stream.pipe(
new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
// JSONオブジェクト → CSV行
const csvRow = this.objectToCsv(chunk);
callback(null, csvRow);
}
})
);
// ステップ3: S3アップロード
const s3Key = `exports/${userId}/${Date.now()}.csv`;
await this.s3Service.uploadStream(s3Key, csvStream);
// ステップ4: 署名付きURL生成
const downloadUrl = await this.s3Service.getSignedUrl(
s3Key,
86400 // 24時間有効
);
// ステップ5: メール通知
await this.emailService.send({
to: user.email,
subject: 'エクスポートが完了しました',
template: 'export-complete',
data: { downloadUrl, expiresIn: '24時間' },
});
// ジョブ結果を返す
return {
s3Key,
downloadUrl,
rowCount: stream.rowCount,
completedAt: new Date(),
};
} catch (error) {
// エラー処理(conventions.mdに従う)
throw new ExportError('エクスポート処理に失敗', {
cause: error,
contextId: job.id,
userId,
});
}
}
}
5.3 テスト
describe('Export Feature', () => {
describe('Performance', () => {
it('10万件のエクスポートが5分以内に完了', async () => {
const startTime = Date.now();
const job = await exportQueue.add('generate-export', {
userId: testUser.id,
filters: { limit: 100000 },
});
await job.finished();
const duration = Date.now() - startTime;
expect(duration).toBeLessThan(5 * 60 * 1000); // 5分
});
it('メモリ使用量が2GB以下', async () => {
const initialMemory = process.memoryUsage().heapUsed;
await exportQueue.add('generate-export', {
userId: testUser.id,
filters: { limit: 100000 },
});
const peakMemory = process.memoryUsage().heapUsed;
const memoryIncrease = peakMemory - initialMemory;
expect(memoryIncrease).toBeLessThan(2 * 1024 * 1024 * 1024); // 2GB
});
});
describe('Error Handling', () => {
it('S3アップロード失敗時、リトライする', async () => {
s3Service.upload = jest.fn()
.mockRejectedValueOnce(new Error('Network error'))
.mockResolvedValueOnce({ key: 'exports/test.csv' });
const job = await exportQueue.add('generate-export', testData);
await job.finished();
expect(s3Service.upload).toHaveBeenCalledTimes(2); // 1回失敗、1回成功
});
});
});
効果:
- ✅ 設計判断の根拠が明確
- ✅ トレードオフを意識した実装
- ✅ レビュアーが理解しやすい
- ✅ ドキュメントとして再利用可能
パターン2: デバッグ用CoT
デバッグは、問題の原因を論理的に追跡するプロセスです。 Chain-of-Thoughtを使うことで、エージェントはボトルネック特定から根本原因の分析、最適化案の評価まで、体系的なデバッグプロセスを実行できます。思考過程が可視化されるため、人間のレビュアーも判断の妥当性を検証しやすくなります。
プロンプト:
"""
以下のコードでパフォーマンス問題が発生しています。
【現象】
- APIレスポンスが平均2秒
- 目標は200ms以下
【コード】
(コードを提示)
思考過程を明示して、パフォーマンス問題を分析・解決してください:
1. ボトルネック特定
- どこが遅いか?
- なぜ遅いか?
2. 測定方法の提案
- どう計測するか?
3. 最適化案(3つ)
- それぞれの効果予測
4. 推奨案の実装
"""
エージェントの出力例:
1. ボトルネック特定
問題箇所の分析:
問題A: N+1クエリ問題
for (const post of posts) {
post.comments = await this.commentRepository.findByPostId(post.id);
post.likes = await this.likeRepository.countByPostId(post.id);
}
影響:
- posts が10件 → 20回のDBクエリ(comments x10, likes x10)
- posts が100件 → 200回のDBクエリ
- 1クエリ平均10ms → 2秒になる理由
問題B: 並列化されていない
const user = await ...; // 直列
const posts = await ...; // 直列
for loop ... // 直列
const notifications = await ...; // 直列
影響:
- 並列実行可能なクエリを直列実行 → 無駄な待ち時間
2. 測定方法の提案
計測コードの追加:
async getUserDashboard(userId: string) {
const timings = {};
const t0 = Date.now();
const user = await this.userRepository.findById(userId);
timings.user = Date.now() - t0;
const t1 = Date.now();
const posts = await this.postRepository.findByUserId(userId);
timings.posts = Date.now() - t1;
const t2 = Date.now();
for (const post of posts) {
post.comments = await this.commentRepository.findByPostId(post.id);
post.likes = await this.likeRepository.countByPostId(post.id);
}
timings.postDetails = Date.now() - t2;
const t3 = Date.now();
const notifications = await this.notificationRepository.findByUserId(userId);
timings.notifications = Date.now() - t3;
this.logger.debug('Dashboard timings', timings);
return { user, posts, notifications };
}
3. 最適化案
案1: JOIN + 集約クエリで一発取得
アプローチ: SQLレベルで全て取得
SELECT
p.*,
COUNT(DISTINCT c.id) as comment_count,
COUNT(DISTINCT l.id) as like_count
FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
LEFT JOIN likes l ON l.post_id = p.id
WHERE p.user_id = ?
GROUP BY p.id
効果予測:
- クエリ数: 200回 → 1回
- 予想時間: 2000ms → 50ms
- 改善率: 97.5%
デメリット:
- SQLが複雑
- ORMの抽象化を破る可能性
案2: DataLoader パターン
アプローチ: バッチ処理 + キャッシング
const commentLoader = new DataLoader(async (postIds) => {
const comments = await this.commentRepository.findByPostIds(postIds);
return postIds.map(id =>
comments.filter(c => c.postId === id)
);
});
for (const post of posts) {
post.comments = await commentLoader.load(post.id);
}
効果予測:
- クエリ数: 200回 → 2回(comments, likes)
- 予想時間: 2000ms → 100ms
- 改善率: 95%
デメリット:
- DataLoaderの導入が必要
- キャッシュ管理の複雑性
案3: 並列化 + 必要最小限のデータ
アプローチ: Promise.allで並列化
async getUserDashboard(userId: string) {
// 並列実行
const [user, posts, notifications] = await Promise.all([
this.userRepository.findById(userId),
this.postRepository.findByUserIdWithCounts(userId), // カウントのみ
this.notificationRepository.findByUserId(userId),
]);
return { user, posts, notifications };
}
// Repository側で集約
async findByUserIdWithCounts(userId: string) {
return this.prisma.post.findMany({
where: { userId },
select: {
id: true,
title: true,
_count: {
select: {
comments: true,
likes: true,
}
}
}
});
}
効果予測:
- クエリ数: 200回 → 3回(並列実行)
- 予想時間: 2000ms → 150ms
- 改善率: 92.5%
デメリット:
- コメントの詳細は取得できない(カウントのみ)
4. 推奨案の実装
案1(JOIN + 集約)を推奨
理由:
- 最も高速(97.5%改善)
- クエリ数が最小(1回)
- 追加ライブラリ不要
- ダッシュボードではカウントのみで十分(詳細は別途取得)
実装:
4.1 Repository層
// post.repository.ts
async findByUserIdWithStats(userId: string): Promise<PostWithStats[]> {
return this.prisma.$queryRaw`
SELECT
p.id,
p.title,
p.content,
p.created_at,
COUNT(DISTINCT c.id)::int as comment_count,
COUNT(DISTINCT l.id)::int as like_count
FROM posts p
LEFT JOIN comments c ON c.post_id = p.id AND c.deleted_at IS NULL
LEFT JOIN likes l ON l.post_id = p.id
WHERE p.user_id = ${userId}
AND p.deleted_at IS NULL
GROUP BY p.id, p.title, p.content, p.created_at
ORDER BY p.created_at DESC
`;
}
4.2 Service層
async getUserDashboard(userId: string): Promise<DashboardData> {
// 並列実行
const [user, posts, notifications] = await Promise.all([
this.userRepository.findById(userId),
this.postRepository.findByUserIdWithStats(userId), // 最適化版
this.notificationRepository.findByUserId(userId),
]);
if (!user) {
throw new UserNotFoundError(`User ${userId} not found`);
}
return {
user,
posts,
notifications: notifications.slice(0, 10), // 最新10件のみ
};
}
4.3 パフォーマンステスト
describe('getUserDashboard Performance', () => {
beforeEach(async () => {
// 100件のポスト、各ポストに10コメント、20いいねを作成
await seedTestData({
users: 1,
postsPerUser: 100,
commentsPerPost: 10,
likesPerPost: 20,
});
});
it('200ms以下でレスポンスを返す', async () => {
const start = Date.now();
await service.getUserDashboard(testUser.id);
const duration = Date.now() - start;
expect(duration).toBeLessThan(200);
});
it('DBクエリ数が3回以下', async () => {
const queries = [];
prisma.$on('query', (e) => queries.push(e));
await service.getUserDashboard(testUser.id);
expect(queries.length).toBeLessThanOrEqual(3);
});
});
効果:
- ✅ 思考過程の可視化
- ✅ 複数案の比較検討
- ✅ 最適解の根拠が明確
- ✅ 実装 + テストまで完結
エージェント別の最適化戦略
Claude Code、Cursor、GitHub Copilotなど、各AIコーディングエージェントには異なる強みと特性があります。それぞれの特徴を理解し、適切に使い分けることで、開発効率を最大化できます。本セクションでは、エージェントごとの最適な使い方と、複数エージェントを組み合わせた効果的なワークフローを紹介します。
各エージェントの特性
それぞれのエージェントには、異なる強みと適性があります。 これらの特性を理解し、タスクに応じて使い分けることが、開発効率を最大化する鍵となります。例えば、Claude Codeは大量のコンテキストを深く理解する能力に優れ、Cursorはエディタとの統合による迅速な実装に強く、Copilotはコーディング中のリアルタイム支援に最適です。
エージェント | 強み | 弱み | 最適な用途 |
---|---|---|---|
Claude Code | 長文コンテキスト理解、深い分析力 | ファイル操作の制限 | 設計・アーキテクチャ分析・包括的レビュー |
Cursor | IDE統合、迅速なファイル操作 | プロンプト長制限 | 実装・リファクタ・複数ファイル生成 |
GitHub Copilot | リアルタイムコード補完 | コンテキスト範囲狭い | インライン補完・定型コード生成 |
Claude Code 最適化戦略
Claude Codeは、大量のコンテキストを深く理解する能力に優れています。 この特性を活かして、プロジェクト全体の包括的な分析や、複雑な設計判断、詳細なコードレビューといった、深い思考を要するタスクに活用するのが最適です。
強み: 長文コンテキストの理解
プロンプト:
"""
以下の3つのドキュメントを全て読んで、
包括的なアーキテクチャ分析をしてください:
@.ai/team/context.md (3000行)
@.ai/team/architecture.md (2000行)
@.ai/domains/*/context.md (各1000行 x 5ファイル)
分析内容:
1. 現在のアーキテクチャの課題
2. マイクロサービス化の是非
3. 移行計画(リスク評価含む)
"""
Claude Codeは大量のコンテキストを適切に処理可能です。
最適なプロンプト設計
Claude Codeの強みを最大限に活かすには、体系的なプロンプト構造が重要です。 大量のコンテキストを効率的に処理できる特性を活かして、複数のコンテキストファイルを参照し、段階的な思考プロセスを経て、包括的な成果物を生成させます。以下のテンプレートは、Claude Codeの能力を最大限に引き出すために最適化されています。
# Claude Code向けプロンプトテンプレート
## フェーズ1: コンテキスト収集
@.ai/team/context.md
@.ai/domains/{関連ドメイン}/context.md
@{関連する既存実装ファイル}
## フェーズ2: 目的の明確化
【達成したいこと】
{1-2文で簡潔に}
## フェーズ3: 制約と要件
【必須要件】
- {機能要件1}
- {機能要件2}
【制約】
- {技術制約1}
- {設計原則2}
## フェーズ4: 思考プロセス
以下のステップで考えてください:
1. {ステップ1}
2. {ステップ2}
3. {ステップ3}
## フェーズ5: 出力形式
{コード / 設計書 / レビューコメント など}
Cursor 最適化戦略
Cursorは、IDEとの密接な統合により、シームレスな開発体験を提供します。 ファイル操作、コード生成、リファクタリングといった日常的な開発作業を、エディタを離れることなく効率的に実行できます。この即応性を活かして、実装フェーズでの生産性を最大化します。
強み: IDE統合とファイル操作
// Cursorの強み: インラインコード生成
// 1. コメントベースのプロンプト
プロンプト(コメントで記述):
"""
// @.cursorrules を参照して、
// UserServiceと同じパターンで
// ProductServiceを実装
// 以下のメソッドが必要:
// - create(dto)
// - findById(id)
// - update(id, dto)
// - delete(id)
"""
// 2. 選択範囲のリファクタ
// コードを選択して:
// "このコードを@conventions.mdのパターンに従ってリファクタ"
// 3. 複数ファイル生成
// "UserModuleと同じ構造でProductModuleを作成"
// → module, service, controller, repository を自動生成
最適な使い方
Cursorの真価は、エディタ内で完結する高速なイテレーションサイクルにあります。 ファイルを開いた状態で直接指示を出し、即座に実装を生成・修正できる特性を活かして、実装フェーズでの生産性を最大化します。コメントベースのプロンプトや選択範囲のリファクタなど、IDE統合の利点を最大限に活用しましょう。
# Cursor向け使用パターン
## パターン1: 段階的実装
1. 型定義から始める
コメント: "DTOを定義"
2. インターフェースを実装
コメント: "上記のDTOを使ったServiceインターフェース"
3. 実装を完成
コメント: "Serviceの実装、@patterns.md参照"
## パターン2: 既存コード拡張
1. 既存ファイルを開く
2. 追加したい箇所にコメント
3. Cursorが文脈を理解して生成
## パターン3: バルク編集
1. 複数ファイルを選択
2. 一括で変更を指示
例: "全てのServiceでロギングを追加"
GitHub Copilot 最適化戦略
Copilotは、コーディング中のリアルタイム支援に特化しています。 タイプしながら即座に適切な補完を提供する能力は、開発のリズムを維持しながら、品質の高いコードを書くことを可能にします。ファイルの先頭にコンテキストを配置することで、この補完精度をさらに向上させることができます。
強み: リアルタイム補完
// Copilotの強み: コンテキストを読んだインライン補完
// 1. ファイル冒頭にコンテキストコメント
/**
* Product Service
*
* @context UserServiceと同じパターンを踏襲
* @architecture Clean Architecture
* @conventions conventions.md参照
*/
// 2. メソッドシグネチャを書くだけ
async findById(id: string): Promise<Product> {
// Copilotが自動補完
// → エラーハンドリング
// → ログ出力
// → 型チェック
// すべて conventions.md に従った実装
}
// 3. テストも自動生成
describe('ProductService', () => {
it('should find product by id', async () => {
// Copilotがテストコードを補完
// 既存のUserService.test.tsのパターンを踏襲
});
});
ファイル構造での最適化
Copilotは、ファイルの先頭から現在のカーソル位置までのコンテキストを参照します。 そのため、ファイルの冒頭に「コンテキストブロック」として、設計意図、使用パターン、参照すべき規約などを記載しておくことで、補完の精度が劇的に向上します。これは、Copilotに「このファイルの文脈」を理解させるための効果的なテクニックです。
// ファイルの先頭に「コンテキストブロック」を配置
/**
* @file product.service.ts
* @context
* - Architecture: Clean Architecture(@architecture.md)
* - Pattern: UserServiceと同じDI + Repository パターン
* - Error Handling: CustomError + ロギング(@conventions.md)
* - Testing: Jest + supertest(@conventions.md)
*
* @example
* const service = new ProductService(repository);
* const product = await service.findById('123');
*/
@Injectable()
export class ProductService {
// Copilotはこのコンテキストを参照して補完
}
エージェント併用戦略
それぞれのエージェントの強みを理解し、適材適所で使い分けることが、最高の成果への近道です。 設計にはClaude Code、実装にはCursor、日常のコーディングにはCopilotと、開発プロセスの各段階で最適なツールを選択することで、全体の生産性と品質を最大化できます。
ワークフロー例:
// ステップ1: Claude Codeで設計
プロンプト(Claude Code):
"""
@context.md を読んで、
決済システムの設計を提案してください。
以下を含めてください:
- アーキテクチャ図
- API設計
- データモデル
- セキュリティ考慮事項
- エラーハンドリング戦略
"""
// ステップ2: Cursorで実装
// Claude Codeの設計書を.ai/domains/payment/design.mdに保存
プロンプト(Cursor):
"""
@.ai/domains/payment/design.md の設計に基づいて、
PaymentModuleを実装してください。
"""
// ステップ3: Copilotで細部の補完
// Cursorが生成した骨組みに対して、
// Copilotが詳細なロジックを補完
// ステップ4: Claude Codeでレビュー
プロンプト(Claude Code):
"""
@src/modules/payment/* を読んで、
以下の観点でレビューしてください:
- 設計書との整合性
- セキュリティ上の問題
- パフォーマンス上の懸念
- テストカバレッジ
改善提案を具体的に。
"""
メタプロンプティング実践
メタプロンプティングは、「プロンプトを生成・改善するためのプロンプト」を活用する高度なテクニックです。エージェント自身にプロンプトの改善を依頼したり、コンテキストファイルの生成を任せることで、プロンプトエンジニアリングの作業自体を効率化できます。
メタプロンプティングとは
メタプロンプティングは、プロンプトエンジニアリング自体を自動化する高度なテクニックです。 エージェントに「より良いプロンプトを書かせる」ことで、プロンプト作成の負担を軽減しながら、品質を向上させることができます。これは、AIを使ってAIの使い方を改善するという、メタレベルのアプローチです。
メタプロンプティング = プロンプト自体を生成・改善するプロンプト
通常:
あなた → プロンプト → エージェント → 出力
(プロンプト作成は人間の責任)
メタ:
あなた → メタプロンプト → エージェント → 最適化されたプロンプト → 出力
(プロンプト作成もAIが支援)
パターン1: プロンプト改善依頼
曖昧なプロンプトを、AIに具体的で効果的なプロンプトへと変換させます。 初心者でも、エージェントに改善を依頼することで、ベテランが書くようなプロンプトを生成できます。プロジェクトのコンテキストを参照させることで、そのプロジェクトに最適化されたプロンプトが得られます。
プロンプト:
"""
以下のプロンプトを、より効果的に改善してください:
【現在のプロンプト】
「ログイン機能を作って」
【問題点】
- 具体性に欠ける
- 制約が不明確
- 出力形式が指定されていない
【改善してほしい観点】
1. 具体的な要件を明示
2. 技術スタックを指定
3. コンテキストファイルを参照
4. 段階的な実装ステップを含める
@context.md のプロジェクト情報を考慮してください。
"""
エージェントの出力:
"""
【改善版プロンプト】
@.ai/team/context.md
@.ai/domains/auth/context.md
@.ai/domains/auth/examples.md
を参照して、ログイン機能を実装してください。
## 要件
### 機能要件
- メールアドレス + パスワードでログイン
- JWT認証(アクセストークン15分、リフレッシュトークン7日)
- ログイン失敗5回でアカウントロック(15分間)
- ログイン履歴の記録
### 非機能要件
- レスポンス時間: 95%ile < 500ms
- 同時ログイン: 10,000 req/sec
- セキュリティ: OWASP Top 10対策
### 技術スタック
- NestJS + TypeScript
- Prisma ORM
- JWT(jsonwebtoken)
- bcrypt(パスワードハッシュ)
## 実装ステップ
### Phase 1: DTO定義
- LoginDto(バリデーション含む)
- LoginResponseDto
### Phase 2: Service層
- AuthService.login()
- パスワード検証
- トークン生成
- ログイン履歴記録
### Phase 3: Controller層
- POST /api/v1/auth/login
- エラーハンドリング
- レート制限
### Phase 4: テスト
- ユニットテスト(Serviceロジック)
- 統合テスト(APIエンドポイント)
- パフォーマンステスト
## 制約
- ADR-001の認証方式に従う
- conventions.mdのエラーハンドリングパターンを使用
- 既存のUserServiceを活用
## 出力形式
各Phaseごとに、コード + テストを生成してください。
Phase完了後、次のPhaseに進む前に確認を求めてください。
"""
パターン2: コンテキストファイル生成プロンプト
新しいドメインやモジュールを追加する際、コンテキストファイルの作成もエージェントに任せることができます。 既存のコンテキストファイルをテンプレートとして参照させることで、プロジェクト全体で一貫性のある構造を維持しながら、新しいコンテキストを効率的に生成できます。これにより、ドキュメント作成の負担が大幅に軽減されます。
プロンプト:
"""
新しいドメイン「通知システム」のコンテキストファイルを
生成してください。
以下のテンプレートを使用:
@.ai/domains/auth/context.md(参考)
含めるべき情報:
- ドメイン概要
- 技術的決定事項
- 外部サービス連携(SendGrid, Twilio)
- データモデル
- API設計
- エラーハンドリング
- パフォーマンス要件
生成先: .ai/domains/notification/context.md
"""
エージェントの出力:
"""
以下の内容で context.md を生成します:
# 通知システムコンテキスト
> 最終更新: 2024-10-12
> 担当: Notification Team
> バージョン: 1.0.0
## ドメイン概要
通知システムは、ユーザーに対してリアルタイムまたは非同期で
情報を届けるための中核機能です。
### 責務
- 通知の作成・配信
- 配信チャネルの管理(メール、SMS、プッシュ)
- 通知設定の管理
- 配信履歴の記録
### 境界
- ✅ 通知の配信ロジック
- ✅ ユーザー設定の管理
- ❌ 通知内容の生成(各ドメインの責務)
- ❌ テンプレート管理(別システム)
## 技術的決定事項
### ADR-030: 通知配信の非同期化
**決定**: すべての通知配信をBullMQで非同期処理
**理由**:
- API レスポンスの高速化
- リトライ機構の実装が容易
- スケーラビリティの向上
**トレードオフ**:
- 即時配信の遅延(数秒)
- ジョブキューの運用コスト
### ADR-031: マルチチャネル対応
**決定**: Strategy Patternで各チャネルを抽象化
```typescript
interface NotificationChannel {
send(notification: Notification): Promise<void>;
}
class EmailChannel implements NotificationChannel { }
class SMSChannel implements NotificationChannel { }
class PushChannel implements NotificationChannel { }
外部サービス連携
SendGrid(メール配信)
API Key: 環境変数 SENDGRID_API_KEY
From Address: noreply@example.com
レート制限: 10,000通/日(Freeプラン)
実装パターン:
await this.sendgrid.send({
to: user.email,
from: 'noreply@example.com',
subject: notification.subject,
html: notification.body,
templateId: notification.templateId,
});
Twilio(SMS配信)
Account SID: 環境変数 TWILIO_ACCOUNT_SID
Auth Token: 環境変数 TWILIO_AUTH_TOKEN
From Number: +1234567890
レート制限: 100通/秒
データモデル
model Notification {
id String @id @default(uuid())
userId String
type NotificationType
channel NotificationChannel
title String
body String
data Json?
status NotificationStatus
sentAt DateTime?
readAt DateTime?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
@@index([userId, status])
@@index([createdAt])
}
enum NotificationType {
SYSTEM_ALERT
MESSAGE
REMINDER
}
enum NotificationChannel {
EMAIL
SMS
PUSH
}
enum NotificationStatus {
PENDING
SENT
FAILED
READ
}
API設計
POST /api/v1/notifications
通知を作成(非同期配信)
{
"userId": "user-123",
"type": "MESSAGE",
"channel": "EMAIL",
"title": "新着メッセージ",
"body": "〇〇さんからメッセージが届きました",
"data": { "messageId": "msg-456" }
}
GET /api/v1/notifications
ユーザーの通知一覧
クエリパラメータ:
-
status
: PENDING | SENT | READ -
page
,limit
: ページネーション
PATCH /api/v1/notifications/:id/read
既読にする
エラーハンドリング
配信失敗時の対応
try {
await channel.send(notification);
} catch (error) {
// リトライ設定
if (error.isRetryable) {
throw new RetryableError('配信に失敗しました', {
cause: error,
retryAfter: 60, // 60秒後にリトライ
});
}
// 恒久的な失敗(例: メールアドレス不正)
await this.notificationRepository.updateStatus(
notification.id,
NotificationStatus.FAILED
);
this.logger.error('通知配信に失敗', {
notificationId: notification.id,
channel: notification.channel,
error,
});
}
パフォーマンス要件
指標 | 目標値 |
---|---|
API レスポンス時間 | 95%ile < 100ms |
通知配信遅延 | 平均 < 5秒 |
配信成功率 | > 99% |
同時配信数 | 1,000通/秒 |
テスト戦略
ユニットテスト
- 各Channelの送信ロジック
- リトライ機構
- ステータス遷移
統合テスト
- 外部サービスのモック
- エンドツーエンドの配信フロー
負荷テスト
- 1,000通/秒の配信
- メモリリーク確認
"""
### パターン3: デバッグプロンプト生成
**エラーログやパフォーマンス問題から、効果的なデバッグプロンプトを自動生成します。** 問題の現象を入力するだけで、エージェントが体系的な調査手順と解決策を含む包括的なデバッグプロンプトを作成します。これにより、デバッグプロセスの標準化と効率化が実現できます。
```typescript
プロンプト:
"""
以下のエラーログから、効果的なデバッグプロンプトを生成してください:
【エラーログ】
Error: Query timeout
at PostgresQueryRunner.query (query-runner.ts:123)
at UserRepository.findAll (user.repository.ts:45)
at UserService.getAllUsers (user.service.ts:67)
Slow Query Log:
SELECT * FROM users
LEFT JOIN posts ON users.id = posts.user_id
LEFT JOIN comments ON posts.id = comments.post_id
WHERE users.created_at > '2024-01-01'
[Duration: 45.2s]
デバッグプロンプトには以下を含めてください:
1. 問題の特定
2. 調査ステップ
3. 解決策の提案
4. 予防策
"""
エージェントの出力:
"""
【生成されたデバッグプロンプト】
以下のクエリタイムアウト問題をデバッグしてください。
## 現象
- クエリ実行時間: 45.2秒
- エラー: Query timeout
- 影響範囲: UserService.getAllUsers()
## 問題のクエリ
```sql
SELECT * FROM users
LEFT JOIN posts ON users.id = posts.user_id
LEFT JOIN comments ON posts.id = comments.post_id
WHERE users.created_at > '2024-01-01'
デバッグステップ
ステップ1: データ量の確認
以下を調査してください:
-- 各テーブルの件数
SELECT 'users', COUNT(*) FROM users;
SELECT 'posts', COUNT(*) FROM posts;
SELECT 'comments', COUNT(*) FROM comments;
-- WHERE条件に該当する件数
SELECT COUNT(*) FROM users
WHERE users.created_at > '2024-01-01';
ステップ2: EXPLAIN ANALYZE
クエリプランを確認:
EXPLAIN ANALYZE
SELECT * FROM users
LEFT JOIN posts ON users.id = posts.user_id
LEFT JOIN comments ON posts.id = comments.post_id
WHERE users.created_at > '2024-01-01';
以下をチェック:
- Sequential Scan(フルスキャン)が発生しているか
- JOIN のコスト
- インデックスが使用されているか
ステップ3: インデックスの確認
-- 既存のインデックス確認
SELECT * FROM pg_indexes
WHERE tablename IN ('users', 'posts', 'comments');
ステップ4: コードレビュー
@src/modules/users/user.repository.ts
@src/modules/users/user.service.ts
以下を確認:
- N+1問題が発生していないか
- 不要なJOINがないか
- SELECT *を使用している理由
解決策の提案
提案1〜3を、それぞれ以下の観点で評価してください:
- 効果(クエリ時間の改善見込み)
- 実装コスト
- リスク
予防策
同様の問題を防ぐために:
- クエリの複雑性メトリクス
- スロークエリの監視設定
- ページネーション必須化
- インデックス設計のレビュープロセス
を提案してください。
"""
---
## トラブルシューティング
プロンプトエンジニアリング2.0を実践する際によく遭遇する問題と、その解決策を紹介します。出力の不安定性、コンテキストサイズの制限、古い情報の参照といった課題に対する具体的な対処法を理解することで、より確実にAIエージェントを活用できるようになります。
### 問題1: エージェントの出力が不安定
**出力の不安定性は、プロンプトの曖昧さが主な原因です。** エージェントは「推測」によって補完しようとするため、明示的な指示がないと、毎回異なる解釈をしてしまいます。コンテキスト参照と明確な制約を追加することで、この問題は大幅に改善できます。
**症状**:
- 同じプロンプトでも出力がバラバラ
- 時々規約を無視したコードを生成
- 品質にムラがある
**原因分析**:
```typescript
// 問題のあるプロンプト
プロンプト: "ユーザー登録機能を作って"
// 問題点:
// ❌ コンテキストの参照がない
// ❌ 制約が不明確
// ❌ 曖昧な表現
解決策:
// 改善版プロンプト
プロンプト:
"""
@.ai/team/context.md
@.ai/domains/users/context.md
@.ai/domains/users/examples.md
上記を必ず参照して、ユーザー登録機能を実装してください。
【必須要件】(これらを必ず守る)
✓ TypeScript strict mode
✓ class-validator でDTOバリデーション
✓ CustomErrorクラスでエラーハンドリング
✓ bcryptでパスワードハッシュ化(ソルトラウンド10)
【禁止事項】(これらは絶対に使わない)
✗ anyの使用
✗ console.log/error(Logger使用)
✗ プレーンなtry-catch
【参考実装】
@src/modules/auth/auth.service.ts の
register() メソッドと同じパターンで実装
【出力形式】
1. DTO定義
2. Service実装
3. ユニットテスト
各ステップで、規約準拠を確認しながら進めてください。
"""
効果:
- ✅ 出力の一貫性が向上
- ✅ 規約違反が減少
- ✅ 品質が安定
問題2: コンテキストが大きすぎてエラー
コンテキストウィンドウには物理的な上限があります。 すべての情報を一度に詰め込もうとするのではなく、戦略的に分割・階層化することで、必要な情報を効率的に提供できます。このセクションでは、大規模プロジェクトでも破綻しないコンテキスト管理の手法を紹介します。
症状:
- "Context length exceeded" エラー
- プロンプトが長すぎて送信できない
原因:
コンテキストファイルの合計サイズが大きすぎる
例: context.md (5000行) + architecture.md (3000行) + ...
解決策1: コンテキストの分割
単一の巨大ファイルを、目的別の小さなファイルに分割します。 プロンプトでは、タスクに必要なファイルのみを選択的に参照することで、コンテキストサイズを制御できます。この「必要なものだけ読み込む」アプローチにより、大規模プロジェクトでもスケーラブルなコンテキスト管理が可能になります。
# Before: 1つの巨大ファイル
.ai/team/context.md (10,000行) ❌
# After: 目的別に分割
.ai/team/
├── context.md (概要のみ、500行) ✅
├── architecture.md (アーキテクチャ、2000行)
├── conventions.md (規約、1500行)
├── tech-stack.md (技術詳細、1000行)
└── glossary.md (用語集、500行)
# プロンプトでは必要なものだけ参照
プロンプト:
"""
@.ai/team/context.md # 常に参照
@.ai/team/architecture.md # アーキテクチャ関連の時のみ
"""
解決策2: 階層的参照
エントリーポイントとなる軽量なコンテキストファイルを用意し、詳細情報は別ファイルへの参照として記載します。 エージェントは必要に応じて詳細ファイルを読み込むことができ、最初は概要のみで軽量に処理を開始できます。この「遅延ロード」的なアプローチにより、柔軟性と効率性を両立できます。
# .ai/team/context.md(エントリーポイント)
# プロジェクトコンテキスト
## 概要
{プロジェクトの簡単な説明}
## 詳細情報
詳細は以下を参照:
- アーキテクチャ: @architecture.md
- コーディング規約: @conventions.md
- 技術スタック: @tech-stack.md
## このファイルに含める情報
{最小限の重要情報のみ}
---
# プロンプトでの使い方
プロンプト:
"""
@context.md を読んで、概要を把握してください。
{タスク内容}
詳細が必要な場合、context.mdで言及されている
他のファイルを参照してください。
"""
解決策3: サマリーの活用
詳細なコンテキストファイルの要約版を作成します。 日常的なタスクではサマリーを使用し、詳細な設計や複雑な問題解決が必要な場合のみフルバージョンを参照するという使い分けができます。これにより、ほとんどのケースでコンテキストサイズを大幅に削減できます。
# .ai/team/context-summary.md
# プロジェクトコンテキスト要約
このファイルは、詳細なコンテキストのサマリーです。
クイックリファレンスとして使用してください。
## 技術スタック(要約)
- Backend: Node.js + NestJS
- Frontend: Next.js + React
- Database: PostgreSQL + Prisma
- 詳細: @tech-stack.md
## アーキテクチャ(要約)
- Clean Architecture
- レイヤー: Controller → Service → Repository
- 詳細: @architecture.md
## コーディング規約(要約)
- TypeScript strict mode必須
- エラーハンドリング: CustomErrorクラス
- テスト: Jest、カバレッジ80%以上
- 詳細: @conventions.md
---
# プロンプト戦略
1. 最初は summary.md のみ参照(軽量)
2. 必要に応じて詳細ファイルを追加参照
"""
問題3: エージェントが古い情報を参照
コンテキストファイルを更新しても、エージェントが古い情報を使い続けることがあります。 これは、プロンプトで明示的に「最新版を参照する」ことを指定していないことが原因です。コンテキストファイルに更新履歴を記録し、プロンプトで最新版の参照を明示することで、この問題を防げます。
症状:
- 更新したはずの規約を無視
- 廃止した技術スタックを使用
- 古いパターンで実装
原因:
コンテキストファイルは更新したが、
エージェントが古いキャッシュを参照している
解決策:
# 1. コンテキストに更新日を明記
# プロジェクトコンテキスト
> **最終更新: 2024-10-12**
> **バージョン: 3.2.0**
> **重要な変更: JWT有効期限を15分に短縮(ADR-025)**
## 最近の重要な変更
### 2024-10-12: JWT有効期限変更
- 変更前: 24時間
- 変更後: 15分
- 理由: セキュリティ強化
- 影響: すべての認証関連コードで更新必要
### 2024-10-10: Redis導入
- Session管理をRedisに移行
- 既存のメモリストアは廃止
- 移行ガイド: @docs/redis-migration.md
# 2. プロンプトで明示的に最新版を指定
プロンプト:
"""
【重要】
@.ai/team/context.md の最新版(2024-10-12更新)を
必ず参照してください。
特に以下の最近の変更を考慮:
- JWT有効期限: 15分(24時間ではない)
- Session管理: Redis使用(メモリストアは廃止)
これらを踏まえて、ログイン機能を実装してください。
"""
# 3. 廃止事項を明示
# .ai/team/deprecated.md
# 廃止された技術・パターン
## ❌ 使用禁止
### メモリベースSession管理
**廃止日**: 2024-10-10
**理由**: スケーラビリティの問題
**代替**: Redis Session Store
**移行ガイド**: @docs/redis-migration.md
### JWT有効期限24時間
**廃止日**: 2024-10-12
**理由**: セキュリティリスク
**代替**: 15分 + リフレッシュトークン
**影響**: すべての認証コードで更新必要
## 使用時の警告
もし上記のパターンが提案された場合:
1. 即座に指摘
2. 代替案を提示
3. 移行ガイドを参照
ケーススタディ
実際のプロジェクトで、コンテキストエンジニアリングとプロンプトエンジニアリング2.0がどのような効果をもたらしたかを、2つの事例を通じて紹介します。スタートアップでの導入事例と大規模リファクタリングの事例から、投資対効果や具体的な成果を確認できます。
ケース1: スタートアップでの導入
小規模チームでも、コンテキストエンジニアリング2.0は大きな効果を発揮します。 このケースでは、5人のチームがコンテキストとプロンプトパターンを整備することで、コード品質の安定化とオンボーディング時間の大幅短縮を実現しました。投資回収期間はわずか1.5ヶ月で、継続的な効果が得られています。
背景:
- チーム規模: 5人
- プロダクト: SaaSプラットフォーム
- 課題: コード品質のバラつき、オンボーディングに時間
導入前の状況:
// メンバーAのコード
async createUser(data: any) {
try {
return await db.users.create(data);
} catch (e) {
console.log(e);
}
}
// メンバーBのコード
async createUser(userData: CreateUserDto): Promise<User> {
const user = await this.userRepository.create(userData);
if (!user) {
throw new UserCreationError('Failed to create user');
}
return user;
}
// バラバラ... 😱
導入ステップ:
4週間の段階的な導入計画により、リスクを最小化しながら成果を最大化しました。 最初の週でチーム全員が合意するコンテキストファイルを作成し、2週目で具体例を追加、3週目でプロンプトパターンを共有、4週目で効果を測定しました。この計画的なアプローチが、チーム全体の賛同と継続的な改善につながりました。
## Week 1: コンテキストファイル作成
- .ai/team/context.md
- .ai/team/conventions.md
- チーム全員でレビュー・合意
## Week 2: Few-shot例の追加
- 各モジュールの「良い例」を抽出
- .ai/domains/*/examples.md に記録
## Week 3: プロンプトパターンの共有
- チーム共通のプロンプトテンプレート作成
- Slackで共有・フィードバック
## Week 4: 効果測定
- コードレビュー時間: 50%削減
- 規約違反: 80%減少
- 新メンバーの生産性: 2週間で戦力化
導入後の状況:
// 全メンバーが統一されたコードを生成
// プロンプト(全員共通)
"""
@.ai/team/context.md
@.ai/domains/users/examples.md
Example 2のパターンで、ユーザー作成機能を実装
"""
// 出力(誰が実行しても同じ)
async createUser(userData: CreateUserDto): Promise<User> {
try {
const user = await this.userRepository.create(userData);
if (!user) {
throw new UserCreationError('Failed to create user', {
context: { userData },
});
}
this.logger.info('User created successfully', {
userId: user.id,
});
return user;
} catch (error) {
this.logger.error('User creation failed', {
error,
userData,
});
throw error;
}
}
// 一貫性のあるコード ✅
ROI計算:
コンテキスト構築時間: 40時間(Week 1-3)
月間削減時間:
- コードレビュー: 20時間/月
- 修正作業: 15時間/月
- オンボーディング: 30時間/新メンバー
投資回収期間: 約1.5ヶ月
ケース2: 大規模リファクタリング
レガシーコードのモダナイゼーションは、最も困難なプロジェクトの一つです。 このケースでは、5万行のレガシーコードをモダンアーキテクチャに移行するにあたり、「理想の形」をコンテキスト化し、段階的移行プロンプトを整備することで、従来の3倍の速度でリファクタを完了し、バグ混入率を1/5に抑えることに成功しました。
背景:
- レガシーコード: 5万行
- 技術的負債: 高
- 目標: モダンアーキテクチャへの移行
課題:
- 全体を把握できる人が少ない
- リファクタのパターンが統一されていない
- エージェントが既存コードのパターンを誤学習
解決アプローチ:
成功の鍵は、「理想の形」を明確に定義し、段階的移行を徹底したことでした。 まず、リファクタ後のターゲットアーキテクチャをコンテキストファイルとして文書化し、すべてのリファクタ作業がこの「北極星」を参照するようにしました。さらに、一度に全てを変えるのではなく、テストで保護しながらPhaseごとに進める戦略により、安全性と速度を両立できました。
// ステップ1: 「理想の形」をコンテキスト化
// .ai/refactoring/target-architecture.md
## リファクタ後の理想形
### Before(レガシー)
```typescript
// controllers/user.js
app.post('/users', (req, res) => {
const { name, email } = req.body;
db.query('INSERT INTO users...', (err, result) => {
if (err) {
res.status(500).json({ error: err });
} else {
res.json(result);
}
});
});
After(目標)
// modules/users/users.controller.ts
@Controller('users')
export class UsersController {
@Post()
async create(@Body() dto: CreateUserDto): Promise<User> {
return this.usersService.create(dto);
}
}
移行ルール
- Express → NestJS
- Callback → async/await
- 生SQL → Prisma ORM
- エラーハンドリングの統一
// ステップ2: 段階的移行プロンプト
プロンプト:
"""
@.ai/refactoring/target-architecture.md を参照して、
以下のレガシーコードをリファクタしてください:
@controllers/user.js
【重要な制約】
- 既存の動作を100%維持
- テストケースを先に書く
- 段階的に移行(一度に全部変えない)
【リファクタ手順】
Phase 1: テストケース作成(現在の動作を担保)
Phase 2: 型定義追加(型安全性)
Phase 3: NestJSモジュール化(アーキテクチャ移行)
Phase 4: Prisma導入(ORM化)
Phase 5: エラーハンドリング統一
各Phaseごとに、テストが通ることを確認してください。
"""
**成果**:
- リファクタ速度: 従来の3倍
- バグ混入: 従来の1/5
- チーム全体で一貫したパターン
---
## まとめ
### プロンプトエンジニアリング2.0の本質
**プロンプトエンジニアリング2.0の本質は、「知識の構造化と再利用」にあります。** コンテキストでプロジェクトの知識を体系化し、Few-shotで具体的なパターンを示し、Chain-of-Thoughtで思考プロセスを明示することで、AIエージェントから一貫した高品質な出力を引き出します。これは単なるテクニックの集合ではなく、AIとの協働における新しいパラダイムです。
```mermaid
graph TD
A[コンテキスト<br/>プロジェクトの知識] --> D[一貫した高品質出力]
B[Few-shot<br/>具体的パターン] --> D
C[CoT<br/>思考プロセス] --> D
D --> E[チーム全体の生産性向上]
D --> F[コード品質の安定化]
D --> G[知識の組織的蓄積]
style D fill:#90EE90
style E fill:#FFD700
style F fill:#FFD700
style G fill:#FFD700
成功の5原則
プロンプトエンジニアリング2.0を成功させるための5つの原則をまとめます。 これらは、数多くのプロジェクトでの実践から抽出されたエッセンスであり、どのような規模や種類のプロジェクトにも適用可能です。
1. 構造化されたコンテキスト
コンテキストは、プロジェクトの「憲法」として機能します。 散在する知識を体系的に整理し、誰もがアクセス・参照できる形で維持することで、チーム全体の生産性が向上します。
- プロジェクトの「憲法」として機能
- 3層構造(システム・タスク・補足)で整理
- Git管理で進化
2. 具体的なプロンプト設計
曖昧さは、エージェントの混乱と出力の不安定性につながります。 明確で具体的なプロンプトを書くことで、期待通りの結果を安定的に得られます。
- 曖昧な表現を排除
- 制約を明示
- 出力形式を指定
3. Few-shot学習の活用
具体例は、抽象的な説明よりも遥かに効果的です。 良い例と悪い例の両方を示すことで、エージェントは「何をすべきか」と「何を避けるべきか」を確実に学習します。
- 良い例・悪い例の両方を含める
- 段階的な複雑性で理解を深化
- コメントで意図を明示
4. Chain-of-Thoughtの実践
思考プロセスを可視化することで、AIの推論能力が飛躍的に向上します。 複雑な問題ほど、段階的な思考を経ることで、より質の高い解決策が得られます。
- 複雑なタスクは思考過程を明示
- 設計判断の根拠を記録
- レビュー可能なアウトプット
5. 継続的改善
プロンプトとコンテキストは、コードと同じく進化する資産です。 定期的に見直し、改善を重ねることで、チーム全体の知識が蓄積され、開発効率が継続的に向上します。
- プロンプトもコードと同じく進化
- メタプロンプティングで最適化
- チームでナレッジ共有
今日から始められること
プロンプトエンジニアリング2.0は、今すぐ始められます。 大規模な準備や複雑なセットアップは不要です。わずか30分で基本的なコンテキストとプロンプトテンプレートを作成し、即座に効果を実感できます。以下のクイックスタートガイドに従って、まずは小さく始めてみましょう。
# 30分でできるクイックスタート
## ステップ1: 基本コンテキストの作成(15分)
**まずは最小限のコンテキストファイルを作成します。** プロジェクトの技術スタック、最重要ルール、禁止事項の3点だけを記載するだけでも、エージェントの出力品質は大きく向上します。
```bash
mkdir -p .ai/team
cat > .ai/team/context.md << 'EOF'
# プロジェクトコンテキスト
## 技術スタック
- Backend: {あなたの技術スタック}
- Frontend: {あなたの技術スタック}
- Database: {あなたの技術スタック}
## コーディング規約
- {最重要ルール3つ}
## 禁止事項
- {やってはいけないこと3つ}
EOF
ステップ2: プロンプトテンプレートの作成(10分)
次に、再利用可能なプロンプトテンプレートを作成します。 このテンプレートを雛形として使うことで、毎回一から考える必要がなくなり、一貫性のあるプロンプトを効率的に作成できます。
# my-prompt-template.md
@.ai/team/context.md を読んで、
{タスク内容}を実装してください。
【要件】
- {要件1}
- {要件2}
【制約】
- {制約1}
- {制約2}
【参考実装】
@{既存の良い実装}
ステップ3: 実際に使ってみる(5分)
実際にエージェントに投げて、効果を確認します。 出力をレビューし、気づいたことや改善点をコンテキストファイルに追記していくことで、継続的に品質が向上します。この「試す→レビュー→改善」のサイクルが、プロンプトエンジニアリング2.0の基本です。
- エージェントに上記プロンプトを投げる
- 出力をレビュー
- 気づきをcontext.mdに追記
これだけでも効果を実感できます!
### 次のステップ
**プロンプトエンジニアリング2.0は、段階的に成熟させていくものです。** 焦らず、1週間、2週間、1ヶ月と時間をかけて、少しずつ洗練させていきましょう。各段階で新しい要素を追加することで、着実にチーム全体の能力が向上していきます。
```mermaid
graph LR
A[今日<br/>基本コンテキスト] --> B[1週間後<br/>Few-shot例追加]
B --> C[2週間後<br/>CoT導入]
C --> D[1ヶ月後<br/>チーム展開]
D --> E[継続的改善]
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#ffe1f5
style D fill:#e1ffe1
style E fill:#90EE90
関連記事(コンテキストエンジニアリングシリーズ)
本記事は、コンテキストエンジニアリングシリーズの第3弾です。 シリーズ全体を通じて読むことで、個人での活用からチーム展開、さらには開発手法としての体系化まで、包括的な知識を獲得できます。順番に読み進めることをお勧めしますが、興味のあるトピックから読んでも理解できるよう構成されています。
-
コーディングエージェント時代のコンテキストエンジニアリング実践ガイド
- 個人利用の基礎(前提知識)
-
コンテキストウィンドウの処理フローと動作メカニズム 完全解説
- 技術的詳細(深い理解)
-
プロンプトエンジニアリング2.0 - コンテキストを制する者がAIを制する
- コンテキスト管理 + プロンプト設計の統合
-
コーディングエージェントのメモリ設計 - 長期記憶システムの実装
- 外部化したコンテキストの管理・検索
-
- チーム活用(組織展開)
-
コンテキスト駆動開発(CDD) - AIファーストな開発手法
- 開発手法としての体系化
-
- 複数エージェントの協調動作
-
- トラブルシューティングに特化
コンテキストを制する者が、AIエージェントを制する。
さあ、あなたのプロジェクトで実践してみましょう!
付録:章のクイックガイド(すぐ効く要約)
ここまでお読みいただき、ありがとうございました。
この記事は約10万文字の大ボリュームでしたので、「あの内容、どこに書いてあったっけ?」となることもあるかもしれません。
そこで、各章のエッセンスを3点(到達点・対象・落とし穴)で要約したクイックリファレンスを用意しました。
このセクションの使い方
- 初めて読む方: ざっと目を通して、全体像を把握してから本編へ
- 既読の方: 必要な箇所を素早く見つけるインデックスとして活用
- チーム共有時: 各章の価値を端的に説明する際のチートシートに
各章の3点要約
1. コンテキスト設計の基礎
- 到達点: AIが誤解しない土台づくり(前提・制約・優先順位の明示)
- 対象: はじめてコンテキストを整える人/チーム
- 落とし穴: 「全部重要」指定でAIが迷走
2. Few-shot / Chain-of-Thought の実践
- 到達点: 再現性のある出力(良い例×悪い例の提示、思考の手順化)
- 対象: 品質のブレやすい生成を安定させたい場面
- 落とし穴: 例のドメイン不一致、手順の粒度過不足
3. メタプロンプトとテンプレート化
- 到達点: チームで再利用できるプロンプト資産
- 対象: 横展開・教育・レビューを効率化したい組織
- 落とし穴: テンプレの抽象度が高すぎて現場適用できない
4. よくある失敗と対策
- 到達点: 典型的失敗(曖昧/矛盾/情報過多/不足)を未然に回避
- 対象: 運用につまずきがちな全てのプロジェクト
- 落とし穴: 症状ベースで場当たり修正→再発
5. ケーススタディ(実戦)
- 到達点: Before/Afterで適用効果を短時間で把握
- 対象: 上層部説明/合意形成/導入判断
- 落とし穴: 成果指標が曖昧で説得力が弱い
ショートケース(すぐ真似できる2本)
ケースA: API仕様書生成の品質がブレる
Before: 同じ要求でも毎回粒度/表現が不揃いでレビューが難航
❌ プロンプト(改善前)
「このエンドポイントの仕様書を書いて」
✅ プロンプト(改善後|Few-shot + 出力フォーマット固定)
「以下のフォーマットに従って仕様書を作成してください。既存仕様の語彙/用語整合を優先。
【フォーマット】
- エンドポイント: /api/v1/users/{id}
- メソッド: GET
- リクエスト: パス/クエリ/ヘッダ(型/必須)
- レスポンス: 200/4xxのJSON例(型/必須)
- 例外/制約: 認可, レート制限
【良い例】
- 用語は既存のUserドメインに合わせる(Accountは禁止)
- 型はTypeScript準拠
」
After: レビュー時間 40%短縮、表記ゆれほぼ解消
ケースB: 実装ポリシーの不徹底でコードが揺れる
Before: 例外/Result型が混在、テスト観点も人により異なる
❌ コンテキスト(改善前)
「基本的にTypeScriptで」
✅ コンテキスト(改善後|優先順位と禁止事項の明示)
「言語はTypeScript固定。例外はResult型で返却(例外throw禁止、ライブラリエラー除く)。
テスト優先順位: ユニット > 統合 > E2E。命名/フォーマットは`.editorconfig`/ESLintに準拠。」
After: PR差戻し率 30%→10%、レビュー観点の共通化
よくある落とし穴(チェックリスト)
- 章の導入で「誰が/何が/どのくらいで/何が得られる」を明示
- 例はドメイン語彙を統一(既存用語と矛盾しない)
- 出力フォーマットを固定(見出し/表/コード)
- 禁止事項を明文化(曖昧語/例外throwなど)
- 成果指標(時間/コスト/欠陥率)を計測