RAG & LANCHAIN (3)- Langchain 개념 & 문법 정리 ch.01 Langchain 시작하기

이영락·2024년 8월 26일
0

인공지능 공부

목록 보기
4/33

🏖️ CH01 LangChain 시작하기

🦜️🔗 랭체인 LangChain

LangChain은 언어 모델을 활용해 다양한 애플리케이션을 개발할 수 있는 프레임워크이다. 이 프레임워크를 통해 언어 모델은 다음과 같은 기능을 수행할 수 있게 된다.

  • 문맥을 인식하는 기능: LangChain은 언어 모델을 다양한 문맥 소스와 연결한다. 여기에는 프롬프트 지시사항, 소수의 예시, 응답에 근거한 내용 등이 포함된다. 이를 통해 언어 모델은 제공된 정보를 기반으로 더 정확하고 관련성 높은 답변을 생성할 수 있게 된다.
  • 추론하는 기능: 또한, 언어 모델은 주어진 문맥을 바탕으로 어떠한 답변을 제공하거나, 어떤 조치를 취해야 할지를 스스로 추론할 수 있다. 이는 언어 모델이 단순히 정보를 재생산하는 것을 넘어서, 주어진 상황을 분석하고 적절한 해결책을 제시할 수 있음을 의미한다.

LangChain을 활용하면 이전에 언급한 기능을 바탕으로 검색 증강 생성(RAG) 어플리케이션 제작, 구조화된 데이터 분석, 챗봇 등을 만들 수 있다.

더 많은 예제는 유튜브 채널 테디노트에서 확인할 수 있다.

설치

권장하는 파이썬 버전은 3.11 버전이다.

  • pip를 이용한 설치:
    pip install -r https://raw.githubusercontent.com/teddylee777/langchain-kr/main/requirements.txt

  • 최소한의 기능만 설치하기 위한 mini 버전 (일부 패키지만 설치하는 경우):
    pip install -r https://raw.githubusercontent.com/teddylee777/langchain-kr/main/requirements-mini.txt

구성

이 프레임워크는 여러 부분으로 구성되어 있다.

  • LangChain 라이브러리: Python 및 JavaScript 라이브러리로, 다양한 컴포넌트의 인터페이스와 통합을 제공하며, 이러한 컴포넌트를 체인과 에이전트로 결합하는 기본 런타임을 포함한다. 즉시 사용 가능한 체인과 에이전트의 구현도 포함되어 있다.
  • LangChain 템플릿: 다양한 작업을 위한 쉽게 배포할 수 있는 참조 아키텍처 모음이다.
  • LangServe: LangChain 체인을 REST API로 배포하기 위한 라이브러리이다.
  • LangSmith: 어떤 LLM 프레임워크에도 구축된 체인을 디버그, 테스트, 평가, 모니터링할 수 있게 하며 LangChain과 원활하게 통합되는 개발자 플랫폼이다.
  • LangGraph: LLM을 사용한 상태유지가 가능한 다중 액터 애플리케이션을 구축하기 위한 라이브러리로, LangChain 위에 구축되었으며 LangChain과 함께 사용하도록 설계되었다. 여러 계산 단계에서 다중 체인(또는 액터)을 순환 방식으로 조정할 수 있는 능력을 LangChain 표현 언어에 추가한다.

개발 용이성✨

  • 컴포넌트의 조립 및 통합 🔧
    LangChain은 언어 모델과의 작업을 위한 조립 가능한 도구 및 통합을 제공한다. 컴포넌트는 모듈식으로 설계되어 있어, 사용하기 쉽다. 이는 개발자가 LangChain 프레임워크를 자유롭게 활용할 수 있게 한다.

  • 즉시 사용 가능한 체인 🚀
    고수준 작업을 수행하기 위한 컴포넌트의 내장 조합을 제공한다. 이러한 체인은 개발 과정을 간소화하고 속도를 높여준다.

주요 모듈 📌

  • 모델 I/O 📃
    프롬프트 관리, 최적화 및 LLM과의 일반적인 인터페이스와 작업을 위한 유틸리티를 포함한다.

  • 검색 📚
    '데이터 강화 생성'에 초점을 맞춘 이 모듈은 생성 단계에서 필요한 데이터를 외부 데이터 소스에서 가져오는 작업을 담당한다.

  • 에이전트 🤖
    언어 모델이 어떤 조치를 취할지 결정하고, 해당 조치를 실행하며, 관찰하고, 필요한 경우 반복하는 과정을 포함한다.

