[AI 실험실]/[개인 프로젝트] GraphRAG / GraphRAG | Query Cluster 집계 추가.md

GraphRAG | Query Cluster 집계 추가

조회

2026년 4월 16일 | 프로젝트


query 세트가 여섯 개쯤만 되어도, 어떤 질문 묶음이 흔들렸는지를 내가 다시 손으로 묶어 보는 시간이 은근히 길어졌다. 최근 GraphRAG 쪽은 rank shift 이유 라벨까지는 잘 남기고 있었는데, 그다음 단계에서 여전히 coverage-sensitive 류 질문이 같이 무너졌는지, 아니면 한두 개 query만 우연히 흔들린 건지를 내가 다시 눈으로 분류해야 했다.

그래서 이번에는 scoring 공식을 하나 더 늘리기보다, query 파일 자체에 묶음 정보를 넣고 group 단위로 읽는 판독면을 먼저 붙였다. `profile_eval`이 이제는 `{label, query, cluster, tags}` 구조를 읽고, default 대비 결과 안에 cluster_summariestag_summaries를 같이 남긴다. 말하자면 query 하나의 흔들림을 보는 단계에서, 어떤 질문 가족이 같이 흔들리는지 보는 단계로 한 칸 올라간 셈이다.

Path bridge profile by query cluster
path_bridge_focus를 cluster 단위로 다시 보니, incident-core는 버티고 coverage-sensitive가 먼저 무너지는 모양이 한 번에 드러났다.

1. 왜 query group을 먼저 붙였는가

직전 단계의 reason label은 분명 도움이 됐다. `path-over-coverage`, `coverage-loss`, `top1-flip` 같은 신호를 붙여 두니, 흔들린 query를 다시 열 때 해석 속도는 빨라졌다. 그런데 여기에도 여전히 마지막 손작업이 남아 있었다. 예를 들어 `partial-coverage`와 `service-dashboard`가 둘 다 흔들렸다면, 그 두 건이 사실상 같은 종류의 질문인지, 혹은 완전히 다른 사례인지까지는 summary가 직접 말해 주지 않았다.

나는 이 프로젝트에서 숫자보다 재진입 비용을 더 신경 쓰고 있다. score delta를 예쁘게 남기는 일보다, 다음 반복에서 무엇을 다시 볼지 빠르게 정해 주는 면이 더 중요하다는 뜻이다. 실제 문서셋으로 가면 query는 몇 개가 아니라 몇십 개가 될 텐데, 그때는 개별 query 라벨보다 어느 cluster가 같이 흔들리는지가 먼저 보여야 실험이 덜 흐려진다. 그래서 이번에는 retrieval 공식을 바꾸는 대신, query group 요약을 먼저 붙였다.

  • query line은 개별 질문의 top-1, edge, coverage 변화를 본다.
  • cluster summary는 같은 질문 묶음이 같이 흔들리는지 본다.
  • tag summary는 질문 성격이 겹치는 축을 더 가볍게 다시 본다.

2. 이번에 실제로 바뀐 것

구현은 생각보다 단순하다. query 파일 포맷을 문자열 리스트에서 한 단계만 넓혔다. 이제는 `label`, `query` 외에 `cluster`, `tags`를 같이 넣을 수 있고, 로더가 이 메타데이터를 읽어서 비교 결과와 text summary까지 그대로 끌고 간다. 덕분에 프로필을 돌린 뒤 곧바로 cluster-groups, tag-groups 섹션이 생기고, 각 query 줄에도 `cluster=...`, `tags=...`가 같이 찍힌다.

이번 샘플 query 세트는 세 묶음으로 나눴다. `incident-core`에는 핵심 장애 경로 질문을, `doc-bridges`에는 문서 연결형 질문을, `coverage-sensitive`에는 coverage를 덜 보기 시작할 때 흔들릴 가능성이 높은 질문을 넣었다. 태그는 `dashboard`, `partial-coverage`, `docs`, `stable`, `path-shift` 같은 가벼운 축으로 붙였다. 여기서 중요한 점은 taxonomy를 화려하게 만드는 게 아니라, 다음 실험을 다시 열 때 바로 의미가 떠오를 정도로만 얇게 붙이는 것이다.

PYTHONPATH=src:. python -m graphrag_mvp.profile_eval --dataset data/sample_documents.json --queries-file data/sample_queries.json --profile configs/relation_weight_dense.json --profile configs/path_bridge_focus.json --top-k 3 --trace-dir traces/profile_eval_bundle_iter15_cluster_groups

이 한 번의 실행으로 지금은 profile별 trace, default 대비 diff, rank-shift report, 그리고 그 안의 cluster / tag summary까지 같이 남는다. 예전 같으면 output을 본 뒤 내가 메모장에 다시 "이건 dashboard 쪽 질문들"이라고 적었을 부분이, 이제는 코드 안에서 바로 계산된다.

3. profile 수준에서 읽힌 차이

샘플 숫자는 꽤 분명했다. relation_weight_dense는 top-1 agreement 100%, stable top-k 100%로 남았다. score 성격은 조금 달라지지만 질문 묶음 자체는 건드리지 않는 안정형 기준선에 가깝다. 반대로 path_bridge_focus는 평균 path delta는 올리지만 coverage delta를 크게 깎으면서, 전체 top-1 agreement를 67%까지 낮췄다. 여기까지만 보면 그냥 "조금 공격적인 profile" 정도로 읽힐 수 있다.

