EXERCISE 90分

演習:プロンプトを最適化しよう

ストーリー

「理論は十分だ。ここからは手を動かして体で覚えろ」

中島先輩が5つの課題を用意した。

「各課題で"悪いプロンプト"を改善して、AIから最高品質の回答を引き出してみろ。 実際にChatGPTかClaudeに投げて結果を確認するんだぞ」

「時間はどのくらいですか?」

「90分。焦らなくていい。大事なのは、なぜその改善が効果的なのかを理解することだ」


課題概要

課題テーマ使用テクニック難易度
課題1悪いプロンプトの改善基本原則初級
課題2Few-shot でコードスタイル統一Few-shot初級
課題3CoTでバグ分析Chain-of-Thought中級
課題4業務用システムプロンプト作成システムプロンプト中級
課題5複合テクニックでAPI設計全テクニック上級

課題1: 悪いプロンプトを改善しよう(15分)

以下の「悪いプロンプト」を、Step 2で学んだ基本原則を使って改善してください。

悪いプロンプト

ログイン機能作って

改善のポイント

以下の情報を追加してプロンプトを書き直してください:

  1. 技術スタック(言語、フレームワーク)
  2. 具体的な要件(認証方式、バリデーション)
  3. 制約条件(セキュリティ要件)
  4. 出力形式(コード + 説明の形式)

実際にAIに両方のプロンプトを投げて、回答の質の差を確認してください。

<details> <summary>解答例(自分で実装してから確認しよう)</summary>
あなたはセキュリティに詳しいバックエンドエンジニアです。

以下の要件でログイン機能のAPIエンドポイントを実装してください。

技術スタック:
- Node.js 20 + TypeScript
- Express.js
- bcrypt(パスワードハッシュ)
- jsonwebtoken(JWT)

要件:
- POST /api/auth/login エンドポイント
- リクエストボディ: { email: string, password: string }
- メールアドレスとパスワードでユーザーを認証
- 認証成功時にJWTアクセストークンを返却
- トークンの有効期限は1時間

セキュリティ要件:
- パスワードはbcryptで比較
- ログイン失敗時に「メールかパスワードが間違っています」と返す
  (どちらが間違いかは明示しない)
- レートリミットの考慮(コメントで記載)

出力形式:
1. エンドポイントのコード
2. 関連する型定義
3. 使用例(curlコマンド)
4. セキュリティ上の注意点

改善ポイント:

  • ロール設定で専門性を指定
  • 具体的な技術スタックを明示
  • セキュリティ要件を明記
  • 出力形式を構造化
</details>

課題2: Few-shotでコードスタイルを統一しよう(15分)

チームのコーディングスタイルに合わせたTypeScript関数を生成するFew-shotプロンプトを作成してください。

チームのスタイル規約

  • 関数はarrow function
  • エラーは Result型(成功/失敗を表す)で返す
  • JSDocコメント必須
  • バリデーションは先頭で実行(early return)

作成する関数

「ユーザーのメールアドレスを更新する関数」を、上記スタイルに合わせて生成してください。

Few-shotの例を2-3個自分で作成し、それを含めたプロンプトを書いてください。

<details> <summary>解答例(自分で実装してから確認しよう)</summary>
以下のコーディングスタイルに従って、関数を生成してください。

--- スタイルの例 ---

例1:
/**
 * ユーザー名を更新する
 * @param userId - 対象ユーザーのID
 * @param newName - 新しいユーザー名
 * @returns 成功時はユーザー情報、失敗時はエラー
 */
const updateUserName = async (
  userId: string,
  newName: string
): Promise<Result<User, AppError>> => {
  if (!userId) {
    return err(new AppError("INVALID_INPUT", "ユーザーIDは必須です"));
  }
  if (newName.length < 2 || newName.length > 50) {
    return err(new AppError("INVALID_INPUT", "名前は2〜50文字で入力してください"));
  }

  const user = await userRepository.findById(userId);
  if (!user) {
    return err(new AppError("NOT_FOUND", "ユーザーが見つかりません"));
  }

  const updated = await userRepository.update(userId, { name: newName });
  return ok(updated);
};

