[2026] LangChain 실전 가이드 | LLM 애플리케이션 개발 프레임워크
이 글의 핵심
LangChain으로 LLM 애플리케이션을 만드는 완벽 가이드. Chains, Agents, Memory, RAG 구현, 벡터 스토어, 프롬프트 템플릿까지 실전 예제로 완벽 이해.
들어가며
LangChain은 LLM 기반 애플리케이션을 쉽게 만들 수 있는 프레임워크입니다. 복잡한 워크플로우, 메모리 관리, 외부 도구 연동을 간단하게 구현할 수 있습니다.
실무 경험: VOD 콘텐츠 자동 태깅 시스템을 LangChain으로 구축하면서, 수천 개의 영상 메타데이터를 자동으로 분류하고 검색 정확도를 40% 향상시킨 경험을 공유합니다. 이 글에서 다룰 내용:
- LangChain 기본 개념과 설치
- Chains: 여러 단계를 연결
- Agents: 자율적으로 도구 사용
- Memory: 대화 이력 관리
- RAG: 문서 검색 기반 답변
- Vector Stores: 임베딩과 유사도 검색
- 실전 예제
목차
- LangChain 시작하기
- Chains: 작업 연결
- Prompt Templates
- Memory: 대화 이력
- Agents: 자율 도구 사용
- RAG 구현
- Vector Stores
- 실전 예제
1. LangChain 시작하기
설치
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# Python
pip install langchain langchain-openai langchain-community
# 추가 패키지
pip install chromadb # 벡터 스토어
pip install tiktoken # 토큰 계산
pip install faiss-cpu # 벡터 검색
# Node.js
npm install langchain @langchain/openai
기본 설정
개념: LangChain에서 LLM을 사용하려면 먼저 모델을 초기화해야 합니다. ChatOpenAI 클래스는 OpenAI의 GPT 모델을 LangChain에서 사용할 수 있게 래핑합니다.
주요 파라미터:
model: 사용할 모델 (gpt-4o, gpt-4o-mini 등)temperature: 창의성 조절 (0=결정적, 1=창의적)max_tokens: 최대 출력 토큰 수
import os
from langchain_openai import ChatOpenAI
# API 키 설정 (환경 변수 사용 권장)
os.environ[OPENAI_API_KEY] = "sk-..."
# LLM 초기화
llm = ChatOpenAI(
model="gpt-4o-mini", # 모델 선택: gpt-4o-mini는 빠르고 저렴
temperature=0.7, # 0.0~1.0: 낮을수록 일관적, 높을수록 창의적
max_tokens=1000 # 최대 출력 길이 제한
)
# 간단한 호출
response = llm.invoke("What is LangChain?")
print(response.content)
# 출력: "LangChain is a framework for developing applications powered by language models..."
실행 결과:
LangChain is a framework for developing applications powered by language models.
It provides tools for prompt management, chains, agents, memory, and more.
주의사항:
- API 키는 절대 코드에 하드코딩하지 말고 환경 변수나
.env파일 사용 temperature=0은 일관된 답변이 필요할 때 (예: 데이터 추출)temperature=0.7~1.0은 창의적 작업에 적합 (예: 글쓰기)
2. Chains: 작업 연결
기본 Chain
개념: Chain은 여러 컴포넌트를 파이프라인으로 연결하는 LangChain의 핵심 개념입니다. | 연산자(LCEL)를 사용하여 프롬프트 → LLM → 파서를 순차적으로 연결합니다.
LCEL (LangChain Expression Language):
|연산자로 컴포넌트를 체인처럼 연결- 이전 단계의 출력이 자동으로 다음 단계의 입력이 됨
- 간결하고 읽기 쉬운 코드 동작 흐름:
- 프롬프트 템플릿: 입력 변수를 받아 프롬프트 생성
- LLM: 프롬프트를 받아 응답 생성
- 출력 파서: LLM 응답을 원하는 형식으로 변환 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
# 1. 프롬프트 템플릿 정의
prompt = ChatPromptTemplate.from_template(
"Translate the following to {language}: {text}"
# {language}, {text}는 실행 시 전달할 변수
)
# 2. LLM 초기화
llm = ChatOpenAI(model="gpt-4o-mini")
# 3. 출력 파서 (LLM 응답을 문자열로 변환)
output_parser = StrOutputParser()
# 4. Chain 구성 (LCEL - LangChain Expression Language)
# prompt → llm → output_parser 순서로 실행
chain = prompt | llm | output_parser
# 5. 실행
result = chain.invoke({
"language": "Korean", # 프롬프트의 {language}에 대입
"text": "Hello, how are you?" # 프롬프트의 {text}에 대입
})
print(result) # "안녕하세요, 어떻게 지내세요?"
실행 결과:
안녕하세요, 어떻게 지내세요?
내부 동작:
prompt.invoke({"language": "Korean", "text": "Hello, how are you?"})→"Translate the following to Korean: Hello, how are you?"llm.invoke("Translate the following to Korean: Hello, how are you?")→AIMessage(content="안녕하세요, 어떻게 지내세요?")output_parser.invoke(AIMessage(...))→"안녕하세요, 어떻게 지내세요?"실무 활용:
- 다국어 번역 API
- 텍스트 요약 파이프라인
- 데이터 변환 및 정제
Sequential Chain
개념: Sequential Chain은 여러 LLM 호출을 순차적으로 연결하는 파이프라인입니다. 각 Chain의 출력이 다음 Chain의 입력으로 자동 전달되어, 복잡한 작업을 단계별로 처리할 수 있습니다. 사용 시나리오:
- 콘텐츠 생성: 주제 → 개요 → 본문
- 데이터 분석: 수집 → 정제 → 분석 → 리포트
- 코드 리뷰: 분석 → 문제 발견 → 개선 제안 핵심 개념:
output_key: 각 Chain의 출력을 명명하여 다음 Chain에서 참조input_variables: 최초 입력 변수output_variables: 최종 출력 변수 (중간 결과 포함 가능) 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.chains import LLMChain, SequentialChain
from langchain.prompts import PromptTemplate
# Chain 1: 주제 생성
topic_prompt = PromptTemplate(
input_variables=[keyword], # 입력: 키워드
template="Generate a blog post topic about {keyword}"
)
topic_chain = LLMChain(
llm=llm,
prompt=topic_prompt,
output_key="topic" # 출력 키: 다음 Chain에서 {topic}으로 참조 가능
)
# Chain 2: 개요 작성
outline_prompt = PromptTemplate(
input_variables=[topic], # 입력: 이전 Chain의 "topic"
template="Create an outline for: {topic}"
)
outline_chain = LLMChain(
llm=llm,
prompt=outline_prompt,
output_key="outline" # 출력 키: {outline}
)
# Chain 3: 본문 작성
content_prompt = PromptTemplate(
input_variables=["topic", "outline"], # 입력: 두 개의 이전 결과
template="Write a blog post:\nTopic: {topic}\nOutline: {outline}"
)
content_chain = LLMChain(
llm=llm,
prompt=content_prompt,
output_key="content" # 출력 키: {content}
)
# Sequential Chain으로 연결
overall_chain = SequentialChain(
chains=[topic_chain, outline_chain, content_chain], # 실행 순서
input_variables=[keyword], # 최초 입력
output_variables=["topic", "outline", "content"] # 최종 출력 (중간 결과 포함)
)
# 실행
result = overall_chain.invoke({"keyword": "Python async programming"})
print(result[content])
실행 결과 예시: 다음은 python를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
{
"topic": "Mastering Python Async Programming: A Complete Guide",
"outline": """
1. Introduction to Asyncio
2. Understanding Event Loop
3. async/await Syntax
4. Practical Examples
5. Common Pitfalls
""",
"content": """
# Mastering Python Async Programming
Async programming in Python allows you to write concurrent code...
[전체 블로그 글 내용]
"""
}
동작 흐름:
keyword="Python async programming"입력- topic_chain 실행 →
topic="Mastering Python Async Programming..." - outline_chain 실행 (topic 사용) →
outline="1. Introduction..." - content_chain 실행 (topic + outline 사용) →
content="# Mastering..."비용 및 성능:
- API 호출 횟수: 3회 (각 Chain마다 1회)
- 예상 토큰: 약 1,500~3,000 토큰 (모델에 따라 다름)
- 실행 시간: 약 5~15초 (네트워크 속도에 따라 다름) 주의사항:
- 중간 Chain 실패 시 전체 파이프라인 중단
output_key가 겹치면 덮어쓰기됨- 각 Chain은 독립적으로 LLM 호출 (병렬 실행 불가) 최적화 팁:
temperature=0.7: 창의적 글쓰기에 적합max_tokens제한으로 비용 절감- 캐싱으로 동일 입력 재사용 시 비용 절감
3. Prompt Templates
기본 템플릿
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
from langchain.prompts import PromptTemplate
# 단순 템플릿
template = PromptTemplate(
input_variables=[product],
template="Write a product description for {product}"
)
prompt = template.format(product="iPhone 15")
Chat Prompt Template
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
system_template = "You are a {role} with {experience} years of experience."
system_message = SystemMessagePromptTemplate.from_template(system_template)
human_template = "{question}"
human_message = HumanMessagePromptTemplate.from_template(human_template)
chat_prompt = ChatPromptTemplate.from_messages([
system_message,
human_message
])
messages = chat_prompt.format_messages(
role="Python developer",
experience=10,
question="How do I optimize database queries?"
)
Few-Shot Prompt Template
from langchain.prompts import FewShotPromptTemplate
examples = [
{"input": "happy", "output": "sad"},
{"input": "hot", "output": "cold"},
{"input": "fast", "output": "slow"}
]
example_template = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}"
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_template,
prefix="Find the antonym:",
suffix="Input: {word}\nOutput:",
input_variables=[word]
)
print(few_shot_prompt.format(word="big"))
4. Memory: 대화 이력
ConversationBufferMemory
개념: ConversationBufferMemory는 대화 이력을 전체 저장하는 가장 기본적인 메모리입니다. 모든 메시지를 그대로 보관하여 LLM이 전체 대화 맥락을 파악할 수 있게 합니다. 장점:
- 전체 대화 맥락 유지
- 구현이 간단
- 정보 손실 없음 단점:
- 대화가 길어지면 토큰 비용 증가
- 컨텍스트 길이 제한에 걸릴 수 있음 사용 시나리오:
- 짧은 대화 (10~20턴 이내)
- 전체 맥락이 중요한 상담 챗봇
- 디버깅 및 테스트
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
# 메모리 초기화
memory = ConversationBufferMemory()
# 대화 체인 생성
conversation = ConversationChain(
llm=llm,
memory=memory, # 메모리 연결
verbose=True # 내부 동작 로그 출력
)
# 첫 번째 대화
print(conversation.predict(input="Hi, I'm John"))
# 출력: "Hello John! How can I help you today?"
# 두 번째 대화 (이전 대화 기억)
print(conversation.predict(input="What's my name?"))
# 출력: "Your name is John."
# 메모리 내용 확인
print(memory.load_memory_variables({}))
# 출력: {
# "history": "Human: Hi, I'm John\nAI: Hello John! How can I help you today?\nHuman: What's my name?\nAI: Your name is John."
# }
실행 결과: 아래 코드는 code를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
첫 번째 응답: Hello John! How can I help you today?
두 번째 응답: Your name is John.
메모리 내용:
{
"history": """
Human: Hi, I'm John
AI: Hello John! How can I help you today?
Human: What's my name?
AI: Your name is John.
"""
}
내부 동작:
- 사용자 입력 → 메모리에 저장
- 전체 대화 이력 + 새 입력 → LLM에 전달
- LLM 응답 → 메모리에 저장
- 응답 반환 토큰 사용량:
- 1턴: ~50 토큰
- 5턴: ~250 토큰
- 10턴: ~500 토큰
- 주의: 대화가 길어질수록 비용 증가
ConversationBufferWindowMemory
개념: ConversationBufferWindowMemory는 최근 N개 메시지만 유지하는 슬라이딩 윈도우 방식의 메모리입니다. 오래된 대화는 자동으로 삭제되어 토큰 비용을 절감합니다. 장점:
- 토큰 비용 예측 가능 (일정하게 유지)
- 긴 대화에서도 컨텍스트 길이 제한 회피
- 최근 맥락에 집중 단점:
- 오래된 정보 손실
- 대화 초반 내용 참조 불가 사용 시나리오:
- 긴 대화 (50턴 이상)
- 최근 맥락만 중요한 경우
- 비용 최적화가 필요한 경우 아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.memory import ConversationBufferWindowMemory
# 최근 5개 메시지만 유지 (5턴 = Human 5개 + AI 5개)
memory = ConversationBufferWindowMemory(k=5)
conversation = ConversationChain(
llm=llm,
memory=memory
)
# 예시: 10번 대화 후
# 메모리에는 최근 5턴만 남음 (6~10번째 대화)
# 1~5번째 대화는 자동 삭제됨
실행 예시:
# 1번째 대화
conversation.predict(input="My name is John")
# 2번째 대화
conversation.predict(input="I live in Seoul")
# 3번째 대화
conversation.predict(input="I like Python")
# 4번째 대화
conversation.predict(input="I'm 30 years old")
# 5번째 대화
conversation.predict(input="I work as a developer")
# 6번째 대화
conversation.predict(input="What's my name?")
# 출력: "I don't have that information in our recent conversation."
# 이유: 1번째 대화가 삭제되어 이름 정보 손실
k 값 선택 가이드:
k=3: 매우 짧은 맥락 (비용 최소화)k=5: 일반적인 대화 (권장)k=10: 긴 맥락 유지k=20: 매우 긴 맥락 (비용 증가) 토큰 사용량:k=5: 약 250 토큰 (일정)k=10: 약 500 토큰 (일정)- 장점: 대화가 100턴이어도 토큰은 일정
ConversationSummaryMemory
개념: ConversationSummaryMemory는 대화 이력을 LLM으로 요약하여 저장합니다. 전체 대화를 압축하여 토큰을 절약하면서도 핵심 정보를 유지합니다. 장점:
- 긴 대화에서도 토큰 절약
- 핵심 정보 유지
- 오래된 대화도 요약 형태로 보존 단점:
- 요약 과정에서 추가 LLM 호출 (비용 발생)
- 세부 정보 손실 가능
- 요약 품질이 LLM 성능에 의존 사용 시나리오:
- 매우 긴 대화 (100턴 이상)
- 전체 맥락이 필요하지만 토큰 절약이 중요한 경우
- 고객 상담 이력 요약 동작 원리:
- 새 메시지 추가
- 일정 길이 초과 시 자동 요약
- 요약본 + 최근 메시지 유지
from langchain.memory import ConversationSummaryMemory
# 대화를 요약하여 저장 (토큰 절약)
memory = ConversationSummaryMemory(llm=llm)
conversation = ConversationChain(
llm=llm,
memory=memory
)
# 예시 대화
conversation.predict(input="My name is John and I live in Seoul")
conversation.predict(input="I'm a Python developer with 5 years of experience")
conversation.predict(input="I'm working on a machine learning project")
# ....많은 대화 ...
# 메모리 확인
print(memory.load_memory_variables({}))
# 출력: {
# "history": "John is a Python developer with 5 years of experience,
# living in Seoul and working on a machine learning project."
# }
실행 결과 예시: 아래 코드는 code를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
원본 대화 (200 토큰):
Human: My name is John and I live in Seoul
AI: Nice to meet you, John!
Human: I'm a Python developer with 5 years of experience
AI: That's impressive!
Human: I'm working on a machine learning project
AI: Sounds exciting!
요약 (50 토큰):
"John is a Python developer with 5 years of experience,
living in Seoul and working on a machine learning project."
비용 분석:
- 요약 없이: 10턴 = 500 토큰
- 요약 사용: 10턴 = 100 토큰 (요약본) + 50 토큰 (요약 비용)
- 절감: 약 70% 토큰 절약 언제 사용할까?:
- ✅ 대화가 20턴 이상
- ✅ 전체 맥락이 중요
- ✅ 토큰 비용 절감 필요
- ❌ 세부 정보가 중요한 경우 (예: 법률 상담)
5. Agents: 자율 도구 사용
기본 Agent
개념: Agent는 LLM이 자율적으로 도구를 선택하고 사용하는 시스템입니다. 사용자 질문을 분석하여 어떤 도구를 어떤 순서로 사용할지 스스로 결정합니다. ReAct 패턴 (Reasoning + Acting):
- Thought: 무엇을 해야 할지 생각
- Action: 도구 선택 및 실행
- Observation: 도구 실행 결과 확인
- 반복: 최종 답변까지 반복 Agent 타입:
ZERO_SHOT_REACT_DESCRIPTION: 도구 설명만 보고 사용 (가장 일반적)CONVERSATIONAL_REACT_DESCRIPTION: 대화 이력 포함STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION: 복잡한 입력 처리 사용 시나리오:- 복잡한 질문 (여러 단계 필요)
- 외부 API 호출 (검색, 계산, 데이터베이스)
- 동적 워크플로우 (미리 정의할 수 없는 경우) 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.agents import initialize_agent, AgentType, Tool
from langchain_openai import ChatOpenAI
# 도구 1: Wikipedia 검색
def search_wikipedia(query):
# 실제로는 Wikipedia API 호출
return f"Wikipedia result for: {query}"
# 도구 2: 계산기
def calculate(expression):
try:
return str(eval(expression)) # 실무에서는 ast.literal_eval 사용 권장
except:
return "Invalid expression"
# 도구 목록 정의
tools = [
Tool(
name="Wikipedia", # 도구 이름 (Agent가 참조)
func=search_wikipedia, # 실행할 함수
description="Search Wikipedia for information" # LLM이 이 설명을 보고 도구 선택
),
Tool(
name="Calculator",
func=calculate,
description="Calculate mathematical expressions. Input should be a valid Python expression."
)
]
# Agent 초기화
agent = initialize_agent(
tools=tools, # 사용 가능한 도구 목록
llm=ChatOpenAI(model="gpt-4o-mini", temperature=0), # temperature=0: 일관된 도구 선택
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # ReAct 패턴 사용
verbose=True # 내부 사고 과정 출력
)
# 실행
result = agent.run("What is the population of Seoul multiplied by 2?")
실행 결과 (verbose=True): 다음은 code를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
> Entering new AgentExecutor chain...
Thought: I need to find the population of Seoul first, then multiply it by 2.
Action: Wikipedia
Action Input: "Seoul population"
Observation: Seoul has a population of approximately 9.7 million people.
Thought: Now I need to multiply 9.7 million by 2.
Action: Calculator
Action Input: "9.7 * 2"
Observation: 19.4
Thought: I now know the final answer.
Final Answer: The population of Seoul multiplied by 2 is approximately 19.4 million.
> Finished chain.
내부 동작 흐름:
- 질문 분석: “서울 인구 × 2”
- 계획 수립: Wikipedia 검색 → 계산
- Wikipedia 도구 실행: “Seoul population” 검색
- 결과 확인: 9.7 million
- Calculator 도구 실행: “9.7 * 2” 계산
- 결과 확인: 19.4
- 최종 답변: “19.4 million” 주의사항:
temperature=0권장 (일관된 도구 선택)- 도구
description이 매우 중요 (LLM이 이를 보고 선택) - 무한 루프 방지:
max_iterations설정 가능 - 비용: 여러 번 LLM 호출 (Thought마다 1회) 실무 팁:
- 도구 개수는 5~10개 이내 권장 (너무 많으면 선택 어려움)
- 도구 이름은 명확하고 직관적으로
description에 입력 형식 명시
Custom Tool
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.tools import BaseTool
from typing import Optional
class WeatherTool(BaseTool):
name = "weather"
description = "Get current weather for a location"
def _run(self, location: str) -> str:
# 실제 날씨 API 호출
return f"Weather in {location}: Sunny, 20°C"
async def _arun(self, location: str) -> str:
# 비동기 버전
return self._run(location)
tools = [WeatherTool()]
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
6. RAG 구현
문서 로딩
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.document_loaders import TextLoader, PyPDFLoader, WebBaseLoader
# 텍스트 파일
loader = TextLoader("document.txt")
documents = loader.load()
# PDF
loader = PyPDFLoader("document.pdf")
documents = loader.load()
# 웹 페이지
loader = WebBaseLoader("https://example.com")
documents = loader.load()
문서 분할
개념: 문서 분할(Chunking)은 긴 문서를 작은 조각으로 나누는 과정입니다. LLM의 컨텍스트 길이 제한과 검색 정확도를 위해 필수적입니다. 왜 필요한가?:
- LLM 컨텍스트 길이 제한 (예: 8K, 128K 토큰)
- 검색 정확도 향상 (작은 청크가 더 정확)
- 비용 절감 (관련 부분만 LLM에 전달) 주요 파라미터:
chunk_size: 각 청크의 최대 크기 (문자 수)chunk_overlap: 청크 간 겹치는 부분 (문맥 유지)length_function: 길이 측정 함수 (보통len또는 토큰 카운터) RecursiveCharacterTextSplitter의 동작:
- 먼저
\n\n(문단)으로 분할 시도 - 안 되면
\n(줄)으로 분할 - 안 되면
.(문장)으로 분할 - 안 되면 공백으로 분할
- 최후에는 문자 단위로 분할 아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 각 청크 최대 1000자
chunk_overlap=200, # 청크 간 200자 겹침 (문맥 유지)
length_function=len # 길이 측정: 문자 수 기준
)
# 문서 분할
chunks = text_splitter.split_documents(documents)
print(f"Split into {len(chunks)} chunks")
# 예시 출력
# Split into 45 chunks
실행 예시: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 원본 문서 (3000자)
document = """
LangChain is a framework for developing applications powered by language models.
[....2900자 더 ...]
"""
# 분할 결과
chunks = text_splitter.split_text(document)
# chunks[0]: "LangChain is a framework....(1000자)"
# chunks[1]: "....framework for developing....(1000자)" (200자 겹침)
# chunks[2]: "....applications powered....(1000자)" (200자 겹침)
chunk_overlap의 중요성: 아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
청크 1: "LangChain은 LLM 애플리케이션 개발 프레임워크입니다."
청크 2 (overlap 없음): "주요 기능은 Chains, Agents, Memory입니다."
→ 문맥 단절!
청크 1: "LangChain은 LLM 애플리케이션 개발 프레임워크입니다."
청크 2 (overlap 200): "...개발 프레임워크입니다. 주요 기능은 Chains, Agents, Memory입니다."
→ 문맥 유지!
chunk_size 선택 가이드:
- 500자: 짧은 문서, 정확한 검색
- 1000자: 일반적인 경우 (권장)
- 2000자: 긴 문맥이 필요한 경우
- 4000자: 매우 긴 문맥 (비용 증가) chunk_overlap 선택 가이드:
- 0: 문맥 불필요 (예: 독립적인 Q&A)
- 100~200: 일반적인 경우 (권장)
- chunk_size의 10~20%: 경험적 최적값 실무 팁:
- 토큰 기반 분할:
tiktoken으로 정확한 토큰 수 측정 - 문서 타입별 최적화: PDF, HTML, 코드마다 다른 전략
- 메타데이터 유지: 청크에 원본 파일명, 페이지 번호 등 포함
벡터 스토어 생성
개념: 벡터 스토어는 텍스트를 벡터(숫자 배열)로 변환하여 저장하고, 유사도 검색을 가능하게 하는 데이터베이스입니다. 동작 원리:
- 임베딩: 텍스트 → 벡터 (예: 1536차원 배열)
- 저장: 벡터를 데이터베이스에 저장
- 검색: 질문을 벡터로 변환 → 유사한 벡터 찾기 임베딩이란?:
- 텍스트의 의미를 숫자로 표현
- 비슷한 의미 = 비슷한 벡터
- 예: “dog” ≈ “puppy” (벡터 거리가 가까움) Chroma의 특징:
- 로컬 파일 기반 (설치 간단)
- 빠른 검색 속도
- 메타데이터 필터링 지원
- 영구 저장 가능 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain_openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
# 임베딩 모델 초기화
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small" # OpenAI 임베딩 모델
# 1536차원 벡터 생성
# 비용: $0.00002 / 1K 토큰
)
# 벡터 스토어 생성
vectorstore = Chroma.from_documents(
documents=chunks, # 분할된 문서 청크
embedding=embeddings, # 임베딩 모델
persist_directory="./chroma_db" # 저장 경로 (영구 저장)
)
# 저장 후 로드
# vectorstore = Chroma(
# persist_directory="./chroma_db",
# embedding_function=embeddings
# )
실행 과정: 아래 코드는 code를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
1. 청크 45개 입력
↓
2. 각 청크를 임베딩 (45번 API 호출)
청크 1: "LangChain is..." → [0.12, -0.34, 0.56, ...] (1536차원)
청크 2: "Chains allow..." → [0.23, -0.11, 0.78, ...]
...
↓
3. 벡터 + 원본 텍스트를 Chroma DB에 저장
↓
4. 디스크에 영구 저장 (./chroma_db/)
비용 계산:
청크 45개 × 평균 500자 = 22,500자 ≈ 5,600 토큰
임베딩 비용: 5,600 토큰 × $0.00002 / 1K = $0.00011 (약 0.15원)
임베딩 모델 비교:
| 모델 | 차원 | 비용 | 성능 |
|---|---|---|---|
| text-embedding-3-small | 1536 | $0.00002/1K | 빠름, 저렴 |
| text-embedding-3-large | 3072 | $0.00013/1K | 느림, 고성능 |
| text-embedding-ada-002 | 1536 | $0.00010/1K | 구버전 |
| persist_directory의 역할: | |||
| 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다. |
# 첫 실행: 벡터 스토어 생성 및 저장
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 이후 실행: 저장된 벡터 스토어 로드 (임베딩 재생성 불필요)
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
실무 팁:
- 대량 문서: 배치 임베딩으로 속도 향상
- 비용 절감: 캐싱으로 중복 임베딩 방지
- 메타데이터 활용: 필터링으로 검색 정확도 향상
RAG Chain
개념: RAG (Retrieval-Augmented Generation)는 검색 + 생성을 결합한 기법입니다. 질문과 관련된 문서를 먼저 검색하고, 그 문서를 참고하여 LLM이 답변을 생성합니다. RAG의 장점:
- 최신 정보 제공 (LLM 학습 데이터 이후)
- 환각(Hallucination) 감소
- 특정 도메인 지식 활용
- 출처 추적 가능 동작 흐름:
- 질문 임베딩: 질문을 벡터로 변환
- 유사도 검색: 벡터 스토어에서 관련 문서 검색
- 컨텍스트 구성: 검색된 문서를 프롬프트에 포함
- 답변 생성: LLM이 문서를 참고하여 답변 chain_type 옵션:
stuff: 모든 문서를 한 번에 전달 (가장 간단, 권장)map_reduce: 각 문서 개별 처리 후 결합 (긴 문서)refine: 순차적으로 답변 개선 (정교한 답변)map_rerank: 각 문서별 점수 매겨 최고 선택 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.chains import RetrievalQA
# RAG Chain 생성
qa_chain = RetrievalQA.from_chain_type(
llm=llm, # 답변 생성 LLM
chain_type="stuff", # 모든 문서를 한 번에 전달
retriever=vectorstore.as_retriever(
search_kwargs={"k": 3} # 상위 3개 문서 검색
)
)
# 질문
question = "What is the main topic of the document?"
answer = qa_chain.run(question)
print(answer)
실행 과정: 다음은 code를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
1. 질문: "What is the main topic of the document?"
↓
2. 질문 임베딩: [0.45, -0.23, 0.67, ...] (1536차원)
↓
3. 유사도 검색 (코사인 유사도):
청크 12: 유사도 0.89 ✓
청크 7: 유사도 0.85 ✓
청크 23: 유사도 0.82 ✓
↓
4. 프롬프트 구성:
"Based on the following context, answer the question.
Context:
[청크 12 내용]
[청크 7 내용]
[청크 23 내용]
Question: What is the main topic of the document?
Answer:"
↓
5. LLM 답변 생성:
"The main topic of the document is LangChain,
a framework for building LLM applications..."
k 값 선택 가이드:
k=1: 가장 관련 있는 문서만 (빠름, 저렴)k=3: 일반적인 경우 (권장)k=5: 복잡한 질문 (느림, 비용 증가)k=10: 매우 복잡한 질문 (컨텍스트 길이 주의) chain_type 비교: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# stuff: 모든 문서를 한 번에 전달 (가장 빠름)
# 프롬프트: Context: [doc1] [doc2] [doc3]\nQuestion: ...
# map_reduce: 각 문서 개별 처리 후 결합
# 1단계: 각 문서별 답변 생성 (3번 LLM 호출)
# 2단계: 답변들을 종합 (1번 LLM 호출)
# 총 4번 LLM 호출 (비용 증가)
# refine: 순차적으로 답변 개선
# 1단계: doc1로 답변 생성
# 2단계: doc2를 보고 답변 개선
# 3단계: doc3를 보고 답변 개선
# 총 3번 LLM 호출
실무 팁:
- 대부분의 경우
chain_type="stuff"사용 k=3이 비용과 성능의 균형점return_source_documents=True로 출처 확인 가능
고급 RAG
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
qa = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=vectorstore.as_retriever(),
memory=memory
)
# 대화형 질문
print(qa({"question": "What is RAG?"}))
print(qa({"question": "Can you explain more about it?"}))
7. Vector Stores
Chroma
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
# 생성
vectorstore = Chroma.from_texts(
texts=["LangChain is awesome", "Python is great"],
embedding=embeddings,
persist_directory="./db"
)
# 유사도 검색
results = vectorstore.similarity_search("LangChain", k=2)
for doc in results:
print(doc.page_content)
FAISS
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.vectorstores import FAISS
# 생성
vectorstore = FAISS.from_texts(
texts=["text1", "text2"],
embedding=embeddings
)
# 저장/로드
vectorstore.save_local("faiss_index")
vectorstore = FAISS.load_local("faiss_index", embeddings)
Pinecone (클라우드)
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.vectorstores import Pinecone
import pinecone
pinecone.init(api_key="...", environment="...")
vectorstore = Pinecone.from_texts(
texts=["text1", "text2"],
embedding=embeddings,
index_name="my-index"
)
8. 실전 예제
예제 1: 문서 Q&A 챗봇
목표: 여러 문서를 읽고 질문에 답하는 대화형 챗봇을 만듭니다. 이전 대화를 기억하여 “그것에 대해 더 설명해줘” 같은 후속 질문도 처리합니다. 핵심 기능:
- 디렉토리 내 모든 문서 자동 로딩
- RAG로 관련 문서 검색 후 답변
- 대화 이력 기억 (ConversationBufferMemory)
- 후속 질문 처리 가능 사용 시나리오:
- 사내 문서 Q&A 시스템
- 기술 문서 도우미
- 고객 지원 챗봇
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import Chroma
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
class DocumentQABot:
def __init__(self, docs_path):
"""
문서 Q&A 챗봇 초기화
Args:
docs_path: 문서가 있는 디렉토리 경로
"""
# 1. 문서 로딩
loader = DirectoryLoader(
docs_path,
glob="**/*.txt" # 모든 하위 디렉토리의 .txt 파일
)
documents = loader.load()
print(f"Loaded {len(documents)} documents")
# 2. 문서 분할
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 각 청크 1000자
chunk_overlap=200 # 200자 겹침
)
chunks = text_splitter.split_documents(documents)
print(f"Split into {len(chunks)} chunks")
# 3. 벡터 스토어 생성
embeddings = OpenAIEmbeddings()
self.vectorstore = Chroma.from_documents(
chunks,
embeddings,
persist_directory="./db" # 영구 저장
)
print("Vector store created")
# 4. LLM 및 메모리 초기화
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0 # 일관된 답변
)
memory = ConversationBufferMemory(
memory_key="chat_history", # 메모리 키
return_messages=True # 메시지 형식으로 반환
)
# 5. 대화형 RAG Chain 생성
self.qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=self.vectorstore.as_retriever(
search_kwargs={"k": 3} # 상위 3개 문서 검색
),
memory=memory # 대화 이력 저장
)
def ask(self, question):
"""
질문하기
Args:
question: 사용자 질문
Returns:
답변 문자열
"""
response = self.qa_chain({"question": question})
return response[answer]
# 사용 예시
bot = DocumentQABot("./docs")
# 첫 번째 질문
print(bot.ask("What is the main topic?"))
# 출력: "The main topic is LangChain, a framework for..."
# 후속 질문 (이전 대화 기억)
print(bot.ask("Can you explain more?"))
# 출력: "LangChain provides several key features..."
실행 과정: 다음은 code를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
1. 초기화 (bot = DocumentQABot("./docs"))
├─ 문서 로딩: ./docs/**/*.txt
├─ 분할: 1000자 청크로 분할
├─ 임베딩: 각 청크를 벡터로 변환
└─ 벡터 스토어 저장: ./db/
2. 첫 번째 질문 ("What is the main topic?")
├─ 질문 임베딩
├─ 유사도 검색: 상위 3개 청크
├─ 프롬프트 구성: 청크 + 질문
├─ LLM 답변 생성
└─ 메모리 저장: 질문 + 답변
3. 두 번째 질문 ("Can you explain more?")
├─ 이전 대화 로드 (메모리)
├─ "it"이 "LangChain"을 가리킴을 이해
├─ 관련 문서 재검색
└─ 답변 생성
메모리 내용 예시: 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
{
"chat_history": [
HumanMessage(content="What is the main topic?"),
AIMessage(content="The main topic is LangChain..."),
HumanMessage(content="Can you explain more?"),
AIMessage(content="LangChain provides...")
]
}
확장 아이디어: 다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 출처 문서 반환
# 함수 정의 및 구현
def ask_with_sources(self, question):
response = self.qa_chain({"question": question})
return {
"answer": response[answer],
"sources": response.get("source_documents", [])
}
# 스트리밍 답변
def ask_stream(self, question):
for chunk in self.qa_chain.stream({"question": question}):
yield chunk
# 메모리 초기화
def reset(self):
self.qa_chain.memory.clear()
실무 팁:
- 문서 업데이트 시 벡터 스토어 재생성 필요
persist_directory로 재시작 시 빠른 로딩- 대량 문서는 배치 처리로 속도 향상
예제 2: 코드 생성 Assistant
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
class CodeGenerator:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0.2)
self.prompt = ChatPromptTemplate.from_messages([
("system", """You are an expert programmer.
Generate clean, well-commented code following best practices.
Include error handling and type hints."""),
("user", """Language: {language}
Task: {task}
Generate code:""")
])
self.chain = self.prompt | self.llm | StrOutputParser()
def generate(self, task, language="python"):
return self.chain.invoke({
"language": language,
"task": task
})
# 사용
generator = CodeGenerator()
code = generator.generate(
"Read a CSV file and calculate column averages",
"python"
)
print(code)
예제 3: 다국어 번역기
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
class Translator:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o-mini")
self.prompt = ChatPromptTemplate.from_messages([
("system", "You are a professional translator."),
("user", "Translate to {target_lang}:\n\n{text}")
])
self.chain = self.prompt | self.llm
def translate(self, text, target_lang):
response = self.chain.invoke({
"text": text,
"target_lang": target_lang
})
return response.content
# 사용
translator = Translator()
result = translator.translate("Hello, world!", "Korean")
print(result) # "안녕하세요, 세계!"
RAG 심화
커스텀 Retriever
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.schema import BaseRetriever, Document
from typing import List
class CustomRetriever(BaseRetriever):
def _get_relevant_documents(self, query: str) -> List[Document]:
# 커스텀 검색 로직
# 예: 데이터베이스 쿼리, API 호출 등
results = search_my_database(query)
return [Document(page_content=r) for r in results]
async def _aget_relevant_documents(self, query: str) -> List[Document]:
return self._get_relevant_documents(query)
Reranking
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
# 기본 retriever
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
# Compressor (관련성 높은 것만 선택)
compressor = LLMChainExtractor.from_llm(llm)
# Compression retriever
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=base_retriever
)
# 사용
docs = compression_retriever.get_relevant_documents("What is LangChain?")
비용 최적화
토큰 계산
개념: LLM API는 토큰 단위로 과금됩니다. 토큰은 단어보다 작은 단위로, 영어는 약 0.75 단어 = 1 토큰, 한글은 약 0.5 글자 = 1 토큰입니다. 왜 중요한가?:
- 비용 예측 및 관리
- 컨텍스트 길이 제한 확인
- 최적화 지점 발견 토큰 계산 방법:
- tiktoken: OpenAI 공식 토큰 카운터
- get_openai_callback: LangChain 내장 콜백
import tiktoken
def count_tokens(text, model="gpt-4o-mini"):
"""
텍스트의 토큰 수 계산
Args:
text: 계산할 텍스트
model: 모델 이름 (토큰화 방식이 모델마다 다름)
Returns:
토큰 수 (int)
"""
encoding = tiktoken.encoding_for_model(model)
return len(encoding.encode(text))
# 예시
text = "LangChain is a framework for building LLM applications."
tokens = count_tokens(text)
print(f"Tokens: {tokens}") # 출력: Tokens: 11
# Chain에서 토큰 추적
from langchain.callbacks import get_openai_callback
with get_openai_callback() as cb:
result = chain.invoke({"input": "Explain LangChain"})
print(f"Total Tokens: {cb.total_tokens}") # 총 토큰
print(f"Prompt Tokens: {cb.prompt_tokens}") # 입력 토큰
print(f"Completion Tokens: {cb.completion_tokens}") # 출력 토큰
print(f"Total Cost: ${cb.total_cost:.6f}") # 총 비용
실행 결과 예시: 아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
Tokens: 11
Total Tokens: 523
Prompt Tokens: 45
Completion Tokens: 478
Total Cost: $0.000785
토큰 계산 상세: 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 영어
text_en = "Hello, how are you?"
tokens_en = count_tokens(text_en) # 약 6 토큰
# 비율: 5 단어 / 6 토큰 ≈ 0.83 단어/토큰
# 한글
text_ko = "안녕하세요, 어떻게 지내세요?"
tokens_ko = count_tokens(text_ko) # 약 20 토큰
# 비율: 10 글자 / 20 토큰 ≈ 0.5 글자/토큰
비용 계산:
# GPT-4o-mini 가격 (2026년 기준)
# 입력: $0.15 / 1M 토큰
# 출력: $0.60 / 1M 토큰
prompt_tokens = 45
completion_tokens = 478
# 비용 계산
input_cost = (prompt_tokens / 1_000_000) * 0.15
output_cost = (completion_tokens / 1_000_000) * 0.60
total_cost = input_cost + output_cost
print(f"Input Cost: ${input_cost:.6f}") # $0.000007
print(f"Output Cost: ${output_cost:.6f}") # $0.000287
print(f"Total Cost: ${total_cost:.6f}") # $0.000294
get_openai_callback 활용: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 여러 Chain 호출의 총 비용 추적
with get_openai_callback() as cb:
# 여러 작업 수행
result1 = chain1.invoke({"input": "..."})
result2 = chain2.invoke({"input": "..."})
result3 = chain3.invoke({"input": "..."})
# 전체 통계
print(f"Total Calls: {cb.successful_requests}")
print(f"Total Tokens: {cb.total_tokens}")
print(f"Total Cost: ${cb.total_cost:.6f}")
실무 비용 예시: 아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
RAG 시스템 (1000 질문/일):
- 평균 프롬프트: 500 토큰 (검색된 문서 포함)
- 평균 답변: 200 토큰
- 일일 비용: 1000 × (500×0.15 + 200×0.60) / 1M = $0.195
- 월간 비용: $5.85
최적화 팁:
- 프롬프트 길이 최소화
max_tokens제한 설정- 캐싱으로 중복 호출 방지
- 저렴한 모델 우선 사용 (gpt-4o-mini)
캐싱
개념: 캐싱은 동일한 입력에 대한 LLM 응답을 저장하여, 재호출 시 API를 사용하지 않고 저장된 결과를 반환합니다. 장점:
- 비용 절감 (API 호출 0회)
- 속도 향상 (즉시 응답)
- API 제한 회피 단점:
- 메모리/디스크 사용
- 캐시 무효화 관리 필요
- 동일 입력만 캐시 적중 캐시 타입:
- InMemoryCache: 메모리에 저장 (빠름, 재시작 시 삭제)
- SQLiteCache: 파일에 저장 (영구, 재시작 후에도 유지)
- RedisCache: Redis에 저장 (분산 시스템) 아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.cache import InMemoryCache, SQLiteCache
from langchain.globals import set_llm_cache
# 옵션 1: 메모리 캐시 (빠르지만 재시작 시 삭제)
set_llm_cache(InMemoryCache())
# 옵션 2: SQLite 캐시 (영구 저장)
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
# 사용 예시
llm.invoke("What is 2+2?") # 첫 호출: API 호출 (0.5초)
llm.invoke("What is 2+2?") # 두 번째: 캐시 반환 (0.001초)
실행 결과:
첫 번째 호출:
- API 호출: ✓
- 응답 시간: 0.5초
- 비용: $0.0001
- 결과: "2+2 equals 4"
두 번째 호출 (동일 입력):
- API 호출: ✗ (캐시 적중)
- 응답 시간: 0.001초 (500배 빠름)
- 비용: $0 (무료)
- 결과: "2+2 equals 4" (동일)
캐시 키 생성: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 캐시 키는 프롬프트 + 모델 + 파라미터로 생성
# 예시:
# 입력 1: "What is 2+2?" + gpt-4o-mini + temp=0.7
# → 캐시 키: hash("What is 2+2?gpt-4o-mini0.7")
# 입력 2: "What is 2+2?" + gpt-4o-mini + temp=0.7
# → 동일 캐시 키 → 캐시 적중!
# 입력 3: "What is 2+2?" + gpt-4o-mini + temp=0.8
# → 다른 캐시 키 → 캐시 미스 (API 호출)
실무 활용 예시: 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# FAQ 챗봇: 자주 묻는 질문 캐싱
set_llm_cache(SQLiteCache(database_path="faq_cache.db"))
faq_questions = [
"What are your business hours?",
"How do I reset my password?",
"What is your return policy?"
]
# 첫 실행: 각 질문마다 API 호출
for q in faq_questions:
answer = llm.invoke(q) # API 호출
# 이후 실행: 모두 캐시에서 반환 (무료)
for q in faq_questions:
answer = llm.invoke(q) # 캐시 반환
비용 절감 효과: 아래 코드는 code를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
시나리오: FAQ 챗봇 (10개 질문, 각 100회/일 반복)
캐싱 없음:
- 일일 호출: 10 × 100 = 1,000회
- 일일 비용: 1,000 × $0.0001 = $0.10
- 월간 비용: $3.00
캐싱 사용:
- 첫날 호출: 10회 (캐시 생성)
- 이후 호출: 0회 (캐시 사용)
- 일일 비용: $0.001 (첫날만)
- 월간 비용: $0.001
- 절감: 99.97%
캐시 무효화: 아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.cache import SQLiteCache
from langchain.globals import set_llm_cache, clear_llm_cache
# 캐시 설정
cache = SQLiteCache(database_path=".langchain.db")
set_llm_cache(cache)
# 캐시 초기화 (모든 캐시 삭제)
clear_llm_cache()
# 특정 캐시만 삭제 (수동)
import os
os.remove(".langchain.db")
주의사항:
- 시간 민감 정보: 날씨, 주가 등은 캐싱 부적합
- 개인화 답변: 사용자별 다른 답변은 캐싱 효과 낮음
- 캐시 크기: SQLite 파일이 커질 수 있음 (주기적 정리 필요) 최적 사용 사례:
- ✅ FAQ 시스템
- ✅ 문서 요약 (동일 문서 반복)
- ✅ 번역 (동일 문장 반복)
- ❌ 실시간 정보 조회
- ❌ 창의적 글쓰기 (매번 다른 결과 필요)
에러 처리
Retry 설정
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
max_retries=3,
timeout=30
)
Fallback
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from langchain.chat_models import ChatOpenAI
from langchain.llms import OpenAI
# 메인 모델
primary_llm = ChatOpenAI(model="gpt-4o")
# 폴백 모델 (저렴)
fallback_llm = ChatOpenAI(model="gpt-4o-mini")
# Fallback chain
llm = primary_llm.with_fallbacks([fallback_llm])
실전 프로젝트: 문서 분석 API
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from fastapi import FastAPI, UploadFile, HTTPException
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
import tempfile
import os
app = FastAPI()
class DocumentAnalyzer:
def __init__(self):
self.embeddings = OpenAIEmbeddings()
self.llm = ChatOpenAI(model="gpt-4o-mini")
self.vectorstores = {}
def process_document(self, file_path, doc_id):
# 문서 로딩
loader = PyPDFLoader(file_path)
documents = loader.load()
# 분할
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(documents)
# 벡터 스토어 생성
vectorstore = Chroma.from_documents(
chunks,
self.embeddings,
persist_directory=f"./db/{doc_id}"
)
self.vectorstores[doc_id] = vectorstore
return len(chunks)
def query(self, doc_id, question):
if doc_id not in self.vectorstores:
raise ValueError("Document not found")
qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
retriever=self.vectorstores[doc_id].as_retriever()
)
return qa_chain.run(question)
analyzer = DocumentAnalyzer()
@app.post("/upload")
async def upload_document(file: UploadFile):
# 임시 파일로 저장
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
content = await file.read()
tmp.write(content)
tmp_path = tmp.name
try:
doc_id = file.filename
chunks = analyzer.process_document(tmp_path, doc_id)
return {"doc_id": doc_id, "chunks": chunks}
finally:
os.unlink(tmp_path)
@app.post("/query")
async def query_document(doc_id: str, question: str):
try:
answer = analyzer.query(doc_id, question)
return {"answer": answer}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
LangChain vs 직접 API 호출
직접 API 호출이 나은 경우
아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 간단한 작업
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Translate to Korean: Hello"}]
)
LangChain이 나은 경우
아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 복잡한 워크플로우
chain = (
load_documents
| split_documents
| create_embeddings
| store_in_vectordb
| retrieve_relevant
| generate_answer
)
베스트 프랙티스
1. 환경 변수 관리
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ✅ .env 파일
OPENAI_API_KEY=sk-...
PINECONE_API_KEY=...
# ✅ 코드에서 로드
from dotenv import load_dotenv
load_dotenv()
2. 에러 처리
아래 코드는 python를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ✅ 모든 Chain에 try-except
try:
result = chain.invoke({"input": "..."})
except Exception as e:
logger.error(f"Chain error: {e}")
# 폴백 로직
3. 로깅
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ✅ Verbose 모드로 디버깅
chain = ConversationChain(llm=llm, verbose=True)
# ✅ 커스텀 콜백
from langchain.callbacks import StdOutCallbackHandler
chain.invoke({"input": "..."}, callbacks=[StdOutCallbackHandler()])
4. 테스트
아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 단위 테스트
def test_chain():
chain = create_my_chain()
result = chain.invoke({"input": "test"})
assert len(result) > 0
참고 자료
- LangChain Documentation
- LangChain Cookbook
- LangChain Templates
- LangSmith - 디버깅 및 모니터링 한 줄 요약: LangChain은 LLM 애플리케이션 개발을 단순화하는 프레임워크로, Chains, Agents, Memory, RAG를 쉽게 구현할 수 있으며, 복잡한 AI 워크플로우 구축에 최적화되어 있습니다.