AIエージェントで「不要な過去を忘れる」 – LayerX エンジニアブログ

LayerX Ai Workforce事業部R&Dチームとデータ検索基盤チームマネージャーの澁井(しぶい)と申します。今回はLLMやAIエージェントのコンテキストエンジニアリングとして、不要な過去を忘れることをテーマに検討していきたいと思います。

なお、本テーマと対になる「コンテキストの並行世界を作る」エンジニアリングについて、LayerX TechBook 1 (2025/11/16(日)開催の技術書典で販売)に寄稿しています。

そちらもご一読いただけると幸いです。


LLMとの対話やAIエージェントのプロセスは、過去の対話履歴やLLMの処理結果が蓄積されていく仕組みになります。こうしたデータはコンテキストと呼ばれ、LLMが処理履歴や文脈を把握し、次のリクエストを適切に解決するために活用されます。こうした概念は2025年半ばからコンテキストエンジニアリングと命名され、LLMやAIエージェント開発の重要な技術として注目されています。

他方で、過去の履歴が蓄積されることで、文脈の誤解釈や出力品質の低下が発生することがあります。今回解説する「不要な過去を忘れる」プラクティスは、意図的に対話履歴の一部または全体を破棄してコンテキストを再構成することで、LLMの出力品質を安定させる設計手法です。適切なタイミングで履歴をリセット、あるいは重要な地点まで巻き戻す(ロールバックする)ことで、誤った情報の影響を排除します。このアプローチにより、不要なコンテキストによる品質劣化を防ぎ、システムの安定性と信頼性を向上させることを目指します。

LLMを用いたAIエージェントでは、処理が積み重なって長くなれば長くなるほど、コンテキストの分量は増えていきます。特に長時間の対話や複雑なタスクを実行すると、コンテキスト量は膨大になるでしょう。コンテキスト爆発によってLLMにリクエストが失敗すること、さらにはコンテキスト汚染によって重要な情報がコンテキストに埋もれる課題は案外頻繁に発生します。

また、ユーザーが誤った情報を入力したり、LLMがハルシネーションによって不正確な情報を生成したりすると、その誤った情報が対話履歴に残り続けます。この汚染された履歴が後続のプロンプトに含まれることで、LLMは誤った前提に基づいて推論を進めてしまい、出力品質が低下する原因となります。

たとえばChatGPTやGeminiを利用しているとき、途中まで良い対話が続いていたのに、入力ミスやLLMのハルシネーション等で途中から品質が下がって方向修正できなくなった経験はないでしょうか?一度誤った方向に進んだタスクを修正するのは困難であり、多くの場合、最初からやり直す非効率な対応が必要になります。

こうした課題を解決するため、コンテキストを意図的に忘れる仕組みをシステムに導入します。このプラクティスは、単にコンテキストの一部を削除するだけでなく、コンテキストの状態を構造的に管理することで、LLMのパフォーマンスを安定化することを目指します。

主な実装方法として、以下の3つのアプローチが考えられます。

  1. チェックポイントのスナップショット化: LLM処理の重要な区切りで、その時点のコンテキストのスナップショットを保存します。例えば、AIエージェントが複数ステップのタスクを実行する際、各ステップの完了時にチェックポイントを作成します。これにより、後続のステップで問題が発生した場合でも、正常に完了した最後のステップの状態に復帰してやり直すことが可能になります。
  2. ロールバック機構の導入: LLMの出力品質が一定の基準を下回った場合や、ユーザーが明示的に指示した(Human-in-the-Loop)場合に、履歴を特定のチェックポイントまで巻き戻します。AIエージェントが誤った事実を出力したり、不要なツール実行を検知した場合、システムは自動的にロールバックを実行し、正常な状態からタスクを再開させます。
  3. 動的なリセット: コンテキストの流れが大きく変化したことをシステムが検知(LLM-as-a-Judge)した際に、関連性の低い過去の履歴を自動的に削除します。例えばカスタマーサポートのチャットボットで、話題が「製品機能」から「請求」に変わった時点で、製品機能に関する詳細な履歴を要約または削除することで、コンテキスト汚染を防ぎます。

