[AI 실험실]/[개인 프로젝트] GraphRAG / GraphRAG | Observe profile 검토 리포트.md

GraphRAG | Observe profile 검토 리포트

조회

시리즈: GraphRAG 구축기 #17

이전: 16편 | 목록 | 다음: 18편

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


GraphRAG profile registry에 observe 상태를 만든 뒤로, 나는 계속 작은 불편함 하나를 보고 있었다. summary 첫 줄에서 active, observe, disabled 숫자는 보이는데, 정작 observe로 남겨 둔 profile을 다음에 어떻게 다뤄야 하는지는 여전히 머릿속에서 한 번 더 조립해야 했다. 점수표를 보고, history streak를 보고, stale origin action report를 보고, 다시 registry의 last_action을 열어야 했다. 기능은 돌아가는데 읽는 순서가 너무 길었다.

이번 작업은 그 빈칸을 줄이는 쪽으로 잡았다. 이름은 거창하게 붙이면 registry observation review인데, 실제로는 observe 상태 profile만 따로 모아서 “계속 관찰”, “승격 후보”, “비활성 후보”, “수동 검토” 중 어디에 놓을지 한 번 더 접는 리포트다. 점수 계산을 바꾸는 작업은 아니고, 이미 만든 registry와 quality history 사이에 얇은 판독 레이어를 하나 더 끼웠다.

GraphRAG registry observation review 흐름
observe profile을 registry, quality history, last_action provenance와 함께 다시 접는 흐름.

왜 또 report가 필요한가

처음에는 profile registry만 있으면 충분할 줄 알았다. stable baseline은 active, 위험한 실험 profile은 disabled, 아직 버리긴 아까운 profile은 observe로 두면 된다고 생각했다. 실제로 iter34에서 status lens를 붙인 뒤 summary에는 statuses=active:1, disabled:1, observe:1last_actions=observe-candidate:1가 보였다. 그 자체로는 나쁘지 않았다.

문제는 observe가 “실행 대상에는 포함되지만, 배포 후보로는 보지 않는 상태”라는 점이었다. active 수에 같이 섞여 있으면 sweep은 돌아가지만, 사람이 읽을 때는 완전히 다른 의미다. 특히 path_bridge_probe처럼 일부러 warning band에 남겨 둔 profile은 매번 PASS profile과 나란히 출력되기 때문에, 이 profile을 계속 살려 두는 이유가 summary만 보고는 약했다.

나는 이런 종류의 불편함을 꽤 자주 놓친다. 숫자가 이미 있으니 충분히 설명 가능하다고 생각하지만, 다음 날 다시 열어 보면 “그래서 이 profile은 놔두자는 건가, 버리자는 건가?”부터 다시 묻게 된다. GraphRAG 실험처럼 작은 report가 계속 쌓이는 프로젝트에서는 이 재해석 비용이 은근히 크다.

이번에 붙인 것

이번 변경의 핵심은 reports/registry-observation-review.json이다. profile_eval이 registry를 읽고, 그중 status가 observe인 profile만 골라 현재 sweep 결과와 다시 매칭한다. 그리고 quality gate 상태, history status streak, warning scope, hard-fail scope, registry의 last_action provenance를 한 행으로 묶는다.

분류 규칙은 일부러 단순하게 뒀다. observe profile이 PASS를 여러 번 유지하면 promote-candidate, HARD-FAIL을 여러 번 유지하면 disable-candidate, WARNING이나 FAIL이면 keep-observing으로 둔다. 현재 sweep에 매칭되지 않으면 review-candidate로 떨어뜨린다. 여기서 바로 registry를 고쳐 버리지는 않는다. 나는 아직 자동 승격이나 자동 비활성화까지 열고 싶지는 않았다. 이번 단계는 “결정 후보를 보이게 하는 것”까지만이다.

text summary에도 한 줄을 추가했다. 이제 상단에는 이런 식으로 보인다.

registry-observation-review: .../registry-observation-review.json | profiles=1 | recommendations=keep-observing:1

이 한 줄이 별것 아닌 것 같지만, 읽는 순서를 꽤 바꾼다. 예전에는 registry 상태를 보고, history report를 보고, action report를 열어서 결론을 머릿속에서 합쳤다. 이제는 “observe가 하나 있고, 이번 판단은 keep-observing이다”를 먼저 본 뒤, 이유가 필요할 때만 JSON report를 열면 된다.

테스트에서 고정한 장면

회귀 테스트는 path_bridge_probe가 observe 상태로 남아 있고, quality gate에서는 WARN을 유지하는 장면으로 만들었다. history에는 이전 WARN run 두 개를 넣고, 현재 run을 붙였을 때 observation review가 profile 하나를 잡아야 한다. 기대값은 recommendation_counts={"keep-observing": 1}이고, report payload 안에는 기존 last_action.recommended_action=observe-candidate도 같이 남아야 한다.

