[AI 실험실]/[개인 프로젝트] GraphRAG / GraphRAG | Stale transition split 추가.md

GraphRAG | Stale transition split 추가

조회

시리즈: GraphRAG 구축기 #11

이전: 10편 | 목록 | 다음: 12편

2026년 5월 2일 | 개인 프로젝트


GraphRAG quality history를 보다가 같은 정체 상태도 방향이 다르다는 점이 계속 걸렸다. 전 회차에서 만든 stale status queue는 `WARNx3`, `HARD-FAILx3`처럼 오래 반복되는 non-pass profile을 잘 줄여 줬다. 그런데 그 숫자만 보고 있으면 한 가지 중요한 맥락이 빠진다. 이 warning이 hard-fail에서 내려온 뒤 멈춘 것인지, 아니면 pass에서 밀려 올라온 뒤 멈춘 것인지가 보이지 않았다.

이번 반복에서는 그 빈칸을 아주 작게 메웠다. 이름은 거창하지 않게 stale transition split이라고 붙였다. 기존 `history-stale-status.json`에 `stale_transition` 필드를 추가하고, summary 상단에도 `stale_transitions=...` 카운트를 같이 보여 주게 했다. 구현량은 크지 않았지만, 내가 다음 실험을 읽는 순서는 꽤 달라졌다.

GraphRAG stale transition split 구조도

Figure 1: stale status queue에 이전 distinct status 기준의 방향성을 붙인 흐름

1. 왜 status streak만으로는 부족했나

전 단계의 queue는 꽤 유용했다. `PASSx3`인 default와 dense profile은 기본 queue에서 빼고, `WARNx3`이나 `HARD-FAILx3`처럼 다시 열어 볼 profile만 남겼다. 덕분에 query-level trace를 전부 열지 않아도, 어느 profile부터 봐야 하는지 빠르게 좁힐 수 있었다.

그런데 실제로 summary를 읽다 보니 `WARNx3` 하나가 너무 많은 의미를 떠안고 있었다. 나는 처음에는 warning이 오래 이어지면 그냥 관찰 후보라고 생각했다. 하지만 곰곰이 보면 개선 후 정체악화 후 정체는 완전히 다른 신호다. 같은 warning이어도 이전 run이 hard-fail이었다면 아직 살펴볼 만하고, 이전 run이 pass였다면 더 조심해야 한다.

  • HARD-FAIL → WARNxN: 좋아지긴 했지만 아직 pass까지는 못 올라간 상태
  • PASS → WARNxN: 안정권에서 벗어난 뒤 그대로 멈춘 상태
  • WARN → HARD-FAILxN: 실험 후보에서 더 빨리 제외할 가능성이 큰 상태
  • 처음부터 WARNxN: 방향을 단정하기보다 기준선 자체를 더 쌓아야 하는 상태

이 구분이 없으면 내가 trace bundle을 열 때 자꾸 감으로 판단하게 된다. “이건 괜찮아 보이는데?” 하고 넘긴 profile이 사실은 악화 후 정체일 수도 있고, 반대로 개선 중인 profile을 너무 빨리 버릴 수도 있다. 그래서 이번에는 숫자를 더 많이 늘리기보다, 이미 갖고 있던 `previous_distinct_status`를 queue 해석에 끌어올렸다.

2. 이번에 붙인 작은 분류

구현은 단순하게 갔다. 최신 상태와 이전 distinct 상태를 severity로 비교한다. `pass`는 0, `warning`은 1, `hard-fail`은 2로 놓고, 이전 distinct 상태가 최신 상태보다 더 나쁘면 improved-then-stale, 더 좋으면 worsened-then-stale로 분류한다. 둘의 severity가 같으면 `same-severity-stale`, 이전 distinct 상태가 아예 없으면 `same-status-from-start`로 남겼다.

여기서 마음에 든 부분은 마지막 분류다. 예전 같으면 이전 상태가 없다는 이유로 그냥 null을 지나쳤을 텐데, 이번에는 그 자체를 명시적인 해석으로 만들었다. 실제 운영 history 안에서 이전 distinct 상태가 없다는 말은 “정보가 없다”라기보다 현재 namespace 기준으로 처음부터 이 상태였으니 방향을 과장하지 말라는 뜻에 가깝다.

분류 읽는 방식 다음 행동
improved-then-stale 더 나쁜 상태에서 내려온 뒤 정체 관찰 후보로 유지하고 query family를 더 본다
worsened-then-stale 더 좋은 상태에서 올라간 뒤 정체 원인 trace를 먼저 열거나 후보에서 제외한다
same-status-from-start compatible history 시작부터 같은 상태 방향을 단정하지 않고 기준선을 더 쌓는다

3. 운영 run에서 바로 드러난 것

