RAG์™€ LLM 2026๋…„ํŒ: ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘์„ ์œ„ํ•œ ๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

2026๋…„ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘์„ ์œ„ํ•œ RAG ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ ํŒŒ์ดํ”„๋ผ์ธ, ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์ฒญํ‚น, ์ž„๋ฒ ๋”ฉ, ์—์ด์ „ํ‹ฑ RAG, Graph RAG๋ฅผ ํฌ๊ด„์ ์œผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

RAG์™€ LLM 2026๋…„ํŒ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘ ๊ฐ€์ด๋“œ

๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ(RAG: Retrieval-Augmented Generation)์€ LLM์˜ ์ถœ๋ ฅ์„ ์‚ฌ์‹ค์— ๊ธฐ๋ฐ˜ํ•œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ๋’ท๋ฐ›์นจํ•˜๋Š” ํ‘œ์ค€ ์•„ํ‚คํ…์ฒ˜๋กœ ์ž๋ฆฌ ์žก์•˜์Šต๋‹ˆ๋‹ค. 2026๋…„ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฐ AI ์—”์ง€๋‹ˆ์–ด๋ง ๋ฉด์ ‘์—์„œ๋Š” RAG ๊ด€๋ จ ์งˆ๋ฌธ์ด ๊ธฐ์กด ML(๋จธ์‹ ๋Ÿฌ๋‹) ์ฃผ์ œ์™€ ํ•จ๊ป˜ ์ถœ์ œ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ์‹œ์Šคํ…œ ์„ค๊ณ„ ์—ญ๋Ÿ‰๊ณผ ์‹ค์ œ ๊ตฌํ˜„ ๋Šฅ๋ ฅ์ด ๋™์‹œ์— ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.

RAG๋ฅผ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์„ค๋ช…ํ•˜๋ฉด?

๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ(RAG)์€ ๊ฒ€์ƒ‰ ์‹œ์Šคํ…œ(์ง€์‹ ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰)๊ณผ LLM ์ƒ์„ฑ๊ธฐ๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ, ๋ชจ๋ธ์ด ํ•™์Šต ๋ฐ์ดํ„ฐ์˜ ๊ธฐ์–ต์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์‹ค์ œ ๋ฌธ์„œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค.

RAG ํŒŒ์ดํ”„๋ผ์ธ์˜ ์—”๋“œํˆฌ์—”๋“œ ๋™์ž‘ ์›๋ฆฌ

RAG ์‹œ์Šคํ…œ์€ ๋‘ ๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ง€์‹ ๋ฒ ์ด์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์˜คํ”„๋ผ์ธ ์ธ์ œ์Šค์ฒœ ๋‹จ๊ณ„์™€, ๊ด€๋ จ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์˜จ๋ผ์ธ ์ฟผ๋ฆฌ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

์ธ์ œ์Šค์ฒœ ๊ณผ์ •์—์„œ๋Š” ์›๋ณธ ๋ฌธ์„œ๊ฐ€ ํด๋ฆฌ๋‹, ์ฒญํ‚น, ์ž„๋ฒ ๋”ฉ ์ฒ˜๋ฆฌ๋ฅผ ๊ฑฐ์ณ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ถ”๋ก  ์‹œ์—๋Š” ์‚ฌ์šฉ์ž์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋™์ผํ•œ ์ž„๋ฒ ๋”ฉ ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ฅด๋ฉฐ, ์ตœ๊ทผ์ ‘ ์ด์›ƒ ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ๊ฐ€์žฅ ๊ด€๋ จ์„ฑ์ด ๋†’์€ ์ฒญํฌ๊ฐ€ ๊ฒ€์ƒ‰๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฒญํฌ๊ฐ€ LLM ํ”„๋กฌํ”„ํŠธ์˜ ์ผ๋ถ€๊ฐ€ ๋˜์–ด, ์ƒ์„ฑ๋˜๋Š” ๋‹ต๋ณ€์ด ์†Œ์Šค ์ž๋ฃŒ์— ๊ทผ๊ฑฐํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

python
# rag_pipeline.py
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

# Offline: ingest documents into the vector store
def build_index(documents: list[str]) -> Chroma:
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=512,       # tokens per chunk
        chunk_overlap=64,     # overlap preserves context at boundaries
        separators=["\n\n", "\n", ". ", " "]
    )
    chunks = splitter.create_documents(documents)
    embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
    return Chroma.from_documents(chunks, embeddings)

# Online: retrieve + generate
def query(vectorstore: Chroma, question: str) -> str:
    retriever = vectorstore.as_retriever(
        search_type="mmr",    # Maximal Marginal Relevance for diversity
        search_kwargs={"k": 5, "fetch_k": 20}
    )
    prompt = ChatPromptTemplate.from_template(
        "Answer based on this context only:\n{context}\n\nQuestion: {question}"
    )
    chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | ChatOpenAI(model="gpt-4o", temperature=0)
    )
    return chain.invoke(question).content

์ด ํŒŒ์ดํ”„๋ผ์ธ์€ RAG์˜ ํ•ต์‹ฌ ๋ฃจํ”„(์ฒญํ‚น, ์ž„๋ฒ ๋”ฉ, ์ €์žฅ, ๊ฒ€์ƒ‰, ์ƒ์„ฑ)๋ฅผ ๋‹ค๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. search_type="mmr" ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๊ฐ€ ๊ด€๋ จ์„ฑ๊ณผ ๋‹ค์–‘์„ฑ์„ ๋™์‹œ์— ๊ฐ–์ถ”๋„๋ก ๋ณด์žฅํ•˜์—ฌ, ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ๋‚ด์˜ ์ค‘๋ณต์„ ์ค„์ž…๋‹ˆ๋‹ค.

