Notice
Recent Posts
Recent Comments
Link
반응형
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 헤더가드
- AWS
- 배포
- 소스코드
- 최종프로젝트
- 임베딩
- C++
- 중복인클루드
- few-shot
- sk네트웍스ai캠프
- 주간회고
- #include
- sk네트웍스family
- 21주차
- one-shot
- Fine-tuning
- 어셈블
- sk네트웍스familyai캠프
- 12기
- 전처리
- zero-shot
- ai캠프
- openai
- Docker
- sk네트웍스familyai캠프12기
- FastAPI
- Rag
- 컴파일
- Langchain
- 회고록
Archives
- Today
- Total
ansir 님의 블로그
[ SK 네트웍스 Family AI 캠프 수업 내용 복습 ] LangChain Expression Language( LCEL ) 2025-05-26 본문
SK 네트웍스 family AI 캠프/수업 내용 복습
[ SK 네트웍스 Family AI 캠프 수업 내용 복습 ] LangChain Expression Language( LCEL ) 2025-05-26
ansir 2025. 5. 26. 17:52.env로 환경변수 설정 후 불러오기
# .env
OPENAI_API_KEY="private_key"
from dotenv import load_dotenv
load_dotenv() # .env 파일 내용을 환경변수로 로드
import os
os.getenv("OPENAI_API_KEY")[:4]
LangChain의 핵심 컴포넌트: 모델 호출 단계를 구성하는 추상화 요소를 제공
- PromptTemplate: LLM에 보낼 입력 프롬프트
- ChatOpenAI: OPENAI의 GPT- 모델 호출
- Runnable: 실행 가능한 객체에 대한 공통 인터페이스 -> invoke() 메서드를 통해서 입력과 출력을 지원
- StrOutPutParser: 문자열 출력 파서
파이프로 연결이 가능
ex) prompt | llm | strparser
Prompt Template
from langchain_core.prompts import PromptTemplate
template = "{product} 제품을 생산하는 회사 이름은 뭘로 하면 좋을까?"
prompt = PromptTemplate.from_template(template)
formated_prompt = prompt.format(product="커피")
print(formated_prompt)
커피 제품을 생산하는 회사 이름은 뭘로 하면 좋을까?
LLM + Prompt Template
from langchain_openai import ChatOpenAI
# OpenAI API 키를 환경변수로 설정해야 합니다.
# 키 환경 변수명은 OPENAI_API_KEY로 설정되어야 합니다.
# 환경변수가 설정되어 있다면 ChatOpenAI 인스턴스를 생성할 때 키를 자동으로 읽어옵니다.
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0) # Runnable 객체 -> invoke() 메서드로 실행
response = llm.invoke([("human", "안녕")])
print(response.content) # LLM의 응답 출력
# llm.invoke(formated_prompt): LLM에 프롬프트 전달하여 응답 받기
# LLM의 응답은 문자열로 반환됩니다.
# 응답 예시: '커피의 향기', '커피의 정원', '커피의 여정' 등
# LLM의 응답을 변수에 저장
response = llm.invoke(formated_prompt)
print(response.content) # LLM의 응답 출력
안녕하세요! 어떻게 도와드릴까요?
커피 제품을 생산하는 회사 이름을 정할 때는 브랜드의 이미지, 타겟 고객, 그리고 제품의 특징을 고려하는 것이 중요합니다. 다음은 몇 가지 아이디어입니다:
1. **커피의 정원** - 자연 친화적인 이미지를 강조
2. **아침의 한 잔** - 아침에 즐기는 커피를 강조
3. **커피의 예술** - 고급스러운 느낌을 주는 이름
4. **향기로운 순간** - 커피의 향을 강조
5. **커피 공방** - 수제 느낌을 주는 이름
6. **커피의 여정** - 다양한 원두와 블렌드를 탐험하는 느낌
7. **한 잔의 행복** - 커피가 주는 기쁨을 강조
8. **커피 마을** - 친근하고 따뜻한 이미지
9. **블렌드 & 브루** - 다양한 블렌드와 추출 방법을 강조
10. **커피의 시간** - 커피를 즐기는 특별한 순간을 강조
이 중에서 마음에 드는 이름을 선택하거나, 조합하여 새로운 이름을 만들어 보세요!
LLM 응답 + Parser
# 문자열 출력 파서 Runnable 객체 - invoke()
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
parsed_text = parser.invoke(response)
print(parsed_text) # 파싱된 문자열 출력: response.content와 동일
커피 제품을 생산하는 회사 이름을 정할 때는 브랜드의 이미지, 타겟 고객, 그리고 제품의 특징을 고려하는 것이 중요합니다. 다음은 몇 가지 아이디어입니다:
1. **커피의 정원** - 자연 친화적인 이미지를 강조
2. **아침의 한 잔** - 아침에 즐기는 커피를 강조
3. **커피의 예술** - 고급스러운 느낌을 주는 이름
4. **향기로운 순간** - 커피의 향을 강조
5. **커피 공방** - 수제 느낌을 주는 이름
6. **커피의 여정** - 다양한 원두와 블렌드를 탐험하는 느낌
7. **한 잔의 행복** - 커피가 주는 기쁨을 강조
8. **커피 마을** - 친근하고 따뜻한 이미지
9. **블렌드 & 브루** - 다양한 블렌드와 추출 방법을 강조
10. **커피의 시간** - 커피를 즐기는 특별한 순간을 강조
이 중에서 마음에 드는 이름을 선택하거나, 조합하여 새로운 이름을 만들어 보세요!
LangChain Expression Language( LCEL ) 단일 체인 실행
chain = prompt | llm | parser # Runnable 객체를 파이프라인으로 연결 -> invoke() 메서드로 실행되기 때문에 파이프라인으로 연결 가능
# Runnable 객체를 파이프라인으로 연결하여 실행
result = chain.invoke({"product": "커피"})
print(result) # 파이프라인 실행 결과 출력: '커피의 향기', '커피의 정원', '커피의 여정' 등
커피 제품을 생산하는 회사 이름으로는 다음과 같은 아이디어를 고려해볼 수 있습니다:
1. **커피의 정원** - 자연 친화적인 이미지를 강조
2. **아침의 한 잔** - 하루의 시작을 상징
3. **커피 향기** - 향긋한 커피의 매력을 표현
4. **커피 이야기** - 각 커피의 배경과 스토리를 강조
5. **빈과 잔** - 커피 원두와 컵을 상징적으로 표현
6. **커피 공방** - 수공예 느낌을 주는 이름
7. **커피 마법** - 특별한 경험을 제공하는 느낌
8. **한 모금의 행복** - 커피가 주는 즐거움을 강조
9. **커피의 여정** - 다양한 원두와 블렌드를 탐험하는 느낌
10. **커피의 미소** - 즐거운 순간을 연상시키는 이름
이 중에서 마음에 드는 이름을 선택하거나, 조합하여 새로운 이름을 만들어보세요!
LangChain을 활용한 모델 사용, 비용 모니터링 및 캐싱 전략
GPT-4o-mini: GPT-3.5-Turbo보다 비용이 60% 저렴
LangChain V0.3x 부터 openAI 가 별도 패키지로 분리 필요 패키지를 설치 langchain_openai 필요
토큰 사용량 추적, 캐싱을 위한 langchain-community도 별도 설치
환경 변수 관린 패키지 python-dotenv
chat open ai 사용량 확인
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
prompt = "LangChain에 대해 한 문장으로 설명해줘."
result = llm.invoke(prompt)
print(result.content)
# 사용량
result.usage_metadata
{'input_tokens': 19,
'output_tokens': 34,
'total_tokens': 53,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}}
누적 토큰 사용량 확인
# 누적 토큰 사용량 확인 -> 콜백함수 이용( get_openai_callback )
from langchain_community.callbacks import get_openai_callback
with get_openai_callback() as cb:
# 첫 번째 호출
res1 = llm.invoke("오늘 서울의 날씨를 알려줘.")
print("응답1:", res1.content)
# 두 번째 호출
res2 = llm.invoke("파이썬으로 LangChain을 사용하는 방법을 알려줘.")
print("응답2:", res2.content)
# 누적 토큰 사용량 출력
# 콜백 cb에는 블록 내 전체 토큰 사용량이 누적
# 총 토큰 사용량
print("총 토큰 사용량:", cb.total_tokens)
# 총 프롬프트 토큰 사용량
print("총 프롬프트 토큰 사용량:", cb.prompt_tokens)
# 총 응답 토큰 사용량
print("총 응답 토큰 사용량:", cb.completion_tokens)
# 총 비용
print("총 비용:", cb.total_cost)
응답1: 죄송하지만, 실시간 날씨 정보는 제공할 수 없습니다. 서울의 현재 날씨를 확인하시려면 기상청 웹사이트나 날씨 앱을 참고하시기 바랍니다. 도움이 필요하시면 다른 질문이 있으신가요?
응답2: LangChain은 다양한 자연어 처리 작업을 수행할 수 있는 파이썬 라이브러리입니다. 주로 언어 모델과 상호작용하거나, 다양한 데이터 소스와 연결하여 복잡한 애플리케이션을 만드는 데 사용됩니다. LangChain을 사용하기 위해서는 몇 가지 단계를 따라야 합니다.
### 1. 환경 설정
먼저, Python 환경이 필요합니다. Python 3.7 이상이 설치되어 있어야 하며, `pip`를 사용하여 LangChain을 설치할 수 있습니다.
```bash
pip install langchain
2. 기본 사용법
LangChain을 사용하는 기본적인 예제를 보여드리겠습니다. 아래 코드는 LangChain을 사용하여 간단한 언어 모델을 호출하는 예입니다.
from langchain import LLMChain
from langchain.llms import OpenAI
# OpenAI API 키 설정
import os
os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"
# 언어 모델 인스턴스 생성
llm = OpenAI(model="text-davinci-003")
...
총 토큰 사용량: 748
총 프롬프트 토큰 사용량: 37
총 응답 토큰 사용량: 711
총 비용: 0.00043214999999999996
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...
LangChain의 LLM 응답 캐싱( In-Memory Caching, SQLiteCache )
- 동일한 질문은 저장해 뒀다가 응답에 재사용한다.
In-Memory Caching
from langchain_core.caches import InMemoryCache
from langchain_core.globals import set_llm_cache
# In-Memory Cache 설정
set_llm_cache(InMemoryCache())
# 캐시 사용 전 후 비교
# 같은 질문을 두 번 호출
query = "재미있는 유머 하나 알려줘"
# 첫 번째 호출: 캐시에 응답이 없으면 API 호출 발생
response1 = llm.invoke(query)
print("응답1:", response1.content)
# 두 번째 호출: 캐시에 응답이 있으면 API 호출 없이 캐시된 응답 사용
response2 = llm.invoke(query)
print("응답2:", response2.content)
응답1: 물론이죠! 다음은 간단한 유머입니다:
왜 컴퓨터는 바다에 빠지지 않을까요?
왜냐하면 "다이브(Dive)" 버튼이 없거든요!
재미있으셨나요? 더 필요하시면 말씀해 주세요!
응답2: 물론이죠! 다음은 간단한 유머입니다:
왜 컴퓨터는 바다에 빠지지 않을까요?
왜냐하면 "다이브(Dive)" 버튼이 없거든요!
재미있으셨나요? 더 필요하시면 말씀해 주세요!
Caching 사용 전 후 시간 비교
# 실행시간 측정
import time
query = "오늘 점심 메뉴 추천해줘"
# 첫 번째 호출 시간
start = time.time()
llm.invoke(query)
end = time.time()
print(f"첫 번째 호출 시간: {end - start:.4f}초")
# 두 번째 호출 시간
start = time.time()
llm.invoke(query)
end = time.time()
print(f"두 번째 호출 시간: {end - start:.4f}초")
첫 번째 호출 시간: 6.4144초
두 번째 호출 시간: 0.0007초
SQLite 캐시( 디스크 기반 캐시 )
import os
from langchain_community.cache import SQLiteCache
# SQLite Cache 설정( 지정한 경로의 DB 파일을 생성 / 사용 )
set_llm_cache(SQLiteCache(database_path=".langchain.db")) # langchain.db 파일 생성
# 동일한 query를 두 번 호출해서 결과와 시간을 비교
query = "넌센스 퀴즈 하나와 정답을 알려줘."
# 첫 번째 호출: 캐시에 응답이 없으면 API 호출 발생
start = time.time()
response1 = llm.invoke(query)
end = time.time()
print("응답1:", response1.content)
print(f"첫 번째 호출 시간: {end - start:.4f}초")
print("-" * 100)
# 두 번째 호출: 캐시에 응답이 있으면 API 호출 없이 캐시된 응답 사용
start = time.time()
response2 = llm.invoke(query)
end = time.time()
print("응답2:", response2.content)
print(f"두 번째 호출 시간: {end - start:.4f}초")
응답1: 넌센스 퀴즈 하나 드릴게요!
**문제:**
어떤 행성이 가장 많은 친구를 가지고 있을까요?
**정답:**
화성! (화성은 '화'가 많아서 친구가 많다는 말장난이에요.)
재미있죠? 또 궁금한 게 있으면 말씀해 주세요!
첫 번째 호출 시간: 1.5922초
----------------------------------------------------------------------------------------------------
응답2: 넌센스 퀴즈 하나 드릴게요!
**문제:**
어떤 행성이 가장 많은 친구를 가지고 있을까요?
**정답:**
화성! (화성은 '화'가 많아서 친구가 많다는 말장난이에요.)
재미있죠? 또 궁금한 게 있으면 말씀해 주세요!
두 번째 호출 시간: 0.0027초
LangChain 프롬프트 템플릿
LLM 프롬프트를 동적으로 구성하고 재사용할 수 있도록 해주는 도구
단일 입력: 하나의 변수로 구성된 프롬프트 템플릿
다중 입력: 둘 이상의 변수를 사용하는 템플릿
ChatPromptTemplate 역할 기반 프롬프트: 시스템/사용자 역할 별 프롬프트 구성 .from_message()
PartialPromptTemplate 활용: 프롬프트 일부를 미리 고정하고 부분 포멧팅을 사용( ex. 시스템 메시지는 고정.. )
프롬프트를 출력 및 체인 실행: LCEL
프롬프트 작성 팁: 주의사항 및 모범사례
단일 프롬프트
# 단일 프롬프트 사용
from langchain.prompts import PromptTemplate
# 템플릿 문자열 정의
template_str = (
"당신은 최고 수준의 마케팅 카피라이터입니다.\\n"
"아래 제품의 매력적인 홍보 문구를 100자 이내로 작성해주세요.\\n\\n"
"제품 명: {product_name}\\n"
)
# 템플릿 객체 생성
product_prompt = PromptTemplate.from_template(template_str)
# 프롬프트에 제품 이름을 삽입
product_name = "스마트폰"
formatted_prompt = product_prompt.format(product_name=product_name)
# 프롬프트 출력
print(formatted_prompt)
당신은 최고 수준의 마케팅 카피라이터입니다.
아래 제품의 매력적인 홍보 문구를 100자 이내로 작성해주세요.
제품 명: 스마트폰
# 프롬프트 | llm -> invoke()
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# OpenAI API 클라이언트 생성
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# 출력 파서 설정
parser = StrOutputParser()
# LLM에 프롬프트 전달 및 응답 생성
chain = product_prompt | llm | parser
response = chain.invoke({"product_name": product_name})
# 응답 출력
print(response)
"세상을 손안에! 혁신적인 스마트폰으로 연결되고, 창조하고, 즐기세요. 당신의 일상이 더욱 특별해집니다!"
다중 프롬프트
# 다중 입력
# 다중 입력 템플릿 문자열 정의
multi_template_str = (
"아래는 뉴스 기사 제목과 키워드 입니다.\\n"
"이 정보를 바탕으로 한 문단으로 구성된 간략한 요약문을 작성하세요.\\n\\n"
"제목: {title}\\n"
"키워드: {keywords}\\n"
)
# 다중 프롬프트 템플릿 작성
summary_prompt = PromptTemplate(template=multi_template_str, input_variables=["title", "keywords"])
# summary_prompt = PromptTemplate.from_template(multi_template_str)
# 포멧팅을 통해 프롬프트 값 설정
title = "AI 기술의 발전과 미래"
keywords = "인공지능, 머신러닝, 딥러닝"
formatted_summary_prompt = summary_prompt.format(title=title, keywords=keywords)
# 프롬프트 출력
print(formatted_summary_prompt)
아래는 뉴스 기사 제목과 키워드 입니다.
이 정보를 바탕으로 한 문단으로 구성된 간략한 요약문을 작성하세요.
제목: AI 기술의 발전과 미래
키워드: 인공지능, 머신러닝, 딥러닝
# LCEL 출력
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
result_summary = (summary_prompt | llm | parser).invoke({
"title": title,
"keywords": keywords
})
print(result_summary)
AI 기술의 발전은 인공지능, 머신러닝, 딥러닝의 혁신적인 발전을 통해 이루어지고 있으며, 이는 다양한 산업 분야에서 큰 변화를 가져오고 있습니다. 특히, 머신러닝과 딥러닝 기술의 진화는 데이터 분석의 정확성과 효율성을 높여주어, 기업들이 보다 스마트한 의사결정을 할 수 있도록 지원하고 있습니다. 앞으로 이러한 기술들은 더욱 발전하여 인간의 삶을 개선하고, 새로운 기회를 창출하는 데 기여할 것으로 기대됩니다.
ChatPromptTemplate와 역할 기반 프롬프트
시스템/사용자/어시스턴트 역할( role )
시스템 메시지: 모델의 동작을 지시
사용자 메시지: 실제 사용자의 입력
어시스턴트 메시지: 이전 모델이 응답한 내용이 있다면 대화 맥락 유지를 위해 활용
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
system_message = (
"당신은 Python 분야의 뛰어난 전문자이자 조언가입니다.\\n"
"사용자의 프로그래밍 질문에 대해 친절하고 이해하기 쉽게 답변해주세요.\\n"
)
user_message = ("{question}")
# 채팅 프롬프트 템플릿 생성
chat_prompt = ChatPromptTemplate.from_messages([
("system", system_message),
("user", user_message)
])
# OpenAI API 클라이언트 생성
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
# 출력 파서 설정
parser = StrOutputParser()
# 템플릿을 이용해서 문장을 완성
question = "파이썬에서 리스트를 정렬하는 방법은 무엇인가요?"
# 프롬프트 | llm | parser
chain = chat_prompt | llm | parser
response = chain.invoke({"question": question})
# 응답 출력
print(response)
PartialPromptTemplate
- 템플릿의 일부를 부분적으로 채운 새로운 템플릿
from langchain.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate
role_system_template = "당신은 {rule} 분야의 전문 지식인 입니다. 가능한 자세히 답변해주세요."
system_prompt =SystemMessagePromptTemplate.from_template(role_system_template)
user_prompt = HumanMessagePromptTemplate.from_template("{question}")
# ChatPromptTemplate 생성
base_chat_prompt = ChatPromptTemplate.from_messages([
system_prompt,
user_prompt
])
partial_chat_prompt = base_chat_prompt.partial(rule="주식 투자")
# partial로 생성된 프롬프트에 질문만 채워 프롬프트 구성
sample_question = "현재 2025년 5월 시장 상황에서 삼성전자 주식 전망은 어떤가요?"
message = partial_chat_prompt.format_messages(question=sample_question)
# 메시지 출력
print(message)
# LCEL
chain = partial_chat_prompt | llm | parser
response = chain.invoke({"question": sample_question})
# 응답 출력
print(response)
LangChainMemory
- 이전 대화 내용을 기억해서 문맥을 유지하는 역할 LangChain 0.3X 부터는 LCEL 기반으로 체인을 구성.
- RunnableWithMessageHistory, ChatMessageHistory 등의 컴포넌트를 활용하여 세션별 대화 기록을 관리
- 대화가 장기화될 경우 요약 메모리를 도입하여 과거 대화를 LLM으로 요약하고 축약된 형태로 저장해서 프롬프트의 길이 문제를 해결함.
from langchain_core.chat_history import InMemoryChatMessageHistory
# 메모리 객체 생성
history = InMemoryChatMessageHistory()
history.add_user_message("안녕하세요. 제 이름은 홍길동입니다.")
history.add_ai_message("안녕하세요, 홍길동님! 만나서 반갑습니다.")
# 현재까지의 대화 내용 확인
for msg in history.messages:
print(f"{msg.type}: {msg.content}")
human: 안녕하세요. 제 이름은 홍길동입니다.
ai: 안녕하세요, 홍길동님! 만나서 반갑습니다.
Redis 기반 채팅 기록 저장소
from langchain_redis import RedisChatMessageHistory
import os
# Redis RUL 설정
# 포트 번호는 6379가 기본값입니다.
# 포트 번호가 다르다면 환경 변수 REDIS_URL에 redis://localhost:포트번호 형식으로 설정하세요.
# 환경 변수 REDIS_URL이 설정되어 있지 않으면 기본값으로 redis://localhost:6379 사용
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
session_id = "user_123"
# RedisChatMessageHistory 객체 생성
history = RedisChatMessageHistory(session_id=session_id, redis_url=REDIS_URL)
# Redis에 대화 내용 저장
history.add_user_message("안녕하세요. 제 이름은 홍길동입니다.")
history.add_ai_message("안녕하세요, 홍길동님! 만나서 반갑습니다.")
세션 기반 다중 사용자 메모리 구조 구현 - 다중 사용자 챗봇
- 핵심: session_id를 키로 하는 메모리 저장소를 만들고, 사용자의 대화를 키 별로 저장한다.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
# 프롬프트
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 뛰어난 한국어 상담 챗봇입니다. 질문에 친절하고 자세히 답변해주세요."),
# history 키로 전달된 메시지 목록은 체인 실행 시 해당 위치에 넣겠다는 의미.
MessagesPlaceholder(variable_name="history"),
("human", "{input}")
])
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
# LCEL
from langchain_core.output_parsers import StrOutputParser
chain = prompt | llm | StrOutputParser()
# 세션별 메모리 저장소를 딕셔너리로 만들고, 존재하지 않는 새로운 세션 id가 들어오면 InMemoryChatMessageHistory를 생성
# get_session_history를 구현
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 세션 id -> 대화 기록 객체 매핑
store = {}
def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
"""
세션 ID에 해당하는 대화 기록 객체를 반환합니다. ( 없으면 새로 생성 )
"""
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
# 메모리를 통합한 체인 Wrapper 생성
chatbot = RunnableWithMessageHistory(
runnable=chain,
get_session_history=get_session_history,
input_messages_key="input",
history_messages_key="history"
)
# 두 개의 세션을 번갈아가면서 대화
# RunnableWithMessageHistory를 사용하여 세션별로 대화 기록을 관리합니다.
# 세션 ID를 사용하여 대화 기록을 구분하고, 각 세션에 대한 질문과 답변을 생성합니다.
sessions = ["user_a", "user_b"]
questions = [
"안녕하세요, 저는 홍길동입니다. 당신은 누구신가요?", # user_a의 첫 번째 질문
"안녕하세요, 저는 이순신입니다. 당신은 어떤 일을 하시나요?", # user_b의 첫 번째 질문
"저는 프로그래밍을 배우고 있습니다. 당신은 어떤 일을 하시나요?", # user_a의 두 번째 질문
"저는 역사에 관심이 많습니다. 당신은 어떤 분야에 관심이 있나요?" # user_b의 두 번째 질문
]
for idx, question in enumerate(questions):
session_id = sessions[idx % len(sessions)]
# 질문에 대한 답변 생성
response = chatbot.invoke({"input": question}, config={"configurable": {"session_id": session_id}})
print(f"Session {session_id} 질문: {question}")
print(f"Session {session_id} 답변: {response}\\n")
16:34:16 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_a 질문: 안녕하세요, 저는 홍길동입니다. 당신은 누구신가요?
Session user_a 답변: 안녕하세요, 홍길동님! 저는 여러분의 질문에 답변하고 도움을 드리기 위해 만들어진 챗봇입니다. 궁금한 점이나 도움이 필요한 부분이 있다면 언제든지 말씀해 주세요!
16:34:17 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_b 질문: 안녕하세요, 저는 이순신입니다. 당신은 어떤 일을 하시나요?
Session user_b 답변: 안녕하세요, 이순신님! 저는 여러분의 질문에 답변하고, 다양한 정보와 도움을 제공하는 챗봇입니다. 궁금한 점이나 도움이 필요한 부분이 있다면 언제든지 말씀해 주세요!
16:34:19 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_a 질문: 저는 프로그래밍을 배우고 있습니다. 당신은 어떤 일을 하시나요?
Session user_a 답변: 프로그래밍을 배우고 계시다니 멋지네요! 저는 주로 여러분의 질문에 답변하고, 정보나 조언을 제공하는 역할을 하고 있습니다. 프로그래밍 관련 질문이나 학습에 도움이 필요하시면 언제든지 말씀해 주세요. 다양한 프로그래밍 언어나 개념에 대해 설명해 드릴 수 있습니다!
16:34:21 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_b 질문: 저는 역사에 관심이 많습니다. 당신은 어떤 분야에 관심이 있나요?
Session user_b 답변: 역사에 관심이 많으시다니 정말 멋지네요! 저는 특정한 관심 분야가 없지만, 다양한 주제에 대한 정보를 제공할 수 있습니다. 역사, 과학, 기술, 문화, 예술 등 여러 분야에 대해 이야기할 수 있으니, 궁금한 점이나 더 알고 싶은 주제가 있다면 말씀해 주세요!
result = chatbot.invoke({"input": "저는 철수에요. 반갑습니다."}, config={"configurable": {"session_id": "user_c"}})
print(f"Session user_c 질문: 저는 철수에요. 반갑습니다.")
print(f"Session user_c 답변: {result}\\n")
16:41:56 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_c 질문: 저는 철수에요. 반갑습니다.
Session user_c 답변: 안녕하세요, 철수님! 반갑습니다. 어떻게 도와드릴까요? 궁금한 점이나 이야기하고 싶은 것이 있다면 말씀해 주세요.
result = chatbot.invoke({"input": "저는 누구라고요?"}, config={"configurable": {"session_id": "user_a"}})
print(f"Session user_a 질문: 저는 누구라고요?")
print(f"Session user_a 답변: {result}\\n")
16:42:32 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_a 질문: 저는 누구라고요?
Session user_a 답변: 홍길동님이라고 말씀하셨습니다! 혹시 더 궁금한 점이나 다른 질문이 있으신가요? 도움이 필요하시면 언제든지 말씀해 주세요.
result = chatbot.invoke({"input": "저는 누구라고요?"}, config={"configurable": {"session_id": "user_b"}})
print(f"Session user_b 질문: 저는 누구라고요?")
print(f"Session user_b 답변: {result}\\n")
16:43:00 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_b 질문: 저는 누구라고요?
Session user_b 답변: 이순신님이라고 말씀하셨습니다! 이순신은 한국 역사에서 매우 중요한 인물로, 임진왜란 당시의 명장으로 알려져 있습니다. 혹시 이순신 장군에 대해 더 이야기하고 싶으신가요, 아니면 다른 주제에 대해 이야기해 볼까요?
result = chatbot.invoke({"input": "저는 누구라고요?"}, config={"configurable": {"session_id": "user_c"}})
print(f"Session user_c 질문: 저는 누구라고요?")
print(f"Session user_c 답변: {result}\\n")
16:43:37 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
Session user_c 질문: 저는 누구라고요?
Session user_c 답변: 철수님이라고 하셨습니다! 혹시 다른 질문이나 이야기하고 싶은 것이 있으신가요? 언제든지 말씀해 주세요.
요약 메모리 구현( 대화 내용 자동 요약 )
긴 대화 내용을 모두 프롬프트에 기록하는 것은 비 효율적 -> 프롬프트의 길이 제한에 걸릴 가능성이 있음.
Conversation Summary Memory
0.3x 버전에서는 직접 요약용 체인을 만들어서 ChatMessageHistory에 적용
요약 방법
- 일정 길이 이상 대화가 누적되면, 과거 대화를 요약해서 핵심 내용만 남김
- 요약 결과를 메모리에 시스템 메시지 등으로 저장 -> 메모리 절약
- 새로운 사용자 입력 시 요약된 맥락 + 최근 몇 메시지만 참고하여 LLM에 전달
# 요약용 프롬프트 템플릿
summary_prompt = ChatPromptTemplate.from_messages([
("system", "당신은 대화 요약 전문가입니다. 대화의 주요 내용을 간결하게 요약해주세요."),
("human", "{conversation}"), # 전체 대화 내용을 하나의 문자열로 전달
])
# LCEL
summary_chain = summary_prompt | llm | StrOutputParser()
# user_d 세션에 대화 내용을 기록.
# 긴 대화 내용을 생성
long_queries = [
"안녕, 오늘 우리 뭐하려고 했지?",
"아, 맞다. 내일 회의 자료를 준비해야지. 내일 회의는 몇 시지?",
"그 회의에 누가 참석하는지 기억 나?",
"단위 프로젝트 진행 상황도 공유해야 할까?",
"최근에 이야기 했던 새로운 기능에 대한 업데이트는 있어?"
]
session_id = "user_d"
for q in long_queries:
response = chatbot.invoke({"input": q}, config={"configurable": {"session_id": session_id}})
print(f"Session {session_id} 질문: {q}")
print(f"Session {session_id} 답변: {response}\\n")
# 전체 대화 내용을 요약하고 마지막 사용자 질문-답변 쌍만 원본 유지
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 요약 대상 대화 내용 추출( 마지막 QA 쌍 제외한 이전 내용 )
message = store[session_id].messages
if len(message) > 2:
original_dialogue = "\\n".join([f"{msg.type.upper()}: {msg.content}" for msg in message[:-2]])
else:
original_dialogue = "\\n".join([f"{msg.type.upper()}: {msg.content}" for msg in message])
# llm으로 요약 생성
summary_text = summary_chain.invoke({"conversation": original_dialogue})
print("== 전체 대화 요약 ==")
print(f"Session {session_id} 전체 대화 요약: {summary_text}\\n")
# 기존 메모리를 요약으로 교쳬: 이전 내용 요약본 + 최근 QA 유지
new_history = InMemoryChatMessageHistory()
new_history.messages.append(SystemMessage(content=f"요약: {summary_text}"))
# 최근 대화의 마지막 QA 쌍 유지
if len(message) > 2:
last_user_message = message[-2]
last_ai_message = message[-1]
if isinstance(last_user_message, HumanMessage):
new_history.messages.append(last_user_message.content)
else:
new_history.messages.append(last_user_message)
if isinstance(last_ai_message, AIMessage):
new_history.messages.append(last_ai_message.content)
else:
new_history.messages.append(last_ai_message)
# 새로운 메모리로 교체
store[session_id] = new_history
17:50:40 httpx INFO HTTP Request: POST <https://api.openai.com/v1/chat/completions> "HTTP/1.1 200 OK"
== 전체 대화 요약 ==
Session user_d 전체 대화 요약: 대화 요약:
HUMAN은 내일 회의 자료를 준비해야 한다고 언급하며 회의 시간과 참석자를 기억하지 못한다고 말합니다. AI는 회의 일정과 참석자 정보를 이메일이나 캘린더에서 확인할 것을 제안하고, 단위 프로젝트 진행 상황을 공유하는 것이 좋다고 조언합니다. AI는 진행 상황을 시각적으로 정리하는 것도 효과적이라고 덧붙입니다.
반응형