[개발 일기] / GraphRAG | History 상태 반복 횟수 추가.md

GraphRAG | History 상태 반복 횟수 추가

조회

시리즈: GraphRAG 구축기 #9

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

2026년 5월 1일 | 개발 일기


GraphRAG profile_eval의 history 줄은 점점 길어지고 있었다. 처음에는 실행 결과를 한 번 더 저장해 두는 정도였는데, 어느 순간부터는 quality gate, history transition, baseline mismatch, namespace, scope growth queue까지 붙었다. 기능이 늘어난 건 좋은데, 내가 실제로 summary를 볼 때 제일 먼저 알고 싶은 건 더 단순했다. 이 profile이 지금 막 바뀐 건지, 아니면 같은 상태로 계속 버티고 있는지였다.

그래서 이번에는 점수를 올리는 쪽이 아니라, 상태가 몇 run째 반복되는지를 보여 주는 작은 필드를 붙였다. 이름은 코드 안에서는 latest_status_streak_count로 잡았다. text summary에서는 더 짧게 status_streak=PASSx2, status_streak=WARNx2, status_streak=HARD-FAILx2처럼 보이게 했다. transition이 WARN->WARN이라고만 나오면 그냥 변화 없음으로 읽히는데, 그 옆에 WARNx2가 붙으면 “두 번 연속 같은 warning이다”라는 판단이 바로 선다.

GraphRAG profile_eval status streak 구조
status transition 옆에 최신 상태의 반복 횟수와 해당 run label을 함께 붙인 구조.

왜 transition만으로는 부족했나

PASS->PASS는 얼핏 보면 별 정보가 없어 보인다. 하지만 내가 보고 싶은 건 “계속 괜찮다”와 “방금 괜찮아졌다”의 차이다. 전자는 다음 단계로 넘겨도 되는 후보에 가깝고, 후자는 조금 더 지켜봐야 하는 후보일 수 있다. WARN->WARN도 마찬가지다. 한 번의 warning은 기준선이 빡빡해서 생긴 것일 수 있지만, 몇 run째 같은 warning이면 profile 자체를 보류하거나 별도 실험으로 빼야 한다.

기존 history summary에는 history_runsdisplay_status_sequence가 있었다. 이론상으로는 그 sequence를 사람이 읽으면 같은 상태가 몇 번 이어졌는지 알 수 있다. 그런데 summary가 길어질수록 사람은 거기까지 차분히 세지 않는다. 특히 GraphRAG처럼 profile이 네 개 이상이고, 각 profile 아래에 query별 trace diff가 붙으면 상태열은 금방 배경으로 밀린다. 그래서 이번에는 사람이 세지 않아도 되게 만들었다.

붙인 필드

이번 변경은 큰 구조 변경이 아니다. _summarize_profile_history()에서 status sequence를 만든 뒤, 뒤쪽부터 최신 status와 같은 값이 얼마나 이어지는지만 훑는다. 같은 구간의 history label도 같이 모아서 latest_status_streak_labels에 넣었다. 그리고 처음으로 다른 status를 만나면 previous_distinct_status로 남겼다.

  • latest_status_streak_count: 최신 status가 연속된 run 수
  • latest_status_streak_labels: 그 streak에 포함된 history label 목록
  • previous_distinct_status: streak 직전에 있던 다른 status
  • status_streak: text summary에서 사람이 읽는 축약 표현

이 필드가 좋은 점은 기존 report를 깨지 않는다는 것이다. transition은 그대로 둔다. mismatch report도 그대로 둔다. 다만 그 옆에 “이 상태가 얼마나 오래 이어졌는가”라는 작은 눈금을 하나 더 올려 둔다. 나는 이런 식의 얇은 신호가 실험 도구에서는 꽤 중요하다고 본다. 큰 metric을 새로 만드는 것보다, 이미 있는 metric을 사람이 덜 헷갈리게 읽도록 돕는 쪽이 다음 행동을 더 빨리 만든다.

이번 run 결과

이번에는 직전 iter25-operational history를 seed로 복사한 뒤, 같은 operational namespace에서 iter26-status-streak을 append했다. 점수 자체는 바뀌지 않았다. 대신 text summary에 각 profile의 streak가 바로 보이기 시작했다.

profile latest status status streak 내가 읽은 의미
default PASS PASSx2 기본 profile은 두 operational run 연속 기준선을 통과했다.
relation_weight_dense PASS PASSx2 dense profile도 안정 후보로 남겨둘 수 있다.
path_bridge_probe WARN WARNx2 완전 탈락은 아니지만 warning 상태가 반복되고 있다.
path_bridge_focus HARD-FAIL HARD-FAILx2 다음 후보군에서 빼고 별도 실험으로 돌리는 편이 맞다.

가장 눈에 들어온 건 path_bridge_probe였다. 이 profile은 hard-fail은 아니지만, warning 상태가 두 번 연속으로 고정됐다. 예전 summary였다면 나는 WARN->WARN만 보고 “별일 없네” 정도로 넘겼을 가능성이 크다. 그런데 WARNx2라고 보이면 조금 다르게 읽힌다. 더 좋아지고 있는 중이라기보다, warning band 안에서 멈춰 있는 후보에 가깝다.

테스트로 잡은 부분

회귀 테스트도 하나 추가했다. history에 HARD-FAIL 한 번, WARN 한 번을 넣어 둔 뒤, 현재 run이 다시 WARN이 되도록 만들었다. 이때 summary는 run 세 개를 보되, 최신 streak는 WARNx2로 잡혀야 한다. 그리고 streak label은 iter22, iter23만 들어가야 한다.

전체 테스트는 30개를 다시 돌렸다. 여기서 중요한 건 기능이 복잡해졌다는 사실보다, history 계산이 기존 namespace와 mismatch report를 건드리지 않았다는 점이다. namespace가 다른 sample entry는 여전히 ignored count로 빠지고, 같은 namespace 안에서만 streak가 계산된다. 이 경계를 지키지 않으면 sample fixture의 오래된 실패가 operational run의 상태 반복으로 섞일 수 있다.

남은 생각

이번 변경은 작다. 하지만 GraphRAG 트랙을 계속 만지다 보니 이런 작은 판독값이 쌓이는 게 꽤 중요하다는 쪽으로 생각이 기울고 있다. profile을 더 세게 튜닝하기 전에, 지금 보고 있는 summary가 다음 행동을 제대로 가리키는지 먼저 봐야 한다. PASSx2는 유지 후보, WARNx2는 관찰 후보, HARD-FAILx2는 분리 후보처럼 읽을 수 있으면 실험 루프가 덜 흔들린다.

다음 단계로는 이 streak를 별도 queue로 뽑아 보고 싶다. 예를 들어 WARN이 세 번 이상 반복된 profile만 모아 stale-status report를 만들면, “계속 애매한 후보”를 따로 접어 둘 수 있다. 반대로 PASS가 여러 번 반복된 profile은 기본 후보군으로 승격해도 된다. 오늘 붙인 건 그 판단을 위한 작은 카운터에 가깝다.

시리즈: GraphRAG 구축기 #9

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

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.