XGEN 15일 무료 체험 — 설치 없이 브라우저에서 바로 시작하세요체험 신청
PlateerAI Labs
·Reading Time | 2 min
작성 | 김진수

빈 그래프를 측정한 A/B에서 시작한 검색 재설계(6편)

그래프가 실제로 호출됐는지부터 검증하고, 질문 유형별 기여도를 바탕으로 ReAct 루프를 병렬 검색과 단일 합성으로 교체합니다.

빈 그래프를 측정한 A/B에서 시작한 검색 재설계(6편) — 커버 일러스트Series · 온톨로지 개발기시리즈 6번째 글 · 전체 10편 보기

지식그래프 설계 · 6/10

ReAct 검색의 목표는 도구를 여러 번 호출하는 데 있지 않았습니다. 관계, 목록, 집계 질문에서 벡터 검색보다 넓은 근거를 찾되 단순 사실 질문까지 느리게 만들지 않는 것이 목표였습니다. 실제 운영 경로에서는 단일 사실에도 여러 턴이 실행됐고, 누적된 원문이 답변을 흐리는 경우가 생겼습니다.

초기 A/B 결과는 멀티턴 검색이 더 느리고 정확도도 낮다고 나왔습니다. 그대로 구조를 되돌리기 전에 실행 기록을 확인했고, 평가가 데이터가 없는 그래프를 조회한 뒤 사실상 벡터 검색만으로 답했다는 것을 찾았습니다. 잘못 연결된 평가로 검색 구조를 결정할 수는 없었습니다.

그래프가 실제로 호출된 실행만 다시 측정한 뒤 질문 유형별 기여를 비교했습니다. 그 결과 관계 탐색의 장점은 유지하면서도 모델 턴을 줄일 수 있도록 벡터, 그래프, 클래스, 정형 데이터의 후보를 병렬로 모으고 한 번만 합성하는 구조로 바꿨습니다.

옵션이 아니라 실제 호출을 확인했습니다

첫 비교에서는 멀티턴 검색이 기존 검색보다 느리고 정확도도 낮게 나왔습니다. 그러나 실행 기록을 따라가 보니 평가 하네스가 표시 이름을 그래프 식별자로 사용하고 있었습니다. 요청은 존재하지만 비어 있는 그래프를 조회했고, 답변은 벡터 검색만으로 만들어졌습니다.

이 결과는 폐기했습니다. 설정에 graph=true가 있다는 사실이 아니라 다음 조건을 모두 만족해야 그래프 검색 실행으로 인정했습니다.

라벨 검색도 전체 문자열 스캔에서 Lucene 기반 jena-text 인덱스로 바꿨습니다. 아래 수치는 이 색인과 dataset assembler를 별도로 준비한 격리 환경의 결과입니다. 배포 환경에 같은 구성이 없으면 text:query 경로는 재현되지 않습니다. 그래프 기능을 켠 상태와 운영 가능한 검색 경로는 같은 말이 아닙니다.

그래프의 기여는 질문 유형마다 달랐습니다

그래프가 실제로 연결된 상태에서 고정 질문 100개를 비교했습니다. 모든 비교는 같은 질문, 답변 모델, 벡터 근거, 시간 예산과 판정 기준을 사용하고 그래프 근거의 사용 여부만 바꿨습니다. 정답성은 같은 판정 기준으로 계산한 0~1 점수입니다. 당시 데이터에서 새 검색 구성의 정답성은 0.465, 기존 구성은 0.230이었고 완전 정답은 각각 34개와 13개였습니다. 이 값은 해당 평가 조건의 결과이지 일반적인 제품 성능 보장은 아닙니다.

더 중요한 결과는 유형별 격리 A/B였습니다. 같은 벡터 근거와 답변 모델을 두고 그래프 근거만 껐다 켰습니다.

