
챗봇이면 이전 대화를 기억해야된다. 그에 관련한 메모리 내용이다.
저장하는 메모리에도 종류가 여러가지 있음. 목적별로 다른 종류의 메모리를 랭체인에 탑재해야함.
'ConversationBufferMemory'을 사용하면 이전의 모든 메시지를 저장한 다음 다음 출력 변수로 사용하여 메시지를 추출할 수 있습니다. 채팅 내용이 길어질 수록 메모리가 무한하게 커지고 비용이 크게 부과되어 비효율적이 된다는 단점이 있습니다.
from langchain.memory import ConversationBufferMemory
# 메모리 관련 API 사용법은 동일하다. initializing -> save_context -> load_memory_variables
# init시 휴먼과 로봇의 대화체로 리턴하고 싶다면 return_messages=True, History (string)으로 반환하고 싶다면 False 적어주기
memory = ConversationBufferMemory(return_messages=True)
memory.save_context({'input':"Hi i am single"},{'output:': "hi human. i am Ai assistant!"})
memory.load_memory_variables({})
시간에 따른 대화 목록을 메모리에 유지합니다. 지정한 K개의 갯수만큼 마지막으로부터 기억합니다. 이는 버퍼가 너무 커져 비용이 많이 들지 않도록 가장 최근 상호 작용의 채팅 셋을 유지하는 데 유용할 수 있습니다.
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(return_messages=True, k=3)
def add_messages(input, output):
memory.save_context({"input":input}, {"output": output})
add_messages(1,1)
add_messages(2,2)
add_messages(3,3)
add_messages(4,4)
memory.load_memory_variables({}) # 1번 대화를 메모리에 저장하지 않는 것을 확인할 수 있다.
시간이 지남에 따라 대화의 요약을 만듭니다. 시간이 지남에 따라 대화의 정보를 압축하는 데 유용합니다. 대화 요약 메모리는 대화가 진행되는 대로 요약하고 현재 요약 내용을 메모리에 저장합니다. 그러면 이 메모리를 사용하여 지금까지의 대화의 요약 내용을 프롬프트/체인에 삽입할 수 있습니다. 이 메모리는 토큰을 너무 많이 차지하는 긴 대화에 요긴하게 쓰입니다
from langchain.memory import ConversationSummaryMemory # Conversation summarizer to chat memory
from langchain.chat_models import ChatOpenAI
summary_llm = ChatOpenAI(temperature=0.1) # ConversationSummaryMemory은 llm 사용이 필요하다
memory = ConversationSummaryMemory(llm=summary_llm)
def add_messages(input, output):
memory.save_context({"input":input}, {"output": output})
def get_history():
return memory.load_memory_variables({})
add_messages("Hi I'm Nicolas, I live in South Korea", "Wow that is so cool!")
add_messages("South Kddorea is so pretty", "I wish I could go!!!")
get_history()
메모리에 최근 상호 작용의 버퍼를 저장하고 있지만 오래된 상호 작용을 완전히 저장하는 것이 아니라 요약으로 컴파일하여 저장합니다. 저장할 시점을 결정하기 위해 대화 세트 수가 아니라 토큰 길이를 사용합니다.
(근데 왜인지 모르게 얘만 SSL 에러 나옴)
from langchain.memory import ConversationSummaryBufferMemory
from langchain.chat_models import ChatOpenAI
summary_llm = ChatOpenAI(temperature=0.1)
memory = ConversationSummaryBufferMemory(
llm=summary_llm,
max_token_limit=100,
return_messages=True
)
def add_messages(input, output):
memory.save_context({"input":input}, {"output": output})
def get_history():
return memory.load_memory_variables({})
add_messages('hello i am student and my major is computer science', 'wow pretty good!')
get_history()
대화 내용 엔티티의 KG(knowledge graph) 를 만들어 중요한 요약본을 생성하여 메모리에 저장하는 방식
from langchain.memory import ConversationKGMemory
from langchain.chat_models import ChatOpenAI
summary_llm = ChatOpenAI(temperature=0.1)
memory = ConversationKGMemory(
llm=summary_llm,
return_messages=True
)
def add_messages(input, output):
memory.save_context({"input":input}, {"output": output})
def get_history():
return memory.load_memory_variables({})
add_messages("say hi to sam", "who is sam")
add_messages("sam is a friend", "okay")
memory.load_memory_variables({"input": "who is sam"})
메모리를 체인에 꽂아 history를 가진 체인을 만드는 방법
from langchain.memory import ConversationBufferWindowMemory
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain #off-self-chain 사용, 자동으로 LLM으로부터 응답값을 가져오고 memory를 업데이트한다.
from langchain.prompts import PromptTemplate
llm = ChatOpenAI(temperature=0.1)
memory = ConversationBufferWindowMemory(
k=3,
memory_key="chat_history",
)
template = """
You are a helpful AI talking to a human.
{chat_history}
Human:{question}
You:
"""
chain = LLMChain(
llm=llm,
memory=memory,
prompt=PromptTemplate.from_template(template),
verbose=True,
)
chain.predict(question="My name is dahyeon bae")
chain.predict(question="I live in Seoul")
chain.predict(question="What is my name? and where i am live?")
text format이 아닌 message format으로 LLM과 연속적인 대화를 하기 위해서 return_messages=True를 넣어준다. 그리고 ChatPromptTemplate으로 변경하여 메세지 format으로 인터렉션하도록 변경한다.
변수가 뒤죽박죽의 메시지 목록 (System, AI, Human) 이라고 생각할 때 MessagesPlaceholder를 사용하여 대화를 간단하게 사용하고자 하는 기법이다.
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
# text format이 아닌 message format으로 인터렉션 하기 위해 return_messages=True를 넣어준다. 그리고 ChatPromptTemplate으로 변경한다.
memory = ConversationBufferMemory(
k=3,
memory_key="chat_history",
return_messages=True,
)
# 변수가 뒤죽박죽의 메시지 목록 (System, AI, Human) 더미라고 생각하고 사용하는 방법입니다.
# Prompt template that assumes variable is already list of messages.
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful AI talking to a human"),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{question}"),
]
)
chain = LLMChain(
llm=llm,
memory=memory,
prompt=prompt,
verbose=True,
)
chain.predict(question="My name is dahyeon bae")
chain.predict(question="I live in Seoul")
chain.predict(question="What is my name? and where i am live?")
LCEL 써서 Customizing 하기. 수동으로 메모리를 저장하기.
매번 chain.invoke, memory.load_memory_variables, memory.save_context 적기 귀찮다고 느껴진다면..
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough # Adding values to chain state
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
llm = ChatOpenAI(temperature=0.1)
memory = ConversationBufferMemory(
k=3,
return_messages=True,
# memory_key="history", history라고 적으면 default 값으로 알아들음
)
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful AI talking to a human"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"), # 변수
]
)
# LCEL 써서 Customizing 하기!
# LCEL Based Memory
def load_memory(_):
return memory.load_memory_variables({})["history"]
# RunnablePassthrough.assign(...) 정적 메서드는 입력 값을 받아 할당 함수에 전달된 추가 인수를 추가합니다.
# 이는 이후 단계의 입력으로 사용할 딕셔너리를 추가적으로 만들 때 유용하며, 이는 LCEL 에 주로 사용되는 패턴입니다.
# The RunnablePassthrough.assign(...) static method takes an input value and adds the extra arguments passed to the assign function.
# This is useful when additively creating a dictionary to use as input to a later step, which is a common LCEL pattern.
chain = RunnablePassthrough.assign(history=load_memory) | prompt | llm
def invoke_chain(question):
result = chain.invoke({"question": question})
memory.save_context(
{"input": question}, # Human
{"output": result.content}, # AI
)
print(result)
invoke_chain("My name is dahyeon bae. And my major is computer science")
invoke_chain("What is my name? And Do you know what is my mojor?")