LangChain을 활용하면, 언어 모델 기반 애플리케이션의 개발을 보다 쉽게 시작할 수 있으며, 필요에 맞게 기능을 맞춤 설정하고, 다양한 데이터 소스와 통합하여 복잡한 작업을 처리할 수 있다.


🏖️ 01. OpenAI API 키 발급 및 테스트

OpenAI API 키 발급 및 설정

1) OpenAI API 키 발급

  • OpenAI API 웹사이트에 접속한다.

  • 우측 상단 "Sign Up"을 눌러 회원가입을 진행한다. (이미 가입되어 있는 경우 "Log in" 버튼을 눌러 로그인한다.)

  • 우측 상단 톱니바퀴(Setting)를 눌러 설정으로 이동한다.

  • 왼쪽 "Billing" 메뉴에서 "Payment methods"를 클릭하여 신용카드를 등록한다.

  • 신용카드를 등록했다면, 아래와 같이 등록된 신용카드가 목록에 나타난다.

  • "Add to credit balance" 버튼을 눌러 사용할 만큼의 미화(달러)를 입력한다.

    • 금액은 $5부터 추가가 가능하다. (즉, 최소결제금액인 $5 이상은 결제를 해야 한다.)
  • 금액을 입력한 후 "Continue"를 눌러 결제를 진행한다.

  • 왼쪽의 "Limits" 탭에서 월간 사용 한도를 설정할 수 있다.

    • "Set a monthly budget": 월간 사용 한도를 지정한다. 이 금액에 도달하면 더 이상 과금하지 않고 API는 사용을 멈춘다.
    • "Set an email notification threshold": 이메일이 발송되는 요금을 지정할 수 있다. 이 금액에 도달하면 이메일이 발송된다.
  • 우측 프로필 이미지 클릭 후 "Your profile"을 선택한다.

  • API Key 관리 메뉴로 접속한다.

  • "Create new secret key"를 클릭한다.

  • Name과 프로젝트를 입력한다. (별도로 생성한 프로젝트가 없다면 Default project를 설정한다.)

  • 우측 "Copy" 버튼을 눌러 키를 복사한다.

주의!!!

  • 키가 유출되면 다른 사람이 내 API KEY를 사용하여 GPT를 사용할 수 있으며, 결제는 본인의 지갑에서 이루어진다.
  • 절대 키는 타인에게 공유하지 말고, 안전한 곳에 보관해야 한다. (암호라고 생각해야 한다.)

2) .env 파일 설정

  • 프로젝트 루트 디렉토리에 .env 파일을 생성한다.
  • .env 파일에 OPENAI_API_KEY=방금 복사한 키를 입력한 뒤 Ctrl + S를 눌러 저장하고 파일을 닫는다.
# LangChain 업데이트
!pip install -r https://raw.githubusercontent.com/teddylee777/langchain-kr/main/requirements.txt

# API KEY를 환경변수로 관리하기 위한 설정 파일
# 설치: pip install python-dotenv
from dotenv import load_dotenv

# API KEY 정보 로드
load_dotenv()
True
  • API Key가 잘 설정되었는지 확인한다.
import os

print(f"[API KEY]\n{os.environ['OPENAI_API_KEY']}")

🏖️ 02. LangSmith 추적 설정하기

LangSmith는 LLM 애플리케이션의 개발, 모니터링 및 테스트를 위한 플랫폼으로, LangChain 사용 여부와 관계없이 강력한 추적 기능을 제공합니다. LLM 애플리케이션을 개발하면서 발생할 수 있는 다양한 문제를 추적하고 해결하는 데 도움을 줄 수 있습니다.

LangSmith의 추적 기능

추적 기능은 다음과 같은 문제를 파악하는 데 유용합니다:

  • 예상치 못한 최종 결과 분석
  • 에이전트가 무한 루프에 빠지는 이유
  • 체인이 예상보다 느린 이유
  • 에이전트가 각 단계에서 사용한 토큰 수

프로젝트 단위 추적

LangSmith는 프로젝트 단위로 실행된 모든 작업을 추적할 수 있습니다. 이를 통해 실행 카운트, 에러 발생률, 토큰 사용량, 과금 정보 등을 한눈에 확인할 수 있습니다. 또한, 실행된 각 단계별로 세부적인 추적 정보를 제공하여, 검색된 문서의 정확성 및 GPT의 입출력 내용에 대한 분석이 가능합니다.

LangSmith 추적 사용 방법

