채팅 기록(Conversational) 체인
•
Conversation Chain은 이전 메시지의 메모리를 통합하여 지속적인 대화를 유지한다.
•
LLM의 Memory 동작 방식을 잘 모르겠다면 이전 포스트 Memorization를 참고하고 다시 오자!
•
이점
◦
스레드/별도 세션에 대한 기본 지원: Conversation Chain에서 이 기능을 사용하려면 체인 외부에 별도의 메모리 클래스를 인스턴스화해야 한다.
◦
보다 명시적인 매개변수: Conversation Chain에는 숨겨진 기본 프롬프트가 포함되어 있어 혼동을 일으킬 수 있다.
◦
스트리밍 지원: Conversation Chain은 콜백을 통한 스트리밍만 지원한다.
동작 과정
1. Query Input
사용자가 Query를 입력하면, Input 단계로 넘어가며 Memory를 통해 이전 대화 기록을 참조한다.
2. 입력 변환
•
그 다음, PromptTemplate은 입력을 LLM이 이해하기 쉬운 프롬프트 형식으로 변환하여, Chat LLM에 전달한다.
3. Output
•
Chat LLM이 생성한 출력은 Memory에도 저장되고 OutputParser를 통해 사용자가 원하는 형식으로 가공된다.
4. 응답(Response)
최종적으로 이 응답이 Response로 반환된다.
실습
기본 세팅
설치
•
해당 과정은 cpu를 사용해서 진행하여서 faiss-cpu 모듈을 사용하였다.
!pip install -U langchain langchain-community langchain-core langchain-openai langgraph faiss-cpu
Python
복사
Key 등록
•
아래 방법이나 .env에 정의하자.
import os
os.environ['OPENAI_API_KEY'] = '여기에 사용하는 키 입력'
Python
복사
LLM 모델 정의 & 모듈 불러오기
import uuid
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
from langchain_core.messages import HumanMessage, SystemMessage
model = ChatOpenAI(model="gpt-4o-mini")
Python
복사
Node
# 모델을 호출하는 함수를 정의
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": response}
Python
복사
# 새 그래프 정의
workflow = StateGraph(state_schema=MessagesState)
# 순환할 두 노드를 정의
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
Python
복사
•
인메모리 체크포인트 세이버.
# 메모리 추가
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
Python
복사
Display에 띄워보자.
•
jupyter 노트북 환경에서 그래프를 보기위해 IPython을 사용했다.
from IPython.display import Image
Image(app.get_graph().draw_mermaid_png())
Python
복사
UUID
•
UUID 생성
# 스레드 아이디: 이 특정 대화를 식별하는 고유 키
# 여기서는 임의의 uuid를 생성한다.
thread_id = uuid.uuid4()
> thread_id
UUID('614388be-0682-40f6-97fb-ba8b933c2b73')
Python
복사
•
config
config = {"configurable": {"thread_id": thread_id}}
Python
복사
App - Stream_mode
•
출력을 스트리밍할 모드로, 기본값은 self.stream_mode이다.
•
옵션으론 'values', 'updates', 'debug’가 있다.
values
•
각 단계에 대한 상태의 현재 값을 출력
stream_mode="values"
query = "제 이름은 홍길동입니다."
input_messages = [
SystemMessage("당신은 선생님입니다."),
HumanMessage(query)
]
response = app.stream({"messages": input_messages}, config, stream_mode=stream_mode)
for event in response:
event["messages"][-1].pretty_print()
Python
복사
================================ Human Message =================================
제 이름은 홍길동입니다.
================================== Ai Message ==================================
안녕하세요, 홍길동님! 만나서 반갑습니다. 어떻게 도와드릴까요?
Python
복사
•
기억하는지 확인
input_messages = [HumanMessage("제 이름은 뭔가요?")]
response = app.stream({"messages": input_messages}, config, stream_mode=stream_mode)
for event in response:
event["messages"][-1].pretty_print()
Python
복사
================================ Human Message =================================
제 이름은 뭔가요?
================================== Ai Message ==================================
홍길동님이라고 말씀하셨습니다. 맞나요? 추가로 궁금한 점이 있으시면 말씀해 주세요!
Python
복사
updates
•
각 단계에 대한 상태의 업데이트만 내보낸다.
•
출력은 노드 이름을 키로 하고 업데이트된 값을 값으로 하는 딕셔너리이다.
stream_mode="updates"
input_messages = [HumanMessage("외국인에게 추천할 한국음식 하나만 추천해줘?")]
response = app.stream({"messages": input_messages}, config, stream_mode=stream_mode)
event = next(iter(response))
> event
{'model': {'messages': AIMessage(content='외국인에게 추천할 한국 음식으로 **비빔밥**을 추천합니다. 비빔밥은 다양한 재료가 조화를 이루는 맛있는 요리로, 고추장과 함께 비벼 먹으면 풍부한 맛을 즐길 수 있습니다. 또한, 채소와 고기를 함께 즐길 수 있어 영양가도 높습니다. 비빔밥은 색감도 아름다워 시각적으로도 매력적이어서 외국인에게 좋은 인상을 줄 수 있을 것입니다!',
...
'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 0}})}}
> event['model']['messages'].pretty_print()
================================== Ai Message ==================================
외국인에게 추천할 한국 음식으로 **비빔밥**을 추천합니다.
...
Python
복사
debug
•
각 단계에 대한 디버그 이벤트를 내보낸다.
stream_mode="debug"
input_messages = [HumanMessage("대한민국의 수도는 어디인가요?")]
response = app.stream({"messages": input_messages}, config, stream_mode=stream_mode)
Python
복사
•
이벤트(Event) 확인
event = next(iter(response))
event.keys()
> event['step']
6
> event['type']
checkpoint
> event['timestamp']
2024-11-02T03:20:13.354645+00:00
> event['payload']
{'config': {'tags': [],
'metadata': ChainMap({}),
'callbacks':
...
'step': 6,
'parents': {}},
'next': ['__start__'],
'tasks': [{'id': 'e237a6de-a78a-90aa-bd25-e06cdf7d919c',
'name': '__start__',
'interrupts': (),
'state': None}]}
Python
복사
◦