"검색은 분명히 잘 했는데, 답이 엉뚱해요."
RAG로 챗봇을 만들어본 분이라면 이 한 문장에 한 번쯤 머리를 쥐어뜯어 보셨을 거예요. 사내 문서를 제대로 찾아왔는데 정작 답변은 문서에 없는 내용을 자신만만하게 지어내거나, 분명 자료에 답이 있는데도 "잘 모르겠다"며 발을 빼거나. 스탠퍼드 연구에 따르면 RAG에 가드레일을 결합하면 단독 LLM 대비 환각(hallucination)을 96%까지 줄일 수 있다고 해요. 그런데 막상 만들어보면, 그 마지막 몇 퍼센트가 사람을 잡죠.
오늘은 처음 접하는 분도 따라올 수 있게, 용어 하나하나를 그 작동 원리부터 짚으면서 두 가지를 다뤄볼게요. RAG 챗봇을 어떻게 만드는지, 그리고 잘 만들어도 끝까지 남는 환각 문제를 최근 한 논문이 어떻게 전혀 다른 각도로 풀었는지까지요.
그래서 RAG가 대체 뭔가요?
먼저 LLM부터 짚을게요. LLM(대규모 언어 모델)은 ChatGPT 같은 AI의 핵심이에요. 인터넷에 있는 방대한 글을 미리 학습해서, 입력된 문장 다음에 올 단어를 확률적으로 예측하며 문장을 만들어내요. 핵심은, 이 모델이 학습한 적 없는 정보는 알 수 없다는 거예요. 우리 제품 매뉴얼, 어제 바뀐 사내 규정, 비공개 계약서는 학습 데이터에 없으니 답할 근거가 아예 없죠.
그런데 모델은 모른다고 말하기보다, 학습한 패턴에서 가장 그럴듯한 문장을 만들어내는 경향이 있어요. 그래서 근거가 없을 때 사실처럼 보이는 틀린 내용을 생성하는데, 이게 바로 환각이에요.
이 문제를 푸는 방식이 RAG(Retrieval-Augmented Generation, 검색 증강 생성)예요. 동작은 단순해요. 질문이 들어오면 모델이 곧장 답하게 두지 않고, 먼저 외부 지식 베이스(우리 문서함)에서 질문과 관련된 부분을 찾아온 뒤, 그 내용을 모델 입력에 함께 넣어줘요. 모델은 자기 기억이 아니라 입력으로 받은 문서를 근거로 답을 쓰게 되니, 학습하지 않은 우리 회사 정보도 정확히 다룰 수 있게 되죠.
전체 흐름은 세 단계예요. 우리 문서를 검색하기 좋은 형태로 미리 가공해 저장해두는 인덱싱, 질문이 들어오면 관련 조각을 꺼내오는 검색(Retrieval), 그 조각을 근거로 모델이 답을 만드는 생성(Generation). 이 세 단계 각각을 얼마나 정교하게 다듬느냐에서 실제 품질이 갈려요.
RAG 챗봇, 실제로는 이렇게 만들어요
데모용 RAG는 하루면 붙여요. 진짜 어려운 건 "데모는 잘 되는데 실전에서 무너지는" 그 간극이에요. 그 간극을 좁히는 단계들을 하나씩, 작동 원리까지 짚어볼게요.
청킹 — 문서를 어떤 단위로 나누느냐가 절반이에요
우리 문서는 보통 길어요. 문서 전체를 한 번에 모델에 넣을 수도 없고, 넣더라도 정작 필요한 한 문단을 정확히 짚어내기 어려워요. 그래서 문서를 일정 크기 단위로 나눠, 각 조각을 검색 대상으로 만들어요. 이 작업이 청킹(chunking)이에요.
단순히 글자 수로 끊으면 문장이 어중간하게 잘려요. 그래서 의미가 이어지는 단위로 끊는 시멘틱 청킹을 쓰면 검색 정확도가 15~30% 올라가요. 너무 잘게 나누면 앞뒤 맥락이 사라지고, 너무 크게 나누면 질문과 무관한 내용까지 함께 검색돼요. 그래서 조각끼리 일부 구간을 겹치게(오버랩) 나눠, 경계에서 중요한 문장이 잘려나가는 걸 막아요.
용도 | 권장 청크 크기 | 겹침(오버랩) |
FAQ·짧은 Q&A | 128~256 토큰 | 0~20 토큰 |
일반·기술 문서 | 512~1024 토큰 | 50~100 토큰 |
법률·의학 전문 문서 | 300~600 토큰 | 50~100 토큰 |
연구 논문·장문 | 500~1000 토큰 | 100~200 토큰 |
표의 토큰은 모델이 텍스트를 처리하는 최소 단위예요. 한 단어가 통째로 한 토큰이 되기도 하고, 긴 단어는 여러 조각으로 쪼개지기도 해요. 한국어는 대략 한 글자가 1~2토큰이라, 512토큰이면 두세 문단 분량이에요. 자세한 청킹 전략은 Weaviate 블로그에 정리돼 있어요.
임베딩과 벡터 DB — 의미로 검색하는 원리
나눠놓은 조각을 컴퓨터가 "비슷한 의미"로 찾으려면, 글자 그대로가 아니라 의미를 비교할 수 있어야 해요. 그래서 각 조각을 의미가 담긴 숫자들의 나열, 즉 벡터로 변환하는데 이 작업이 임베딩(embedding)이에요.
핵심 성질은, 의미가 비슷한 문장일수록 벡터값도 서로 가깝게 나온다는 점이에요. "환불 규정"과 "돈을 돌려받는 방법"은 글자는 달라도 의미가 가까우니 벡터값도 가까워요. 그래서 질문이 들어오면 질문도 같은 방식으로 벡터로 바꾼 뒤, 벡터값이 가장 가까운 조각들을 계산으로 찾아내요. 글자가 똑같지 않아도 의미가 가까우면 검색되는 원리가 여기 있어요.
이 벡터들을 저장해두고, 질문 벡터와 가장 가까운 벡터를 빠르게 찾아주는 데이터베이스가 벡터 DB예요. 규모에 따라 고르면 돼요. 맛보기 단계면 무료에 가벼운 Chroma, 비용 대비 속도를 원하면 Qdrant, 키워드와 의미 검색을 함께 써야 하는 큰 조직이면 Weaviate, 운영 안정성이 최우선이면 Pinecone이 자주 선택돼요. 의미를 벡터로 바꾸는 임베딩 모델은 입문이면 OpenAI 계열, 다국어가 중요하면 BGE-M3 같은 모델이 강하고요.
기본 검색만으로는 부족해요 — 검색 고도화
"의미가 가까운 조각 찾기"만으로는 실전 품질에 못 미쳐요. 의미는 비슷한데 핵심 단어(제품 코드, 약어, 고유명사)가 정확히 안 맞으면 엉뚱한 조각을 가져오거든요. 그래서 보강 기법을 얹어요.
첫째, 하이브리드 검색은 의미 기반 벡터 검색과, 단어가 정확히 일치하는지 보는 키워드 검색(BM25)을 동시에 돌린 뒤 두 결과를 합치는 방식이에요. 의미와 정확한 철자를 둘 다 챙기는 거죠. 둘째, HyDE는 모델이 질문에 대한 가상의 답변을 먼저 생성하고, 그 답변을 벡터로 바꿔 검색하는 방식이에요. 질문이 짧아 검색이 잘 안 될 때, 내용이 풍부한 가상 답변으로 검색 정확도를 높여요.
셋째, 리랭킹(Reranking)은 1차 검색으로 후보를 넉넉히 가져온 다음, 더 정밀한 모델로 각 후보의 관련도를 다시 채점해 순위를 재정렬하는 단계예요. 1차에서 빠르게 넓게 거르고, 2차에서 정확하게 좁히는 구조죠. 넷째, Contextual Retrieval은 각 조각에 그 조각이 전체 문서에서 어떤 맥락인지를 한 줄 요약으로 덧붙여, 조각만 떼어내도 의미가 모호해지지 않게 만들어요.
67% 감소. 앤트로픽이 제시한 Contextual Retrieval로 줄어든 검색 실패율이에요. 각 조각에 맥락 한 줄을 덧붙인 것만으로요.
자세한 방법은 앤트로픽 공식 블로그에 나와 있어요. 최고 품질을 원하면 질문 분류 → 하이브리드+HyDE 검색 → 리랭킹 → 요약 압축 순으로 파이프라인을 쌓는 게 정석이에요.
환각을 줄이는 프롬프트 설계
검색을 아무리 잘해도, 마지막 생성 단계에서 모델이 멋대로 답하면 헛수고예요. 그래서 모델에 주는 지시문(프롬프트)에 네 가지를 명시해요. 반드시 제공된 컨텍스트만 사용할 것, 컨텍스트에 없으면 "모른다"고 명확히 말할 것, 출처를 인용할 것, 검색된 컨텍스트가 없으면 답변을 거부할 것.
핵심 원칙 하나만 기억하면 돼요. 관련성이 약한 자료를 억지로 끼워 답하느니, 아무것도 반환하지 않는 게 낫다는 거예요. 관련성이 일정 기준에 못 미치는 자료는 컨텍스트에서 빼고, 빈손이면 차라리 거절하도록 설계하는 거죠. 이 "모르면 모른다고 한다"는 동작이, 뒤에서 다룰 논문의 핵심과 정확히 맞닿아요.

