トラブルシューティング

AIエージェントのコンテキスト上限問題を解決する方法

Media Lab

AIエージェントのコンテキスト上限問題を解決する方法

AIエージェントを長時間使ったり、大量のドキュメントを処理しようとすると「コンテキスト長が上限に達した」というエラーに遭遇することがあります。この問題の仕組みと解決策を解説します。

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

LLMは一度に処理できる情報量に「コンテキストウィンドウ」という上限があります。これはトークン(単語や文字の単位)で計測され、会話履歴・システムプロンプト・ユーザー入力・エージェントの思考ログ・ツール実行結果がすべてこのウィンドウに収まる必要があります。

主要モデルのコンテキストウィンドウ:

  • GPT-4o: 128Kトークン(約10万文字)
  • Claude 3.5 Sonnet: 200Kトークン(約15万文字)
  • Gemini 1.5 Pro: 1Mトークン(約75万文字)
  • Llama 3.2: 128Kトークン

長時間のエージェント実行では、会話履歴とツール実行ログが蓄積して上限に達します。

解決策1:会話履歴のサマリゼーション

最も一般的な対処法は、古い会話履歴を定期的に要約して圧縮することです。

def summarize_history(conversation_history, llm_client):
    if len(str(conversation_history)) < 50000:  # 5万文字以下なら不要
        return conversation_history
    
    # 古い部分を要約
    old_messages = conversation_history[:-10]  # 最新10件以外を要約対象
    recent_messages = conversation_history[-10:]  # 最新10件はそのまま保持
    
    summary = llm_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "user", 
             "content": f"以下の会話を200文字以内で要約してください:{old_messages}"}
        ]
    )
    
    return [{"role": "system", "content": f"会話の要約:{summary.choices[0].message.content}"}] + recent_messages

AIエージェントのメモリ機能でも解説したように、サマリゼーションは長期エージェントの必須機能です。

解決策2:スライディングウィンドウ

最新のN件のメッセージのみをコンテキストに含め、古いものは破棄します。シンプルですが重要な情報が失われるリスクがあります。

def apply_sliding_window(conversation_history, max_messages=30):
    if len(conversation_history) <= max_messages:
        return conversation_history
    
    # システムプロンプトは必ず保持
    system_messages = [m for m in conversation_history if m['role'] == 'system']
    other_messages = [m for m in conversation_history if m['role'] != 'system']
    
    # 最新max_messages件のメッセージを保持
    recent_messages = other_messages[-(max_messages - len(system_messages)):]
    
    return system_messages + recent_messages

解決策3:RAGによる外部化

重要な情報をベクトルデータベースに外部化し、必要な時だけ検索して取得します。会話全体を保持するのではなく「何について話したか」のキーワードと意味的インデックスを保存します。

大量のドキュメントを処理する場合は、ドキュメント全体を一度に渡すのではなく、質問に関連する部分だけをRAGで取得してコンテキストに含めます。

解決策4:チャンク分割での処理

長いドキュメントは小さなチャンクに分割して順番に処理します。

def process_long_document(document, chunk_size=2000, overlap=200):
    chunks = []
    start = 0
    while start < len(document):
        end = start + chunk_size
        chunk = document[start:end]
        chunks.append(chunk)
        start = end - overlap  # オーバーラップで文脈を維持
    
    results = []
    for i, chunk in enumerate(chunks):
        result = llm_client.analyze(chunk, context=f"パート {i+1}/{len(chunks)}")
        results.append(result)
    
    # 全チャンクの結果を統合
    final_summary = llm_client.summarize(results)
    return final_summary

解決策5:より大きなコンテキストを持つモデルへの移行

コンテキスト問題が頻繁に発生する場合は、より大きなコンテキストウィンドウを持つモデルへの移行を検討します。Gemini 1.5 Proの1Mトークンは、ほとんどのユースケースで上限に達しません。

ただし、大きなコンテキストを使うほどコストも増加するため、AIエージェントのコスト超過防止方法と合わせてコスト管理も検討してください。

予防的な設計

コンテキスト問題は後から対処するよりも、最初から予防的に設計することが効率的です。

  • システムプロンプトを必要最小限の長さに保つ
  • ツール実行の詳細ログはすべてコンテキストに含めず、要約のみ含める
  • 長期エージェントにはサマリゼーションを初期設計から組み込む
  • コンテキスト使用量をモニタリングして80%に達したら自動的に圧縮する

まとめ

コンテキスト上限問題の解決策は、サマリゼーション・スライディングウィンドウ・RAG・チャンク分割の4つが主要です。AIエージェントのエラートラブルシューティングの観点からも、コンテキスト管理は長期安定運用の鍵となります。ユースケースに合った適切な組み合わせを選択して実装しましょう。