ループ: 意図 → 生成 → レビュー → 洗練
本書のすべては一つのサイクルに帰結する。これを身体に染み込ませよう。
ループは出力があなたの基準を満たすまで回る——そしてレビューは毎回くぐり抜けねばならない関門だ:
┌──────────────────────────────────────────────┐
│ │
▼ │
┌────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐
│ 意図 │ ─▶ │ 生成 │ ─▶ │ 確認 │ ─▶ │ 改善 │
│ 仕様 │ │ (AI) │ │ あなた │ │ あなた │
└────────┘ └──────────┘ └───┬────┘ └──────────┘
│
合格? ▼
┌──────────┐
│ 出荷 │
└──────────┘
- 意図。 求めるものと、重要な制約を述べる——入力、出力、エッジケース、スタック、スタイル。曖昧な意図は曖昧なコードを生む。
- 生成。 モデルに実装を書かせる。構文を逐一手取り足取りせず、振る舞いを記述する。
- レビュー。 返ってきたものを読む。それは実際に目的を果たしているか。空のリスト、null、失敗したネットワーク呼び出しを処理しているか。それは動作する最もシンプルなバージョンか。
- 洗練。 何が間違っているかを指摘し、具体的な修正を与え、再生成する。基準を満たすまで繰り返す。
ほとんどの初心者はステップ3を飛ばす。生成し、動いた、次へ進む。そして記述しなかったケースでプロダクションが壊れる。レビューのステップこそ、エンジニアリングが起きる場所だ。
このループでの良い最初のプロンプトはこんな感じだ。
TypeScript 関数 `parseDuration(input: string): number` を書いて。
"1h30m", "45s", "2d" のような文字列を合計秒数に変換する。
要件:
- 単位をサポート: d (日), h (時間), m (分), s (秒).
- 複数の単位を組み合わせ可能: "1h30m" = 5400.
- 不正な入力(空, 不明な単位, 負数)は明確なメッセージ付きの
Error を throw して拒否する。
- 外部ライブラリ禁止。エッジケースを網羅する単体テスト5個を含める。
関数とテストのみを表示して。
その構造に注目してほしい。正確なシグネチャ、明示的なルール、列挙されたエッジケース、述べられた制約、そしてテストの要求。あなたはモデルが正しく当ててくれることを願っているのではない——悪い推測を生む曖昧さを取り除いているのだ。
洗練のステップも、最初のプロンプトと同じくらい一つのスキルだ。曖昧なフィードバックは曖昧な修正を生む。この二つの修正指示を比べてみよう。
悪い例: "これは間違ってる、直して"
良い例: "parseDuration('1h30m') が 5400 ではなく 90 を返す —
数値を合計しているが単位の倍率を無視している。合計する前に
各値をその単位の秒数(d=86400, h=3600, m=60, s=1)で掛けて。
さらに組み合わせケース '2d3h' のテストも追加して。"
良いほうは症状、原因、そして修正方法を名指しする。あなたは同僚のプルリクエストをレビューするようにレビューしているのだ——そして正確に指し示すほど、費やすラウンドは少なくなる。各ループは収束すべきだ。堂々巡りをしていると感じたら、それは意図が仕様不足だったというサインだ。再生成をやめて、仕様を書き直せ。