RAG — 검색 증강 생성(Retrieval-Augmented Generation)

LLM은 학습 컷오프(training cutoff) 이전에 본 정보는 두루 알지만, 회사 내부 문서나 코드베이스(codebase), 지난주 회의록은 알지 못합니다. RAG는 관련 문서를 검색해 프롬프트(prompt) 안에 끼워 넣는 방식으로 이 한계를 해결합니다. 프로덕션 AI에서 가장 많이 배포되는 패턴이며, 이 과정에서 하나만 직접 만들어야 한다면 RAG 파이프라인(pipeline)을 추천합니다.

유형: Build
언어: Python
선수 지식: Phase 10 (LLMs from Scratch), Phase 11 Lessons 01-05
예상 시간: 약 90분
관련: Phase 5 · 23 (Chunking Strategies for RAG)에서는 6가지 청킹(chunking) 알고리즘과 각 방식이 유리한 상황을 다룹니다. Phase 5 · 22 (Embedding Models Deep Dive)는 임베더(embedder) 선택을 다룹니다. Phase 11 · 07 (Advanced RAG)는 하이브리드 검색(hybrid search), 재정렬(reranking), 질의 변환(query transformation)을 다룹니다.

학습 목표

  • 문서 로딩(document loading), 청킹(chunking), 임베딩(embedding), 벡터 저장소(vector storage), 검색(retrieval), 생성(generation)을 모두 갖춘 완전한 RAG 파이프라인을 직접 만들어 봅니다.
  • 적절한 색인(indexing)을 갖춘 벡터 데이터베이스(vector database; ChromaDB, FAISS, Pinecone)를 사용해 의미 기반 검색(semantic search)을 구현합니다.
  • 지식 기반 애플리케이션(application)에서 미세 조정(fine-tuning)보다 RAG가 선호되는 이유를 비용(cost), 최신성(freshness), 출처 표시(attribution) 관점에서 설명합니다.
  • 검색 지표(retrieval metric; precision, recall)와 생성 지표(generation metric; faithfulness, relevance)를 사용해 RAG 품질을 평가합니다.

문제

회사용 챗봇(chatbot)을 만들었다고 가정해 봅니다. 한 고객이 "엔터프라이즈 플랜의 환불 정책은 어떻게 되나요?"라고 묻습니다. LLM은 일반적인 SaaS 환불 정책에 대한 두루뭉술한 답을 내놓습니다. 그러나 실제 정책은 200쪽짜리 사내 위키(internal wiki) 어딘가에 묻혀 있고, 거기에는 엔터프라이즈 고객에게는 60일 환불 기간과 일할 계산 환불(pro-rated refund)이 제공된다고 적혀 있습니다. LLM은 이 문서를 한 번도 본 적이 없습니다. 학습되지 않은 것은 알 수 없는 법입니다.

미세 조정은 한 가지 해법입니다. LLM을 가져와 사내 문서로 추가 학습시키고, 갱신된 모델을 배포하는 방식입니다. 이 방법은 작동하기는 하지만 심각한 문제를 안고 있습니다. 미세 조정에는 수천 달러 단위의 컴퓨팅 비용이 들고, 문서가 바뀌는 순간 모델은 곧바로 낡은 상태(stale)가 됩니다. 모델이 어떤 출처(source)에서 답을 끌어왔는지 추적할 방법도 없습니다. 회사가 다음 달 다른 제품 라인을 인수하면 또다시 미세 조정을 해야 합니다.

RAG는 또 다른 해법입니다. 모델 자체는 그대로 둡니다. 질문이 들어오면 문서 저장소(document store)에서 관련 구절(passage)을 검색하고, 그 구절을 질문 앞쪽 프롬프트에 붙여 넣은 뒤, 모델이 그 구절을 컨텍스트(context)로 삼아 답하도록 만듭니다. 문서 저장소는 몇 분 안에 갱신할 수 있고, 어떤 문서가 검색되었는지 정확히 들여다볼 수 있으며, 모델 자체는 전혀 바뀌지 않습니다. RAG가 프로덕션에서 지배적인 패턴인 이유가 여기에 있습니다. 더 저렴하고, 더 최신이며, 감사(audit)가 더 쉽고, 어떤 LLM과도 조합할 수 있습니다.

사전 테스트

2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요

1.RAG는 무엇의 약자이며 어떤 문제를 해결하나요?

2.지식 기반 애플리케이션에서 미세 조정(fine-tuning)보다 RAG가 선호되는 이유는 무엇인가요?

