RAG
사용자 발화 입력부터 최종 응답 출력까지의 처리 과정이 분기나 피드백 루프 없이 단 하나의 직렬 경로(일방통행)로만 순차 실행되는 구조를 뜻합니다.
[사용자 입력] ──> [입력 전처리/의도 파악] ──> [지식 검색/로직 수행] ──> [응답 생성] ──> [최종 출력]
핵심 특징
주요 한계점
발전된 파이프라인 형태와의 비교
| 구분 | 단선 파이프라인 (Linear Chain) | 순환·분기형 에이전트 파이프라인 (Graph/Loop) |
|---|---|---|
| 흐름 구조 | 일방통행 직렬 연결 (A → B → C) | 조건부 분기(Router), 순환 루프(Graph DAG) |
| 오류 처리 | 실패 시 시스템 에러 또는 오답 출력 | 자가 평가(Reflection) 후 재검색·재작성 루프 수행 |
| 도구 활용 | 정해진 순서대로 1회 호출 | 결과에 따라 필요한 도구를 자율적으로 반복 호출 |
| 구현 예시 | 단순 LangChain SequentialChain | LangGraph, LangSmith, AutoGen, Multi-Agent 시스템 |
거대 언어 모델(LLM)을 단독 텍스트 생성기에 머무르게 하지 않고, 외부 데이터베이스, API, 계산 도구 등과 연결해 실무형 애플리케이션(RAG 시스템, 자율 에이전트 등)으로 조립·구축할 수 있도록 돕는 오픈소스 오케스트레이션 프레임워크입니다.
[입력 프롬프트] ──> [LCEL 체인 파이프라인] ──> [벡터 DB / 도구 연동] ──> [LLM 추론] ──> [구조화된 출력]
핵심 구성 요소
|) 문법을 사용해 구성 요소들을 직관적으로 엮고, 비동기(async) 처리, 스트리밍, 병렬 실행을 기본 지원하는 선언적 파이프라인 문법입니다.파이썬 기본 파이프라인 예시 (LCEL)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 1. 프롬프트 정의
prompt = ChatPromptTemplate.from_template("{topic}에 대해 핵심만 3줄로 요약해 줘.")
# 2. 모델 설정
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 3. 체인 결합 (LCEL)
chain = prompt | model | StrOutputParser()
# 4. 실행
response = chain.invoke({"topic": "Docker"})
print(response)
도입 장단점
| 구분 | 장점 | 단점 및 고려사항 |
|---|---|---|
| 개발 생산성 | 수백 가지 서드파티 도구·DB 연동 어댑터가 완비되어 프로토타입 구축 속도가 매우 빠름 | 과도한 추상화 레이어로 인해 세부 내부 로직 디버깅 및 트러블슈팅 난도가 높음 |
| 확장성 | LCEL 기반의 스트리밍, 비동기 호출, 병렬 처리 기본 내장 | 라이브러리 업데이트 및 패키지 구조 개편이 잦아 버전 의존성 관리가 까다로움 |
복잡한 내부 세부 구현 사항(네트워크 통신, 파싱, API 규격 등)을 감추고, 개발자가 공통의 단순한 인터페이스(메서드 규격)만 다루도록 만드는 객체지향 프로그래밍(OOP) 및 소프트웨어 설계 핵심 기법입니다.
자동차가 내연기관 엔진인지 전기 모터인지 상관없이 운전자는 엑셀과 브레이크 페달이라는 동일한 인터페이스로 조작하듯, LangChain은 모델이나 DB 종류가 달라도 동일한 함수 규격으로 통제할 수 있게 만듭니다.
LangChain의 주요 4대 추상화 계층
BaseChatModel):.invoke(), .stream(), .batch()라는 동일한 메서드로 호출하도록 감싸(Wrapper), 코드 수정 없이 모델 객체만 교체할 수 있게 합니다.PromptTemplate):{variable}) 하나로 통합 관리합니다.BaseOutputParser):VectorStore, Retriever):.as_retriever()를 통해 일관된 get_relevant_documents() 인터페이스로 조회합니다.코드 비교: 원시 API vs LangChain 추상화 (LCEL)
1. 원시 API 직접 구현 시 (추상화 없음)
# OpenAI API 기준 (모델을 바꾸려면 엔드포인트와 응답 처리 코드를 전부 뜯어고쳐야 함)
import openai
client = openai.OpenAI()
raw_res = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "RAG 설명해줘"}],
)
text = raw_res.choices[0].message.content
2. LangChain 추상화 적용 시 (LCEL)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# from langchain_anthropic import ChatAnthropic # 모델 교체 시 이 줄만 변경
prompt = ChatPromptTemplate.from_template("{topic}에 대해 설명해 줘.")
model = ChatOpenAI(model="gpt-4o") # 추상 인터페이스 구현체
parser = StrOutputParser()
# 파이프라인 결합
chain = prompt | model | parser
result = chain.invoke({"topic": "RAG"})
추상화의 실무적 장단점
| 구분 | 장점 | 단점 및 주의점 |
|---|---|---|
| 개발 생산성 | 코드 몇 줄로 LLM + 벡터 DB + 도구를 연결해 빠른 RAG/에이전트 프로토타입 구현 가능 | 세부 레이어가 여러 겹 감싸져 있어 API 에러나 성능 병목 발생 시 디버깅이 어려움 |
| 코드 이식성 | 모델(OpenAI ↔ 로컬 vLLM)이나 벡터 DB를 교체할 때 비즈니스 로직 수정 최소화 | LangChain 고유 문법(LCEL, 체인 클래스)을 별도로 익혀야 하며 프레임워크 락인 발생 |
거대 언어 모델(LLM)을 이용해 순환(Loop)과 조건부 분기(Branching)가 포함된 복잡한 에이전트 워크플로우를 구축하기 위해 LangChain 팀이 개발한 그래프 기반 오케스트레이션 라이브러리입니다.
기존 LangChain의 체인(LCEL)이 앞선 단계에서 다음 단계로만 흐르는 단선형(DAG, 비순환 방향 그래프) 방식이었다면, LangGraph는 상태를 유지하며 실패 시 이전 단계로 되돌아가거나 재시도하는 순환형 상태 머신(Cyclic State Machine)을 지원합니다.
┌────────────────────────┐
│ 사용자 입력 / 시작 │
└───────────┬────────────┘
▼
┌───────────────────┐
┌───>│ 에이전트 노드 │<───┐
│ │ (추론 및 도구 판단)│ │
│ └─────────┬─────────┘ │
│ │ │
│ [조건부 엣지 분기] │
│ / \ │
│ (도구 필요) (완료) │
│ / \ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
└─┤ 도구 실행│ │최종 출력│ │
└─────────┘ └─────────┘ │
└──────────-------─────┘
(자가 교정 / 재검색 루프)
핵심 아키텍처 요소
TypedDict나 Pydantic 모델)입니다.State)를 입력받아 작업을 수행(LLM 추론, 도구 호출, DB 쿼리 등)한 후, 갱신된 상태 딕셔너리를 반환합니다.LangChain (LCEL) vs LangGraph 비교
| 비교 항목 | LangChain (LCEL) | LangGraph |
|---|---|---|
| 흐름 구조 | 단선 직렬 / 비순환 (DAG) | 순환 허용 그래프 (Cyclic Graph) |
| 상태 관리 | 입력과 출력이 파이프를 통해 전달 | 공유 상태(State) 기반 중앙 집중식 관리 |
| 오류 복구 | 실패 시 중단되거나 단순 Fallback | 반성(Reflection) 노드를 통한 자가 수정 루프 |
| 적합한 작업 | 정형화된 RAG 파이프라인, 단순 질의응답 | 멀티 에이전트 협업, 코드 디버깅 반복, 복합 업무 자동화 |
간단한 구현 예시 (상태 기반 루프 구조)
from typing import Annotated, TypedDict
from langgraph.graph import END, StateGraph
from langgraph.graph.message import add_messages
# 1. 전역 상태 정의
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
# 2. 노드 함수 정의
def call_model(state: AgentState):
# LLM 호출 로직 (생략)
return {"messages": ["모델의 응답 또는 도구 요청"]}
def run_tool(state: AgentState):
# 외부 도구 실행 로직 (생략)
return {"messages": ["도구 실행 결과"]}
# 3. 라우팅 조건 함수
def should_continue(state: AgentState):
last_message = state["messages"][-1]
if "도구 요청" in str(last_message):
return "tools"
return END
# 4. 그래프 빌드 및 엣지 연결
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", run_tool)
workflow.set_entry_point("agent")
workflow.add_conditional_edges(
"agent", should_continue, {"tools": "tools", END: END}
)
workflow.add_edge("tools", "agent") # 도구 결과를 들고 다시 에이전트로 순환(Loop)
app = workflow.compile()
LangGraph는 단선 파이프라인의 가장 큰 한계였던 "오류 전파 및 자가 교정 불가" 문제를 해결하여, 상용 수준의 자율 에이전트와 정교한 RAG(Self-RAG, Corrective RAG)를 구현할 때 사실상의 표준 프레임워크로 활용됩니다.
그래프 아키텍처 기반으로 에이전트의 '행동'과 '흐름 제어'를 정의하는 두 핵심 구성 요소입니다.
노드가 무엇을 할 것인가(작업 단위)를 정의한다면, 엣지는 다음에 어디로 갈 것인가(이동 경로 및 분기)를 결정합니다.
1. 노드 (Nodes): 작업을 수행하는 독립된 실행 단위
노드는 그래프의 상태(State)를 입력받아 특정 작업을 처리한 뒤, 변경된 상태를 반환하는 Python 함수 또는 Runnable 객체입니다.
State)를 전달받습니다.ToolNode): 모델이 요청한 외부 함수(검색, 계산기, API 호출 등)를 실제로 실행합니다.2. 엣지 (Edges): 노드 간의 이동 경로 및 제어 흐름
엣지는 한 노드의 실행이 끝난 후 다음에 실행할 노드를 연결하는 통로입니다. LangGraph는 단순 직렬 이동뿐만 아니라 조건부 분기와 순환(Loop)을 엣지로 제어합니다.
A -> B).workflow.add_edge("node_a", "node_b") 형태로 등록합니다.workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", "end": END})tools 노드로, 작업이 완료되었으면 END 노드로 이동".START / set_entry_point)와 최종 작업을 마치는 종료 노드(END)를 지정합니다.노드와 엣지의 상호작용 구조
| 요소 | 역할 | 입력 및 출력 | 코드 상의 형태 |
|---|---|---|---|
| State (상태) | 노드 간 데이터를 주고받는 전역 버퍼 | 스키마 정의 (TypedDict, Pydantic) | class AgentState(TypedDict): ... |
| Node (노드) | 상태를 소비하고 갱신하는 실행 주체 | State 입력 ➔ dict 반환 | def run_agent(state): return {...} |
| Edge (엣지) | 제어권(실행 순서) 전달 및 조건부 분기 | 이전 노드 ➔ 라우팅 판단 ➔ 다음 노드 | workflow.add_edge() / add_conditional_edges() |
# 노드 등록
workflow.add_node("llm_reasoning", call_llm) # 노드 1
workflow.add_node("execute_tools", run_tools) # 노드 2
# 엣지 연결 (순환 구조 형성)
workflow.set_entry_point("llm_reasoning")
# 조건부 엣지: 결과에 따라 tools로 가거나 종료(END)
workflow.add_conditional_edges(
"llm_reasoning", decide_next_step, {"tools": "execute_tools", "done": END}
)
# 일반 엣지: 도구 실행 후 다시 LLM 판단 노드로 복귀 (Loop)
workflow.add_edge("execute_tools", "llm_reasoning")
LangChain 생태계에서 개발한 LLMOps(대형 언어 모델 운영) 전용 모니터링, 디버깅, 테스팅 및 평가 플랫폼입니다.
LangChain이나 LangGraph 같은 복잡한 프레임워크는 내부가 여러 추상화 계층으로 감싸져 있어 "블랙박스"처럼 동작하기 쉬운데, LangSmith는 각 단계의 입출력, 지연 시간, 토큰 비용 등을 투명하게 시각화하여 상용화 수준으로 끌어올리는 관측성(Observability) 도구입니다.
[사용자 요청] ──> [LangChain / LangGraph 실행] ──> [최종 응답]
│
▼ (자동 추적 전송)
┌───────────────────────┐
│ LangSmith │
│ • 각 단계 입출력 로깅 │
│ • 지연 시간 / 토큰 추적│
│ • 데이터셋 기반 평가 │
└───────────────────────┘
핵심 4대 기능
적용 방식 (코드 수정 최소화)
LangSmith는 별도의 복잡한 로깅 코드를 삽입할 필요 없이, 환경 변수 설정만으로 기존 LangChain/LangGraph 파이프라인과 자동 연동됩니다.
# 환경 변수 설정만으로 자동 추적 활성화
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="ls__your_api_key"
export LANGCHAIN_PROJECT="my-rag-service"
LangChain 생태계 3대 축 요약
| 도구 | 주 역할 | 비유 |
|---|---|---|
| LangChain | 선형 파이프라인 및 도구·DB 추상화 조립 | 기본 레고 블록 및 부품 결합기 |
| LangGraph | 상태 기반 순환 루프 및 복합 멀티 에이전트 제어 | 복잡한 궤도와 회로를 가진 기계 장치 |
| LangSmith | 전 과정 모니터링, 디버깅, 품질 평가, 비용 추적 | 종합 계측기 및 관제실 모니터 |
2.규칙
API 키는 코드에 직접 쓰지 않는다 -> .env 파일에 저장
.env 파일은 절대 깃허브에 올리지 않는다
답변 생성은 API로 한다 (claude-haiku-4-5, gpt-4o-mini 같은 저렴한 모델)
막히면 체크포인트 번호와 함께 오픈채팅으로 질문
도전 1
로컬 환경 구축
아나콘다 프롬프트에서 새 conda 환경 만들기
필요한 라이브러리 설치
VS Code에서 .py 파일로 작업 환경 잡기
.env 파일에 API 키 저장, python-dotenv로 불러오기
터미널에서 LLM API 한 번 호출해보기
체크포인트 1 : 내 PC 터미널에서 API 호출 성공
도전 2. 문서 하나 잡고 RAG 만들기
문서를 하나 고른다
예) 우리 학교 학사규정, 근로기준법, 주택임대차보호법, 관심 있는 회사 규정 등
PDF가 다루기 편합니다
텍스트 추출 -> 정제 -> 청킹
내 문서에 맞는 청킹 기준을 직접 정해보세요 (조항 단위? 글자 수? 문단?)
임베딩 후 Chroma에 저장
PersistentClient 사용 -> 한 번 만든 임베딩은 디스크에 저장해두고 재사용
질문 -> 검색 -> 답변 흐름 완성
답변 끝에 근거(조항 번호, 페이지 등)가 나오도록
문서에 없는 내용은 "문서에서 확인되지 않습니다"라고 답하도록
체크포인트 2 : 터미널에서 질문하면 근거가 붙은 답이 나온다
도전 3. AI로 화면 입히기
Streamlit으로 채팅 화면 만들기
AI에게 도전 2 코드를 주고 화면을 붙여달라고 요청해보세요
브라우저 localhost에서 질문하고 답변 받기
화면이 느리거나 이상하면 왜 그런지 먼저 의심해보기
힌트 : Streamlit은 버튼을 누를 때마다 무엇을 다시 실행할까?
체크포인트 3 : 브라우저에서 질문하고 답과 근거를 본다
추가 과제
대화 이력 유지 (ROOT 6강 멀티턴 활용)
청킹 전략 2개를 화면에서 바꿔가며 결과 비교
여러 문서를 넣고 문서별로 필터링해서 검색
비상구
PC가 너무 느려서 임베딩이 안 끝나면
-> 임베딩만 코랩 GPU에서 만들고, Chroma 저장 폴더를 zip으로 받아서 로컬에 풀어서 사용
제출 및 벨로그 업로드 - 오늘까지 아닙니다
오픈채팅에 아래 내용 텍스트로 공유
완성 화면 캡처 1장 (못 끝냈으면 도달한 체크포인트 번호)
고른 문서와 청킹 기준
제일 오래 막힌 곳 한 줄
추석 이후 개인 프로젝트로 키운다면 뭘 해보고 싶은지 한 줄