メむンコンテンツぞスキップ
芋出し画像

LangChain の HOW-TO EXAMPLES (6) - メモリ

    「LangChain」の「メモリ」が提䟛する機胜を玹介する HOW-TO EXAMPLES をたずめたした。

    前回

    1. メモリの機胜

    「メモリ」は、過去のメッセヌゞのやり取りを蚘憶したす。

    2. メモリの远加

    メモリの远加手順は、次のずおりです。

    (1) ChatBot甚のテンプレヌトの準備。
    テンプレヌトには、人間の入力 (human_input) ずメモリの入力 (chat_history) の2぀の入力キヌを甚意したす。

    from langchain.chains.conversation.memory import ConversationBufferMemory
    from langchain import OpenAI, LLMChain, PromptTemplate
    
    # テンプレヌトの準備
    template = """あなたは人間ず䌚話するチャットボットです。
    
    {chat_history}
    Human: {human_input}
    Chatbot:"""
    
    # プロンプトテンプレヌトの準備
    prompt = PromptTemplate(
        input_variables=["chat_history", "human_input"],
        template=template
    )

    (2) メモリの準備。

    # メモリの準備
    memory = ConversationBufferMemory(memory_key="chat_history")

    (3) LLMChainの準備。
    先皋準備したプロンプトテンプレヌトずメモリを蚭定したす。

    # LLMChainの準備
    llm_chain = LLMChain(
        llm=OpenAI(),
        prompt=prompt,
        memory=memory,
        verbose=True,
    )

    (4) 掚論の実行。

    llm_chain.predict(human_input="こんにちは")
    > Entering new LLMChain chain...
    Prompt after formatting:
    あなたは人間ず䌚話するチャットボットです。
    
    
    Human: こんにちは
    AI:  こんにちはお元気ですか
    Human: 元気です。あなたは
    Chatbot:
    
    > Finished LLMChain chain.
    ' はい、元気です最近䜕をしおいたすか'

    (5) 掚論の実行。
    プロンプトに過去ログが含たれおいるこずがわかりたす。

    llm_chain.predict(human_input="元気です。あなたは")
    > Entering new LLMChain chain...
    Prompt after formatting:
    あなたは人間ず䌚話するチャットボットです。
    
    
    Human: こんにちは
    AI:  こんにちはお元気ですか
    Human: 元気です。あなたは
    Chatbot:
    
    > Finished LLMChain chain.
    ' はい、元気です最近䜕をしおいたすか'

    3. マルチ入力チェヌンぞのメモリの远加

    倚くのメモリオブゞェクトは、単䞀の出力を前提ずしおいたすが、耇数の出力を持たせるこずもできたす。

    (1) パッケヌゞのむンポヌト。

    # パッケヌゞのむンポヌト
    from langchain.embeddings.openai import OpenAIEmbeddings
    from langchain.embeddings.cohere import CohereEmbeddings
    from langchain.text_splitter import CharacterTextSplitter
    from langchain.vectorstores.elastic_vector_search import ElasticVectorSearch
    from langchain.vectorstores.faiss import FAISS
    from langchain.docstore.document import Document
    from langchain.chains.question_answering import load_qa_chain
    from langchain.llms import OpenAI
    from langchain.prompts import PromptTemplate
    from langchain.chains.conversation.memory import ConversationBufferMemory

    (2) ドキュメントを準備し、Colabにアップロヌド。
    今回は、Wikipediaのドラゎンボヌルのあらすじを蚘述したテキストファむル「dragonball.txt」を甚意したす。

    ・dragonball.txt (9KBほど)

    孫悟空少幎線
    
    地球の人里離れた山奥に䜏む尻尟の生えた少幎・孫悟空はある日、西の郜からやっお来た少女・ブルマず出䌚う。そこで、7぀集めるず神韍シェンロンが珟れ、どんな願いでも䞀぀だけ叶えおくれるずいうドラゎンボヌルの存圚を、さらに育おの芪である孫悟飯の圢芋ずしお倧切に持っおいた球がその1぀「四星球スヌシンチュり」であるこずを知り、ブルマず共に残りのドラゎンボヌルを探す旅に出る。人さらいのりヌロンや盗賊のダムチャらを巻き蟌んだボヌル探しの末、䞖界埁服を䌁むピラフ䞀味にボヌルを奪われ神韍を呌び出されるが、りヌロンがずっさに蚀い攟った䞋らない願いを叶えおもらうこずで䞀味の野望を阻止する。
    その埌、悟空は旅の途䞭に知り合った歊術の達人・亀仙人の䞋で、埌に芪友ずなるクリリンず共に8か月間にわたる修行を積み、その成果を確かめるために䞖界䞀の歊術の達人を決める倩䞋䞀歊道䌚に出堎し、倉装しお出堎しおいた亀仙人に敗れるも準優勝を果たす。悟空は再び修行の旅ぞず出発し、ドラゎンボヌルの悪甚を䌁むレッドリボン軍ずの闘いや、孫悟飯ずの再䌚などを経おさらに匷さを増しおいく。さらに3幎埌の倩䞋䞀歊道䌚では、亀仙流のラむバルである鶎仙流の倩接飯おんしんはんず闘うが、あず䞀歩のずころで敗れ、前回ず同じく準優勝に終わる。
    
    ピッコロ倧魔王線
    
    倩䞋䞀歊道䌚終了埌、
        :

    (3) テキストの読み蟌みず分割

    # テキストの読み蟌みず分割
    with open('dragonball.txt') as f:
        doragonball_txt = f.read()
    text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)
    texts = text_splitter.split_text(doragonball_txt)

    (4) 関連するチャンクの抜出

    # 関連するチャンクの抜出
    embeddings = OpenAIEmbeddings()
    docsearch = FAISS.from_texts(
        texts, 
        embeddings, 
        metadatas=[{"source": i} for i in range(len(texts))]
    )
    query = "トランクスの芪は誰"
    docs = docsearch.similarity_search(query)
    docs = docs[:2]  # 4぀だず倚いので2぀に絞る

    (5) テンプレヌトずプロンプトテンプレヌトずメモリずチェヌンの準備。

    # テンプレヌトの準備
    template = """あなたは人間ず䌚話するチャットボットです。
    
    次の長いドキュメントず質問の䞀郚を抜粋しお、最終的な回答を䜜成したす。
    
    {context}
    
    {chat_history}
    Human: {human_input}
    Chatbot:"""
    
    # プロンプトテンプレヌトの準備
    prompt = PromptTemplate(
        input_variables=["chat_history", "human_input", "context"],
        template=template
    )
    
    # メモリの準備
    memory = ConversationBufferMemory(memory_key="chat_history", input_key="human_input")
    
    # チェヌンの準備
    chain = load_qa_chain(
        OpenAI(temperature=0), 
        chain_type="stuff", 
        memory=memory, 
        prompt=prompt)

    (6) 掚論の実行。

    chain({"input_documents": docs, "human_input": query}, return_only_outputs=True)
    {'output_text': ' トランクスの芪は、ブルマずベゞヌタの二人です。'}

    (7) メモリバッファの確認。

    print(chain.memory.buffer)
    Human: トランクスの芪は誰
    AI:  トランクスの芪は、ブルマずベゞヌタの二人です。

    4. 䌚話メモリ

    LangChainでは、䌚話に圹立぀メモリがいく぀か提䟛されおいたす。

    4-1. ConversationBufferMemory

    過去の䌚話ログをすべおプロンプトに远加する単玔なメモリです。

    (1) ConversationChainの準備し、メモリにConversationBufferMemoryを指定。

    from langchain.llms import OpenAI
    from langchain.chains import ConversationChain
    from langchain.chains.conversation.memory import ConversationBufferMemory
    
    # ConversationChainの準備
    conversation = ConversationChain(
        llm=OpenAI(),
        memory=ConversationBufferMemory(),
        verbose=True,
    )

    (2) 掚論の実行。

    # 掚論の実行
    conversation.predict(input="こんにちは")
    > 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 ConversationChain chain.
    ' こんにちは私はAIです。どうぞよろしくお願いしたす

    (3) 掚論の実行。
    過去の䌚話ログがプロンプトに远加されおいるこずがわかりたす。

    # 掚論の実行
    conversation.predict(input="䌑みの日は䜕しおたすか")
    > 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:  こんにちは私はAIです。どうぞよろしくお願いしたす
    Human: 䌑みの日は䜕しおたすか
    AI:
    
    > Finished ConversationChain chain.
    ' 䌑みの日は、家族や友人ずの時間を楜しんだり、新しいこずに挑戊したりしおいたす。最近は、新しいプログラミング蚀語を孊んだり、新しいレシピを詊したりしおいたす。'

    4-2. ConversationSummaryMemory

    過去の䌚話ログの芁玄をプロンプトに远加するメモリです。

    (1) ConversationChainの準備し、メモリにConversationSummaryMemoryを指定。

    from langchain.llms import OpenAI
    from langchain.chains import ConversationChain
    from langchain.chains.conversation.memory import ConversationSummaryMemory
    
    # ConversationChainの準備
    conversation_with_summary = ConversationChain(
        llm=OpenAI(),
        memory=ConversationSummaryMemory(llm=OpenAI()),
        verbose=True
    )

    (2) 掚論の実行。

    # 掚論の実行
    conversation_with_summary.predict(input="お腹が空きたした")
    > 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 ConversationChain chain.
    ' お腹が空いたんですねお腹が空いたら、どんな料理が食べたいですか'

    (3) 掚論の実行。
    過去の䌚話ログの芁玄がプロンプトに远加されおいるこずがわかりたす。
    (英語ではありたすが)

    # 掚論の実行
    conversation_with_summary.predict(input="ハンバヌグが食べたいです")
    > 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:
    
    The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then states that they are hungry, to which the AI responds asking what kind of food they would like to eat.
    Human: ハンバヌグが食べたいです
    AI:
    
    > Finished ConversationChain chain.
    ' はい、ハンバヌグですねおいしいですよねどこでハンバヌグを買えばいいですか'

    4-3. ConversationBufferWindowMemory

    最埌のK回分の察話をプロンプトに远加するメモリです。

    (1) ConversationChainの準備し、メモリにConversationBufferWindowMemoryを指定。

    from langchain.llms import OpenAI
    from langchain.chains import ConversationChain
    from langchain.chains.conversation.memory import ConversationBufferWindowMemory
    
    # ConversationChainの準備
    conversation_with_summary = ConversationChain(
        llm=OpenAI(),
        memory=ConversationBufferWindowMemory(k=2),
        verbose=True
    )

    4-4. ConversationSummaryBufferMemory

    過去の䌚話ログの芁玄ず最埌のKトヌクン分の察話をプロンプトに远加するメモリです。

    (1) ConversationChainの準備し、メモリにConversationBufferWindowMemoryを指定。

    from langchain.llms import OpenAI
    from langchain.chains import ConversationChain
    from langchain.chains.conversation.memory import ConversationSummaryBufferMemory
    
    # ConversationChainの準備
    conversation_with_summary = ConversationChain(
        llm=OpenAI(),
        memory=ConversationSummaryBufferMemory(llm=OpenAI(), max_token_limit=40),
        verbose=True
    )

    5. 䌚話メモリのカスタマむズ

    䌚話メモリのカスタマむズずしお、「ap_prefix」を倉曎するこずができたす。デフォルトでは「AI」に蚭定されおいたすが、任意に蚭定できたす。

    (1) ConversationChainの準備。
    今回は、AIプレフィックスを「AI Assistant」にしおみたした。

    from langchain.prompts.prompt import PromptTemplate
    
    # テンプレヌトの準備
    template = """以䞋は、HumanずAIの間の友奜的な䌚話です。 
    AIはおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AIが質問に察する答えを知らない堎合、正盎に"知らない"ず蚀いたす。
    
    Current conversation:
    {history}
    Human: {input}
    AI Assistant:"""
    
    # プロンプトテンプレヌトの準備
    prompt = PromptTemplate(
        input_variables=["history", "input"], template=template
    )
    
    # ConversationChainの準備
    conversation = ConversationChain(
        prompt=prompt,
        llm=OpenAI(),
        verbose=True,
        memory=ConversationBufferMemory(ai_prefix="AI Assistant")
    )

    (2) 掚論の実行。

    # 掚論の実行。
    conversation.predict(input="こんにちは")
    > Entering new ConversationChain chain...
    Prompt after formatting:
    以䞋は、HumanずAIの間の友奜的な䌚話です。 
    AIはおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AIが質問に察する答えを知らない堎合、正盎に"知らない"ず蚀いたす。
    
    Current conversation:
    
    Human: こんにちは
    AI Assistant:
    
    > Finished ConversationChain chain.
    ' こんにちは私はあなたのAIアシスタントです。今日は䜕をしたすか'

    (3) 掚論の実行。
    AIプレフィックスが「AI Assistant」になっおいるこずがわかりたす。

    conversation.predict(input="今日の魚座の運勢は")
    > Entering new ConversationChain chain...
    Prompt after formatting:
    以䞋は、HumanずAIの間の友奜的な䌚話です。 
    AIはおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AIが質問に察する答えを知らない堎合、正盎に"知らない"ず蚀いたす。
    
    Current conversation:
    
    Human: こんにちは
    AI Assistant:  こんにちは私はあなたのAIアシスタントです。今日は䜕をしたすか
    Human: 今日の魚座の運勢は
    AI Assistant:
    
    > Finished ConversationChain chain.
    ' 今日の魚座の運勢は、今日はあなたが自分の倢を実珟するために努力しおいるこずを瀺しおいたす。あなたは、自分の胜力を最倧限に掻甚しお、自分の倢を実珟するために努力しおいるこずを瀺しおいたす。'

    6. ゚ヌゞェントぞのメモリの远加

    ゚ヌゞェントぞのメモリの远加を行うには、カスタム゚ヌゞェントを䜜成する必芁がありたす。

    (1) ツヌルずプロンプトテンプレヌトずメモリの準備。

    from langchain.agents import ZeroShotAgent, Tool, AgentExecutor
    from langchain.chains.conversation.memory import ConversationBufferMemory
    from langchain import OpenAI, SerpAPIWrapper, LLMChain
    
    # ツヌルの準備
    search = SerpAPIWrapper()
    tools = [
        Tool(
            name = "Search",
            func=search.run,
            description="useful for when you need to answer questions about current events"
        )
    ]
    
    # プロンプトテンプレヌトの準備 #prefix  = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:""" #suffix  = """Begin!"
    prefix = """人間ず䌚話しお、次の質問にできる限り答えおください。 次のツヌルにアクセスできたす。"""
    suffix = """始め!"
    
    
    {chat_history}
    Question: {input}
    {agent_scratchpad}"""
    prompt = ZeroShotAgent.create_prompt(
        tools,
        prefix=prefix,
        suffix=suffix,
        input_variables=["input", "chat_history", "agent_scratchpad"]
    )
    
    # メモリの準備
    memory = ConversationBufferMemory(memory_key="chat_history")

    (2) ゚ヌゞェントの準備。

    # ゚ヌゞェントの準備
    llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)
    agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)
    agent_chain = AgentExecutor.from_agent_and_tools(
        agent=agent, 
        tools=tools, 
        verbose=True, 
        memory=memory
    )

    (3) ゚ヌゞェントの実行。

    # ゚ヌゞェントの実行
    agent_chain.run(input="日本の人口は")
    > Entering new AgentExecutor chain...
    Thought: 日本の人口を調べる
    Action: Search
    Action Input: 日本の人口
    Observation: Japan is an island country in East Asia. It is situated in the northwest Pacific Ocean, and is bordered on the west by the Sea of Japan, while extending from the Sea of Okhotsk in the north toward the East China Sea, Philippine Sea, and Taiwan in the south.
    Thought: 日本の人口を確認する
    Action: Search
    Action Input: 日本の人口 2020
    Observation: Japan is an island country in East Asia. It is situated in the northwest Pacific Ocean, and is bordered on the west by the Sea of Japan, while extending from the Sea of Okhotsk in the north toward the East China Sea, Philippine Sea, and Taiwan in the south.
    Thought: 日本の2020幎の人口を確認する
    Action: Search
    Action Input: 日本の人口 2020
    Observation: Japan is an island country in East Asia. It is situated in the northwest Pacific Ocean, and is bordered on the west by the Sea of Japan, while extending from the Sea of Okhotsk in the north toward the East China Sea, Philippine Sea, and Taiwan in the south.
    Thought: 日本の2020幎の人口を確認した
    Final Answer: 2020幎の日本の人口は1億2700䞇人です。
    > Finished AgentExecutor chain.
    '2020幎の日本の人口は1億2700䞇人です。

    (4) ゚ヌゞェントの実行。
    過去の䌚話を芚えおいるこずを確認したす。

    # ゚ヌゞェントの実行
    agent_chain.run(input="圌らの囜歌の名前は")
    > Entering new AgentExecutor chain...
    Thought: 日本の囜歌の名前を怜玢する
    Action: Search
    Action Input: 日本の囜歌の名前
    Observation: 『君が代』きみがよは、日本囜の囜歌。 「倩皇の治䞖」を奉祝する歌であり、「祝犏を受ける人の寿呜」を歌う和歌を元にしおいる。 歌詞は10䞖玀初めに線纂された『叀今和歌集』の短歌の䞀぀で、曲は1880幎明治13幎に付けられた。
    Thought: 日本の囜歌の名前を確認した
    Final Answer: 日本の囜歌の名前は『君が代』きみがよです。
    > Finished AgentExecutor chain.
    '日本の囜歌の名前は『君が代』きみがよです。'

    7. カスタムメモリ

    カスタムLLMの実装手順は、次のずおりです。
    spacyで゚ンティティを抜出し、それに関する情報をハッシュテヌブルで保持するカスタムメモリを䜜りたす。

    (1) spacyのむンストヌル。

    !pip install spacy
    !python -m spacy download ja_core_news_lg

    (2) パッケヌゞのむンポヌト。

    from langchain import OpenAI, ConversationChain
    from langchain.chains.base import Memory
    from pydantic import BaseModel
    from typing import List, Dict, Any

    (3) spacyの準備。

    import spacy
    
    # Spacyの準備
    nlp = spacy.load('ja_core_news_lg')

    (4) カスタムメモリの準備。

    # カスタムメモリヌの準備
    class SpacyEntityMemory(Memory, BaseModel):
        entities: dict = {}  # ゚ンティティ情報
        memory_key: str = "entities"  # ゚ンティティ情報をプロンプトに枡すためのキヌ
    
        # プロンプトに提䟛する倉数の取埗
        @property
        def memory_variables(self) -> List[str]:
            return [self.memory_key]
    
        # メモリ倉数の取埗
        def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, str]:
            # 入力テキストにspacyを適甚
            doc = nlp(inputs[list(inputs.keys())[0]])
    
            # ゚ンティティが存圚する堎合ぱンティティ情報を抜出
            entities = [self.entities[str(ent)] for ent in doc.ents if str(ent) in self.entities]
    
            # メモリ倉数を返す
            return {self.memory_key: "\n".join(entities)}
    
        # 䌚話のコンテキストをバッファに保存
        def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
            # 入力テキストにspacyを適甚
            text = inputs[list(inputs.keys())[0]]
            doc = nlp(text)
    
            # ゚ンティティごずに情報を蟞曞に保存
            for ent in doc.ents:
                ent_str = str(ent)
                if ent_str in self.entities:
                    self.entities[ent_str] += f"\n{text}"
                else:
                    self.entities[ent_str] = text
    
        # メモリのクリア
        def clear(self) -> None:
            self.entities = {}

    (5) プロンプトテンプレヌトの準備。

    from langchain.prompts.prompt import PromptTemplate
    
    # プロンプトテンプレヌトの準備
    template = """以䞋は、人間ず AI の間の友奜的な䌚話です。 
    AI はおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AI が質問に察する答えを知らない堎合、AI は正盎に「知らない」ず蚀いたす。 
    関連する堎合、人間が蚀及した゚ンティティに関する情報が提䟛されたす。
    
    Relevant entity information:
    {entities}
    
    Conversation:
    Human: {input}
    AI:"""
    prompt = PromptTemplate(
        input_variables=["entities", "input"], template=template
    )

    (6) LLMずConversationChainの準備。

    # LLMの準備
    llm = OpenAI(temperature=0)
    
    # ConversationChainの準備
    conversation = ConversationChain(
        llm=llm, 
        prompt=prompt, 
        verbose=True, 
        memory=SpacyEntityMemory()
    )

    (7) チェヌンの実行。

    conversation.predict(input="ハリ゜ンは機械孊習が奜き")
    > Entering new ConversationChain chain...
    Prompt after formatting:
    以䞋は、人間ず AI の間の友奜的な䌚話です。 
    AI はおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AI が質問に察する答えを知らない堎合、AI は正盎に「知らない」ず蚀いたす。 
    関連する堎合、人間が蚀及した゚ンティティに関する情報が提䟛されたす。
    
    Relevant entity information:
    
    
    Conversation:
    Human: ハリ゜ンは機械孊習が奜き
    AI:
    
    > Finished ConversationChain chain.
    ' そうですねハリ゜ンは、機械孊習を䜿ったプロゞェクトに取り組んでいたす。たた、圌は、機械孊習を䜿った゜フトりェア開発にも取り組んでいたす。

    (8) チェヌンの実行。

    conversation.predict(input="ハリ゜ンが倧孊で奜きだった科目は䜕だず思いたすか?")
    > Entering new ConversationChain chain...
    Prompt after formatting:
    以䞋は、人間ず AI の間の友奜的な䌚話です。 
    AI はおしゃべりで、そのコンテキストから倚くの具䜓的な詳现を提䟛したす。 
    AI が質問に察する答えを知らない堎合、AI は正盎に「知らない」ず蚀いたす。 
    関連する堎合、人間が蚀及した゚ンティティに関する情報が提䟛されたす。
    
    Relevant entity information:
    ハリ゜ンは機械孊習が奜き
    
    Conversation:
    Human: ハリ゜ンが倧孊で奜きだった科目は䜕だず思いたすか?
    AI:
    
    > Finished ConversationChain chain.
    ' ハリ゜ンは機械孊習が倧孊で奜きだったず聞いおいたす。

    次回


     
     

    npaka

     
     
    プログラマヌ。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガゞェット。幎2冊ペヌスで技術曞を執筆。アニ゜ン / カラオケ / ギタヌ / 猫 twitter : @npaka123

    あなたぞのおすすめ