๊ฒ€์ƒ‰ ํ’ˆ์งˆ์„ ์ขŒ์šฐํ•˜๋Š” ์ฒญํ‚น ์ „๋žต

์ฒญํ‚น์€ ๋‹ค๋ฅธ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ณด๋‹ค ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ๋ถ€์ ์ ˆํ•œ ์ฒญํ‚น์€ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋ถ€์กฑํ•œ ๋‹จํŽธ์ด๋‚˜ ๋ฌด๊ด€ํ•œ ์ •๋ณด๋กœ ์‹ ํ˜ธ๊ฐ€ ํฌ์„๋œ ๋‹จํŽธ์„ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ์›์ธ์ด ๋ฉ๋‹ˆ๋‹ค.

2026๋…„ ํ”„๋กœ๋•์…˜ ์‹œ์Šคํ…œ์—์„œ๋Š” ์„ธ ๊ฐ€์ง€ ์ฒญํ‚น ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ฃผ๋ฅ˜๋ฅผ ์ด๋ฃจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณ ์ • ํฌ๊ธฐ ์ฒญํ‚น์€ ์„ค์ •๋œ ํ† ํฐ ์ˆ˜(๋ณดํ†ต 256~512 ํ† ํฐ)๋กœ ํ…์ŠคํŠธ๋ฅผ ๋ถ„ํ• ํ•˜๊ณ  ์˜ค๋ฒ„๋žฉ์„ ๋‘ก๋‹ˆ๋‹ค. ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋ฌธ์žฅ์ด๋‚˜ ์•„์ด๋””์–ด ์ค‘๊ฐ„์—์„œ ๋ถ„ํ• ๋˜๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ๋งจํ‹ฑ ์ฒญํ‚น์€ ์—ฐ์†๋œ ๋ฌธ์žฅ ๊ฐ„์˜ ์ž„๋ฒ ๋”ฉ ์œ ์‚ฌ๋„๋ฅผ ์ธก์ •ํ•˜์—ฌ ์ฃผ์ œ์˜ ๊ฒฝ๊ณ„๋ฅผ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์œ ์‚ฌ๋„๊ฐ€ ์ž„๊ณ„๊ฐ’ ์•„๋ž˜๋กœ ๋–จ์–ด์ง€๋ฉด ์ƒˆ๋กœ์šด ์ฒญํฌ๊ฐ€ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ๊ฐ ์ฒญํฌ๋Š” ํ…์ŠคํŠธ์˜ ์ž„์˜ ๋‹จํŽธ์ด ์•„๋‹Œ ์ผ๊ด€๋œ ์•„์ด๋””์–ด๋ฅผ ๋‹ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ ˆ์ดํŠธ ์ฒญํ‚น์€ ๋จผ์ € ํŠธ๋žœ์Šคํฌ๋จธ ๋ชจ๋ธ์„ ๋ฌธ์„œ ์ „์ฒด์— ์ ์šฉํ•˜์—ฌ ์ปจํ…์ŠคํŠธ๊ฐ€ ํฌํ•จ๋œ ํ† ํฐ ์ž„๋ฒ ๋”ฉ์„ ์ƒ์„ฑํ•œ ํ›„, ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ธฐ์กด ์ฒญํ‚น์ด ํŒŒ๊ดดํ•˜๋Š” ์žฅ๊ฑฐ๋ฆฌ ์˜์กด ๊ด€๊ณ„๋ฅผ ๋ณด์กดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

python
# semantic_chunking.py
import numpy as np
from sentence_transformers import SentenceTransformer

def semantic_chunk(text: str, threshold: float = 0.3) -> list[str]:
    """Split text where semantic similarity drops below threshold."""
    model = SentenceTransformer("all-MiniLM-L6-v2")
    sentences = text.split(". ")
    embeddings = model.encode(sentences)

    chunks, current_chunk = [], [sentences[0]]

    for i in range(1, len(sentences)):
        # Cosine similarity between consecutive sentences
        sim = np.dot(embeddings[i-1], embeddings[i]) / (
            np.linalg.norm(embeddings[i-1]) * np.linalg.norm(embeddings[i])
        )
        if sim < threshold:       # topic shift detected
            chunks.append(". ".join(current_chunk))
            current_chunk = [sentences[i]]
        else:
            current_chunk.append(sentences[i])

    chunks.append(". ".join(current_chunk))  # final chunk
    return chunks

๋ฉด์ ‘์—์„œ์˜ ํ•ต์‹ฌ ํฌ์ธํŠธ๋Š” ์ฒญํฌ ํฌ๊ธฐ๊ฐ€ ์ •๋ฐ€๋„์™€ ์žฌํ˜„์œจ ๊ฐ„์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ž‘์€ ์ฒญํฌ(100 ํ† ํฐ)๋Š” ๊ฒ€์ƒ‰ ์ •๋ฐ€๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋งŒ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋‹จํŽธํ™”๋ฉ๋‹ˆ๋‹ค. ํฐ ์ฒญํฌ(1000 ํ† ํฐ)๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ๋ณด์กดํ•˜์ง€๋งŒ ์ž„๋ฒ ๋”ฉ์˜ ํŠน์ด์„ฑ์ด ํฌ์„๋ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๋•์…˜ ์‹œ์Šคํ…œ์—์„œ๋Š” 10~20%์˜ ์˜ค๋ฒ„๋žฉ์„ ๋‘” 256~512 ํ† ํฐ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ

๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ž„๋ฒ ๋”ฉ์„ ์ €์žฅํ•˜๊ณ  ๊ณ ์† ๊ทผ์‚ฌ ์ตœ๊ทผ์ ‘ ์ด์›ƒ(ANN) ๊ฒ€์ƒ‰์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ๊ณผ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ ์ ˆํ•œ ์กฐํ•ฉ์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ๊ฒ€์ƒ‰ ๋ ˆ์ดํ„ด์‹œ์™€ ์ •ํ™•๋„์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

2026๋…„์˜ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์€ ๋ช‡ ๊ฐ€์ง€ ๊ณ ์„ฑ๋Šฅ ์˜ต์…˜์œผ๋กœ ์ˆ˜๋ ด๋˜์—ˆ์Šต๋‹ˆ๋‹ค. OpenAI์˜ text-embedding-3-large(3072์ฐจ์›)์™€ BAAI์˜ bge-m3, Cohere์˜ embed-v4 ๊ฐ™์€ ์˜คํ”ˆ์†Œ์Šค ๋Œ€์•ˆ์ด ๊ฐ•๋ ฅํ•œ ๋‹ค๊ตญ์–ด ๊ฒ€์ƒ‰ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. MTEB ๋ฆฌ๋”๋ณด๋“œ๋Š” ์ž„๋ฒ ๋”ฉ ํ’ˆ์งˆ ๋น„๊ต๋ฅผ ์œ„ํ•œ ํ‘œ์ค€ ๋ฒค์น˜๋งˆํฌ๋กœ ๊ณ„์† ํ™œ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Pinecone, Weaviate, Milvus, Qdrant, pgvector ๋“ฑ์˜ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ฐ๊ฐ ๋‹ค๋ฅธ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

| ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค | ์ธ๋ฑ์‹ฑ | ๋งค๋‹ˆ์ง€๋“œ | ๊ฐ•์  | |----------|----------|---------|----------| | Pinecone | ๋…์ž ๋ฐฉ์‹ | ์ง€์› | ๊ฐ„ํŽธํ•จ, ์„œ๋ฒ„๋ฆฌ์Šค ์Šค์ผ€์ผ๋ง | | Weaviate | HNSW | ์ง€์›/์…€ํ”„ | ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ฒ€์ƒ‰(๋ฒกํ„ฐ + BM25) | | Milvus | IVF, HNSW | ์ง€์›/์…€ํ”„ | ์ˆ˜์‹ญ์–ต ๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์…‹ | | Qdrant | HNSW | ์ง€์›/์…€ํ”„ | ํ•„ํ„ฐ๋ง + ํŽ˜์ด๋กœ๋“œ ์ €์žฅ | | pgvector | IVF, HNSW | ์…€ํ”„ | PostgreSQL ํ†ตํ•ฉ |

๋ฉด์ ‘์—์„œ ์ค‘์š”ํ•œ ๊ฒƒ์€ HNSW(Hierarchical Navigable Small World) ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ๋Œ€ํ•œ ์ดํ•ด์ž…๋‹ˆ๋‹ค. HNSW๋Š” ๋‹ค์ธต ๊ทธ๋ž˜ํ”„๋ฅผ ๊ตฌ์ถ•ํ•˜์—ฌ ๊ฐ ๋…ธ๋“œ๊ฐ€ ์ตœ๊ทผ์ ‘ ์ด์›ƒ์— ์—ฐ๊ฒฐ๋˜๋ฉฐ, ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€๋ฅผ ๋Œ€๊ฐ€๋กœ O(log n) ๊ฒ€์ƒ‰์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Data Science & ML ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ฒ€์ƒ‰: ๋ฐ€์ง‘ ๋ฒกํ„ฐ์™€ ํฌ์†Œ ๋ฒกํ„ฐ์˜ ๊ฒฐํ•ฉ

์ˆœ์ˆ˜ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์€ ์ •ํ™•ํ•œ ํ‚ค์›Œ๋“œ ๋งค์นญ์ด๋‚˜ ํฌ๊ท€ ์šฉ์–ด ๊ฒ€์ƒ‰์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ์ˆœ์ˆ˜ ๋ ‰์‹œ์ปฌ ๊ฒ€์ƒ‰(BM25)์€ ์‹œ๋งจํ‹ฑ ์œ ์‚ฌ์„ฑ์„ ๋†“์นฉ๋‹ˆ๋‹ค. ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ฒ€์ƒ‰์€ ์ด ๋‘˜์„ ๊ฒฐํ•ฉํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์ด๋ฉฐ, 2026๋…„ ํ”„๋กœ๋•์…˜ RAG ์‹œ์Šคํ…œ์—์„œ๋Š” ๊ธฐ๋ณธ ์„ค์ •์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํ‘œ์ค€ ํŒจํ„ด์—์„œ๋Š” Reciprocal Rank Fusion(RRF)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‘ ๋ฆฌํŠธ๋ฆฌ๋ฒ„์˜ ๋žญํ‚น ๊ฒฐ๊ณผ๋ฅผ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.

python
# hybrid_retrieval.py
from rank_bm25 import BM25Okapi
import numpy as np

