0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コンテキストウィンドウの処理フローと動作メカニズム 完全解説

Posted at

目次

  1. 概念の整理
  2. 技術的背景
  3. コンテキストウィンドウの構成要素
  4. 1ターンあたりの処理フロー
  5. コンテキストの動作アルゴリズム
  6. スライディングメカニズム
  7. 予算管理と数式モデル
  8. 具体例: 10ターン連続対話
  9. 実装視点: 疑似コード
  10. シーケンス図
  11. 実運用のベストプラクティス

概念の整理

基本用語

用語 意味 関係性
コンテキスト モデルが推論に使える参照情報の集合(指示、履歴、RAG結果、ツール出力など) 包括的な概念
コンテキストウィンドウ 1ターンの推論で入力として載せられる上限(トークン数) コンテキストの「扱える量」を決める制約
パッキング (packing) ウィンドウの中に、何をどの順番・粒度で詰めるかを決める処理 ウィンドウ管理の核心
退避 (eviction) 古い・冗長な履歴を外部化または削除する処理 ウィンドウ容量確保
圧縮 (compaction) 履歴を要約や参照ハンドルに置換する処理 ウィンドウ効率化

作業机の比喩

LLMは「作業用の机(ワーキングメモリ)」を持っており、その机の上(ウィンドウ)に置ける資料が限られています。必要なものを取捨選択して置く必要があり、古い資料は要約してファイリングするか、棚(外部ストレージ)に戻す必要があります。


技術的背景

なぜウィンドウ制限があるのか?

1. トークン数の上限とアテンション計算

  • Transformerベースのモデルは、入力をトークン列として扱い、self-attentionを計算
  • 計算コスト: 入力長の二乗オーダー(O(n²))に近くなる
  • 入力が長すぎるとモデル計算が現実的でなくなる

参考文献:

  • letta.com: Context window anatomy
  • IBM: Transformer architecture
  • hopsworks.ai: Context management

2. 訓練時制約

  • モデルが訓練されたときの最大コンテキスト長(シーケンス長)の制約
  • それを大きく超える範囲では性能が劣化したりロジックが破綻
  • LongRoPE2等の技術: RoPE(回転位置埋め込み)をスケールすることで長文脈対応

参考文献:

  • arXiv: LongRoPE2研究
  • arXiv: 最大コンテキスト長と実効コンテキスト長のギャップ研究

3. 計算資源・メモリ制約

  • 長い入力 → GPUメモリを大量消費
  • 推論遅延も増大
  • コストとのトレードオフが重要

コンテキストウィンドウの構成要素

"アナトミー"視点での分解

コンポーネント 役割 特記事項
システムプロンプト/基本ルール モデルの振る舞いを制御する初期指示 常にコンテキスト先頭に配置
対話履歴 ユーザ発話/モデル応答の流れを記憶 古いものは適切に要約・除外されるべき
補助ドキュメント/外部知見 RAGや検索などで取り出した参照文書 スニペット・要約化して挿入
ツール出力/ログ 呼び出した計算、APIの戻り値、ログ断片 生データは重いため要約化・圧縮
ユーザ属性・環境設定 利用者の嗜好、過去決定、ポリシーなど 副次的だが応答精度に大きく作用
制約・禁止事項 守らなければいけないルール、禁止パターン 常に参照できるように維持

ウィンドウ内配置の基本方針

  • Pinned(固定): システムプロンプト、定義済み・変化しない部分を常設
  • 動的部分: 対話履歴や長文はスニペット化、要約して代表情報だけ残す
  • 外部化: 詳細データは外部ストレージに保存し、必要時に取得

1ターンあたりの処理フロー

高解像度フロー図

各ステップの詳細

ステップ 処理内容 目的
B: 質問正規化 曖昧語("最新"など)を絶対日付に補正、出力形式を確定 意図の明確化
C〜E: 情報取得 RAGやAPIの結果を要点化・ハイライト化してから候補化 情報の質を向上
F: パッキング ウィンドウ割当(Pinned/History/Evidence/Scratch)を使い優先度順に詰める リソース最適配分
G〜I: 調整 超過時は"古い・低価値・冗長"から要約/除外、詰め直しを繰り返す ウィンドウ制約順守
K〜M: 保存 ログはそのまま増やさず、節目で/compactにより「決定・根拠・制約」を固定化 長期記憶の構造化

コンテキストの動作アルゴリズム

予算割当(バジェット)の典型例