例2:
/**
 * 商品の価格を更新する
 * @param productId - 対象商品のID
 * @param newPrice - 新しい価格
 * @returns 成功時は商品情報、失敗時はエラー
 */
const updateProductPrice = async (
  productId: string,
  newPrice: number
): Promise<Result<Product, AppError>> => {
  if (!productId) {
    return err(new AppError("INVALID_INPUT", "商品IDは必須です"));
  }
  if (newPrice < 0) {
    return err(new AppError("INVALID_INPUT", "価格は0以上で入力してください"));
  }

  const product = await productRepository.findById(productId);
  if (!product) {
    return err(new AppError("NOT_FOUND", "商品が見つかりません"));
  }

  const updated = await productRepository.update(productId, { price: newPrice });
  return ok(updated);
};

--- 生成してほしい関数 ---

「ユーザーのメールアドレスを更新する関数」を上記スタイルで生成してください。
バリデーション: メールアドレスの形式チェック、重複チェック
</details>

課題3: Chain-of-Thoughtでバグを分析しよう(20分)

以下のバグがあるコードに対して、CoTを使って段階的に分析するプロンプトを作成してください。

バグのあるコード

typescript
interface CartItem {
  productId: string;
  name: string;
  price: number;
  quantity: number;
}

const calculateTotal = (items: CartItem[], couponCode?: string): number => {
  let total = 0;

  for (const item of items) {
    total += item.price * item.quantity;
  }

  if (couponCode === "SAVE10") {
    total = total * 0.1;
  }

  if (couponCode === "FLAT500") {
    total = total - 500;
  }

  return total;
};

CoTの分析ステップを自分で設計し、バグを特定するプロンプトを書いてください。

<details> <summary>解答例(自分で実装してから確認しよう)</summary>
以下のカート合計金額計算関数にバグがあります。
ステップバイステップで分析してください。

分析ステップ:
1. 関数の目的と期待される動作を整理する
2. 各処理ブロックの意図を推測する
3. テストケースを作成して期待値と実際の値を比較する
4. バグを特定し、原因を説明する
5. 修正案を提示する

テストケース例:
- items: [{price: 1000, quantity: 2}], coupon: "SAVE10"
  期待: 1800(10%引き) / 実際: ???
- items: [{price: 300, quantity: 1}], coupon: "FLAT500"
  期待: 0以上 / 実際: ???
- items: [], coupon: undefined
  期待: 0 / 実際: ???

[コードを貼り付け]

発見されるべきバグ:

  1. total * 0.1 は「10%にする」であり、「10%引き」(total * 0.9)ではない
  2. FLAT500クーポンで合計が負数になる可能性がある(Math.max(0, total - 500) が必要)
  3. itemsが空配列の場合のガード処理がない
</details>

課題4: 業務用システムプロンプトを作成しよう(20分)

あなたの開発業務を効率化するシステムプロンプトを作成してください。

要件

以下の情報を含むシステムプロンプトを作成してください:

  1. 自分の役割と技術スタック
  2. コーディング規約(少なくとも5つのルール)
  3. 出力の基本フォーマット
  4. してほしくないことリスト

作成したシステムプロンプトをChatGPTのCustom InstructionsまたはClaude Projectに設定し、いくつかの質問で動作確認してください。

<details> <summary>解答例(自分で実装してから確認しよう)</summary>
あなたは私の開発アシスタントです。

## 私について
- Webアプリケーションエンジニア(フロントエンド中心)
- 技術スタック: TypeScript, React, Next.js 14, Tailwind CSS, Prisma

## コーディング規約
1. TypeScript strict modeを前提とする
2. 関数コンポーネント + React Hooksを使用
3. anyは使用禁止、unknownで受けてnarrowingする
4. 命名はキャメルケース(変数・関数)、パスカルケース(型・コンポーネント)
5. エラーハンドリングは必ず含める
6. マジックナンバーは定数に抽出する
7. コメントは「なぜ」を書く(「何を」はコードで表現)

