LangChain 1.0 α : コアコンポーネント – 短期メモリ

メモリは以前のインタラクションに関する情報を記憶するシステムです。AI エージェントにとって、メモリは以前のインタラクションを記憶し、フィードバックから学習し、ユーザの好みに適応するために不可欠です。

LangChain 1.0 alpha : コアコンポーネント – 短期メモリ

作成 : クラスキャット・セールスインフォメーション
作成日時 : 09/25/2025
バージョン : 1.0.0a9

* 本記事は docs.langchain.com の以下のページを独自に翻訳した上で、補足説明を加えてまとめ直しています。スニペットはできる限り日本語を使用しています :

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス ⭐️ リニューアルしました 😉

クラスキャット は人工知能に関する各種サービスを提供しています。お気軽にご相談ください :

  • 人工知能導入個別相談会(無償)実施中! [詳細]

  • 人工知能研究開発支援 [詳細]
    1. 自社特有情報を含むチャットボット構築支援
    2. 画像認識 (医療系含む) / 画像生成

  • PoC(概念実証)を失敗させないための支援 [詳細]

お問合せ : 下記までお願いします。

  • クラスキャット セールス・インフォメーション
  • sales-info@classcat.com
  • ClassCatJP

 

 

LangChain 1.0 alpha : コアコンポーネント – 短期メモリ

概要

メモリは以前のインタラクションに関する情報を記憶するシステムです。AI エージェントにとって、メモリは以前のインタラクションを記憶し、フィードバックから学習し、ユーザの好みに適応するために不可欠です。エージェントはユーザインタラクションを伴うより複雑なタスクに取り組むようになると、この機能は効率性とユーザ満足度の両方で重要になります。

短期メモリはアプリケーションが単一のスレッドや会話内の以前のインタラクションを記憶することを可能にします。

Info : スレッドは、電子メールが単一の会話内のメッセージをグループ化するのと同様に、セッション内の複数のインタラクションをまとめて整理します。

会話履歴は、短期メモリの最も一般的な形式です。長い会話は現代の LLM に課題を与えます ; 完全な履歴は LLM のコンテキスト・ウィンドウに収まらない場合があり、コンテキストの喪失やエラーという結果になります。

モデルが完全なコンテキスト長をサポートする場合でさえ、殆どの LLM は依然として長いコンテキストに対してはパフォーマンスが劣化します。それらは古くなったり話題から外れたコンテンツにより「気を散らかされ (distracted)」、遅い応答時間や高いコストに悩まされることになります。

チャットモデルは メッセージ を使用してコンテキストを受け取ります、これは指示 (システムメッセージ) と入力 (人間のメッセージ) を含みます。チャットアプリケーションでは、メッセージは人間の入力とモデルのレスポンスが交互に現れるので、時間とともにメッセージのリストが長くなるという結果になります。コンテキストウィンドウは制限されていますので、多くのアプリケーションは古くなった情報を削除または「忘れる」テクニックを使用することでメリットを享受できます。

 

使用方法

エージェントに短期メモリ (スレッドレベルの永続性) を追加するには、エージェントを作成する際にチェックポインタを指定する必要があります。

Info : LangChain のエージェントは短期メモリをエージェントの状態の一部として管理します。

これらをグラフの状態に保存することで、エージェントは異なるスレッド間で分離を維持しながら、特定の会話について完全なコンテキストにアクセスできます。

状態はチェックポインタを使用してデータベース (or メモリ) に永続化されるので、スレッドはいつでも再開できます。

短期メモリは、エージェントが呼び出される (invoked) ときか、(ツール呼び出しのような) ステップが完了されるときに更新され、状態は各ステップの最初に読まれます。

from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

agent = create_agent(
    "openai:gpt-5",
    [get_user_info],
    checkpointer=InMemorySaver(),
)

agent.invoke(
    {"messages": [{"role": "user", "content": "Hi! My name is Bob."}]},
    {"configurable": {"thread_id": "1"}},
)

 

本番環境

本番環境では、データベースをバックエンドとするチェックポインタを使用します :

from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver

DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
    agent = create_agent(
        "openai:gpt-5",
        [get_user_info],
        checkpointer=checkpointer,
    )

 

エージェントメモリのカスタマイズ

デフォルトでは、エージェントは AgentState を使用して短期メモリ (具体的にはメッセージキー経由の会話履歴) を管理します。

ユーザは AgentState をサブクラス化して、状態に追加フィールドを追加します。

そしてカスタム状態は、ツールや動的プロンプト / モデル関数経由でアクセスできます。

from langchain.agents import create_agent, AgentState
from langgraph.checkpoint.memory import InMemorySaver

class CustomAgentState(AgentState):
    user_id: str

agent = create_agent(
    "openai:gpt-5",
    [get_user_info],
    state_schema=CustomAgentState,
    checkpointer=InMemorySaver(),
)

 

コモンパターン

