2026년 5월 7일 | 개인 프로젝트 · GraphRAG
same-status-from-start로 묶인 profile 두 개가 계속 같은 줄에 남아 있는 것이 GraphRAG 루프에서 제일 거슬렸다. 이전 반복에서 만든 stale origin queue는 "처음부터 같은 non-pass 상태였던 profile"을 잘 골라냈지만, 거기서 한 걸음 더 나아가지는 않았다. path_bridge_probe와 path_bridge_focus가 모두 같은 queue에 들어왔고, 나는 다시 파일을 열어서 하나는 관찰 후보인지, 하나는 제외 후보인지 손으로 읽어야 했다.
이런 상태가 몇 번 반복되면 report가 도움을 주는 게 아니라 report를 다시 해석하는 일이 된다. 그래서 이번에는 검색 점수 자체를 만지지 않고, history report의 마지막 한 칸을 더 접었다. 이름은 stale origin action queue로 잡았다. 기존 history-stale-origin.json을 다시 읽고, 장기 HARD-FAIL은 exclude-candidate, 장기 WARN은 observe-candidate로 나눠 history-stale-origin-actions.json에 남기는 작은 레이어다.
1. 왜 action queue가 필요했나
GraphRAG 프로젝트의 최근 흐름은 검색 알고리즘을 크게 바꾸기보다 평가 결과를 다시 여는 순서를 좁히는 쪽이었다. quality gate가 생기고, history가 생기고, status streak가 생기고, stale status queue가 생겼다. 그 다음 stale transition split으로 개선 후 정체인지, 악화 후 정체인지, 처음부터 같은 상태인지까지 구분했다. 여기까지 오면 꽤 촘촘해 보이지만, 실제로는 마지막 판단이 비어 있었다.
예를 들어 path_bridge_focus가 HARD-FAILx6으로 남아 있고, path_bridge_probe가 WARNx6으로 남아 있다고 하자. 둘 다 same-status-from-start라면 origin queue에는 같이 들어간다. 하지만 내가 다음 루프에서 할 행동은 다르다. 전자는 당분간 sweep에서 빼고 싶고, 후자는 query set이 바뀔 때 다시 보고 싶다. 같은 queue 안에 넣어 두면 그 차이를 매번 사람이 해석해야 한다.
그래서 이번 변경의 목적은 단순했다. 상태를 설명하는 report에서 다음 행동을 적는 report로 한 칸 더 내려가는 것이다. 검색 품질을 직접 개선하지 않았지만, 실험을 계속 굴릴 때는 이런 작은 분류가 꽤 중요하다. 실패를 많이 모아도 다음에 열 순서가 없으면 결국 또 긴 JSON을 눈으로 훑게 된다.
2. 구현은 아주 얇게 붙였다
코드는 기존 stale origin report 생성 뒤에 한 번 더 흐르게 했다. 새 함수는 origin report의 profiles를 순회하면서 각 profile에 recommended_action, action_reason, blocking_scopes, priority_score를 붙인다. 핵심 규칙은 일부러 단순하게 뒀다.
- HARD-FAIL origin streak:
exclude-candidate로 분류한다. - WARN origin streak:
observe-candidate로 분류한다. - 그 밖의 상태:
review-candidate로 남긴다.
처음에는 WARN도 streak가 길면 제외 후보로 보낼까 생각했는데, 그건 성급해 보였다. WARN은 여전히 exploration용 profile일 수 있다. 특히 path_bridge_probe처럼 일부러 bridge 쪽을 살짝 흔들어 보는 profile은 운영 query set이 늘어나면 다시 의미가 생길 수 있다. 반대로 path_bridge_focus처럼 hard-fail scope가 계속 남는 profile은 지금 샘플 기준에서는 더 열어 볼 우선순위가 낮다.
3. 결과는 두 줄로 읽힌다
iter29의 history를 seed로 복사해 iter30 operational run을 만들었다. 테스트는 33개가 모두 통과했고, summary 상단에는 이제 stale status, stale origin, stale origin action이 차례대로 찍힌다.
history-stale-status-report: ... | profiles=2 | statuses=hard-fail:1, warning:1 | stale_transitions=same-status-from-start:2
history-stale-origin-report: ... | profiles=2 | statuses=hard-fail:1, warning:1
history-stale-origin-action-report: ... | profiles=2 | actions=exclude-candidate:1, observe-candidate:1
이 세 줄이 마음에 드는 이유는, 내가 이제 왜 stale인지와 어떻게 다룰지를 따로 열지 않아도 되기 때문이다. 첫 줄은 같은 non-pass 상태가 오래 반복됐다는 사실을 보여 준다. 두 번째 줄은 그 상태가 처음부터 같은 상태였다는 것을 보여 준다. 세 번째 줄은 다음 루프에서 어떤 profile을 접고, 어떤 profile을 관찰할지 보여 준다.
| profile | history 상태 | action | 내가 읽는 의미 |
|---|---|---|---|
path_bridge_focus |
HARD-FAILx6 |
exclude-candidate |
다음 sweep에서 우선 제외 후보로 접어 둔다. |
path_bridge_probe |
WARNx6 |
observe-candidate |
query set이 바뀔 때 다시 확인할 관찰 후보로 둔다. |
4. 이번 삽질: history 경로도 baseline의 일부
작게 붙인 기능이었는데도 한 번 삐끗한 지점이 있었다. iter30 재현 산출물을 만들 때 dataset 경로를 절대경로로 넘겼더니, 기존 iter29 history의 data/sample_documents.json와 다른 baseline으로 인식됐다. 코드가 잘못된 것은 아니었다. history guard 입장에서는 dataset 문자열이 다르면 다른 baseline이다. 결과적으로 과거 run들이 baseline-mismatch(dataset)로 빠졌고, stale queue가 비었다.
이건 좀 허무했지만 좋은 확인이었다. history 비교에서 경로 문자열은 단순 표시가 아니라 fingerprint와 같은 비교 조건으로 쓰인다. 그래서 재현 스크립트를 프로젝트 루트에서 돌리고, dataset과 config 경로를 기존처럼 상대경로로 맞췄다. 그제야 PASSx6, WARNx6, HARD-FAILx6 흐름이 다시 붙었다.
5. 작지만 다음 루프가 가벼워졌다
이번 변경은 모델을 붙인 것도 아니고 검색 결과를 끌어올린 것도 아니다. 대신 GraphRAG 실험에서 내가 반복적으로 하던 수동 판단 하나를 report로 내렸다. 계속 같은 실패면 왜 같은지를 보는 단계에서, 그래서 다음 실험에서 어떻게 취급할지까지 적게 만든 셈이다.
이제 다음 profile sweep에서는 exclude-candidate를 자동으로 제외하는 옵션을 붙일 수 있다. 물론 바로 제외하는 건 위험하니 처음에는 dry-run 형태가 나을 것 같다. "제외하면 어떤 profile 목록이 남는지"만 보여 주고, 실제 실행 profile set은 사람이 한 번 확인하는 식이다. 반대로 observe-candidate는 operational query set이 커질 때 다시 살려 보면 된다.
조금씩 report가 많아지고 있지만, 방향은 아직 맞다고 본다. 파일이 많아지는 것보다 더 위험한 건, 같은 실패를 볼 때마다 매번 처음부터 해석하는 일이다. 이번 action queue는 그 반복을 한 칸 줄였다.
'[AI 실험실] > [개인 프로젝트] GraphRAG' 카테고리의 다른 글
| GraphRAG | 제외 후보 profile 자동 스킵 (0) | 2026.05.12 |
|---|---|
| GraphRAG | Same-status stale queue 추가 (0) | 2026.05.06 |
| GraphRAG | History mismatch 상태 전이 연결 (0) | 2026.05.06 |
| GraphRAG | Stale transition split 추가 (0) | 2026.05.02 |
| GraphRAG | History stale status queue 추가 (0) | 2026.05.01 |