Home / LangChain / LangChain
Module

LangChain

LangChain 생태계 · LCEL · ChatBedrockConverse · BedrockEmbeddings

⏱ 60분 46 / 189

LangChain

ChatBedrockConverse · LCEL · @tool · RAG 체인

파이프 연산자로 LLM 애플리케이션을 조립한다

LangChain 1.0 + Amazon Bedrock — LCEL 파이프라인으로 RAG까지 빠르게 구축

LangChain 아키텍처

패키지 계층 · 설치 · Bedrock 연동

PART 1 · LangChain 아키텍처

LangChain 아키텍처

패키지 계층과 Bedrock 연동 진입점

조립식 파이프라인

langchain-core의 Runnable 프로토콜 위에 프로바이더별 패키지를 조합합니다 — Bedrock 연동은 langchain-aws 하나로 시작합니다.

  • langchain-core — Runnable · BaseMessage · BaseTool
  • langchain-aws — ChatBedrockConverse · BedrockEmbeddings
  • langchain — 에이전트 빌딩 블록
  • langgraph — StateGraph 오케스트레이션
takeaway

pip install langchain-aws 한 줄이면 Bedrock 연동이 시작됩니다 — 레거시는 langchain-classic으로 옮겨지고 1.0은 에이전트 빌딩 블록에 집중합니다.

PART 1 · LangChain 아키텍처

LangChain 1.0 패키지 구조

langchain-core 위에 프로바이더별 패키지를 조합

langchain-aws

ChatBedrockConverse, BedrockEmbeddings — Amazon Bedrock 전용 통합

langchain

에이전트 빌딩 블록 — create_agent, 도구 유틸, 프롬프트 유틸

langgraph

StateGraph 기반 에이전트 오케스트레이션 — langchain-core 위에 구축, 루프·분기·상태 관리

langchain-core

Runnable 프로토콜, BaseMessage, BaseTool — 모든 패키지의 기반

takeaway

네 패키지 모두 langchain-core의 Runnable 프로토콜 위에 서 있습니다 — 어떤 컴포넌트든 | 연산자로 연결되는 이유입니다.

PART 1 · LangChain 아키텍처

LangChain 1.0 마이그레이션

레거시 API의 행선지 — 에이전트 빌딩 블록 중심 재편

0.3.x 레거시 1.0 정본
ReAct 에이전트 create_react_agent (deprecated) langchain.agents의 create_agent
체인 조립 create_retrieval_chain · RetrievalQA LCEL 파이프 직접 조립
레거시 체인·유틸 langchain 본체에 혼재 langchain-classic 패키지로 분리
takeaway

마이그레이션의 방향은 하나입니다 — 레거시 헬퍼를 걷어내고 LCEL과 create_agent로 수렴합니다.

PART 1 · LangChain 아키텍처

핵심 컴포넌트

LangChain 생태계를 구성하는 주요 빌딩 블록

ChatModel

LLM 래퍼 — invoke/stream/batch 통합 인터페이스

PromptTemplate

변수 바인딩 + 역할 분리 — 재사용 가능한 프롬프트

OutputParser

응답을 str/JSON/Pydantic으로 구조화 파싱

Retriever

벡터 검색 추상화 — VectorStore.as_retriever()

PART 1 · LangChain 아키텍처

설치와 Bedrock 연동

pip install 한 줄 + 모델 ID만 지정하면 시작

python
# 설치
pip install langchain-aws

# Bedrock 연동 — 3줄
from langchain_aws import ChatBedrockConverse

llm = ChatBedrockConverse(
    model="us.anthropic.claude-sonnet-4-6",
    temperature=0,
    region_name="us-east-1",
)

response = llm.invoke("서울 여행 추천해줘")

모델과 프롬프트

ChatBedrockConverse · 호출 패턴 · ChatPromptTemplate

PART 2 · 모델과 프롬프트

모델과 프롬프트

모델 래퍼 · 호출 패턴 · 프롬프트 템플릿

하나의 인터페이스, 모든 파운데이션 모델

ChatBedrockConverse가 Bedrock의 모든 파운데이션 모델을 하나의 인터페이스로 감쌉니다 — 상황에 맞는 호출 패턴과 재사용 가능한 프롬프트를 조합합니다.

  • ChatBedrockConverse — Converse API 래퍼
  • 호출 3패턴 — invoke · stream · batch
  • ChatPromptTemplate — 변수 바인딩 + 역할 분리
takeaway

모델 전환은 model= 파라미터 하나 — 호출 코드와 프롬프트는 그대로 재사용됩니다.

PART 2 · 모델과 프롬프트

ChatBedrockConverse 파라미터

Converse API 래퍼의 생성 인자 — 모델 지정부터 리전까지

핵심 포인트

