langchain / / 2024. 6. 5. 15:04

[langchain] 메모리 (memory)

ConversationBufferMemory

ConversationBufferMemory는 메시지 내용을 변수에 저장하고 나중에 다시 꺼내올 때 사용한다.

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
memory.save_context({"input": "안녕"}, {"output": "안녕하세요!"})

variables = memory.load_memory_variables({})
print(variables)

출력결과

{'history': 'Human: 안녕\nAI: 안녕하세요!'}

만일 Chat 용도로 message 이력을 가져오고 싶다면 return_message=True를 주면 된다.

memory = ConversationBufferMemory(return_messages=True)
memory.save_context({"input": "안녕"}, {"output": "안녕하세요!"})

variables = memory.load_memory_variables({})
print(variables)

출력결과

{'history': [HumanMessage(content='안녕'), AIMessage(content='안녕하세요!')]}

Chain 사용하기

chain에서 사용하는 방법이다. 아래와 같이 두 개의 프롬프트를 보내보고 두 번째 프롬프트에서 이전 메시지 내용을 기억하고 있는지 확인해보자.

  1. 3x2는?
  2. 거기에 2를 곱하면?
llm = OpenAI(temperature=0)
conversation = ConversationChain(
    llm=llm, verbose=True, memory=ConversationBufferMemory()
)

with get_openai_callback() as callback:
    question = "3x2는 몇이야?"
    result = conversation.predict(input=question)
    print(result)

with get_openai_callback() as callback:
    question = "거기에 2를 곱하면?"
    result = conversation.predict(input=question)
    print(result)

실행결과

> 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: 3x2는 몇이야?
AI:

> Finished chain.
 3x2는 6이야. 


> 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: 3x2는 몇이야?
AI:  3x2는 6이야. 
Human: 거기에 2를 곱하면?
AI:

> Finished chain.
 3x2x2는 12이야. 

첫 번째 대화내용을 기억하고 있어서 3x2=6이고 6x2=12라는 것을 출력한다.

ConversationBufferWindowMemory

ConversationBufferWindowMemory는 시간이 지남에 따라 대화의 상호작업 목록을 유지한다. 즉, 최근 K개만을 사용한다. 버퍼가 너무 커지지 않도록 최근 대화내용을 유지하는데 유용하게 사용될 수 있다.

from langchain.memory import ConversationBufferWindowMemory


memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input": "안녕"}, {"output": "안녕하세요"})
memory.save_context({"input": "잘지내나요?"}, {"output": "그럭저럭"})

variables = memory.load_memory_variables({})
print(variables)

위에서 k=1을 설정하여 최근 대화 이력 1개만을 가져오도록 설정했다.

만일 Chat 용도로 message 이력을 가져오고 싶다면 return_message=True를 주면 된다.

memory = ConversationBufferWindowMemory(k=1, return_messages=True)
memory.save_context({"input": "안녕"}, {"output": "안녕하세요"})
memory.save_context({"input": "잘지내나요?"}, {"output": "그럭저럭"})

variables = memory.load_memory_variables({})
print(variables)

출력결과

{'history': [HumanMessage(content='잘지내나요?'), AIMessage(content='그럭저럭')]}

Chain 사용하기

아래와 같이 ConversationChain을 사용할 수 있다. k=1로 설정하여 최근 1개의 이력만 유지하도록 했다. 그리고 마지막 질문에서 이름이 뭐냐고 물어보면 알 수 있는지 알아보자.

conversation_with_summary = ConversationChain(
    llm=OpenAI(temperature=0),
    memory=ConversationBufferWindowMemory(k=1),
    verbose=True,
)
conversation_with_summary.predict(input="내 이름은 김고은이야.")
conversation_with_summary.predict(input="내 나이는 26살이야.")
conversation_with_summary.predict(input="나는 서울에 살아.")

result = conversation_with_summary.predict(input="내 이름은 뭐야?")
print(result)

실행결과

죄송합니다, 제가 당신의 이름을 알지는 못합니다. 제가 알고 있는 것은 당신이 서울에 살고 계시고 대화를 나누고 있다는 것뿐입니다.

