기술

RAG 검색 경로를 둘로 나눴더니, 넓은 질문과 좁은 질문이 각자 답을 찾았습니다

2026.04.139분 읽기

커뮤니티 요약까지 만들었는데, 그다음이 문제였습니다

이전 글에서 Leiden 알고리즘으로 클러스터를 찾고, LLM으로 요약을 만들어서 Level 0→1→2 계층까지 쌓았거든요. "인증서 갱신 누가 했어?"는 Level 0에서, "올해 인프라 전체 현황은?"은 Level 2에서 찾을 수 있는 구조였습니다.

그런데 막상 파이프라인에 붙이려니까, 한 가지가 빠져 있었어요. 질문이 들어왔을 때 이 질문을 커뮤니티 요약에서 찾아야 하는지, 개별 문서에서 찾아야 하는지를 누가 판단하느냐는 거였습니다. 요약을 만들어놓기만 하고, 꺼내 쓸 타이밍을 정하지 않은 거죠.


Local Search와 Global Search, 둘은 역할이 다릅니다

정리하면 검색 경로가 두 가지입니다.

Local Search는 기존에 해오던 방식이에요. 질문을 벡터로 변환하고, Qdrant에서 개별 문서·이메일·엔티티 중 가장 유사한 것을 top-K로 가져옵니다. "물류서버 IP가 뭐야?" 같은 질문에는 이게 정확하거든요. 딱 맞는 문서 한두 건을 찾으면 끝이니까요.

Global Search는 커뮤니티 요약을 검색하는 방식입니다. 개별 문서가 아니라, 클러스터 요약 20~30장을 검색해서 넓은 범위의 답변을 만듭니다. "올해 인프라 이슈 전체 정리해줘"처럼 특정 문서 하나로는 답이 안 되는 질문에 필요한 경로예요.

Local Search

Global Search

검색 대상

개별 문서, 이메일, 엔티티

커뮤니티 요약 (Level 0~2)

잘 맞는 질문

좁은 질문 (특정 사실, 수치, 담당자)

넓은 질문 (전체 현황, 기간별 정리, 트렌드)

top-K 커버리지

관련 문서 중 일부만

전체 데이터가 요약에 반영

답변 성격

정확한 팩트 1~2개

여러 주제를 아우르는 종합

문제는 이 둘 중 뭘 쓸지를 질문이 들어올 때 실시간으로 결정해야 한다는 점입니다.

1.jpeg


경로를 나누는 방법, 세 가지를 시도했습니다

질문의 "범위"를 판별하는 방법을 여러 가지 고민했거든요.

LLM한테 맡기기

첫 번째로 떠오른 건 LLM 판별이었습니다. 질문을 LLM에 넣고 "이 질문이 넓은 질문인지 좁은 질문인지 판단해줘"라고 맡기는 거예요. 직관적이고 구현도 간단합니다.

그런데 이미 30번 글에서 LLM 라우팅의 한계를 경험했거든요. LLM한테 검색 경로를 맡겼더니 프롬프트를 아무리 고쳐도 확률적으로 틀리는 문제가 있었습니다. 똑같은 구조를 또 쓰면 같은 문제가 반복될 게 뻔했어요.

게다가 의도분석에 LLM을 한 번 더 호출하면 응답 시간이 늘어납니다. 22번 글에서 의도분석이 병목이었던 경험도 있고요.

키워드 기반 규칙

두 번째는 키워드 매칭이었습니다. "전체", "정리", "현황", "추이" 같은 단어가 들어가면 Global Search로, 아니면 Local Search로 보내는 규칙이요.

간단하고 빠르지만, 한계가 명확합니다. "전체 서버 목록 중에 물류서버 IP만 알려줘"는 "전체"가 들어가지만 실제로는 좁은 질문이거든요. 반대로 "최근에 뭐가 바뀌었지?"는 키워드가 없지만 넓은 질문입니다.

둘 다 쓰고 합치기

세 번째가 결국 선택한 방식입니다. 경로를 나누지 않고, 두 경로를 항상 같이 돌리는 거예요.

질문이 들어오면 Local Search와 Global Search를 병렬로 실행합니다. Local에서는 개별 문서 top-K를, Global에서는 커뮤니티 요약 top-K를 각각 가져오고, 두 결과를 합쳐서 LLM에 넣습니다.

방법

장점

단점

LLM 판별

직관적

확률적 오류, 응답 시간 증가

키워드 규칙

빠름

오분류 많음

둘 다 실행

판별 오류 없음

검색 비용 2배


둘 다 돌리면 비용이 2배 아닌가?

