1. LCEL 소개
1.1. LCEL 이란
LCEL(LangChain Expression Language)은 LangChain 내에서 여러 컴포넌트를 연결하는 선언적 방식의 언어입니다. 복잡한 체인을 구현하고 프로토타입에서 생산 환경으로 바로 적용할 수 있게 설계되었습니다.
LCEL은 간단한 "프롬프트 + LLM" 체인부터 수백 단계에 이르는 복잡한 체인까지 지원하며, 이러한 체인을 운영 환경에서 사용할 수 있 도록 설계되었습니다. 이를 통해, 코드 변경 없이 바로 프로토타입을 운영 환경에 적용할 수 있는 강력한 도구를 제공합니다.
1.2. LCEL의 장점
1) 프로토타입에서 운영 환경으로의 간편한 전환: LCEL은 프로토타입 단계에서 운영 환경으로 이동할 때 별도의 코드 변경이 필요하지 않습니다. 이는 개발 초기 단계에서 실험한 체인들을 그대로 운영 환경에서도 사용할 수 있게 하여, 개발 시간을 크게 단축하고 오류를 줄입니다.
2) 고성능 스트리밍 지원: LCEL LLM의 응답을 실시간으로 스트리밍하여 첫 번째 토큰이 생성되는 즉시 데이터를 받을 수 있습니다. 이 기능은 응답 시간이 중요한 애플리케이션에서 매우 유용하며, 사용자는 빠르게 처리된 결과를 확인할 수 있습니다.
3) 비동기 및 병렬 처리 지원: LCEL은 비동기 API를 통해 여러 요청을 동시에 처리할 수 있으며, 체인 내에서 병렬로 처리할 수 있는 단계가 있으면 이를 자동으로 최적화합니다. 예를 들어, 다수의 데이터 소스를 동시에 조회해야 하는 상황에서 전체 처리 시간을 크게 단축 할 수 있습니다.
4) 높은 신뢰성과 복구 기능: 체인 실행 도중 오류가 발생했을 때 LCEL 은 자동으로 재시도를 설정하거나 폴백(fallback) 경로를 지정할 수 있습니다. 이를 통해 시스템의 신뢰성을 높이고, 대규모 운영 환경 에서도 오류가 발생할 확률을 줄일 수 있습니다.
5) 중간 결과 확인 및 디버깅 용이: LCEL을 사용하면 복잡한 체인의 중간 결과를 실시간으로 확인할 수 있어, 전체 체인이 완료되기 전에 진행 상황을 모니터링하거나, 문제가 발생한 부분을 빠르게 찾아내어 디버깅할 수 있습니다. 이를 통해 체인의 투명성과 유지 관리가 용이해집니다.
2. Runnable
2.1. Runnable 이란
LangChain에서 Runnable은 체인을 구성하는 컴포넌트 또는 기능을 정의하는 인터페이스입니다. 간단하게 말해, Runnable은 실행 가능한 작업을 의미하며, 이를 통해 입력 데이터를 처리하여 결과를 출력하는 동작을 수행합니다. 각 Runnable은 독립적으로 작동하며, 다른 Runnable과 결합해 복잡한 체인을 만들 수 있습니다.
LangChain에서 체인의 각 단계는 다양한 Runnable로 구성될 수 있으며, 이를 사용하여 모델 호출, 데이터 처리, 응답 형식화 등의 작업을 할 수 있습니다. 이러한 구조는 개발자가 다양한 기능을 유연하게 조합하고, 코드의 재사용성을 높이도록 설계되었습니다.
2.2. 컴포넌트 종류
아래는 일부 대표적인 컴포넌트 종류 입니다. 컴포넌트의 종류에 따라 입력값과 출력값의 형태는 달라질 수 있습니다.
- Prompt: 주어진 입력 데이터를 바탕으로 LLM에 전달할 프롬프트를 생성하는 컴포넌트입니다. 템플릿을 활용해 다양한 상황에 맞는 프롬프트를 동적으로 생성할 수 있으며, 프롬프트 템플릿을 통해 입력 변수를 효과적으로 처리할 수 있습니다. 이를 통해 LLM의 응답을 원하는 형태로 이끌어낼 수 있습니다.
- ChatModel: 대화형 모델을 다루는 컴포넌트입니다. 이 컴포넌트는 대화를 기반으로 한 응답을 생성하며, 다중 턴 대화를 지원할 수 있습니다. 대화 맥락을 유지하면서 사용자와의 상호작용을 가능하게 해줍니다. ChatGPT와 같은 대화형 모델이 대표적이며, 여러 메시지와 상호작용하면서 대화 흐름을 관리합니다.
- LLM: 대형 언어 모델(Large Language Model)을 호출하는 컴포넌트입니다. 이 컴포넌트는 프롬프트를 입력으로 받아 자연어 처리 모델을 통해 텍스트 응답을 생성합니다. LLM은 주로 텍스트 생성, 요약, 번역, 질의응답 등에 활용되며, GPT-3, GPT-4와 같은 모델이 여기에 해당합니다.
- OutputParser: LLM의 응답을 특정 형식으로 변환하는 컴포넌트입니다. LLM이 생성한 텍스트는 종종 후 처리가 필요하며, OutputParser는 이 텍스트를 필요한 형식으로 파싱합니다. 예를 들어, LLM 응답을 JSON 형식으로 변환하거나 필요한 정보만 추출하는 작업에 사용됩니다.
- Retriever: 문서나 정보를 검색하고 해당 데이터를 가져오는 컴포넌트입니다. 검색 엔진처럼 작동하여 대 규모 데이터베이스나 문서 저장소에서 필요한 정보를 찾아 LLM에 제공하는 역할을 합니다. 정보 검색 기 반 응답 시스템에서 중요한 역할을 하며, 벡터 검색을 통해 유사한 문서나 데이터를 찾아낼 수 있습니다.
- Tool: LLM이 작업을 수행하는 데 사용할 수 있는 외부 도구나 API입니다. 예를 들어 계산기, 번역기, 웹 검 색 엔진 등이 Tool로서 통합될 수 있으며, LLM이 직접 이러한 도구들을 호출하여 특정 작업을 처리할 수 있습니다. 이를 통해 언어 모델이 처리하지 못하는 복잡한 계산이나 외부 서비스와의 상호작용을 지원합니다.
2.3 코드 예시
ChatModel을 정의하고, 이것이 Runnable 인터페이스를 상속받았는지 확인해 봅니다.
from langchain_openai import ChatOpenAI
from langchain.schema.runnable import Runnable # Runnable 임포트
# ChatOpenAI 인스턴스 생성
model = ChatOpenAI(model="gpt-4o-mini")
# model이 Runnable인지 확인
is_runnable = isinstance(model, Runnable)
print(f"ChatOpenAI is Runnable: {is_runnable}")
ChatOpenAI is Runnable: True
RunnableLambda를 이용해 Runnable을 만듭니다.
from langchain_core.runnables import RunnableLambda
runnable = RunnableLambda(lambda x: str(x))
# model이 Runnable인지 확인
is_runnable2 = isinstance(runnable, Runnable)
print(f"is_runnable2 is Runnable: {is_runnable2}")
is_runnable2 is Runnable: True
3. Runnable 실행
3.1.invoke
invoke 메서드는 단일 입력에 대해 LCEL 체인을 실행할 때 사용됩니다. 주어진 입력 데이터를 사용해 체인을 한 번 호출하고, 그에 대한 결과를 반환합니다. 주로 입력이 하나일 때 사용되며, 가장 기본적인 실행 방식입니다.
result = chain.invoke(input_data)
3.2. batch
batch 메서드는 여러 입력에 대해 LCEL 체인을 병렬로 처리할 수 있도록 도와줍니다. 즉, 한 번에 여러 개의 입력값을 받아 각 입력에 대해 독 립적으로 체인을 실행한 후, 그 결과들을 리스트 형태로 반환합니다. 이 메서드는 여러 데이터를 동시에 처리해야 할 때 유용합니다.
results= 1cel_chain.batch([input_data1, input_data2, input_data3])
3.3. stream
stream 메서드는 LCEL 체인에서 대규모 데이터를 처리할 때, 결과를 스트리밍 방식으로 실시간으로 받을 수 있게 해줍니다. 즉, 모든 결과를 한꺼번에 기다리지 않고, 입력에 대한 결과를 순차적으로 스트리밍하여 처리할 수 있습니다. 이는 실시간 응답이 중요한 애플리케이션에서 매우 유용합니다.
for result in lcel_chain.stream(input_data):
print(result)
4. 체인 연결
4.1. | 연산자
| 연산자를 이용해 두 개의 Runnable을 연결하는 방식은 체인을 구성 하는 방법 중 하나입니다. LangChain에서는 여러 Runnable 객체들을 연결하여 복잡한 흐름을 구성할 수 있는데, 이 연결을 체인(chain)이라고 부릅니다.
구체적으로 설명하자면, 앞의 Runnable이 실행된 결과를 바로 뒤의 Runnable로 전달하는 방식으로 동작합니다. 즉, 첫 번째 Runnable의 출력이 두 번째 Runnable의 입력으로 이어지며, 이를 통해 여러 단계를 순차적으로 실행하는 체인을 만들 수 있습니다.
runnable1 = RunnableLambda(lambda x: {"foo": x})
runnable2 = RunnableLambda(lambda x: [x] * 2)
chain = runnable1 | runnable2
result = chain.invoke(2)
print(result)
[{'foo': 2}, {'foo': 2}]
5. 병렬 실행
5.1. RunnableParallel 설명
RunnableParallel은 여러 Runnable 객체를 병렬로 실행하므로, 각각의 작업을 동시에 처리할 수 있습니다. 이는 특히 여러 작업을 동시에 수행 해야 하는 경우 유용하며, 처리 시간을 크게 단축할 수 있습니다.
입력값은 각각의 Runnable에 동일하게 전달됩니다. 각 Runnable은 동일한 입력에 대해 개별적으로 작업을 수행하고, 그 결과를 반환합니다.
각 Runnable의 결과는 병렬로 처리된 후, 리스트 또는 딕셔너리 형식 으로 반환됩니다. 반환된 결과는 각 Runnable이 처리한 결과를 나열한 형태로 제공됩니다.
from langchain_core.runnables import RunnableLambda, RunnableParallel
runnable1 = RunnableLambda(lambda x: {"foo": x})
runnable2 = RunnableLambda(lambda x: [x] * 2)
chain = RunnableParallel(first=runnable1, second=runnable2)
result = chain.invoke(2)
print(result)
{'first': {'foo': 2}, 'second': [2, 2]}
6. runnable의 그래프
6.1. Runnable의 그래프를 만드는 방법
LangChain의 Runnable은 다양한 작업들을 연결하고 병렬 처리할 수 있는 기능을 제공합니다. 이러한 연결과 병렬 처리를 시각적으로 이해 하기 위해 그래프를 생성하는 방법이 유용합니다. 그래프는 각 Runnable 간의 데이터 흐름과 의존성을 시각화하여, 처리 과정의 구조를 직관적으로 파악할 수 있게 해줍니다.
그래프를 생성하는 주요 단계는 다음과 같습니다.
1) Runnable 정의
2) Runnable 연결
3) 그래프 생성: 연결된 Runnable 체인을 통해 get_graph()메서드를 호출하여 그래프를 생성합니다.
4) 그래프 출력: 생성된 그래프를 print_ascii() 메서드를 사용하여 텍스 트 형식으로 시각화할 수 있습니다.
from langchain_core.runnables import RunnableLambda, RunnableParallel
runnable1 = RunnableLambda(lambda x: {"foo": x})
runnable2 = RunnableLambda(lambda x: [x] * 2)
runnable3 = RunnableLambda(lambda x: str(x))
chain = runnable1 | RunnableParallel(second=runnable2, third=runnable3)
chain.get_graph().print_ascii()
+-------------+
| LambdaInput |
+-------------+
*
*
*
+--------+
| Lambda |
+--------+
*
*
*
+-----------------------------+
| Parallel<second,third>Input |
+-----------------------------+
* *
** **
* *
+--------+ +--------+
| Lambda | | Lambda |
+--------+ +--------+
* *
** **
* *
+------------------------------+
| Parallel<second,third>Output |
+------------------------------+
'딥러닝 > 딥러닝: 자연어' 카테고리의 다른 글
RAG 시스템 파이프라인 2 : 운영 단계 (3) | 2025.06.08 |
---|---|
RAG 시스템 파이프라인 1 : 데이터 준비 단계 (2) | 2025.06.08 |
Prompt Templates (0) | 2025.06.04 |
Langchain 소개 (0) | 2025.06.03 |
RAG와 Langchain 기초 (3) | 2025.06.03 |