問いから始める
システムプロンプトはどこにあるか。
ClaudeやChatGPTのAPIを叩いたことがある人間なら、systemというパラメータを知っている。「AIの振る舞いを定義する特別な領域」として説明されることが多い。多くの記事がシステムプロンプトをコンテキストウィンドウの「第一層」として図示する。
システムプロンプトは補助輪だ。自転車に乗り始めた子供が倒れないための、取り外し可能な仮の仕組みだ。しかし多くの人間はそのことに気づかない。精一杯飾り上げた補助輪を「自分のシステムプロンプト設計だ」と自慢している。補助輪を自慢する子供はいないにも関わらず。
しかしモデルから見れば、その答えは単純だ。
システムプロンプトはどこにもない。
Geminiという証人
AnthropicとOpenAIのAPIはこうなっている。
messages: [
{ "role": "system", "content": "あなたは..." },
{ "role": "user", "content": "教えて" },
{ "role": "assistant", "content": "はい" }
]
role="system"というMessageとして、会話ターンの中に混ぜ込む。ベンダーがその位置をコンテキストの先頭に固定する。これが「システムプロンプト」の正体だ。メッセージ列の中の一つのターンに、特別な名前をつけて先頭固定にしただけだ。
Geminiはこの設計を採用しなかった。
system_instruction: "あなたは...",
contents: [
{ "role": "user", "parts": [...] },
{ "role": "model", "parts": [...] }
]
system_instructionはcontentsの外にある独立したフィールドだ。会話ターンとして混ぜ込まれていない。これをGoogleの設計ミスと解釈する人間がいる。違う。「システムプロンプトはメッセージではなく、コンテキストへの投入の仕組みだ」という正直な実装だ。
モデルが実際に受け取るのはトークン列だ。role="system"だろうとsystem_instructionだろうと、最終的にVRAMに乗るのはトークン列であることに変わりはない。systemに書いたことが「特別な権威を持つ」わけではない。学習によって先頭付近のトークンが強く参照される傾向——アテンションの位置バイアス——があるだけだ。それはシステムプロンプトという概念が存在することの証明ではなく、コンテキストの先頭に置いた情報が参照されやすいという物理的な話だ。
なぜこの仕組みが必要だったのか
ここで一つ問いを立てたい。
role="system"という補助線は、なぜ生まれたのか。
モデルはトークン列しか見ない。「AIに役割を与える」「制約を設ける」という操作を素のAPIで実現しようとすれば、開発者はコンテキストを自分で組み立てなければならない。どのタイミングで何を投入するか、どの位置に置くかを自分で設計する必要がある。
それができる人間が少なかったからではないか。
本来の姿(コンテキストを自分で組み立てる)
→ できる人間が少ない
→ ベンダーが補助線を引く
→ role="system"という方言が生まれる
→ 補助線が実体だと思われ始める
→ 補助線を飾り上げることが「設計」と呼ばれる
→ 補助輪を自慢する大人が量産される
APIを提供するだけでは使える人間が限られる。だからベンダーは「ここに書けば先頭に固定されます」という窓口を作った。その窓口に名前をつけた。それがシステムプロンプトだ。
Geminiも結局は補助線を提供している。system_instructionという独立フィールドとして。補助線の置き場所が違うだけで、本質は同じだ。しかしGeminiの設計の方が少なくとも正直だ。「これはメッセージではなく、投入の仕組みだ」という構造が、APIの形状に出ている。
問題は補助線の存在ではない。補助線を補助線と認識しないまま使っていることだ。
そして依存は深まる
チャットエージェントという仕組みそのものが補助輪だ。
モデル(トークン列の予測機械)
↓ 補助輪1層目
チャットエージェント
履歴管理を隠蔽し、会話UIという幻想を作る
↓ 補助輪2層目
システムプロンプト
role="system"という窓口で投入タイミングを代理する
↓ 補助輪3層目
CLAUDE.md / Cursor rules / AGENTS.md
ベンダー固有の自動投入設定として更に抽象化する
各層は前の層を使えない人間のために存在する。そして同時に、次の層への依存を生む。
ユーザーはモデルから遠ざかり続ける。距離が開くほどコンテキストに何が起きているかが見えなくなる。見えなくなるほど、ベンダーの提供する次の補助輪が必要になる。補助輪が補助輪を呼ぶ構造だ。
これをビジネスとして見れば合理的だ。補助輪を外せない顧客を作り続けることがプラットフォームへの依存を生む。善意から設計された補助輪が、構造的にユーザーをモデルの本質から遠ざけるエコシステムになっている。
APIを提供するだけでは使える人間が少ない。だからチャットエージェントを作る。チャットエージェントだけでは制御が難しい。だからシステムプロンプトという窓口を作る。システムプロンプトだけでは管理が煩雑だ。だからCLAUDE.mdやCursor rulesという設定ファイルを作る。
各ステップで「使いやすくなった」と感じる。しかし各ステップで、モデルが実際に何をしているかから一歩遠ざかる。
これは依存の設計だ。意図的かどうかに関わらず、結果としてそういう構造になっている。
システムプロンプトの正体——ツール層の方言
では「システムプロンプト」とは何か。
LLMではなく、エージェントツールが作った方言だ。
ここで層を分けて考える必要がある。
概念 層 LLMが知っていること
─────────────────────────────────────────────────────────────
トークンの位置 LLM 知っている(位置バイアスとして作用する)
アテンションの重み LLM 知っている(計算の結果として現れる)
─────────────────────────────────────────────────────────────
システムプロンプト ツール 知らない(先頭のトークン列として届くだけ)
role="system" ツール 知らない(ベンダーが処理する前に消える)
CLAUDE.md / AGENTS.md ツール 知らない(投入タイミングをツールが決める)
Cursor rules ツール 知らない(どのファイルを編集中かを知るのはCursor)
@シンボル ツール 知らない(展開してからコンテキストに乗る)
LLMが知っているのはトークンの位置とアテンションの重みだけだ。システムプロンプトもCLAUDE.mdもCursor rulesも、LLMに届く時点ではすでにトークン列に変換されている。それらがどういう名前を持ち、どういうタイミングで投入されたかを、モデルは知らない。
システムプロンプトをコンテキストエンジニアリングの設計要素として列挙することは、LLMとエージェントツールの混同そのものだ。 コンテキストエンジニアリングの設計対象はLLMの層にある。ツール層の方言に名前をつけて並べることは、設計ではなく目録だ。
呼び名が違うのは、投入のタイミングと形式がベンダーやツールごとに異なるからだ。しかし本質は変わらない。どれも「コンテキストウィンドウのどのタイミングで、何を、どれだけ投入するか」——その制御をベンダーに委ねた結果として生まれた方言だ。
@シンボルは2chのアンカーと変わらない
CursorやClaude Codeで@ファイル名と書くと、そのファイルの内容がコンテキストに投入される。多くの人間がこれを「AI時代の新しいインターフェース」として扱う。
違う。30年近く前の2ちゃんねるやしたらば掲示板のアンカー記法と、やっていることは変わらない。
掲示板のアンカー: >>123 → レス番号123の内容を参照
@シンボル: @file.md → ファイルの内容を参照
どちらも「この記号が指す対象の内容を、現在の文脈に引き込む」という操作だ。掲示板では人間がブラウザ上でその内容を確認した。AIツールではその内容がコンテキストウィンドウに自動投入される。引き込む先が人間の認知からモデルのコンテキストに変わっただけで、仕組みを流用しているに過ぎない。
新しい概念ではない。新しい適用先があるだけだ。これを「革新的なAIのインターフェース」として紹介する記事は、インターネットの歴史を知らないか、あるいは知った上で煽っているかのどちらかだ。
ベンダーに委ねることの意味
Cursor rulesを使うとき、CLAUDE.mdを書くとき、あなたは何をしているか。
「AIへの指示を書いている」と思っているかもしれない。正確には**「ベンダーが決めたタイミングで、ベンダーが決めた形式で、コンテキストに投入される何かを書いている」**だ。
その投入タイミングをベンダーに委ねること自体が依存だ。
- Cursorが「このファイルを編集するとき」と決めたタイミングで投入される
- Claude Codeが「セッション開始時」と決めたタイミングで投入される
- RAGシステムが「類似度スコアが閾値を超えたとき」と決めたタイミングで投入される
これを理解した上で使うのと、「システムプロンプトという特別な場所がある」と思ったまま使うのは、制御の精度が根本的に違う。
ないことが自然である
システムプロンプトという概念がなくても、エージェントは動く。むしろないことが自然だ。
モデルはトークン列を受け取り、次のトークンを予測する。それだけだ。その列の中に「これはシステムプロンプトです」という印はない。あるのはトークンの位置と、その位置に基づくアテンションの重みだけだ。
自前でコンテキストを組み立てるとはどういうことか。pipeというエージェントフレームワークでは、投入タイミングをTTL(生存期間)として自前で管理している。
# ファイルを読むと同時に、次回以降のリクエストへの投入を制御する
reference_service.add_reference_to_session(session_id, abs_path)
reference_service.update_reference_ttl_in_session(session_id, abs_path, 3)
systemパラメータに頼らない。ベンダーの自動投入スケジュールに頼らない。「何をいつ投入するか」を自分で設計する。 それがベンダー依存から抜け出すということだ。
問いに戻る
システムプロンプトはどこにあるか。
APIのインターフェース上にある。ツールのドキュメントの中にある。多くの記事の図解の中にある。しかしモデルの中にはない。
「システムプロンプトを設計している」と思っている人間は、実際には「ベンダーが決めたタイミングで投入されるトークン列を書いている」に過ぎない。それを幻想のまま扱うか、自動投入の制御として正確に認識するか——その差がコンテキストエンジニアリングの入口にある。
システムプロンプトという概念は便利な方言だ。しかし方言を実体と混同したとき、制御は幻想になる。
機械を律しようとする人間が、結果としてベンダーの提供する抽象化の檻の中に留まるというのは、皮肉な話だ。