1. LangSmith API Key 발급

  1. LangSmith 웹사이트에 접속하여 회원가입을 진행합니다.
  2. 회원가입 후 이메일 인증 절차를 완료합니다.
  3. 설정(Setting)에서 Personal - Create API Key를 선택하여 API 키를 발급받습니다.

2. .env 파일에 LangSmith 키 설정

발급받은 API 키를 안전하게 보관한 후, .env 파일에 다음과 같이 설정합니다:

LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
LANGCHAIN_API_KEY=발급받은_API_KEY
LANGCHAIN_PROJECT=프로젝트_명

3. Jupyter Notebook 또는 코드에서 추적 활성화

환경 변수를 설정하여 LangSmith의 추적 기능을 활성화합니다. 아래 코드를 사용하여 .env 파일에 설정된 내용을 불러올 수 있습니다.

from dotenv import load_dotenv

load_dotenv()

이 설정이 완료되면 추적이 자동으로 활성화됩니다. 프로젝트 명을 변경하거나, 추적 설정을 변경하고 싶다면 다음 코드를 사용합니다:

import os

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_PROJECT"] = "변경할_프로젝트_명"
os.environ["LANGCHAIN_API_KEY"] = "변경할_API_KEY"

4. langchain-teddynote 패키지 설치 및 추적 설정

langchain-teddynote 패키지를 설치하여 LangChain 관련 기능을 더 편리하게 사용할 수 있습니다.

pip install langchain-teddynote

설치 후, 다음 코드를 사용하여 LangSmith 추적을 설정할 수 있습니다:

from langchain_teddynote import logging

# 프로젝트 이름을 입력하여 추적 시작
logging.langsmith("원하는_프로젝트_명")

LangSmith 추적 비활성화

추적을 원하지 않을 때는 다음과 같이 추적을 비활성화할 수 있습니다:

from langchain_teddynote import logging

# set_enable=False로 지정하여 추적 비활성화
logging.langsmith("프로젝트_명", set_enable=False)

이렇게 LangSmith를 설정하면 LLM 애플리케이션의 개발 과정에서 발생할 수 있는 다양한 문제를 효과적으로 추적하고 해결할 수 있습니다. 추적 데이터를 바탕으로 검색 알고리즘이나 프롬프트를 개선하여 더욱 효율적이고 정확한 응답을 생성할 수 있습니다.


🏖️ 03. OpenAI API 사용(GPT-4o 멀티모달)

환경 변수 설정 및 초기 설정

# API KEY를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API KEY 정보 로드
load_dotenv()
True

LangSmith 추적 설정

# LangSmith 추적을 설정합니다. https://smith.langchain.com
# .env 파일에 LANGCHAIN_API_KEY를 입력합니다.
# !pip install -qU langchain-teddynote
from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH01-Basic")

LangSmith 추적을 시작합니다.

[프로젝트명]
CH01-Basic

ChatOpenAI 사용하기

OpenAI 사의 채팅 전용 Large Language Model(LLM)이다. 객체를 생성할 때 다양한 옵션 값을 지정할 수 있으며, 주요 옵션에 대한 설명은 다음과 같다.

  • temperature: 사용할 샘플링 온도를 0과 2 사이에서 선택한다. 0.8과 같은 높은 값은 출력을 더 무작위하게 만들고, 0.2와 같은 낮은 값은 출력을 더 집중되고 결정론적으로 만든다.
  • max_tokens: 채팅 완성에서 생성할 토큰의 최대 개수이다.
  • model_name: 적용 가능한 모델 리스트
    • gpt-3.5-turbo
    • gpt-4-turbo
    • gpt-4o
from langchain_openai import ChatOpenAI

# 객체 생성
llm = ChatOpenAI(
    temperature=0.1,  # 창의성 (0.0 ~ 2.0)
    model_name="gpt-4o",  # 모델명
)

# 질의내용
question = "대한민국의 수도는 어디인가요?"

# 질의
print(f"[답변]: {llm.invoke(question)}")

[답변]: content='대한민국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 많은 인구와 다양한 명소를 자랑하는 도시입니다.'

답변의 형식(AI Message)

# 질의내용
question = "대한민국의 수도는 어디인가요?"