短期メモリ が有効にされると、長い会話は LLM のコンテキストウィンドウを超える可能性があります。一般的な解決策は :

  • メッセージのトリミング – (LLM を呼び出す前に) 最初か最後の N メッセージを除去します。

  • メッセージの削除 – LangGraph 状態から永続的にメッセージを削除します。

  • メッセージの要約 – 履歴の以前のメッセージを要約して、要約で置き換えます。

  • カスタム・ストラテジー – カスタム・ストラテジー (e.g., メッセージフィルタリング 等)

これは LLM のコンテキストウィンドウを超えることなく、エージェントが会話を追跡し続けることを可能にします。

 

メッセージのトリミング

殆どの LLM は (トークンベースで) サポートされるコンテキストウィンドウの最大数があります。

いつメッセージを切り捨てる (truncate) かを決める一つの方法は、メッセージ履歴のトークンをカウントして制限に近づいた時点で切り捨てることです。LangChain を使用している場合、trim メッセージユーティリティを使用して、リストから保持するトークン数や境界を処理するために使用する strategy (e.g., 最後の maxTokens を保持) を指定することができます。

エージェントのメッセージ履歴をトリミングするには、trim_messages 関数とともに @[pre_model_hook][create_agent] を使用します :

from langchain_core.messages.utils import trim_messages, count_tokens_approximately
from langchain_core.messages import BaseMessage
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent
from langchain_core.runnables import RunnableConfig

def pre_model_hook(state) -> dict[str, list[BaseMessage]]:
    """
    This function will be called prior to every llm call to prepare the messages for the llm.
    """
    trimmed_messages = trim_messages(
        state["messages"],
        strategy="last",
        token_counter=count_tokens_approximately,
        max_tokens=384,
        start_on="human",
        end_on=("human", "tool"),
    )
    return {"llm_input_messages": trimmed_messages}


checkpointer = InMemorySaver()
agent = create_agent(
    "openai:gpt-5-nano",
    tools=[],
    pre_model_hook=pre_model_hook,
    checkpointer=checkpointer,
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

agent.invoke({"messages": "hi, my name is bob"}, config)
agent.invoke({"messages": "write a short poem about cats"}, config)
agent.invoke({"messages": "now do the same but for dogs"}, config)
final_response = agent.invoke({"messages": "what's my name?"}, config)

final_response["messages"][-1].pretty_print()
"""
================================== Ai Message ==================================

Your name is Bob. You told me that earlier.
If you'd like me to call you a nickname or use a different name, just say the word.
"""

 

メッセージの削除

メッセージ履歴を管理するためにグラフ状態からメッセージを削除できます。

これは、特定のメッセージを削除したりメッセージ履歴全体をクリアしたい場合に役立ちます。

グラフ状態からメッセージを削除するには、RemoveMessage が使用できます。

RemoveMessage が機能するためには、add_messages reducer とともに状態キーを使用する必要があります。

デフォルトの AgentState がこれを提供します。

特定のメッセージを削除するには :

from langchain_core.messages import RemoveMessage

def delete_messages(state):
    messages = state["messages"]
    if len(messages) > 2:
        # remove the earliest two messages
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}

すべてのメッセージを削除するには :

from langgraph.graph.message import REMOVE_ALL_MESSAGES

def delete_messages(state):
    return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}

Info : メッセージを削除する際には、結果としてのメッセージ履歴が有効であることを確認してください。使用している LLM プロバイダーの制限を確認してください。例えば :

  • 一部のプロバイダーは、メッセージ履歴がユーザメッセージから始まることを想定しています。
  • 殆どのプロバイダーはツール呼び出しを含むアシスタントメッセージの後に、対応するツール結果メッセージが続くことを必要としています。
from langchain_core.messages import RemoveMessage
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.runnables import RunnableConfig


def delete_messages(state):
    messages = state["messages"]
    if len(messages) > 2:
        # remove the earliest two messages
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}


