BIM 칼럼니스트 강태욱의 이슈 & 토크
이번 호에서는 랭체인(LangChain) 아키텍처와 동작 방법을 분석한다. 현재 챗GPT(ChatGPT)와 비슷한 인공지능 챗봇 서비스 개발 등에 대중적으로 사용되는 랭체인은 LLM(Large Language Model : 대규모 언어 모델) 통합과 PDF 등 다양한 데이터 소스를 지원하여 LLM 모델 활용성을 극대화한다. 이 글을 통해 LLM 서비스 개발에 필요한 랭체인의 아키텍처와 동작 원리를 이해할 수 있을 것이다.
■ 강태욱
건설환경 공학을 전공하였고 소프트웨어 공학을 융합하여 세상이 돌아가는 원리를 분석하거나 성찰하기를 좋아한다. 건설과 소프트웨어 공학의 조화로운 융합을 추구하고 있다. 팟캐스트 방송을 통해 이와 관련된 작은 메시지를 만들어 나가고 있다. 현재 한국건설기술연구원에서 BIM/GIS/FM/BEMS/역설계 등과 관련해 연구를 하고 있으며, 연구위원으로 근무하고 있다.
페이스북 | www.facebook.com/laputa999
홈페이지 | https://dxbim.blogspot.com
팟캐스트 | http://www.facebook.com/groups/digestpodcast
그림 1
랭체인은 LLM에 원하는 결과를 얻을 수 있도록 다양한 프롬프트 입력 및 구조화된 출력, RAG, 튜닝과 같은 기능을 제공하는 라이브러리다. 랭체인 설치는 다음과 같이 진행할 수 있다.
pip install langchain
랭체인의 기본 사용법
랭체인은 모델 입출력, 데이터 검색, 에이전트 지원, 체인, 컨텍스트 메모리 기능을 제공하며, LCEL(LangChain Expression Language)을 이용해 각 구성요소를 유기적으로 연결시킬 수 있다. LCEL은 유닉스 파이프라인 개념을 차용했다. 다음은 LCEL의 예시를 보여준다.
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import BaseOutputParser
# LCEL 예시
chain = ChatPromptTemplate() | ChatOpenAI() | CustomOutputParser()
이와 더불어 목적에 맞는 다양한 프롬프트 템플릿, 구조화된 출력을 제공한다.
from langchain.output_parsers.json import SimpleJsonOutputParser
json_prompt = PromptTemplate.from_template(
"Return a JSON object with `birthdate` and `birthplace` key that answers the following question: {question}"
)
json_parser = SimpleJsonOutputParser() # JSON 파서
# 프롬프트, 모델, 파서 체인 생성
json_chain = json_prompt | model | json_parser # 유닉스 파이프라인 개념 차용함.
result_list = list(json_chain.stream({"question": "When and where was Elon Musk born?"}))
print(result_list)
그림 2
랭체인 구조 분석
패키지 구조
랭체인 구조를 분석하기 위해, 깃허브(GitHub)의 랭체인 소스코드를 다운로드한 후 UML로 모델링해 본다. 주요 패키지는 <그림 3>과 같다.
- 랭체인 소스코드 : https://github.com/langchain-ai/langchain
그림 3
cli는 랭체인의 커맨드 라인 인터페이스(command line interface), core는 랭체인의 핵심 구현 코드가 정의된다. 이 부분은 <그림 4>와 같은 패키지로 구성된다.
그림 4
참고로, 이 패키지들은 <그림 5>의 일부이다.
그림 5. 랭체인 v.0.2.0 패키지
LCEL 언어 동작 구조
이 중에 핵심적인 것만 분석해 본다. 우선, LCEL의 동작 방식을 위해 어떤 디자인 패턴을 구현하였는지 확인한다. 이 부분은 runnables 패키지가 담당한다. 이 언어는 유닉스의 파이프라인 처리를 다음과 같이 흉내낸다.
z = a | b | c
z.stream('abc')
이를 위해 파이썬(Python) 문법을 적극 사용하고 있다. 우선 ‘|’ 연산자를 오버로딩(overloading)하기 위해, 파이썬 Runnable 클래스를 정의해 ‘__or__’ 연산자를 구현한다. 이 연산자는 self object와 right object 두 객체를 입력받아 리스트를 만든 후 리턴하는 역할을 한다. 앞의 예시에서 보면, ‘a | b’를 실행 가능한 객체 리스트로 만들어 리턴한다. 결론적으로 a, b, c 객체를 리스트로 만들고 이 리스트를 z에 할당한다.
■ 자세한 기사 내용은 PDF로 제공됩니다.