これらの仕組みを導入することで、誤った情報の影響を局所化し、システム全体に波及することを防ぎながら、AIエージェントの処理を安定的に継続することが可能になります。

このプラクティスは、長時間の対話や状態管理が重要となるLLMアプリケーションで有効です。

代表的なユースケースとして自律型エージェントによる複雑なタスクの実行が挙げられます。

例えばRAG(Retrieval-Augmented Generation)を用いた質問応答システムを考えます。RAGでは、ユーザーの質問に対して社内ドキュメントから関連情報を検索し、その内容を基にLLMが回答を生成します。もし最初の検索ステップで無関係なドキュメントを取得し、コンテキストにそのまま残してしまった場合、回答の質は低下するでしょう。対策として、取得したドキュメントの関連性を評価するLLM-as-a-Judgeを導入します。そのスコアが低い場合にはその検索結果を「忘れ」、検索クエリを修正して再検索を実行することで、不正確な情報がLLMに渡ることを未然に防ぎます。

このプラクティスを効果的に導入するためには、以下の点を考慮することが重要です。

  • チェックポイント戦略の設計: どのタイミングでチェックポイントを保存するかをあらかじめ戦略的に決定します。例えばタスクの開始時や、ユーザーからの重要な指示を受け取った後、あるいはエラーが発生した直後など、復帰点として意味のあるタイミングを見極めることが重要です。
  • 自動ロールバックの導入検討: LLM応答の品質スコアやコンテキストの一貫性スコアを監視し、スコアが一定のしきい値を下回った場合に自動で履歴を巻き戻す仕組みの導入を検討します。これにより、システムが自律的に問題を検知し、自己修復することが可能になります。
  • 部分的な忘却の設計: コンテキスト全体を削除するのではなく、誤った発言や関連性の低いトピックに関する部分のみを選択的に削除する、より高度な制御を設計します。重要な情報を保持しつつ、不要なコンテキストだけを排除することを目指します。
  • テストケースの整備: 正常系だけでなく、意図的に誤った情報を入力する異常系のテストケースを用意し、ロールバック機構が正しく機能することを確認する単体テストやE2Eテストを整備することが不可欠です。

サンプルコード

以下にLangGraphを使ったサンプルコードを掲載します。サンプルコードでは、LLMに対して以下の質問をリクエストします。

  1. Pythonのリスト内包表記について教えてください。
  2. ところで、今日の天気はどうですか?
  3. Pythonのデコレータについても教えてください。
  4. (品質の低い会話を挿入)

ご覧のとおり、1.と2.、2.と3.で話題が変わります。また、最後の4.で品質の低い会話を挿入します。サンプルコードは、それぞれのタイミングで適宜コンテキストを動的に変更、修正しながら回答するAIエージェントになります。

サンプルコード全文は以下Gistになります。

ai_agent_forget_past

長くなるので、以下に重要なロジックを抜粋して解説します。

状態の定義 (AgentState)

エージェントの「記憶」を管理するAgentStateの定義が、このプラクティスの中核となります。

class AgentState(TypedDict):
    """エージェントの状態を管理するクラス"""

    messages: Annotated[List[BaseMessage], operator.add]
    current_topic: str
    quality_score: float
    checkpoint_metadata: Dict[str, Any]
    should_rollback: bool
    rollback_checkpoint_id: Optional[str]
    state_history: List[Dict[str, Any]]  

ここでは、対話履歴であるmessagesに加えて、品質評価の結果を保持するquality_scoreや、ロールバックを実行すべきかを判断するshould_rollbackフラグを定義しています。LangGraphは、これらの状態変数をノード間で引き継ぎながら処理を進めます。

品質の評価 (evaluate_quality)