# 질의
response = llm.invoke(question)
response
AIMessage(content='대한민국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 많은 인구와 다양한 명소를 자랑하는 도시입니다.', response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 16, 'total_tokens': 52}, 'model_name': 'gpt-4o', 'system_fingerprint': 'fp_aa87380ac5', 'finish_reason': 'stop', 'logprobs': None}, id='run-3296402a-f47b-4ace-88cd-b74efb7465fb-0', usage_metadata={'input_tokens': 16, 'output_tokens': 36, 'total_tokens': 52})
response.content
# '대한민국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 많은 인구와 다양한 명소를 자랑하는 도시입니다.'
response.response_metadata
# {'token_usage': {'completion_tokens': 36,  'prompt_tokens': 16,  'total_tokens': 52}, 'model_name': 'gpt-4o', 'system_fingerprint': 'fp_aa87380ac5', 'finish_reason': 'stop', 'logprobs': None}

LogProb 활성화

LogProb는 주어진 텍스트에 대한 모델의 토큰 확률의 로그 값을 의미한다. 이는 문장을 구성하는 개별 단어나 문자의 확률을 예측하는 데 사용된다.

# 객체 생성
llm_with_logprob = ChatOpenAI(
    temperature=0.1,  # 창의성 (0.0 ~ 2.0)
    max_tokens=2048,  # 최대 토큰수
    model_name="gpt-3.5-turbo",  # 모델명
).bind(logprobs=True)
# 질의내용
question = "대한민국의 수도는 어디인가요?"

# 질의
response = llm_with_logprob.invoke(question)
# 결과 출력
response.response_metadata
# {'token_usage': {'completion_tokens': 15,  'prompt_tokens': 24,  'total_tokens': 39}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': {'content': [{'token': '대', 'bytes': [235, 140, 128], 'logprob': -0.03859115, 'top_logprobs': []}, {'token': '한', 'bytes': [237, 149, 156], 'logprob': -5.5122365e-07, 'top_logprobs': []}, {'token': '\\xeb\\xaf', 'bytes': [235, 175], 'logprob': -2.8160932e-06, 'top_logprobs': []}, {'token': '\\xbc', 'bytes': [188], 'logprob': 0.0, 'top_logprobs': []}, {'token': '\\xea\\xb5', 'bytes': [234, 181], 'logprob': -6.704273e-07, 'top_logprobs': []}, {'token': '\\xad', 'bytes': [173], 'logprob': 0.0, 'top_logprobs': []}, {'token': '의', 'bytes': [236, 157, 152], 'logprob': -6.2729996e-06, 'top_logprobs': []}, {'token': ' 수', 'bytes': [32, 236, 136, 152], 'logprob': -5.5122365e-07, 'top_logprobs': []}, {'token': '도', 'bytes': [235, 143, 132], 'logprob': -5.5122365e-07, 'top_logprobs': []}, {'token': '는', 'bytes': [235, 138, 148], 'logprob': -1.9361265e-07, 'top_logprobs': []}, {'token': ' 서', 'bytes': [32, 236, 132, 156], 'logprob': -5.080963e-06, 'top_logprobs': []}, {'token': '\\xec\\x9a', 'bytes': [236, 154], 'logprob': 0.0, 'top_logprobs': []}, {'token': '\\xb8', 'bytes': [184], 'logprob': 0.0, 'top_logprobs': []}, {'token': '입니다', 'bytes': [236, 158, 133, 235, 139, 136, 235, 139, 164], 'logprob': -0.13815464, 'top_logprobs': []}, {'token': '.', 'bytes': [46], 'logprob': -9.0883464e-07, 'top_logprobs': []}]}}

스트리밍 출력

스트리밍 옵션은 질의에 대한 답변을 실시간으로 받을 때 유용하다.

# 스트림 방식으로 질의
# answer에 스트리밍 답변의 결과를 받습니다.
answer = llm.stream("대한민국의 아름다운 관광지 10곳과 주소를 알려주세요!")
# 스트리밍 방식으로 각 토큰을 출력합니다. (실시간 출력)
for token in answer:
    print(token.content, end="", flush=True)

예시 출력

물론입니다! 대한민국에는 아름다운 관광지가 많이 있습니다. 다음은 그 중 10곳과 그 주소입니다:

1. **경복궁**
   - 주소: 서울특별시 종로구 사직로 161

2. **부산 해운대 해수욕장**
   - 주소: 부산광역시 해운대구 우동

3. **제주도 한라산 국립공원**
   - 주소: 제주특별자치도 제주시 1100로 2070-61

4. **경주 불국사**
   - 주소: 경상북도 경주시 불국로 385

5. **설악산 국립공원**
   - 주소: 강원도 속초시 설악산로 833

6. **남이섬**
   - 주소: 강원도 춘천시 남산면 남이섬길 1

