[AI 실험실]/[개인 프로젝트] GraphRAG / GraphRAG | Rank Guard 적용·거절 근거 리포트.md

GraphRAG | Rank Guard 적용·거절 근거 리포트

조회

시리즈: GraphRAG 구축기 #28

이전: 27편 | 목록 | 다음 없음

2026년 7월 17일 | 개인 프로젝트 · GraphRAG


Rank Guard가 발동한 결과만 기록하면 성공한 한 건은 잘 설명할 수 있지만, 나머지 질의가 왜 그대로였는지는 알기 어렵다. 지난 회차에 문서 브리지 질의의 순위를 좁게 보정한 뒤 trace를 다시 읽다가 이 빈칸이 눈에 걸렸다. 정책이 scope에서 걸러졌는지, 현재 1위가 이미 완전 커버인지, 대안 후보가 없었는지, 점수 차이가 허용 범위를 넘었는지가 모두 같은 “미발동”으로 남아 있었다.

이번 회차에는 검색 결과와 함께 Rank Guard의 결정 과정을 돌려주는 경로를 추가했다. 최종 순위가 바뀐 경우뿐 아니라 건너뛴 이유도 질의별로 남기고, 여섯 질의의 상태를 한 파일에 집계했다. 순위 보정 자체를 더 세게 만든 작업은 아니다. 오히려 작은 예외 정책이 어디까지 개입했는지를 나중에 되짚을 수 있게 한 작업에 가깝다.

GraphRAG Rank Guard가 6개 질의를 scope mismatch 4건, 기존 full coverage 1건, applied 1건으로 분기하는 결정 흐름
Figure 1. 여섯 질의를 적용·거절 이유로 나눈 Rank Guard 결정 흐름

발동한 근거만으로는 절반만 설명됐다

이전 구현은 guard가 적용되면 승격 후보와 비교 후보의 base score, coverage, supporting-edge 수, 실제 gap, adjustment를 guard_evidence에 저장했다. top_k=1에서도 비교 청크가 결과 밖으로 사라지지 않도록 근거를 별도 객체로 남겼기 때문에, 적용된 한 건은 꽤 잘 재현할 수 있었다.

문제는 적용되지 않은 질의였다. 샘플 query set에는 같은 doc-bridges cluster의 안정 질의도 있고, 전혀 다른 incident-corecoverage-sensitive 질의도 있다. 모두 순위가 유지됐지만 이유는 서로 달랐다. 이 차이를 보려면 query metadata와 Top-1 coverage를 사람이 따로 맞춰 봐야 했다. 나는 정책이 작을수록 이런 비적용 근거가 더 중요하다고 봤다. 예외 범위를 좁혔다고 말하려면 바뀐 한 건만이 아니라 바뀌지 않은 다섯 건의 종료 지점도 보여야 하기 때문이다.

검색 결과와 decision audit을 같이 반환

기존 search()의 반환형은 그대로 유지하고, 결과 목록과 decision 목록을 묶어 주는 search_with_audit()를 새로 만들었다. 일반 호출은 전처럼 결과만 받는다. profile evaluator처럼 정책 판독이 필요한 호출부만 audit이 포함된 응답을 사용한다. 덕분에 기존 CLI나 pipeline을 깨지 않고 평가 경로를 확장할 수 있었다.

decision에는 rule ID, 실제 query context, guard가 요구한 cluster와 tag, 비교 청크, 대안 후보, base-score gap, 최대 허용 gap, 남은 headroom을 기록한다. 종료 상태는 scope 불일치, 기존 Top-1의 완전 커버, edge 수 조건 불일치, 대안 후보 부재, gap 초과, 실제 적용처럼 나눴다. profile evaluator는 이 값을 각 trace의 rank_guard_audits에 넣고 프로파일 단위의 집계 리포트도 만든다.

상태 건수 판독 의미
scope-mismatch 4 cluster 또는 필수 tag가 달라 후보 비교 전 종료
top-already-full-coverage 1 같은 scope지만 현재 1위가 이미 질의 엔터티를 모두 커버
applied 1 후보 모양과 gap 조건을 모두 통과해 순위 보정

여섯 질의에서 정책이 멈춘 자리

전체 query set을 다시 실행하니 scope mismatch 4건, 기존 full coverage 1건, 실제 적용 1건으로 나뉘었다. Payment API와 Dashboard 계열 네 질의는 cluster 또는 tag가 다르므로 후보 점수 비교까지 가지 않았다. 이 네 건에서 Rank Guard가 순위에 영향을 줄 가능성은 없었다.

planner-root-causedoc-bridgesdocs를 모두 만족했다. 그래도 기존 Retrieval planner의 coverage가 이미 1.0이라 top-already-full-coverage에서 멈췄다. 같은 scope에 들어왔다고 무조건 full-coverage 후보를 찾아 나서는 것이 아니라, 현재 결과가 질의 엔터티를 놓친 경우에만 다음 단계로 진행한다는 계약이 실제 trace에도 남았다.

실제로 적용된 것은 지난 회차와 같은 manual-incident 한 건이었다. GraphRAG overview의 base score는 3.3746, Retrieval planner는 3.7085였고 차이는 0.3339였다. 후보의 coverage와 edge 형태까지 조건에 맞아 0.3340을 더했고 최종 점수 3.7086으로 한 칸 올라갔다. 기존 quality gate는 PASS를 유지했다.

0.35 경계에서 0.0161만 남았다

집계 리포트는 임계값에 가장 가까운 decision도 고른다. 현재는 manual-incident가 유일한 gap 비교 사례이고, 허용 gap 0.3500에서 실제 gap 0.3339를 뺀 headroom은 0.0161이다. 숫자만 보면 통과 여유가 아주 크지는 않다. profile 점수가 조금만 흔들려도 적용 여부가 달라질 수 있는 구간이다.

경계 테스트에서는 최대 gap을 0.3338로 낮춰 같은 질의를 다시 돌렸다. 실제 gap 0.3339가 임계값보다 0.0001 커지자 순위는 보정되지 않았고 상태가 score-gap-exceeded, headroom이 -0.0001로 남았다. 반대로 0.35에서는 applied가 유지됐다. 처음에는 단순히 “순위가 안 바뀌어야 한다”만 테스트하려 했는데, decision까지 고정하니 임계값의 포함 관계와 거절 이유를 함께 검증할 수 있었다.

다음에는 미발동 분포를 데이터로 볼 차례

전체 테스트는 65개가 통과했고, 구현과 테스트, 실험 문서를 프로젝트 저장소에 커밋해 원격에도 반영했다. 이번 리포트로 샘플 여섯 건의 종료 이유는 보이지만, 아직 실사용 질의에서 어떤 상태가 많이 나오는지는 모른다. 특히 preferred-candidate-missingscore-gap-exceeded가 반복된다면 rule의 후보 모양이나 최대 gap을 다시 검토할 근거가 된다.

남아 있는 coverage-sensitive 순위 변화는 이번 guard에 합치지 않을 생각이다. 먼저 별도 scope에서 같은 decision audit을 켜고, 현재 1위가 어디에서 탈락하는지부터 볼 예정이다. 한 번 만든 예외 정책에 다른 실패 유형을 계속 넣는 대신, 적용·거절 분포를 보고 정책을 유지할지 지울지 판단하는 쪽이 지금 단계의 GraphRAG MVP에는 더 맞다.

시리즈: GraphRAG 구축기 #28

이전: 27편 | 목록 | 다음 없음

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.