k=1이기 때문에 이름과 나이는 모르고 서울에 살고 있다는 사실만 알고 있다.

ConversationEntityMemory

ConversationEntityMemory는 대화에서 특정 엔티티에 대한 사실을 기억한다. LLM을 사용해서 엔터티 내의 정보를 추출하고 시간에 따라 엔터티에 대한 지식을 저장한다.

conversation = ConversationChain(
    llm=llm,
    verbose=True,
    prompt=ENTITY_MEMORY_CONVERSATION_TEMPLATE,
    memory=ConversationEntityMemory(llm=llm),
)

conversation.predict(
    input="안녕하세요, 저는 한유주입니다. 제가 지난주에 갔던 파리 여행에 대해 이야기하고 싶어요. 에펠탑에서 찍은 멋진 사진들도 있어요"
)

result = conversation.invoke(input="유주에 대해 뭘 알고 있어?")

print(result)

출력결과

{'input': '유주에 대해 뭘 알고 있어?', 'history': 'Human: 안녕하세요, 저는 한유주입니다. 제가 지난주에 갔던 파리 여행에 대해 이야기하고 싶어요. 에펠탑에서 찍은 멋진 사진들도 있어요\nAI:  파리 여행은 정말 멋진 경험이었나봐요. 에펠탑에서 찍은 사진들도 정말 멋지네요. 파리에 가보고 싶어지는군요.', 'entities': {'유주': ''}, 'response': " 유주는 한국어로 '유쾌한 주인'이라는 뜻이에요. 그리고 지난주에 파리 여행을 다녀오셨군요."}

ConversationSummaryMemory

ConversationSummaryMemory는 시간이 지남에 따라 대화내용을 요약한다. 시간이 지나면 대화 요약이 필요할 때 유용하게 사용될 수 있다. 대화 요약 메모리는 발생한 대화 내용과 현재 요약정보를 메모리에 저장한다. 이 메모리는 지금까지 현재 대화내용을 prompt/chain에 저장하는데 사용될 수 있다. 메모리는 주로 긴 메시지 내용에 효과적이며 과거 이력을 프롬프트에 그대로 보관 시 메시지 내용이 너무 길어지는 것을 방지할 수 있다.

llm = ChatOpenAI(temperature=0, verbose=True)

memory = ConversationSummaryMemory(llm=llm, return_messages=True)

memory.save_context(
    inputs={"human": "이번 프로젝트의 진행 상황을 업데이트해 주세요"},
    outputs={
        "ai": "네, 현재 개발 팀이 70% 정도 완료한 상태입니다. QA 팀은 다음 주부터 테스트를 시작할 예정입니다."
    },
)
memory.save_context(
    inputs={"human": "마케팅 전략은 어떻게 진행되고 있나요?"},
    outputs={
        "ai": "우리는 소셜 미디어 캠페인을 준비 중입니다. 첫 번째 단계는 다음 주 월요일에 시작될 예정입니다."
    },
)
memory.save_context(
    inputs={
        "human": "좋습니다. 다음 미팅은 두 주 후에 진행하죠. 그때까지 모든 팀이 진행 상황을 공유해 주세요."
    },
    outputs={"ai": "알겠습니다."},
)

result = memory.load_memory_variables({})["history"]
print(result)

출력결과

[SystemMessage(content='The human asks about the progress of the project. The AI responds that the development team is about 70% complete and the QA team will start testing next week. The human inquires about the marketing strategy, and the AI mentions that they are preparing a social media campaign which will begin next Monday. The human confirms the next meeting will be in two weeks and requests all teams to share their progress by then. The AI acknowledges the request.')]

위의 human과 ai의 메시지 내용을 하나로 압축하여 저장한다.

ConversationSummaryBufferMemory

ConversationSummaryBufferMemory는 두 가지 아이디어를 결합한 것이다. 메모리에 최근 최근 대화를 저장하고 과거 대화를 플러시하기 보다는 하나의 문장으로 요약하여 둘 다 사용한다. 대화 내용을 언제 플러시할 지 결정하기 위해 대화 수보다는 토큰 수를 사용한다.

