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

(수정: ) learning by Seven Fingers Studio 15분
LangChainLCELChainPythonAI

langchain guide 04 lcel

처음에 LCEL 문법 보고 “이게 뭐야?” 했는데, 써보니까 진짜 직관적이더라고요. 리눅스 파이프 써본 사람이면 바로 이해됩니다.

이전 글에서 프롬프트 템플릿을 배웠죠? 근데 템플릿만 있으면 뭐해요, 실제로 AI한테 보내서 답변을 받아야죠. 오늘은 프롬프트 → 모델 → 출력을 하나로 연결하는 **LCEL(LangChain Expression Language)**을 배워볼 거예요.

LCEL이 처음엔 좀 낯설 수 있는데, 알고 보면 진짜 직관적이에요. 리눅스 터미널에서 | (파이프) 쓰듯이 컴포넌트들을 연결하는 거거든요.

LCEL이 뭔가요?

LCEL은 LangChain의 컴포넌트들을 체인처럼 연결하는 문법이에요. 핵심은 **파이프 연산자 |**입니다.

프롬프트 | 모델 | 출력파서

데이터가 왼쪽에서 오른쪽으로 흘러가는 거예요:

  1. 프롬프트에 변수를 넣으면
  2. 모델이 답변을 생성하고
  3. 출력 파서가 결과를 정리해줌

첫 번째 체인 만들기

가장 기본적인 체인부터 만들어볼게요:

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}")

실행 결과:

- 클라우드는 인터넷을 통해 서버, 저장소, 데이터베이스 등의 컴퓨팅 자원을 제공하는 서비스입니다.
- 메타버스는 현실과 가상이 융합된 3차원 디지털 공간에서 사회·경제 활동이 이루어지는 세계입니다.
- 양자컴퓨터는 양자역학 원리를 활용해 기존 컴퓨터보다 복잡한 연산을 빠르게 처리하는 차세대 컴퓨터입니다.

langchain guide 04 lcel

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)

실행 결과:

파이썬은 1991년 귀도 반 로섬이 개발한 프로그래밍 언어로, 읽기 쉽고 배우기 쉬운 문법이 특징입니다. 데이터 분석, 웹 개발, 인공지능 등 다양한 분야에서 널리 사용됩니다.

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"])

실행 결과:

=== 장점 ===
1. 출퇴근 시간 절약으로 개인 시간 확보
2. 유연한 근무 환경으로 집중력 향상
3. 사무실 유지 비용 절감
=== 단점 ===
1. 팀원 간 소통 부족 가능성
2. 업무와 일상의 경계 모호
3. 자기 관리 능력 필요

장점과 단점 분석이 동시에 실행돼서 시간이 절약돼요!

체인 안에 체인 넣기

체인은 중첩해서 사용할 수도 있어요:

# 첫 번째 체인: 주제 추출
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는 '인공지능'의 줄임말이에요. 컴퓨터가 사람처럼 생각하고 배울 수 있게 만든 기술이에요. 마치 로봇 친구가 스스로 공부해서 똑똑해지는 것처럼요!

첫 번째 체인에서 “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)

실행 결과:

The weather is really nice today. It's a perfect day for a walk.

운영자 실전 노트

실제 프로젝트 진행하며 겪은 문제

  • 파이프라인 중간에 에러 나도 어디서 났는지 몰라서 디버깅 지옥 → 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 등)을 연결하는 방법을 알아본다!


다음 글 보기

← 이전 글
LangChain 독학 가이드 3 - 프롬프트 템플릿
다음 글 →
LangChain 독학 가이드 5 - 다양한 LLM 연결하기
← 블로그 목록으로