신선함을 잃지 않는 RAG: 최신 웹 데이터와 갱신 루프
RAG 시스템은 마지막 크롤링만큼 신선합니다. 검색 수학은 쉬운 부분이고, 어려운 부분은 각 소스가 고유의 시간에 따라 다시 크롤링되고, 다시 청크되고, 다시 임베딩되어 답변이 신선함을 잃기 전에 갱신 루프를 만드는 것입니다.
검색 증강 생성은 이제 기업 AI 시스템의 절반 정도에 도입되었습니다 — 최근 조사에서 채택률이 51%로 증가했으며, 이는 1년 전 31%에서 증가한 수치입니다. 그러나 대부분의 RAG 프로젝트는 동일한 실패 모드를 공유합니다: 한 번 채워지고 나면 세상은 계속 변화합니다. 검색 수학(쿼리를 임베딩하고, 벡터를 검색하고, 상위 청크를 프롬프트에 넣는 것)은 쉽고 잘 문서화된 부분입니다. 어려운 부분, 즉 당신의 어시스턴트가 신뢰할 수 있는지 아니면 자신감 있게 잘못된지를 결정하는 부분은 신선함입니다 — 각 소스가 답변이 신선함을 잃기 전에 다시 크롤링되고, 다시 청크되고, 다시 임베딩되는 파이프라인입니다. 이 가이드는 마크다운 우선 수집과 소스별 TTL을 핵심으로 하는 갱신 루프를 구축하는 방법에 관한 것입니다.
신선하지 않은 검색은 검색하지 않는 것보다 나쁩니다
RAG의 전체 약속은 모델이 고정된 학습 데이터 대신 검색된 증거로부터 답변한다는 것입니다. 신선함을 깨면 약속도 깨집니다: 지원 봇이 지난 분기의 반품 정책을 인용하고, 가격 어시스턴트가 중단된 수치를 인용하며, 연구 보조자가 답변을 변경한 업데이트를 놓칩니다. 더 나쁜 것은, 검색된 청크가 권위적으로 보이기 때문에 모델이 완전한 자신감으로 이를 진술한다는 것입니다. 신선하지 않은 인덱스는 소리 없이 실패하며, 이는 가장 위험한 종류입니다. 코퍼스를 최신 상태로 유지하는 것은 선택 사항이 아닙니다; 이는 추가 단계를 통해 근거를 제공하는 것과 환각하는 것의 차이입니다.
마크다운을 수집하고, 원시 HTML은 수집하지 마세요
모든 다운스트림의 품질은 수집 시 결정됩니다. 원시 HTML은 검색에 재앙입니다 — 탐색, 쿠키 배너, 푸터 및 스크립트 태그가 유사성 검색을 오염시키고 컨텍스트 토큰을 낭비하는 노이즈 청크가 됩니다. 깨끗한 접근 방식은 마크다운 우선입니다: 각 페이지를 크롤링하고 주 콘텐츠만 구조화된 마크다운으로 추출하여, 청크되기 전에 보일러플레이트를 버립니다. 우리의 Scraper API는 마크다운을 직접 반환하고, JavaScript를 필요에 따라 렌더링하며, IP를 회전시켜 차단된 페이지가 코퍼스에 구멍을 남기지 않도록 합니다 — 이는 대부분의 RAG 스택이 간신히 유지하는 불안정한 Puppeteer-and-BeautifulSoup 수집 레이어를 대체합니다. web-data-for-LLMs 엔드포인트는 크롤링, 정리 및 구조화를 하나의 호출로 감싸며, 바로 이 목적을 위해 설계되었습니다.
import requests
# markdown-first ingestion: clean text, JS rendered, IPs rotated
def fetch_markdown(url):
r = requests.post(
"https://api.quantumproxies.io/scrape",
headers={"Authorization": f"Bearer {QP_KEY}"},
json={"url": url, "format": "markdown", "render": True},
timeout=60,
)
doc = r.json()
return {
"url": url,
"markdown": doc["markdown"], # no nav, no boilerplate
"fetched_at": doc["fetchedAt"], # timestamp for TTL logic
"content_hash": doc["contentHash"], # skip re-embed if unchanged
}
작게 청크하고, 메타데이터를 포함하세요
깨끗한 마크다운을 얻은 후, 이를 대략 300에서 500 토큰의 청크로 나누세요 — 검색된 청크가 주제에 밀접하게 관련되도록 충분히 작고, 일관된 생각을 유지할 만큼 충분히 큽니다. 문장 및 제목 경계를 존중하는 재귀적 분할기를 사용하여 중간 단어를 자르지 않도록 하세요. 중요한, 종종 생략되는 단계는 메타데이터입니다: 모든 청크는 소스 URL, 타임스탬프 및 콘텐츠 해시를 포함해야 합니다. 이 메타데이터가 신선함을 가능하게 합니다 — 검색은 최신성으로 필터링할 수 있으며, 갱신 루프는 방금 변경된 소스에 속하는 청크를 식별할 수 있습니다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1800, chunk_overlap=200, # ~400 tokens per chunk
)
def to_chunks(doc):
parts = splitter.split_text(doc["markdown"])
return [{
"text": p,
"source": doc["url"],
"fetched_at": doc["fetched_at"], # drives recency filtering
"ttl": ttl_for(doc["url"]), # per-source refresh clock
} for p in parts]
현재 모델 — text-embedding-3-large, Cohere 모델 또는 오픈 Sentence-BERT 변형 —로 이러한 청크를 임베딩하고, 벡터를 Pinecone, Weaviate, Milvus 또는 FAISS와 같은 벡터 데이터베이스에 메타데이터와 함께 저장하세요. 유사성 임계값을 조정하고, 특히 메타데이터 필터링을 활성화하여 쿼리가 예를 들어 최근 30일 이내에 가져온 청크만을 요구할 수 있도록 하세요.