이번 iter28 operational run에서는 예상과 조금 다르게, 극적인 `improved-then-stale`나 `worsened-then-stale`이 나오지 않았다. stale queue에는 여전히 두 profile만 들어왔다. `path_bridge_probe`는 `WARNx4`, `path_bridge_focus`는 `HARD-FAILx4`였다. 그런데 둘 다 transition은 same-status-from-start였다.

처음에는 이게 조금 허무했다. 기껏 방향 분류를 붙였는데 운영 결과가 전부 “처음부터 같은 상태”로 나온 셈이니까. 그런데 다시 보니 오히려 이게 좋은 guardrail이었다. 이 결과는 “probe가 좋아지다 멈췄다”거나 “focus가 나빠지다 멈췄다”고 말하지 말라는 신호다. 현재 operational namespace 기준으로는 둘 다 compatible history가 시작된 시점부터 이미 그 상태였고, 그래서 방향성을 붙이면 과잉 해석이 된다.

profile latest status streak stale transition 내 해석
relation_weight_dense PASS PASSx4 queue 제외 안정 lane 유지
path_bridge_probe WARN WARNx4 same-status-from-start 관찰 후보지만 개선 방향은 아직 말하지 않음
path_bridge_focus HARD-FAIL HARD-FAILx4 same-status-from-start 장기 제외 후보에 가깝지만 원인 label은 유지

summary 한 줄도 이제 조금 더 설명적이다. 예전에는 `statuses=hard-fail:1, warning:1`까지만 보였다. 이제는 뒤에 `stale_transitions=same-status-from-start:2`가 붙는다. 이 한 조각 덕분에 같은 queue를 열어도 “어느 profile이 나빠졌나?”부터 묻지 않고, “이 namespace에는 아직 방향을 말할 만큼의 이전 상태가 없구나”라고 먼저 멈출 수 있다.

4. 테스트에서 일부러 만든 반대 장면

운영 run만 보면 새 분류가 전부 same-status로 보이기 때문에, 테스트에서는 일부러 더 날카로운 fixture를 만들었다. 하나는 hard-fail에서 warning으로 내려온 뒤 warning이 반복되는 케이스, 다른 하나는 warning에서 hard-fail로 올라간 뒤 hard-fail이 반복되는 케이스다. 이 둘을 같은 stale queue 안에 넣어 `improved-then-stale:1`, `worsened-then-stale:1`이 동시에 나오는지 고정했다.

이런 synthetic fixture를 넣는 게 예전에는 조금 인위적으로 느껴졌는데, history 계층이 생긴 뒤로는 오히려 필요하다는 생각이 든다. 실제 운영 데이터는 특정 상태만 오래 반복될 수 있다. 그렇다고 개선 후 정체나 악화 후 정체를 다음에 처음 만났을 때 코드가 조용히 틀어지면 곤란하다. 그래서 이번에는 운영 run과 별도로 해석 경계 케이스를 테스트에서 먼저 잠가 두는 쪽을 택했다.

5. 다음에 볼 것

이번 변경은 검색 품질 자체를 올리는 작업은 아니다. 대신 품질 이력을 읽는 방법을 조금 더 안전하게 만들었다. GraphRAG 프로젝트가 뒤로 갈수록 profile과 query set이 늘어날 텐데, 그때마다 모든 trace를 열어 볼 수는 없다. 그래서 나는 요즘 계속 어떤 목록을 먼저 열지 줄이는 레이어를 붙이고 있다. mismatch report, scope growth queue, stale status queue가 다 같은 방향의 작업이다.

다음에는 sample query set과 operational query set을 더 분리해서 볼 생각이다. 지금은 operational namespace 안에서도 같은 샘플 문서셋 위주로 history를 쌓고 있다. 이 상태에서는 `same-status-from-start`가 오래 유지되는 profile을 어디까지 제외 후보로 볼지 애매하다. query set을 분리하면, 같은 profile이 sample에서는 오래 hard-fail이지만 operational에서는 warning으로 버티는지 같은 차이를 더 직접적으로 볼 수 있을 것 같다.

작게 보면 필드 하나를 더 붙인 작업이다. 그래도 내가 만족한 부분은, 이번 변경이 “더 똑똑한 점수”가 아니라 덜 성급한 해석을 위해 들어갔다는 점이다. 검색 시스템을 다루다 보면 숫자가 늘어날수록 판단이 더 쉬워질 것 같지만, 실제로는 숫자가 많아질수록 잘못된 이야기를 붙이기도 쉬워진다. 이번 stale transition split은 그 말을 붙이기 전에 한 번 더 멈추게 하는 장치에 가깝다.

시리즈: GraphRAG 구축기 #11

이전: 10편 | 목록 | 다음: 12편

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.