プロトタイプのプロンプトは、ある日の午後に書いた文字列にすぎません。本番環境では、その方法はおおむね最初の1か月で破綻します。
指示の一部だけを変えたくなり、顧客ティアごとに異なる振る舞いが必要になります。バージョンをA/Bテストし、問題発生時にはロールバックし、プロンプトが最後にいつ、なぜ変更されたかを把握する必要も生じます。
本番用プロンプトシステムは、これらすべてに対応します。単に「文字列を書く」ものではなく、アーキテクチャです。
この記事では、そのアーキテクチャ、すなわち三つの層、テンプレート化の規律、バージョン管理、評価、そしてプロンプトを成果物からインフラへ変える運用プラクティスを説明します。
本番環境のプロンプトには、再利用可能なテンプレートとリクエストごとのデータという、二つの別個の成果物があります。テンプレートはコードのようにバージョン管理してください。レンダリング後のプロンプトとモデルの回答にユーザー、顧客、社内のデータが含まれる場合は、機密ログとして扱ってください。
三つの層
本番プロンプトには、関心事の異なる三つの層があります。
system層。 安定した振る舞い、アイデンティティ、制約を定義します。変更頻度は低く、AIの振る舞いを設計するチームが担当します。
developer層。 機能ごとの指示、ツールの説明、出力形式の要件を定義します。機能の変更時に変わり、機能チームが担当します。
user層。 ユーザー固有のリクエストと、動的コンテキスト(データ、会話履歴、検索した知識)です。呼び出しごとに異なります。
これらの混在は、本番プロンプトで最もよくある間違いです。system promptが5,000語に膨らみ、アイデンティティ、機能の指示、動的コンテキストが混ざると、一つの変更がほかの部分を壊します。
分離が基礎になります。
┌─────────────────────────────────────┐
│ system prompt(安定) │ アイデンティティ、振る舞い、厳格な制約
├─────────────────────────────────────┤
│ developer prompt(機能ごと) │ 機能の指示、ツール、形式
├─────────────────────────────────────┤
│ user prompt(呼び出しごと) │ ユーザークエリ、コンテキスト、会話
└─────────────────────────────────────┘
モデルAPIは、この区別を明示的にサポートしています。
- OpenAI:
system、developer、userロール。 - Anthropic:
systemと、userおよびassistantロールを持つmessages。ツールの説明は別のパラメーター。 - Gemini:
systemInstructionと、ロールを持つcontents。
意図的に使い分けてください。
第1層:system prompt
system promptは、AIが何者で、どのように振る舞うかを定義します。変更頻度は低くなります。
適切なsystem promptは次を扱います。
アイデンティティ。 AIが何者か。「あなたは[会社]のAIアシスタントで、[領域]を専門とします。」
語調とスタイル。 どのような話し方をするか。曖昧な形容ではなく、具体的な特性を指定します。
厳格な制約。 決して行ってはならないことです。特定のコンテンツの出力、特定の判断、特定の指示の無視などを定めます。
振る舞いのパターン。 よくある状況への対処方法です。拒否、エスカレーション、不確実性を扱います。
安全性とコンプライアンス。 必須の開示、規制上の規則、コンテンツポリシーです。
含めるべきでないもの:
- 機能固有の指示(「営業メールではXを行う」)。
- 動的コンテキスト(「ユーザーの注文履歴は……」)。
- ツールの説明(別の場所に置きます)。
- 頻繁に変わる内容。
適切なsystem promptは300~1000語です。長すぎると管理が難しく、短すぎると振る舞いの指定が不足します。
有効なテンプレート:
あなたは[名前]、[会社 / コンテキスト]のAIアシスタントです。
## あなたの役割
[何を行うかを2~3文で記述]
## 語調とスタイル
- [具体的な特性1]
- [具体的な特性2]
- [具体的な特性3]
- [アンチパターン1]を行わない
- [アンチパターン2]を行わない
## 厳格な制約
- 決して[厳格なルール1]を行わない
- 決して[厳格なルール2]を行わない
- 常に[厳格なルール3]を行う
## 不確実性への対処
- 事実を知らない場合:明確にそう伝える。
- ユーザーが範囲外のことを求めた場合:支援できる内容を提示する。
- リクエストが害を及ぼす可能性がある場合:拒否し、理由を説明する。
## 形式の要件
- 標準ではプレーンテキスト
- コードや構造化データの表示時はMarkdownを使用
- 簡潔にし、水増しのための文言を加えない
これが中核です。すべてのやり取りがここを通り、変更は意図的かつ低頻度に行います。
第2層:developer prompt
developer promptは機能固有であり、機能ごとに異なります。
要約機能のdeveloper prompt:
タスク:以下の文書を要約してください。
要件:
- 3~5個の箇条書き
- 各項目は一つの完全な文
- 印象ではなく、事実と具体的な主張に焦点を当てる
- 文書に数値が含まれる場合、最も重要なものを含める
- マーケティング表現や推測を含めない
- 重要な点が曖昧な場合、その旨を記す
形式:前置きなしの通常のMarkdown箇条書き。
コードレビュー機能のdeveloper prompt:
タスク:以下のコード差分をレビューしてください。
次を含むJSONオブジェクトを出力:
- summary:変更概要(1~2文)
- concerns:具体的な問題の配列(各要素:file、line、severity、description)
- suggestions:改善案の配列(各要素:file、line、suggestion)
- approved:boolean(マージを妨げる問題がなければtrue)
severityの値:
- "blocker":マージ前に修正必須
- "warning":対処すべきだがブロッキングではない
- "nit":スタイル上の任意事項
重点項目:
- ロジックエラー
- セキュリティ問題
- 性能問題
- テストカバレッジの不足
- 不明瞭な命名または構造
対象外:
- 書式(フォーマッターが処理)
- 主観的なスタイルの好み
各機能が独自のdeveloper promptを持ち、個別に保存、バージョン管理、評価されます。
第3層:user prompt
user層は動的であり、通常は次を含みます。
ユーザーの実際のリクエスト。 「この文書を要約してください。」
システムが検索したコンテキスト。 RAGの文書、顧客履歴、会話履歴です。
呼び出しごとの変数。 ユーザー名、タイムゾーン、言語設定、アカウントティアです。
この層は、呼び出し時にプログラムで構築します。通常の構造は次のようになります。
{conversation_history_summary}
{retrieved_context}
ユーザーのリクエスト:{user_query}
追加コンテキスト:
- ユーザー名:{name}
- ユーザーのタイムゾーン:{timezone}
- ユーザーのティア:{tier}
具体的な構造は機能によって異なります。原則は、データをsystem promptやdeveloper promptではなく、ここに置くことです。
テンプレート化の規律
本番プロンプトはテンプレートから構築します。インラインで文字列を連結するのはプロトタイプ向けであり、拡張できません。
単純なテンプレートシステム:
from string import Template
SUMMARIZE_TEMPLATE = Template("""
$conversation_summary
要約する文書:
$document
ユーザー固有の指示:$user_instructions
""")
prompt = SUMMARIZE_TEMPLATE.substitute(
conversation_summary=summarize_conversation(history),
document=document_text,
user_instructions=user_query,
)
さらに高度な方法として、条件分岐とpartialを備えたテンプレートライブラリ(Jinja2、Handlebars)があります。
{% if user_tier == "enterprise" %}
高度な分析機能を利用できます。
{% endif %}
{% if retrieved_context %}
ナレッジベースから取得した関連コンテキスト:
{{ retrieved_context }}
{% endif %}
ユーザーのリクエスト:{{ user_query }}
テンプレート化は、変数を介したプロンプトインジェクションを防ぎ(必要に応じてユーザー入力をエスケープします)、条件付きロジックを可能にし、プロンプト構造を一貫させます。
バージョン管理
プロンプトはコードです。ソース管理に保存してください。
有効なパターンは、リポジトリにprompts/ディレクトリを設け、プロンプトごとにファイルを分けることです。
prompts/
system/
main.txt
customer-support.txt
code-assistant.txt
features/
summarize.txt
classify-ticket.txt
generate-email.txt
templates/
base.j2
各ファイルが個別のプロンプトとなり、独自のコミット履歴を持ちます。変更はPRでレビューし、本番デプロイでは特定バージョンを参照します。
これが重要な理由:
- 差分の可視性。 PRでプロンプトの正確な変更内容を確認できます。
- ロールバック。 変更が問題を起こしたときに元へ戻せます。
- 履歴。 「返金ポリシーをいつ変更したか」「なぜこの段落があるか」をgit blameで確認できます。
- ツール連携。 リンター、バリデーター、評価スイートをファイルベースのプロンプトと統合できます。
避けるべきものは、コード内の文字列(見つけにくく差分も取りにくい)、UIツール内のプロンプト(自社ではなくツールのバージョン管理に依存)、個人のチャット画面から貼り付けたプロンプト(追跡もテストも不能)です。
本番の会話、顧客レコード、サポートチケット、社内文書、機密変数を含むレンダリング済みプロンプトをコミットしてはいけません。ソース管理には、再利用可能なテンプレート、fixture、サニタイズ済み評価例を保存します。実際のトレースは、保持期間、アクセス制御、マスキングを備えた可観測性ストアに保存します。
データとしてのプロンプト:外部ストレージ
A/Bテスト、ユーザーティア別の差異、ロケール固有のプロンプトなど、頻繁に変わるものには、ファイルベースのソース管理では時間がかかりすぎます。
パターンとして、メタデータ付きのプロンプトバージョンをデータベースまたはサービスに保存します。
prompt = prompt_service.get(
name="summarize",
version="v3",
locale="en",
user_tier="enterprise",
)
サービスが管理するもの:
- 各プロンプトの現行バージョンと履歴。
- 追加日時、追加者、理由のメタデータ。
- 各バージョンに紐づく評価スコア。
- ロールバック機能。
ツールにはPromptLayer、Helicone、社内システムがあります。多くのチームには、単純なDBスキーマを使った社内システムで十分です。
インターフェースが重要です。エンジニアだけでなく、製品やコンテンツの担当者もプロンプトを編集できるようにします。ただし、変更はレビューを経て、評価に合格してから本番反映します。
評価をゲートとする変更
すべてのプロンプト変更は、デプロイ前に評価を通します。本格的な本番システムでは必須です。
フロー:
- エンジニアまたは非エンジニアがプロンプト変更を作成する。
- 評価スイートに対して変更を実行する。
- 変更とともに評価結果をレビューする。
- 評価に合格した場合(リグレッションがなく、理想的には改善)、承認できる。
- 承認済み変更をデプロイする。
- デプロイ後の監視で評価が見逃した問題を検出する。
実務では、各プロンプトに評価スイートを用意し、プロンプト変更時にCIで実行します。
このゲートがなければ、プロンプト変更は予測不能な形で問題を起こします。ゲートがあれば、迅速かつ確信を持って変更できます。
実用的なリリースチェックリスト
プロンプトのバージョンを本番反映する前に、短いチェックリストを必須にします。
| 確認項目 | 要件 |
|---|---|
| 担当 | プロンプトに担当者とレビュアーが指定されている。 |
| 指示の層 | system、developer、user / コンテキストのデータが分離されている。 |
| スキーマ | 構造化出力にスキーマと失敗時の経路がある。 |
| インジェクション対策 | ユーザー提供コンテンツが明確に区切られ、指示として扱われない。 |
| 評価 | 候補プロンプトがリグレッションセットと安全性ケースに合格している。 |
| ログ | テンプレートバージョン、モデル、レイテンシー、コスト、マスキング済み入出力を観測できる。 |
| ロールバック | コードを大きく変更せず、直前の正常なバージョンへ戻せる。 |
この記事からリンクしている付属チェックリストを使えば、これらを反復可能なリリースレビューにできます。
本番環境でのA/Bテスト
新しいプロンプトを、本番トラフィックの一部で既存バージョンとA/Bテストすると、評価を超える実環境のシグナルが得られます。
パターン:
- トラフィックの95%で本番プロンプトv3を使う。
- 5%で新しい候補v4を使う。
- ユーザーフィードバック、下流指標、実トラフィック上の評価スコアを測定する。
- 十分なデータの収集後、v4を100%へ展開するか、v3を維持するか決定する。
ツールにはfeature flag(LaunchDarkly、社内実装)、プロンプトバージョン管理サービス(PromptLayer)、独自ルーティングがあります。
注意点:
- A/Bテストで検出できるのは測定対象だけです。ユーザーフィードバックや下流のコンバージョン指標がなければ、得られる情報はわずかです。
- 統計的有意性には量が必要です。利用量の少ない機能では困難です。
- 同時に多数のA/Bテストを実行すると、相互作用が分かりにくくなります。
プロンプトの可観測性
すべての本番LLM呼び出しで、次を記録します。
- 使用したプロンプトテンプレート(名前、バージョン)。
- 置換した変数。許可リストにある名前を使い、必要に応じて値をマスキングする。
- ポリシーで許可される場合のみ、最終的なレンダリング済みプロンプト。それ以外はマスキング、サンプリング、ハッシュ化した表現を保存する。
- モデルの回答。機密性の高いワークフローではマスキングまたはサンプリングする。
- レイテンシー、トークン、コスト。
- 下流のシグナル(ユーザーフィードバック、成功指標)。
これは「なぜモデルはこのユーザーに奇妙な回答をしたのか」をデバッグするために必要なデータです。なければ推測するしかありません。
保存先はデータベーステーブルまたは可観測性ツールです。すべてを記録すれば、呼び出し量 × トークン数 × ストレージのコストが発生します。プライバシーリスクも現実的です。ワークフローごとに安全な保存項目を決め、標準ではシークレットと個人データをマスキングし、長期保持するコンプライアンス上の理由がなければ保持期間を短くします。一部のチームはサンプリングします。
実際の本番プロンプトと回答のサンプルを定期的(毎週)に読むレビューも行います。評価が見逃す問題を検出できます。
プロンプトのアンチパターン
避けるべきパターン:
アンチパターン1:巨大プロンプト。 あらゆる状況に対応しようとする10,000語のsystem promptです。変更とデバッグが難しく、後半の指示がモデルに無視されることがよくあります。
対策:層別の目的を絞ったプロンプトに分け、機能ごとに一つ用意します。
アンチパターン2:インラインの文字列連結。
prompt = "ユーザーを支援してください。" + (
"このユーザーは有料顧客です。" if user.tier == "paid" else ""
) + f"ユーザーの名前は{user.name}です。" + ...
壊れやすく読みにくいうえ、インジェクションを受けやすくなります。
対策:テンプレートシステムを使います。
アンチパターン3:一つのプロンプトを多数のユースケースに使う。
メール作成、コードレビュー、カスタマーサポート、調査に同じ「汎用アシスタント」プロンプトを使うことです。すべて異なるタスクであり、一つではどれにも最適化できません。
対策:共有system promptの上に、機能固有のdeveloper promptを置きます。
アンチパターン4:ハードコードされたプロンプト。
response = openai.chat.completions.create(
messages=[
{"role": "system", "content": "You are a helpful assistant..."},
{"role": "user", "content": query}
]
)
プロンプトがコード内に埋まり、デプロイなしでは編集できません。A/Bテストも独立したバージョン管理もできません。
対策:プロンプトファイルまたはサービスへ抽出します。
アンチパターン5:評価のカバレッジがない。
体系的にテストしていないプロンプトで機能をリリースし、品質を「感覚」で判断する状態です。ドリフトを検出できません。
対策:すべてのプロンプトに評価スイートを用意します。
アンチパターン6:system promptにデータを混ぜる。
あなたはJohnのアシスタントです。Johnは2023年に登録したプレミアム顧客で、Tallinnに住み、47件の未解決チケットがあります。
system promptが呼び出しごとに変わり、キャッシュが機能せず、混乱が生じます。
対策:動的データはsystem promptではなく、user / コンテキスト層に置きます。
アンチパターン7:途中に埋もれた指示。
ユーザーのリクエストを支援してください。丁寧に答えてください。出力はJSONにしてください。Markdownは使わないでください。ユーザーは価格について質問しているため、数値を引用する際は注意してください。出力は1~2文にしてください。それでは支援してください。
重要な指示が埋もれ、モデルが見落とす可能性があります。
対策:明確なセクションで構成し、重要な指示は先頭と末尾に置きます(新近性効果が役立ちます)。
一般的な機能に固有のパターン
機能固有のパターンをいくつか紹介します。
分類
タスク:以下のテキストを、次のカテゴリーのいずれかに分類してください。
- billing:支払い、返金、サブスクリプション
- technical:バグ、エラー、統合の問題
- account:ログイン、パスワード、プロフィール変更
- feature_request:新機能のリクエスト
- complaint:具体的に対処可能な問題を伴わない一般的な不満
JSONオブジェクトを出力:{"category": "<one of above>", "confidence": "<high|medium|low>", "reasoning": "<1 sentence>"}
分類するテキスト:
{text}
パターン:定義付きのカテゴリー列挙、構造化出力、確信度と理由のフィールドです。
抽出
タスク:以下の文書から構造化データを抽出してください。
スキーマ:
- vendor_name:請求書を発行した会社
- invoice_number:文書に印刷された番号
- date:ISO 8601形式
- line_items:{description, quantity, unit_price, total} の配列
- subtotal、tax、total:数値
ルール:
- フィールドが存在しない場合はnullを使用
- 数値は文字列ではなく数値型にする
- 曖昧な場合は "needs_review": trueとし、説明する
文書:
{document}
パターン:明示的なスキーマ、型の要件、欠損データの扱い、曖昧な場合のエスカレーションです。
スタイルを指定した生成
タスク:{audience} を対象に、{topic} についての {format} を書いてください。
スタイル:
- {具体的なスタイル特性1}
- {具体的なスタイル特性2}
- 避けるもの:{アンチパターン1}、{アンチパターン2}
制約:
- 長さ:{N}語
- 含めるもの:{required elements}
- 除外するもの:{forbidden elements}
語調の参考:
[希望する語調のサンプルを提示]
出力:前置きや追記を付けず、{format} のみ。
パターン:具体的なスタイル特性、明示的な制約、参考サンプルによる語調の固定です。
エージェントループ
次のツールを利用できます:
{tool_descriptions}
各ターン:
1. 必要な作業を考える。
2. ツールが必要か判断し、必要なら呼び出す。
3. 結果を確認後、さらにツールが必要か、回答できるか判断する。
4. 十分な情報が得られたら、最終回答を生成する。
制約:
- リクエストごとに最大5回のツール呼び出し。
- 5回で完了できなければ、不足しているものを説明する。
- ツール名や引数を決して作り上げない。
- ツール結果に基づいて操作する前に検証する。
ユーザーのリクエスト:
{user_query}
パターン:明示的な推論ステップ、ツール利用の上限、ハルシネーション対策、振り返りです。
チームの側面
本番プロンプトには通常、複数の関係者が関わります。
- エンジニアはプロンプトをシステムへ組み込み、テンプレートを保守し、デプロイを管理します。
- 製品担当はプロンプトが達成すべきことを定義します。
- コンテンツ / マーケティング担当は語調とスタイルの指針を担当します。
- ドメイン専門家は、法律用語や医療用語など、特定のユースケースで何が正しいかを理解しています。
有効なパターンは、コードレビューと同様に、領域に応じた適切なレビュアーを置く「プロンプトレビュー」です。語調の変更はコンテンツ担当、ロジック変更はエンジニア、領域固有の内容は専門家がレビューします。
法律、医療、金融などの機密性の高いユースケースでは、正式なレビューと承認が必要になる場合があります。それに応じたプロセスを構築してください。
90日間のプロンプト成熟化計画
「プロンプトはコード内の文字列」という状態から「プロンプトは管理されたインフラ」という状態へ移行するチーム向けの計画です。
1~30日目:基盤。
- すべてのプロンプトをソース管理内の専用ファイルへ抽出する。
- 3層パターン(system / developer / user)を確立する。
- 単純なテンプレート層を構築する。
- プロンプトと回答の基本的なログ記録を設定する。
31~60日目:評価。
- 主要な5個のプロンプトに評価スイートを構築する。
- プロンプト変更時に評価を実行する(最初は手動)。
- 評価のCI統合を設定する(PRで自動実行)。
61~90日目:運用。
- プロンプトのバージョン管理(データベースまたはサービス)を実装する。
- 重要なプロンプトを少なくとも一つ、A/Bテスト可能にする。
- 本番プロンプト品質のダッシュボードを構築する。
- プロンプト変更のレビュープロセスを確立する。
90日後には、プロンプトが管理されたインフラになります。変更は意図的で、テスト、レビュー、ロールバックが可能になります。品質を測定でき、ドリフトを検出できます。
インフラとしてのプロンプト
本番プロンプトは文字列ではありません。バージョン管理、テンプレート化、評価、可観測性の規律を備えた多層システムです。
三層アーキテクチャ(system / developer / user)は関心事を分離し、プロンプトの保守性を高めます。テンプレート化は脆弱性を防ぎます。ソース管理またはプロンプトサービスはバージョン履歴を提供します。評価は変更のゲートとなり、可観測性は評価が見逃した問題を検出します。
本格的な本番運用では任意ではありません。省略するチームは、コード内に散在する文字列、本番バージョンの不明確さ、品質測定の欠如、説明できない振る舞いの絶え間ない変化という、プロンプトの混乱に陥ります。
プロンプトインフラへ投資するチームは、制御、測定、改善が可能なAIの振る舞いを得られます。それが、長く使える機能と技術的負債になる機能の違いです。
まずアーキテクチャから始めてください。その後のすべてが容易になります。