def reciprocal_rank_fusion(
    dense_results: list[str],
    sparse_results: list[str],
    k: int = 60
) -> list[str]:
    """Merge dense (vector) and sparse (BM25) results using RRF."""
    scores: dict[str, float] = {}

    for rank, doc_id in enumerate(dense_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)

    for rank, doc_id in enumerate(sparse_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)

    # Sort by combined RRF score, highest first
    return sorted(scores.keys(), key=lambda d: scores[d], reverse=True)

ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ฒ€์ƒ‰์€ '์–ดํœ˜ ๋ถˆ์ผ์น˜' ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ '๊ตฌ๋… ์ทจ์†Œ'์— ๋Œ€ํ•ด ์งˆ๋ฌธํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ๊ด€๋ จ ๋ฌธ์„œ์—์„œ๋Š” '๊ณ„์ • ํ•ด์ง€ ์ •์ฑ…'์ด๋ผ๋Š” ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. BM25๊ฐ€ ์ •ํ™•ํ•œ ์šฉ์–ด ์ผ์น˜๋ฅผ ํฌ์ฐฉํ•˜๋Š” ๋ฐ˜๋ฉด, ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์€ ์‹œ๋งจํ‹ฑ ๊ด€๋ จ์„ฑ์„ ํŒŒ์•…ํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ๋žญํ‚น: 2๋‹จ๊ณ„ ํ•„ํ„ฐ

๊ฒ€์ƒ‰์€ ํ›„๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋ฆฌ๋žญํ‚น์€ ๊ทธ๊ฒƒ๋“ค์„ ์‹ค์ œ ๊ด€๋ จ์„ฑ์— ๋”ฐ๋ผ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค. Cohere Rerank๋‚˜ bge-reranker-v2.5-gemma2-lightweight ๊ฐ™์€ ํฌ๋กœ์Šค ์ธ์ฝ”๋” ๋ฆฌ๋žญ์ปค๋Š” ๊ฐ ์ฟผ๋ฆฌ-๋ฌธ์„œ ์Œ์„ ๋™์‹œ์— ์Šค์ฝ”์–ด๋งํ•˜์—ฌ, ๋ฐ”์ด ์ธ์ฝ”๋” ์œ ์‚ฌ๋„๋ณด๋‹ค ํ›จ์”ฌ ์ •ํ™•ํ•œ ๊ด€๋ จ์„ฑ ์ ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

2๋‹จ๊ณ„ ๊ฒ€์ƒ‰ ํŒŒ์ดํ”„๋ผ์ธ -- ๋„“์€ 1๋‹จ๊ณ„ ๋ฆฌ์ฝœ(๋ฒกํ„ฐ + BM25๋กœ ์ƒ์œ„ 50~100๊ฐœ ํ›„๋ณด), ์ด์–ด์„œ ์ •๋ฐ€ ๋ฆฌ๋žญํ‚น(ํ”„๋กฌํ”„ํŠธ์šฉ ์ƒ์œ„ 5~10๊ฐœ) -- ์€ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค. 1๋‹จ๊ณ„์—์„œ ๊ณ ์† ANN ๊ฒ€์ƒ‰์„ ์‚ฌ์šฉํ•˜๊ณ , ๋น„์šฉ์ด ๋†’์€ ํฌ๋กœ์Šค ์ธ์ฝ”๋”๋Š” ์†Œ์ˆ˜์˜ ํ›„๋ณด ์„ธํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ ˆ์ดํ„ด์‹œ๋ฅผ ๊ด€๋ฆฌ ๊ฐ€๋Šฅํ•œ ์ˆ˜์ค€์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌ๋žญํ‚น ๊ด€๋ จ ๋ฉด์ ‘ ํฌ์ธํŠธ

ํฌ๋กœ์Šค ์ธ์ฝ”๋”๊ฐ€ ๋ฐ”์ด ์ธ์ฝ”๋”๋ณด๋‹ค ์ •ํ™•ํ•œ ์ด์œ ๋Š” ์ฟผ๋ฆฌ์™€ ๋ฌธ์„œ๋ฅผ ํŠธ๋žœ์Šคํฌ๋จธ์˜ ๋ชจ๋“  ๋ ˆ์ด์–ด์—์„œ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐ”์ด ์ธ์ฝ”๋”๋Š” ๊ฐ๊ฐ์„ ๋…๋ฆฝ์ ์œผ๋กœ ์ž„๋ฒ ๋”ฉํ•˜๋ฏ€๋กœ ์„ธ๋ฐ€ํ•œ ์ƒํ˜ธ์ž‘์šฉ ์‹ ํ˜ธ๊ฐ€ ์†์‹ค๋ฉ๋‹ˆ๋‹ค. ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋Š” ์†๋„์ž…๋‹ˆ๋‹ค. ํฌ๋กœ์Šค ์ธ์ฝ”๋”๋Š” ์‚ฌ์ „ ์ธ๋ฑ์‹ฑ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์—์ด์ „ํ‹ฑ RAG: ๋‹จ๋ฐœ์„ฑ ๊ฒ€์ƒ‰์„ ๋„˜์–ด์„œ

๋‚˜์ด๋ธŒ RAG๋Š” ํ•œ ๋ฒˆ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ „๋ถ€์ž…๋‹ˆ๋‹ค. ์—์ด์ „ํ‹ฑ RAG๋Š” LLM์„ ์–ธ์ œ ๊ฒ€์ƒ‰ํ• ์ง€, ๋ฌด์—‡์„ ๊ฒ€์ƒ‰ํ• ์ง€, ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ถฉ๋ถ„ํ•œ์ง€๋ฅผ ํŒ๋‹จํ•˜๋Š” ์ถ”๋ก  ์—์ด์ „ํŠธ๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

