LangChain 1.0의 아키텍처, LCEL 파이프 문법, 미들웨어, 콘텐츠 블록, OpenTelemetry 통합을 실전 예제와 함께 분석합니다.
LangChain은 2026년 1.0 릴리스를 통해 안정된 API를 확립했습니다. "2.0까지 브레이킹 체인지 없음"을 공약하며, 프로덕션 환경에서의 신뢰성을 보장합니다. 이전 버전들에서 비판받던 과도한 추상화를 줄이고, 명시적이고 예측 가능한 설계로 전환했습니다.
LangChain 1.0의 패키지 구조는 세 계층으로 나뉩니다.
LangChain 1.0에서는 더 이상 langchain 단일 패키지에 모든 것이 포함되지 않습니다. langchain-core를 중심으로 필요한 통합 패키지만 추가하는 방식으로 의존성을 관리합니다.
LangChain 1.0의 핵심 추상화는 Runnable입니다. 모든 컴포넌트가 Runnable 인터페이스를 구현하며, 이를 통해 일관된 방식으로 호출, 스트리밍, 배치 처리가 가능합니다.
from langchain_core.runnables import Runnable
# 모든 Runnable이 공통으로 제공하는 메서드
# .invoke(input) - 단일 입력 처리
# .stream(input) - 스트리밍 출력
# .batch(inputs) - 배치 처리
# .ainvoke(input) - 비동기 호출
# .astream(input) - 비동기 스트리밍
# .astream_events() - 이벤트 스트리밍LCEL은 LangChain 1.0의 핵심 혁신입니다. 파이프 연산자(|)를 사용하여 컴포넌트를 연결하는 선언적 문법으로, 체인의 구조를 코드에서 직관적으로 파악할 수 있습니다.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 프롬프트 -> 모델 -> 파서 체인
prompt = ChatPromptTemplate.from_template(
"다음 주제에 대해 3문장으로 설명해주세요: {topic}"
)
model = ChatOpenAI(model="gpt-4o", temperature=0.7)
parser = StrOutputParser()
chain = prompt | model | parser
# 실행
result = chain.invoke({"topic": "마이크로서비스 아키텍처"})파이프 연산자 |는 Python의 __or__ 매직 메서드를 활용합니다. 왼쪽 컴포넌트의 출력이 오른쪽 컴포넌트의 입력으로 자동 전달됩니다.
여러 체인을 동시에 실행해야 할 때 RunnableParallel을 사용합니다.
from langchain_core.runnables import RunnableParallel
# 동일한 입력으로 여러 체인을 병렬 실행
analysis_chain = RunnableParallel(
summary=prompt_summary | model | parser,
keywords=prompt_keywords | model | parser,
sentiment=prompt_sentiment | model | parser,
)
# 세 체인이 동시에 실행됨
results = analysis_chain.invoke({"text": "분석할 텍스트..."})
# results["summary"], results["keywords"], results["sentiment"]입력에 따라 다른 처리 경로를 선택할 수 있습니다.
from langchain_core.runnables import RunnableBranch
# 조건에 따라 다른 체인 실행
route = RunnableBranch(
(lambda x: "코드" in x["question"], code_chain),
(lambda x: "데이터" in x["question"], data_chain),
general_chain, # 기본 경로
)
result = route.invoke({"question": "Python 코드를 리뷰해주세요"})임의의 Python 함수를 체인에 포함시킬 수 있습니다.
from langchain_core.runnables import RunnableLambda
def preprocess(input_dict: dict) -> dict:
"""입력 전처리"""
return {
"question": input_dict["question"].strip().lower(),
"context": input_dict.get("context", ""),
}
chain = RunnableLambda(preprocess) | prompt | model | parserRunnableLambda 내부에서 부수 효과(side effect)가 있는 작업(파일 쓰기, DB 업데이트 등)을 수행하는 것은 권장하지 않습니다. 체인이 재실행되거나 배치 처리될 때 예상치 못한 결과를 초래할 수 있습니다.
LangChain 1.0은 미들웨어(Middleware) 개념을 도입하여 로깅, 캐싱, 속도 제한 등의 횡단 관심사를 체인 코드와 분리할 수 있게 했습니다.
from langchain_core.runnables import RunnableConfig
# 기본 체인 정의
chain = prompt | model | parser
# 재시도 미들웨어 적용
chain_with_retry = chain.with_retry(
stop_after_attempt=3,
wait_exponential_jitter=True,
)
# 폴백 미들웨어 적용
fallback_model = ChatOpenAI(model="gpt-4o-mini")
fallback_chain = prompt | fallback_model | parser
chain_with_fallback = chain_with_retry.with_fallbacks(
[fallback_chain]
)
# 속도 제한
from langchain_core.rate_limiters import InMemoryRateLimiter
rate_limiter = InMemoryRateLimiter(
requests_per_second=10,
check_every_n_seconds=0.1,
)
model_with_rate_limit = ChatOpenAI(
model="gpt-4o",
rate_limiter=rate_limiter,
)from langchain_core.runnables import RunnableConfig
from langchain_core.callbacks import CallbackManagerForChainRun
import time
def timing_middleware(chain):
"""실행 시간을 측정하는 미들웨어"""
original_invoke = chain.invoke
def timed_invoke(input, config=None, **kwargs):
start = time.perf_counter()
result = original_invoke(input, config=config, **kwargs)
elapsed = time.perf_counter() - start
print(f"Chain execution: {elapsed:.3f}s")
return result
chain.invoke = timed_invoke
return chainLangChain 1.0은 콘텐츠 블록(Content Blocks)을 통해 멀티모달 입출력을 체계적으로 처리합니다. 텍스트, 이미지, 오디오, 도구 호출 결과를 통합된 형식으로 다룰 수 있습니다.
from langchain_core.messages import HumanMessage
# 텍스트 + 이미지 멀티모달 메시지
message = HumanMessage(
content=[
{"type": "text", "text": "이 이미지에서 무엇이 보이나요?"},
{
"type": "image_url",
"image_url": {"url": "https://example.com/image.png"},
},
]
)
response = model.invoke([message])콘텐츠 블록은 모델 간 호환성을 보장합니다. OpenAI, Anthropic, Google 등 서로 다른 공급자의 멀티모달 API 형식을 LangChain이 자동으로 변환합니다.
LangChain 1.0은 OpenTelemetry를 네이티브로 지원합니다. 별도의 설정 없이 모든 체인 실행이 자동으로 트레이싱됩니다.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
# OpenTelemetry 설정
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://localhost:4317")
provider.add_span_processor(BatchSpanExporter(exporter))
trace.set_tracer_provider(provider)
# LangChain은 자동으로 OpenTelemetry 트레이스를 생성
chain = prompt | model | parser
result = chain.invoke({"topic": "AI"})
# -> 자동으로 span 생성: prompt -> model -> parser각 컴포넌트의 실행 시간, 입출력, 토큰 사용량이 OpenTelemetry 스팬에 자동으로 기록됩니다. 이 데이터는 Jaeger, Grafana Tempo, LangSmith 등으로 전송하여 시각화할 수 있습니다.
LangSmith를 사용하면 OpenTelemetry 데이터에 더해 LLM 특화 분석 -- 프롬프트 버전 비교, 토큰 비용 추적, A/B 테스트 등 -- 을 활용할 수 있습니다. 프로덕션 환경에서는 LangSmith와의 연동을 권장합니다.
지금까지 배운 개념을 종합하여 실전 RAG 체인을 구성해보겠습니다.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import (
RunnablePassthrough,
RunnableParallel,
)
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 1. 벡터 스토어 설정
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="docs",
embedding_function=embeddings,
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 2. 프롬프트 정의
rag_prompt = ChatPromptTemplate.from_template("""
다음 컨텍스트를 바탕으로 질문에 답변해주세요.
컨텍스트에 없는 내용은 "해당 정보를 찾을 수 없습니다"라고 답변해주세요.
컨텍스트:
{context}
질문: {question}
""")
# 3. 모델 설정 (속도 제한 + 폴백)
from langchain_core.rate_limiters import InMemoryRateLimiter
rate_limiter = InMemoryRateLimiter(requests_per_second=10)
primary_model = ChatOpenAI(
model="gpt-4o",
rate_limiter=rate_limiter,
)
fallback_model = ChatOpenAI(model="gpt-4o-mini")
# 4. 문서 포맷팅 함수
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 5. RAG 체인 조합
rag_chain = (
RunnableParallel(
context=retriever | format_docs,
question=RunnablePassthrough(),
)
| rag_prompt
| primary_model.with_fallbacks([fallback_model])
| StrOutputParser()
).with_retry(stop_after_attempt=3)
# 6. 실행
result = rag_chain.invoke("LangChain의 LCEL이란 무엇인가요?")이 예제에서는 검색과 질문 전달을 병렬로 처리하고, 모델에 폴백과 재시도를 적용하여 프로덕션 수준의 안정성을 확보했습니다.
langchain-core 중심의 모듈화된 패키지 구조를 채택했습니다.|)으로 체인을 선언적으로 구성하며, 모든 컴포넌트가 Runnable 인터페이스를 구현합니다.3장에서는 LangChain 생태계의 에이전트 오케스트레이션 프레임워크인 LangGraph를 심층 분석합니다. StateGraph와 MessageGraph의 차이, 듀러블 상태와 체크포인팅, 조건부 엣지와 사이클, 그리고 휴먼인더루프 패턴까지 실전 예제와 함께 다루겠습니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
LangGraph 1.0/1.1의 StateGraph, 듀러블 상태, 조건부 엣지, 휴먼인더루프, type-safe 스트리밍을 실전 예제와 함께 분석합니다.
LLM 애플리케이션이 복잡해지는 이유를 분석하고, 오케스트레이션의 정의와 역할, 2026년 주요 프레임워크 생태계를 조망합니다.
LlamaIndex의 데이터 커넥터, 인덱스 유형, 쿼리 엔진, 그리고 이벤트 드리븐 Workflows 1.0을 실전 예제와 함께 분석합니다.