領域 割合 説明
Pinned(目的・制約・出力契約・既決事項) 20–35% 変わらない定義や制約
History(直近数ターンの要旨) 10–25% 対話の流れを保持
Evidence(RAG/ツールの根拠スニペット) 30–50% 回答の根拠となる情報
Scratch(モデルの思考余白) 15–30% 生成に必要な残余

重要: Scratch(出力のための余白)が不足すると品質が落ちるため、最低枠を先に確保するのが実務では必須

メッセージパッキングの優先順位

  1. Pinned(固定)- 常に確保
  2. 現ターンのユーザ質問(正規化済み)
  3. 現ターンの根拠(Evidence)
  4. 直近の要旨履歴(History;古い順に削る)
  5. 参考ログ/補足(余裕があれば)

退避・圧縮(Eviction/Compaction)ポリシー

優先的に退避する順序

  1. 重複・反復の多い長文(ストリーム断片/同義反復)
  2. 古い履歴(一定ターン以上前)で再参照回数が少ないもの
  3. 低スコアのRAGスニペット(同一根拠の下位重複)
  4. 生ログ(長いツール出力や大型コード塊)→ 要約へ

退避先の形式

  • 段階的に圧縮: 詳細 → 1行要旨 → 段落要旨 → 構造化要約
  • 本文は外部ストレージに永続化(ベクトルDB、オブジェクトストレージ、messages.jsonl)
  • ウィンドウには参照ハンドル(パス/ID)のみを残す

スライディングメカニズム

「古いものが消えて新しい場所へ移動する」詳細

ターン内スライド(直前→直後)

プロセス:

  1. 入力前: Historyには直近Nターンの要旨が入っている
  2. 推論直前: 新しいEvidenceを追加し、予算内に収まるまでHistory末尾から縮約・削除
  3. 出力後: このターンの問答を"短い要旨"にまとめ、Historyの先頭側に挿入
  4. 最古処理: 最も古い要旨は下位層(外部保存+参照ハンドル)に降格

区切りでの圧縮(/compact)

実行タイミング:

  • フェーズ完了時
  • 長文膨張時
  • モデル切替前

処理内容:

  1. 「決定・根拠・制約・未解決」をまとめる
  2. Pinnedに昇格 or 別"固定要旨"として常設
  3. それ以前の詳細履歴は参照ハンドル化して普段は出さない
  4. 必要になればRAGで引き戻す

予算管理と数式モデル

予算計算の基本式

変数定義:

  • W = 最大ウィンドウ(例: 16,000トークン)
  • S = Scratch最小保証(例: 3,000トークン)
  • P = Pinned固定(例: 2,000トークン)
  • Emax = Evidence上限(例: 7,000トークン)
  • Hmax = History上限 = W - S - P - Eの残り

パッキング処理フロー

段階的要約の粒度

レベル 粒度 用途
粗要約 1–3行 軽度の超過時
中要約 5–10行 中程度の超過時
精要約 構造化JSON 重度の超過時

判断基準:

  • 30%超過 → 粗要約に落とす
  • 50%超過 → 中要約に落とす
  • 70%超過 → 精要約+外部化

具体例: 10ターン連続対話

初期設定

  • W = 16,000トークン
  • P = 2,500トークン(目的・制約・出力契約+既決方針)
  • S = 3,000トークン(余白)
  • Emax = 7,000トークン
  • Hmax = 3,500トークン(残り)

ターン別の推移

ターン1

Evidence: 3,000トークン
History: 0トークン
合計: 2,500 + 3,000 + 3,000 = 8,500トークン
状態: 余裕あり
処理: 出力後、ターン1の要旨(約300トークン)をHistoryへ

ターン4

Evidence: 6,500トークン
History: 3件 × 各300 = 900トークン
合計: 2,500 + 3,000 + 6,500 + 900 = 12,900トークン
状態: まだOK

ターン6(長文RAG多め)

Evidence: 9,000トークン → 超過!
処理:
  1. 低スコアEvidenceを削除 → 7,000トークンへ
  2. Historyを粗要約で半分に圧縮(3件 × 150 = 450トークン)
合計: 2,500 + 3,000 + 7,000 + 450 = 12,950トークン
状態: 収まる

ターン8(会話が長引く)

Evidence: 6,000トークン
History: 7件 × 各150 = 1,050トークン
合計: 2,500 + 3,000 + 6,000 + 1,050 = 12,550トークン
状態: OK

ターン10(区切り)

