03-langchain-conversational-memory

2024. 1. 11. 19:02langchain

대화 기억

대화 기억은 챗봇이 채팅과 같은 방식으로 우리의 질문에 응답할 수 있는 방법입니다. 이는 일관된 대화를 가능하게 하며, 이것이 없으면 모든 쿼리는 과거 상호 작용을 고려하지 않고 완전히 독립적인 입력으로 처리됩니다.

메모리를 사용하면 "에이전트"가 사용자와의 이전 상호 작용을 기억할 수 있습니다. 기본적으로 에이전트는 상태 비저장입니다. 즉, 들어오는 각 쿼리는 다른 상호 작용과 독립적으로 처리됩니다. 상태 비저장 에이전트에 존재하는 유일한 것은 현재 입력이며 다른 것은 아무것도 없습니다.

In [ ]:
!python3 -m venv langchain
!source langchain/bin/activate
!pip install langchain openai tiktoken
In [ ]:
import inspect

from getpass import getpass
from langchain import OpenAI
from langchain.chains import LLMChain, ConversationChain
from langchain.chains.conversation.memory import (ConversationBufferMemory, 
                                                  ConversationSummaryMemory, 
                                                  ConversationBufferWindowMemory,
                                                  ConversationKGMemory)
from langchain.callbacks import get_openai_callback
import tiktoken

메시지가 표시되면 openai api 키를 입력하기만 하면 됩니다.

In [ ]:
OPENAI_API_KEY = getpass()
In [ ]:
llm = OpenAI(
    temperature=0, 
    openai_api_key=OPENAI_API_KEY,
    model_name='text-davinci-003'  # can be used with llms like 'gpt-3.5-turbo'
)

나중에 count_tokens 유틸리티 함수를 활용해 보겠습니다. 이를 통해 각 호출에 사용되는 토큰 수를 계산할 수 있습니다.

In [ ]:
# def count_tokens(chain, query):             # 어떤 이유인지는 모르겠으나 count_tokens를 뒤에서 호출하면 에러가 남
#    with get_openai_callback() as cb:
#        result = chain.run(query)
#        print(f'Spent a total of {cb.total_tokens} tokens')
#
#    return result

이제 대화 기억에 대해 살펴보겠습니다.

기억이란 무엇입니까?

정의: 기억은 사용자와의 이전 상호작용을 기억하는 에이전트의 능력입니다(챗봇을 생각해 보세요)

기억의 공식적인 정의는 다음과 같습니다.

기본적으로 체인과 에이전트는 상태 비저장입니다. 즉, 들어오는 각 쿼리를 독립적으로 처리합니다. 일부 애플리케이션(챗봇이 좋은 예임)에서는 단기적으로나 장기적으로 이전 상호 작용을 기억하는 것이 매우 중요합니다. 바로 이를 위해 '메모리'라는 개념이 존재합니다.
라이브러리가 제공하는 다양한 메모리 모듈을 자세히 살펴보기 전에 이러한 예에 사용할 체인인 ConversationChain.을 소개하겠습니다.

In [ ]:
conversation = ConversationChain(
    llm=llm, 
)
In [ ]:
print(conversation.prompt.template)

이 체인의 메시지는 사용자와 채팅하고 진실된 답변을 제공하도록 지시하는 것입니다.

In [ ]:
print(inspect.getsource(conversation._call), inspect.getsource(conversation.apply))

여기서는 정말 마법 같은 일이 벌어지지 않고 LLM을 통과하는 간단한 과정만 거치면 됩니다. 실제로 이 체인은 수정 없이 LLMChain에서 직접 다음 메소드를 상속합니다.

In [ ]:
print(inspect.getsource(LLMChain._call), inspect.getsource(LLMChain.apply))

따라서 기본적으로 이 체인은 사용자의 입력과 대화 기록을 결합하여 의미 있는(그리고 진실되기를 바라는) 응답을 생성합니다.

이제 우리가 사용할 체인의 기본 사항을 이해했으므로 메모리에 들어갈 수 있습니다.

메모리 유형

메모리 유형 #1: ConversationBufferMemory

ConversationBufferMemory은 이름에서 알 수 있듯이 프롬프트에서 컨텍스트의 일부로 이전 대화에서 발췌한 버퍼를 유지합니다.

주요 기능: ConversationBufferMemory는 이전 대화 부분을 완전히 수정되지 않은 원시 형식으로 유지합니다.

In [ ]:
conversation_buf = ConversationChain(
    llm =llm,
    memory= ConversationBufferMemory()
)

ConversationBufferMemory 사용자 프롬프트를 전달합니다.

In [ ]:
print(conversation_buf.memory.buffer)

Memory type #2: ConversationSummaryMemory