0/2 답변 완료

개념

RAG 패턴(RAG Pattern)

전체 패턴은 네 단계로 정리됩니다.

graph LR
    Q["사용자 질의"] --> R["검색(Retrieve)"]
    R --> A["프롬프트 보강(Augment Prompt)"]
    A --> G["생성(Generate)"]
    G --> Ans["답변"]

    subgraph "Retrieve"
        R --> Embed["질의 임베딩"]
        Embed --> Search["벡터 저장소 검색"]
        Search --> TopK["상위 k개 청크 반환"]
    end

    subgraph "Augment"
        TopK --> Format["청크를 프롬프트 형식으로 정리"]
        Format --> Combine["사용자 질문과 결합"]
    end

    subgraph "Generate"
        Combine --> LLM["LLM이 답변 생성"]
        LLM --> Cite["검색된 문서에 근거한 답변"]
    end

질의 → 검색(retrieve) → 프롬프트 보강(augment) → 생성(generate). 모든 RAG 시스템은 이 패턴을 따릅니다. 프로덕션 RAG 시스템 사이의 차이는 각 단계의 세부 구현에 있습니다. 어떻게 청킹하고, 어떻게 임베딩하며, 어떻게 검색하고, 어떻게 프롬프트를 구성하느냐에 따라 결과가 달라집니다.

왜 RAG가 미세 조정보다 유리한가

관심사미세 조정(Fine-tuning)RAG
비용1회 학습당 $1,000-$100,000+질의당 $0.01-$0.10 (임베딩 + LLM)
최신성재학습 전까지 낡은 상태 유지문서를 다시 색인하면 몇 분 안에 갱신됨
감사 가능성답을 출처까지 추적하기 어려움어떤 구절이 검색되었는지 정확히 보여줄 수 있음
환각(Hallucination)여전히 자유롭게 환각 가능검색된 문서에 근거(grounded)함
데이터 프라이버시학습 데이터가 모델 가중치(weights)에 박힘문서는 벡터 저장소 안에만 머무름

미세 조정은 모델 가중치를 영구적으로 바꿉니다. RAG는 모델의 컨텍스트만 일시적으로 바꿉니다. 대부분의 애플리케이션에서 원하는 것은 일시적인 컨텍스트입니다.

미세 조정이 더 유리한 경우는 한 가지입니다. 프롬프트만으로는 얻기 어려운 특정한 스타일(style)이나 어투(tone), 추론 패턴(reasoning pattern)을 모델에 심어야 할 때입니다. 사실 기반의 지식 검색이라면 RAG가 매번 더 적합합니다.

임베딩 모델(Embedding Models)

임베딩 모델은 텍스트를 밀집 벡터(dense vector)로 바꿉니다. 의미가 비슷한 텍스트는 이 고차원 공간(high-dimensional space)에서 서로 가까운 벡터로 표현됩니다. 예를 들어 "How do I reset my password?"와 "I need to change my password"는 공유하는 단어가 적어도 거의 같은 벡터를 만들고, "The cat sat on the mat"은 그와 매우 다른 벡터를 만듭니다.

자주 쓰이는 임베딩 모델(2026년 기준 — 전체 분석은 Phase 5 · 22 참조):

모델차원제공자비고
text-embedding-3-small1536 (Matryoshka)OpenAI대부분의 사용 사례에서 가격 대비 성능이 가장 좋음
text-embedding-3-large3072 (Matryoshka)OpenAI더 높은 정확도, 256/512/1024로 잘라 쓸 수 있음
Gemini Embedding 23072 (Matryoshka)GoogleMTEB 검색 상위권, 8K 컨텍스트
voyage-41024/2048 (Matryoshka)Voyage AI도메인 변형 모델 제공 (code, finance, law)
Cohere embed-v41024 (Matryoshka)Cohere다국어가 강하고 128K 컨텍스트 지원
BGE-M31024 (dense + sparse + ColBERT)BAAI (open-weight)하나의 모델에서 세 가지 표현 제공
Qwen3-Embedding4096 (Matryoshka)Alibaba (open-weight)open-weight 검색 점수 상위권
all-MiniLM-L6-v2384Open-weight (Sentence Transformers)프로토타입용 기준선(baseline)

이 강의에서는 TF-IDF를 사용해 간단한 임베딩을 직접 만들어 봅니다. TF-IDF가 프로덕션 시스템의 표준이라는 뜻은 아닙니다. "텍스트가 들어가면 벡터가 나오고, 비슷한 텍스트는 비슷한 벡터가 된다"는 개념을 손으로 확인하기 위한 선택입니다.

