단순한 RAG를 넘어서
기본 검색의 한계
단순한 RAG를 넘어서은(는) CoddyKit의 무료 AI Prompt Engineering 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Prompt Engineering 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Prompt Engineering 강의에는 총 4개의 강의가 포함되어 있습니다.
순진한 RAG의 작동 방식
순진한 RAG는 기준선입니다. 문서를 청크로 나누고, 임베딩하고, 벡터를 저장한 다음, 쿼리를 임베딩하고, 코사인 유사도로 상위 k개를 검색하고, 청크를 프롬프트에 넣어 생성합니다. 강력한 출발점이지만 규모가 커지면 예측 가능한 방식으로 실패합니다.
이러한 실패 양상을 이해하는 것은 이 과정에서 다루는 고급 기법(재순위화, 압축, 쿼리 재작성)을 익히기 위한 전제 조건입니다.
def naive_rag(query, k=5):
q = embed(query)
chunks = vector_store.search(q, k) # top-k by cosine
context = '\n\n'.join(c.text for c in chunks)
return llm('Context:\n' + context + '\n\nQ: ' + query)검색 재현율과 정밀도
순진한 상위 k개 검색은 원시 벡터 유사도를 최적화하지만, 이는 관련성과 표면적인 의미적 유사성을 뒤섞습니다. 작은 k는 답을 놓칠 위험이 있고(낮은 재현율), 큰 k는 방해 정보로 문맥을 넘치게 합니다(낮은 정밀도).
검색을 이끄는 임베딩 유사도는 실제 관련성을 대략적으로 나타내는 지표일 뿐이며, 여러 후속 문제의 근본 원인이 됩니다.
# The core dilemma
# small k -> may miss the gold chunk (recall problem)
# large k -> distractors crowd context (precision + cost problem)
# Advanced RAG decouples 'retrieve many' from 'use few'임베딩 불일치 문제
쿼리와 문서는 서로 다른 언어적 문체로 작성되는 경우가 많습니다. 짧은 질문과 긴 서술형 문단이 그 예입니다. 이중 인코더의 임베딩은 관련 있는 답변이 질문과 다르게 표현되었다는 이유로 답변을 질문에서 멀리 배치할 수 있습니다(어휘 불일치 문제).
이 때문에 검색 전에 쿼리를 문서 공간에 맞게 변형하는 쿼리 재작성이나 HyDE 같은 기법이 사용됩니다.
# Query: 'how do I revoke a token?'
# Doc: 'Token invalidation is performed via the /sessions endpoint.'
# Lexically and semantically distant -> bi-encoder may miss it
sim = cos(embed('how do I revoke a token?'),
embed('Token invalidation via /sessions')) # may be low중간에 묻히는 정보
올바른 청크를 검색했더라도 많은 청크를 한꺼번에 넣으면 중간 소실 효과가 발생합니다. 모델이 긴 문맥의 가운데에 배치된 내용에 충분히 주의를 기울이지 않는 현상입니다. 10개 중 3번째 순위에 묻힌 올바른 청크는 사실상 무시될 수 있습니다.
이 때문에 재순위화로 가장 좋은 청크를 모델이 주의를 기울이는 위치에 놓고, 압축으로 문맥을 줄여 어떤 내용도 묻히지 않게 합니다.
# Retrieval rank != attention rank
# Place the highest-relevance chunk at the START or END,
# never stranded in the middle of a large concatenation.방해 정보에 대한 민감성
LLM은 무관한 문맥에 민감합니다. 그럴듯하지만 틀린 청크를 추가하면 올바른 청크가 함께 있어도 답변이 잘못된 방향으로 끌려갈 수 있습니다. 검색된 문맥이 많다고 해서 항상 더 좋은 것은 아닙니다.
따라서 정밀도가 중요합니다. 밀접하게 선별하고 재순위화한 압축 문맥이 느슨하게 관련된 청크를 대량으로 넣는 것보다 나은 경우가 많습니다.
# Empirically: appending a single highly-similar but WRONG chunk
# can flip a previously-correct answer. RAG quality depends on
# keeping distractors OUT, not just getting the gold chunk IN.청크 분할의 문제
고정 크기 분할은 문장 중간에서 아이디어를 끊고, 주장을 근거와 분리하며, 구조적 문맥(어느 섹션인지, 어느 문서인지)을 제거합니다. 단독으로 읽으면 자연스러운 청크도 주변 내용이 없으면 쓸모없거나 오해를 불러일으킬 수 있습니다.
고급 처리 과정에서는 의미를 보존하기 위해 구조 인식형 분할, 중첩, 상위 문서 확장, 메타데이터를 사용합니다.
def structure_aware_chunks(doc, max_tokens=400, overlap=50):
sections = split_by_headings(doc) # respect document structure
chunks = []
for sec in sections:
for c in sliding_window(sec.text, max_tokens, overlap):
chunks.append(Chunk(c, meta={'section': sec.title}))
return chunks의미 기반 검색만으로 생기는 누락
순수한 밀집 검색은 정확한 일치가 필요한 경우를 놓칩니다. 식별자, 오류 코드, 드문 고유 명사, API 이름이 그 예입니다. 사용자는 바로 이런 경우에 문자 그대로의 정확성을 기대합니다. 하이브리드 검색은 밀집 검색의 의미 신호와 희소 검색의 BM25/키워드 신호를 결합하여 두 가지 요구를 모두 처리합니다.
상호 순위 결합은 가중치를 조정하지 않고 두 순위 목록을 병합하는 간단하고 견고한 방법입니다.
def rrf(dense_ranks, sparse_ranks, k0=60):
scores = {}
for ranks in (dense_ranks, sparse_ranks):
for rank, doc_id in enumerate(ranks):
scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k0 + rank)
return sorted(scores, key=scores.get, reverse=True)오래되고 검증할 수 없는 문맥
순진한 RAG에는 최신성이나 출처 정보에 대한 개념이 없습니다. 오래된 문서를 검색할 수 있고, 주장을 출처에 연결하는 기본 기능도 없으므로 신뢰성이 떨어지며 환각을 감지하기도 어렵습니다.
고급 시스템은 메타데이터(타임스탬프, 출처, 버전)를 추가하고 이를 기준으로 필터링하며, 답변을 검증할 수 있도록 생성기가 청크 식별자를 인용하도록 요구합니다.
def filtered_retrieve(q, after_date):
cands = vector_store.search(embed(q), k=50)
fresh = [c for c in cands if c.meta['date'] >= after_date]
return fresh # then re-rank; generator must cite c.id피드백도 적응도 없음
순진한 RAG는 아무런 판단 없이 검색합니다. 검색이 실패한 시점을 알 수 없고, 검색이 전혀 필요하지 않은 경우를 판단할 수도 없으며, 반복해서 개선할 수도 없습니다. 고급 패턴은 관련성 검사, 조건부 검색, 그리고 결과가 부실해 보일 때 쿼리를 다시 구성하는 다단계 에이전트 기반 검색을 추가합니다.
처리 과정은 한 번의 순방향 실행이 아니라 자기 평가를 수행하는 반복 구조가 됩니다.
def adaptive_rag(q):
chunks = retrieve(q)
if relevance_score(q, chunks) < 0.4:
q2 = rewrite_query(q) # reformulate and retry
chunks = retrieve(q2)
if relevance_score(q, chunks) < 0.4:
return 'I could not find this in the sources.'
return generate(q, chunks)고급 RAG 구성 요소
이러한 실패를 종합하면 고급 처리 과정은 메타데이터를 포함한 구조 인식형 분할, 높은 재현율의 하이브리드 검색, 정밀도를 위한 교차 인코더 재순위화기, 문맥을 맞추고 집중시키기 위한 문맥 압축, 불일치를 해결하는 쿼리 재작성 및 HyDE, 인용을 포함한 관련성 게이트를 차례로 구성합니다.
다음 수업에서는 각 구성 요소를 만듭니다. 핵심 흐름은 폭넓게 검색한 다음 적극적으로 필터링하고 정제하는 것입니다.
def advanced_rag(q):
cands = hybrid_retrieve(rewrite_query(q), k=50) # high recall
top = rerank(q, cands)[:8] # precision
ctx = compress(q, top) # focus + fit
return generate_with_citations(q, ctx) # verifiable최적화 전에 측정하십시오
새로운 장치를 추가하기 전에 실제로 어떤 실패를 겪고 있는지 진단하십시오. 검색 재현율@k(정답 청크가 검색되었는가)를 답변 정확도(생성기가 해당 청크를 사용하는가)와 따로 측정하십시오. 재현율 문제와 정밀도 문제에는 서로 다른 해결책이 필요합니다.
두 부분을 모두 계측하십시오. 실제 문제가 청크 분할이나 쿼리 불일치인데도 재순위화기를 덧붙이지 마십시오.
def diagnose(eval_set):
return {
'recall@5': recall_at_k(eval_set, k=5), # retrieval health
'recall@50': recall_at_k(eval_set, k=50), # ceiling with rerank
'answer_acc': answer_accuracy(eval_set), # generation health
}빠른 확인
RAG의 실패 원인을 진단하십시오.
복습
핵심 요점:
- 순진한 RAG(청크 분할, 임베딩, 상위 k개 검색, 삽입, 생성)는 예측 가능한 실패를 보이는 강력한 기준선입니다.
- 이중 인코더의 유사도는 관련성을 대략적으로 나타내는 지표이며, 쿼리와 문서의 문체 불일치는 재현율을 떨어뜨립니다.
- 문맥이 많다고 해서 더 좋은 것은 아닙니다. 방해 정보에 대한 민감성과 중간 소실 현상으로 인해 k가 커질수록 답변이 저하됩니다.
- 청크 분할의 문제, 의미 기반 검색만으로 생기는 누락, 오래된 정보, 피드백 부족이 모두 순진한 RAG를 제한합니다.
- 고급 RAG는 폭넓게 검색한 다음 필터링합니다. 하이브리드 검색, 재순위화, 압축, 쿼리 재작성, 관련성 게이트를 사용하며, 최적화하기 전에 재현율과 답변 정확도를 따로 측정해야 합니다.
AI 튜터와 함께 AI Prompt Engineering을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 53
- 레슨
- 199
자주 묻는 질문
“단순한 RAG를 넘어서” 강의는 무료인가요?
네 — “단순한 RAG를 넘어서” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Prompt Engineering 강의 전체를 잠금 해제할 수 있습니다. AI Prompt Engineering 강의에는 총 4개의 강의가 포함되어 있습니다.
“단순한 RAG를 넘어서”에서 뭘 배우나요?
기본 검색의 한계 브라우저에서 직접 실행하는 실습 코드로 AI Prompt Engineering을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Prompt Engineering을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Prompt Engineering은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“단순한 RAG를 넘어서” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Prompt Engineering 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Prompt Engineering 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 단순한 RAG를 넘어서
- 검색된 청크 재순위화
- 컨텍스트 압축
- 질의 재작성과 HyDE