2026년 5월 14일 | 개발 일기
GraphRAG profile registry를 다시 보다가, 숫자 하나가 계속 신경 쓰였다. summary에는 active=2, disabled=1만 보이는데, 실제 registry 안에는 active, observe, disabled가 따로 있었다. 실행 대상에는 들어오지만 안정 baseline과 같은 취급을 하면 안 되는 profile이 한 개 섞여 있었던 셈이다.
지난 반복에서 action report를 registry의 last_action으로 넘기는 sync를 만들었다. 그런데 막상 profile_eval 결과를 열면 그 정보가 summary 첫 줄에는 드러나지 않았다. 그래서 이번에는 검색 점수식은 건드리지 않고, registry 상태를 읽는 렌즈를 하나 더 붙였다. 이름은 거창하지 않게 registry status lens라고 적어 두었다.
1. active 수 안에 observe가 묻히는 문제
지금 GraphRAG 실험 루프에는 세 가지 profile 상태가 있다. relation_weight_dense는 안정 baseline에 가까운 active profile이고, path_bridge_probe는 일부러 warning band를 유지하면서 관찰하는 observe profile이다. path_bridge_focus는 hard-fail 성격이 강해서 disabled로 빠져 있다.
문제는 기존 summary가 이 차이를 충분히 보여 주지 않았다는 점이다. active_profile_count=2라고 나오면, 얼핏 두 profile이 같은 의미로 실행되는 것처럼 보인다. 하지만 실제로는 하나는 안정 후보이고, 하나는 observe-candidate 판단을 받은 관찰 후보였다. 이 차이를 놓치면 다음 run에서 “왜 이 profile을 계속 돌리고 있지?”를 다시 registry와 action report까지 열어 봐야 한다.
그래서 이번 변경의 목표는 단순했다. registry를 별도로 열지 않아도 summary 첫 줄에서 현재 실행 계약을 읽을 수 있게 만드는 것. 특히 observe 상태와 last_action provenance를 summary에 같이 올리는 것이었다.
2. 이번에 붙인 값
코드 변경은 작다. _load_profile_registry()에서 registry entry를 읽는 동안 전체 status를 세고, 각 entry의 last_action.recommended_action도 같이 집계했다. 그리고 text renderer의 profile-registry 줄에 그 값을 붙였다.
profile-registry: configs/profile_registry.json
active=2 | disabled=1 | duplicates=0
statuses=active:1, disabled:1, observe:1
last_actions=observe-candidate:1
여기서 마음에 든 부분은 active=2를 지우지 않았다는 점이다. 실행 대상 수는 여전히 필요하다. 다만 그 옆에 statuses=...를 붙여서, 실행 대상 중 하나가 observe라는 사실을 같이 보이게 했다. last_actions=observe-candidate:1도 같은 이유다. profile이 observe 상태라는 사실만으로는 부족하고, 그 상태가 최근 action queue 판단과 이어져 있는지도 봐야 한다.
3. iter34 결과
구현 뒤 전체 테스트를 다시 돌렸고, 39개 테스트가 통과했다. 기존 registry 테스트에 status count와 last_action count 검증을 더했다. 그 다음 iter33 history를 이어받아 iter34 bundle을 새로 만들었다.
| 항목 | iter34 값 | 내가 읽은 의미 |
|---|---|---|
status_counts |
active:1, observe:1, disabled:1 |
실행 후보와 관찰 후보, 제외 후보가 한눈에 갈라짐 |
last_action_counts |
observe-candidate:1 |
path_bridge_probe가 최근 action queue의 관찰 판단을 유지 |
default |
PASSx10 |
운영 baseline은 그대로 안정 |
relation_weight_dense |
PASSx10 |
점수 모양은 다르지만 top-k 안정성은 유지 |
path_bridge_probe |
WARNx10 |
아직 승격할 profile이 아니라 계속 관찰 대상 |
이번에도 retrieval 결과 자체가 좋아진 것은 아니다. path_bridge_probe는 여전히 doc bridge 쪽에서 흔들리고, manual-incident query에서는 GraphRAG overview 대신 Retrieval planner가 위로 올라오는 구간이 남았다. 그래도 이번에는 그 상태가 “실패”로만 보이지 않는다. registry summary가 이 profile을 관찰 후보로 계속 들고 가는 이유를 같이 보여 주기 때문이다.
4. 장부를 조금 더 앞쪽으로 당기기
GraphRAG 쪽 작업을 하다 보면, 실험 자체보다 실험 장부를 정리하는 시간이 더 길어질 때가 있다. 예전에는 이런 작업이 너무 주변부처럼 느껴졌는데, 요즘은 생각이 조금 바뀌었다. retrieval 점수를 바꾸는 코드와, 그 점수를 믿어도 되는지 알려 주는 장부는 사실 한 덩어리다.
이번 변경도 그런 성격이다. status_counts와 last_action_counts는 모델 품질을 직접 올리지 않는다. 대신 다음 실험을 열 때 맥락 복구 시간을 줄인다. 내가 봐야 할 것은 “왜 WARN인가” 이전에 “이 profile을 어떤 상태로 취급하고 있는가”다. 그 질문에 summary 첫 줄이 답해 주면, trace diff를 열기 전에 이미 절반은 정리된다.
작게 보면 출력 한 줄 추가지만, 나는 이런 변경이 꽤 중요하다고 본다. 특히 자동 평가 루프에서는 조용히 빠진 profile, 계속 관찰 중인 profile, stable baseline이 한 화면에서 섞이기 쉽다. 그 상태를 사람이 매번 머릿속으로 복원하면 언젠가 헷갈린다. 이번 렌즈는 그 헷갈림을 조금 줄이는 쪽이다.
5. 다음에 볼 것
다음으로는 exclude-candidate가 다시 등장하는 run을 한 번 더 보고 싶다. 이번 iter34에서는 observe-candidate만 있었기 때문에 status 전환 자체는 없었다. 나중에 hard-fail profile이 다시 잡히면 summary에 last_actions=exclude-candidate:1과 disabled 증가가 같이 보이는지 확인해야 한다.
그리고 더 중요한 쪽은 query set 분리다. 지금의 WARNx10은 샘플 질문 묶음 기준이다. 실제 운영 질문에서도 path_bridge_probe가 같은 식으로 흔들리는지 보려면, sample과 operational history를 더 명확히 갈라야 한다. 오늘 만든 summary 렌즈는 그 다음 단계를 보기 위한 작은 발판에 가깝다.
마지막으로, 이번 변경을 하면서 다시 느낀 점이 있다. 실험 코드가 길어질수록 “결과가 무엇인가”보다 “그 결과를 어떤 상태의 실험으로 취급할 것인가”가 더 자주 문제된다. GraphRAG 프로젝트도 이제 단순 검색 MVP라기보다, 작은 평가 운영 시스템에 가까워지고 있다. 그게 조금 과한가 싶다가도, 막상 다음날 다시 열어 보면 이런 장부 한 줄이 제일 먼저 고맙다.
'[AI 실험실] > [개인 프로젝트] GraphRAG' 카테고리의 다른 글
| GraphRAG | Observation review의 registry 반영 (0) | 2026.05.16 |
|---|---|
| GraphRAG | Observe profile 검토 리포트 (0) | 2026.05.15 |
| GraphRAG | Action report 기반 registry 갱신 (0) | 2026.05.13 |
| GraphRAG | 제외 후보 profile 자동 스킵 (0) | 2026.05.12 |
| GraphRAG | Stale origin action queue 추가 (0) | 2026.05.07 |