소스별 TTL: 신선함의 핵심
대부분의 팀이 저지르는 실수는 모든 것을 하나의 일정에 따라 갱신하는 것입니다 — 가격에는 너무 느리고 참조 문서에는 낭비적인 야간 전체 재크롤링. 서로 다른 소스는 서로 다른 속도로 노후화되므로 각 소스의 실제 콘텐츠 이동 속도에 맞는 수명을 부여하세요. 실시간 가격 및 재고는 몇 분의 TTL이 필요할 수 있으며, 뉴스, 목록 및 포럼 스레드는 한 시간, 순위 및 카탈로그는 하루, 문서, 정책 및 참조 자료는 일주일 이상이 필요할 수 있습니다. 각 청크에 소스의 TTL을 태그하고, 스케줄러는 시계가 만료된 소스만 재크롤링합니다. 콘텐츠 해시는 효율성 밸브입니다: 재크롤링된 페이지가 바이트 단위로 동일하다면 임베딩 단계를 완전히 건너뛰고 타임스탬프만 재설정하세요.
import time
TTL = {"prices": 300, "news": 3600, "catalog": 86400, "docs": 604800}
def needs_refresh(chunk, now=None):
now = now or time.time()
return (now - chunk["fetched_at"]) > chunk["ttl"]
# refresh only what has expired; re-embed only what actually changed
for src in due_sources(TTL):
doc = fetch_markdown(src.url)
if doc["content_hash"] != src.last_hash:
reindex(to_chunks(doc)) # re-chunk + re-embed
src.touch(doc["content_hash"]) # reset the clock either way
이것이 데모와 프로덕션 시스템을 구분하는 루프입니다. 검색을 예측 가능하게 유지하고, 실제로 세상이 변한 곳에만 컴퓨팅을 사용하며, 소스별로 실제 신선함 보장을 제공할 수 있습니다. 라이브 데이터에 LLM을 기반으로 하는 더 넓은 그림을 보려면 LLMs에 신선한 웹 데이터를 제공하는 방법 가이드를 참조하고, 단일 사이트를 인용 가능한 지식 베이스로 변환하는 방법은 지원 봇 지식 베이스 구축을 참조하세요.