処理: /compact 実行
  1. 「決定・根拠・制約・未解決」を再編
  2. 重要決定の一部をPinnedに昇格(P: 2,500 → 2,800)
  3. これ以前のHistory詳細は外部化
  4. ハンドルだけ残す(History: 2件 × 150 = 300トークンに縮小)
結果: ウィンドウが大幅にクリーンアップされる

スライディングの可視化


実装視点: 疑似コード

コンテキスト組み立て関数

type Block = { 
  tokens: number; 
  text: string; 
  meta?: any 
};

function assembleContext(input: {
  pinned: Block,                 // 固定
  userTurn: Block,               // 正規化済み質問
  evidences: Block[],            // スコア降順
  history: Block[],              // 新→旧
  windowMax: number,             // W
  scratchMin: number,            // S
  evidenceMax: number            // Emax
}): Block[] {
  let budget = input.windowMax - input.scratchMin;
  const ctx: Block[] = [];

  // 1) pinned を確保
  pushWithBudget(ctx, input.pinned, budget);

  // 2) current user turn を確保
  pushWithBudget(ctx, input.userTurn, budget);

  // 3) evidences を追加(evidenceMax を尊重)
  let usedE = 0;
  for (const ev of input.evidences) {
    if (usedE + ev.tokens > input.evidenceMax) break;
    pushWithBudget(ctx, ev, budget);
    usedE += ev.tokens;
  }

  // 4) history を追加(新→旧の順)
  for (const h of input.history) {
    const ok = pushWithBudget(ctx, h, budget);
    if (!ok) break;  // 予算切れで終了
  }

  // 5) 超過していたら段階要約/退避して再試行
  if (budget < 0) {
    const reduced = compactAndEvict(
      input.history, 
      input.evidences
    );
    return assembleContext({ 
      ...input, 
      ...reduced 
    });
  }

  return ctx;
}

// ヘルパー関数
function pushWithBudget(
  ctx: Block[], 
  block: Block, 
  budget: number
): boolean {
  if (budget < block.tokens) return false;
  ctx.push(block);
  budget -= block.tokens;
  return true;
}

退避戦略の実装

function compactAndEvict(
  history: Block[], 
  evidences: Block[]
): { history: Block[], evidences: Block[] } {
  
  // 戦略1: Historyの末尾から粗要約化
  let compactedHistory = history.map((h, idx) => {
    if (idx >= history.length - 3) {
      // 古い3件を粗要約
      return summarizeCoarse(h);
    }
    return h;
  });

  // 戦略2: それでもダメなら削除
  if (getTotalTokens(compactedHistory) > threshold) {
    compactedHistory = compactedHistory.slice(0, -2);
  }

  // 戦略3: Evidenceは下位スコアから削除
  let compactedEvidences = evidences;
  if (getTotalTokens(compactedEvidences) > threshold) {
    // 同根拠の重複は先に統合
    compactedEvidences = deduplicateEvidences(evidences);
    // それでも多ければ下位削除
    compactedEvidences = compactedEvidences.slice(
      0, 
      Math.floor(evidences.length * 0.7)
    );
  }

  // 戦略4: 最終手段 - Pinnedの冗長削除
  // (実装省略 - 定義・方針のみに絞る)

  return {
    history: compactedHistory,
    evidences: compactedEvidences
  };
}

function summarizeCoarse(block: Block): Block {
  // LLMまたはルールベースで1-3行要約
  return {
    tokens: Math.floor(block.tokens * 0.3),
    text: `[要約] ${block.text.slice(0, 100)}...`,
    meta: { ...block.meta, summarized: true }
  };
}

function deduplicateEvidences(evidences: Block[]): Block[] {
  // 同一ソース・近接チャンクを統合
  const grouped = groupBy(evidences, e => e.meta.sourceId);
  return grouped.map(group => mergeChunks(group));
}

シーケンス図

ターン間の"移動"感を示すシーケンス

詳細な状態遷移


実運用のベストプラクティス

1. Scratch最小保証を必ず死守

問題: 出力余白がゼロに近いと、理屈は合っていても答えが崩れる

対策:

const SCRATCH_MIN = 3000;  // 最低3000トークン確保
const SCRATCH_IDEAL = 4000; // 理想は4000トークン

if (availableTokens < SCRATCH_MIN) {
  throw new Error('Insufficient scratch space');
}

2. "決定・制約・出力契約"はPinnedで常時可視

理由: 毎ターンの再学習を防ぎ、安定度が上がる

実装例:

