왜 확신에 찬 답변이 답변이 없는 것보다 더 나쁠 수 있는가

사내 챗봇 구축을 마쳤습니다. 회사가 보유한 모든 인사 정책, 엔지니어링 사양, 온보딩 문서를 입력했습니다. 신입 사원이 고객 식사 비용 한도에 대해 묻습니다. 봇이 즉시 응답합니다. 아주 자신만만하게 들립니다. 봇이 말한 한도는 인당 75달러입니다.

실제 정책은 50달러입니다. 봇이 답변을 지어낸 것입니다. 파일을 열어본 적도 없습니다. 그저 몇 년 전 학습 데이터에 숨겨진 패턴을 바탕으로 추측했을 뿐입니다. 이것이 프라이빗 문서를 대상으로 가공되지 않은 대규모 언어 모델(LLM)을 실행할 때 마주하는 냉혹한 현실입니다. 모델은 내부 지식에 접근할 권한이 없습니다. 필요한 사실이 학습 가중치 외부에 있을 때, 모델은 모른다고 인정하는 대신 이야기를 지어냅니다. 실제 서비스 환경에서 이는 더 이상 재미있는 해프닝이 아니라 리스크가 됩니다.

검색 증강 생성(Retrieval-Augmented Generation), 즉 RAG는 바로 이 문제를 해결하기 위해 만들어졌습니다. 모델에게 모든 것을 기억하라고 요구하는 대신, 정보를 찾아볼 수 있게 해주는 것입니다.

추측에서 읽기로

가공되지 않은 LLM을 사진을 찍듯 기억하는 천재적인 동료라고 생각해보세요. 다만 당신이 입사하기 전에 퇴사한 동료 말입니다. 그들은 유려한 문장을 쓰고, 논리 퍼즐을 풀며, 개념을 쉬운 용어로 설명할 수 있습니다. 하지만 지난 분기의 API 변경 사항에 대해 물어본다면, 그들은 그저 그럴듯하게 들리는 말을 지어낼 것입니다. 그들에게는 다른 선택지가 없습니다.

RAG는 그 동료에게 서류 캐비닛을 제공합니다. 사용자가 질문을 하면, 시스템은 질문을 모델에게 무작정 던지지 않습니다. 먼저 관련 문서를 검색하여 프롬프트에 컨텍스트(context)로 집어넣은 다음, 모델에게 이를 읽고 응답하도록 요청합니다. 모델은 사실을 회상하는 단계에서, 눈앞에 있는 사실을 이해하는 단계로 전환됩니다.

이 흐름은 오프라인 작업과 온라인 응답이라는 두 부분으로 명확히 나뉩니다.

1단계: 준비 단계 (오프라인)

누군가 질문을 입력하기 훨씬 전부터, 여러분은 어지럽게 널려 있는 문서 모음을 검색 가능한 지식 베이스로 변환해야 합니다. 이 기초 작업이 RAG 시스템의 성공 여부를 결정합니다.

**문서 로더(Document loaders)**가 시작점입니다. 이 커넥터들은 PDF, Notion 워크스페이스, SharePoint 폴더, 웹 페이지, 내부 위키에서 원시 텍스트를 가져옵니다. 여기서 첫 번째 난관에 부딪힙니다. 로더가 Word 문서에서는 깨끗한 텍스트를 추출할 수 있지만, 텍스트 레이어가 없는 이미지 형태의 스캔된 PDF에서는 제대로 작동하지 않을 수 있습니다. 로더가 빈 문자열을 반환하면 데이터베이스에는 아무것도 저장되지 않고, 나중에 사용자는 아무런 경고 없이 "모르겠습니다"라는 답변을 받게 됩니다. 로더가 실제로 무엇을 추출했는지 항상 확인하십시오. 파이프라인을 신뢰하기 전에 각 소스의 문서 몇 개를 무작위로 점검(spot check)해 보세요.

다음은 텍스트 분할(text splitting), 즉 청킹(chunking)입니다. 80페이지 분량의 보안 정책을 한 번에 프롬프트에 넣을 수는 없습니다. 컨텍스트 제한을 초과하고 노이즈 속에 신호가 묻혀버릴 것입니다. 대신 문서를 청크(chunk) 단위로 자릅니다. 핵심은 적절한 크기를 선택하는 것입니다. 단일 문장처럼 너무 작은 청크는 중요한 컨텍스트를 놓치기 쉽습니다. "모든 요청은 관리자의 승인을 받아야 합니다"라는 청크는 이 규칙이 해외 출장에만 적용된다는 사실을 누락할 수 있습니다. 반대로 전체 챕터처럼 너무 큰 청크는 임베딩을 희석시키고, 한 번에 15개의 서로 다른 주제를 다루기 때문에 검색을 혼란스럽게 만듭니다. 실제로 많은 팀이 300~500 토큰 사이의 청크 크기로 시작하며, 문장이 경계선에서 잘려 훼손되지 않도록 50 토큰 정도의 중첩(overlap)을 둡니다. 콘텐츠에 따라 이를 조정하십시오. API 문서는 더 작은 청크를 허용합니다. 법률 계약서는 조건부 논리를 보존하기 위해 종종 더 큰 청크가 필요합니다.

청킹이 완료되면 각 조각은 **임베딩(embedding)**으로 변환됩니다. 이는 텍스트를 모델에 통과시켜 청크의 의미론적 의미를 나타내는 숫자 리스트인 벡터(vector)를 출력하는 과정입니다. 유사한 개념들은 이 수학적 공간에서 서로 가까운 곳에 위치하게 됩니다. "401k 매칭 정책"과 "은퇴 기여 규칙"은 "401k 매칭 정책"과 "사무실 프린터 설정"보다 서로 더 가까이 위치할 것입니다. 이러한 벡터는 Pinecone, Weaviate 또는 Chroma와 같은 오픈 소스 대안을 포함한 **벡터 데이터베이스(vector database)**에 저장됩니다. 벡터 저장소는 단순히 데이터를 쌓아두는 곳이 아닙니다. 이는 근사 최근접 이웃(approximate nearest-neighbor) 검색에 최적화된 인덱스로, 수백만 개의 문서 사이에서도 밀리초 단위로 가장 관련성 높은 청크를 찾아낼 수 있게 해줍니다.

2단계: 실시간 경로 (온라인)

사용자가 마침내 "고객 접대 식사를 위한 출장 경비 정산 정책은 무엇인가요?"라고 물으면, 라이브 파이프라인이 작동하기 시작합니다.