プロンプトとコンテキスト — 窓は広がったがタダではない
一言でいうと
コンテキストウィンドウは、モデルが一度に見られるトークンの上限です。入れられるということと、入れたほうがよいということは、まったく別の話です。
なぜ必要なのか
コンテキスト長が伸びるにつれて、「全部入れればいいのではないか」という考えが自然になりました。実際には、3つのコストがついてきます。
お金。入力トークンにも料金がかかります。リクエストのたびに文書全体を入れると、そのコストがリクエスト数だけ掛け算されます。
レイテンシ。入力が長いほど、最初のトークンまでの時間が延びます。アテンションの計算が、長さに対して線形より速く増えるからです。
品質。これが最も直感に反します。関係のない内容を多く入れるほど、正解を見つける能力が落ちます。長いコンテキストでは、途中に置かれた情報が特に無視されやすいという観察が、何度も報告されています。そのため、正解が入ってさえいればよいという仮定は危険です。どこにどう置かれたかが重要です。
どう動くのか
プロンプト設計の実用的な原則をいくつか整理します。
役割と指示を前に、データを後ろに。モデルは指示を先に読み、その枠組みであとの内容を解釈します。データを先に流し込んで最後に指示を付けると、前半が方向なしに読まれます。
出力形式を例で示す。「JSONで答えなさい」という言葉より、実際のJSONの例1つのほうがはるかに強力です。ただし、例はトークンを食うので、個数と長さのバランスが必要です。
段階を分けさせる。複雑な推論で、中間の過程を書かせると、精度が上がります。ただし、すべての質問にこの方式を強制すると、トークンとレイテンシが増え、単純な質問ではかえって紛らわしい答えが出ることもあります。
コンテキストに優先順位を付ける。検索で取ってきた文書をそのままつなげる代わりに、スコアの高いものを前後の端に配置する戦略がよく使われます。中間が弱いという観察を逆に利用するのです。
システムプロンプトは契約書です。そこに書かれた制約が守られないなら、たいていは2つのうちのどちらかです。制約が曖昧であるか(例: 「簡潔に」)、後ろに来るデータがその制約と衝突しているかです。特に、ユーザーが入れたテキストの中に指示文のように見える文があると、モデルがそれに従おうとすることがあります。外部から入ってきた内容はデータであって指示ではないという境界を、プロンプトの構造とアプリケーションの両側で守る必要があります。
現場での姿
コンテキストの予算を明示的に管理するのがよいです。全体のウィンドウがいくつで、システムプロンプトが何トークンで、検索結果に何トークンを割り当て、出力に何トークンを残すかを、数値で決めておきます。そうしないと、ユーザーが長い文書を貼り付けた日に、出力が切れる事故が起きます。
そして、プロンプトをコードのようにバージョン管理する必要があります。1行を直したときに、どのケースがよくなり、どのケースが悪くなるのかは、評価なしでは判断できません。評価セットのないプロンプトの修正は、ただのギャンブルです。
長いコンテキストでは真ん中が消える
コンテキストウィンドウが128Kだからといって、その中のすべてを同じように使うわけではありません。複数の研究が同じ現象を報告しました。一番前と一番後ろはよく使い、真ん中は見逃します(lost in the middle)。
[시스템 프롬프트] [문서 1] [문서 2] … [문서 20] [질문]
↑ 잘 본다 ↑ 여기가 약하다 ↑ 잘 본다
そのため、検索で取ってきた文書を入れるときは、最も関連するものを一番前か一番後ろに置きます。関連度順に並べて前に置き、質問を最後にもう一度繰り返す方式が、実務でよく効きます。
文書を20個入れるより、リランキングで5個だけ選んで入れたほうが精度が高いことが多くあります。入れられるからといってすべて入れると、かえって悪くなります。
トークンを節約する構造
同じ結果を出しながらコストを減らす方法がいくつかあります。
- システムプロンプトを前に固定します。プレフィックスキャッシュが、その部分の計算を再利用します。リクエストごとに変わる値(ユーザー名、時刻)を前に入れると、キャッシュが壊れます。
- 例(few-shot)を減らします。例が5個のほうが2個より優れているかを、評価セットで確認します。たいてい2、3個で十分で、残りはトークンを使うだけです。
- 出力を制限します。
max_tokensを実際の必要より大きくすると、モデルが冗長になります。構造化された出力(JSONスキーマ)を求めると、短くなり、パースも簡単になります。 - 長い会話を要約してつなぎます。全履歴を毎回送る代わりに、要約と直近の数ターンを送ります。
プロンプトをコードのように扱う
prompts/
summarize/
v3.md ← 버전을 파일로
eval.jsonl ← 그 버전의 평가셋
baseline.json ← 그 버전의 점수
プロンプトをコードに文字列として埋め込んでおくと、誰がいつなぜ変えたのかが残りません。ファイルとして置いてバージョンを付ければ、元に戻すこととA/B比較ができるようになります。
そして、プロンプト変更のPRで、評価ハーネスが自動で動くようにします。これがないと、「感覚的によくなった」で変えることになり、リグレッションをユーザーが先に見つけます。
続くクイズで確認すること
コンテキストを増やすことが、なぜいつも得にならないのか、プロンプトの構造が結果をどう変えるのかを説明できるかを確認します。