1Mコンテキストの意味

DeepSeek V4の1Mトークンのコンテキストウィンドウは、おおよそ次のようなものです:

  • 70万文字の中国語——『三体』全巻の長さに相当
  • 1000ページのPDF文書
  • 10万行のコード
  • 数百回の会話履歴

しかし、ウィンドウが大きいほど戦略が必要です——無造作にデータを詰め込むと、トークンコストとレイテンシが制御不能になります。

メッセージ切り詰め戦略

マルチターン会話では、最新のNターンを保持し、それ以前のメッセージを切り詰めるのが最も一般的なニーズです:

def trim_conversation(messages, max_turns=20):
    """システムプロンプトと最新のmax_turnsターンの会話を保持"""
    system_msgs = [m for m in messages if m['role'] == 'system']
    chat_msgs = [m for m in messages if m['role'] != 'system']

    # 最新のmax_turnsターンを保持(各ターン = user + assistant)
    trimmed = chat_msgs[-(max_turns * 2):]

    return system_msgs + trimmed

# 使用法
messages = [{"role": "system", "content": "あなたはプログラミングアシスタントです"}]
# ... 複数ターン後
messages = trim_conversation(messages, max_turns=15)

response = client.chat.completions.create(
    model='deepseek-v4-flash',
    messages=messages
)

要約圧縮戦略

より良い方法は、履歴メッセージを単に破棄するのではなく、要約に圧縮することです:

async def compress_history(messages, threshold_turns=10):
    """会話がthreshold_turnsターンを超えた場合、初期メッセージを要約に圧縮"""
    system_msgs = [m for m in messages if m['role'] == 'system']
    chat_msgs = [m for m in messages if m['role'] != 'system']

    if len(chat_msgs) <= threshold_turns * 2:
        return messages  # 圧縮不要

    # 初期メッセージをテキストに変換
    early = chat_msgs[:-(threshold_turns * 2)]
    recent = chat_msgs[-(threshold_turns * 2):]

    history_text = '\n'.join([f"{m['role']}: {m['content'][:200]}" for m in early])

    # 要約を生成
    summary_response = await client.chat.completions.create(
        model='deepseek-v4-flash',
        messages=[{
            "role": "user",
            "content": f"以下の会話の重要な情報を中国語の1段落で要約してください:\n{history_text}"
        }]
    )

    summary = summary_response.choices[0].message.content

    return system_msgs + [
        {"role": "system", "content": f"[過去の会話要約] {summary}"}
    ] + recent

長文ドキュメントの分割処理

非常に長いドキュメント(例:本全体)の場合、分割処理が最も実用的な方法です:

def chunk_document(text, chunk_size=30000):
    """長いテキストをコンテキストウィンドウに適したチャンクに分割"""
    chunks = []
    for i in range(0, len(text), chunk_size):
        chunks.append(text[i:i+chunk_size])
    return chunks

async def analyze_long_doc(text, question):
    chunks = chunk_document(text)
    findings = []

    # フェーズ1:各チャンクを分析
    for i, chunk in enumerate(chunks):
        response = await client.chat.completions.create(
            model='deepseek-v4-flash',
            messages=[{
                "role": "system",
                "content": "テキストチャンクから質問に関連する重要な情報を抽出してください。関連情報がない場合は「なし」と答えてください。"
            }, {
                "role": "user",
                "content": f"質問:{question}\nテキストチャンク [{i+1}/{len(chunks)}]:{chunk}"
            }]
        )
        result = response.choices[0].message.content
        if result != 'なし':
            findings.append(f"[チャンク {i+1}] {result}")

    # フェーズ2:集約分析
    context = '\n\n'.join(findings)
    final = await client.chat.completions.create(
        model='deepseek-v4-pro',  # 集約にはProを使用して品質を確保
        messages=[{
            "role": "user",
            "content": f"以下の分析結果に基づいて質問に答えてください:{question}\n\n{context}"
        }]
    )
    return final.choices[0].message.content

トークン監視

# 各リクエスト後にトークン使用量を確認
response = client.chat.completions.create(...)
usage = response.usage
print(f"入力: {usage.prompt_tokens} トークン")
print(f"出力: {usage.completion_tokens} トークン")
print(f"合計: {usage.total_tokens} トークン")

# コンテキスト使用率を推定
if usage.prompt_tokens > 800000:
    print("警告:コンテキスト使用率が80%を超えています。圧縮を有効にすることを検討してください!")

ベストプラクティスまとめ

  • 日常会話ではメッセージ数を20ターン以内に保ち、切り詰め戦略を使用
  • 履歴を保持する必要がある場合は要約圧縮を使用
  • 非常に長いドキュメントには分割処理と集約の2段階法を使用
  • 常にトークン使用量を監視し、上限に近づいたら事前に対処