const pinnedContent = {
  objective: "...",
  constraints: [...],
  outputFormat: {...},
  decisions: [...]
};

// 常にコンテキスト先頭に配置
context.unshift(pinnedContent);

3. RAGの冗長排除

問題: 同根拠の近接チャンクが重複してウィンドウを圧迫

対策:

function deduplicateChunks(chunks: Chunk[]): Chunk[] {
  // 同一ドキュメント・近接位置のチャンクを統合
  return chunks.reduce((acc, chunk) => {
    const existing = acc.find(c => 
      c.docId === chunk.docId && 
      Math.abs(c.position - chunk.position) < 100
    );
    
    if (existing) {
      existing.text += '\n' + chunk.text;
      existing.score = Math.max(existing.score, chunk.score);
    } else {
      acc.push(chunk);
    }
    
    return acc;
  }, []);
}

効果: ウィンドウ効率が跳ね上がる(30-50%の削減も可能)

4. 節目で/compact

目的: 履歴を溜めない。決定と根拠を薄く固定化し、詳細は索引に回す

実行タイミング:

  • フェーズ完了時(例: 要件定義終了)
  • 一定ターン数経過時(例: 20ターンごと)
  • ウィンドウ使用率が80%超過時
  • モデル変更前

実装例:

function shouldCompact(context: Context): boolean {
  return (
    context.turnCount % 20 === 0 ||
    context.windowUsage > 0.8 ||
    context.phaseCompleted
  );
}

async function executeCompact(context: Context) {
  // 1. 重要な決定と根拠を抽出
  const essence = await extractEssence(context.history);
  
  // 2. Pinnedに昇格
  context.pinned = mergePinned(context.pinned, essence);
  
  // 3. 詳細履歴を外部化
  const handles = await externalizeHistory(context.history);
  
  // 4. Historyを参照ハンドルのみに置き換え
  context.history = handles;
  
  // 5. 索引を再構築
  await rebuildIndex(context);
}

5. ログは"長文そのまま"を入れない

問題: 生ログは数千〜数万トークンになることも

対策: 要約+原本ハンドルで戻せる状態に

function processToolOutput(output: string): Block {
  if (output.length > 1000) {
    // 長文は要約
    const summary = summarize(output);
    const handle = saveToStorage(output);
    
    return {
      tokens: countTokens(summary),
      text: summary,
      meta: {
        type: 'tool_output',
        fullDataHandle: handle,
        retrievable: true
      }
    };
  }
  
  return {
    tokens: countTokens(output),
    text: output,
    meta: { type: 'tool_output' }
  };
}

6. 段階的要約の閾値設定

const SUMMARIZATION_THRESHOLDS = {
  coarse: { overagePercent: 30, targetCompression: 0.3 },
  medium: { overagePercent: 50, targetCompression: 0.5 },
  fine: { overagePercent: 70, targetCompression: 0.7 }
};

function selectSummarizationLevel(overage: number): string {
  const percent = (overage / windowMax) * 100;
  
  if (percent < SUMMARIZATION_THRESHOLDS.coarse.overagePercent) {
    return 'coarse';
  } else if (percent < SUMMARIZATION_THRESHOLDS.medium.overagePercent) {
    return 'medium';
  } else {
    return 'fine';
  }
}

7. ウィンドウ使用率のモニタリング

function monitorWindowUsage(context: Context) {
  const usage = {
    pinned: context.pinned.tokens,
    history: sumTokens(context.history),
    evidence: sumTokens(context.evidence),
    scratch: context.windowMax - (
      context.pinned.tokens + 
      sumTokens(context.history) + 
      sumTokens(context.evidence)
    )
  };
  
  const usagePercent = {
    pinned: (usage.pinned / context.windowMax) * 100,
    history: (usage.history / context.windowMax) * 100,
    evidence: (usage.evidence / context.windowMax) * 100,
    scratch: (usage.scratch / context.windowMax) * 100
  };
  
  // アラート条件
  if (usagePercent.scratch < 15) {
    console.warn('Scratch space critically low!');
  }
  
  if (usagePercent.history > 30) {
    console.warn('History taking too much space - consider compacting');
  }
  
  return { usage, usagePercent };
}

まとめ

コンテキストウィンドウ管理の要点

  1. 予算管理: Pinned、History、Evidence、Scratchの明確な割当
  2. 優先順位: 固定 > 現質問 > 根拠 > 履歴の順で配置
  3. 段階的圧縮: 粗要約 → 中要約 → 精要約 → 外部化の段階的処理
  4. スライディング: 新しい情報を先頭に追加し、古い情報を末尾から外部化
  5. /compact: 定期的な構造化要約で長期記憶を最適化