profile top-1 agreement stable top-k avg pathΔ avg covΔ reason summary 내가 읽은 의미
relation_weight_dense 100% 100% +0.1667 -0.2500 stable 랭킹은 건드리지 않고 점수 성격만 바꾸는 안정형 기준선
path_bridge_focus 67% 50% +0.4833 -1.0222 path-over-coverage:3 cluster별로 흔들림이 갈리는 실험형 프로필

이번에 좋았던 지점은 바로 그 다음 줄이었다. 이제는 같은 profile이라도 어떤 cluster를 주로 흔드는지가 같이 나온다. 즉 profile 전체 평균 하나만 보는 대신, 어디에 비용이 생기는지를 group별로 바로 읽을 수 있다. 나는 이 한 칸이 생긴 것만으로도 tuning 메모가 훨씬 덜 감상적으로 바뀌었다.

4. cluster로 다시 보니 뭐가 달라졌는가

path_bridge_focus를 cluster로 묶어 다시 보면 패턴이 더 선명하다. `incident-core`는 두 질의 모두 버텼다. 반면 `coverage-sensitive`는 두 질의가 모두 흔들렸고 stable top-k는 0%까지 내려갔다. `doc-bridges`는 중간이었다. 한 건은 유지됐고, `manual-incident` 한 건만 실제 top-1 뒤집힘으로 이어졌다. 즉 이 profile이 무조건 나쁜 것이 아니라, coverage를 덜 봐도 괜찮은 묶음과 바로 무너지는 묶음이 갈린다는 사실이 이번에 드러난 셈이다.

cluster query 수 top-1 agreement stable top-k rank shift 대표 이유 내가 읽은 의미
incident-core 2 100% 100% 0 stable 핵심 경로 질의는 끝까지 버텼다.
doc-bridges 2 50% 50% 1 path-over-coverage:1 문서 연결형 질의는 일부만 흔들렸다.
coverage-sensitive 2 50% 0% 2 path-over-coverage:2 coverage를 덜 보자마자 가장 먼저 무너진 묶음이었다.

특히 `coverage-sensitive`가 `path-over-coverage:2`로 바로 찍히는 장면이 좋았다. 이전에도 개별 query를 열면 알 수 있는 정보였지만, 이제는 summary에서 바로 "이 profile은 coverage를 깎으면서 이 질문 묶음을 먼저 흔든다"고 읽힌다. 이런 식이면 다음 실험도 더 직접적으로 잡힌다. 예를 들면 "coverage-sensitive만 덜 흔들리게 하고 doc-bridges 이득은 유지할 수 있는가" 같은 질문으로 바로 넘어갈 수 있다.

Path bridge profile by tag
tag 축으로 다시 보면 dashboard와 partial-coverage가 동시에 흔들렸고, docs는 한 건만 흔들렸다.

5. tag 집계가 남긴 실전적인 포인트

tag summary는 cluster보다 더 가볍지만 생각보다 유용했다. `dashboard` 태그가 붙은 세 질의는 top-1 agreement 67%, stable top-k 33%였다. `partial-coverage` 태그 두 건은 둘 다 흔들렸다. 반면 `stable` 태그 세 건은 전부 유지됐다. 나는 이런 가벼운 축이 있어야 나중에 실제 서비스 질문을 넣었을 때도 taxonomy를 무겁게 만들지 않고 빠르게 판독면을 키울 수 있다고 본다.

tag query 수 top-1 agreement stable top-k rank shift 내가 읽은 포인트
dashboard 3 67% 33% 2 Customer Dashboard가 섞인 질문은 흔들림이 바로 드러났다.
partial-coverage 2 50% 0% 2 불완전 매칭을 허용하는 질문은 path bias 영향을 크게 받았다.
docs 2 50% 50% 1 문서 브리지 질문은 한 건만 top-1이 뒤집혔다.
path-shift 1 0% 0% 1 manual-incident 한 건이 바로 shift로 이어졌다.

이번 변경이 retrieval 품질을 바로 끌어올린 것은 아니다. 대신 어떤 질문 묶음에 비용이 생기는지를 빨리 읽는 면은 확실히 좋아졌다. GraphRAG처럼 edge, path, coverage가 같이 섞이는 프로젝트에서는 공식 하나를 더 얹는 일보다, 그 공식이 어느 query 가족에 부작용을 만드는지 먼저 읽는 편이 다음 반복을 훨씬 덜 허무하게 만든다.

6. 이번 기준선 이후에 바로 해볼 것

이제 남은 일은 명확하다. 샘플용 cluster/tag를 실제 문서셋용 taxonomy로 바꾸고, group별 precision / coverage 지표를 붙여야 한다. 그다음에는 `coverage-sensitive` 묶음만 유독 약해지는 profile을 빠르게 걸러낼 수 있을 것이다. 이번에는 질문 묶음을 읽는 판독면을 만들었고, 다음에는 그 판독면 위에 실제 품질 지표를 얹는 순서가 맞아 보인다.

테스트는 현재 17개를 다시 돌려 통과를 확인했다. sidecar 저장소에는 `Add GraphRAG query cluster summaries` 커밋까지 남겼다. 당장 체감되는 변화는 화려하지 않지만, query 수가 늘어날수록 이런 얇은 집계 레이어가 실험 리듬을 꽤 많이 살려 준다. 나는 요즘 GraphRAG에서 이런 종류의 정리가 성능 수치 하나보다 더 오래 남는 기준선이라고 느끼고 있다.

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.