2026๋…„์—๋Š” ๋‹ค๋‹จ๊ณ„ ์ถ”๋ก ์ด ํ•„์š”ํ•œ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ์—์ด์ „ํ‹ฑ RAG๊ฐ€ ์ง€๋ฐฐ์ ์ธ ํŒจํ„ด์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ž๊ธฐ ํ‰๊ฐ€: ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ๊ฐ€ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ‰๊ฐ€
  • ์žฌ์ฟผ๋ฆฌ: ์ดˆ๊ธฐ ๊ฒฐ๊ณผ๊ฐ€ ๋ถˆ์ถฉ๋ถ„ํ•  ๊ฒฝ์šฐ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๋ฅผ ์žฌ๊ตฌ์„ฑ
  • ๋ผ์šฐํŒ…: ๋‹ค์–‘ํ•œ ์ง€์‹ ์†Œ์Šค(๋ฒกํ„ฐ DB, SQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, API) ์ค‘์—์„œ ์„ ํƒ
  • ๊ฒ€์ฆ: ์—ฌ๋Ÿฌ ๊ฒ€์ƒ‰ ํŒจ์‹œ์ง€ ๊ฐ„์˜ ์‚ฌ์‹ค ๊ด€๊ณ„๋ฅผ ๊ต์ฐจ ํ™•์ธ
python
# agentic_rag.py
from langgraph.graph import StateGraph, END
from typing import TypedDict

class RAGState(TypedDict):
    question: str
    documents: list[str]
    generation: str
    retries: int

def retrieve(state: RAGState) -> RAGState:
    """Retrieve documents from vector store."""
    docs = vectorstore.similarity_search(state["question"], k=5)
    return {"documents": [d.page_content for d in docs]}

def grade_documents(state: RAGState) -> str:
    """Decide if documents are relevant enough to answer."""
    prompt = f"Are these documents relevant to: {state['question']}?\n"
    prompt += "\n".join(state["documents"])
    relevance = llm.invoke(prompt)  # returns 'relevant' or 'not_relevant'
    return "generate" if "relevant" in relevance.content else "rewrite"

def rewrite_query(state: RAGState) -> RAGState:
    """Reformulate the query for better retrieval."""
    new_query = llm.invoke(
        f"Rewrite this query for better search results: {state['question']}"
    )
    return {"question": new_query.content, "retries": state["retries"] + 1}

# Build the agent graph
workflow = StateGraph(RAGState)
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade", grade_documents)      # conditional routing
workflow.add_node("rewrite", rewrite_query)
workflow.add_node("generate", generate_answer)

workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade")
workflow.add_conditional_edges("grade", grade_documents,
    {"generate": "generate", "rewrite": "rewrite"})
workflow.add_edge("rewrite", "retrieve")          # retry loop
workflow.add_edge("generate", END)

์ด ํŒจํ„ด -- ๊ฒ€์ƒ‰, ํ‰๊ฐ€, ํ•„์š” ์‹œ ์ฟผ๋ฆฌ ์žฌ์ž‘์„ฑ ํ›„ ์žฌ์‹œ๋„ -- ์€ Corrective RAG(CRAG)๋กœ ์•Œ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. LangGraph ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์›Œํฌํ”Œ๋กœ๋ฅผ ์กฐ๊ฑด ๋ถ„๊ธฐ๊ฐ€ ์žˆ๋Š” ์œ ํ–ฅ ์ˆœํ™˜ ๊ทธ๋ž˜ํ”„๋กœ ๋ชจ๋ธ๋งํ•˜์—ฌ, ๊ฒ€์ฆ ๋‹จ๊ณ„, ํœด๋จผ์ธ๋”๋ฃจํ”„ ์ฒดํฌํฌ์ธํŠธ, ๋ฉ€ํ‹ฐ์†Œ์Šค ๋ผ์šฐํŒ…์˜ ์ถ”๊ฐ€๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Graph RAG: ๊ตฌ์กฐํ™”๋œ ์ง€์‹ ๊ฒ€์ƒ‰

Graph RAG๋Š” ๋ฌธ์„œ์—์„œ ์—”ํ‹ฐํ‹ฐ์™€ ๊ด€๊ณ„๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์ง€์‹ ๊ทธ๋ž˜ํ”„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ , ๊ทธ๋ž˜ํ”„์™€ ๋ฒกํ„ฐ ์Šคํ† ์–ด ์–‘์ชฝ์— ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์•„ํ‚คํ…์ฒ˜๋Š” ๋น„์ •ํ˜• ํ…์ŠคํŠธ ์œ ์‚ฌ์„ฑ์ด ์•„๋‹Œ ๋ช…์‹œ์ ์ธ ์—”ํ‹ฐํ‹ฐ ๊ฐ„ ๊ด€๊ณ„์— ๋‹ต๋ณ€์„ ๊ทผ๊ฑฐํ•จ์œผ๋กœ์จ, ์‚ฌ์‹ค ๊ด€๋ จ ์ฟผ๋ฆฌ์—์„œ์˜ ํ• ๋ฃจ์‹œ๋„ค์ด์…˜์„ ์ค„์ž…๋‹ˆ๋‹ค.