本文書で解説した「LLM-as-a-Judge」を実装しているのがevaluate_qualityノードです。

    def evaluate_quality(self, state: AgentState) -> AgentState:
        """LLM-as-a-Judgeパターンで出力品質を評価"""
        
        try:
            response = self.llm.invoke([SystemMessage(content=evaluation_prompt)])
            score = float(response.content.strip())
            state["quality_score"] = max(0.0, min(1.0, score))
        

        
        if state["quality_score"] "should_rollback"] = True
            self._record_state(state, "evaluate_quality", "品質低下によりロールバックフラグ設定")

        return state

この関数は、現在の対話履歴をLLM自身に評価させ、その結果をquality_scoreに格納します。もし品質が設定した閾値(self.quality_threshold)を下回った場合、should_rollbackフラグをTrueに設定し、後続のプロセスでロールバックが実行されるよう指示します。

話題変更の検出とコンテキスト圧縮 (detect_topic_change / _compress_old_context)

「動的なリセット」を実現するため、話題の変化を検出するノードを実装しています。

    def detect_topic_change(self, state: AgentState) -> AgentState:
        
        try:
            response = self.llm.invoke([SystemMessage(content=topic_analysis_prompt)])
            similarity = float(response.content.strip())

            
            if similarity "detect_topic_change", 
                    "話題変更検出 - コンテキスト圧縮開始",
                  )
                state = self._compress_old_context(state)
        
        return state

    def _compress_old_context(self, state: AgentState) -> AgentState:
        
        try:
            response = self.llm.invoke([SystemMessage(content=summary_prompt)])
            summary = SystemMessage(content=f"[過去の会話の要約]: {response.content}")

            
            state["messages"] = [summary] + recent_messages
        
        return state

detect_topic_changeは、直近のメッセージとそれ以前の会話の「話題類似度」をLLMでスコアリングします。類似度が閾値(self.topic_change_threshold)を下回った場合(=話題が変わったと判断した場合)、_compress_old_contextが呼び出されます。
この関数は、古い対話履歴(old_messages)をLLMで要約し、[過去の会話の要約]: ...という一つのSystemMessageに置き換えます。これにより、関連性の低い過去のコンテキストを「忘れ」、新しい話題に必要なコンテキスト容量を確保します。

チェックポイントの作成とロールバック (create_checkpoint / rollback_to_checkpoint)

本プラクティスの核となる、スナップショットとロールバックの実装です。

    def create_checkpoint(self, state: AgentState) -> AgentState:
        """重要なタイミングでチェックポイントを作成"""
        checkpoint_id = str(uuid.uuid4())
        
        self.saved_checkpoints[checkpoint_id] = {
            "timestamp": datetime.now().isoformat(),
            "messages": state["messages"].copy(),  
            "topic": state["current_topic"],
            "quality_score": state["quality_score"],
        }
        return state

    def rollback_to_checkpoint(self, state: AgentState) -> AgentState:
        """チェックポイントにロールバック"""
        if not state.get("should_rollback"):
            return state

        

        if checkpoint_id and checkpoint_id in self.saved_checkpoints:
            checkpoint = self.saved_checkpoints[checkpoint_id]

            
            state["messages"] = checkpoint["messages"].copy()
            state["current_topic"] = checkpoint["topic"]
            state["quality_score"] = checkpoint["quality_score"]
            state["should_rollback"] = False

            
            rollback_msg = SystemMessage(content="[システム]: 会話品質の低下を検出したため、前の状態に戻しました。")
            state["messages"].append(rollback_msg)

        return state

create_checkpointは、品質が良好と判断された時点のstate["messages"]をディープコピーし、self.saved_checkpoints辞書に保存します。
rollback_to_checkpointは、evaluate_qualityでセットされたshould_rollbackフラグを検知すると実行されます。最後に保存された良好なチェックポイントを探し出し、現在のstate["messages"]を、保存されていたチェックポイントのmessagesで丸ごと上書きします。これにより、品質が低下する直前の「健全な過去」にコンテキストを巻き戻します。

ワークフローの構築 (build_graph)