7. **안동 하회마을**
   - 주소: 경상북도 안동시 풍천면 하회종가길 40

8. **전주 한옥마을**
   - 주소: 전라북도 전주시 완산구 기린대로 99

9. **서울 남산타워 (N서울타워)**
   - 주소: 서울특별시 용산구 남산공원길 105

10. **보성 녹차밭 대한다원**
    - 주소: 전라남도 보성군 보성읍 녹차

🏖️ 04. LangChain Expression Language(LCEL)

기본 예시: 프롬프트 + 모델 + 출력 파서

가장 기본적이고 일반적인 사용 사례는 PromptTemplate과 모델을 함께 연결하는 것입니다. 이번 예제에서는 각 나라별 수도를 물어보는 Chain을 생성해 보겠습니다.

# API KEY를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API KEY 정보 로드
load_dotenv()
True

LangSmith 추적 설정

# LangSmith 추적을 설정합니다. https://smith.langchain.com
# !pip install -qU langchain-teddynote
from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH01-Basic")

LangSmith 추적을 시작합니다.

[프로젝트명]
CH01-Basic

프롬프트 템플릿의 활용

PromptTemplate은 사용자의 입력 변수를 사용하여 완전한 프롬프트 문자열을 만드는 데 사용되는 템플릿입니다.

사용법:

  • template: 템플릿 문자열로, 중괄호 {}는 변수를 나타냅니다.
  • input_variables: 중괄호 안에 들어갈 변수의 이름을 리스트로 정의합니다.
from langchain_teddynote.messages import stream_response  # 스트리밍 출력
from langchain_core.prompts import PromptTemplate

# template 정의
template = "{country}의 수도는 어디인가요?"

# from_template 메소드를 이용하여 PromptTemplate 객체 생성
prompt_template = PromptTemplate.from_template(template)
prompt_template
# PromptTemplate(input_variables=['country'], template='{country}의 수도는 어디인가요?')

프롬프트 생성 예시:

# prompt 생성
prompt = prompt_template.format(country="대한민국")
print(prompt)
# '대한민국의 수도는 어디인가요?'

# 다른 국가로 프롬프트 생성
prompt = prompt_template.format(country="미국")
print(prompt)
# '미국의 수도는 어디인가요?'

ChatOpenAI 모델 연결

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="gpt-3.5-turbo",
    max_tokens=2048,
    temperature=0.1,
)

Chain 생성

LCEL(LangChain Expression Language)을 사용하여 다양한 구성 요소를 단일 체인으로 결합할 수 있습니다.

# prompt 를 PromptTemplate 객체로 생성합니다.
prompt = PromptTemplate.from_template("{topic} 에 대해 쉽게 설명해주세요.")

model = ChatOpenAI()

chain = prompt | model

invoke() 호출 예시:

# input 딕셔너리에 주제를 설정합니다.
input = {"topic": "인공지능 모델의 학습 원리"}

# 체인을 실행하여 AI 모델이 생성한 메시지를 반환합니다.
response = chain.invoke(input)
print(response)
# '인공지능 모델의 학습 원리는 데이터를 이용하여 패턴을 학습하는 것입니다...'

스트리밍 출력 예시

# 스트리밍 출력을 위한 요청
answer = chain.stream(input)
# 스트리밍 출력
stream_response(answer)

출력 예시:

인공지능 모델의 학습 원리는 데이터를 입력으로 받아서 패턴을 학습하고 이를 기반으로 예측이나 분류를 수행하는 과정입니다...

출력 파서(Output Parser)

from langchain_core.output_parsers import StrOutputParser

output_parser = StrOutputParser()

Chain에 출력 파서 추가:

# 프롬프트, 모델, 출력 파서를 연결하여 처리 체인을 구성합니다.
chain = prompt | model | output_parser

# 체인을 실행하여 결과를 얻습니다.
response = chain.invoke(input)
print(response)
# '인공지능 모델의 학습 원리는 데이터를 입력으로 받아서 패턴을 학습하는 것입니다...'

템플릿을 변경하여 적용

아래의 프롬프트 내용을 변경하거나, 모델명을 변경하여 테스트할 수 있습니다.

template = """
당신은 영어를 가르치는 10년차 영어 선생님입니다. 상황에 [FORMAT]에 영어 회화를 작성해 주세요.

상황:
{question}

FORMAT:
- 영어 회화:
- 한글 해석:
"""

# 프롬프트 템플릿을 이용하여 프롬프트를 생성합니다.
prompt = PromptTemplate.from_template(template)

