Langchain Memoly機能について


はじめに

本日はLngchainの6つの機能のうちの1つであるMemoryについて解説していきます。

Memoryとは

Memoryは、チェインズやエージェントの内部における状態を保持する機能です。基本的に、チャットモデルの中でのチャットのやり取りは性的化されており、勝手にデータが保持されることはありません。チャットGPTのwebサービスのように、特定のチャット内での過去のチャットを引き継ぎたい場合は、Pythonの配列などに保存する必要があります。また、特定のチェーンの中でのチャット履歴を次のチェーンに引き継ぎたい場合も、その情報を渡す処理を書く必要があります。このような処理を短いコードで記述しやすくしてくれたツールがラングチェインメモリーです。

Langchain Memoryの機能

Langchain Memoryの機能を理解する上で知っておくべき概念は主に3つあります。

Chat Message History

Chat Message Historyは、チャットの履歴データを管理するための機能です。具体的には、HumanMessageやAIMessageなどが格納された配列の操作をしやすくしてくれる便利ツールです。

Simple Memory

Simple Memoryは、チェーン上で最初に設定できる共有メモリーの機能です。時間や場所などの前提情報を格納することができ、SequentialChainのインスタンスを作成するときにSimple Memoryを紐付けると、そのSequentialChainの中のすべてのチェーンにおいて前提情報となる変数を用いることができます。

Buffer Memory

Buffer Memoryは、ChainsやAgents上における履歴データの共有メモリーの機能です。Buffer Memoryのメモリ空間上の履歴データは、ChatMessageHistoryのクラスをベースにしています。このメモリ空間上のデータをチェイン図鑑やエージェント間で受け渡す機能が加わったChatMessageHistoryをパッケージしたクラスと解釈することもできます。

Pythonを用いた具体的な実装方法

環境構築

ラングチェインを用いるためには、まずpip install langchainを実行してインストールします。

!pip3 install langchain openai

また、OpenAIのモデルを用いるためにはpip install openaiを実行します。ライブラリのインストールが完了したら、OpenAIのAPIキーを設定します。

import os

os.environ["OPENAI_API_KEY"] = "your openai_API_key"

1. Chat Message Historyの使い方

ChatMessageHistoryというクラスからインスタンスを作成し、add_user_messageというメソッドによってHumanMessageを、add_ai_messageというメソッドによってAIMessageを追加できます。

それでは実行してみましょう。

from langchain.memory import ChatMessageHistory
from langchain.schema import messages_to_dict
from langchain.schema import messages_from_dict

history = ChatMessageHistory()

history.add_user_message("マッチョとは何?")
history.add_ai_message("マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。")

message_list = history.messages
message_dict = messages_to_dict(message_list)
print(message_list)
print(message_dict)

message_list_from_dict = messages_from_dict(message_dict)
print(message_list_from_dict)

これに対する実行結果は次の通りです。

[HumanMessage(content='マッチョとは何?', additional_kwargs={}, example=False), AIMessage(content='マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。', additional_kwargs={}, example=False)]
[{'type': 'human', 'data': {'content': 'マッチョとは何?', 'additional_kwargs': {}, 'example': False}}, {'type': 'ai', 'data': {'content': 'マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。', 'additional_kwargs': {}, 'example': False}}]
[HumanMessage(content='マッチョとは何?', additional_kwargs={}, example=False), AIMessage(content='マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。', additional_kwargs={}, example=False)]

実行した結果、「messages」という属性の中にこのメッセージのリストが格納されています。

また、これはmessages_to_dictとmessages_from_dictというメソッドを用いて、配列と辞書型のデータを双方向に変換可能となっております。

2. Simple Memoryの使い方

複数の引数と返り値を用いることができるSequentialChainのSequentialChainの中に、SimpleMemoryというインスタンスを格納しています。

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import SequentialChain
from langchain.memory import SimpleMemory

llm = OpenAI(model_name="text-davinci-003")
prompt_1 = PromptTemplate(
    input_variables=["adjective", "subject", "age"],
    template="{age}において、{adjective}{subject}に一番オススメの方法は?:",
)
chain_1 = LLMChain(llm=llm, prompt=prompt_1, output_key="muscle_road")

prompt_2 = PromptTemplate(
    input_variables=["muscle_road", "age"],
    template="現在は{age}だとします。{muscle_road}を学ぶためにやるべきことを3ステップで100文字で教えて。",
)
chain_2 = LLMChain(llm=llm, prompt=prompt_2, output_key="training_step")

overall_chain = SequentialChain(
    chains=[chain_1, chain_2],
    input_variables=["adjective", "subject"],
    output_variables=["muscle_road", "training_step"],
    verbose=True,
    memory=SimpleMemory(memories={"age": "80歳"}),
)
output = overall_chain({
    "adjective": "ゴリゴリの",
    "subject": "マッチョマン",
})
print(output)

