LLMコンテキストエンジニアリング実践:Prompt組み立て、Token予算とRAGコンテキスト設計
概要
- Context Engineering(コンテキストエンジニアリング)は2026年のLLMアプリケーション本番展開の中核スキル。Prompt Engineeringより範囲が広く、実務に直結する
- Token予算はハード制約:システムプロンプト、ツール定義、RAG検索、会話履歴を層別に配分しないと、ウィンドウ超過による切り捨てかコスト爆発のどちらかに陥る
- Prompt組み立ては文字列連結ではなく、優先度付きのコンテキストパイプライン。各層に明確な予算と退避戦略がある
- RAGコンテキスト注入の3大落とし穴:検索ノイズが重要情報を埋没、Chunk境界による意味切断、重複コンテンツによるToken浪費
- 本記事では原理からPython実装までの完全ソリューションを提供。Token予算計算機と本番向けContext Managerを含む
目次
- なぜPrompt Engineeringだけでは不十分か
- Context Engineeringの中核概念
- Token予算:コンテキストウィンドウの「財務計画」
- Prompt組み立てパイプライン設計
- RAGコンテキスト注入のベストプラクティス
- コンテキスト圧縮と退避戦略
- Python本番向けContext Manager実装
- 面接頻出ポイントと落とし穴ガイド
- まとめと関連記事
なぜPrompt Engineeringだけでは不十分か
2024年は「良いPromptの書き方」が話題でしたが、2026年に企業がLLMアプリを本番展開する際に本当のボトルネックとなるのは「一文の書き方」ではなく、コンテキスト全体の組織化です。
実際の本番障害事例
あるECカスタマーサポートAIがローンチ1週間でクレーム率が急増。調査の結果:
| 現象 | 根本原因 | 影響 |
|---|---|---|
| AIの回答が注文と無関係 | RAGが10件の過去チケットを取得し、現在の注文コンテキストを埋没 | 的外れな回答 |
| マルチターン後に応答が遅延 | 会話履歴が無制限に蓄積、Token超過で切り捨て発生 | ユーザー意図の喪失 |
| 月間API請求が予算の3倍 | システムプロンプトを毎回送信、Prompt Caching未活用 | コスト爆発 |
これらは**「Promptの文言を最適化」では解決できません**。すべてContext Engineeringの領域です。
Prompt Engineering vs Context Engineering
| 観点 | Prompt Engineering | Context Engineering |
|---|---|---|
| 焦点 | 単一プロンプトの文言と形式 | コンテキスト全体の組織、予算、ライフサイクル |
| 適用範囲 | 簡単なQ&A、単発タスク | マルチターン、RAG、Agent、ツール呼び出し |
| 中核スキル | ロール設定、Few-shot、CoT | Token予算、コンテキスト層別化、圧縮退避 |
| 本番複雑度 | 低 | 高(キャッシュ、検索、状態管理) |
| 2026年面接での重要度 | 基礎 | 高頻度アーキテクチャ問題 |
一言で言えば:**Prompt Engineeringは「何を言うか」、Context Engineeringは「モデルに何を見せるか」**を管理します。
Context Engineeringの中核概念
コンテキストの4層構造
LLMへの完全な入力を「情報サンドイッチ」と考えます:
┌─────────────────────────────────────────────┐
│ Layer 1: システム層 (System Context) │
│ ロール定義、行動制約、出力形式、セキュリティ │
├─────────────────────────────────────────────┤
│ Layer 2: ツール層 (Tool Context) │
│ Function Calling定義、MCPツール、Schema │
├─────────────────────────────────────────────┤
│ Layer 3: 知識層 (Knowledge Context) │
│ RAG検索結果、ナレッジグラフ、業務ルール │
├─────────────────────────────────────────────┤
│ Layer 4: 会話層 (Conversation Context) │
│ マルチターン履歴、現在の入力、推論ステップ │
└─────────────────────────────────────────────┘
各層に独立したToken予算があり、層間には優先順位があります。総Tokenがウィンドウ上限に近づくと、低優先度の層から退避します。
コンテキストのライフサイクル
- 組み立て(Assembly):優先度に基づき各層をマージ
- 検証(Validation):Token総量、形式準拠、機密情報フィルタ
- 送信(Dispatch):LLM API呼び出し、Prompt Cachingで重複Token削減
- 更新(Update):モデル応答を会話層に追加
- 圧縮(Compaction):会話が長くなったら要約または低価値履歴を退避
Token予算:コンテキストウィンドウの「財務計画」
なぜToken予算が必要か
128Kコンテキストウィンドウのモデルでも、本番環境では:
- システムプロンプト + ツール定義:通常 2K-8K Token
- RAGで10文書 × 500 Token = 5K Token
- 50ターンの会話履歴 × 200 Token = 10K Token
- 出力予約:4K Token
合計で既に 21K-27K Token。複雑なAgentや長文書RAGでは容易に50Kを超えます。予算管理なしでは危険です。
推奨予算配分表(128Kウィンドウ)
| 層 | 予算タイプ | Token上限 | 退避戦略 |
|---|---|---|---|
| システム層 | 固定 | 4K | 退避しない、Prompt Caching活用 |
| ツール層 | 固定 | 6K | 使用頻度に応じて動的ロード |
| 知識層 | 弾性 | 20K | 関連性スコアで切り捨て |
| 会話層 | 弾性 | 30K | スライディングウィンドウ + 要約圧縮 |
| 出力予約 | 固定 | 8K | — |
| 安全バッファ | 固定 | 10K | 見積もり誤差による超過防止 |
Tokenカウンターの3つの実装
方式1:tiktokenによる正確なカウント(推奨)
import tiktoken
def count_tokens(text: str, model: str = "gpt-4o") -> int:
encoding = tiktoken.encoding_for_model(model)
return len(encoding.encode(text))
def budget_check(layers: dict[str, str], max_tokens: int = 128000) -> dict:
usage = {name: count_tokens(content) for name, content in layers.items()}
total = sum(usage.values())
return {
"usage": usage,
"total": total,
"remaining": max_tokens - total,
"overflow": total > max_tokens,
}
方式2:文字数推定(高速プリチェック)
中国語は約1.5-2文字/Token、英語は約4文字/Token。組み立て前の高速チェック用。
方式3:APIレスポンスの実際の使用量
response.usageから実消費を取得し、監視と動的予算調整に使用。
Prompt組み立てパイプライン設計
パイプラインアーキテクチャ
ユーザー入力 ──→ [意図認識] ──→ [知識検索] ──→ [コンテキスト組み立て] ──→ [予算検証] ──→ LLM API
│ │ │ │
▼ ▼ ▼ ▼
ロードするツール決定 Top-K文書フィルタ 優先度でマージ 超過時は圧縮
退避優先順位ルール
Token予算が不足した場合、以下の順序で切り捨て:
- 会話層の最も古いメッセージ(直近Nターン + 最初のシステムやり取りを保持)
- 知識層の低スコア文書(Rerankスコアの低い順に削除)
- 未使用のツール定義(このターンで呼び出す可能性のあるツールのみ保持)
- 最後にシステム層(固定内容としてPrompt Cachingを活用)
本番向けアセンブラー実装
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class ContextLayer:
name: str
content: str
priority: int
max_tokens: int
cacheable: bool = False
@dataclass
class ContextBudget:
total_limit: int = 128000
output_reserve: int = 8000
safety_buffer: int = 10000
@property
def available(self) -> int:
return self.total_limit - self.output_reserve - self.safety_buffer
class ContextAssembler:
def __init__(self, budget: ContextBudget):
self.budget = budget
self.layers: list[ContextLayer] = []
def add_layer(self, layer: ContextLayer) -> None:
self.layers.append(layer)
self.layers.sort(key=lambda l: l.priority)
def assemble(self) -> str:
available = self.budget.available
parts: list[str] = []
for layer in self.layers:
tokens = count_tokens(layer.content)
if tokens <= layer.max_tokens and tokens <= available:
parts.append(layer.content)
available -= tokens
elif tokens > layer.max_tokens:
truncated = self._truncate(layer.content, layer.max_tokens)
parts.append(truncated)
available -= layer.max_tokens
return "\n\n---\n\n".join(parts)
def _truncate(self, text: str, max_tokens: int) -> str:
encoding = tiktoken.encoding_for_model("gpt-4o")
tokens = encoding.encode(text)
if len(tokens) <= max_tokens:
return text
return encoding.decode(tokens[:max_tokens])
RAGコンテキスト注入のベストプラクティス
RAGはContext Engineeringで最も落とし穴が多い部分です。
文書注入の3つの形式
形式1:XMLタグでラップ(推奨)
<retrieved_documents>
<document source="product_manual_v3.pdf" score="0.92">
製品保証期間は購入日から12ヶ月、人的損傷は保証対象外...
</document>
<document source="faq_returns.md" score="0.87">
7日間の無条件返品は商品の完好性が必要、送料は購入者負担...
</document>
</retrieved_documents>
形式2:番号付き引用(トレーサビリティ向け)
[1] 製品保証期間は購入日から12ヶ月...(出典:product_manual_v3.pdf)
[2] 7日間の無条件返品...(出典:faq_returns.md)
上記の参考資料に基づいて回答し、引用番号を明記してください。
RAGコンテキストの5つの鉄則
| ルール | 説明 | 違反時の影響 |
|---|---|---|
| 関連性閾値フィルタ | 0.7未満の文書は注入しない | ノイズ増加、幻覚増加 |
| 重複排除 | 同一文書の異なるChunkをマージ | Token浪費 |
| メタデータ保持 | 出典、時間、バージョンを明記 | トレーサビリティ欠如 |
| 位置優先 | 最も関連性の高い文書を先頭に | Lost in the Middle |
| 動的Top-K | 残り予算に応じてK値を調整 | 予算超過時のハード切り捨て |
Lost in the Middle問題
スタンフォード大学の研究(2023年)により、関連情報がコンテキスト中央にあるとモデルのリコール精度が大幅に低下することが判明。本番での対策:
- 最も関連性の高い2文書を先頭に配置
- 次に関連する文書を末尾に配置
- 中央にはシステムプロンプトやツール定義などの固定コンテンツ
コンテキスト圧縮と退避戦略
戦略1:スライディングウィンドウ
直近Nターンを保持。N=10-20を推奨。
戦略2:要約圧縮
SUMMARIZE_PROMPT = """以下の会話履歴を要約に圧縮してください。以下を保持:
1. ユーザーの核心的な要求と制約条件
2. 確認済みの重要事実(注文番号、製品型番など)
3. 未解決の問題
会話履歴:
{history}
要約(200字以内):"""
戦略3:構造化状態抽出
Agentシナリオでは、完全な会話ではなく構造化されたセッション状態を維持:
{
"user_intent": "注文物流の確認",
"entities": {"order_id": "ORD-20260703-8842"},
"resolved": ["注文の存在を確認", "配送業者は順豊"],
"pending": ["配達予定日"]
}
TokenコストがO(ターン数)からO(1)に削減されます。
Python本番向けContext Manager実装
class ProductionContextManager:
def __init__(
self,
model: str = "gpt-4o",
budget: ContextBudget = ContextBudget(),
):
self.model = model
self.budget = budget
self.assembler = ContextAssembler(budget)
self.conversation_history: list[dict] = []
def build_context(
self,
user_input: str,
system_prompt: str,
retrieved_docs: Optional[list[dict]] = None,
tools: Optional[list[dict]] = None,
) -> str:
self.assembler.layers.clear()
self.assembler.add_layer(ContextLayer(
name="system", content=system_prompt,
priority=0, max_tokens=4000, cacheable=True,
))
if tools:
tools_text = self._format_tools(tools)
self.assembler.add_layer(ContextLayer(
name="tools", content=tools_text,
priority=1, max_tokens=6000,
))
if retrieved_docs:
docs_text = self._format_rag_docs(retrieved_docs)
self.assembler.add_layer(ContextLayer(
name="knowledge", content=docs_text,
priority=2, max_tokens=20000,
))
history_text = self._format_history()
self.assembler.add_layer(ContextLayer(
name="conversation", content=history_text,
priority=3, max_tokens=30000,
))
user_layer = f"<user_query>\n{user_input}\n</user_query>"
self.assembler.add_layer(ContextLayer(
name="current_input", content=user_layer,
priority=4, max_tokens=4000,
))
return self.assembler.assemble()
def _format_rag_docs(self, docs: list[dict]) -> str:
filtered = [d for d in docs if d.get("score", 0) >= 0.7]
filtered.sort(key=lambda d: d["score"], reverse=True)
parts = ["<retrieved_documents>"]
for doc in filtered:
parts.append(
f' <document source="{doc["source"]}" score="{doc["score"]:.2f}">\n'
f' {doc["content"]}\n'
f' </document>'
)
parts.append("</retrieved_documents>")
return "\n".join(parts)
def update_history(self, role: str, content: str) -> None:
self.conversation_history.append({"role": role, "content": content})
if len(self.conversation_history) > 40:
self._compact_history()
def _compact_history(self) -> None:
old = self.conversation_history[:20]
self.conversation_history = self.conversation_history[20:]
summary = f"[Earlier conversation summary: {len(old)} messages condensed]"
self.conversation_history.insert(0, {"role": "system", "content": summary})
面接頻出ポイントと落とし穴ガイド
よく聞かれる5つの質問
Q1:Context EngineeringとPrompt Engineeringの違いは?
Context Engineeringは入力コンテキスト全体の組織、予算、ライフサイクル管理を扱うシステムレベルの設計。Prompt Engineeringは単一プロンプトの文言最適化というテクニックレベルの最適化。本番では前者がシステムの稼働可否とコストを決定し、後者が回答品質を決定する。
Q2:128Kコンテキストなら圧縮不要?
いいえ。128Kは理論上限。実際にはコスト(Token課金)、レイテンシ(コンテキストが長いほど推論が遅い)、注意力減衰(Lost in the Middle)、マルチユーザー並行時のリソース競合を考慮する必要がある。本番では必ず予算管理が必要。
Q3:RAGで10文書すべて注入すべき?
いいえ。関連性フィルタ、重複排除、Rerank、予算に応じた動的Top-Kが必須。ノイズ文書は幻覚率を大幅に増加させる。
Q4:Prompt CachingとContext Engineeringの関係は?
Prompt Cachingはコスト最適化手段。Context Engineeringが何をキャッシュすべきかを決定する。システムプロンプトやツール定義はcacheable、会話層とRAG結果はキャッシュ不適。
Q5:マルチAgentシステムでのコンテキスト管理は?
各Agentが独立したContext Managerを維持。Agent間は完全な会話履歴ではなく構造化状態を渡す。Agent Aの完全なコンテキストをAgent Bに渡すとTokenが指数的に膨張する。
本番落とし穴チェックリスト
| 落とし穴 | 症状 | 対策 |
|---|---|---|
| 会話履歴の無制限蓄積 | マルチターン後に遅延、超過切り捨て | スライディングウィンドウ + 要約 |
| RAGノイズ注入 | 回答がユーザーの質問から逸脱 | 関連性閾値 + Rerank |
| システムプロンプトの重複課金 | APIコスト高騰 | Prompt Caching |
| ツール定義の全量ロード | 8K+ Tokenが未使用ツールに浪費 | 意図に応じた動的ロード |
| Token監視の欠如 | コスト超過に後から気づく | 呼び出しごとにusageメトリクス記録 |
マルチモデルコンテキストの差別化管理
TokenCounter インターフェースを抽象化。GPT の tiktoken で Claude の使用量を推定しない。
Prompt Caching 本番実践
システムプロンプトとツール定義をキャッシュ可能としてマーク。90% キャッシュヒット率で API コスト約 80% 削減。
企業カスタマーサービス改造事例
平均入力 Token 45K→12K、月額 API コスト ¥28万→¥9万、誤回答率 23%→6%。
Context Engineering vs ファインチューニング
80% の「モデル性能不足」はコンテキスト組織の問題。まず Context Engineering を最適化。
2026年トレンド
コンテキスト認識 Reranker、適応型 Token 予算、クロスセッション記憶、マルチモーダル管理、コンテキストセキュリティ。
まとめと関連記事
Context Engineeringは2026年のLLMアプリケーションがデモから本番へ移行する分岐点。中核は3つ:層別コンテキスト管理、厳格なToken予算、インテリジェントな圧縮退避。
設計要点の振り返り:
- コンテキストは4層:システム、ツール、知識、会話 — 各層に独立予算
- Token予算はハード制約、128Kウィンドウ≠無制限使用
- RAG注入はフィルタ、重複排除、ソート必須、Lost in the Middle回避
- 会話圧縮3戦略:スライディングウィンドウ、要約圧縮、構造化状態抽出
- Prompt Caching + Context Pipelineがコスト最適化のコンボ
関連記事:
参考資料:
ブラウザローカルツールを無料で試す →