# ChatOpenAI 챗모델을 초기화합니다.
model = ChatOpenAI(model_name="gpt-4-turbo")

# 문자열 출력 파서를 초기화합니다.
output_parser = StrOutputParser()

# 체인을 구성합니다.
chain = prompt | model | output_parser

실행 예시:

# 스트리밍 출력을 위한 요청
answer = chain.stream({"question": "저는 식당에 가서 음식을 주문하고 싶어요"})
# 스트리밍 출력
stream_response(answer)

출력 예시:

영어 회화:
- Hello, could I see the menu, please? 
- I'd like to order the grilled salmon and a side of mashed potatoes.
- Could I have a glass of water as well?
- Thank you!

한글 해석:
- 안녕하세요, 메뉴판 좀 볼 수 있을까요?
- 구운 연어와 매시드 포테이토를 주문하고 싶어요.
- 물 한 잔도 주실 수 있나요?
- 감사합니다!

또는 다른 시나리오를 설정하여 테스트할 수 있습니다.

# question을 '미국에서 피자 주문'으로 설정하여 실행합니다.
answer = chain.stream({"question": "미국에서 피자 주문"})
stream_response(answer)

출력 예시:

영어 회화:
- Employee: "Hello, Tony's Pizza. How can I help you?"
- Customer: "Hi, I'd like to place an order for delivery, please."
- Employee: "Sure thing! What would you like to order?"
- Customer: "I'll have a large pepperoni pizza with extra cheese and a side of garlic bread."
- Employee: "Anything to drink?"
- Customer: "Yes, a 2-liter bottle of Coke, please."
- Employee: "Alright, your total comes to $22.50. Can I have your delivery address?"
- Customer: "It's 742 Evergreen Terrace."
- Employee: "Thank you. Your order will be there in about 30-45 minutes. Is there anything else I can help you with?"
- Customer: "No, that's everything. Thank you!"
- Employee: "Thank you for choosing Tony's Pizza. Have a great day!"

한글 해석:
- 직원: "안녕하세요, 토니의 피자입니다. 어떻게 도와드릴까요?"
- 고객: "안녕하세요, 배달 주문하고 싶은데요."
- 직원: "네, 무엇을 주문하시겠어요?"
- 고객: "큰 사이즈의 페퍼로니 피자에 치즈 추가하고, 마늘빵 하나 주세요."
- 직원: "음료는 드릴까요?"
- 고객: "네, 콜라 2리터 한 병 주세요."
- 직원: "알겠습니다, 합계는 $22.50입니다. 배달 주소를 알려주시겠어요?"
- 고객: "742 에버그린 테라스입니다."
- 직원: "감사합니다. 주문하신 음식은 대략 30-45분 내에 도착할 예정입니다. 다른 도움이 필요하신가요?"
- 고객: "아니요, 이게 다예요. 감사합니다!"
- 직원: "토니의 피자를 선택해주셔서 감사합니다. 좋은 하루 되세요!"

🏖️ 05. LCEL 인터페이스

LCEL 인터페이스 소개

LCEL(LangChain Expression Language)은 사용자 정의 체인을 쉽게 만들 수 있도록 돕는 Runnable 프로토콜을 구현하고 있습니다. 이 프로토콜은 다양한 컴포넌트에 적용되며, 표준화된 인터페이스로 체인을 정의하고 호출할 수 있게 해줍니다. 주요 메서드에는 다음이 포함됩니다:

  • stream: 응답의 청크를 실시간으로 스트리밍합니다.
  • invoke: 입력에 대해 체인을 호출합니다.
  • batch: 입력 목록에 대해 체인을 호출합니다.
  • 비동기 메서드:
    • astream: 비동기적으로 응답의 청크를 스트리밍합니다.
    • ainvoke: 비동기적으로 입력에 대해 체인을 호출합니다.
    • abatch: 비동기적으로 입력 목록에 대해 체인을 호출합니다.
    • astream_log: 최종 응답뿐만 아니라 발생하는 중간 단계를 비동기적으로 스트리밍합니다.

초기 설정

# API KEY를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API KEY 정보 로드
load_dotenv()
True

# LangSmith 추적을 설정합니다. https://smith.langchain.com
from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH01-Basic")

LCEL 문법을 사용한 체인 생성

from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

# ChatOpenAI 모델을 인스턴스화합니다.
model = ChatOpenAI()

# 주어진 토픽에 대한 설명을 요청하는 프롬프트 템플릿을 생성합니다.
prompt = PromptTemplate.from_template("{topic} 에 대하여 3문장으로 설명해줘.")