벡터 유사도(Vector Similarity)

두 벡터가 있을 때 유사도(similarity)를 어떻게 측정할까요? 대표적인 방법이 세 가지 있습니다.

코사인 유사도(Cosine similarity): 두 벡터 사이 각도의 코사인 값입니다. -1(정반대)부터 1(같은 방향)까지의 값을 가집니다. 크기(magnitude)는 무시하고 방향만 봅니다. RAG의 기본값입니다.

cosine_sim(a, b) = dot(a, b) / (||a|| * ||b||)

내적(Dot product): 두 벡터의 원시 내적입니다. 더 큰 벡터가 더 높은 점수를 받습니다. 크기 자체가 정보를 담고 있는 경우, 예를 들어 더 긴 문서가 더 관련성이 높을 수 있는 상황에서 유용합니다.

dot(a, b) = sum(a_i * b_i)

L2 거리(Euclidean distance): 벡터 공간에서의 직선 거리입니다. 거리가 작을수록 더 비슷합니다. 크기 차이에 민감합니다.

L2(a, b) = sqrt(sum((a_i - b_i)^2))

코사인 유사도가 표준입니다. 크기로 정규화(normalize)하기 때문에 길이가 서로 다른 문서도 자연스럽게 다룰 수 있습니다. 누군가 "벡터 검색(vector search)"이라고 말한다면 거의 항상 코사인 유사도를 의미합니다.

청킹 전략(Chunking Strategies)

문서는 하나의 벡터로 임베딩하기에는 너무 깁니다. 50쪽짜리 PDF는 수십 가지 주제를 담고 있어 한 벡터로 표현하면 형편없는 임베딩이 나옵니다. 그래서 문서를 청크(chunk)로 나누고 각 청크를 따로 임베딩합니다.

고정 크기 청킹(Fixed-size chunking): N개 토큰마다 나눕니다. 단순하고 예측이 쉽습니다. 512토큰 청크에 50토큰 오버랩(overlap)을 두면 청크 1은 토큰 0-511, 청크 2는 토큰 462-973이 됩니다. 오버랩은 운 나쁜 위치에서 문장이 잘리지 않도록 도와줍니다.

의미 기반 청킹(Semantic chunking): 자연스러운 경계에서 나눕니다. 문단(paragraph), 절(section), 마크다운 헤더 같은 단위가 그 예입니다. 각 청크가 의미 있는 단위가 되므로 구현은 더 복잡하지만 검색 품질은 더 좋아집니다.

재귀적 청킹(Recursive chunking): 먼저 가장 큰 경계(절 헤더)에서 자르려고 시도합니다. 절이 여전히 너무 크면 문단 경계에서 자르고, 문단마저 너무 크면 문장 경계에서 자릅니다. LangChain의 RecursiveCharacterTextSplitter 방식이며 실무에서 잘 작동합니다.

청크 크기는 생각보다 훨씬 중요합니다.

  • 너무 작으면(64-128토큰): 각 청크에 맥락이 부족합니다. 예를 들어 "It increased 15% last quarter"는 "it"이 무엇인지 모르면 의미가 없습니다.
  • 너무 크면(2048토큰 이상): 한 청크가 여러 주제를 덮어 관련성이 흐려집니다. 매출 데이터를 검색했는데 매출 10%, 인원 90%로 구성된 청크가 돌아올 수 있습니다.
  • 적정 구간(256-512토큰): 스스로 의미가 통할 만큼 충분한 맥락을 담고, 동시에 한 주제에 집중되어 있습니다.

대부분의 프로덕션 RAG 시스템은 256-512토큰 청크에 50토큰 오버랩을 사용합니다. Anthropic의 RAG 가이드라인도 이 범위를 권장합니다.

벡터 데이터베이스(Vector Databases)

임베딩이 만들어졌다면 어딘가에 저장하고 검색할 수단이 필요합니다. 주요 선택지는 다음과 같습니다.

데이터베이스형태적합한 용도
FAISS라이브러리 (in-process)프로토타입, 소~중 규모 데이터셋
Chroma경량 DB로컬 개발, 소규모 배포
Pinecone매니지드(managed) 서비스운영 부담 없이 쓰는 프로덕션
Weaviate오픈소스 DB자체 호스팅(self-hosted) 프로덕션
pgvectorPostgres 확장이미 Postgres를 쓰는 경우
Qdrant오픈소스 DB고성능 자체 호스팅