์ธ์ œ์Šค์ฒœ ํŒŒ์ดํ”„๋ผ์ธ์€ ๊ฐ ๋ฌธ์„œ ์ฒญํฌ์—์„œ ํŠธ๋ฆฌํ”Œ(์ฃผ์–ด, ์ˆ ์–ด, ๋ชฉ์ ์–ด)์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ์‹œ์—๋Š” ์งˆ๋ฌธ ๋‚ด์˜ ๊ด€๋ จ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹๋ณ„ํ•˜๊ณ , ์ง€์‹ ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ ์—ฐ๊ฒฐ๋œ ์‚ฌ์‹ค์„ ๊ฒ€์ƒ‰ํ•œ ํ›„, ๊ทธ๋ž˜ํ”„ ๊ฒ€์ƒ‰์œผ๋กœ ์–ป์€ ์ปจํ…์ŠคํŠธ์™€ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์œผ๋กœ ์–ป์€ ํŒจ์‹œ์ง€๋ฅผ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค.

Graph RAG๋Š” '๋ฌธ์„œ X์—์„œ ์–ธ๊ธ‰๋œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ด๋„๋Š” ํŒ€์€ ์–ด๋””์ธ๊ฐ€?'์™€ ๊ฐ™์€ ๋ฉ€ํ‹ฐํ™‰ ์ถ”๋ก  ์ฟผ๋ฆฌ์— ํƒ์›”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์—ฌ๋Ÿฌ ๋ฌธ์„œ์— ๊ฑธ์นœ ์‚ฌ์‹ค์„ ์—ฐ๊ฒฐํ•ด์•ผ ํ•˜๋Š” ์ฟผ๋ฆฌ๋กœ, ๋‹จ์ผ ์ฒญํฌ์— ์™„์ „ํ•œ ๋‹ต๋ณ€์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์ˆ˜ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์œผ๋กœ๋Š” ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

Graph RAG์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„

Graph RAG๋Š” ์‚ฌ์‹ค ์ •ํ™•๋„๋ฅผ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋งŒ(์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋งŽ์€ ์ฟผ๋ฆฌ์—์„œ ํ• ๋ฃจ์‹œ๋„ค์ด์…˜์„ ์ตœ๋Œ€ 40% ๊ฐ์†Œ), ์„ฑ์ˆ™ํ•œ ์—”ํ‹ฐํ‹ฐ ์ถ”์ถœ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๋…ธ์ด์ฆˆ๊ฐ€ ๋งŽ์€ ์ถ”์ถœ์€ ๋…ธ์ด์ฆˆ๊ฐ€ ๋งŽ์€ ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, ๋‚˜์ด๋ธŒ RAG๋ณด๋‹ค ๊ฒฐ๊ณผ๊ฐ€ ์•…ํ™”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

RAG ์‹œ์Šคํ…œ ํ‰๊ฐ€: ์ค‘์š”ํ•œ ์ง€ํ‘œ

RAG ํ‰๊ฐ€๋Š” ๊ฒ€์ƒ‰ ์ง€ํ‘œ์™€ ์ƒ์„ฑ ์ง€ํ‘œ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. ์žฅ์•  ์ง„๋‹จ์„ ์œ„ํ•ด์„œ๋Š” ๋‘ ๊ฐ€์ง€๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ธก์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฒ€์ƒ‰ ์ง€ํ‘œ:

  • Recall@k: ๊ด€๋ จ ๋ฌธ์„œ๊ฐ€ ์ƒ์œ„ k๊ฐœ ๊ฒฐ๊ณผ์— ํฌํ•จ๋˜์—ˆ๋Š”๊ฐ€?
  • MRR(Mean Reciprocal Rank): ์ฒซ ๋ฒˆ์งธ ๊ด€๋ จ ๊ฒฐ๊ณผ์˜ ์ˆœ์œ„๋Š” ์–ผ๋งˆ๋‚˜ ๋†’์€๊ฐ€?
  • NDCG: ๋žญํ‚น์ด ์ด์ƒ์ ์ธ ๊ด€๋ จ์„ฑ ์ˆœ์„œ์™€ ์ผ์น˜ํ•˜๋Š”๊ฐ€?

์ƒ์„ฑ ์ง€ํ‘œ:

  • ์ถฉ์‹ค๋„(Faithfulness): ๋‹ต๋ณ€์ด ๊ฒ€์ƒ‰๋œ ์ปจํ…์ŠคํŠธ์˜ ์ •๋ณด๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๊ฐ€? (ํ• ๋ฃจ์‹œ๋„ค์ด์…˜ ์ธก์ •)
  • ๋‹ต๋ณ€ ๊ด€๋ จ์„ฑ(Answer relevance): ๋‹ต๋ณ€์ด ์›๋ž˜ ์งˆ๋ฌธ์— ๋Œ€์‘ํ•˜๊ณ  ์žˆ๋Š”๊ฐ€?
  • ์ปจํ…์ŠคํŠธ ์ •๋ฐ€๋„(Context precision): ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๊ฐ€ ์‹ค์ œ๋กœ ๋‹ต๋ณ€์— ํ™œ์šฉ๋˜์—ˆ๋Š”๊ฐ€?

Ragas์™€ DeepEval ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” LLM-as-judge ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋Ÿฌํ•œ ํ‰๊ฐ€๋ฅผ ์ž๋™ํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘ ๋นˆ์ถœ ์งˆ๋ฌธ์—์„œ๋Š” RAG ํ‰๊ฐ€ ์„ค๊ณ„๊ฐ€ ์ ์  ๋” ๋งŽ์ด ํฌํ•จ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, RAG ์‹œ์Šคํ…œ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ์ธก์ •ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์„ค๋ช…์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์˜ ์žฅ์•  ๋ชจ๋“œ์™€ ๋””๋ฒ„๊น…

