← Back to Blog

プロンプトの品質が、そのままコードの品質を決める

曖昧なプロンプトと、精密に構造化されたプロンプトを対比したイラスト

2人が、一見同じに見える依頼をAIエージェントに渡し、まったく異なる品質の結果を得ます。これらの依頼は実際には何が違うのか、そしてAIが作業を始める前にそれをどう見抜けるのでしょうか。答えは単純なルールです。依頼を詳しく具体的に書くほど、AIはそれをうまく実行します。当たり前に聞こえますが、実際にはこれが悪い結果の最も多い原因です。モデルの能力不足ではなく、依頼の記述不足なのです。

悪いプロンプト:「ログインのバグを直して。」 良いプロンプト:「AuthService.login() で、ユーザーが大文字を含むメールアドレスを入力するとエラーが発生する。DB の比較が大文字小文字を区別するためである。メールアドレスの正規化を比較の前に行うよう修正すること。パスワードのハッシュ化や LoginThrottle.ts のレート制限には触れないこと。」

2つ目のプロンプトには、1つ目にない重要な4点が含まれています。

  1. 一般的な説明ではなく、正確な場所(ファイル/関数)。
  2. 症状だけでなく、根本原因の説明。
  3. 単に「直して」ではなく、具体的に何をするか。
  4. 壊してはならないもの。変更に対する明示的な境界線。

4点目がないと、AIは触ってほしくなかったものまで「改善」しがちで、まさにその瞬間に下流のロジックが壊れます。

これは5歳の子どもに何をすべきか説明するのと似ています。何を、どのようにするかを伝えますが、同じくらい大切なのは、してはいけないことを伝えることです。子どもにとってその境界線は主に教育的なものですが、AIにとっては、調べてはならない、変更してはならない対象の正確な定義になります。そしてその境界線は、何を探すべきかというどんな説明よりも、結果をはるかに鋭くします。

同じ考え方が1つのプロンプトの中にも当てはまります。無関係な要件を一度に詰め込むと、AIはどれを優先すべきか分からず、意図とは違う解釈をすることがよくあります。そこから単純なルールが導かれます。1つのプロンプトには、1つの具体的なこと。

これは手書きのプロンプトにも、自動生成されるタスクにも同じように当てはまります。そして、この規律こそが自動化できるものです。プロンプトが人間がたまたま覚えていることから生まれるのではなく、AITM があなたに代わって維持している、コードに関する実際の最新の知識(コントラクト、DOMマップ、ビジネスロジック)から生まれるようにするのです。

プロンプトの構造そのものを見る前に、もう1つ知っておく価値のあることがあります。AITM はすべてのタスクプロンプトを同じ方法で書くわけではありません。タスクが作成されると、まずそれ自身が(またはあなたが選択して)3つのカテゴリのいずれかに分類されます。bug(既存の振る舞いの修正)、enhancement(すでにあるものの改善)、feature(まったく新しい機能)のいずれかで、カテゴリごとに異なる要件と検証基準が生成されます。ここではまず、3種類すべてに共通するもの、つまり固定された5ブロック構成に注目しましょう。

AITM が実際にどうしているか:本物のプロンプト構造

AITM はタスクプロンプトを自由形式では書きません。チャットやブレインストーミングから生成されるすべてのタスクプロンプトは、次の順序で並ぶ5つの固定ブロックで構成されます。各ブロックは、放置すれば不明確なタスクにつながる特定の問題を1つずつ解決します。

  1. Role — エージェントがどのような役割で、どの程度保守的な姿勢を取るべきかを、最大3文で記述します。このブロックがないと、エージェントはどれだけ「創造性」を発揮してよいか分からず、ガードレールのない創造性は、まさに望まない変更につながります。
  2. Context — タスクが存在する理由。ユーザーの報告、問題の経緯、その領域の担当者などです。「Context (background only — implement nothing from this block)」という一文で区切られています。この一文がないと、エージェントは要件に集中せず、背景説明の中の何かを誤って実装してしまうおそれがあるためです。
  3. Requirements — 要件ごとに番号付きブロックを1つ設け、それぞれに正確で検証可能な受け入れ基準を付けます。要件を番号付きブロックに分けることは、先に述べた問題、すなわち1ブロックに1つの具体的なこと、複数の要件を1文に混ぜないこと、を直接解決します。
  4. Scope — 3行構成です。In scope(変更してよいもの)、Out of scope(変更してはならないもの、およびその担当者)、Must not break(成り立ち続けなければならないコントラクト)。これは「AIに、してはいけないことも伝える」というルールを直接実装したものです。このブロックがないと、エージェントは変更の境界がどこにあるのか分かりません。
  5. Output and verification — タスクが作成するすべての新規ファイルと、この特定の変更が壊しうるものだけに絞った具体的な検証コマンド。こうすることで、タスクは「できたように見える」ではなく、「このコマンドが通った」という測定可能な基準で完了します。

%AUTH-012 のようなコントラクト参照に注目してください。これは特定のコントラクトへの参照で、AITM が認識する正確な構文で挿入され、振る舞いの完全な記述に自動的に展開されます。「自分でプロンプトを書く」ことと、正確な構造と引用を代わりに強制してくれるシステムとの違いは、まさにここにあります。

コントラクトには実際に何が含まれるのでしょうか。AITM におけるコントラクトは、コードコメントではなく、プロジェクトの SQLite データベース内のレコードです。短い ID(モジュールのプレフィックスと番号)、モジュール(その振る舞いがシステムのどの部分に属するか)、そして最も重要な behavior フィールドを持ちます。behavior は、仕組みを正確に複数文で記述したもので、多くの場合、ファイル内の特定の行や、その振る舞いが生まれたタスク番号への参照を含みます。さらに、コントラクトが関わるファイルの一覧、必要に応じて danger zone、関連する変数の一覧も持ちます。エージェントがプロンプト内の「Must not break」参照を見ると、AITM は素のコードの代わりにこの記述全体を挿入します。これにより、エージェントは破ってはならない振る舞いを正確に把握できます。

ではユニットテストはどうでしょうか。テストにも独自のコードがあります。AITM のテストの各テストブロックは、説明の冒頭に一意のタグを持ちます。これにより、コントラクトと同じ精度で特定のテストを指定できます。コントラクトはテストファイルへのパスとも対応付けられているので、コントラクト参照は振る舞いの記述だけでなく、それを守るテストへの直接のリンクも運ぶことになります。