고급 RAG 기술: 챗봇 빌더의 플레이북에서 얻은 교훈
요약
기본적인 RAG 단계를 넘어 검색 품질과 응답 일관성을 높이기 위한 실무적인 고급 기술들을 소개합니다. 의도 탐지(Intent Detection)의 재도입, 함수 호출을 통한 에이전트 기능 결합, 그리고 질문 기반 인덱싱 전략을 통해 챗봇의 성능을 최적화하는 방법을 다룹니다.
핵심 포인트
- 의도 탐지를 결합하여 빈번한 질문에 대해 비용과 속도를 최적화할 수 있음
- Function Calling을 활용해 RAG와 에이전트의 경계를 허물고 동작을 트리거함
- 답변 대신 질문 자체를 인덱싱하여 검색의 의미론적 정확도를 높임
- 표준 RAG와 사전 정의된 답변 경로를 혼합하여 신뢰도를 향상함
고급 RAG 기술: 챗봇 빌더의 플레이북에서 얻한 교훈
대부분의 RAG 튜토리얼은 "청크화(chunk), 임베딩(embed), 검색(retrieve), 생성(generate)" 단계에서 멈춥니다. 하지만 기본적인 RAG 챗봇을 출시하고 나면 다른 종류의 문제들에 직면하게 됩니다. 기술적으로는 작동하지만 잘못된 청크를 반환하는 검색, 이전 문맥에 의존하는 모호한 후속 질문을 던지는 사용자, 그리고 개인화된 느낌 대신 일반적인 느낌을 주는 응답 등이 그것입니다.
이 기술들은 대화형 AI와 챗봇을 구축하는 데 거의 10년을 보낸 사람으로부터 얻은 것들입니다. 검색 품질을 개선하고, 대화 기록을 잘 처리하며, 응답이 템플릿화된 느낌이 아니라 개인화된 느낌을 주도록 만드는 실질적이고 구현 수준의 아이디어들입니다.
의도 탐지(Intent Detection)의 재도입
RAG가 기본 접근 방식이 되기 전, 챗봇 빌더들은 의도 탐지 (intent detection) — 들어오는 질문을 카테고리(급여 질문, 지원 요청 등)로 분류하고 각 질문을 사전에 작성된 주제 전문가 승인 답변에 매핑하는 방식 — に 크게 의존했습니다. RAG가 존재한다고 해서 그 방식이 사라진 것은 아닙니다. RAG와 결합할 가치가 충분합니다.
패턴: 사용자의 질문을 가져와 의도를 예측하고, 만약 알려진 카테고리와 일치한다면 미리 작성된 답변을 직접 제공합니다. 이를 통해 신뢰도가 높고 자주 묻는 질문에 대해서는 LLM과 벡터 검색(vector search)을 완전히 건너뛸 수 있습니다. 확신할 수 있는 의도가 예측되지 않을 때만 표준 RAG(문서 저장소에 대한 벡터 검색)로 전환합니다. 이 방식은 가장 흔한 질문들에 대해 속도, 비용 절감, 답변 일관성을 제공하며, RAG는 롱테일(long tail) 질문들을 처리하게 합니다.
의도 계층으로서의 함수 호출 (Function Calling)
동일한 아이디어의 더 현대적인 변형은 다음과 같습니다: 전용 NLU 모델 대신 함수/도구 호출 (function/tool calling) (대부분의 주요 LLM SDK에서 사용 가능 — OpenAI의 function calling, Claude의 tool calling 등)을 의도 탐지 메커니즘으로 사용하는 것입니다.
만약 함수가 예측된다면 — 예를 들어, 사용자가 "면접 일정을 잡고 싶어요"라고 말하고 모델이 schedule_interview 함수를 예측한다면 — 실제 동작을 트리거할 수 있습니다: 스케줄링 API를 호출하거나, 외부 소스에서 최신 데이터를 가져오거나, 나중에 사용하기 위해 사용자 정보를 저장하는 등의 작업입니다. 만약 예측된 함수가 없다면, 표준 검색 (retrieval) 단계로 넘어갑니다. 이는 "RAG"와 "에이전트 (agent)" 사이의 경계를 모호하게 만듭니다. 즉, 검색은 API 호출, 데이터베이스 쿼리 또는 기타 도구 호출과 함께 시스템이 취할 수 있는 여러 가능한 경로 중 하나가 됩니다.
답변만이 아닌, 질문을 인덱싱하라
이것은 검색 품질을 높이기 위해 가장 영향력이 크면서도 구현하기 쉬운 트릭 중 하나입니다: FAQ를 가공되지 않은 답변 텍스트가 아닌, 질문별로 인덱싱하여 저장하십시오.
그 직관은 다음과 같습니다. 만약 _"개발자 급여는 8만 달러입니다"_와 같은 가공되지 않은 사실을 저장하고 사용자가 "개발자 급여가 얼마인가요?"라고 묻는다면, 질문과 해당 사실 사이의 의미론적 거리 (semantic distance)는 — 어느 정도 가깝기는 하겠지만 — 최적의 상태만큼 가깝지는 않습니다. 하지만 대신 질문 자체("개발자 급여가 얼마인가요?")를 인덱싱된 벡터로 저장하고 답변을 메타데이터로 부착한다면, 유사하게 표현된 사용자의 질문은 저장된 질문과 거의 직접적으로 매핑됩니다. 이는 훨씬 더 긴밀한 일치(match)를 제공하며, 의미론적으로 훨씬 더 신뢰할 수 있는 검색을 가능하게 합니다.
대규모 구축 방법: FAQ를 수동으로 작성하는 것은 확장성이 떨어집니다. 대신, 이를 자동으로 생성하십시오: 기존의 문서 청크 (chunk)를 "답변"으로 취급하고, 각 청크에 대해 LLM을 사용하여 해당 청크가 답하는 질문을 생성합니다. 생성된 질문을 검색 키 (retrieval key)로 저장하고, 원래의 청크를 관련 답변으로 저장하십시오.
또한 이를 폴백 계층 구조 (fallback hierarchy)로 구성할 수도 있습니다: 먼저 FAQ를 확인하고 (FAQ는 큐레이션되었으며 신뢰할 수 있기 때문입니다), FAQ 매칭이 발견되지 않을 경우에만 가공되지 않은 문서 검색으로 폴백하십시오.
청킹 (Chunking)에 대해 구체적으로 말하자면: 보편적으로 "정답"인 청크 크기는 없습니다. 이는 청크 크기의 문제라기보다 일관된 데이터 형식의 문제이며, 이상적인 청크는 여러 주제를 섞기보다는 하나의 일관된 주제를 다루어야 합니다.
쿼리 재구문 (Query Rephrasing) 및 멀티 쿼리 확장 (Multi-Query Expansion)
실제로 검색되는 내용을 개선하는 것을 목표로 하며, 서로 반대 방향으로 작동하는 두 가지 기술입니다.
**쿼리 재구문 (Query rephrasing)**은 대화 기록을 사용하여 모호한 후속 질문의 의미를 명확히 합니다. 만약 사용자가 단독으로 "급여가 얼마인가요?"라고 묻는다면, 이는 검색을 수행하기에 너무 추상적입니다. 하지만 대화 기록을 통해 봇이 이전에 "직책이 무엇인가요?"라고 물었고 사용자가 "엔지니어"라고 답한 것이 확인된다면, LLM을 사용하여 쿼리를 대화 기록을 인지한 버전인 "엔지니어의 급여는 얼마인가요?"로 다시 작성할 수 있습니다. 이를 통해 모든 역할에 대한 급여 수치들이 뒤섞여 나오는 대신, 정확히 관련 있는 청크로 검색 범위를 좁힐 수 있습니다.
**멀티 쿼리 확장 (Multi-query expansion)**은 반대 방향으로 작동합니다. 단일 쿼리를 좁히는 대신, 사용자의 질문에 대해 여러 가지 변형된 질문들("얼마나 주나요?", "급여가 어떻게 되나요?", "제가 얼마를 벌게 될까요?")을 생성하고 그 모두에 대해 검색을 수행합니다. 이는 단일 문구로는 놓칠 수 있는 관련 청크를 찾아낼 확률을 높여주지만, 추가적인 LLM 호출과 더 많은 검색 오버헤드(Retrieval overhead)가 발생한다는 비용이 따릅니다. 이는 폴백(Fallback)용으로 남겨두기에 좋은 기술입니다. 먼저 직접적인/재구문된 쿼리를 시도하고, 초기 시도가 빈 결과로 돌아올 경우에만 멀티 쿼리 확장으로 범위를 넓히십시오.
대화 기록 처리 (세 가지 접근 방식과 보너스)
대화 기록을 잘 통합하는 것은 실제 RAG 챗봇을 구축할 때 가장 까다로운 부분 중 하나입니다. 복잡도 순으로 대략 다음과 같은 몇 가지 접근 방식이 있습니다.
1. 원시 기록 주입 (Raw history injection). 가장 간단한 접근 방식입니다. 전체 대화 기록(또는 전체 기록이 비현실적이라면 마지막 5~10회의 대화)을 검색된 컨텍스트 (Context)와 함께 프롬프트에 직접 붙여넣고, 모델이 이를 사용하도록 지시합니다. 효과는 있지만, 대화가 길어질수록 확장성(Scalability)이 떨어집니다.
2. 대화 기록 요약 (Chat history summarization). 전체 대화 기록을 읽고 간결한 요약을 생성하는 추가적인 LLM 패스를 실행하며, 이 요약본을 원문 기록 (Raw transcript) 대신 프롬프트에 삽입합니다. 이를 통해 프롬프트 크기를 작게 유지할 수 있으며, 결정적으로 높은 커스터마이징 (Customizability)이 가능합니다. 즉, 일반적인 요약 대신 사용자의 사례에 따라 중요한 정보(예: 시간이 지남에 따라 사용자 프로필 구축)만을 추출하도록 요약 프롬프트를 조정할 수 있습니다.
3. 과거 대화에 대한 RAG (RAG over past conversations). 요약하는 대신, 과거의 대화 기록(또는 그 요약본)을 그 자체로 검색 가능한 코퍼스 (Corpus)로 취급합니다. 새로운 질문이 들어오면 문서 저장소 (Document store)를 검색함과 동시에 과거 대화에서도 관련 있는 이전 문맥 (Context)을 검색하여, 두 가지 모두를 프롬프트에 전달합니다. 이는 사용자가 현재 세션의 이전 내용뿐만 아니라, 이전 세션에서 논의되었던 내용을 언급할 때 유용합니다.
보너스: 채팅으로부터의 구조화된 데이터 추출 (Structured data extraction from chat). 채팅이 진행되는 동안, 사용자에 대한 특정하고 안정적인 사실을 뽑아내는 추출 단계를 실행합니다. 예를 들어, 언어 학습 챗봇의 경우 사용자의 영어 수준이 B2라는 사실을 추출하여 저장하는 식입니다. 이를 자유 형식의 텍스트 (Free text)가 아닌 구조화된 데이터 (SQL, JSON)로 영구 저장합니다. 이렇게 하면 매번 원문 대화에서 다시 유도해낼 필요 없이, 해당 사용자의 향후 모든 프롬프트에 주입할 수 있는 지속적인 속성 (Durable property)이 됩니다.
안정적인 사용자 속성 vs. 변동하는 사용자 속성 (Stable vs. Fluctuating User Properties)
BMW 챗봇 사례 연구에서 빌려온, 개인화 (Personalization)를 위한 특히 유용한 사고 모델(Mental model)은 다음과 같습니다: 사용자에 대해 알고 있는 정보를 두 가지 범주로 나누는 것입니다.
- Stable properties (안정적 속성) — 대화 전반에 걸쳐(그리고 종종 세션 전반에 걸쳐) 유지되는 것들입니다: 기술적 숙련도, 역할, 언어 수준, 선호도 등입니다. LLM을 통해 대화 기록을 한 번 훑어 이 정보들을 추출한 뒤, 향후 모든 프롬프트에 주입하십시오.
- Fluctuating properties (변동적 속성) — 현재 순간에만 유효한 것들입니다: 사용자가 지금 좌절하고 있다거나, 서두르고 있다거나, 확신이 없어 보이는 상태 등입니다. 이를 매 턴(turn)마다 예측하여(장기적으로 저장하지 않음) 오직 _현재_의 응답(톤, 속도, 상세 수준)을 조정하는 데 사용하십시오.
변동적 속성에 가벼운 규칙 레이어(rules layer)를 부착할 수도 있습니다. 예를 들어, "사용자가 좌절한 것처럼 보이면 X를 하고, 확신이 없어 보이면 Y를 하라"와 같이 고객 지원 및 영업의 베스트 프랙티스를 하드코딩된 로직이 아닌 검색 가능한 가이드라인으로 인코딩하는 방식입니다. 이러한 가이드라인 자체는 LLM이 과거의 성공적인 지원 대화 코퍼스(corpus)를 분석하게 함으로써 대규모로 추출할 수 있습니다.
추측하는 대신 명확한 질문 던지기
직접적으로 가져올 만한 가치가 있는 기술은 다음과 같습니다: 모호한 검색 결과로부터 항상 답변을 강제하는 대신, 시스템이 실제로 자신 있게 답변할 수 있을 만큼 충분한 정보를 가지고 있는지 결정하는 체크 단계를 추가하고, 정보가 부족하다면 명확한 질문을 던지는 것입니다.
흐름은 다음과 같습니다:
- 명확성을 위해 사용자의 질의를 재작성합니다 (예: "얼마나" → "급여가 얼마인가요")
- 평소처럼 청크(chunks)를 검색합니다.
- 추가적인 LLM 체크를 실행합니다: 사용자의 질문과 검색된 컨텍스트(context)를 고려할 때, 이것이 자신 있게 답변할 수 있을 만큼 구체적인가, 아니면 무언가 누락되었는가?
- 정보가 누락된 경우 (예: 검색된 청크가 "엔지니어"와 "의사"의 급여를 모두 다루고 있지만, 사용자가 어느 쪽인지 명시하지 않은 경우), 추측하는 대신 명확한 질문을 생성합니다 ("엔지니어이신가요, 아니면 의사이신가요?")
- 그렇지 않다면, 직접 답변합니다.
이 방식은 매우 구체적이고 분기되는 문서가 있는 도메인(국가와 요금제에 따라 달라지는 통신 요금 등이 좋은 예시입니다)에서 특히 가치 있는데, 이러한 곳에서는 잘못된 추측이 후속 질문보다 더 나쁘기 때문입니다.
가장 중요한 단 하나의 조언
단 한 가지만 기억해야 한다면: 데이터 품질 (data quality)이 모든 검색 기법 (retrieval trick)을 압도합니다. 쓰레기가 들어가면 쓰레기가 나옵니다 (Garbage in, garbage out). 아무리 영리한 쿼리 재작성 (query rewriting), FAQ 인덱싱 (FAQ indexing), 또는 명확화 질문 (clarifying-question) 로직을 사용하더라도, 오래되었거나 모순되거나 제대로 정리되지 않은 소스 데이터(source data)를 보완할 수는 없습니다. 다운스트림 (downstream)의 무엇인가를 최적화하기 전에, 소스 데이터의 정제 (cleaning), 중복 제거 (deduplicating), 그리고 모순 여부 확인에 먼저 투자하십시오.
그 외에 이 목록에서 가장 영향력이 크고 구현하기 쉬운 기술은 FAQ 인덱싱 (FAQ-indexing) 방식 (대규모로 생성된 '질문-as-키' 방식)과 명확화 질문 (clarifying-questions) 패턴입니다. 이 두 가지 모두 기존 RAG 시스템에 비교적 간단하게 결합할 수 있으며, 체감되는 답변 품질을 비약적으로 향상시키는 경향이 있습니다.
이러한 기술들은 서로 배타적이지 않습니다. 대부분의 프로덕션 RAG (production RAG) 시스템은 결국 여러 기술을 결합하게 됩니다. 즉, 1차 단계로서의 의도/함수 호출 라우팅 (intent/function-call routing), 일반적인 질문을 위한 FAQ 스타일의 검색 (FAQ-style retrieval), 모호한 후속 질문을 위한 쿼리 재작성 (query rewriting), 그리고 시간이 지남에 따른 개인화를 위한 채팅 기록 요약 (chat-history summarization) 또는 추출 (extraction) 등을 함께 사용합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기