RAG ์‹œ์Šคํ…œ์€ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ํŒจํ„ด์œผ๋กœ ์žฅ์• ๋ฅผ ์ผ์œผํ‚ต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ์•„๋Š” ๊ฒƒ์€ ๋ฉด์ ‘๊ณผ ์‹ค์ œ ๋ฐฐํฌ ๋ชจ๋‘์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์˜ค์—ผ์€ ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„ ๊ด€๋ จ ์‹ ํ˜ธ๊ฐ€ ํฌ์„๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. LLM์ด 10๊ฐœ์˜ ์ฒญํฌ๋ฅผ ๋ฐ›์ง€๋งŒ ์œ ์šฉํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ๊ฒƒ์€ 2๊ฐœ๋ฟ์ธ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ํ•ด๊ฒฐ์ฑ…: ๋ฆฌ๋žญ์ปค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„ํ„ฐ๋งํ•˜๊ณ  ๋ฆฌํŠธ๋ฆฌ๋ฒ„์˜ top-k๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค.

์ฒญํ‚น ์•„ํ‹ฐํŒฉํŠธ๋Š” ๊ณ ์ • ํฌ๊ธฐ ๋ถ„ํ• ์ด ๋ฌธ์žฅ, ํ…Œ์ด๋ธ”, ์ฝ”๋“œ ๋ธ”๋ก์„ ์ค‘๊ฐ„์—์„œ ์ž˜๋ผ๋‚ผ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰๋œ ์ฒญํฌ๋Š” ๊ตฌ๋ฌธ์ ์œผ๋กœ ๋ถˆ์™„์ „ํ•˜๊ณ  ์‹œ๋งจํ‹ฑ์ ์œผ๋กœ ๋ฌด์šฉํ•ฉ๋‹ˆ๋‹ค. ์‹œ๋งจํ‹ฑ ์ฒญํ‚น ๋˜๋Š” ๋ฌธ์„œ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ คํ•œ ๋ถ„ํ• (ํ—ค๋”, ๋‹จ๋ฝ, ์ฝ”๋“œ ํŽœ์Šค ์กด์ค‘)๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž„๋ฒ ๋”ฉ ๋“œ๋ฆฌํ”„ํŠธ๋Š” ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Œ์—๋„ ๋ฒกํ„ฐ ์Šคํ† ์–ด์— ์ด์ „ ๋ชจ๋ธ์˜ ์ž„๋ฒ ๋”ฉ์ด ๊ทธ๋Œ€๋กœ ์ €์žฅ๋˜์–ด ์žˆ์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ ๋ชจ๋ธ๋กœ ์ธ์ฝ”๋”ฉ๋œ ์ฟผ๋ฆฌ๊ฐ€ ์ด์ „ ๋ชจ๋ธ๋กœ ๊ตฌ์ถ•๋œ ๋ฒกํ„ฐ ๊ณต๊ฐ„์„ ๊ฒ€์ƒ‰ํ•˜๊ฒŒ ๋˜์–ด ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์ด ์ €ํ•˜๋ฉ๋‹ˆ๋‹ค. ํ•ด๊ฒฐ์ฑ…: ๋ชจ๋ธ ๋ณ€๊ฒฝ ํ›„ ์ „์ฒด ์ฝ”ํผ์Šค๋ฅผ ์žฌ์ž„๋ฒ ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

