スキップしてメイン コンテンツに移動

AIに「記憶」を持たせる!LangChain Memory完全実装マニュアル

AIに「記憶」を持たせる!LangChain Memory完全実装マニュアル

はじめに

「さっきの話だけど...」とAIに話しかけても、「何の話ですか?」と返されてガッカリしたことはありませんか? LLMは基本的に一問一答(ステートレス)であり、過去のやり取りを覚えていません。

ユーザーと文脈を共有した自然な会話を実現するためには、アプリケーション側で「記憶(Memory)」を管理し、毎回プロンプトに過去ログを含めて送信する必要があります。LangChainは、この面倒な処理を劇的に簡単にする仕組みを提供しています。

LLMは基本ステートレス

Mechanism

Memoryの仕組みは実は単純です。毎回「過去の会話 + 新しい質問」をセットにしてLLMに送っているだけです。

しかし、会話が長くなるとトークン制限(入力文字数の上限)に引っかかったり、APIコストが嵩んだりします。そのため、「最新の10件だけ残す」「要約して保存する」といった戦略が必要になります。

ChatMessageHistoryの基本

まずは手動で履歴を管理する方法を見てみましょう。

from langchain_core.messages import HumanMessage, AIMessage
from langchain_community.chat_message_histories import ChatMessageHistory

history = ChatMessageHistory()

# 会話の追加
history.add_user_message("こんにちは!")
history.add_ai_message("こんにちは、何かお手伝いしましょうか?")

# 履歴の確認
print(history.messages)

これを llm.invoke(history.messages) のように渡せば、文脈を維持した回答が得られます。

RunnableWithMessageHistoryによる自動化

LangChain v0.2以降のモダンな書き方(LCEL)では、RunnableWithMessageHistory を使ってチェーン自体をラップし、セッションIDごとに履歴を自動管理させるのが一般的です。

from langchain_core.runnables.history import RunnableWithMessageHistory

# 履歴を取得する関数を定義(ここではインメモリだが、実際はDBから取得)
store = {}
def get_session_history(session_id: str):
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
)

# セッションIDを指定して実行
chain_with_history.invoke(
    {"input": "私の好きな色は赤です"},
    config={"configurable": {"session_id": "user_123"}}
)

次に同じセッションIDで「私の好きな色は?」と聞けば、「赤ですね」と答えてくれます。

履歴の保存(Redis/SQL)

メモリ上の履歴はプログラムを再起動すると消えます。永続化するには、RedisChatMessageHistoryPostgresChatMessageHistory を使います。

LangChain Communityパッケージには多くのDB用クラスが用意されており、わずか数行の変更で会話ログをデータベースに保存できるようになります。

まとめ

Memoryの実装は、チャットボット開発の肝です。まずは簡単なインメモリ管理から始めて、必要に応じてRedisなどの外部ストアへの移行を検討しましょう。

Legacy Alert

古い記事でよく見る `ConversationChain` は現在非推奨(Legacy)扱いになりつつあります。LCELベースの書き方(今回紹介した方法)に慣れておくことを強くお勧めします。

次回は、これまで断片的に登場してきた謎のパイプ記法 | 、すなわち「LCEL (LangChain Expression Language)」について徹底解説します。

このブログ記事はAIを利用して自動生成されました。

コメント