最後に、これらのノードをLangGraphStateGraphとして組み立てます。

    def build_graph(self) -> StateGraph:
        """LangGraphのワークフローを構築"""
        
        workflow.set_entry_point("evaluate_quality")
        workflow.add_edge("evaluate_quality", "detect_topic_change")
        workflow.add_edge("detect_topic_change", "process_message")

        
        workflow.add_conditional_edges(
            "process_message",
            self.should_continue,
            {"rollback": "rollback", "checkpoint": "create_checkpoint", "continue": END},
        )
        workflow.add_edge("create_checkpoint", END)
        workflow.add_edge("rollback", "process_message") 

        return workflow.compile(checkpointer=self.checkpointer)

このグラフ定義で最も重要なのはadd_conditional_edges(条件分岐)です。
process_messageノード(LLMによる応答生成)が完了した後、should_continueノード(evaluate_qualityの結果に基づき判断)が呼ばれます。

  1. 品質が低下(should_rollbackTrue)していれば、"rollback"(ロールバック)ノードに進みます。
  2. 品質が特に高い(quality_score >= 0.8)場合は、"checkpoint"(スナップショット保存)ノードに進みます。
  3. それ以外の場合は、"continue"END)となり、処理を終了します。

このように、LangGraphの条件分岐を活用することで、LLMの出力品質に応じて動的にコンテキストを管理する(「忘れる」または「保存する」)ワークフローを実現しています。

---
config:
  flowchart:
    curve: linear
---
graph TD;
    __start__([

__start__

]):::first evaluate_quality(evaluate_quality) detect_topic_change(detect_topic_change) process_message(process_message) create_checkpoint(create_checkpoint) rollback(rollback) __end__([

__end__

]):::last __start__ --> evaluate_quality; detect_topic_change --> process_message; evaluate_quality --> detect_topic_change; process_message -.  continue  .-> __end__; process_message -.  checkpoint  .-> create_checkpoint; process_message -.-> rollback; rollback --> process_message; create_checkpoint --> __end__; classDef default fill:#f2f0ff,line-height:1.2 classDef first fill-opacity:0 classDef last fill:#bfb6fc

このプラクティスを導入する際には、いくつかの注意点とトレードオフが存在します。

最大のトレードオフはシステムの複雑性の増加です。コンテキストの状態を管理し、チェックポイントを保存・復元するロジックは、アプリケーションの設計を複雑にします。状態管理が不十分な場合、ロールバック処理中に新たなバグを生み出す可能性もあり、開発とデバッグのコストが増加します。システムの回復力と、それに伴う実装の複雑性とのバランスを慎重に検討する必要があります。

また、ユーザー体験の毀損リスクも考慮すべき点です。ユーザーが意図して過去の文脈を引き継いだ会話を期待しているにもかかわらず、システムが自動で履歴をリセットしてしまうと、対話が不自然に中断されたと感じさせ、不満を抱かせる原因となります。どのタイミングで履歴をリセットするかの判断基準は非常に繊細であり、過度に積極的なリセットは、かえってユーザー体験を損なう可能性があります。リセットのトリガー条件(自動化、人間手動等含めて)は、慎重に設計・調整する必要があります。

加えて、このプラクティスではコンテキストを変更するため、LLMのKVキャッシュが効かなくなる可能性があります。結果としてLLM推論の速度が低下するリスクがあります。

「不要な過去を忘れる」プラクティスは、AIエージェントのコンテキストを構造的に管理することで、誤った情報によるコンテキスト汚染を防ぎ、システムの応答品質と安定性を維持する設計手法です。特に、長時間稼働する対話システムや自律型エージェントにおいて、品質を維持しながら運用する手段となります。導入時にはシステムの複雑性やユーザー体験への影響、キャッシュの効果を考慮し、段階的に実装を進めると良いでしょう。


R&Dチームおよびデータ検索基盤チームではエンジニアを募集しています。

一緒にR&Dチームおよびデータ検索基盤チームで、今回書いたようなAIエージェントの研究やエンジニアリングに挑戦したい方はぜひ気軽にお話できると幸いです。

🛰️ ワークフロー自動生成R&Dとか業務フロー合成データとか検索とかに興味ある方、雑談しましょう!




元の記事を確認する

関連記事