๋…ธํ›„ํ™”๋œ ์ธ๋ฑ์Šค๋Š” ์ธ์ œ์Šค์ฒœ ํŒŒ์ดํ”„๋ผ์ธ์ด ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ์— ๋’ค์ฒ˜์ ธ ์˜ค๋ž˜๋œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋จธ์‹ ๋Ÿฌ๋‹ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ด๋ฅผ ํ•™์Šต-์„œ๋น™ ์Šคํ์— ๋น„์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ์‹œ์Šคํ…œ์ด ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ ๋ถ„ํฌ์™€ ๋‹ค๋ฅธ ๊ฒƒ์„ ์ฐธ์กฐํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ฒฐ๋ก 

  • RAG๋Š” ๊ฒ€์ƒ‰(์ง€์‹ ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰)๊ณผ LLM ์ƒ์„ฑ์„ ๊ฒฐํ•ฉํ•˜์—ฌ, ๋ชจ๋ธ ์žฌํ•™์Šต ์—†์ด ์‚ฌ์‹ค์— ๊ธฐ๋ฐ˜ํ•œ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
  • ์ฒญํ‚น ์ „๋žต์ด ๊ฒ€์ƒ‰ ํ’ˆ์งˆ์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ์‹œ๋งจํ‹ฑ ์ฒญํ‚น๊ณผ ๋ ˆ์ดํŠธ ์ฒญํ‚น์€ ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ๊ณ ์ • ํฌ๊ธฐ ๋ถ„ํ• ์„ ๋Šฅ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
  • ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ฒ€์ƒ‰(๋ฐ€์ง‘ ๋ฒกํ„ฐ + ํฌ์†Œ BM25)๊ณผ Reciprocal Rank Fusion์€ ํ”„๋กœ๋•์…˜์˜ ๊ธฐ๋ณธ ์„ค์ •์ด๋ฉฐ, ์ˆœ์ˆ˜ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰์œผ๋กœ๋Š” ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋Š” ์–ดํœ˜ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค
  • ํฌ๋กœ์Šค ์ธ์ฝ”๋” ๋ฆฌ๋žญ์ปค๋Š” ๊ด‘๋ฒ”์œ„ํ•œ ๊ฒ€์ƒ‰ ํ›„ ์ •๋ฐ€๋„ ๊ณ„์ธต์„ ์ถ”๊ฐ€ํ•˜๋ฉฐ, ์†Œ์ˆ˜์˜ ํ›„๋ณด ์„ธํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ ˆ์ดํ„ด์‹œ๋ฅผ ํ—ˆ์šฉ ๋ฒ”์œ„ ๋‚ด๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค
  • ์—์ด์ „ํ‹ฑ RAG(๊ฒ€์ƒ‰, ํ‰๊ฐ€, ์žฌ์ž‘์„ฑ, ์žฌ์‹œ๋„)์™€ Graph RAG(์—”ํ‹ฐํ‹ฐ-๊ด€๊ณ„ ์ถ”์ถœ)๋Š” 2026๋…„์˜ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์•„ํ‚คํ…์ฒ˜ ๋ฐœ์ „์ด๋ฉฐ, ๋‚˜์ด๋ธŒ RAG๊ฐ€ ์‹คํŒจํ•˜๋Š” ๋ณต์žกํ•œ ๋ฉ€ํ‹ฐํ™‰ ์ฟผ๋ฆฌ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค
  • ํ‰๊ฐ€๋Š” ๊ฒ€์ƒ‰ ์ง€ํ‘œ(Recall@k, MRR)์™€ ์ƒ์„ฑ ์ง€ํ‘œ(์ถฉ์‹ค๋„, ๋‹ต๋ณ€ ๊ด€๋ จ์„ฑ)๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ, ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์œ„์น˜๋ฅผ ์ง„๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
  • ๊ฐ€์žฅ ํ”ํ•œ ํ”„๋กœ๋•์…˜ ์žฅ์•  -- ์ปจํ…์ŠคํŠธ ์˜ค์—ผ, ์ฒญํ‚น ์•„ํ‹ฐํŒฉํŠธ, ์ž„๋ฒ ๋”ฉ ๋“œ๋ฆฌํ”„ํŠธ, ๋…ธํ›„ํ™”๋œ ์ธ๋ฑ์Šค -- ๋Š” ์‹๋ณ„๋˜๋ฉด ๋ชจ๋‘ ๊ฐ„๋‹จํžˆ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

ํƒœ๊ทธ

#RAG
#LLM
#data-science
#vector-database
#embeddings

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

Machine Learning Algorithms Guide

๋จธ์‹ ๋Ÿฌ๋‹ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์™„๋ฒฝ ํ•ด์„ค: ๊ธฐ์ˆ  ๋ฉด์ ‘์„ ์œ„ํ•œ ์ข…ํ•ฉ ๊ฐ€์ด๋“œ

๋จธ์‹ ๋Ÿฌ๋‹ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ์ˆ  ๋ฉด์ ‘ ๊ฐ€์ด๋“œ. ์„ ํ˜• ๋ชจ๋ธ, ์˜์‚ฌ๊ฒฐ์ • ํŠธ๋ฆฌ, ์•™์ƒ๋ธ”, ํด๋Ÿฌ์Šคํ„ฐ๋ง, ํ‰๊ฐ€ ์ง€ํ‘œ, ์ •๊ทœํ™”๋ฅผ scikit-learn ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ฒด๊ณ„์ ์œผ๋กœ ํ•ด์„คํ•ฉ๋‹ˆ๋‹ค.

์‹ ๊ฒฝ๋ง, ํ†ต๊ณ„ ์ฐจํŠธ, Python ์ฝ”๋“œ๊ฐ€ ์–ด๋‘์šด ๋ฐฐ๊ฒฝ์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘ ์งˆ๋ฌธ

2026๋…„ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘ ์งˆ๋ฌธ 25์„ 

ํ†ต๊ณ„, ๋จธ์‹ ๋Ÿฌ๋‹, ํ”ผ์ฒ˜ ์—”์ง€๋‹ˆ์–ด๋ง, ๋”ฅ๋Ÿฌ๋‹, SQL, ์‹œ์Šคํ…œ ์„ค๊ณ„๋ฅผ ๋ง๋ผํ•˜๋Š” ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ๋ฉด์ ‘ ์งˆ๋ฌธ 25์„  โ€” Python ์ฝ”๋“œ ์˜ˆ์ œ์™€ ์‹ฌ์ธต ํ•ด์„ค ํฌํ•จ.

PyTorch vs TensorFlow ๋”ฅ๋Ÿฌ๋‹ ํ”„๋ ˆ์ž„์›Œํฌ ๋น„๊ต 2026

PyTorch vs TensorFlow 2026๋…„ ๋น„๊ต: ์–ด๋–ค ๋”ฅ๋Ÿฌ๋‹ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ

2026๋…„ PyTorch vs TensorFlow์„ ์„ฑ๋Šฅ ๋ฒค์น˜๋งˆํฌ, ๋ฐฐํฌ, ์—์ฝ”์‹œ์Šคํ…œ, ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ ๋น„๊ตํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉํ•œ ๋”ฅ๋Ÿฌ๋‹ ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ์„ ๋•๋Š” ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.