[ RAG 파이프라인 인덱싱→검색→생성 3단계 다이어그램]
그래도 환각이 남는 진짜 이유
여기까지 다 했는데도 환각이 남아요. 청킹도 다듬고, 하이브리드 검색에 리랭킹까지 붙이고, 프롬프트로 "컨텍스트만 쓰라"고 명시했는데도 가끔 엉뚱한 답이 나오죠. 왜 그럴까요?
근본 원인은 검색이 아니라 모델 자체에 있어요. 앞에서 LLM은 인터넷 글을 대량으로 학습했다고 했죠. 그래서 검색해온 문서의 내용과 자기가 학습으로 외운 지식이 서로 충돌하면, 종종 학습한 지식 쪽을 우선해버려요. 분명 컨텍스트로 정확한 자료를 줬는데도, 모델이 자기 기억을 끌어와 답하는 거죠. 이걸 컨텍스트-메모리 충돌이라고 해요.
최근 공개된 OCC-RAG 논문에 이 문제를 정확히 보여주는 실험이 나와요. 일부러 틀린 문서를 모델에 줬어요. "샤를 드골이 2022년 미국 초대 대통령이 되었다"는, 현실과 어긋나는 문장이요. 그리고 "미국 초대 대통령이 누구냐"고 물었죠.
큰 모델은 자기가 학습한 지식대로 "조지 워싱턴"이라 답했어요. 현실로는 맞지만, 제공된 문서는 어긴 답이에요. 반대로 작은 특화 모델은 문서에 적힌 대로 "드골"이라 답했고요.
여기서 헷갈리기 쉬운 개념 두 개를 구분해볼게요. 사실에 부합하는 답(truthful)과 제공된 문서에 부합하는 답(faithful)은 다른 거예요. 상식 퀴즈라면 "조지 워싱턴"이 맞아요. 하지만 사내 규정 최신본, 어제 갱신된 계약서처럼 "제공된 문서가 곧 정답 기준"인 상황에선, 모델이 자기 지식을 끌어오면 오히려 오류가 돼요. 이때는 문서에 충실한 답이 정답이죠. 그런데 모델이 클수록 학습한 지식이 강해서 이걸 더 자주 어겨요. 바로 여기서 OCC-RAG의 발상이 시작돼요.
OCC-RAG: 작은 모델이 큰 모델을 이긴 방법
OCC-RAG는 OCC 팀이 공개한 소형 언어 모델(SLM) 패밀리예요. 발상이 명쾌해요. 모든 걸 다 아는 거대 모델을 쓰는 대신, "제공된 문서에 충실하게 답하기" 한 가지에만 특화된 작은 모델을 제대로 훈련시키자는 거죠.
모델 크기 표기부터 짚을게요. 모델 이름에 붙는 0.6B, 4B에서 B는 10억(billion), 모델이 학습으로 조정한 내부 수치(파라미터)의 개수예요. 파라미터가 많을수록 표현력은 커지지만, 그만큼 연산량과 메모리, 비용도 함께 늘어나요. 0.6B는 6억 개, 4B는 40억 개니까 약 6배 이상 차이죠. 보통은 큰 모델이 낫다고 보지만, 이 논문은 그 통념을 뒤집어요.
충실성과 거절을 데이터로 가르쳤어요
OCC-RAG가 노린 능력은 세 가지예요. 첫째, 멀티홉 추론이에요. 한 문서에서 곧바로 답이 나오지 않고, 여러 문서의 정보를 단계적으로 연결해야 답에 도달하는 질문을 다루는 능력이죠. 예를 들어 "A 드라마 주인공이 속한 조직은?"은, 먼저 주인공이 누구인지 확인하고, 그다음 그 인물의 소속을 찾는 두 단계를 거쳐야 해요. 둘째, 자기 지식이 아니라 제공된 문서를 따르는 충실성. 셋째, 근거가 부족하면 답하지 않고 물러서는 안전한 거절이에요.
이 세 가지를 위키피디아 기반으로 만든 300만 개가 넘는 합성 질의응답 데이터로 훈련시켰어요. 주목할 점은, 단순 정답만 학습시킨 게 아니라 일부러 답이 없는 문제를 섞었다는 거예요. 그래서 모델이 무리하게 답을 지어내는 대신, "이건 답할 수 없어요"라고 판단하는 동작 자체를 학습하게 했죠.
답하는 과정 자체를 구조로 만들었어요
OCC-RAG가 답을 생성하는 방식도 특징적이에요. 답만 곧장 출력하지 않고, 다섯 단계를 순서대로 거쳐요. 질문이 무엇을 묻는지 분석하고, 어떤 출처가 관련 있는지 따지고, 그것들을 엮어 추론하고, 답할 수 있는지 없는지 판정한 뒤, 마지막에 답을 출력하죠.
특히 중요한 건, 답을 출력하기 직전에 "답변 가능 / 불가능"을 명시적으로 선언한다는 점이에요. 거절을 모호한 표현으로 흘리는 게 아니라, 분명한 라벨로 먼저 찍게 만든 거죠. 게다가 추론 과정에서 문서 원문을 그대로 인용하니, 답의 근거가 투명하게 드러나요. 답변 근거를 제시해야 하는 고객 상담봇이나 법률·연구 챗봇에 잘 맞는 구조예요. 이 방식은 출처를 인용하는 소형 모델을 다룬 Pleias-RAG 연구의 흐름을 잇고 있어요.