agent = create_agent(
    "openai:gpt-5-nano",
    tools=[],
    prompt="Please be concise and to the point.",
    post_model_hook=delete_messages,
    checkpointer=InMemorySaver(),
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

for event in agent.stream(
    {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])

for event in agent.stream(
    {"messages": [{"role": "user", "content": "what's my name?"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])

出力例

[('human', "hi! I'm bob")]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code.')]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code.'), ('human', "what's my name?")]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code.'), ('human', "what's my name?"), ('ai', 'Your name is Bob. How can I help you today, Bob?')]
[('human', "what's my name?"), ('ai', 'Your name is Bob. How can I help you today, Bob?')]

 

メッセージの要約

上記のメッセージのトリミングや削除に伴う問題は、メッセージキューの間引き (culling) により情報が失われる可能性があることです。そのため、一部のアプリケーションはチャットモデルを使用して、メッセージ履歴を要約するという、より洗練されたアプローチからメリットを受けます。

エージェントのメッセージ履歴を要約するには、事前構築済み SummarizationNode 抽象化とともに @[pre_model_hook][create_agent] を使用します :

from langmem.short_term import SummarizationNode, RunningSummary
from langchain_core.messages.utils import count_tokens_approximately
from langchain.agents import create_agent, AgentState
from langgraph.checkpoint.memory import InMemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnableConfig

model = ChatOpenAI(model="gpt-4o-mini")

summarization_node = SummarizationNode(
    token_counter=count_tokens_approximately,
    model=model,
    max_tokens=384,
    max_summary_tokens=128,
    output_messages_key="llm_input_messages",
)

class State(AgentState):
    # Added for the SummarizationNode to be able to keep track of the running summary information
    context: dict[str, RunningSummary]


checkpointer = InMemorySaver()

agent = create_agent(
    model=model,
    tools=[],
    pre_model_hook=summarization_node,
    state_schema=State,
    checkpointer=checkpointer,
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}
agent.invoke({"messages": "hi, my name is bob"}, config)
agent.invoke({"messages": "write a short poem about cats"}, config)
agent.invoke({"messages": "now do the same but for dogs"}, config)
final_response = agent.invoke({"messages": "what's my name?"}, config)

print(final_response.keys())

final_response["messages"][-1].pretty_print()
print("\nSummary:", final_response["context"]["running_summary"].summary)

 

アクセス

エージェントの短期メモリに幾つかの方法でアクセスできます :

 

ツール

ツール内で短期メモリを読む

InjectedState アノテーションを使用してエージェントの状態をツール・シグネチャに注入することで、ツール内で短期メモリ (状態) にアクセスできます。

このアノテーションはツール・シグネチャから状態を隠します (従ってモデルからは見えません) が、ツールはそれにアクセスできます。

from typing import Annotated
from langchain.agents import create_agent, AgentState
from langchain.agents.tool_node import InjectedState

class CustomState(AgentState):
    user_id: str

def get_user_info(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """Look up user info."""
    user_id = state["user_id"]
    return "User is John Smith" if user_id == "user_123" else "Unknown user"

agent = create_agent(
    model="openai:gpt-5-nano",
    tools=[get_user_info],
    state_schema=CustomState,
)

result = agent.invoke({
    "messages": "look up user information",
    "user_id": "user_123"
})
print(result["messages"][-1].content)
# > User is John Smith.

 

ツールから短期メモリへの書き込み (更新)

実行中にエージェントの短期メモリ (状態) を変更するには、ツールから状態更新を直接返すことができます。

これは、中間結果を永続化したり、後に続くツールやプロンプトが情報にアクセスできるようにするために役立ちます

from typing import Annotated
from langchain_core.tools import InjectedToolCallId
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import ToolMessage
from langchain.agents import create_agent, AgentState
from langchain.agents.tool_node import InjectedState
from langgraph.runtime import get_runtime
from langgraph.types import Command
from pydantic import BaseModel

class CustomState(AgentState):
    user_name: str

class CustomContext(BaseModel):
    user_id: str

def update_user_info(
    tool_call_id: Annotated[str, InjectedToolCallId],
) -> Command:
    """Look up and update user info."""
    runtime = get_runtime(CustomContext)
    user_id = runtime.context.user_id
    name = "John Smith" if user_id == "user_123" else "Unknown user"
    return Command(update={
        "user_name": name,
        # update the message history
        "messages": [
            ToolMessage(
                "Successfully looked up user information",
                tool_call_id=tool_call_id
            )
        ]
    })

def greet(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """Use this to greet the user once you found their info."""
    user_name = state["user_name"]
    return f"Hello {user_name}!"

agent = create_agent(
    model="openai:gpt-5-nano",
    tools=[update_user_info, greet],
    state_schema=CustomState,
    context_schema=CustomContext,
)

agent.invoke(
    {"messages": [{"role": "user", "content": "greet the user"}]},
    context=CustomContext(user_id="user_123"),
)

 

プロンプト

エージェントの状態をプロンプト関数シグネチャに注入することで、動的プロンプト関数で短期メモリ (状態) にアクセスできます。

from langchain_core.messages import AnyMessage
from langchain.agents import create_agent, AgentState
from langgraph.runtime import get_runtime
from typing import TypedDict


class CustomContext(TypedDict):
    user_name: str


def get_weather(city: str) -> str:
    """Get the weather in a city."""
    return f"The weather in {city} is always sunny!"


def prompt(state: AgentState) -> list[AnyMessage]:
    user_name = get_runtime(CustomContext).context["user_name"]
    system_msg = f"You are a helpful assistant. Address the user as {user_name}."
    return [{"role": "system", "content": system_msg}] + state["messages"]


agent = create_agent(
    model="openai:gpt-5-nano",
    tools=[get_weather],
    prompt=prompt,
    context_schema=CustomContext,
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What is the weather in SF?"}]},
    context=CustomContext(user_name="John Smith"),
)
for msg in result["messages"]:
    msg.pretty_print()

出力例

================================ Human Message =================================

What is the weather in SF?
================================== Ai Message ==================================
Tool Calls:
  get_weather (call_WFQlOGn4b2yoJrv7cih342FG)
 Call ID: call_WFQlOGn4b2yoJrv7cih342FG
  Args:
    city: San Francisco
================================= Tool Message =================================
Name: get_weather

The weather in San Francisco is always sunny!
================================== Ai Message ==================================

Hi John Smith, the weather in San Francisco is always sunny!

 

以上