LangChain 독학 가이드 4 - LCEL 체인 만들기
이전 글에서 프롬프트 템플릿을 배웠죠? 근데 템플릿만 있으면 뭐해요, 실제로 AI한테 보내서 답변을 받아야죠. 오늘은 프롬프트 → 모델 → 출력을 하나로 연결하는 **LCEL(LangChain Expression Language)**을 배워볼 거예요.
LCEL이 처음엔 좀 낯설 수 있는데, 알고 보면 진짜 직관적이에요. 리눅스 터미널에서 | (파이프) 쓰듯이 컴포넌트들을 연결하는 거거든요.
LCEL이 뭔가요?
LCEL은 LangChain의 컴포넌트들을 체인처럼 연결하는 문법이에요. 핵심은 **파이프 연산자 |**입니다.
프롬프트 | 모델 | 출력파서
데이터가 왼쪽에서 오른쪽으로 흘러가는 거예요:
- 프롬프트에 변수를 넣으면
- 모델이 답변을 생성하고
- 출력 파서가 결과를 정리해줌
첫 번째 체인 만들기
가장 기본적인 체인부터 만들어볼게요:
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
load_dotenv()
# 1. 프롬프트 템플릿
prompt = ChatPromptTemplate.from_template(
"{topic}에 대해 한 문장으로 설명해줘."
)
# 2. 모델
model = ChatOpenAI(model="gpt-3.5-turbo")
# 3. 출력 파서
output_parser = StrOutputParser()
# 체인 연결!
chain = prompt | model | output_parser
# 실행
result = chain.invoke({"topic": "인공지능"})
print(result)
실행 결과:
prompt | model | output_parser 이 한 줄이 핵심이에요! 세 개의 컴포넌트가 파이프로 연결된 거죠.
invoke, batch, stream
체인을 실행하는 방법이 세 가지 있어요:
invoke - 하나씩 실행
# 하나의 입력 처리
result = chain.invoke({"topic": "블록체인"})
print(result)
실행 결과:
batch - 여러 개 한번에
# 여러 입력을 한번에 처리
results = chain.batch([
{"topic": "클라우드"},
{"topic": "메타버스"},
{"topic": "양자컴퓨터"}
])
for r in results:
print(f"- {r}")
실행 결과:
stream - 실시간 출력
ChatGPT처럼 글자가 하나씩 나오게 하고 싶다면:
# 스트리밍 출력
for chunk in chain.stream({"topic": "머신러닝"}):
print(chunk, end="", flush=True)
실행 결과:
RunnablePassthrough - 입력 그대로 전달
가끔 입력값을 그대로 다음 단계로 전달해야 할 때가 있어요. 그때 RunnablePassthrough를 써요:
from langchain_core.runnables import RunnablePassthrough
# 원본 질문과 답변을 함께 출력하고 싶을 때
prompt = ChatPromptTemplate.from_template(
"질문: {question}\n\n위 질문에 대해 답변해주세요."
)
chain = (
{"question": RunnablePassthrough()}
| prompt
| model
| output_parser
)
result = chain.invoke("파이썬이 뭐야?")
print(result)
실행 결과:
RunnableParallel - 병렬 실행
여러 작업을 동시에 처리하고 싶을 때 사용해요:
from langchain_core.runnables import RunnableParallel
# 장점과 단점을 동시에 분석
pros_prompt = ChatPromptTemplate.from_template(
"{topic}의 장점 3가지를 간단히 말해줘."
)
cons_prompt = ChatPromptTemplate.from_template(
"{topic}의 단점 3가지를 간단히 말해줘."
)
pros_chain = pros_prompt | model | output_parser
cons_chain = cons_prompt | model | output_parser
# 병렬 실행
parallel_chain = RunnableParallel(
pros=pros_chain,
cons=cons_chain
)
result = parallel_chain.invoke({"topic": "재택근무"})
print("=== 장점 ===")
print(result["pros"])
print("\n=== 단점 ===")
print(result["cons"])
실행 결과:
장점과 단점 분석이 동시에 실행돼서 시간이 절약돼요!
체인 안에 체인 넣기
체인은 중첩해서 사용할 수도 있어요:
# 첫 번째 체인: 주제 추출
extract_prompt = ChatPromptTemplate.from_template(
"다음 문장에서 핵심 주제를 한 단어로 추출해줘: {text}"
)
extract_chain = extract_prompt | model | output_parser
# 두 번째 체인: 주제 설명
explain_prompt = ChatPromptTemplate.from_template(
"{topic}에 대해 초등학생도 이해할 수 있게 설명해줘."
)
explain_chain = explain_prompt | model | output_parser
# 체인 연결
full_chain = (
{"topic": extract_chain}
| explain_prompt
| model
| output_parser
)
result = full_chain.invoke({
"text": "요즘 ChatGPT 때문에 AI가 화제야"
})
print(result)
실행 결과:
첫 번째 체인에서 “AI”라는 주제를 추출하고, 두 번째 체인에서 그걸 설명하는 거예요.
실전 예시: 번역 체인
실제로 쓸만한 번역 체인을 만들어볼게요:
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
translate_prompt = ChatPromptTemplate.from_messages([
("system", "당신은 전문 번역가입니다. 자연스럽고 정확하게 번역해주세요."),
("human", "다음 텍스트를 {target_lang}로 번역해주세요:\n\n{text}")
])
model = ChatOpenAI(model="gpt-3.5-turbo")
parser = StrOutputParser()
translate_chain = translate_prompt | model | parser
# 한국어 → 영어
result = translate_chain.invoke({
"target_lang": "영어",
"text": "오늘 날씨가 정말 좋네요. 산책하기 딱 좋은 날이에요."
})
print(result)
실행 결과:
정리
오늘 배운 LCEL 핵심:
- 파이프 연산자
|: 컴포넌트를 연결 - invoke: 하나의 입력 처리
- batch: 여러 입력 한번에 처리
- stream: 실시간 스트리밍 출력
- RunnablePassthrough: 입력값 그대로 전달
- RunnableParallel: 여러 체인 병렬 실행
LCEL은 LangChain의 핵심 문법이에요. 이걸 알아야 RAG, Agent 같은 복잡한 기능도 만들 수 있습니다. 다음 글에서는 다양한 LLM(GPT, Claude, Gemini 등)을 연결하는 방법을 알아볼게요!