model= / model_id=
양쪽 kwarg 모두 유효 — model_id가 정본 필드, model은 alias
temperature=0
RAG처럼 정확성이 핵심인 워크로드의 기본 설정
region_name=
모델을 호출할 AWS 리전 지정 — 리전별 모델 가용성 확인 필요
ChatBedrock (구)
deprecated — 신규 코드는 ChatBedrockConverse만 사용

코드

python
from langchain_aws import ChatBedrockConverse

# 모델 지정 — model_id=(정본)와 model=(alias) 모두 동작
llm = ChatBedrockConverse(
    model_id="us.anthropic.claude-sonnet-4-6",
    temperature=0,
    region_name="us-east-1",
)

# 모델 전환 — 같은 래퍼에서 모델 ID만 교체
lite = ChatBedrockConverse(
    model="us.amazon.nova-2-lite-v1:0",
    region_name="us-east-1",
)
PART 2 · 모델과 프롬프트

3가지 호출 패턴

동기 · 스트리밍 · 배치 — 상황에 맞게 선택

invoke (동기)
  • 전체 응답을 한 번에 반환
  • 가장 단순 — 대부분의 경우 충분
  • result = llm.invoke("질문")
stream (스트리밍)
  • 토큰 단위로 실시간 전달
  • 사용자 체감 지연 감소
  • for chunk in llm.stream(...)
batch (배치)
  • 여러 입력을 한 번에 병렬 처리
  • 평가, 데이터 생성에 적합
  • results = llm.batch([...])
PART 2 · 모델과 프롬프트

ChatPromptTemplate

변수 바인딩 + 역할 분리 — 재사용 가능한 프롬프트

메서드용도예시
from_messages()멀티 역할 조합[("system", "..."), ("human", "{question}")]
from_template()단일 템플릿"컨텍스트: {context}\n질문: {question}"
invoke(dict)변수 주입.invoke({"question": "서울 맛집"})
| (파이프)체인 연결prompt | llm | parser
takeaway

from_messages()로 역할을 분리하고 {변수}로 동적 값을 주입합니다 — 완성된 프롬프트는 | 파이프로 llm에 바로 연결됩니다.

LCEL과 도구

LCEL 파이프 연산자 · @tool · bind_tools

PART 3 · LCEL과 도구

LCEL과 도구

파이프 조립과 도구 호출

파이프로 조립한다

| 연산자로 Runnable 컴포넌트를 직렬 연결하고, @tool로 등록한 Python 함수를 모델이 호출하게 만듭니다.

  • LCEL — | 연산자로 Runnable 직렬 조립
  • @tool — Python 함수를 LangChain 도구로 변환
  • bind_tools — 모델에 도구 바인딩 → tool_calls
takeaway

체인은 재사용 파이프라인입니다 — 프롬프트·모델·파서를 한 번 조립하면 invoke 한 번으로 실행됩니다.

PART 3 · LCEL과 도구

LCEL — 파이프로 조립

| 연산자로 Runnable 컴포넌트를 직렬 연결 — 파이프를 관통하는 고객 문의 한 건





"배송이 3일째 안 와요"
입력 — 고객 문의 1건



chain = prompt | llm | parser

PromptTemplate
|
ChatModel
|
OutputParser

이전 컴포넌트의 출력이 | 를 타고 다음 컴포넌트의 입력으로



분류: 배송 지연
출력 — 분류 + 답변 초안



chain.invoke({"question": "배송이 3일째 안 와요"})
— 조립은 한 번, 실행은 invoke · stream · batch 어디서든


takeaway

prompt | llm | parser 한 줄이 곧 실행 가능한 체인입니다 — 다음 장에서 이 문의가 각 단계에서 어떤 모양으로 변하는지 열어봅니다.

PART 3 · LCEL과 도구

체인 속 데이터 실물

"배송이 3일째 안 와요" 한 건이 각 단계를 지나며 바뀌는 모양




입력 dict
{"question": "배송이 3일째 안 와요"}



| prompt
{question} 자리에 문의가 치환됩니다


치환된 프롬프트

System: 당신은 쇼핑몰 고객 지원 상담원입니다. 문의를 분류하고 JSON으로 답하세요.
Human: 배송이 3일째 안 와요




| llm
ChatBedrockConverse가 Bedrock 호출


모델 원시 응답
AIMessage(content='{"category": "배송 지연", "reply": "불편을 드려 죄송합니다. 주문 번호를 알려주시면 배송 상태를 바로 확인하겠습니다."}') (예시 응답)



| parser
JsonOutputParser — content 문자열을 dict로


파싱된 구조 출력
{"category": "배송 지연", "reply": "불편을 드려 죄송합니다. …"}
→ result["category"]로 바로 분기 가능


takeaway

| 는 마법이 아닙니다 — 각 단계의 출력이 그대로 다음 입력이 될 뿐이라, 디버깅할 때 이 중간 실물을 단계별로 찍어볼 수 있습니다.