유사성만이 아닌 커버리지를 위해 검색하세요
쿼리 시 마지막 업그레이드. 순수 벡터 유사성은 제품 코드, 오류 문자열, 고유 명사와 같은 정확한 용어를 놓칩니다 — 키워드 검색이 이를 정확히 찾아냅니다. 하이브리드 검색은 두 가지를 혼합한 후 최신성 필터를 적용하여 시간에 민감한 쿼리가 신선한 청크를 선호하도록 합니다. 검색된 청크를 모든 답변과 함께 기록하여 모델이 실제로 사용한 증거를 감사할 수 있도록 하세요; 그 추적 가능성이 RAG를 설명 가능하게 만들고, 사용자가 발견하기 전에 신선하지 않은 청크를 잡아내는 방법입니다. LLM 기반 추출에서의 기술은 검색된 페이지에서 구조화된 필드를 필요로 할 때 여기서 잘 작동합니다.
자주 묻는 질문
RAG 파이프라인의 데이터를 신선하게 유지하려면 어떻게 해야 하나요?
각 소스에 콘텐츠 변경 속도에 맞는 수명을 할당하고, 모든 청크에 소스 URL과 타임스탬프를 태그하며, 만료된 소스만 재크롤링하는 스케줄러를 실행하세요. 실제로 변경되지 않은 페이지의 재임베딩을 건너뛰기 위해 콘텐츠 해시를 사용하고, 검색 시 최신성 필터링을 활성화하여 시간에 민감한 쿼리가 신선한 청크를 선호하도록 하세요.
RAG를 위해 마크다운을 수집하는 대신 HTML을 수집하는 이유는 무엇인가요?
원시 HTML은 탐색, 배너, 푸터 및 스크립트를 포함하여 유사성 검색을 오염시키고 컨텍스트 토큰을 낭비하는 노이즈 청크가 됩니다. 마크다운 우선 수집은 주 콘텐츠만 추출하여 청크가 깨끗하고 주제에 맞도록 합니다. 렌더링된 마크다운을 반환하는 스크래핑 API는 대부분의 RAG 스택이 의존하는 불안정한 사용자 정의 구문 분석을 제거합니다.
RAG에 적합한 청크 크기는 얼마인가요?
대략 300–500 토큰이 일반적인 적정 크기입니다: 검색된 청크가 밀접하게 관련성을 유지할 만큼 작고, 완전한 생각을 유지할 만큼 큽니다. 문장 및 제목 경계를 존중하는 재귀적 분할기를 사용하고, 경계에서 컨텍스트가 손실되지 않도록 약간의 중첩을 추가하며, 모든 청크에 소스 메타데이터를 첨부하세요.
RAG 데이터 파이프라인을 구축하는 데 프록시가 필요한가요?
어떤 규모로든 열린 웹을 크롤링한다면, 그렇습니다 — 사이트는 하나의 IP에서 반복되는 요청을 제한하고 차단하여 코퍼스에 간격을 남깁니다. 스크래핑 API 또는 회전 프록시는 크롤링을 신뢰할 수 있게 유지하여 소스가 완전히 갱신되고 일정에 맞춰 새로 고쳐지도록 하며, 이는 소스별 TTL이 의존하는 것입니다.
RAG는 신선함에 따라 생존하거나 실패합니다. 깨끗한 마크다운을 수집하고, 메타데이터와 함께 작은 청크로 나누고, 소스별 TTL에서 재임베딩을 추진하여 각 소스가 고유의 시계에 따라 갱신되도록 하세요. 그 루프를 한 번 구축하면 당신의 어시스턴트는 지난달의 스냅샷이 아닌 오늘의 웹에서 답변합니다.