[ OCC-RAG의 5단계 응답 구조 — 질문 분석/출처 분석/추론/판정/답변 출처]
결과 — 0.6B가 4B를 이겼어요
결과가 이 논문의 핵심이에요. 6배 이상 큰 모델과 맞먹거나, 오히려 능가했거든요. 특히 우리가 신경 쓰는 두 항목, 문서 충실성과 거절 정확도에서는 훨씬 큰 모델들을 앞질렀어요. 아래 숫자는 모두 점수가 높을수록 좋은 지표예요.
모델 (크기) | 멀티홉 추론 | 충실성 | 거절 정확도 |
OCC-RAG (0.6B) | 57.6 | 79.9 | 86.9 |
OCC-RAG (1.7B) | 60.9 | 81.4 | 87.2 |
Qwen3 (4B) | 60.6 | 69.7 | 64.1 |
Gemma3 (4B) | 55.8 | 69.8 | 55.8 |
표를 보면, 6억짜리 모델이 40억짜리 모델을 충실성과 거절에서 한참 앞섰어요. 특히 거절 정확도는 86.9 대 64.1로 차이가 크죠. 근거가 부족할 때 답을 지어내지 않고 물러서는 능력에서 작은 모델이 크게 앞선 거예요. 멀티홉 추론에서도 4B급과 비슷한 수준을 보였고요. 모델은 허깅페이스에 공개돼 있어 직접 시험해볼 수도 있어요.
여기서 한 가지를 짚고 싶어요. 충실성은 모델을 키워서 얻는 게 아니라, 올바른 방식으로 학습시켜야 얻는 능력이라는 메시지예요. "더 큰 모델을 쓰면 환각이 줄겠지"라는 통념을 정면으로 뒤집죠.
그래서 우리 챗봇엔 뭘 적용하나요?
논문이 흥미롭다고 끝이 아니죠. 실무에 옮길 지점을 세 가지로 정리해볼게요.
첫째, 평가 지표에 충실성을 넣으세요. 챗봇을 만들었으면 잘 동작하는지 점수로 측정해야 하는데, 이때 자주 쓰는 도구가 RAGAS예요. 네 가지 지표 중 Faithfulness(충실성)가 "답변이 검색된 컨텍스트에 얼마나 충실한지"를 측정해요. 환각을 관리하려면 이 수치를 꾸준히 추적해야 해요. 도입 방법은 RAGAS 공식 문서와 원 논문에 정리돼 있어요.
둘째, 작고 특화된 모델을 후보에 올리세요. 비싼 대형 모델에만 의존할 필요가 없다는 게 OCC-RAG의 함의예요. 작은 모델은 응답이 빠르고 비용이 낮을 뿐 아니라, 외부 API를 거치지 않고 우리 서버 안에서 직접 구동하기도 쉬워요. 보안이 민감한 사내 데이터를 외부로 내보내지 않아도 된다는 건, 기업 입장에선 결정적 장점이죠.
셋째, 권한 제어는 검색 단계에서. 누가 어떤 문서에 접근할 수 있는지는, 답을 생성한 뒤가 아니라 자료를 검색하는 단계에서 적용해야 해요. 그러지 않으면 권한 없는 사용자에게 민감한 문서가 노출될 수 있거든요. 각 문서 조각에 접근 권한 정보를 메타데이터로 붙이고, 검색 전에 필터로 걸러내는 구조가 안전해요.
그리고 이 마지막 한 걸음, 작은 모델을 우리 서버에서 직접 구동하는 단계에서 인프라 이야기가 따라붙어요. 모델이 작아졌다고 일반 PC에서 안정적으로 서비스되는 건 아니거든요. 실제 트래픽을 감당하려면 작업량에 맞는 GPU 구성이 출발점이 돼요.
정리하자면
RAG 챗봇을 잘 만드는 건 결국 디테일 싸움이에요. 문서를 잘 나누고(청킹), 의미로 검색하고(임베딩·벡터 DB), 검색을 보강하고(하이브리드·리랭킹), 프롬프트로 "모르면 모른다"를 명시하는 것까지. 여기까지만 해도 환각은 크게 줄어요.
하지만 마지막 몇 퍼센트, 모델이 검색해온 문서보다 자기가 학습한 지식을 우선하는 그 지점은 검색 보강만으로는 닿지 않아요. OCC-RAG는 그 문제를 모델 크기가 아니라 충실성을 학습시키는 방식으로 풀었고, 6억짜리 작은 모델로 40억짜리를 넘어서며 증명했어요.
"더 큰 모델"이 늘 답은 아니라는 것. 어쩌면 우리에게 필요한 건, 제공된 문서에 충실하고 모르면 솔직한, 잘 훈련된 작은 코어 하나일지도 몰라요.