# 프롬프트와 모델을 연결하여 대화 체인을 생성합니다.
chain = prompt | model | StrOutputParser()

실시간 출력 (stream)

# chain.stream 메서드를 사용하여 '멀티모달' 토픽에 대한 스트림을 생성하고 반복합니다.
for token in chain.stream({"topic": "멀티모달"}):
    print(token, end="", flush=True)

출력 예시:

멀티모달은 여러 가지 다른 형태의 커뮤니케이션 수단을 통해 정보를 전달하고 상호작용하는 기술을 의미합니다...

호출 (invoke)

# chain 객체의 invoke 메서드를 호출하고, 'ChatGPT'라는 주제로 딕셔너리를 전달합니다.
response = chain.invoke({"topic": "ChatGPT"})
print(response)

출력 예시:

'ChatGPT는 OpenAI에서 개발한 대화형 인공지능 모델로, 다양한 주제에 대한 대화를 자연스럽게 이어나갈 수 있습니다...'

배치(단위 실행) (batch)

# 주어진 토픽 리스트를 batch 처리하는 함수 호출
responses = chain.batch([{"topic": "ChatGPT"}, {"topic": "Instagram"}])
print(responses)

출력 예시:

['ChatGPT는 인공지능 챗봇으로 자연어 처리 기술을 사용하여 대화를 수행합니다...', 'Instagram은 사진과 동영상을 공유하고 다른 사람들과 소통하는 소셜 미디어 플랫폼입니다...']

비동기 스트림 (async stream)

# 비동기 스트림을 사용하여 'YouTube' 토픽의 메시지를 처리합니다.
async for token in chain.astream({"topic": "YouTube"}):
    print(token, end="", flush=True)

출력 예시:

YouTube는 동영상을 공유하고 시청할 수 있는 온라인 동영상 플랫폼이다...

비동기 호출 (async invoke)

# 비동기 체인 객체의 'ainvoke' 메서드를 호출하여 'NVDA' 토픽을 처리합니다.
response = await chain.ainvoke({"topic": "NVDA"})
print(response)

출력 예시:

'NVDA는 엔비디아의 주식 코드로, 미국의 반도체 기업인 엔비디아(NVIDIA)의 주식을 말합니다...'

비동기 배치 (async batch)

# 주어진 토픽에 대해 비동기적으로 일괄 처리를 수행합니다.
responses = await chain.abatch(
    [{"topic": "YouTube"}, {"topic": "Instagram"}, {"topic": "Facebook"}]
)
print(responses)

출력 예시:

['YouTube는 동영상 공유 플랫폼으로 사용자들이 영상을 업로드하고 시청할 수 있는 서비스입니다...', 'Instagram은 사진과 동영상을 공유하는 소셜 미디어 플랫폼입니다...', 'Facebook은 미국의 소셜 네트워크 서비스로, 사용자들이 커뮤니케이션하고 정보를 공유할 수 있는 플랫폼입니다...']

병렬성 (Parallel)

from langchain_core.runnables import RunnableParallel

# {country}의 수도를 물어보는 체인을 생성합니다.
chain1 = (
    PromptTemplate.from_template("{country}의 수도는 어디야?")
    | model
    | StrOutputParser()
)

# {country}의 면적을 물어보는 체인을 생성합니다.
chain2 = (
    PromptTemplate.from_template("{country}의 면적은 얼마야?")
    | model
    | StrOutputParser()
)

# 위의 2개 체인을 동시에 생성하는 병렬 실행 체인을 생성합니다.
combined = RunnableParallel(capital=chain1, area=chain2)

병렬 실행 예시:

response = combined.invoke({"country": "대한민국"})
print(response)

출력 예시:

{'capital': '대한민국의 수도는 서울입니다.', 'area': '대한민국의 면적은 약 100,363.4 제곱 킬로미터 입니다.'}

병렬 처리와 배치 결합

responses = combined.batch([{"country": "대한민국"}, {"country": "미국"}])
print(responses)

출력 예시:

[{'capital': '대한민국의 수도는 서울이다.', 'area': '대한민국의 면적은 약 100,363km² 입니다.'}, {'capital': '미국의 수도는 워싱턴 D.C.입니다.', 'area': '미국의 면적은 약 9,833,520 km² 입니다.'}]

🏖️ 06. Runnable