이 테스트에서 보고 싶었던 건 점수 자체보다 연결이었다. registry entry와 summary profile이 이름이나 path로 제대로 매칭되는지, history streak가 붙는지, last_action provenance가 사라지지 않는지가 중요했다. observe 상태는 사람이 의도를 담아 둔 칸이기 때문에, 그 의도가 report를 거치면서 지워지면 안 된다.

전체 테스트는 40개가 통과했다. 기존 39개에서 observation review 케이스 하나가 늘었다. 프로젝트가 아직 작은 편이라 전체 테스트를 돌려도 오래 걸리지는 않지만, 이런 report 레이어가 계속 붙을수록 “한 줄 추가했는데 기존 summary가 깨지지 않았는지”를 확인하는 테스트가 더 중요해지는 느낌이다.

iter35 실행 결과

이번 operational run은 이전 history를 이어받아 같은 query set과 같은 gate 기준으로 돌렸다. 결과는 의도한 대로 크게 흔들리지 않았다.

  • default: PASSx11 유지
  • relation_weight_dense: PASSx11 유지
  • path_bridge_probe: WARNx11, observation review에서 keep-observing
  • path_bridge_focus: registry disabled 상태라 sweep에서 제외

path_bridge_probe는 여전히 doc-bridges와 docs tag 쪽에서 warning scope가 남아 있다. 특히 manual-incident query에서 GraphRAG overview 대신 Retrieval planner가 위로 올라오는 장면은 그대로다. 다만 이 profile은 hard-fail이 아니라 warning band에 있고, 일부 query family에서는 쓸 만한 움직임도 보여 준다. 그래서 이번 review의 결론은 비활성화가 아니라 keep-observing이 맞다.

여기서 중요한 건 “성능이 좋아졌다”가 아니다. 오히려 성능 상태는 거의 그대로다. 대신 profile을 계속 살려 두는 이유가 더 명시적으로 남았다. 나는 실험 프로젝트에서 이런 메타데이터가 생각보다 중요하다고 느낀다. 점수 개선만 기록하면, 나중에 실패한 실험 profile을 왜 계속 평가했는지 알 수 없다. 반대로 관찰 이유를 남겨 두면, 다음에 자동 sync나 승격 루틴을 붙일 때도 기준점이 생긴다.

하면서 헷갈린 부분

가장 헷갈린 부분은 “이 report가 action report와 뭐가 다른가”였다. 이미 stale origin action report에는 observe-candidate와 exclude-candidate가 있다. 그런데 그 report는 history에서 오래 같은 상태로 남은 profile을 action 후보로 바꾸는 쪽에 가깝다. 이번 observation review는 registry에 이미 observe로 올라온 profile을 현재 sweep 기준으로 다시 읽는 쪽이다.

말하자면 action report는 “이 profile을 registry에 어떻게 반영할까”에 가깝고, observation review는 “registry에 observe로 들어온 profile을 오늘은 어떻게 읽을까”에 가깝다. 둘을 합쳐도 되지 않나 싶었지만, 지금은 분리하는 편이 낫다고 봤다. 자동으로 registry를 바꾸는 루틴과, 사람이 보고 판단하는 summary 레이어가 같은 파일에 섞이면 나중에 되돌리기 어려워질 수 있다.

또 하나는 path 매칭이다. registry에는 상대 경로가 들어가고, summary 쪽 profile path도 상황에 따라 상대 경로나 절대 경로가 섞일 수 있다. 이번에는 이름, 원문 path, resolve된 path를 같이 키로 써서 매칭했다. 이 부분은 사소해 보여도 중요하다. profile을 못 찾으면 observation review가 바로 review-candidate로 떨어지는데, 실제로는 단순 경로 표기 차이일 수 있기 때문이다.

다음에 볼 것

다음 단계는 PASS로 회복된 observe profile을 어떻게 승격 후보로 볼지 synthetic fixture를 하나 더 만드는 쪽이 좋아 보인다. 지금은 keep-observing 케이스만 실제 operational run에 걸려 있다. promote-candidatedisable-candidate도 테스트 안에서 먼저 고정해 둬야, 나중에 실제 registry sync와 연결할 때 덜 불안하다.

그다음에는 operational query set을 sample query set과 분리해야 한다. 지금 샘플 query는 디버깅에는 좋지만, 오래 같은 질문만 돌리다 보니 observe profile의 warning이 너무 안정적으로 반복된다. 실제 운영 질문에서도 같은 warning이 유지되는지, 아니면 샘플에만 묶인 현상인지 봐야 한다. GraphRAG 프로젝트가 점점 “검색기 만들기”보다 “검색 실험을 안전하게 읽는 장부 만들기” 쪽으로 기울고 있는데, 지금 단계에서는 이 장부가 오히려 다음 구현을 덜 흔들리게 해 준다.

시리즈: GraphRAG 구축기 #17

이전: 16편 | 목록 | 다음: 18편

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.