이 강의에서는 단순한 인메모리(in-memory) 벡터 저장소를 만듭니다. 벡터를 리스트에 저장하고, 무차별 대입(brute-force) 방식으로 코사인 유사도 검색을 수행합니다. 평탄 색인(flat index)을 사용하는 FAISS와 같은 구조입니다. 느려지기 전까지 대략 10만 개 벡터 정도까지 다룰 수 있습니다. 프로덕션 시스템은 HNSW 같은 근사 최근접 이웃(approximate nearest neighbor; ANN) 알고리즘으로 수백만 개 벡터를 밀리초 단위로 검색합니다.

전체 파이프라인(The Full Pipeline)

graph TD
    subgraph "색인(Indexing, 오프라인)"
        D["문서"] --> C["청킹"]
        C --> E["각 청크 임베딩"]
        E --> S["벡터 + 텍스트 저장"]
    end

    subgraph "질의(Querying, 온라인)"
        Q["사용자 질의"] --> QE["질의 임베딩"]
        QE --> VS["벡터 검색 (top-k)"]
        VS --> P["청크로 프롬프트 구성"]
        P --> LLM["LLM이 답변 생성"]
    end

    S -.->|"같은 벡터 공간"| VS

색인 단계는 문서마다 한 번씩 실행됩니다. 문서가 갱신될 때도 다시 실행됩니다. 질의 단계는 사용자 요청이 들어올 때마다 실행됩니다. 프로덕션에서는 색인이 수백만 개 문서를 수 시간에 걸쳐 처리할 수도 있습니다. 질의는 1초 안에 응답해야 합니다.

실제 숫자(Real Numbers)

대부분의 프로덕션 RAG 시스템은 다음과 같은 파라미터를 사용합니다.

  • k = 5 ~ 10: 질의당 검색해 오는 청크 수
  • 청크 크기 = 256 ~ 512토큰, 오버랩 50토큰
  • 컨텍스트 예산(Context budget): 질의당 검색 결과 2,500-5,000토큰
  • 전체 프롬프트(Total prompt): 약 8,000-16,000토큰 (시스템 프롬프트 + 검색된 청크 + 대화 이력 + 사용자 질의)
  • 임베딩 차원: 모델에 따라 384-3072
  • 색인 처리량: API 임베딩 기준 초당 100-1,000개 문서
  • 질의 지연 시간(Query latency): 검색 50-200ms, 생성 500-3000ms

직접 만들기

Step 1: 문서 청킹(Document Chunking)

def chunk_text(text, chunk_size=200, overlap=50):
    words = text.split()
    chunks = []
    start = 0
    while start < len(words):
        end = start + chunk_size
        chunk = " ".join(words[start:end])
        chunks.append(chunk)
        start += chunk_size - overlap
    return chunks

Step 2: TF-IDF 임베딩(TF-IDF Embeddings)

간단한 임베딩 함수를 만들어 봅니다. TF-IDF(Term Frequency-Inverse Document Frequency)는 신경망 임베딩(neural embedding)은 아니지만, 단어의 중요도를 반영하는 방식으로 텍스트를 벡터로 바꿔 줍니다. 한 문서 안에서 자주 등장하는 단어는 TF가 높아지고, 말뭉치(corpus) 전체에서는 드물게 나타나는 단어가 IDF가 높아집니다. 둘을 곱하면 중요하고 변별력 있는 단어가 큰 값을 갖는 벡터가 만들어집니다.

import math
from collections import Counter

def build_vocabulary(documents):
    vocab = set()
    for doc in documents:
        vocab.update(doc.lower().split())
    return sorted(vocab)

def compute_tf(text, vocab):
    words = text.lower().split()
    count = Counter(words)
    total = len(words)
    return [count.get(word, 0) / total for word in vocab]

def compute_idf(documents, vocab):
    n = len(documents)
    idf = []
    for word in vocab:
        doc_count = sum(1 for doc in documents if word in doc.lower().split())
        idf.append(math.log((n + 1) / (doc_count + 1)) + 1)
    return idf

def tfidf_embed(text, vocab, idf):
    tf = compute_tf(text, vocab)
    return [t * i for t, i in zip(tf, idf)]
def cosine_similarity(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(x * x for x in b))
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot / (norm_a * norm_b)

def search(query_embedding, stored_embeddings, top_k=5):
    scores = []
    for i, emb in enumerate(stored_embeddings):
        sim = cosine_similarity(query_embedding, emb)
        scores.append((i, sim))
    scores.sort(key=lambda x: x[1], reverse=True)
    return scores[:top_k]