実装時の注意点

  • Scratch領域を必ず確保(最低15-30%)
  • RAGの冗長排除で効率アップ
  • 長文ログは要約+ハンドル化
  • 定期的な/compactで健全性維持
  • ウィンドウ使用率の継続的モニタリング

今後の拡張方向

  • 動的予算調整: クエリ複雑度に応じた領域サイズの自動調整
  • 優先度学習: ユーザフィードバックから重要度を学習
  • 階層的要約: 多段階の要約レベルを自動選択
  • コンテキスト予測: 次のターンで必要な情報を先読み

補足: 実運用ベストプラクティスの適用シーン

想定される実装シーン

「実運用のベストプラクティス」セクションは、LLMを使ったアプリケーションやエージェントを実際に開発する際の実装コード例です。

1. 対話型AIアプリケーション

  • チャットボット
  • カスタマーサポートエージェント
  • パーソナルアシスタント

2. RAGシステム

  • ドキュメント検索+生成
  • 社内ナレッジベース
  • 技術サポートシステム

3. エージェントシステム

  • ツール呼び出しを伴うタスク実行エージェント
  • 複数ステップの推論を行うエージェント
  • 長期的なタスク管理エージェント

コード例の具体的な用途

各コード例は、以下のような実装課題に対応しています:

// 例1: Scratch領域の確保(品質維持)
// → LLMに十分な"考える余白"を確保しないと、
//    途中で切れたり、品質が落ちる問題への対策
const SCRATCH_MIN = 3000;

// 例2: RAG冗長排除
// → ベクトル検索で似たチャンクが大量に返ってきて
//    ウィンドウを圧迫する問題への対策
chunks = deduplicateChunks(chunks);

// 例3: /compact実行タイミング
// → 対話が長くなると履歴が溜まりすぎる問題への対策
//    定期的に要約して整理する必要がある
if (shouldCompact(context)) {
  await executeCompact(context);
}

// 例4: ツール出力の処理
// → APIやツールの戻り値が長文すぎて
//    コンテキストを圧迫する問題への対策
const block = processToolOutput(longOutput);

実践例: カスタマーサポートボット

以下は、上記のベストプラクティスを統合した実際のエージェント実装例です:

class CustomerSupportAgent {
  private context: Context;
  private llm: LLMClient;
  private retriever: DocumentRetriever;
  
  constructor(config: AgentConfig) {
    this.context = {
      pinned: config.systemPrompt,
      history: [],
      windowMax: 16000,
      scratchMin: 3000,
      evidenceMax: 7000,
      turnCount: 0
    };
  }
  
  async handleUserQuery(query: string): Promise<string> {
    // 1. 過去の対話履歴をチェック
    const usage = monitorWindowUsage(this.context);
    console.log('Window usage:', usage.usagePercent);
    
    // 2. ウィンドウが圧迫されていたら整理
    if (usage.usagePercent.scratch < 15) {
      console.warn('Scratch space low - executing compact');
      await executeCompact(this.context);
    }
    
    // 3. 定期的なコンパクション
    if (shouldCompact(this.context)) {
      console.log('Periodic compact triggered');
      await executeCompact(this.context);
    }
    
    // 4. RAGで関連情報を取得
    let chunks = await this.retriever.search(query, {
      topK: 10,
      threshold: 0.7
    });
    
    // 5. 冗長なチャンクを排除
    chunks = deduplicateChunks(chunks);
    console.log(`Deduplicated chunks: ${chunks.length}`);
    
    // 6. ツール実行が必要な場合
    const tools = await this.detectRequiredTools(query);
    const toolOutputs = [];
    
    for (const tool of tools) {
      const rawOutput = await tool.execute(query);
      // 長文出力は要約処理
      const processed = processToolOutput(rawOutput);
      toolOutputs.push(processed);
    }
    
    // 7. コンテキストを組み立て
    const finalContext = assembleContext({
      pinned: this.context.pinned,
      userTurn: { 
        text: query, 
        tokens: countTokens(query) 
      },
      evidences: [
        ...chunks.map(c => ({
          tokens: c.tokens,
          text: c.text,
          meta: { source: c.source, score: c.score }
        })),
        ...toolOutputs
      ],
      history: this.context.history,
      windowMax: this.context.windowMax,
      scratchMin: this.context.scratchMin,
      evidenceMax: this.context.evidenceMax
    });
    
    // 8. LLMで応答生成
    const response = await this.llm.generate(finalContext);
    
    // 9. 履歴を更新
    const turnSummary = await this.summarizeTurn(query, response);
    this.context.history.unshift(turnSummary);
    
    // 10. 古い履歴を外部化
    if (this.context.history.length > 10) {
      const old = this.context.history.pop();
      await this.externalizeToStorage(old);
    }
    
    this.context.turnCount++;
    
    return response;
  }
  