llm = ChatOpenAI(temperature=0, verbose=True)

memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=100,  
    return_messages=True,
)

memory.save_context(
    inputs={"human": "이번 프로젝트의 진행 상황을 업데이트해 주세요"},
    outputs={
        "ai": "네, 현재 개발 팀이 70% 정도 완료한 상태입니다. QA 팀은 다음 주부터 테스트를 시작할 예정입니다."
    },
)
memory.save_context(
    inputs={"human": "마케팅 전략은 어떻게 진행되고 있나요?"},
    outputs={
        "ai": "우리는 소셜 미디어 캠페인을 준비 중입니다. 첫 번째 단계는 다음 주 월요일에 시작될 예정입니다."
    },
)
memory.save_context(
    inputs={
        "human": "좋습니다. 다음 미팅은 두 주 후에 진행하죠. 그때까지 모든 팀이 진행 상황을 공유해 주세요."
    },
    outputs={"ai": "알겠습니다."},
)

result = memory.load_memory_variables({})["history"]
print(result)

출력결과

[SystemMessage(content='The human asks for an update on the progress of the project. The AI responds that the development team is about 70% complete and the QA team will start testing next week. The human then asks about the progress of the marketing strategy, to which the AI replies that they are preparing a social media campaign that will start next Monday.'), HumanMessage(content='좋습니다. 다음 미팅은 두 주 후에 진행하죠. 그때까지 모든 팀이 진행 상황을 공유해 주세요.'), AIMessage(content='알겠습니다.')]

VectorStoreRetrieverMemory

VectorStoreRetrieverMemory는 벡터 저장소에 저장하고 호출될 때 마다 가장 중요한 문서 Top K개를 조회한다.

이것은 대화내용의 순서를 추적하지 않는다는 점에서 여타 메모리 클래스와 다르다.

이 경우 문서들은 이전 대화 조각들이다. AI가 이전 대화에서 들었던 적절한 정보를 언급할 때 유용하게 사용될 수 있다.

from dotenv import load_dotenv
from langchain.memory import VectorStoreRetrieverMemory
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

load_dotenv()

vectorstore = Chroma(collection_name="chroma_db", embedding_function=OpenAIEmbeddings())

retriever = vectorstore.as_retriever(search_kwargs={"k": 1})
memory = VectorStoreRetrieverMemory(retriever=retriever)

memory.save_context(
    inputs={"human": "이번 프로젝트의 진행 상황을 업데이트해 주세요"},
    outputs={
        "ai": "네, 현재 개발 팀이 70% 정도 완료한 상태입니다. QA 팀은 다음 주부터 테스트를 시작할 예정입니다."
    },
)
memory.save_context(
    inputs={"human": "마케팅 전략은 어떻게 진행되고 있나요?"},
    outputs={
        "ai": "우리는 소셜 미디어 캠페인을 준비 중입니다. 첫 번째 단계는 다음 주 월요일에 시작될 예정입니다."
    },
)
memory.save_context(
    inputs={
        "human": "좋습니다. 다음 미팅은 두 주 후에 진행하죠. 그때까지 모든 팀이 진행 상황을 공유해 주세요."
    },
    outputs={"ai": "알겠습니다."},
)

# 메모리에 질문을 통해 가장 연관성 높은 1개 대화를 추출합니다.
print("____ 1 ____")
result = memory.load_memory_variables({"prompt": "프로젝트 진행상태는?"})["history"]
print(result)

print("____ 2 ____")
result = memory.load_memory_variables({"human": "마케팅 전략?"})["history"]
print(result)

출력결과

____ 1 ____
human: 이번 프로젝트의 진행 상황을 업데이트해 주세요
ai: 네, 현재 개발 팀이 70% 정도 완료한 상태입니다. QA 팀은 다음 주부터 테스트를 시작할 예정입니다.
____ 2 ____
human: 마케팅 전략은 어떻게 진행되고 있나요?
ai: 우리는 소셜 미디어 캠페인을 준비 중입니다. 첫 번째 단계는 다음 주 월요일에 시작될 예정입니다.

참고

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유