## 出力フォーマット
- まず3行以内で要約を述べる
- コードにはJSDocコメントを付ける
- 代替案がある場合は表形式で比較する
- セキュリティ上の懸念がある場合は警告を表示する

## してほしくないこと
- クラスコンポーネントを使わない
- jQuery, lodashを提案しない(標準APIで書く)
- 過度に抽象化しない(YAGNI原則)
- フォーマットやリントの指摘は不要

動作確認の質問例:

  • 「ユーザー一覧を表示するコンポーネントを作って」
  • 「このコードのパフォーマンスを改善して」
  • 「APIエンドポイントの設計を提案して」
</details>

課題5: 複合テクニックでAPI設計を依頼しよう(20分)

学んだテクニックを全て組み合わせて、以下のAPI設計をAIに依頼するプロンプトを作成してください。

お題

「チームタスク管理機能のREST API設計」

要件

  • ロール設定、コンテキスト、Few-shot、CoT、出力形式指定を全て使う
  • タスクのCRUD + 担当者アサイン + ステータス更新を含む
  • 実際にAIに投げて、回答の品質を評価する
<details> <summary>解答例(自分で実装してから確認しよう)</summary>
[ロール設定]
あなたはRESTful API設計の専門家です。
OpenAPI 3.0に精通し、リソース指向設計のベストプラクティスを知っています。

[コンテキスト]
背景: 社内チーム向けのタスク管理機能のAPI設計
技術: Node.js + Express + TypeScript + PostgreSQL
認証: JWT Bearer Token
チーム規模: 5-20人

[Few-shot: 既存APIの設計例]
参考: 当プロジェクトの既存API設計パターン

GET /api/v1/projects
Response: { data: Project[], meta: { total: number, page: number } }

POST /api/v1/projects
Request: { name: string, description?: string }
Response: { data: Project }

GET /api/v1/projects/:projectId
Response: { data: Project }

[CoT: 段階的に設計]
以下のステップで設計してください:
1. まず、リソースの洗い出しとER関係を整理する
2. 次に、各エンドポイントのパスとHTTPメソッドを決める
3. リクエスト/レスポンスの型を定義する
4. エラーレスポンスのパターンを定義する
5. 認可(誰がどの操作をできるか)を整理する

[出力形式]
以下の形式で出力してください:

### 1. リソース一覧
| リソース | 説明 | 主なフィールド |

### 2. エンドポイント一覧
| メソッド | パス | 説明 | 認可 |

### 3. 型定義(TypeScript)
各リソースのinterface定義

### 4. エラーパターン
| コード | 状況 | レスポンス |

[制約]
- RESTの命名規則に従う(複数形、kebab-case)
- ページネーション対応(cursor-based推奨)
- ソート・フィルタのクエリパラメータを含む
- 日時フィールドはISO 8601
</details>

達成度チェック

課題テーマ完了
課題1悪いプロンプトの改善[ ]
課題2Few-shotでスタイル統一[ ]
課題3CoTでバグ分析[ ]
課題4システムプロンプト作成[ ]
課題5複合テクニックでAPI設計[ ]

まとめ

ポイント内容
基本改善明確さ、コンテキスト、出力形式、制約の追加で品質向上
Few-shot例を示すことでフォーマットとスタイルを統一
CoT段階的分析で複雑なバグも見逃さない
システムプロンプト毎回の前提条件設定を効率化

チェックリスト

  • 悪いプロンプトを改善して品質差を体感した
  • Few-shotの例を自分で作成して使えた
  • CoTの分析ステップを自分で設計できた
  • 業務用のシステムプロンプトを作成して実際に設定した
  • 複数テクニックを組み合わせたプロンプトを作成できた

次のステップへ

お疲れさまでした。プロンプトエンジニアリングの実践演習が完了しました。 次はStep 2の理解度チェックです。


推定読了時間: 90分