LangChain을 쓰는 이유
OpenAI API를 직접 써도 에이전트를 만들 수 있습니다. 그런데 LangChain을 쓰면:
- LLM, 도구, 프롬프트, 메모리를 표준화된 인터페이스로 조합
- 복잡한 체인을 선언적으로 구성
- 다양한 LLM 제공사를 동일한 코드로 사용
- 에이전트·RAG·평가 도구 등 생태계 활용
flowchart LR
subgraph LANGCHAIN["LangChain 핵심 구성"]
P["Prompt\n프롬프트 템플릿"]
L["LLM\nChatOpenAI 등"]
O["OutputParser\n응답 파싱"]
T["Tools\n도구 모음"]
M["Memory\n대화 기록"]
end
P --> L --> O
T --> L
M --> L
설치와 설정
pip install langchain langchain-openai langchain-community python-dotenv
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
load_dotenv()
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
# api_key 자동으로 OPENAI_API_KEY 환경변수 사용
)
첫 번째 체인: LCEL
LCEL (LangChain Expression Language) 은 | 연산자로 컴포넌트를 연결합니다.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1. 프롬프트 템플릿
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 친절한 한국어 어시스턴트입니다."),
("human", "{question}")
])
# 2. 체인 구성 (LCEL)
chain = prompt | llm | StrOutputParser()
# 3. 실행
result = chain.invoke({"question": "파이썬이란 무엇인가요?"})
print(result)
flowchart LR
I["입력\n{question}"] --> P["ChatPromptTemplate"]
P --> L["ChatOpenAI"]
L --> O["StrOutputParser"]
O --> R["문자열 결과"]
프롬프트 템플릿 심화
from langchain_core.prompts import ChatPromptTemplate
# 시스템 + 사용자 메시지
prompt = ChatPromptTemplate.from_messages([
("system", """당신은 {role} 전문가입니다.
다음 규칙을 따르세요: {rules}"""),
("human", "{question}")
])
chain = prompt | llm | StrOutputParser()
result = chain.invoke({
"role": "파이썬 튜터",
"rules": "초보자에게 설명하듯이, 예제 코드 포함",
"question": "리스트 컴프리헨션이란?"
})
출력 파서: 구조화된 응답
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List
class BlogIdea(BaseModel):
title: str = Field(description="블로그 제목")
summary: str = Field(description="한 줄 요약")
tags: List[str] = Field(description="관련 태그 3개")
parser = JsonOutputParser(pydantic_object=BlogIdea)
prompt = ChatPromptTemplate.from_messages([
("system", "다음 형식으로만 응답하세요.\n{format_instructions}"),
("human", "주제: {topic}")
]).partial(format_instructions=parser.get_format_instructions())
chain = prompt | llm | parser
result = chain.invoke({"topic": "AI 에이전트 활용법"})
print(result.title) # AI 에이전트로 업무 자동화하기
print(result.tags) # ['AI', '자동화', '에이전트']
체인 연결: 순차 파이프라인
flowchart LR
T["주제 입력"] --> C1["아이디어 생성 체인"]
C1 --> C2["블로그 초안 작성 체인"]
C2 --> C3["SEO 최적화 체인"]
C3 --> R["최종 결과"]
from langchain_core.runnables import RunnablePassthrough
# 체인 1: 개요 생성
outline_chain = (
ChatPromptTemplate.from_template("'{topic}' 주제의 블로그 개요를 3줄로")
| llm
| StrOutputParser()
)
# 체인 2: 본문 작성 (체인 1의 출력을 입력으로)
draft_chain = (
ChatPromptTemplate.from_template("이 개요로 500자 블로그 초안 작성:\n{outline}")
| llm
| StrOutputParser()
)
# 순차 연결
full_chain = (
{"topic": RunnablePassthrough()}
| {"outline": outline_chain}
| draft_chain
)
result = full_chain.invoke("LangChain 에이전트 입문")
스트리밍
# 스트리밍 출력
for chunk in chain.stream({"question": "머신러닝이란?"}):
print(chunk, end="", flush=True)
# 비동기 스트리밍
async def stream_answer(question: str):
async for chunk in chain.astream({"question": question}):
print(chunk, end="", flush=True)
메모리: 대화 기록 관리
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 세션별 히스토리 저장소
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 프롬프트에 히스토리 추가
prompt_with_history = ChatPromptTemplate.from_messages([
("system", "당신은 친절한 어시스턴트입니다."),
("placeholder", "{chat_history}"), # 대화 기록 삽입
("human", "{input}")
])
chain_with_history = RunnableWithMessageHistory(
prompt_with_history | llm | StrOutputParser(),
get_session_history,
input_messages_key="input",
history_messages_key="chat_history"
)
# 같은 session_id로 대화 유지
config = {"configurable": {"session_id": "user-123"}}
print(chain_with_history.invoke({"input": "내 이름은 철수야"}, config=config))
print(chain_with_history.invoke({"input": "내 이름이 뭐야?"}, config=config))
# → "철수님이라고 하셨습니다."
LCEL 핵심 메서드
| 메서드 | 설명 | 사용 상황 |
|---|---|---|
.invoke() | 동기 단건 실행 | 일반 호출 |
.stream() | 동기 스트리밍 | CLI 출력 |
.batch() | 여러 입력 일괄 처리 | 대량 처리 |
.ainvoke() | 비동기 단건 | FastAPI 등 |
.astream() | 비동기 스트리밍 | 웹 서비스 |
다음 단계: LangGraph
복잡한 멀티스텝 에이전트는 LangChain 체인만으로 한계가 있습니다. LangGraph는 상태 기반 그래프로 더 복잡한 흐름을 표현합니다.
flowchart LR
LC["LangChain\n선형 파이프라인\nA→B→C"] -->|"복잡한 분기·루프"| LG["LangGraph\n상태 그래프\nA→B↔C→D"]
다음 편에서 LangGraph로 실제 에이전트를 구현합니다.
정리
| 개념 | 내용 |
|---|---|
| LCEL | ` |
| ChatPromptTemplate | 시스템·사용자 메시지 템플릿 구성 |
| OutputParser | LLM 응답을 원하는 형식으로 파싱 |
| RunnableWithMessageHistory | 세션별 대화 기록 자동 관리 |
.stream() / .astream() | 실시간 스트리밍 출력 |
다음 편에서는 도구 설계와 에이전트 구현 — LangGraph로 실제 도구를 사용하는 에이전트를 만듭니다.