기술

LLM이 검색 도구를 잘못 골랐을 때, 답변까지 망가지지는 않게 했습니다

2026.05.0510분 읽기

라우팅이 100%가 되지 않았습니다

저희 RAG 시스템에는 LLM 라우팅 단계가 있습니다. 사용자 질문이 들어오면 LLM이 먼저 의도를 분류하고 어떤 검색 도구를 쓸지 결정합니다. 벡터 검색만 쓸지, 그래프 검색을 같이 쓸지, 후속 질문으로 처리할지 같은 결정입니다.

문제는 이 라우팅이 항상 정확하지는 않다는 것이었습니다. "특정 서버에 연결된 시스템이 뭐야?" 같은 질문이 들어오면 그래프 검색이 들어가야 하는데, LLM이 벡터 검색만 돌리고 끝내는 경우가 있었습니다. 답변이 어딘가 비어 있는 상태로 사용자에게 전달됐습니다.

처음에는 프롬프트 엔지니어링으로 풀려고 했습니다. few-shot 예시를 추가하고 분류 기준을 더 명확하게 적었습니다. 그래도 100%는 되지 않았습니다. 라우팅 정확도가 95%까지 올라가도, 매일 들어오는 질문 수를 곱하면 사용자가 체감하는 실패 빈도는 무시할 수 없는 수준이었습니다.


옵션 네 가지를 검토했습니다

라우팅 정확도를 끌어올리는 방법으로 검토한 옵션은 네 가지였습니다.

방식

설명

비용

한계

few-shot 예시 강화

프롬프트에 예시 추가

낮음

이미 적용. 더 늘려도 한계

2단계 LLM 라우팅

1차 LLM이 라우팅, 2차 LLM이 보정

LLM 호출 2배

비용 부담

classifier 학습

질문-라우팅 쌍으로 분류 모델 학습

데이터 수집 + 학습 비용

데이터 충분히 모이기 전엔 적용 어려움

Hybrid (LLM + 자동 보강)

LLM 판단을 그대로 두고, 보조 시그널로 보강

낮음

보강 로직 설계 필요

각 옵션을 검토한 결과를 정리해보겠습니다.

few-shot은 이미 적용한 상태였습니다. 프롬프트에 의도별 예시를 7~8개씩 넣어둔 상태였는데, 그 이상은 효과 대비 프롬프트 토큰만 늘었습니다.

2단계 LLM은 비용이 부담스러웠습니다. 기존에 채팅 한 건당 LLM이 평균 3~4회 호출되고 있었는데, 라우팅을 2단계로 만들면 한 번 더 늘어납니다. 모든 질문이 라우팅 실패에 해당하는 건 아닌데 일괄적으로 LLM 호출을 늘리는 건 효율이 떨어졌습니다.

classifier 학습은 데이터 측면에서 보류했습니다. 사용자 질문 로그가 아직 충분히 모이지 않은 상태였습니다. 데이터가 충분히 쌓이고 라우팅 패턴이 안정된 후에 classifier를 도입하는 것은 좋은 다음 단계가 될 수 있지만, 지금 적용하기엔 시기상조였습니다.

남은 것이 Hybrid 였습니다.


Hybrid 보강 — LLM이 틀려도 답변은 보장됩니다

Hybrid의 핵심 발상은 "LLM 라우팅이 실패하더라도 답변 품질이 안 망가지게 한다"입니다. LLM 판단을 100%로 만들려고 매달리지 않고, 그 옆에 보조 시그널을 두어서 빠진 정보를 자동으로 채우는 구조입니다.

저희가 선택한 보조 시그널은 그래프 엔티티 감지였습니다.

질문: "특정 서버에 연결된 시스템이 뭐야?"
  ↓
1. LLM 라우팅 → vector (잘못 라우팅됨)
2. 질문에서 "특정 서버" 엔티티 감지
3. Neo4j에 해당 노드 존재 확인 → 있음
4. 자동으로 graph_neighbors 결과 추가
5. LLM이 벡터 결과 + 그래프 결과를 합쳐서 답변

LLM이 vector로 잘못 분류해도, 질문 안에 그래프에 존재하는 엔티티 이름이 있으면 자동으로 그래프 검색이 추가됩니다. 사용자는 라우팅이 틀렸는지 모르고, 답변에는 그래프 정보까지 포함되어 도착합니다.

1.jpeg


키워드 하드코딩 대신 그래프 존재 확인을 썼습니다

Hybrid 구조를 처음 떠올렸을 때 가장 먼저 든 생각은 "키워드 리스트를 만들어서 매칭하면 되겠네"였습니다. "연결된", "의존하는", "담당자" 같은 키워드가 들어오면 그래프 검색을 추가하는 방식입니다.

이 방식은 한 번만 만들고 끝나지 않습니다. 새 도메인 용어가 추가될 때마다 키워드 리스트를 늘려야 하고, 다국어를 지원해야 하면 더 복잡해집니다. 그리고 키워드 리스트와 LLM 프롬프트가 따로 놀게 되어서 어느 한쪽 업데이트를 빠뜨리면 동기화 문제가 생깁니다.