In [ ]:
conversation_sum = ConversationChain(
    llm=llm, 
    memory=ConversationSummaryMemory(llm=llm)
)
In [ ]:
print(conversation_sum.memory.prompt.template)

주요 기능: 대화 요약 메모리는 이전 대화를 요약된 형식으로 유지하며 LLM이 요약을 수행합니다.

이 경우 요약 기능을 강화하기 위해 llm을 메모리 생성자에 보내야 합니다.

In [ ]:
conversation_sum = ConversationChain(
    llm =llm, 
    memory= ConversationSummaryMemory(llm=llm )
)

LLM이 있으면 항상 프롬프트가 표시됩니다.

In [ ]:
print(conversation_sum.memory.prompt.template)

각각의 새로운 상호 작용은 요약되어 체인의 메모리로 실행 중인 요약에 추가됩니다.

In [ ]:
print(conversation_sum.memory.buffer)

다음과 같이 tiktoken 토크나이저를 사용하여 OpenAI를 호출하지 않고 사용 중인 토큰 수를 계산할 수 있습니다.

In [ ]:
# initialize tokenizer
tokenizer = tiktoken.encoding_for_model('text-davinci-003')

# show number of tokens for the memory used by each memory type
print(
    f'Buffer memory conversation length: {len(tokenizer.encode(conversation_buf.memory.buffer))}\n'
    f'Summary memory conversation length: {len(tokenizer.encode(conversation_sum.memory.buffer))}'
)

참고 사항: text-davinci-003 및 gpt-3.5-turbo 모델 프롬프트와 답변 사이의 최대 토큰 수는 4096개입니다.

메모리 유형 #3: ConversationBufferWindowMemory

마지막 상호 작용 중 일부를 기억에 유지하지만 의도적으로 가장 오래된 상호 작용을 삭제하는 것입니다.
주요 기능: 대화 버퍼 창 메모리는 대화의 최신 부분을 원시 형식으로 유지합니다

In [ ]:
conversation_bufw = ConversationChain(
    llm=llm, 
    memory=ConversationBufferWindowMemory(k=1)
)
In [ ]:
bufw_history = conversation_bufw.memory.load_memory_variables(
    inputs=[]
)['history']
In [ ]:
print(bufw_history)
In [ ]:
print(
    f'Buffer memory conversation length: {len(tokenizer.encode(conversation_buf.memory.buffer))}\n'
    f'Summary memory conversation length: {len(tokenizer.encode(conversation_sum.memory.buffer))}\n'
    f'Buffer window memory conversation length: {len(tokenizer.encode(bufw_history))}'
)

더 많은 메모리 유형!

ConversationSummaryBufferMemory

주요 기능: 대화 요약 메모리는 가장 초기 대화 부분의 요약을 유지하는 동시에 최신 상호작용에 대한 원시 기억을 유지합니다.

onversation knowledge graph memory

이것은 최근 도입된 매우 멋진 메모리 유형입니다. 이는 서로 다른 개체를 인식하고 이를 술어와 쌍으로 연결하여 (주어, 술어, 목적어) 삼중항을 생성하는 지식 그래프 개념을 기반으로 합니다. . 이를 통해 많은 정보를 모델에 컨텍스트로 제공할 수 있는 매우 중요한 조각으로 압축할 수 있습니다.
주요 기능: 대화 지식 그래프 메모리는 의미론적 관계와 함께 상호작용에서 언급된 모든 항목에 대한 지식 그래프를 유지합니다.

In [ ]:
# you may need to install this library
!pip install -qU networkx
In [ ]:
conversation_kg = ConversationChain(
    llm=llm, 
    memory=ConversationKGMemory(llm=llm)
)
In [ ]:
conversation_kg.memory.kg.get_triples()
ConversationEntityMemory

주요 기능: 대화 항목 메모리는 특정 속성과 함께 언급된 주요 항목을 기억합니다.

기억으로 또 무엇을 할 수 있나요?

langchain의 메모리를 사용하여 할 수 있는 몇 가지 멋진 작업이 있습니다. 우리는 다음을 수행할 수 있습니다.

  • 자체 맞춤형 메모리 모듈 구현
  • 동일한 체인에 여러 메모리 모듈 사용
  • 에이전트를 메모리 및 기타 도구와 결합
In [ ]:
출처 : https://github.com/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/03-langchain-conversational-memory.ipynb

'langchain' 카테고리의 다른 글

04-langchain-chat  (0) 2024.01.13
03a-token-counter  (0) 2024.01.12
02-langchain-체인  (1) 2024.01.10
01-langchain-prompt-templates  (1) 2024.01.09
00-langchain-intro(한글)  (0) 2024.01.08