질문 유형 벡터만 사용 벡터 + 그래프 해석
집계·전체 나열 0.033 0.683 클래스의 전체 개체 집합이 필요함
관계형 조회 0.292 0.521 주어와 술어를 함께 제한한 관계 탐색이 유효함
단일 사실 약 0.46 약 0.47 의미가 가까운 원문 검색만으로 충분함

그래프는 모든 질문에서 벡터 검색을 대체하지 않았습니다. 답이 연결 구조나 집합 자체인 질문에서 차이를 만들었습니다. 단일 사실 질문에서는 그래프 탐색을 더해도 이득이 거의 없었습니다.

질문을 분류하지 않고 네 종류의 근거를 병렬로 모았습니다

질문 앞에 별도의 LLM 분류기를 두면 오분류가 새로운 실패 지점이 됩니다. 대신 비용이 제한된 검색 경로를 병렬로 실행하고, 구조 신호가 없는 경로는 빈 결과를 반환하도록 했습니다.

벡터 원문 검색 ─────────────┐
라벨·이웃 연결성 검색 ──────┤
클래스 전체 집합 조회 ──────┼─→ 근거 정렬 ─→ 답변 합성 1회
주어·술어 정밀 관계 조회 ───┘

클래스 조회는 질문 용어가 실제 클래스 라벨로 해소될 때만 실행합니다. 관계 조회는 질문의 관계어가 그래프의 술어 라벨과 맞을 때 주어 후보와 술어를 함께 제한합니다. 단어 목록을 프롬프트에 하드코딩하지 않고 현재 그래프의 클래스와 술어가 게이트가 됩니다.

검색 결과는 한 번만 합성합니다. 루프가 “근거가 충분한가”를 반복 판단하게 두지 않고, 각 검색기가 맡은 연산을 결정적으로 수행한 뒤 합성기는 모인 근거로 답을 만듭니다. 클래스 전체 목록은 합성 입력의 앞쪽에 두고 출력 예산도 별도로 확보해 “모두”라는 요구가 요약 과정에서 잘리지 않게 했습니다.

좋은 질의 계획이 정확도와 속도를 함께 결정했습니다

정밀 관계 조회의 첫 구현은 술어 전체를 훑어 정확했지만 모든 질문에서 느렸습니다. 엔티티를 먼저 text:query로 바인딩하고, 그 주변에서 매칭된 술어만 검사하도록 순서를 바꿨습니다. URI 문자열을 직접 조합하지 않아 괄호와 공백이 있는 식별자도 안전하게 처리했습니다.

이 구조에서는 관계형 점수가 0.292에서 0.521로 올라갔고 단일 사실 질문의 속도도 유지됐습니다. 최종 단일 합성 구성은 모든 질문을 한 턴에 처리했고, 해당 평가에서 중앙값은 약 2.4초였으며 요청의 95%가 4.1초 안에 끝났습니다.

측정 결과 멀티턴 ReAct가 잘못된 아이디어였던 것은 아닙니다. 관계 검색의 도구와 탐색 경로를 빠르게 확장하는 데 필요했습니다. 다만 색인, 클래스 집합, 정밀 관계 질의가 갖춰진 뒤에는 매 질문마다 모델이 도구 순서를 다시 발견할 필요가 줄었습니다. 그래서 검증한 기능 경로에서는 ReAct 루프를 제거하고 병렬 검색과 단일 합성을 남겼습니다. 이 시점의 결과는 기능 브랜치와 격리 환경 검증이며 운영 배포 완료를 뜻하지 않습니다.

검색 구조를 단일 합성으로 정리한 다음에는 키워드 후보 전체가 아니라 답변에 언급된 노드만 화면에서 구분하는 작업으로 이어졌습니다. 다음 편에서는 그 강조 범위가 어떻게 줄었고, 라벨 기반 연결에 어떤 한계가 남았는지 다룹니다.


이전 편 → CSV 행은 줄이고 정상 클래스는 지키는 방법 다음 편 → 답변에 언급된 그래프 노드만 강조하기

#온톨로지#GraphRAG#검색 평가
블로그 목록으로