LangChain 입문 4편 - LCEL 체인 만들기

처음에 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)
실행 결과:
운영자 실전 노트
실제 프로젝트 진행하며 겪은 문제
- 파이프라인 중간에 에러 나도 어디서 났는지 몰라서 디버깅 지옥 → verbose=True 설정하면 중간 과정 다 보임
- 체인 순서 잘못 연결해서 output이 이상함 → 각 단계 input/output 타입 확인 필수
이 경험을 통해 알게 된 점
- 디버깅 팁: 체인 끊어서 단계별로 print() 찍어보기,
chain.invoke()결과를 변수에 담아 확인 - 타입 체크: prompt는 dict를 받고, model은 messages를 받고, parser는 AIMessage를 받는다는 걸 알아야 함
정리
오늘 배운 LCEL 핵심:
- 파이프 연산자
|: 컴포넌트를 연결 - invoke: 하나의 입력 처리
- batch: 여러 입력 한번에 처리
- stream: 실시간 스트리밍 출력
- RunnablePassthrough: 입력값 그대로 전달
- RunnableParallel: 여러 체인 병렬 실행
LCEL은 LangChain의 핵심 문법이다. 이걸 알아야 RAG, Agent 같은 복잡한 기능도 만들 수 있다.
batch 기능 진짜 편하다. 여러 건 한번에 처리하면 API 호출 최적화도 되고 시간도 절약된다. 대량 데이터 처리할 때 invoke 반복문 돌리지 말고 batch를 써보자.
다음 글에서는 다양한 LLM(GPT, Claude, Gemini 등)을 연결하는 방법을 알아본다!