처음엔 이게 낭비처럼 느껴졌거든요. 좁은 질문에는 Global Search 결과가 필요 없고, 넓은 질문에는 Local Search 결과가 부족할 텐데 둘 다 돌리면 절반은 버리는 게 아닌가 싶었습니다.

그런데 실제로 돌려보니 상황이 달랐어요.

좁은 질문("물류서버 IP가 뭐야?")에서 Local Search가 정확한 답을 가져오면, Global Search 결과는 LLM이 알아서 무시합니다. 커뮤니티 요약에 "물류서버 IP: 10.0.1.5"가 명시적으로 들어있지 않으면 LLM이 Local 결과만 참조하거든요. 불필요한 결과가 섞여도 LLM이 필터링하는 셈이에요.

넓은 질문("올해 인프라 이슈 정리해줘")에서는 반대로 Global Search 결과가 답변의 뼈대가 되고, Local Search에서 잡힌 개별 문서가 세부 팩트를 보충합니다. 오히려 두 결과가 합쳐지면서 답변이 더 풍부해지더라고요.

검색 비용이 2배라고 했는데, 실제로는 그렇게 크지 않았습니다. Qdrant 벡터 검색은 밀리초 단위라 두 번 호출해도 체감 차이가 거의 없고, 진짜 비용은 LLM 호출이거든요. LLM은 한 번만 호출하면서 두 검색 결과를 합쳐서 넣으니까, 전체 비용에서 검색 2회의 추가분은 미미했습니다.

2.jpeg


검색 레벨은 어떻게 고르는가

Global Search에서 커뮤니티 요약을 검색한다고 했는데, Level 0·1·2 중 어떤 레벨의 요약을 검색할지도 결정해야 합니다.

여기서도 같은 원리를 적용했어요. 레벨을 고르지 않고, 전 레벨 요약을 한꺼번에 검색 대상에 넣습니다. Level 0 요약, Level 1 요약, Level 2 요약이 전부 Qdrant에 임베딩으로 들어가 있으니까, 벡터 검색이 질문과 가장 유사한 요약을 알아서 골라줍니다.

"인증서 갱신 누가 했어?"라는 질문에는 Level 0 요약("SSL 인증서 갱신, 담당자 A가 진행...")이 유사도가 높게 잡히고, "올해 인프라 전체 현황 알려줘"라는 질문에는 Level 2 요약("2025년 인프라 운영 전반...")이 유사도가 높게 잡힙니다. 레벨 선택을 사람이나 LLM이 하는 게 아니라, 벡터 유사도가 자동으로 해주는 거예요.

이 방식이 작동하는 이유가 있습니다. 각 레벨의 요약이 담고 있는 추상화 수준이 다르거든요. Level 0 요약에는 구체적 사실("인증서 갱신", "담당자 A")이 들어있고, Level 2 요약에는 상위 개념("인프라 운영 전반", "주요 이슈 현황")이 들어있습니다. 질문의 추상화 수준과 요약의 추상화 수준이 자연스럽게 매칭되는 구조예요.


판별을 안 하는 게 오히려 정확했습니다

시리즈를 돌아보면, 검색 경로를 "정확하게 나누는 것"에 집착하다가 시간을 많이 썼거든요. LLM 판별, 키워드 규칙, 경로 설명 수정... 전부 "어떤 질문에 어떤 경로가 맞는지"를 판단하려는 시도였습니다.

그런데 결론은 판별 자체를 없애는 것이었어요. 두 경로를 항상 같이 돌리고, 어떤 결과를 쓸지는 LLM이 답변을 생성할 때 알아서 고르게 하면 됐습니다.

이전에 온톨로지 기반 라우팅을 도입했을 때도 비슷한 패턴이었거든요. LLM한테 라우팅을 맡기는 대신, 데이터 구조가 알아서 보정하게 만들었던 것처럼, 여기서도 벡터 유사도라는 데이터 구조가 알아서 경로를 선택하게 한 셈입니다.

검색 파이프라인의 최종 구조는 이렇게 됐습니다.

질문 → [Local Search + Global Search 병렬 실행]
       → Local: 개별 문서/엔티티 top-K (Qdrant)
       → Global: 커뮤니티 요약 전 레벨 top-K (Qdrant)
       → 두 결과 합산 → LLM 답변 생성

1편에서 넓은 질문에 top-K가 한계라는 걸 발견하고, 2편에서 커뮤니티 요약으로 커버리지를 100%로 만들었고, 이번 글에서 두 검색을 합쳐서 실제로 쓸 수 있는 파이프라인이 완성됐습니다.