はじめに

「さっき言ったこと、覚えてる?」 「申し訳ありませんが、以前の会話の内容を…」

この経験、ありませんか?

AIが「忘れる」のは、バグでも性能の問題でもない。 コンテキストウィンドウという仕組みを理解していないだけだ。

この記事では、コンテキストウィンドウの本質と、それを踏まえた効率的なAI開発の方法を解説する。


コンテキストウィンドウとは

LLMの「記憶」の正体

LLMには記憶がない。 毎回、入力されたテキスト全体を見て回答を生成している。

    graph TB
    subgraph Context["コンテキストウィンドウ<br/>← この枠に収まる範囲しか「見えない」 →"]
        A["システムプロンプト"]
        B["過去の会話履歴"]
        C["今回の入力"]
    end

    A --> B
    B --> C

    style Context fill:#f0f0f0,stroke:#333,stroke-width:3px
    style A fill:#e1f5ff
    style B fill:#fff3e0
    style C fill:#e8f5e9
  

つまり、「覚えている」のではなく、「毎回全部読んでいる」。 そして、枠を超えた部分は物理的に見えない

2025年現在の主要モデル

モデル コンテキスト長 概算文字数
Claude Sonnet 4 200K tokens 約15万文字
Claude Opus 4 200K tokens 約15万文字
GPT-4o 128K tokens 約10万文字
GPT-4 Turbo 128K tokens 約10万文字
Gemini 1.5 Pro 2M tokens 約150万文字
Gemini 2.0 Flash 1M tokens 約75万文字

注意: 大きければいいわけではない。後述する「Lost in the Middle」問題がある。


なぜ「忘れる」のか

コンテキストの消費内訳

Claude Codeでの典型的なセッション:

    消費されるトークン(概算):

CLAUDE.md(プロジェクト設定)  :  2,000 tokens
会話履歴(10往復)            : 15,000 tokens
読み込んだソースコード        : 50,000 tokens
ツール実行結果                : 20,000 tokens
──────────────────────────────────────────
合計                          : 87,000 tokens
  

200Kのうち、すでに半分近くを消費している。 会話を続けるほど、履歴が積み上がり、限界に近づく。

限界を超えると何が起きるか

    会話1: 「ユーザー認証を実装して」
会話2: 「次にカート機能を」
会話3: 「決済処理を追加して」
  ...
会話15: 「最初に作った認証の修正が必要」
      → 「認証機能の詳細を教えていただけますか?」
  

古い会話履歴が切り捨てられるか、要約されている。 これが「忘れた」の正体。


Lost in the Middle問題

中間の情報は忘れやすい

LLMは最初と最後の情報を重視し、中間を軽視する傾向がある。

    graph LR
    subgraph Attention["LLMの注意度分布"]
        Start["最初<br/>⬆️⬆️⬆️<br/>注意度: 高"]
        Middle["中間<br/>⬇️⬇️⬇️<br/>注意度: 低"]
        End["最後<br/>⬆️⬆️⬆️<br/>注意度: 高"]
    end

    Start -.->|軽視される| Middle
    Middle -.->|重視される| End

    style Start fill:#4caf50,color:#fff
    style Middle fill:#ff9800,color:#fff
    style End fill:#4caf50,color:#fff
    style Attention fill:#fafafa,stroke:#666
  

10,000行のコードを渡して「バグを探して」と言っても、 中間にあるバグは見落とされる可能性が高い。

対策

    1. 重要な指示は最初か最後に置く
2. 長いコードは分割して渡す
3. 「特に〇〇の部分を見て」と明示する
4. 不要な情報は含めない
  

効率的なプロンプト設計

悪い例:コンテキストの無駄遣い

    えーっと、昨日から考えてたんですけど、
ちょっと難しいかもしれないんですが、
ユーザー登録機能を作りたくて、
あ、言い忘れましたがNext.jsを使ってます。
データベースはPostgreSQLで、認証はNextAuthです。
メール確認機能も欲しいかな...どう思います?
  

問題点:

  • 冗長な表現でトークンを浪費
  • 情報が散らばっている
  • 質問なのか依頼なのか不明確

良い例:構造化された指示

    # ユーザー登録機能の実装

## 環境
- Next.js 14 (App Router)
- PostgreSQL + Prisma
- NextAuth.js v5

## 要件
1. メールアドレス + パスワードで登録
2. メール確認(トークン方式)
3. パスワードはbcryptでハッシュ化

## 成果物
- app/api/auth/register/route.ts
- lib/mail.ts
- prisma/schema.prismaへの追加
  

効果:

  • 必要な情報だけを伝達
  • 構造化されていて理解しやすい
  • 成果物が明確

セッション管理の実践

セッションを分割すべきタイミング

    □ 10往復以上の会話になった
