학습 방식
- 책을 읽는다.
- 이해되지 않거나 궁금한 내용을 GPT에 질문한다.
- 책 내용과 대화 내용을 기준으로 챕터를 정리한다.
- 정리된 내용을 다시 읽고, 직접 이해한 표현으로 수정한다.
- 최종 내용을 GitHub에 기록한다.
Chapter 01. RAG 이해하기
2. RAG란 무엇인가?
RAG(Retrieval-Augmented Generation) 는 사용자의 질문과 관련된 외부 정보를 검색(Retrieval)하고, 그 정보를 LLM의 Context에 추가한 뒤 답변을 생성(Generation)하는 방식이다.
사용자 질문
↓
관련 외부 정보 검색 (Retrieval)
↓
검색 결과를 Context로 구성
↓
LLM에 질문 + Context 전달
↓
답변 생성 (Generation)핵심은 다음 한 문장으로 정리할 수 있다.
LLM에게 모든 지식을 학습시키는 대신, 필요한 순간에 외부에서 관련 지식을 찾아 Context로 제공한다.
RAG는 단순히 문서를 LLM에게 전달하는 것과는 다르다. 핵심은 질문에 필요한 정보를 Retrieval을 통해 동적으로 선택하여 Context를 구성한다는 것이다.
3. RAG를 사용하는 이유
3.1 LLM이 학습하지 않은 외부 지식을 사용할 수 있다
LLM이 알지 못하는 사내 문서, 제품 매뉴얼, 계약서, 논문, 서비스 데이터 등을 검색하여 답변에 활용할 수 있다.
3.2 최신 정보를 사용할 수 있다
모델 전체를 다시 학습시키지 않아도 외부 데이터만 갱신하면 새로운 정보를 Retrieval할 수 있다.
3.3 답변의 근거를 추적하기 쉽다
직접 구축한 RAG 시스템에서는 다음과 같은 흐름을 관찰할 수 있다.
사용자 질문
↓
어떤 Query를 사용했는가?
↓
어떤 문서가 검색되었는가?
↓
검색 점수와 순위는 어떠한가?
↓
어떤 Chunk가 최종 Context에 포함되었는가?
↓
어떤 Prompt가 LLM에 전달되었는가?
↓
최종 답변따라서 RAG의 중요한 장점은 LLM의 내부 사고 과정이 투명해진다는 뜻이 아니라, LLM 앞단의 데이터 흐름에 대한 추적 가능성(traceability), 관찰 가능성(observability), 설계 통제력(controllability)이 높아진다는 것이다.
오답이 발생했을 때도 원인을 단계별로 나누어 볼 수 있다.
정답 문서가 검색되지 않음
→ Retrieval 문제
정답 문서는 검색됐지만 순위가 낮음
→ Ranking / ReRanking 문제
필요한 Context가 누락되거나 잘림
→ Context 구성 문제
정확한 Context가 제공됐는데 답변이 틀림
→ Generation 문제4. RAG와 Hallucination
RAG는 Hallucination을 제거하는 기술은 아니지만, 줄이는 데 효과적인 대표적인 방법이다.
특히 LLM이 필요한 사실을 모르기 때문에 그럴듯하게 추측하는 문제를 줄이는 데 도움이 된다.
LLM 단독
질문
↓
모델이 기존 지식에 의존
↓
정보가 부족하면 추측 가능RAG
질문
↓
관련 정보 검색
↓
근거가 되는 Context 제공
↓
Context 기반 답변이를 grounding 관점에서 이해할 수 있다. (grounding: AI의 출력 결과를 원본 근거 데이터에 단단히 연결하는 작업)
RAG는 LLM의 답변을 외부 근거에 grounding하여 Hallucination 가능성을 낮춘다.
하지만 다음과 같은 경우에는 여전히 틀릴 수 있다.
- Retriever가 잘못된 문서를 가져온 경우
- 정답 문서를 아예 검색하지 못한 경우
- 올바른 Context를 LLM이 잘못 해석한 경우
- 관련 문서가 없는데 LLM이 Context 밖의 내용을 생성한 경우
따라서:
답변이 틀렸다 = 무조건 LLM Hallucination
으로 판단해서는 안 된다. RAG에서는 Retrieval과 Generation을 분리해서 문제를 분석해야 한다.
5. RAG 전체 구조
RAG는 크게 사전 준비 단계와 질문 처리 단계로 나누어 이해하면 쉽다.
5.1 사전 준비 단계: Ingestion
RAG에서 Ingestion은 외부 문서를 가져와 검색 가능한 형태로 가공하고 저장하는 전체 준비 과정이다.
원본 문서
↓
Document Loading
↓
Text Splitting / Chunking
↓
각 Chunk Embedding
↓
Vector Store 저장이 과정은 일반적으로 사용자 질문이 들어오기 전에 수행한다.
이 과정 안에서 Vector Store가 빠르게 검색할 수 있도록 Vector Index를 구성하는 과정을 별도로 Indexing이라고 볼 수 있다.
Ingestion
├─ Document Loading
├─ Chunking
├─ Embedding
├─ Vector Store 저장
└─ 검색을 위한 Index 구성따라서 Chapter 01에서는 다음처럼 구분한다.
- Ingestion: 문서를 가져와 가공하고 저장하는 전체 사전 준비 과정
- Indexing: 저장된 데이터를 빠르게 검색할 수 있도록 검색 구조를 만드는 과정
5.2 질문 처리 단계: Retrieval + Generation
사용자 질문
↓
질문 Embedding
↓
Retriever
↓
Vector Store 검색
↓
관련 Chunk 반환
↓
Prompt 구성
├─ 사용자 질문
├─ 검색된 Context
└─ 답변 지침
↓
LLM
↓
답변전체를 한 줄로 줄이면 다음과 같다.
문서 준비
→ Chunking
→ Embedding
→ Vector Store
→ [사용자 질문]
→ Retriever
→ 관련 Context
→ Prompt
→ LLM
→ Answer6. Document Loading
Document Loading은 PDF, 텍스트 파일, 웹페이지, 데이터베이스 등 다양한 데이터 소스에서 정보를 읽어 RAG가 다룰 수 있는 문서 형태로 가져오는 단계이다.
LangChain을 사용하는 경우 여러 데이터 소스를 공통 Document 형태로 다룰 수 있다.
개념적으로는 다음과 같다.
Document
├─ page_content
└─ metadata이 단계의 핵심은 파일 형식 자체가 아니라, 뒤의 Chunking과 Retrieval에서 사용할 텍스트와 출처 정보를 얻는 것이다.
7. Chunking
Chunk란?
Chunk는 원본 문서를 Retrieval에서 검색할 단위로 나눈 텍스트 조각이다.
RAG에서는 일반적으로 문서 전체를 하나의 Embedding으로 만들기보다, 문서를 여러 Chunk로 나누고 각 Chunk를 개별적으로 Embedding한다.
예를 들어 다음과 같은 사내 규정 문서가 있다고 하자.
[연차 정책]
정규직 직원에게는 연간 18일의 연차가 제공됩니다.
입사 첫해에는 근무 개월 수에 따라 연차가 비례 지급됩니다.
미사용 연차는 최대 5일까지 다음 해로 이월할 수 있습니다.
[리프레시 휴가]
3년 이상 근속한 직원에게는 5일의 리프레시 휴가가 제공됩니다.
리프레시 휴가는 연차와 별도로 사용할 수 있습니다.문서의 구조와 의미를 고려해 다음과 같이 나눌 수 있다.
Chunk 1
[연차 정책]
정규직 직원에게는 연간 18일의 연차가 제공됩니다.
입사 첫해에는 근무 개월 수에 따라 연차가 비례 지급됩니다.
미사용 연차는 최대 5일까지 다음 해로 이월할 수 있습니다.Chunk 2
[리프레시 휴가]
3년 이상 근속한 직원에게는 5일의 리프레시 휴가가 제공됩니다.
리프레시 휴가는 연차와 별도로 사용할 수 있습니다.이렇게 나누면 사용자가 다음과 같이 질문했을 때:
"남은 연차를 내년으로 넘길 수 있어?"Retriever는 전체 문서가 아니라 연차 정책에 해당하는 Chunk를 검색 결과로 반환할 수 있다.
Chunk 크기는 하나로 정해져 있지 않으며 문서의 종류와 검색 목적에 따라 달라진다.
Chunk가 지나치게 크면 서로 다른 주제가 하나의 Embedding에 함께 포함되어 검색 결과가 덜 정밀해질 수 있다.
반대로 Chunk가 지나치게 작으면 답변에 필요한 문맥이 여러 Chunk로 분리되어 의미가 충분히 보존되지 않을 수 있다.
따라서 Chunking의 핵심은:
검색 대상의 의미가 충분히 유지되면서도, 질문과 관련된 부분을 선택적으로 찾을 수 있는 단위로 문서를 나누는 것
이다.
실제 Chunking에서는 문단, 문장, 제목/섹션 구조 또는 token 수 등을 기준으로 분할할 수 있다.
8. Token과 Chunk의 차이
Token과 Chunk는 전혀 다른 목적의 단위다.
Token
Token은 모델의 tokenizer가 텍스트를 처리하기 위해 나누는 단위다.
- 1 token이 항상 같은 글자 수를 의미하지 않는다.
- 모델/tokenizer에 따라 같은 문장의 token 수가 달라질 수 있다.
- Context Window의
128K tokens와 같은 숫자는 token 개수 한도를 의미한다. - 각 token이 실제 몇 글자를 나타내더라도 context window에서는 각각 1개의 token으로 계산된다.
Chunk
Chunk는 큰 문서를 검색이나 처리가 용이한 작은 텍스트 단위로 나눈 것이다.
RAG에서는 이 Chunk를 Retrieval의 기본 검색 단위로 사용하는 경우가 많다.
Chunk 크기는 목적에 따라 조정할 수 있다.
100 tokens
500 tokens
1000 tokens
몇 개의 문장
몇 개의 문단
하나의 Section관계는 다음과 같다.
Document
├─ Chunk 1
│ └─ Token Token Token ...
├─ Chunk 2
│ └─ Token Token Token ...
└─ Chunk 3
└─ Token Token Token ...정리하면:
Token은 모델이 텍스트를 읽는 단위이고, Chunk는 RAG가 문서를 검색하는 단위다.
9. Tokenizer와 Context Window
9.1 Tokenizer
Tokenizer는 문자열을 모델이 처리할 token sequence로 변환한다.
한 token이 나타내는 문자열 길이는 일정하지 않다.
"hello" → 하나의 token일 수도 있음
"ing" → 하나의 token일 수도 있음
특정 한글 조각 → 하나의 token일 수도 있음9.2 Context Window
Context Window는 모델이 한 번의 요청에서 처리할 수 있는 token의 최대 개수다.
Context Window
= Prompt
+ System instruction
+ 검색된 Context
+ 사용자 질문
+ 기타 입력
(+ 모델에 따라 출력 예산과 관계)Chunk Size를 결정할 때는 Embedding Model의 입력 제한과 최종 LLM의 Context Window를 함께 고려해야 한다.
9.3 Token을 무조건 길게 만들 수 없는 이유
토큰 하나가 긴 문자열을 표현하면 같은 context window 안에 더 많은 원문을 넣을 수 있다.
하지만 지나치게 긴 token을 많이 만들면:
- Vocabulary가 커질 수 있다.
- 희귀 token이 늘어날 수 있다.
- 새로운 단어나 변형에 대응하기 어려워질 수 있다.
- 자주 등장하지 않는 긴 token은 충분히 학습되지 않을 수 있다.
반대로 너무 작은 token만 사용하면:
- Vocabulary는 줄일 수 있지만
- 같은 문장을 표현하는 데 더 많은 token이 필요하다.
그래서 현대 LLM은 일반적으로 subword 단위 tokenization을 활용해 두 극단 사이에서 절충한다.
10. Token Embedding과 Contextual Representation
LLM은 입력된 token을 바로 문맥적으로 이해하는 것이 아니라, 먼저 각 token ID를 기본 벡터로 변환한다.
Token ID
↓
Token Embedding
↓
기본 Vector Representation이 Token Embedding은 해당 token이 모델에 처음 들어갈 때 사용하는 기본 벡터다.
예를 들어 bank라는 token이 있다고 하자.
bank
↓
Token Embedding
↓
[0.12, -0.44, 0.81, ...]같은 token ID라면 기본 Token Embedding은 동일하다.
하지만 실제 단어의 의미는 주변 문맥에 따라 달라질 수 있다.
예를 들어 다음 두 문장을 보자.
I deposited money in the bank.
I sat on the river bank.두 문장에 등장하는 bank는 같은 token이므로 처음에는 같은 Token Embedding에서 시작할 수 있다.
하지만 Transformer가 주변 token들과의 관계를 처리하면서 bank의 내부 표현이 달라진다.
deposited + money + bank
↓
Transformer
↓
"은행"이라는 문맥이 반영된 Vectorriver + bank
↓
Transformer
↓
"강둑"이라는 문맥이 반영된 Vector이처럼 주변 문맥까지 반영된 token의 내부 벡터 표현을 Contextual Representation이라고 한다.
정리하면:
- Token Embedding
- token ID에 대응하는 기본 벡터
- Transformer에 들어가기 전의 출발점
- 같은 token ID라면 기본 Token Embedding은 동일하다.
- Contextual Representation
- Transformer가 주변 token들과의 관계를 처리한 뒤 만들어지는 벡터
- 같은 token이라도 문맥에 따라 다른 벡터가 될 수 있다.
- 미리 여러 벡터를 저장해두는 것이 아니라, 입력 문맥에 따라 동적으로 계산된다.
전체 흐름은 다음과 같다.
Token
↓
Token ID
↓
Token Embedding
↓
Transformer
↓
Contextual Representation또한 Transformer는 여러 layer로 이루어져 있기 때문에 같은 token의 내부 벡터도 layer를 통과하면서 계속 변화할 수 있다.
Token Embedding
↓
Transformer Layer 1
↓
Transformer Layer 2
↓
...
↓
최종 Contextual RepresentationRAG에서 문서 Chunk나 사용자 Query를 Embedding할 때도 이러한 과정은 Embedding Model 내부에서 수행된다.
개념적으로는 다음과 같다.
Chunk 또는 Query
↓
Tokenization
↓
Token Embedding
↓
Transformer
↓
Contextual Representation
↓
전체 텍스트를 대표하는 Embedding Vector따라서 Vector Store에 저장하거나 검색에 사용하는 것은 개별 Token Embedding이 아니라, 문맥이 반영된 결과를 바탕으로 만들어진 Chunk 또는 Query 전체의 최종 Embedding Vector다.
Token Embedding은 token의 기본 벡터이고, Contextual Representation은 그 token이 현재 문맥에서 어떤 의미로 사용되었는지가 반영된 벡터다.
11. Chunk Overlap
Chunk Overlap은 인접한 Chunk 사이에 일부 내용을 중복해서 포함시키는 방법이다.
예를 들어:
chunk_size = 100
chunk_overlap = 20이라면 개념적으로:
Chunk 1 : 1 ---------------- 100
Chunk 2 : 81 ---------------- 180
↑
20만큼 중복왜 필요한가?
문장이 Chunk 경계에서 잘릴 수 있기 때문이다.
원문:
"신입사원에게는 입사 첫해 근무 개월 수에 따라
연차가 비례 지급됩니다."잘못 잘리면:
Chunk 1:
"신입사원에게는 입사 첫해 근무 개월 수에 따라"
Chunk 2:
"연차가 비례 지급됩니다."각 Chunk의 의미가 불완전해질 수 있다.
Overlap을 사용하면 앞뒤 문맥을 어느 정도 보존할 수 있다.
다만 Overlap이 너무 크면:
- 같은 내용이 여러 번 Embedding됨
- 저장량 증가
- Embedding 비용 증가
- 검색 결과에 비슷한 Chunk가 반복될 수 있음
따라서 Overlap도 클수록 좋은 것이 아니라 조정해야 하는 설계값이다.
12. Embedding
12.1 Embedding이란?
Embedding은 텍스트를 의미를 표현하는 숫자 벡터로 변환하는 과정이다.
"연차는 18일입니다."
↓
Embedding Model
↓
[0.012, -0.441, 0.823, ...]중요한 점은 단순한 문자열 변환 공식이 아니라, 학습된 Embedding Model이 추론을 수행하여 벡터를 만든다는 것이다.
Embedding Model은 API로 호출할 수도 있고, 로컬에서 직접 실행할 수도 있다.
API 방식
텍스트 → 외부 Embedding Model API → Vector
로컬 방식
텍스트 → 로컬 Embedding Model → Vector즉:
Embedding이 어려워서 반드시 API를 써야 하는 것이 아니라, 이미 학습된 모델의 운영을 외부 서비스에 맡길지 직접 실행할지의 차이다.
13. Embedding Model과 LLM은 다르다
Embedding Model과 GPT 같은 생성 모델은 역할이 다르다.
Embedding Model
텍스트
↓
Embedding Model
↓
Vector주요 목적:
- Semantic Search
- 유사도 계산
- RAG Retrieval
LLM
Prompt + Context
↓
LLM
↓
자연어 답변주요 목적:
- 언어 이해
- 추론
- 생성
RAG에서는 두 종류의 모델을 함께 사용할 수 있다.
[검색]
문서 / 사용자 질문
↓
Embedding Model
↓
Vector Search
↓
관련 Chunk
↓
[생성]
질문 + 관련 Chunk
↓
LLM
↓
답변문서 Chunk와 Query는 일반적으로 같은 Embedding Model / 같은 embedding space를 사용해야 비교가 가능하다.
Embedding Space는 임베딩 모델이 텍스트의 의미를 배치하는 벡터 좌표계이고, 문서와 질문이 같은 좌표계에 있어야 거리나 유사도를 비교할 수 있다.
14. Embedding Dimension
Embedding Model은 텍스트 하나를 정해진 차원의 벡터로 표현한다.
예를 들어 OpenAI의 Embedding 모델은 모델에 따라 기본 출력 차원이 다르다.
text-embedding-3-small: 기본 1536 dimensionstext-embedding-3-large: 기본 3072 dimensions
즉:
"연차는 18일입니다."
↓
text-embedding-3-small
↓
[숫자, 숫자, 숫자, ...]
총 1536개1536차원이라는 표현은 GPT 같은 생성 모델의 크기를 뜻하는 것이 아니라, Embedding Model이 반환하는 vector에 몇 개의 숫자가 있는가를 뜻한다.
15. Vector Store
Vector Store는 Embedding Vector를 저장하고 유사한 Vector를 빠르게 검색하기 위한 저장/검색 계층이다.
예시:
- Chroma
- Pinecone
- Qdrant
- Weaviate
- PostgreSQL + pgvector
Vector Store에는 시스템 설계에 따라 다음과 같은 정보가 함께 저장될 수 있다.
Vector
Chunk Text
Metadata예:
embedding:
[0.12, -0.44, ...]
text:
"정규직 직원에게는 연간 18일의 연차가 제공됩니다."
metadata:
source = "company_rule.pdf"
page = 3따라서 Vector Store에서 유사한 vector를 찾은 뒤, 그 vector에 대응되는 Chunk를 바로 반환할 수 있다.
시스템에 따라 Chunk Text 대신 chunk_id만 저장하고 별도의 PostgreSQL, Object Storage, 파일 시스템 등에서 실제 데이터를 조회하는 구조도 가능하다.
16. Vector Store와 RDBMS Index의 비교
Vector DB / Vector Store를 RDBMS와 비교하면 이해하기 쉽다.
RDBMS
Table
├─ Row
├─ Column
├─ B-Tree / Hash Index
└─ SQL QueryVector Store
Vector / Document
├─ Embedding
├─ Metadata
├─ Vector Index
└─ Similarity Search일반적인 RDBMS 검색은:
정확한 값
범위
정렬등을 중심으로 한다.
Vector Search는:
Query Vector와
가장 가까운 Vector를 검색하는 것이 핵심이다.
Vector Index는 Vector Store 자체와 같은 개념이 아니다.
Vector Store는 저장/검색 시스템이고, Vector Index는 그 내부에서 유사한 벡터를 빠르게 찾기 위한 자료구조다.
pgvector는 PostgreSQL에 vector 타입과 vector similarity search 기능을 추가하는 확장으로 이해할 수 있다.
17. Semantic Search의 핵심
Vector Search에서는 특정 단어가 별도의 Index Entry로 반드시 존재해야 하는 것이 아니다.
예를 들어 다음 Chunk가 있다고 하자.
"정규직 직원에게는 연간 18일의 연차가 제공됩니다."이 Chunk 전체를 Embedding하여 Vector Store에 저장한다.
사용자가:
"연차"라고 검색하면 Query도 같은 Embedding Model로 vector화한다.
"연차"
↓
Query Vector
"정규직 직원에게는 연간 18일의 연차가 제공됩니다."
↓
Chunk Vector두 vector가 같은 embedding space에서 가까우면 관련성이 높다고 판단할 수 있다.
따라서 Vector Search는 기본적으로:
정확히 같은 단어가 포함됐는가?
보다
두 텍스트의 의미가 얼마나 가까운가?
를 검색하는 방식이다.
그래서 Query에 연차라는 단어가 없더라도:
"회사에서 유급 휴가가 며칠 나와?"와 같은 의미적으로 가까운 질문으로 관련 Chunk를 찾을 수 있다.
정확한 keyword matching이 중요한 경우에는 BM25 등 어휘 기반 검색을 함께 사용한 Hybrid Search도 고려할 수 있다.
18. Retriever
Retriever는 사용자의 Query를 받아 관련 Document/Chunk를 반환하는 검색 인터페이스 또는 검색 로직이다.
사용자 질문
↓
Retriever
↓
Vector Store 등 검색
↓
관련 Chunk 반환중요한 구분:
Vector Store ≠ Retriever
Embedding Model ≠ RetrieverVector Store는 데이터를 저장하고 검색할 수 있는 시스템이고, Retriever는 그것을 이용해 어떤 방식으로 검색하고 무엇을 반환할지 결정하는 계층에 가깝다.
19. Cosine Similarity와 Top-K
Embedding 기반 검색에서는 Query Vector와 Chunk Vector의 유사성을 측정해야 한다.
대표적인 지표 중 하나가 Cosine Similarity다.
개념적으로:
Query Vector ↔ Chunk Vector
방향이 비슷함
→ 유사도가 높음
→ 의미적으로 관련 있을 가능성이 높음Retriever에서는 Top-K를 설정할 수 있다.
k = 3
→ 가장 관련성이 높은 Chunk 3개 반환
k = 5
→ 가장 관련성이 높은 Chunk 5개 반환Top-K는 얼마나 많은 검색 결과를 최종 Context 후보로 가져올지 결정하는 값이다.
20. MMR
MMR(Maximal Marginal Relevance) 은 단순한 유사도 측정 공식이라기보다, 관련성과 검색 결과 간 다양성을 함께 고려하여 최종 결과를 선택하는 전략이다.
단순 Similarity Top-3:
1. 연차는 18일입니다.
2. 직원의 연차는 총 18일입니다.
3. 정규직 연차는 매년 18일입니다.세 Chunk가 사실상 같은 정보를 말할 수 있다.
MMR을 사용하면:
1. 연차는 18일입니다.
2. 미사용 연차는 5일까지 이월됩니다.
3. 입사 첫해에는 근무 개월에 비례 지급됩니다.처럼 Query와 관련성을 유지하면서 중복을 줄이고 다양한 정보를 선택할 수 있다.
정리:
- Cosine Similarity: Query와 Chunk가 얼마나 유사한가?
- Top-K: 검색 결과를 몇 개 가져올 것인가?
- MMR: 관련성을 유지하면서 서로 너무 비슷한 결과만 나오지 않게 할 것인가?
21. ReRanker
ReRanker는 Retriever가 가져온 후보 문서를 다른 방식으로 더 정밀하게 평가하여 순위를 다시 정하는 구성 요소다.
질문
↓
Retriever
"빠르게 관련 후보 20개 검색"
↓
ReRanker
"20개를 더 정밀하게 평가"
↓
상위 3~5개
↓
LLMRetriever와 ReRanker가 완전히 동일한 지표로 같은 계산을 반복한다면 ReRanking의 의미가 작다.
일반적으로:
- Retriever: 빠르게 넓은 후보군을 찾음
- ReRanker: 후보군을 더 비싸고 정밀한 방식으로 재평가
라는 역할 분담을 한다.
개념적으로는 다음처럼 이해할 수 있다.
Retriever는 Recall을 확보하고, ReRanker는 후보군의 Precision을 높이는 데 도움을 준다.
단, Retriever가 정답 문서를 후보군에 아예 포함시키지 못했다면 ReRanker가 없는 정답을 새로 만들어낼 수는 없다.
Chapter 01에서는 세부 ReRanking 모델까지 학습하지 않고 역할만 기억한다.
22. Prompt와 Context
RAG에서 Prompt는 단순히 Prompt Engineering을 의미하는 것이 아니다.
개념적으로:
Prompt
=
사용자 질문
+ 검색된 Context
+ LLM에게 주는 지시사항예:
다음 Context를 근거로 질문에 답하세요.
[Context]
정규직 직원에게는 연간 18일의 연차가 제공됩니다.
[Question]
올해 연차가 몇 일이야?즉 RAG의 Retrieval 단계가 최종적으로 하는 중요한 일 중 하나는 LLM에게 제공할 좋은 Context를 준비하는 것이다.
23. Context 최적화와 LLM 자체 최적화
두 접근은 구분해서 이해해야 한다.
Context 최적화
모델 자체의 parameter는 그대로 두고 모델에게 무엇을 어떻게 보여줄지 개선한다.
예:
- Prompt Engineering
- RAG
- Chunking
- Retrieval
- ReRanking
- Context 선택/구성
LLM 자체 최적화
학습을 통해 모델 parameter 자체를 변경한다.
예:
- Fine-tuning
- Full Fine-tuning
- LoRA / PEFT 등
따라서:
RAG는 LLM을 다시 학습시키는 방식이 아니라, LLM에게 제공되는 Context를 개선하는 접근이다.
최신 지식이나 사내 문서를 매번 제공하는 목적이라면 RAG와 잘 맞고, 특정 응답 형식이나 작업 행동 자체를 모델에 학습시키려는 목적은 Fine-tuning과 더 관련이 있다.
24. LangChain은 무엇을 해주는가?
RAG 자체와 LangChain은 다른 개념이다.
RAG
= Retrieval + Augmentation + Generation이라는 시스템 구조
LangChain
= RAG를 포함한 LLM 애플리케이션의 여러 구성 요소를
공통 인터페이스로 연결하고 교체하기 쉽게 만드는 프레임워크LangChain 없이도 RAG를 직접 구현할 수 있다.
LangChain 없이 직접 구현
PDF 읽기 코드
(PyMuPDF 등)
↓
직접 작성한 텍스트 분할 코드
↓
OpenAI Embedding API 호출 코드
↓
Chroma 저장 코드
↓
Chroma 검색 코드
↓
직접 Prompt 문자열 생성
↓
OpenAI LLM API 호출이 방식에서도 RAG는 정상적으로 구현할 수 있다.
다만 각 단계가 사용하는 라이브러리나 서비스의 API에 직접 의존하게 된다.
예를 들어 Vector Store를 바꾸려고 한다면:
기존
Chroma 저장 코드
↓
Chroma 검색 코드에서
변경
Pinecone 저장 코드
↓
Pinecone 검색 코드로 바꾸면서 Pinecone의 API와 데이터 형식에 맞게 주변 코드도 수정해야 할 수 있다.
LangChain을 사용하는 경우
LangChain은 각 역할을 공통된 추상화와 인터페이스로 다룰 수 있게 한다.
Document Loader
│
├─ PDF Loader (교체)
├─ Web Loader
└─ 기타 Loader
↓
Text Splitter
│
├─ Recursive Splitter (교체)
└─ 기타 Splitter
↓
Embedding Model
│
├─ OpenAI Embeddings
├─ HuggingFace Embeddings (교체)
└─ 기타 Embeddings
↓
Vector Store
│
├─ Chroma
├─ Pinecone
├─ pgvector (교체)
└─ 기타 Vector Store
↓
Retriever
↓
Prompt
↓
LLM
│
├─ OpenAI
├─ Anthropic (교체)
└─ 기타 Model각 단계의 역할과 입출력 형태를 일정한 인터페이스로 맞추는 것이 핵심이다.
따라서 애플리케이션 전체의 구조는 유지하면서 특정 구성 요소를 교체하기 쉬워진다.
따라서 LangChain의 핵심 가치는 다음과 같이 이해할 수 있다.
LangChain 없이 구현하면 각 외부 라이브러리와 API를 직접 연결해야 하고, LangChain을 사용하면 각 역할을 공통 인터페이스 뒤에 두어 연결과 교체를 쉽게 할 수 있다.
25. Chain과 LCEL / Runnable
LangChain에서는 여러 처리 단계를 하나의 실행 흐름으로 연결할 수 있다.
예를 들어:
Prompt
↓
LLM
↓
Output Parser
이 전체 흐름을 하나의 Chain으로 볼 수 있다.
Runnable
Runnable은 LangChain에서 입력을 받아 어떤 처리를 수행하고 출력을 반환할 수 있는 실행 단위다.
예를 들어 다음과 같은 구성 요소들이 Runnable처럼 동작할 수 있다.
Prompt
→ 입력값을 받아 Prompt를 생성
LLM
→ Prompt를 받아 응답 생성
Output Parser
→ LLM 출력을 원하는 형태로 변환
즉 각 단계가 공통된 실행 인터페이스를 가지기 때문에 서로 연결할 수 있다.
Runnable
↓
Runnable
↓
Runnable
이 Runnable들을 순서대로 연결하면 하나의 실행 파이프라인을 만들 수 있다.
LCEL
LangChain에서는 Runnable들을 | 연산자로 연결해 실행 흐름을 구성할 수 있다.
개념적인 예:
chain = prompt | llm | output_parser
이 흐름은 다음과 같다.
Prompt
↓
LLM
↓
Output Parser
앞 단계의 출력이 다음 단계의 입력으로 전달된다.
이러한 Runnable 조합 방식을 책에서는 LCEL(LangChain Expression Language) 로 설명한다.
RunnableSequence
Runnable 여러 개를 순서대로 연결하면 내부적으로 하나의 RunnableSequence 형태의 실행 흐름이 만들어질 수 있다.
Runnable
↓
Runnable
↓
Runnable
= RunnableSequence
즉 다음처럼 이해할 수 있다.
- Runnable: 실행 가능한 하나의 작업 단위
- RunnableSequence: Runnable 여러 개를 순서대로 연결한 것
- Chain: 이렇게 연결된 전체 실행 파이프라인
- LCEL: Runnable들을 조합해 Chain을 표현하는 방식
공통 실행 인터페이스
Runnable 기반으로 만든 Chain은 공통된 방식으로 실행할 수 있다.
chain.invoke(input)
chain.ainvoke(input)
chain.batch(inputs)
chain.stream(input)
대표적인 의미는 다음과 같다.
invoke: 한 번 실행ainvoke: 비동기 실행batch: 여러 입력을 묶어서 실행stream: 결과를 스트리밍 방식으로 처리
정리하면:
Runnable은 LangChain의 실행 가능한 작업 단위이고, LCEL은 이러한 Runnable들을 연결해 Chain을 만드는 표현 방식이다.
복잡한 상태 관리나 Agent workflow에서는 단순한 Chain보다 LangGraph 같은 별도의 orchestration 방식이 사용될 수 있다.
26. RAG 정확도를 높이는 관점
Chapter 01에서는 세부 최적화 방법까지 들어가지 않지만, RAG 성능은 여러 단계에 의해 결정된다는 점은 기억한다.
좋은 원본 데이터
↓
적절한 Chunking
↓
좋은 Embedding
↓
좋은 Retrieval
↓
필요하면 MMR / ReRanking
↓
좋은 Context 구성
↓
적절한 Prompt
↓
LLM Generation즉 RAG에서 답변 품질이 좋지 않을 때 LLM만 의심해서는 안 된다.
검색 결과가 좋지 않으면 Retrieval 앞단을, 검색 결과는 좋은데 답변이 나쁘면 Prompt / Generation 단계를 확인한다.
27. 이번 챕터에서 헷갈렸던 부분
Q1. RAG는 Hallucination을 없애는가?
아니다.
- Hallucination 가능성을 낮추는 대표적인 grounding 방식이다.
- 하지만 Retrieval 실패나 LLM의 잘못된 해석 때문에 여전히 오답이 발생할 수 있다.
Q2. Vector Store에는 Vector만 들어가는가?
반드시 그렇지는 않다.
일반적으로 다음을 함께 저장할 수 있다.
Vector
Chunk Text
Metadata또는 Vector와 ID만 저장하고 실제 Chunk는 별도 저장소에서 가져올 수도 있다.
Q3. "연차"라는 단어가 Vector DB에 따로 없는데 어떻게 검색하는가?
Chunk 전체와 Query 각각을 같은 Embedding Model로 vector화한 뒤 의미적 거리를 비교한다.
Query "연차"
↓ Embedding
Query Vector
Chunk "정규직 직원에게는 연간 18일의 연차가..."
↓ Embedding
Chunk Vector
두 Vector의 유사도 비교정확한 문자열 일치가 아니라 의미적 유사성을 이용한다.
Q4. Chunk는 Token처럼 아주 작게 나누는가?
아니다.
- Token: 모델 처리 단위
- Chunk: RAG 검색 단위
Chunk 하나 안에는 여러 token이 존재한다.
Q5. Chunk의 사이즈는?
보통은 문단, 절, 몇 개 문장 등 하나의 의미가 어느 정도 유지되면서 검색하기 좋은 크기로 나눈다.
Q6. Chunk Overlap은 Chunk를 그냥 더 크게 만드는 것인가?
정확히는 인접 Chunk 사이에 일부 내용을 중복시키는 것이다.
목적은 Chunk 경계에서 문맥이 끊어지는 것을 줄이는 것이다.
Q7. Embedding은 직접 계산하기 너무 어려워 API를 사용하는가?
Embedding은 학습된 Neural Network Model의 추론 결과다.
- OpenAI 같은 API를 사용할 수도 있고
- 오픈소스 Embedding Model을 직접 실행할 수도 있다.
API 사용은 모델 운영을 외부에 맡기는 선택이지, Embedding이 API로만 가능한 것은 아니다.
Q8. Embedding Model과 GPT 같은 LLM은 같은 모델인가?
아니다.
- Embedding Model: 텍스트 → Vector
- LLM: Prompt / Context → 자연어 출력
RAG에서 서로 다른 역할을 맡는다.
Q9. Chroma, Pinecone, pgvector는 무엇인가?
모두 Vector를 저장하고 검색하는 계층과 관련된 기술이다.
- Chroma: RAG/AI 개발에서 사용하기 쉬운 Vector Store
- Pinecone: 관리형 Vector Database 서비스
- pgvector: PostgreSQL에 Vector 저장/검색 기능을 추가하는 Extension
Q10. Vector DB는 RDBMS의 Index Table 같은 것인가?
비슷한 직관은 도움이 되지만 완전히 같지는 않다.
더 정확한 대응은:
Vector Store ↔ Database / Storage System
Vector Index ↔ B-Tree / Hash Index 같은 검색 자료구조이다.
Q11. Cosine Similarity와 MMR은 같은 목적의 알고리즘인가?
아니다.
- Cosine Similarity: Vector 간 유사성을 측정
- MMR: 유사성뿐 아니라 결과 간 중복/다양성을 함께 고려해 최종 문서를 선택
Q12. Retriever가 이미 Ranking하는데 ReRanker를 왜 사용하는가?
Retriever와 다른 방식으로 후보를 더 정밀하게 평가하기 위해서다.
Retriever
→ 빠르게 후보군 확보
ReRanker
→ 후보군을 더 정밀하게 재평가완전히 같은 지표로 같은 계산을 반복한다면 ReRanking의 의미가 작다.
28. Chapter 01 핵심 구조
이번 챕터에서 가장 중요하게 기억할 구조는 다음이다.
==============================
사전 준비
==============================
원본 문서
↓
Document Loading
↓
Chunking
↓
Embedding Model
↓
Vector Store
==============================
질문 처리
==============================
사용자 질문
↓
Embedding Model
↓
Retriever
↓
Vector Search
↓
Top-K / MMR
↓
(필요하면 ReRanker)
↓
관련 Chunk
↓
Context 구성
↓
Prompt
↓
LLM
↓
AnswerLangChain을 사용하면 이 구성 요소들을 연결하여 실행 흐름을 만들 수 있다.
LangChain Chain
Retriever
↓
Prompt
↓
LLM'AI' 카테고리의 다른 글
| 아주 단순한 수식에서 시작해 머신러닝 이해하기 (0) | 2026.09.14 |
|---|---|
| 프롬프트가 GPT의 답변이 되기까지 (0) | 2026.09.02 |