  private async detectRequiredTools(query: string): Promise<Tool[]> {
    // クエリから必要なツールを判定
    // 例: "注文状況を確認して" → OrderLookupTool
    return [];
  }
  
  private async summarizeTurn(
    query: string, 
    response: string
  ): Promise<Block> {
    // このターンを要約
    const summary = await this.llm.summarize(
      `User: ${query}\nAssistant: ${response}`
    );
    
    return {
      tokens: countTokens(summary),
      text: summary,
      meta: { 
        turn: this.context.turnCount,
        timestamp: Date.now()
      }
    };
  }
  
  private async externalizeToStorage(block: Block): Promise<void> {
    // 外部ストレージ(DBやオブジェクトストレージ)に保存
    const handle = await this.storage.save(block);
    console.log(`Externalized history block: ${handle}`);
  }
}

主要機能の解説

モニタリングと自動調整

// リアルタイムでウィンドウ使用状況を監視
const usage = monitorWindowUsage(this.context);

// 閾値を下回ったら自動で整理
if (usage.usagePercent.scratch < 15) {
  await executeCompact(this.context);
}

RAG最適化

// 検索結果の重複を排除してウィンドウ効率を向上
chunks = deduplicateChunks(chunks);

// 同一ドキュメントの近接チャンクを統合
// → 30-50%のトークン削減が可能

ツール出力の処理

// 長文のツール出力は要約+ハンドル化
const processed = processToolOutput(rawOutput);

// 元データは外部保存、要約だけをコンテキストに

履歴管理

// 新しいターンを先頭に追加
this.context.history.unshift(turnSummary);

// 古いターンは外部化
if (this.context.history.length > 10) {
  const old = this.context.history.pop();
  await this.externalizeToStorage(old);
}

実装時のチェックリスト

  • Scratch領域の最小保証(15-30%)を設定
  • Pinned領域に固定すべき内容(目的、制約、契約)を配置
  • RAGチャンクの重複排除ロジックを実装
  • ツール出力の要約処理を実装
  • 定期的な/compact実行の条件を設定
  • ウィンドウ使用率のモニタリングとログ出力
  • 履歴の外部化・復元機能の実装
  • 段階的要約(粗→中→精)の閾値設定

パフォーマンス指標

指標 推奨値 警告閾値
Scratch使用率 20-30% < 15%
History使用率 10-20% > 30%
Evidence使用率 30-50% > 60%
総ウィンドウ使用率 70-85% > 90%
/compact実行間隔 15-20ターン > 30ターン

デバッグとトラブルシューティング

問題: 応答品質が突然低下

// 原因: Scratch領域の不足
// 対策: ウィンドウ使用状況を確認
const usage = monitorWindowUsage(context);
console.log('Scratch tokens:', usage.scratch);

if (usage.scratch < SCRATCH_MIN) {
  // 緊急コンパクション実行
  await executeCompact(context);
}

問題: 過去の重要な決定を忘れる

// 原因: 重要情報がHistoryから流れてしまった
// 対策: Pinnedに昇格
const importantDecisions = extractKeyDecisions(context.history);
context.pinned = mergePinned(context.pinned, importantDecisions);

問題: レスポンスが遅い

// 原因: Evidence領域が大きすぎる
// 対策: チャンク数を制限、スコアで厳選
const topChunks = chunks
  .sort((a, b) => b.score - a.score)
  .slice(0, 5);  // 上位5件のみ

まとめ: 実装のポイント

  1. 予防的管理: 問題が起きる前にモニタリングと自動調整
  2. 段階的対応: 軽度な問題は軽い対策、重度な問題は強い対策
  3. トレードオフの理解: 情報量 vs 応答品質のバランス
  4. 継続的最適化: ログを分析して閾値を調整
  5. ユーザー体験優先: レスポンス速度と品質のバランス

これらのベストプラクティスを適用することで、長時間の対話でも安定した品質を維持できるLLMアプリケーションを構築できます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?