□ 別のタスクに移る
□ 大きなファイルを複数読み込んだ
□ AIの回答が不正確になってきた
  

引き継ぎプロンプト

新しいセッションを始めるときは、必要な文脈だけを伝える:

    # 前回のセッションからの引き継ぎ

## 完了した作業
- ユーザー認証(JWT + HttpOnly Cookie)
- Prismaスキーマ定義
- メール送信(SendGrid)

## 今回の作業
パスワードリセット機能の実装

## 関連ファイル
- src/lib/auth.ts
- src/routes/auth.ts
- prisma/schema.prisma
  

全ての会話履歴を引き継ぐのではなく、結果だけを引き継ぐ


Claude Codeでの活用

CLAUDE.mdを活用する

プロジェクトルートに CLAUDE.md を置くと、Claude Codeが自動で読み込む。

    # CLAUDE.md

## プロジェクト
ECサイトのバックエンドAPI

## 技術スタック
- Node.js 20 + TypeScript
- Hono(Webフレームワーク)
- Prisma + PostgreSQL

## コマンド
- pnpm dev: 開発サーバー
- pnpm test: テスト
- pnpm build: ビルド

## コーディング規約
- any禁止
- 関数は単一責任
- テストカバレッジ80%以上
  

これにより、毎回同じ説明をする必要がなくなる。

効率的な指示の出し方

    # 商品検索APIの実装

## エンドポイント
GET /api/products/search

## パラメータ
- q: 検索キーワード
- category: カテゴリID(任意)
- minPrice, maxPrice: 価格範囲(任意)
- limit, offset: ページネーション

## 実装ファイル
1. src/routes/products.ts
2. src/services/product-search.ts
3. tests/product-search.test.ts
  

「いい感じに作って」ではなく、具体的に指定する。


並行開発のテクニック

なぜ並行開発が有効か

    graph TB
    subgraph Serial["直列作業(合計時間: A + B + C)"]
        A1[タスクA] --> B1[タスクB]
        B1 --> C1[タスクC]
    end

    subgraph Parallel["並行作業(合計時間: max A, B, C)"]
        A2[タスクA]
        B2[タスクB]
        C2[タスクC]
    end

    style Serial fill:#ffebee
    style Parallel fill:#e8f5e9
    style A1 fill:#2196f3,color:#fff
    style B1 fill:#2196f3,color:#fff
    style C1 fill:#2196f3,color:#fff
    style A2 fill:#4caf50,color:#fff
    style B2 fill:#4caf50,color:#fff
    style C2 fill:#4caf50,color:#fff
  

独立したタスクは、複数のClaude Codeセッションで同時に進められる。

並行開発に適したケース

適している:

  • ユーザー認証 と 商品カタログ(別ドメイン)
  • APIエンドポイント と フロントエンドUI
  • 新機能実装 と 既存コードのリファクタリング

適していない:

  • 認証機能 と 認証が必要なAPI(依存関係あり)
  • 同じファイルを触る作業(コンフリクト)

実践方法

    ターミナル1: claude(認証機能)
ターミナル2: claude(商品機能)
ターミナル3: 開発サーバー + git操作
  

各ターミナルで独立したセッションを持ち、別々のタスクを進行。 定期的に git status でコンフリクトを確認。


よくある失敗と対策

失敗1: 一度に大量の依頼

    ❌ 「認証、商品、カート、決済、全部作って」

✅ 「まず認証機能を作って。完成したら次を依頼する」
  

一度に大量の依頼をすると、コンテキストが溢れて中途半端な結果になる。

失敗2: 曖昧な指示

    ❌ 「もっといい感じにして」

✅ 「エラーハンドリングを追加して。特にDB接続失敗時の処理」
  

具体的に何をしてほしいか明示する。

失敗3: 過去の会話に依存

    ❌ 「さっき作った関数を修正して」

✅ 「src/lib/auth.ts の validateToken 関数を修正して」
  

ファイルパスと関数名を明示する。「さっき」は忘れている可能性がある。


まとめ

コンテキストウィンドウの本質

    LLMは「記憶」しているのではなく、
毎回「全部読んでいる」。

枠を超えたら、物理的に見えない。
だから「忘れる」。
  

効率的なAI開発のポイント

ポイント 具体的な行動
構造化された指示 Markdown形式で要件を整理
セッション分割 10往復を目安に新規セッション
引き継ぎ 結果だけを伝える、履歴は不要
CLAUDE.md活用 プロジェクト設定を永続化
並行開発 独立タスクは複数セッションで

最後に

AIは道具だ。 道具の特性を理解して使えば、生産性は劇的に上がる。

コンテキストウィンドウを意識するだけで、 「AIが忘れる」「思った結果が出ない」という問題の多くは解決する。

コンテキストを制する者が、AI開発を制す。