그래서 키워드 대신 그래프 존재 확인을 보조 시그널로 썼습니다.

# 의사 코드
entities = extract_entities_from_query(question)
for entity in entities:
    if neo4j_node_exists(entity):
        graph_results = graph_neighbors(entity)
        results.extend(graph_results)

질문에서 엔티티 후보를 뽑고, 그게 그래프에 실제로 존재하는 노드인지만 확인합니다. 존재하면 그래프 검색을 자동으로 추가합니다.

이 방식의 장점은 다음과 같습니다.

키워드 관리 불필요: 그래프에 노드가 있으면 자동으로 보강 대상이 됩니다

확장 자동: 새 시스템이 추가되어도 코드를 고칠 필요가 없습니다

다국어 영향 없음: 노드 이름은 보통 시스템명·인명이라 언어 무관입니다

LLM 라우팅과 독립: 한쪽이 실패해도 다른 쪽이 보완합니다

엔티티 추출 자체는 LLM에 의존하지만, 이 LLM 호출은 채팅 전반에서 한 번만 실행되고 결과가 라우팅과 보강 양쪽에서 재사용됩니다. 추가 LLM 호출은 발생하지 않았습니다.


라우팅을 "정답 찾기"가 아니라 "최선 추천"으로 봤습니다

이 작업을 하면서 시각이 한 번 바뀐 지점이 있었습니다.

처음에는 라우팅을 "정답을 골라야 하는 분류 작업"으로 봤습니다. 의도가 5개라면 그중 정확히 하나를 맞춰야 한다는 시각이었습니다. 그래서 LLM이 틀리면 "왜 틀렸지?"가 첫 질문이었고, 프롬프트로 이걸 막으려고 했습니다.

Hybrid를 도입하면서 시각이 바뀌었습니다. 라우팅은 "최선 추천"이고, 그 추천이 부정확할 때 시스템 전체가 보완하면 됩니다. LLM 한 곳에 정확도 부담을 다 떠넘기지 않고, 다른 시그널들이 함께 책임지는 구조입니다.

비슷한 발상은 다른 영역에서도 본 적이 있었습니다. CRAG는 검색 결과 품질이 부족할 때 자동으로 쿼리를 재작성합니다. 검색을 한 번에 정답으로 만들려고 하지 않고, 부족하면 다시 시도하는 구조입니다.

LLM 단일 호출에 정답을 강요하는 게 아니라, 시스템 차원에서 회복 가능한 구조를 만드는 것. 이게 더 현실적인 접근이었습니다.

2.jpeg


사용자 피드백을 라우팅 거버넌스로 발전시킬 계획입니다

지금은 Hybrid 보강이 일방향으로만 동작합니다. LLM이 틀린 라우팅을 시스템이 보완하기는 하지만, "이번에 LLM이 틀렸다"는 정보가 어디에도 누적되지 않습니다.

다음 단계로 생각하고 있는 것은 사용자 피드백을 라우팅 개선에 반영하는 루프입니다. 사용자가 "이 답변은 그래프 검색이 부족했어요"라고 피드백하면, 그 질문 패턴을 라우팅 규칙 후보로 누적하는 구조입니다.

비슷한 발상을 온톨로지 거버넌스에서 이미 적용하고 있습니다. 미매핑 엔티티가 임계 누적되면 정규 타입으로 승격할지 검토하는 흐름입니다.

라우팅 거버넌스도 같은 패턴이 됩니다.

LLM 라우팅 실패 → 사용자 피드백 누적 → 임계 도달 시 검토 → 규칙 또는 학습 데이터로 승격

이렇게 누적된 데이터가 충분히 쌓이면 그때 classifier 학습으로도 갈 수 있습니다. 보류했던 옵션이 시간이 지나면서 자연스럽게 가능해지는 경로입니다.


마무리

LLM 라우팅 정확도를 100%로 만들려고 매달리는 대신, 라우팅이 실패해도 답변 품질이 보장되는 보강 구조를 추가했습니다. 키워드 하드코딩 대신 그래프 노드 존재 확인을 보조 시그널로 사용했고, 새 시스템이 추가되어도 코드를 고칠 필요가 없는 구조로 만들었습니다. LLM 단일 호출에 정확도 부담을 다 떠넘기지 않고 시스템 차원에서 회복 가능한 형태로 설계했습니다.

LLM 라우팅 정확도 때문에 고민 중이라면, 정확도 자체를 끌어올리는 시도와 함께 "라우팅이 틀려도 답변은 보장된다"는 구조를 같이 깔아두는 것을 추천합니다. 한쪽만으로는 사용자 체감 품질이 잘 올라오지 않습니다.

#LLM라우팅#RAG#Hybrid검색#Neo4j#엔티티감지#답변품질#AI전환기