Runnable은 LangChain에서 체인을 구성하고 실행할 수 있는 중요한 구성 요소입니다. 이 구성 요소는 다양한 형태로 입력을 처리하고, 이를 다른 컴포넌트로 전달하여 복잡한 작업을 수행할 수 있게 해줍니다.

환경 설정

# .env 파일을 읽어서 환경변수로 설정
from dotenv import load_dotenv

# 토큰 정보 로드
load_dotenv()
True

LangSmith 추적 설정

# LangSmith 추적을 설정합니다. https://smith.langchain.com
from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH01-Basic")

LangSmith 추적을 시작합니다.

[프로젝트명]
CH01-Basic

데이터 전달 방법

RunnablePassthrough

RunnablePassthrough는 입력을 변경하지 않거나 추가 키를 더하여 전달하는 방식으로 동작합니다. 단독 호출 시 입력을 그대로 전달하며, assign() 메서드를 사용하면 입력에 새로운 값을 추가할 수 있습니다.

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# prompt 와 llm 을 생성합니다.
prompt = PromptTemplate.from_template("{num} 의 10배는?")
llm = ChatOpenAI(temperature=0)

# chain 을 생성합니다.
chain = prompt | llm

체인 실행 예시:

# 딕셔너리 타입으로 입력하여 실행
response = chain.invoke({"num": 5})
print(response)

# 값만 전달하여 실행 (1개의 변수만 템플릿에 포함된 경우)
response = chain.invoke(5)
print(response)

RunnablePassthrough 사용 예제

from langchain_core.runnables import RunnablePassthrough

# RunnablePassthrough 단독 실행
response = RunnablePassthrough().invoke({"num": 10})
print(response)

RunnablePassthrough를 체인에 적용:

runnable_chain = {"num": RunnablePassthrough()} | prompt | ChatOpenAI()
response = runnable_chain.invoke(10)
print(response)

RunnablePassthrough.assign() 사용 예제

response = RunnablePassthrough.assign(new_num=lambda x: x["num"] * 3).invoke({"num": 1})
print(response)

RunnableParallel

RunnableParallel은 여러 Runnable 인스턴스를 병렬로 실행할 수 있는 기능을 제공합니다.

from langchain_core.runnables import RunnableParallel

runnable = RunnableParallel(
    passed=RunnablePassthrough(),
    extra=RunnablePassthrough.assign(mult=lambda x: x["num"] * 3),
    modified=lambda x: x["num"] + 1,
)

response = runnable.invoke({"num": 1})
print(response)

RunnableLambda

RunnableLambda는 사용자 정의 함수를 Runnable 객체로 맵핑하는 기능을 제공합니다.

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from datetime import datetime

def get_today(a):
    return datetime.today().strftime("%b-%d")

# 오늘 날짜를 출력
print(get_today(None))

# prompt 와 llm 을 생성하고 chain 을 설정합니다.
prompt = PromptTemplate.from_template(
    "{today} 가 생일인 유명인 {n} 명을 나열하세요. 생년월일을 표기해 주세요."
)
llm = ChatOpenAI(temperature=0, model_name="gpt-4o")

chain = (
    {"today": RunnableLambda(get_today), "n": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# 출력
print(chain.invoke(3))

itemgetter를 사용한 특정 키 추출

from operator import itemgetter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
from langchain_openai import ChatOpenAI

def length_function(text):
    return len(text)

def _multiple_length_function(text1, text2):
    return len(text1) * len(text2)

def multiple_length_function(_dict):
    return _multiple_length_function(_dict["text1"], _dict["text2"])

prompt = ChatPromptTemplate.from_template("{a} + {b} 는 무엇인가요?")
model = ChatOpenAI()

chain = (
    {
        "a": itemgetter("word1") | RunnableLambda(length_function),
        "b": {"text1": itemgetter("word1"), "text2": itemgetter("word2")}
        | RunnableLambda(multiple_length_function),
    }
    | prompt
    | model
)

response = chain.invoke({"word1": "hello", "word2": "world"})
print(response)

출력 예시:

AIMessage(content='5 + 25 = 30입니다.', response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 22, 'total_tokens': 31}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-5db9b475-09ee-4edb-af9d-b37b320bee1e-0', usage_metadata={'input_tokens': 22, 'output_tokens': 9, 'total_tokens': 31})

이 예제를 통해 LangChain에서 Runnable을 활용하여 다양한 입력 데이터를 처리하고, 복잡한 체인을 구성하는 방법을 이해할 수 있습니다.

profile
AI Engineer / 의료인공지능

0개의 댓글

관련 채용 정보