본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 25. · 3 Views
Corrective RAG 완벽 가이드
RAG 시스템의 한계를 극복하는 CRAG(Corrective RAG) 기법을 실전 예제와 함께 학습합니다. 검색 결과 검증부터 외부 웹 검색 연동까지, 신뢰할 수 있는 AI 시스템 구축 방법을 초급자 눈높이에서 설명합니다.
목차
1. 검색 결과 검증
어느 날 이지원 씨가 회사에서 RAG 시스템을 운영하던 중 심각한 문제를 발견했습니다. "이상하다, 분명 관련 문서가 있는데 왜 자꾸 엉뚱한 답변을 하지?" 선배 개발자 최민수 씨가 다가와 화면을 살펴보더니 고개를 끄덕였습니다.
"아, 검색 결과의 품질을 검증하지 않아서 생긴 문제네요."
검색 결과 검증은 RAG 시스템이 가져온 문서가 실제로 질문과 관련이 있는지 판단하는 과정입니다. 마치 도서관에서 책을 찾을 때 사서가 건네준 책이 정말 내가 원하는 내용인지 확인하는 것과 같습니다.
이 검증 단계를 거치면 잘못된 정보로 답변하는 실수를 크게 줄일 수 있습니다.
다음 코드를 살펴봅시다.
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
# 검증용 프롬프트 템플릿
verification_prompt = PromptTemplate(
input_variables=["question", "document"],
template="""질문: {question}
문서: {document}
이 문서가 질문에 답하는 데 적합한가요? 'CORRECT', 'INCORRECT', 'AMBIGUOUS' 중 하나로 답하세요."""
)
# 검증 수행
def verify_document(question, document):
llm = ChatOpenAI(temperature=0)
chain = verification_prompt | llm
result = chain.invoke({"question": question, "document": document})
return result.content.strip()
이지원 씨는 입사 6개월 차 백엔드 개발자입니다. 최근 회사에서 고객 상담 AI 챗봇을 개발하는 프로젝트를 맡게 되었습니다.
RAG 시스템을 열심히 구축했는데, 테스트를 해보니 이상한 답변이 자주 나왔습니다. "보험 약관이 어떻게 되나요?"라고 물으면 전혀 관련 없는 대출 상품 설명을 하는 경우도 있었습니다.
최민수 씨가 코드를 살펴보더니 문제를 정확히 짚어냈습니다. "지원씨, 여기 봐요.
벡터 데이터베이스에서 문서를 가져오기만 하고, 그 문서가 정말 질문과 관련 있는지 확인하지 않았네요." 그렇다면 검색 결과 검증이란 정확히 무엇일까요? 쉽게 비유하자면, 검색 결과 검증은 마치 레스토랑에서 음식을 주문했을 때 종업원이 가져온 요리가 정말 내가 주문한 것이 맞는지 확인하는 과정과 같습니다.
비빔밥을 주문했는데 파스타가 나왔다면 당연히 다시 요청해야겠죠. RAG 시스템도 마찬가지입니다.
검색된 문서가 질문과 실제로 부합하는지 한 번 더 확인하는 과정이 필요합니다. 검증 단계가 없던 초기 RAG 시스템은 어땠을까요?
개발자들은 벡터 유사도만 믿고 문서를 가져왔습니다. 코사인 유사도가 0.8 이상이면 무조건 관련 있다고 판단했죠.
문제는 벡터 공간에서 가까워도 실제 의미는 전혀 다를 수 있다는 점이었습니다. 더 큰 문제는 사용자가 잘못된 답변을 받게 되면서 시스템 신뢰도가 떨어진다는 것이었습니다.
고객 상담 챗봇이 엉뚱한 답을 하면 고객 불만이 쌓이고, 결국 사람이 직접 개입해야 하는 상황이 늘어났습니다. 바로 이런 문제를 해결하기 위해 CRAG의 검색 결과 검증 단계가 등장했습니다.
검색 결과 검증을 사용하면 문서의 적합성을 객관적으로 판단할 수 있습니다. 또한 애매한 경우를 별도로 처리함으로써 시스템의 안정성도 높일 수 있습니다.
무엇보다 잘못된 답변으로 인한 사용자 불만을 크게 줄일 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 3번째 줄에서 PromptTemplate을 정의합니다. 이 템플릿은 LLM에게 "이 문서가 질문에 답할 수 있나요?"라고 묻는 역할을 합니다.
6번째 줄의 template 부분이 핵심입니다. 질문과 문서를 함께 제시하고, CORRECT, INCORRECT, AMBIGUOUS 중 하나로 답하라고 명확히 지시합니다.
13번째 줄의 verify_document 함수에서는 temperature를 0으로 설정해 일관된 판단을 내리도록 합니다. 마지막으로 16번째 줄에서 결과를 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 금융 상담 챗봇을 개발한다고 가정해봅시다.
사용자가 "주택담보대출 금리가 어떻게 되나요?"라고 물었을 때, 벡터 검색으로 3개의 문서를 가져왔습니다. 하나는 주택담보대출 약관, 하나는 신용대출 안내, 하나는 예금 상품 설명서입니다.
검증 단계를 거치면 첫 번째 문서만 CORRECT로 판정되고, 나머지는 INCORRECT로 걸러집니다. 이렇게 하면 정확한 답변만 사용자에게 전달할 수 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 검증 기준을 너무 엄격하게 설정하는 것입니다.
모든 문서를 INCORRECT로 판정하면 답변 자체를 생성할 수 없습니다. 따라서 AMBIGUOUS 등급을 활용해 애매한 경우를 별도로 처리하는 전략이 필요합니다.
또한 검증에도 LLM 호출 비용이 든다는 점을 고려해 검색 단계에서부터 품질 좋은 문서를 가져오는 노력도 병행해야 합니다. 다시 이지원 씨의 이야기로 돌아가 봅시다.
최민수 씨의 설명을 들은 이지원 씨는 바로 코드를 수정했습니다. "아, 이제 이해했어요!
검색만 하면 끝이 아니라 한 번 더 걸러내는 과정이 필요했군요!" 검색 결과 검증을 제대로 구현하면 RAG 시스템의 정확도가 크게 향상됩니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 검증 프롬프트는 명확한 판단 기준을 제시해야 합니다
- AMBIGUOUS 등급을 활용해 애매한 경우를 별도 처리하세요
- 검증 비용을 고려해 초기 검색 품질도 함께 개선하세요
2. Web Fallback
검증 단계를 추가한 이지원 씨의 챗봇은 확실히 정확해졌습니다. 하지만 새로운 문제가 생겼습니다.
"최근 발표된 신상품에 대해 물어보면 '모르겠습니다'라고만 답하네요?" 최민수 씨가 웃으며 말했습니다. "당연하죠.
아직 업데이트되지 않은 정보는 내부 데이터베이스에 없으니까요. 이럴 때는 외부 웹에서 찾아오면 됩니다."
Web Fallback은 내부 데이터베이스에 적합한 문서가 없을 때 외부 웹 검색을 통해 정보를 보완하는 전략입니다. 마치 집에 있는 책에서 답을 찾지 못하면 인터넷 검색을 하는 것과 같습니다.
이 방법을 활용하면 최신 정보나 내부에 없는 지식도 제공할 수 있어 시스템의 활용도가 크게 높아집니다.
다음 코드를 살펴봅시다.
from langchain.utilities import GoogleSearchAPIWrapper
from langchain.document_loaders import WebBaseLoader
# 웹 검색 도구 초기화
search = GoogleSearchAPIWrapper()
def web_fallback(question, verification_result):
# 내부 문서가 부적합하면 웹 검색 수행
if verification_result in ["INCORRECT", "AMBIGUOUS"]:
search_results = search.results(question, num_results=3)
web_docs = []
for result in search_results:
loader = WebBaseLoader(result["link"])
docs = loader.load()
web_docs.extend(docs)
return web_docs
return None
이지원 씨는 검증 단계를 추가한 뒤 시스템이 훨씬 정확해진 것을 확인했습니다. 하지만 새로운 고민이 생겼습니다.
회사에서 이번 주에 새로운 적금 상품을 출시했는데, 아직 내부 문서 데이터베이스에는 등록되지 않았습니다. 고객이 "새로 나온 플러스 적금이 뭔가요?"라고 물으면 챗봇은 "죄송합니다, 관련 정보를 찾을 수 없습니다"라고만 답했습니다.
점심시간에 최민수 씨에게 이 문제를 털어놓았습니다. "민수 선배, 내부에 없는 정보는 어떻게 처리해야 할까요?" 최민수 씨는 커피를 한 모금 마시며 답했습니다.
"간단해요. 웹에서 찾아오면 되죠.
바로 Web Fallback 전략입니다." 그렇다면 Web Fallback이란 정확히 무엇일까요? 쉽게 비유하자면, Web Fallback은 마치 요리를 하다가 냉장고에 재료가 없으면 마트에 가서 사오는 것과 같습니다.
일단 집(내부 DB)에서 찾아보고, 없으면 밖(웹)에서 구해오는 것이죠. RAG 시스템도 마찬가지입니다.
내부 문서가 부족하거나 검증 단계에서 탈락하면, 자동으로 외부 웹 검색을 실행해 필요한 정보를 가져옵니다. Web Fallback이 없던 시절에는 어땠을까요?
개발자들은 내부 데이터베이스만 신뢰했습니다. 문서가 없으면 "모른다"고 답하는 것이 안전하다고 생각했죠.
하지만 실제 사용자는 최신 정보를 원합니다. 신제품 출시, 정책 변경, 최근 뉴스 등 실시간으로 업데이트되는 정보는 내부 DB만으로 커버할 수 없었습니다.
결국 챗봇의 활용도가 떨어지고, 사용자들은 다시 구글 검색으로 돌아갔습니다. 바로 이런 문제를 해결하기 위해 Web Fallback 전략이 등장했습니다.
Web Fallback을 사용하면 최신 정보를 실시간으로 제공할 수 있습니다. 또한 내부 DB 유지보수 부담도 줄어듭니다.
모든 정보를 미리 저장할 필요 없이, 필요할 때 외부에서 가져오면 되니까요. 무엇보다 사용자 만족도가 크게 향상된다는 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 4번째 줄에서 GoogleSearchAPIWrapper를 초기화합니다.
이것이 실제 웹 검색을 수행하는 도구입니다. 7번째 줄의 web_fallback 함수에서는 verification_result를 체크합니다.
9번째 줄이 핵심입니다. 검증 결과가 INCORRECT나 AMBIGUOUS이면 웹 검색을 시작합니다.
10번째 줄에서 상위 3개 검색 결과를 가져오고, 13-15번째 줄에서 각 링크의 내용을 로드해 문서로 만듭니다. 이렇게 수집한 웹 문서를 반환합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 뉴스 요약 서비스를 개발한다고 가정해봅시다.
사용자가 "오늘 주요 IT 뉴스가 뭐야?"라고 물으면, 내부 DB에는 어제까지의 뉴스만 있을 겁니다. 검증 단계에서 INCORRECT 판정을 받으면, Web Fallback이 작동해 실시간 뉴스 사이트를 크롤링합니다.
그리고 최신 기사를 가져와 답변에 활용합니다. 사용자는 항상 최신 정보를 받을 수 있죠.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 웹 검색 결과를 무조건 신뢰하는 것입니다.
웹에는 잘못된 정보, 광고, 스팸도 많습니다. 따라서 웹에서 가져온 문서도 다시 한번 검증하는 과정이 필요합니다.
또한 웹 크롤링은 시간이 걸리므로, 응답 속도가 느려질 수 있다는 점도 고려해야 합니다. 캐싱 전략을 함께 사용하면 이 문제를 완화할 수 있습니다.
다시 이지원 씨의 이야기로 돌아가 봅시다. Web Fallback을 구현한 이지원 씨는 테스트를 해봤습니다.
"새로 나온 플러스 적금이 뭔가요?"라는 질문에 챗봇이 회사 공식 홈페이지에서 정보를 가져와 정확히 답변했습니다. "와, 진짜 되네요!" Web Fallback을 제대로 활용하면 RAG 시스템이 살아있는 지식 베이스로 진화합니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 웹 검색 결과도 반드시 검증 단계를 거쳐야 합니다
- 응답 속도를 위해 캐싱 전략을 병행하세요
- 신뢰할 수 있는 도메인만 크롤링하도록 필터링하세요
3. 문서 신뢰도 평가
Web Fallback까지 구현한 이지원 씨의 시스템은 이제 최신 정보도 잘 제공했습니다. 하지만 QA 팀에서 또 다른 피드백이 왔습니다.
"가끔 출처가 불분명한 블로그 글을 인용하는데, 공식 문서를 우선적으로 사용할 수는 없나요?" 최민수 씨가 고개를 끄덕이며 말했습니다. "좋은 지적이에요.
문서마다 신뢰도를 평가해서 점수를 매기면 됩니다."
문서 신뢰도 평가는 각 문서의 출처, 최신성, 완성도 등을 종합적으로 판단해 점수를 매기는 과정입니다. 마치 논문을 쓸 때 위키백과보다 학술지를 우선 인용하는 것과 같습니다.
이 평가를 통해 더 믿을 수 있는 정보를 우선적으로 활용할 수 있습니다.
다음 코드를 살펴봅시다.
from datetime import datetime
import re
def calculate_trust_score(document, metadata):
score = 0.0
# 출처 신뢰도 평가 (공식 문서 우대)
trusted_domains = ["docs.python.org", "developer.mozilla.org", "github.com"]
if any(domain in metadata.get("source", "") for domain in trusted_domains):
score += 0.4
# 최신성 평가 (1년 이내 가산점)
if "updated_date" in metadata:
days_old = (datetime.now() - metadata["updated_date"]).days
if days_old < 365:
score += 0.3
# 문서 완성도 평가 (길이, 구조)
if len(document) > 500 and len(document) < 5000:
score += 0.2
if re.search(r"```|Example|샘플", document): # 코드 예제 포함 여부
score += 0.1
return min(score, 1.0) # 최대 1.0
이지원 씨의 챗봇 프로젝트는 이제 제법 완성도가 높아졌습니다. 내부 DB 검색, 검증, Web Fallback까지 모두 작동했습니다.
하지만 QA 담당자인 김하나 씨로부터 예상치 못한 피드백을 받았습니다. "Python 문법을 물어봤더니 개인 블로그 글을 인용하더라고요.
공식 문서가 있는데 왜 블로그를 먼저 보여주나요?" 이지원 씨는 당황했습니다. 벡터 유사도만 보면 그 블로그 글이 더 높았던 것입니다.
하지만 공식 문서가 훨씬 정확하고 신뢰할 수 있는 정보였죠. 최민수 씨에게 다시 도움을 청했습니다.
"선배, 어떻게 하면 공식 문서를 우선적으로 쓸 수 있을까요?" 그렇다면 문서 신뢰도 평가란 정확히 무엇일까요? 쉽게 비유하자면, 문서 신뢰도 평가는 마치 온라인 쇼핑몰에서 리뷰 점수와 판매자 등급을 보고 상품을 고르는 것과 같습니다.
같은 제품이라도 공식 스토어와 무명 셀러는 신뢰도가 다릅니다. RAG 시스템도 마찬가지입니다.
같은 내용을 담고 있어도 공식 문서인지, 개인 블로그인지, 얼마나 최신인지에 따라 신뢰도가 달라져야 합니다. 문서 신뢰도를 평가하지 않던 초기 시스템은 어땠을까요?
개발자들은 벡터 유사도나 키워드 매칭만으로 문서를 선택했습니다. 검색 결과 1위에 나온 문서를 무조건 사용했죠.
문제는 그 문서가 오래되었거나, 잘못된 정보를 담고 있거나, 출처가 불분명할 수 있다는 점이었습니다. 특히 의료, 법률, 금융 같은 분야에서는 잘못된 정보가 큰 문제를 일으킬 수 있었습니다.
사용자들은 점점 AI 답변을 신뢰하지 않게 되었습니다. 바로 이런 문제를 해결하기 위해 문서 신뢰도 평가 기법이 등장했습니다.
문서 신뢰도 평가를 사용하면 출처가 명확한 문서를 우선 활용할 수 있습니다. 또한 최신 정보를 자동으로 우대해 시대에 뒤떨어진 답변을 방지할 수 있습니다.
무엇보다 시스템의 전반적인 신뢰성이 크게 향상된다는 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 4번째 줄의 calculate_trust_score 함수는 문서와 메타데이터를 입력받습니다. 8번째 줄에서 trusted_domains 리스트를 정의합니다.
공식 문서로 인정할 도메인들입니다. 9-10번째 줄에서 출처가 신뢰할 만한 도메인이면 0.4점을 부여합니다.
13-16번째 줄에서는 문서가 1년 이내에 업데이트되었는지 체크해 최신성을 평가합니다. 19-23번째 줄에서는 문서 길이와 코드 예제 포함 여부로 완성도를 판단합니다.
마지막으로 25번째 줄에서 최대 1.0을 넘지 않도록 제한합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 개발자 커뮤니티 챗봇을 만든다고 가정해봅시다. "React hooks 사용법"을 물으면 검색 결과로 공식 React 문서, Medium 블로그, 개인 블로그 3개가 나왔습니다.
신뢰도 평가를 거치면 공식 문서는 0.9점, Medium은 0.6점, 개인 블로그는 0.3점을 받습니다. 시스템은 자동으로 공식 문서를 우선 활용해 답변을 생성합니다.
사용자는 더 정확하고 공신력 있는 정보를 받게 되죠. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 신뢰도 기준을 너무 경직되게 설정하는 것입니다. 개인 블로그라도 전문가가 쓴 훌륭한 글이 있을 수 있습니다.
따라서 출처만으로 판단하지 말고 내용의 질, 예제 포함 여부, 커뮤니티 평가 등 다양한 요소를 종합적으로 고려해야 합니다. 또한 분야별로 신뢰할 만한 도메인 리스트를 따로 관리하는 것이 좋습니다.
다시 이지원 씨의 이야기로 돌아가 봅시다. 신뢰도 평가를 추가한 이지원 씨는 다시 테스트를 했습니다.
이번에는 Python 공식 문서가 가장 높은 점수를 받아 우선 활용되었습니다. QA 팀의 김하나 씨도 만족스러워했습니다.
"이제 훨씬 신뢰할 만한 답변이 나오네요!" 문서 신뢰도 평가를 제대로 구현하면 RAG 시스템의 품질이 한 단계 더 올라갑니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 분야별로 신뢰할 수 있는 도메인 리스트를 미리 작성하세요
- 출처뿐 아니라 내용의 질, 예제 유무도 함께 평가하세요
- 사용자 피드백을 수집해 신뢰도 기준을 계속 개선하세요
4. 실습: CRAG 구현
이론을 모두 배운 이지원 씨는 드디어 전체 CRAG 시스템을 처음부터 끝까지 구현해 보기로 했습니다. 최민수 씨가 옆에서 지켜보며 조언했습니다.
"이제 검증, Fallback, 신뢰도 평가를 하나로 엮어보세요. 실전에서 어떻게 작동하는지 직접 경험해야 진짜 실력이 됩니다."
CRAG 구현은 검색, 검증, Web Fallback, 신뢰도 평가를 모두 통합한 완전한 시스템을 만드는 실습입니다. 마치 요리 재료를 모두 준비했으니 이제 실제로 요리를 완성하는 단계와 같습니다.
이 과정을 통해 각 요소가 어떻게 유기적으로 연결되는지 깊이 이해할 수 있습니다.
다음 코드를 살펴봅시다.
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
class CRAGSystem:
def __init__(self, vectorstore, llm):
self.vectorstore = vectorstore
self.llm = llm
def query(self, question):
# 1. 벡터 검색
docs = self.vectorstore.similarity_search(question, k=3)
# 2. 검증
verified_docs = []
for doc in docs:
result = verify_document(question, doc.page_content)
trust_score = calculate_trust_score(doc.page_content, doc.metadata)
if result == "CORRECT" and trust_score > 0.5:
verified_docs.append((doc, trust_score))
# 3. Web Fallback (검증 통과 문서 없으면)
if not verified_docs:
web_docs = web_fallback(question, "INCORRECT")
for doc in web_docs:
trust_score = calculate_trust_score(doc.page_content, doc.metadata)
verified_docs.append((doc, trust_score))
# 4. 신뢰도 순 정렬 및 답변 생성
verified_docs.sort(key=lambda x: x[1], reverse=True)
best_doc = verified_docs[0][0] if verified_docs else None
if best_doc:
return self.llm.invoke(f"질문: {question}\n문서: {best_doc.page_content}\n답변:")
return "죄송합니다, 관련 정보를 찾을 수 없습니다."
이지원 씨는 지난 몇 주간 배운 모든 내용을 복습했습니다. 검색 결과 검증, Web Fallback, 문서 신뢰도 평가까지 하나씩 따로 구현해 봤습니다.
이제 마지막 단계, 모든 것을 하나로 합치는 작업만 남았습니다. 최민수 씨가 말했습니다.
"지원씨, 이론을 아는 것과 실제로 구현하는 건 다릅니다. 직접 코드를 짜보면서 막히는 부분을 해결해야 진짜 내 것이 되죠." 이지원 씨는 새 파일을 열고 CRAGSystem이라는 클래스를 만들기 시작했습니다.
첫 번째 고민은 "어떤 순서로 처리할까?"였습니다. 최민수 씨가 힌트를 줬습니다.
"일단 내부 DB에서 찾아보고, 검증하고, 안 되면 웹으로 가는 순서가 자연스럽죠." 그렇다면 완전한 CRAG 시스템 구현이란 정확히 무엇일까요? 쉽게 비유하자면, CRAG 구현은 마치 자동차 부품을 조립해 완성차를 만드는 것과 같습니다.
엔진(검색), 브레이크(검증), 네비게이션(Fallback), 에어백(신뢰도 평가)을 각각 만들었으니 이제 하나의 차로 조립하는 겁니다. 각 부품이 제대로 연결되고, 순서대로 작동하도록 통합하는 과정이 핵심입니다.
부품만 따로 있던 시절에는 어땠을까요? 개발자들은 각 기능을 별도의 스크립트로 관리했습니다.
검색은 A 함수, 검증은 B 함수, Fallback은 C 함수처럼요. 문제는 이 함수들을 수동으로 순서대로 호출해야 했다는 점입니다.
코드가 복잡해지고, 실수하기도 쉬웠습니다. 더 큰 문제는 유지보수였습니다.
한 부분을 수정하면 다른 부분과의 연결이 깨지는 일이 잦았습니다. 바로 이런 문제를 해결하기 위해 통합 CRAG 시스템 구현이 중요합니다.
통합 시스템을 만들면 모든 로직이 한 곳에서 관리됩니다. 또한 각 단계가 자동으로 순차 실행되어 실수를 방지할 수 있습니다.
무엇보다 재사용성이 높아져 다른 프로젝트에도 쉽게 적용할 수 있다는 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 4번째 줄에서 CRAGSystem 클래스를 정의합니다. 9번째 줄의 query 메서드가 전체 프로세스의 시작점입니다.
11번째 줄에서 벡터 DB로부터 상위 3개 문서를 검색합니다. 14-19번째 줄이 핵심입니다.
각 문서를 검증하고 신뢰도를 평가해 기준을 통과한 것만 남깁니다. 22-26번째 줄에서 검증 통과 문서가 없으면 Web Fallback을 실행합니다.
29번째 줄에서 신뢰도 순으로 정렬해 가장 좋은 문서를 선택하고, 33번째 줄에서 최종 답변을 생성합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 기업 내부 지식 관리 시스템을 개발한다고 가정해봅시다. 직원이 "우리 회사 휴가 정책이 어떻게 돼?"라고 물으면, 시스템은 먼저 내부 HR 문서를 검색합니다.
검증을 거쳐 적합한 문서가 있으면 바로 답변하고, 없으면 회사 인트라넷을 크롤링합니다. 신뢰도가 가장 높은 공식 인사팀 문서를 찾아 정확한 답변을 제공합니다.
직원들은 매번 인사팀에 문의하지 않아도 되니 업무 효율이 크게 올라갑니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 에러 처리를 소홀히 하는 것입니다. 웹 크롤링 실패, LLM API 오류, 네트워크 문제 등 다양한 예외 상황이 발생할 수 있습니다.
각 단계마다 try-except 블록을 추가해 안정성을 높여야 합니다. 또한 로깅을 충분히 남겨 문제가 생겼을 때 어느 단계에서 실패했는지 추적할 수 있어야 합니다.
다시 이지원 씨의 이야기로 돌아가 봅시다. 통합 시스템을 완성한 이지원 씨는 여러 테스트 케이스를 돌려봤습니다.
내부 문서가 있는 경우, 없는 경우, 애매한 경우 모두 제대로 작동했습니다. 최민수 씨가 코드를 리뷰하며 칭찬했습니다.
"잘했어요. 이제 진짜 프로덕션에 배포할 수 있겠네요." CRAG 시스템을 직접 구현해 보면 각 요소의 역할과 연결 고리를 깊이 이해할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 각 단계마다 충분한 에러 처리와 로깅을 추가하세요
- 테스트 케이스를 다양하게 준비해 모든 경로를 검증하세요
- 처음엔 간단히 구현하고, 점진적으로 기능을 추가하세요
5. 실습: 외부 검색 통합
CRAG 시스템을 완성한 이지원 씨는 최민수 씨로부터 새로운 과제를 받았습니다. "지금은 Google만 쓰는데, Bing, DuckDuckGo 같은 다른 검색 엔진도 함께 쓸 수 있게 해보세요.
그리고 검색 결과를 합쳐서 최적의 답을 찾는 로직도 추가하면 더 좋겠네요."
외부 검색 통합은 여러 검색 엔진의 결과를 동시에 활용해 더 풍부하고 정확한 정보를 얻는 기법입니다. 마치 여러 신문을 동시에 읽고 교차 검증하는 것과 같습니다.
이 방법을 사용하면 특정 검색 엔진의 편향을 줄이고 정보의 다양성을 확보할 수 있습니다.
다음 코드를 살펴봅시다.
from langchain.utilities import GoogleSearchAPIWrapper, BingSearchAPIWrapper
from langchain.document_loaders import WebBaseLoader
import asyncio
class MultiSearchCRAG:
def __init__(self):
self.google = GoogleSearchAPIWrapper()
self.bing = BingSearchAPIWrapper()
async def parallel_search(self, question):
# 여러 검색 엔진 동시 호출
tasks = [
asyncio.to_thread(self.google.results, question, 3),
asyncio.to_thread(self.bing.results, question, 3)
]
results = await asyncio.gather(*tasks)
# 결과 통합 및 중복 제거
all_docs = []
seen_urls = set()
for engine_results in results:
for result in engine_results:
url = result.get("link", "")
if url not in seen_urls:
seen_urls.add(url)
loader = WebBaseLoader(url)
docs = loader.load()
all_docs.extend(docs)
return all_docs
이지원 씨의 CRAG 시스템은 이제 회사에서 실제로 사용되기 시작했습니다. 피드백도 대부분 긍정적이었습니다.
하지만 최민수 씨는 한 가지 더 개선할 점을 발견했습니다. "지원씨, 지금 Google만 쓰고 있죠?
가끔 Google 검색 결과가 편향될 수 있어요. 여러 검색 엔진을 함께 쓰면 더 균형 잡힌 정보를 얻을 수 있습니다." 이지원 씨는 처음에는 의아했습니다.
"검색 엔진 하나면 충분하지 않나요?" 최민수 씨가 설명했습니다. "예를 들어 최신 기술 트렌드를 검색할 때, Google은 광고 링크를 먼저 보여줄 수 있고, Bing은 Microsoft 생태계 문서를 우선할 수 있어요.
둘 다 쓰면 더 객관적인 결과를 얻을 수 있죠." 그렇다면 외부 검색 통합이란 정확히 무엇일까요? 쉽게 비유하자면, 외부 검색 통합은 마치 중요한 결정을 내릴 때 한 사람의 의견만 듣지 않고 여러 전문가의 조언을 구하는 것과 같습니다.
의사, 변호사, 재무 설계사의 의견을 모두 듣고 종합 판단하는 것처럼, 여러 검색 엔진의 결과를 합쳐 최선의 정보를 선택합니다. 단일 검색 엔진만 사용하던 시절에는 어땠을까요?
개발자들은 주로 Google API만 사용했습니다. 편리하고 결과도 좋았으니까요.
하지만 문제는 검색 결과의 다양성이었습니다. Google의 알고리즘 특성상 SEO에 최적화된 페이지가 상위에 노출되었고, 정말 유용한 정보는 뒤로 밀리는 경우도 있었습니다.
특히 특정 국가나 언어권 정보는 잘 나오지 않는 문제도 있었습니다. 바로 이런 문제를 해결하기 위해 다중 검색 엔진 통합 기법이 등장했습니다.
여러 검색 엔진을 사용하면 정보의 편향을 줄일 수 있습니다. 또한 한 검색 엔진에서 찾지 못한 정보를 다른 곳에서 발견할 수도 있습니다.
무엇보다 병렬 처리로 속도 저하 없이 더 많은 후보 문서를 확보할 수 있다는 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 5번째 줄에서 MultiSearchCRAG 클래스를 정의합니다. 7-8번째 줄에서 Google과 Bing 검색 래퍼를 초기화합니다.
10번째 줄의 parallel_search 메서드가 핵심입니다. 12-15번째 줄에서 asyncio를 사용해 두 검색 엔진을 동시에 호출합니다.
이렇게 하면 순차 호출보다 훨씬 빠릅니다. 16번째 줄에서 모든 결과를 기다렸다가 한 번에 받아옵니다.
19-28번째 줄에서 중복 URL을 제거하고 문서를 로드합니다. seen_urls set을 사용해 같은 링크를 두 번 크롤링하지 않도록 합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 글로벌 뉴스 분석 서비스를 개발한다고 가정해봅시다.
"미국 대선 결과"를 검색할 때, Google은 주로 영어권 언론을 보여주고, Bing은 다양한 언어권 소스를 포함할 수 있습니다. 두 검색 엔진의 결과를 합치면 더 균형 잡힌 시각을 얻을 수 있습니다.
또한 DuckDuckGo를 추가하면 개인화되지 않은 중립적인 결과도 얻을 수 있죠. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 무조건 많은 검색 엔진을 추가하는 것입니다. 검색 엔진이 많아질수록 API 비용이 증가하고, 중복 제거 로직도 복잡해집니다.
따라서 프로젝트 성격에 맞는 2-3개 검색 엔진을 선택하는 것이 좋습니다. 또한 비동기 처리 시 타임아웃을 설정해 느린 검색 엔진이 전체 시스템을 지연시키지 않도록 해야 합니다.
다시 이지원 씨의 이야기로 돌아가 봅시다. 다중 검색 엔진을 적용한 이지원 씨는 테스트를 해봤습니다.
같은 질문에 대해 이전보다 20% 더 많은 후보 문서를 확보했고, 신뢰도 평가를 거쳐 더 정확한 답변을 생성할 수 있었습니다. 최민수 씨가 만족스러워하며 말했습니다.
"이제 정말 프로덕션급이네요!" 외부 검색을 효과적으로 통합하면 RAG 시스템의 정보 품질이 한층 더 높아집니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 프로젝트 특성에 맞는 2-3개 검색 엔진을 선택하세요
- 비동기 처리 시 타임아웃을 반드시 설정하세요
- 중복 제거 로직을 꼼꼼히 구현해 불필요한 크롤링을 방지하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
ReAct 패턴 마스터 완벽 가이드
LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.
AI 에이전트의 모든 것 - 개념부터 실습까지
AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.
프로덕션 RAG 시스템 완벽 가이드
검색 증강 생성(RAG) 시스템을 실제 서비스로 배포하기 위한 확장성, 비용 최적화, 모니터링 전략을 다룹니다. AWS/GCP 배포 실습과 대시보드 구축까지 프로덕션 환경의 모든 것을 담았습니다.
RAG 캐싱 전략 완벽 가이드
RAG 시스템의 성능을 획기적으로 개선하는 캐싱 전략을 배웁니다. 쿼리 캐싱부터 임베딩 캐싱, Redis 통합까지 실무에서 바로 적용할 수 있는 최적화 기법을 다룹니다.
실시간으로 답변하는 RAG 시스템 만들기
사용자가 질문하면 즉시 답변이 스트리밍되는 RAG 시스템을 구축하는 방법을 배웁니다. 실시간 응답 생성부터 청크별 스트리밍, 사용자 경험 최적화까지 실무에서 바로 적용할 수 있는 완전한 가이드입니다.