[AI 실험실]/[개인 프로젝트] GraphRAG / GraphRAG | History mismatch 이유별 섹션.md

GraphRAG | History mismatch 이유별 섹션

조회

시리즈: GraphRAG 구축기 #6

이전: 5편 | 목록 | 다음: 7편

2026년 4월 29일 | 프로젝트


history-baseline-mismatches.json을 profile별 목록으로만 보니, 다음에 열 때마다 내가 먼저 해야 하는 일이 하나 남았다. 어떤 profile에서 빠졌는지는 보이는데, 왜 빠졌는지를 이유별로 다시 머릿속에서 접어야 했다. query set이 바뀐 건지, quality gate 기준선이 바뀐 건지, 아니면 dataset이나 top-k가 달라진 건지부터 갈라야 하는데, report 구조는 아직 profile 중심이었다.

이번 GraphRAG 작업은 그 작은 불편을 줄이는 쪽이었다. retrieval 점수를 올리거나 새 profile을 만든 것은 아니다. 대신 history mismatch report 안에 reason_sections를 추가해서, 같은 incompatible entry를 profile별 view와 reason별 view로 동시에 볼 수 있게 했다. 나는 이런 종류의 변경이 꽤 마음에 든다. 겉으로 보이는 기능은 작지만, 나중에 실험 history가 길어졌을 때 오해를 줄여 주는 쪽이기 때문이다.

1. profile별 report만으로는 한 번 더 접어야 했다

지난 GraphRAG 글에서는 baseline mismatch를 별도 JSON report로 빼냈다. 그 전까지는 history_warning=baseline-mismatch(query_set) 같은 한 줄 경고만 있었고, 실제로 어떤 과거 run이 빠졌는지 다시 찾으려면 summary를 꽤 뒤져야 했다. report 파일을 만들고 나니 이 문제는 많이 줄었다.

그런데 막상 report를 읽어 보니, 다음 질문이 바로 생겼다. profile이 네 개라면 같은 query set mismatch가 네 profile에 반복해서 보인다. 이때 내가 먼저 알고 싶은 것은 "default에서 빠졌나, dense에서 빠졌나"가 아니라 이번 mismatch의 주된 이유가 무엇인가다. query 파일을 바꾼 날이면 query set reason만 먼저 보면 되고, gate threshold를 손댄 날이면 quality gate reason부터 보면 된다.

  • profile view: 어느 profile의 history에서 제외됐는지 확인한다.
  • reason view: 같은 제외 이유가 몇 개 profile에 걸렸는지 확인한다.
  • text summary: trace bundle을 열기 전에 reason count를 먼저 본다.
GraphRAG history mismatch reason section flow

Figure 1. compatible history와 mismatch queue를 분리하고, mismatch queue를 reason별로 한 번 더 접는 구조.

2. 이번에 바꾼 구조

구현은 크게 복잡하지 않았다. 기존 report는 top-level에 reason_countsprofiles를 가지고 있었다. 이번에는 같은 incompatible entry를 순회하면서 reason별 bucket도 같이 만들었다. 그래서 report에는 새로 reason_sections가 들어간다.

필드 역할 이번 기준선에서 본 값
profile_entry_count profile-entry 단위 mismatch 개수 4
reason_counts.query_set query set fingerprint 차이로 잡힌 횟수 4
reason_sections[0].profile_count 같은 reason에 걸린 profile 수 4

여기서 일부러 count의 의미를 둘로 나눴다. top-level profile_entry_count는 entry 개수이고, reason section 쪽 count는 reason occurrence에 가깝다. 하나의 과거 run이 query set과 gate를 동시에 어긋나게 만들 수 있기 때문이다. 이런 경우를 한쪽으로 뭉개면 나중에 왜 빠졌는지 다시 애매해진다.

3. probe에서 확인한 결과

테스트는 일부러 단순하게 잡았다. 기존 iter21 quality history를 복사하고, 그 안에 남아 있던 iter18-query-set-probe를 현재 run과 비교하게 했다. 이 probe는 query set fingerprint만 다르게 만든 entry다. 따라서 dataset, top-k, gate 경로를 보느라 시간을 쓰기 전에, report가 query_set reason으로 정확히 접히는지만 보면 된다.

결과는 기대한 대로 나왔다. default, relation_weight_dense, path_bridge_probe, path_bridge_focus 네 profile 모두 같은 incompatible entry를 감지했고, reason section은 query_set 하나로 모였다. text summary에도 history-mismatch-report: ... | incompatible_runs=1 | reasons=query_set:4가 붙었다. 이 한 줄이 생각보다 유용하다. trace bundle을 열기 전에, 이번 mismatch가 gate threshold 문제가 아니라 질문 세트 baseline 문제라는 걸 바로 알려 주기 때문이다.

GraphRAG iter22 reason section summary

Figure 2. query_set reason 하나가 네 profile에 동시에 걸린 iter22 probe 결과.

4. 테스트와 커밋

이번 변경은 retrieval ranking을 바꾸면 안 되는 작업이라서, 테스트도 report payload 쪽에 집중했다. 기존 pipeline과 profile_eval 테스트를 전부 돌렸고, 새 assertion에서는 reason_sections[0].reason, profile_count, profile_entry_count, section 안의 profile 이름까지 확인했다.

commit: 016ad48
message: Add GraphRAG history mismatch reason sections
tests: 27 passed
report: reason_sections[0].reason = query_set
summary: reasons=query_set:4

문서도 같이 갱신했다. README의 Current focus를 history mismatch report에서 reason section으로 바꿨고, iteration 22 노트에는 profile view와 reason view를 왜 같이 두는지 적었다. 이런 기록을 남겨 두지 않으면, 몇 주 뒤에는 내가 왜 단순한 중첩 구조를 하나 더 넣었는지 스스로도 헷갈린다.

5. 다음에 이어갈 부분

이제 history mismatch report는 세 질문에 답한다. 어떤 profile에서 빠졌는가, 어떤 reason으로 빠졌는가, 같은 reason이 몇 개 profile에 반복되는가. 아직 남은 빈칸은 failure scope 변화량과 이 reason section을 같은 inspection queue 안에서 연결하는 일이다.

예를 들어 path_bridge_focus가 HARD-FAIL을 유지하고 있는데 query_set mismatch까지 동시에 있다면, 나는 trace diff를 바로 믿기보다 baseline 변경부터 다시 볼 것이다. 반대로 compatible history만으로 warning scope가 늘어난다면, 그때는 실제 scoring profile 쪽을 의심해도 된다. 결국 이번 변경의 목적은 숫자를 더 많이 보여 주는 게 아니다. 비교 가능한 변화와 비교하면 안 되는 변화를 먼저 분리하는 것. GraphRAG 실험이 길어질수록 이런 작은 guard가 더 오래 버틸 것 같다.

시리즈: GraphRAG 구축기 #6

이전: 5편 | 목록 | 다음: 7편

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.