これに対する実行結果は次の通りです。

> Entering new SequentialChain chain...

> Finished chain.
{'adjective': 'ゴリゴリの', 'subject': 'マッチョマン', 'age': '80歳', 'muscle_road': '\n\n80歳において、ゴリゴリのマッチョマンに一番オススメの方法は、健康的な食事と運動です。それぞれの領域を重視し、バランスよく行う必要があります。健康的な食事では、たんぱく質や脂質を含む栄養素を摂取する必要があります。その他にビタミン、ミネラル、食物繊維なども摂取する必要があります。また、運動も重視して行う必要があります。ウォーキングやジョギングなどの軽い運動を', 'training_step': '\n\n1.まずは安全な場所でストレッチをしましょう。ストレッチを行うことで、身体の柔軟性を高め、腰痛や肩こりなどの痛みを和らげることができます。\n\n2.次に、ゆっくりと歩き始めましょう。歩くスピードを慢めにすることで、音楽などを聴きながら心地よく歩くことができます。\n\n3.最後に、時間をかけてゆっくりと走り始めましょう。走るスピードを少しずつ上げながら、心拍'}

このように、「age」という変数が使われたプロンプトが無事に実行できていることがわかります。

3. Buffer Memoryの使い方

続いてチャットに特化したConversationBufferMemoryを紹介します。

ConversationBufferMemoryのインスタンスは、save_contextというメソッドによって入力と出力を手動で書き込んだり、load_memory_variablesというメソッドによって読み込むこともできます。

こちらを実行してみましょう。

from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()

memory.save_context(
    {"input": "マッチョとは何?"},
    {"output": "マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。"},
)
print(memory.load_memory_variables({}))

これに対する実行結果は次の通りです。

{'history': 'Human: マッチョとは何?\nAI: マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。'}

実行した結果、チャットの履歴が文字列になって出力されます。

もしもこちらを文字列ではなく、メッセージのリストにしたい場合は「return_messages=True」に設定し実行してみましょう。

from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(return_messages=True)

memory.save_context(
    {"input": "マッチョとは何"},
    {"output": "マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。"},
)
print(memory.load_memory_variables({}))

これに対する実行結果は次の通りです。

{'history': [HumanMessage(content='マッチョとは何', additional_kwargs={}, example=False), AIMessage(content='マッチョとは男性がもつという「強靱さ、逞しさ、勇敢さ、好戦性」といった性質を基礎とした思想や信条、行動をあらわす言葉です。', additional_kwargs={}, example=False)]}

実行した結果、HumanMessageやAIMessageなどのメッセージのリストを格納することを確認することができました。

そして、このConversationBufferMemoryは、ConversationChainのインスタンスを作るときに設定すると、それ以降のチャットをすべて自動保存してくれます。

それでは実行してみましょう。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain

llm = OpenAI(model_name="text-davinci-003")
conversation = ConversationChain(
    llm=llm,
    verbose=True,
    memory=ConversationBufferMemory()
)

conversation("マッチョとは何?")

これに対する実行結果は次の通りです。



> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:

Human: マッチョとは何?
AI:

> Finished chain.
{'input': 'マッチョとは何?',
 'history': '',
 'response': ' マッチョとは、外見上、健康的な体を持つ男性のことです。例えば、筋肉質な体をしている人を指します。'}

ここでは、「マッチョとは何か?」と質問して「マッチョとは、外見上、健康的な体を持つ男性のことです。例えば、筋肉質な体をしている人を指します。」と返答が返ってきました。

そして、この後に「より詳しく教えて。」と入力してみましょう。

conversation("より詳しく教えて。")

これに対する実行結果は次の通りです。


> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
Human: マッチョとは何?
AI:  マッチョとは、外見上、健康的な体を持つ男性のことです。例えば、筋肉質な体をしている人を指します。
Human: より詳しく教えて。
AI:

> Finished chain.
{'input': 'より詳しく教えて。',
 'history': 'Human: マッチョとは何?\nAI:  マッチョとは、外見上、健康的な体を持つ男性のことです。例えば、筋肉質な体をしている人を指します。',
 'response': ' マッチョの基本的な特徴として、健康的な体を持つことが挙げられます。健康的な体とは、体重が正常値の範囲内にあり、筋肉が豊富なことを指します。マッチョの外見の特徴は、腹筋、胸筋、腕筋、脚筋などが強調されることです。'}

すると、一つ前の「マッチョとは何?」というチャットを保持した状態で回答を生成してくれます。

最後に

次回はAgentsAgents機能について解説しますので引き続きよろしくお願いします!

この記事が気に入ったらサポートをしてみませんか?