Step 4: 프롬프트 구성(Prompt Construction)

RAG에서 "증강(augmented)"이 실제로 일어나는 지점입니다. 검색된 청크를 가져와 프롬프트로 정리한 뒤, 제공된 컨텍스트만 근거로 답하라고 LLM에 요청합니다.

def build_rag_prompt(query, retrieved_chunks):
    context = "\n\n---\n\n".join(
        f"[Source {i+1}]\n{chunk}"
        for i, chunk in enumerate(retrieved_chunks)
    )
    return f"""Answer the question based ONLY on the following context.
If the context doesn't contain enough information, say "I don't have enough information to answer that."

Context:
{context}

Question: {query}

Answer:"""

Step 5: 전체 RAG 파이프라인

class RAGPipeline:
    def __init__(self):
        self.chunks = []
        self.embeddings = []
        self.vocab = []
        self.idf = []

    def index(self, documents):
        all_chunks = []
        for doc in documents:
            all_chunks.extend(chunk_text(doc))
        self.chunks = all_chunks
        self.vocab = build_vocabulary(all_chunks)
        self.idf = compute_idf(all_chunks, self.vocab)
        self.embeddings = [
            tfidf_embed(chunk, self.vocab, self.idf)
            for chunk in all_chunks
        ]

    def query(self, question, top_k=5):
        query_emb = tfidf_embed(question, self.vocab, self.idf)
        results = search(query_emb, self.embeddings, top_k)
        retrieved = [(self.chunks[i], score) for i, score in results]
        prompt = build_rag_prompt(
            question, [chunk for chunk, _ in retrieved]
        )
        return prompt, retrieved

Step 6: 생성(Generation, simulated)

프로덕션에서는 이 지점에서 LLM API를 호출합니다. 이 강의에서는 검색된 컨텍스트에서 가장 관련성이 높은 문장을 골라내는 방식으로 생성을 모사(simulate)합니다.

def simple_generate(prompt, retrieved_chunks):
    query_words = set(prompt.lower().split("question:")[-1].split())
    best_sentence = ""
    best_score = 0
    for chunk in retrieved_chunks:
        for sentence in chunk.split("."):
            sentence = sentence.strip()
            if not sentence:
                continue
            words = set(sentence.lower().split())
            overlap = len(query_words & words)
            if overlap > best_score:
                best_score = overlap
                best_sentence = sentence
    return best_sentence if best_sentence else "I don't have enough information."

사용해보기

실제 임베딩 모델과 LLM을 사용한다고 해도 코드는 거의 바뀌지 않습니다.

from openai import OpenAI

client = OpenAI()

def embed(text):
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

def generate(prompt):
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0
    )
    return response.choices[0].message.content

Anthropic을 사용한다면 다음과 같이 작성할 수 있습니다.

import anthropic

client = anthropic.Anthropic()

def generate(prompt):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.content[0].text

파이프라인 자체는 동일합니다. 임베딩 함수만 교체하고, 생성 함수만 교체하면 됩니다. 검색 로직, 청킹, 프롬프트 구성은 어떤 모델을 쓰든 그대로 유지됩니다.

규모가 커진 벡터 저장이 필요하다면, 무차별 대입 검색을 본격적인 벡터 데이터베이스로 교체합니다.

import chromadb

client = chromadb.Client()
collection = client.create_collection("my_docs")

collection.add(
    documents=chunks,
    ids=[f"chunk_{i}" for i in range(len(chunks))]
)

results = collection.query(
    query_texts=["What is the refund policy?"],
    n_results=5
)

Chroma는 내부적으로 임베딩을 알아서 처리하며 기본값으로 all-MiniLM-L6-v2를 사용해 벡터를 로컬 데이터베이스에 저장합니다. 패턴은 같고 배관(plumbing)만 다릅니다.

산출물 만들기

이 강의는 다음과 같은 산출물을 만듭니다.

  • outputs/prompt-rag-architect.md — 특정 사용 사례에 맞춰 RAG 시스템을 설계하기 위한 프롬프트
  • outputs/skill-rag-pipeline.md — 에이전트(agent)에게 RAG 파이프라인을 만들고 디버깅(debug)하는 방법을 가르치는 스킬(skill)