PART 3 · LCEL과 도구

LCEL 조합 패턴

함수 연결 · 병렬 입력 · 자동 호출 패턴 — 파이프가 흡수하는 것들

핵심 포인트

retriever | format_docs
일반 Python 함수도 파이프에 연결 — 검색 결과를 문자열로 정리
RunnablePassthrough()
입력을 그대로 통과 — 원본 질문을 프롬프트 변수로 전달
{"context": ..., "question": ...}
dict 조합 — 두 갈래 입력을 병렬로 채워 프롬프트에 공급
invoke · stream · batch
한 번 조립한 체인이 세 호출 패턴을 자동 지원 — 재작성 불필요

코드

python
from langchain_core.runnables import RunnablePassthrough

# 일반 함수 — 파이프에 연결하면 체인의 한 단계로 동작
def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)

# dict 조합 — 두 갈래 입력을 병렬로 채워 프롬프트에 공급
chain = (
    {"context": retriever | format_docs,
     "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

answer = chain.invoke("코르키지 비용은?")
PART 3 · LCEL과 도구

@tool과 bind_tools

Python 함수를 LLM이 호출할 수 있는 도구로 변환

python
from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
    """도시의 현재 날씨를 조회합니다."""
    return f"{city}: 28°C 맑음"

# 모델에 도구 바인딩
llm_with_tools = llm.bind_tools([get_weather])

# 모델이 tool_calls를 생성하면 → 도구 실행 → 결과 반환
result = llm_with_tools.invoke("서울 날씨 알려줘")
# result.tool_calls = [{"name": "get_weather", "args": {"city": "서울"}}]
PART 3 · LCEL과 도구

도구 호출 왕복

tool_calls 생성부터 최종 답변까지 — 질문 하나 뒤에서 오가는 메시지

takeaway

모델은 무엇을 호출할지 판단만 합니다 — 실행과 결과 전달은 앱 코드(또는 LangGraph의 ToolNode)의 몫입니다.

문서 처리

Document · TextSplitter · Embeddings · VectorStore

PART 4 · 문서 처리

문서 처리

RAG의 재료를 만드는 4단계

원문에서 벡터 인덱스까지

어떤 형식의 문서든 Document 구조로 통일한 뒤 청킹·임베딩을 거쳐 VectorStore에 저장합니다.

  • Document — page_content + metadata
  • RecursiveCharacterTextSplitter — chunk_size + overlap
  • BedrockEmbeddings — 텍스트를 벡터로 변환
  • FAISS · OpenSearch — VectorStore 저장
takeaway

Load → Split → Embed → Store 4단계를 거치면 문서가 유사도 검색 가능한 벡터 인덱스가 됩니다.

PART 4 · 문서 처리

문서 처리 파이프라인

원문 → 청킹 → 임베딩 → 벡터 저장

  1. 1
    Load

    PDF/HTML/TXT를 Document(page_content + metadata)로 변환

  2. 2
    Split

    RecursiveCharacterTextSplitter — chunk_size + overlap으로 분할

  3. 3
    Embed

    BedrockEmbeddings — 텍스트를 벡터(1024차원, Titan Embed v2 기준)로 변환

  4. 4
    Store

    FAISS / OpenSearch VectorStore에 저장 → 유사도 검색 가능

takeaway

어떤 형식의 문서든 먼저 Document 구조로 통일합니다 — overlap이 청크 경계의 맥락을 보존합니다.

PART 4 · 문서 처리

Document Loader 5종

포맷별 로더가 원문을 Document 객체로 통일 — 메타데이터는 자동 부착

Loader입력자동 메타데이터
PyPDFLoaderPDF페이지 번호
TextLoaderTXT · MD파일 경로
CSVLoaderCSV행 번호 · 컬럼
Docx2txtLoaderDOCX파일 경로
WebBaseLoaderURLURL · 제목
takeaway

출처가 달라도 결과는 같은 Document(page_content + metadata) — import는 langchain-community의 document_loaders에서, 뒤 단계는 포맷을 신경 쓰지 않습니다.

PART 4 · 문서 처리

청킹 전략

RecursiveCharacterTextSplitter가 기본 — 구분자 계층으로 의미 단위 분할

고정 크기 분할

  • chunk_size=500, overlap=50
  • 구현 단순, 예측 가능
  • 문장 중간에 잘릴 수 있음
  • 범용적 — 대부분의 경우 충분

시맨틱 분할

  • 의미 변화 지점에서 분할
  • 임베딩 유사도로 경계 판단
  • 맥락 보존 우수
  • 비용↑ — 임베딩 호출 추가
takeaway

고정 크기 분할이 범용적이라 대부분의 경우 충분합니다 — 맥락 보존이 중요하면 임베딩 호출 비용을 감수하고 시맨틱 분할을 선택합니다.

PART 4 · 문서 처리

임베딩 모델 선택

차원 · 언어 · 비용 — 세 기준으로 고르는 임베딩 모델

Cohere Embed

다국어 검색 특화

  • 다국어 코퍼스에 특화된 임베딩
  • 검색 최적화 모드 제공
  • Bedrock에서 호출 가능

언제 — 다국어 문서가 섞인 코퍼스, 검색 품질 튜닝이 필요할 때

로컬 모델

sentence-transformers

  • API 비용 0 — 로컬 실행
  • 성능은 관리형 모델 대비 약간 낮음
  • 오프라인 · 실험 환경에 적합

언제 — 비용 없이 빠르게 실험할 때, 네트워크 제약 환경

takeaway

차원이 높을수록 정확하지만 스토리지와 지연이 함께 늘어납니다 — 기본값은 Titan V2(1024차원)로 시작합니다.

RAG 체인

retriever | prompt | llm | parser — LCEL로 조립

PART 5 · RAG 체인

RAG 체인

파이프 4개로 완성하는 RAG와 그 한계

파이프 4개로 RAG 완성

VectorStore를 as_retriever()로 Runnable로 바꾸면, 검색부터 답변 파싱까지 LCEL 한 줄로 조립됩니다.

  • retriever | prompt | llm | parser — 4단계 파이프라인
  • as_retriever() — VectorStore를 Runnable 컴포넌트로
  • 한계 — 조건 분기 · 루프 · 에러 재시도 불가
takeaway

파이프 4개로 RAG가 완성됩니다 — 직선으로 부족해지는 순간이 LangGraph로 넘어가는 신호입니다.

PART 5 · RAG 체인

RAG = 4단계 LCEL

retriever | prompt | llm | parser — 파이프 4개로 완성

python
from langchain_aws import ChatBedrockConverse, BedrockEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_community.vectorstores import FAISS

# 0. 모델 준비 — LLM + 임베딩
llm = ChatBedrockConverse(model="us.anthropic.claude-sonnet-4-6")
embeddings = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0")
# 1. Retriever — 벡터 검색
vectorstore = FAISS.load_local("./index", embeddings, allow_dangerous_deserialization=True)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 2. Prompt — 검색 결과 + 질문 조합
prompt = ChatPromptTemplate.from_template(
    "아래 컨텍스트만 사용하여 질문에 답하세요.\n컨텍스트: {context}\n질문: {question}"
)

# 3. LCEL 체인 조립
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

answer = rag_chain.invoke("서울에서 가볼 만한 관광지는?")  # 4. 실행
PART 5 · RAG 체인

LCEL RAG의 한계

직선 파이프라인의 한계 — 루프·분기가 필요한 순간

LCEL LangGraph
조건 분기 불가 — 항상 같은 경로 add_conditional_edges()
재시도 루프 불가 — 검색 실패 시 멈춤 순환 엣지로 재검색
검증 단계 없음 — 생성 결과 그대로 반환 Self-RAG 검증 노드
상태 관리 없음 — 호출 간 상태 유실 TypedDict State + Checkpointer
takeaway

LCEL은 빠르게 만들기, LangGraph는 프로덕션 제어 — 둘은 보완 관계입니다.

PART 5 · RAG 체인

프레임워크 선택 — LangChain vs Strands

기존 투자와 배포 타겟이 가르는 선택 — LangChain 자산은 유지·확장, 신규 AWS 네이티브는 Strands 검토

LangChain 유지

기존 코드베이스 + LCEL

  • 기존 LangChain 코드 유지보수 · 확장
  • RAG 중심 — LCEL 조립이 간결
  • 방대한 커뮤니티 · 통합 생태계
  • 에이전트 확장은 LangGraph로

Strands 전환

AWS 네이티브 에이전트

  • 신규 프로젝트 + AgentCore 배포
  • 멀티에이전트 · MCP/A2A 네이티브 지원
  • 도구 단위 점진 전환 가능
  • 상세는 Strands SDK 모듈에서
takeaway

정답은 코드베이스에 있습니다 — 기존 LangChain 자산이 있으면 유지·확장, 신규 AWS 네이티브 프로젝트면 Strands를 검토합니다.

PART 5 · RAG 체인

LangChain — 조립식 파이프라인

LCEL로 조립, 분기·루프가 필요해지면 LangGraph로 확장

파이프 연산자로 LLM 애플리케이션을 조립한다

LangChain은 조립식 파이프라인 — 조건 분기·루프가 필요해지는 순간 LangGraph로 확장합니다.

파이프 연산자로 조립하고, 루프가 필요해지면 LangGraph로 확장합니다.

ChatBedrockConverse · LCEL · @tool · TextSplitter · FAISS · RAG 체인