LangChain
LangChain 생태계 · LCEL · ChatBedrockConverse · BedrockEmbeddings
LangChain
ChatBedrockConverse · LCEL · @tool · RAG 체인
파이프 연산자로 LLM 애플리케이션을 조립한다
LangChain 1.0 + Amazon Bedrock — LCEL 파이프라인으로 RAG까지 빠르게 구축
LangChain 아키텍처
패키지 계층 · 설치 · Bedrock 연동
LangChain 아키텍처
패키지 계층과 Bedrock 연동 진입점
조립식 파이프라인
langchain-core의 Runnable 프로토콜 위에 프로바이더별 패키지를 조합합니다 — Bedrock 연동은 langchain-aws 하나로 시작합니다.
- langchain-core — Runnable · BaseMessage · BaseTool
- langchain-aws — ChatBedrockConverse · BedrockEmbeddings
- langchain — 에이전트 빌딩 블록
- langgraph — StateGraph 오케스트레이션
pip install langchain-aws 한 줄이면 Bedrock 연동이 시작됩니다 — 레거시는 langchain-classic으로 옮겨지고 1.0은 에이전트 빌딩 블록에 집중합니다.
LangChain 1.0 패키지 구조
langchain-core 위에 프로바이더별 패키지를 조합
langchain-aws
ChatBedrockConverse, BedrockEmbeddings — Amazon Bedrock 전용 통합
langchain
에이전트 빌딩 블록 — create_agent, 도구 유틸, 프롬프트 유틸
langgraph
StateGraph 기반 에이전트 오케스트레이션 — langchain-core 위에 구축, 루프·분기·상태 관리
langchain-core
Runnable 프로토콜, BaseMessage, BaseTool — 모든 패키지의 기반
네 패키지 모두 langchain-core의 Runnable 프로토콜 위에 서 있습니다 — 어떤 컴포넌트든 | 연산자로 연결되는 이유입니다.
LangChain 1.0 마이그레이션
레거시 API의 행선지 — 에이전트 빌딩 블록 중심 재편
마이그레이션의 방향은 하나입니다 — 레거시 헬퍼를 걷어내고 LCEL과 create_agent로 수렴합니다.
핵심 컴포넌트
LangChain 생태계를 구성하는 주요 빌딩 블록
ChatModel
LLM 래퍼 — invoke/stream/batch 통합 인터페이스
PromptTemplate
변수 바인딩 + 역할 분리 — 재사용 가능한 프롬프트
OutputParser
응답을 str/JSON/Pydantic으로 구조화 파싱
Retriever
벡터 검색 추상화 — VectorStore.as_retriever()
설치와 Bedrock 연동
pip install 한 줄 + 모델 ID만 지정하면 시작
# 설치
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
모델과 프롬프트
모델 래퍼 · 호출 패턴 · 프롬프트 템플릿
하나의 인터페이스, 모든 파운데이션 모델
ChatBedrockConverse가 Bedrock의 모든 파운데이션 모델을 하나의 인터페이스로 감쌉니다 — 상황에 맞는 호출 패턴과 재사용 가능한 프롬프트를 조합합니다.
- ChatBedrockConverse — Converse API 래퍼
- 호출 3패턴 — invoke · stream · batch
- ChatPromptTemplate — 변수 바인딩 + 역할 분리
모델 전환은 model= 파라미터 하나 — 호출 코드와 프롬프트는 그대로 재사용됩니다.
ChatBedrockConverse 파라미터
Converse API 래퍼의 생성 인자 — 모델 지정부터 리전까지
핵심 포인트
- model= / model_id=
- 양쪽 kwarg 모두 유효 — model_id가 정본 필드, model은 alias
- temperature=0
- RAG처럼 정확성이 핵심인 워크로드의 기본 설정
- region_name=
- 모델을 호출할 AWS 리전 지정 — 리전별 모델 가용성 확인 필요
- ChatBedrock (구)
- deprecated — 신규 코드는 ChatBedrockConverse만 사용
코드
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",
)
3가지 호출 패턴
동기 · 스트리밍 · 배치 — 상황에 맞게 선택
invoke (동기)
- 전체 응답을 한 번에 반환
- 가장 단순 — 대부분의 경우 충분
- result = llm.invoke("질문")
stream (스트리밍)
- 토큰 단위로 실시간 전달
- 사용자 체감 지연 감소
- for chunk in llm.stream(...)
batch (배치)
- 여러 입력을 한 번에 병렬 처리
- 평가, 데이터 생성에 적합
- results = llm.batch([...])
ChatPromptTemplate
변수 바인딩 + 역할 분리 — 재사용 가능한 프롬프트
| 메서드 | 용도 | 예시 |
|---|---|---|
| from_messages() | 멀티 역할 조합 | [("system", "..."), ("human", "{question}")] |
| from_template() | 단일 템플릿 | "컨텍스트: {context}\n질문: {question}" |
| invoke(dict) | 변수 주입 | .invoke({"question": "서울 맛집"}) |
| | (파이프) | 체인 연결 | prompt | llm | parser |
from_messages()로 역할을 분리하고 {변수}로 동적 값을 주입합니다 — 완성된 프롬프트는 | 파이프로 llm에 바로 연결됩니다.
LCEL과 도구
LCEL 파이프 연산자 · @tool · bind_tools
LCEL과 도구
파이프 조립과 도구 호출
파이프로 조립한다
| 연산자로 Runnable 컴포넌트를 직렬 연결하고, @tool로 등록한 Python 함수를 모델이 호출하게 만듭니다.
- LCEL — | 연산자로 Runnable 직렬 조립
- @tool — Python 함수를 LangChain 도구로 변환
- bind_tools — 모델에 도구 바인딩 → tool_calls
체인은 재사용 파이프라인입니다 — 프롬프트·모델·파서를 한 번 조립하면 invoke 한 번으로 실행됩니다.
LCEL — 파이프로 조립
| 연산자로 Runnable 컴포넌트를 직렬 연결 — 파이프를 관통하는 고객 문의 한 건
"배송이 3일째 안 와요"
입력 — 고객 문의 1건
→
chain = prompt | llm | parser
PromptTemplate
|
ChatModel
|
OutputParser
이전 컴포넌트의 출력이 | 를 타고 다음 컴포넌트의 입력으로
→
분류: 배송 지연
출력 — 분류 + 답변 초안
chain.invoke({"question": "배송이 3일째 안 와요"})
— 조립은 한 번, 실행은 invoke · stream · batch 어디서든
prompt | llm | parser 한 줄이 곧 실행 가능한 체인입니다 — 다음 장에서 이 문의가 각 단계에서 어떤 모양으로 변하는지 열어봅니다.
체인 속 데이터 실물
"배송이 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"]로 바로 분기 가능
| 는 마법이 아닙니다 — 각 단계의 출력이 그대로 다음 입력이 될 뿐이라, 디버깅할 때 이 중간 실물을 단계별로 찍어볼 수 있습니다.
LCEL 조합 패턴
함수 연결 · 병렬 입력 · 자동 호출 패턴 — 파이프가 흡수하는 것들
핵심 포인트
- retriever | format_docs
- 일반 Python 함수도 파이프에 연결 — 검색 결과를 문자열로 정리
- RunnablePassthrough()
- 입력을 그대로 통과 — 원본 질문을 프롬프트 변수로 전달
- {"context": ..., "question": ...}
- dict 조합 — 두 갈래 입력을 병렬로 채워 프롬프트에 공급
- invoke · stream · batch
- 한 번 조립한 체인이 세 호출 패턴을 자동 지원 — 재작성 불필요
코드
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("코르키지 비용은?")
@tool과 bind_tools
Python 함수를 LLM이 호출할 수 있는 도구로 변환
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": "서울"}}]
도구 호출 왕복
tool_calls 생성부터 최종 답변까지 — 질문 하나 뒤에서 오가는 메시지
모델은 무엇을 호출할지 판단만 합니다 — 실행과 결과 전달은 앱 코드(또는 LangGraph의 ToolNode)의 몫입니다.
문서 처리
Document · TextSplitter · Embeddings · VectorStore
문서 처리
RAG의 재료를 만드는 4단계
원문에서 벡터 인덱스까지
어떤 형식의 문서든 Document 구조로 통일한 뒤 청킹·임베딩을 거쳐 VectorStore에 저장합니다.
- Document — page_content + metadata
- RecursiveCharacterTextSplitter — chunk_size + overlap
- BedrockEmbeddings — 텍스트를 벡터로 변환
- FAISS · OpenSearch — VectorStore 저장
Load → Split → Embed → Store 4단계를 거치면 문서가 유사도 검색 가능한 벡터 인덱스가 됩니다.
문서 처리 파이프라인
원문 → 청킹 → 임베딩 → 벡터 저장
-
1
Load
PDF/HTML/TXT를 Document(page_content + metadata)로 변환
-
2
Split
RecursiveCharacterTextSplitter — chunk_size + overlap으로 분할
-
3
Embed
BedrockEmbeddings — 텍스트를 벡터(1024차원, Titan Embed v2 기준)로 변환
-
4
Store
FAISS / OpenSearch VectorStore에 저장 → 유사도 검색 가능
어떤 형식의 문서든 먼저 Document 구조로 통일합니다 — overlap이 청크 경계의 맥락을 보존합니다.
Document Loader 5종
포맷별 로더가 원문을 Document 객체로 통일 — 메타데이터는 자동 부착
| Loader | 입력 | 자동 메타데이터 |
|---|---|---|
| PyPDFLoader | 페이지 번호 | |
| TextLoader | TXT · MD | 파일 경로 |
| CSVLoader | CSV | 행 번호 · 컬럼 |
| Docx2txtLoader | DOCX | 파일 경로 |
| WebBaseLoader | URL | URL · 제목 |
출처가 달라도 결과는 같은 Document(page_content + metadata) — import는 langchain-community의 document_loaders에서, 뒤 단계는 포맷을 신경 쓰지 않습니다.
청킹 전략
RecursiveCharacterTextSplitter가 기본 — 구분자 계층으로 의미 단위 분할
고정 크기 분할
- chunk_size=500, overlap=50
- 구현 단순, 예측 가능
- 문장 중간에 잘릴 수 있음
- 범용적 — 대부분의 경우 충분
시맨틱 분할
- 의미 변화 지점에서 분할
- 임베딩 유사도로 경계 판단
- 맥락 보존 우수
- 비용↑ — 임베딩 호출 추가
고정 크기 분할이 범용적이라 대부분의 경우 충분합니다 — 맥락 보존이 중요하면 임베딩 호출 비용을 감수하고 시맨틱 분할을 선택합니다.
임베딩 모델 선택
차원 · 언어 · 비용 — 세 기준으로 고르는 임베딩 모델
Titan Embeddings V2 추천
Bedrock 네이티브 — 이 모듈의 기준 모델
- 1024차원 · 한국어 포함 다국어
- BedrockEmbeddings로 바로 연결
- AWS 내부 호출 — 네트워크 지연 최소
언제 — Bedrock 중심 스택, 한국어 문서 RAG의 기본값
Cohere Embed
다국어 검색 특화
- 다국어 코퍼스에 특화된 임베딩
- 검색 최적화 모드 제공
- Bedrock에서 호출 가능
언제 — 다국어 문서가 섞인 코퍼스, 검색 품질 튜닝이 필요할 때
로컬 모델
sentence-transformers
- API 비용 0 — 로컬 실행
- 성능은 관리형 모델 대비 약간 낮음
- 오프라인 · 실험 환경에 적합
언제 — 비용 없이 빠르게 실험할 때, 네트워크 제약 환경
차원이 높을수록 정확하지만 스토리지와 지연이 함께 늘어납니다 — 기본값은 Titan V2(1024차원)로 시작합니다.
RAG 체인
retriever | prompt | llm | parser — LCEL로 조립
RAG 체인
파이프 4개로 완성하는 RAG와 그 한계
파이프 4개로 RAG 완성
VectorStore를 as_retriever()로 Runnable로 바꾸면, 검색부터 답변 파싱까지 LCEL 한 줄로 조립됩니다.
- retriever | prompt | llm | parser — 4단계 파이프라인
- as_retriever() — VectorStore를 Runnable 컴포넌트로
- 한계 — 조건 분기 · 루프 · 에러 재시도 불가
파이프 4개로 RAG가 완성됩니다 — 직선으로 부족해지는 순간이 LangGraph로 넘어가는 신호입니다.
RAG = 4단계 LCEL
retriever | prompt | llm | parser — 파이프 4개로 완성
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. 실행
LCEL RAG의 한계
직선 파이프라인의 한계 — 루프·분기가 필요한 순간
LCEL은 빠르게 만들기, LangGraph는 프로덕션 제어 — 둘은 보완 관계입니다.
프레임워크 선택 — LangChain vs Strands
기존 투자와 배포 타겟이 가르는 선택 — LangChain 자산은 유지·확장, 신규 AWS 네이티브는 Strands 검토
LangChain 유지
기존 코드베이스 + LCEL
- 기존 LangChain 코드 유지보수 · 확장
- RAG 중심 — LCEL 조립이 간결
- 방대한 커뮤니티 · 통합 생태계
- 에이전트 확장은 LangGraph로
Strands 전환
AWS 네이티브 에이전트
- 신규 프로젝트 + AgentCore 배포
- 멀티에이전트 · MCP/A2A 네이티브 지원
- 도구 단위 점진 전환 가능
- 상세는 Strands SDK 모듈에서
정답은 코드베이스에 있습니다 — 기존 LangChain 자산이 있으면 유지·확장, 신규 AWS 네이티브 프로젝트면 Strands를 검토합니다.
LangChain — 조립식 파이프라인
LCEL로 조립, 분기·루프가 필요해지면 LangGraph로 확장
파이프 연산자로 LLM 애플리케이션을 조립한다
LangChain은 조립식 파이프라인 — 조건 분기·루프가 필요해지는 순간 LangGraph로 확장합니다.
ChatBedrockConverse · LCEL · @tool · TextSplitter · FAISS · RAG 체인