연습문제

  1. (쉬움) TF-IDF 임베딩을 단순 단어 가방(bag-of-words) 방식으로 바꿔 봅니다. 단어가 있으면 1, 없으면 0인 이진(binary) 방식입니다. 동일한 표본 문서에서 검색 품질을 비교해 봅니다. TF-IDF는 드문 단어에 더 큰 가중치를 주므로 더 좋은 성능을 보여야 합니다.

  2. (중간) 청크 크기를 바꿔 가며 실험해 봅니다. 같은 문서 집합에 대해 50, 100, 200, 500단어를 각각 시도합니다. 크기마다 동일한 5개 질의를 실행하고, 상위 3개 결과 안에 관련 청크가 몇 번 들어오는지 세어 봅니다. 검색 품질이 정점에 도달하는 적정 구간(sweet spot)을 찾아봅니다.

  3. (중간) 각 청크에 메타데이터(metadata; 출처 문서 이름, 청크 위치)를 덧붙이고, 프롬프트 템플릿(template)을 수정해 LLM이 출처를 인용(cite)하도록 만들어 봅니다.

  4. (중간) 간단한 평가(evaluation)를 구현해 봅니다. 10개의 질문-정답 쌍이 주어졌다고 보고, 각 질문을 RAG 파이프라인에 통과시킨 뒤 검색된 청크 중 답을 포함한 비율을 측정합니다. 이것이 상위 k 검색 재현율(retrieval recall at k)입니다.

  5. (어려움) 대화 맥락을 인식하는(conversation-aware) RAG 파이프라인을 만들어 봅니다. 최근 세 차례의 대화 이력을 유지하고, 검색된 청크와 함께 프롬프트에 포함합니다. 가격을 먼저 물은 뒤 "엔터프라이즈는 어떤가요?"(What about enterprise?)처럼 후속 질문을 던져 동작을 확인합니다.

핵심 용어

용어흔한 설명실제 의미
RAG"문서를 읽는 AI"관련 문서를 검색해 프롬프트에 붙인 뒤, 그 문서에 근거한 답을 생성하는 방식
임베딩(Embedding)"텍스트를 숫자로 바꾸기"의미가 비슷한 텍스트가 비슷한 벡터로 표현되는 밀집 벡터 표현(dense vector representation)
벡터 데이터베이스(Vector database)"AI용 검색 엔진"벡터를 저장하고 유사도 기준으로 최근접 이웃을 찾는 데 최적화된 데이터 저장소
청킹(Chunking)"문서를 조각내기"문서를 256-512토큰 정도의 작은 단위로 나눠, 각 청크를 독립적으로 임베딩하고 검색할 수 있게 하는 작업
코사인 유사도(Cosine similarity)"두 벡터가 얼마나 비슷한가"두 벡터 사이 각도의 코사인. 1은 같은 방향, 0은 직교(orthogonal), -1은 정반대 방향
상위 k 검색(Top-k retrieval)"가장 좋은 k개 결과 가져오기"벡터 저장소에서 질의와 가장 비슷한 k개 청크를 반환하는 방식
컨텍스트 윈도우(Context window)"LLM이 볼 수 있는 텍스트의 양"LLM이 한 번의 요청에서 처리할 수 있는 최대 토큰 수. 검색된 청크는 이 한도 안에 들어가야 합니다.
증강 생성(Augmented generation)"주어진 컨텍스트로 답하기"학습된 지식에만 의존하지 않고, 검색된 문서를 컨텍스트로 삼아 응답을 생성하는 방식
TF-IDF"단어 중요도 점수 매기기"Term Frequency × Inverse Document Frequency. 말뭉치 안에서 얼마나 변별력 있는 단어인지에 따라 가중치를 부여합니다.
색인(Indexing)"문서를 검색 가능하게 준비하기"질의 시점에 검색할 수 있도록 문서를 청킹·임베딩·저장하는 오프라인 과정

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

main
Code

산출물

이 강의에서 생성된 프롬프트, 스킬, 코드 산출물 2개

skill-rag-pipeline

Build and debug RAG pipelines from first principles

Skill
prompt-rag-architect

Design RAG systems for specific use cases with concrete architecture decisions

Prompt

확인 문제

3문제 · 모두 맞추면 완료 표시가 가능합니다

1.기본 RAG 파이프라인의 올바른 단계 순서는 무엇인가요?

2.기본 RAG 시스템에서 흔히 발생하는 실패 양상(failure mode)은 무엇인가요?

3.RAG 품질은 어떻게 평가하나요?

0/3 답변 완료

추가 문제 풀기

AI가 강의 내용을 바탕으로 새로운 문제를 생성합니다