2026년 4월 6일 | 개발 일지
오늘은 GraphRAG 트랙의 최근 git log를 다시 훑어보면서 작업 순서를 정리했다. 바로 직전 커밋들은 relation-weighted graph scoring, configurable scoring profile 쪽에 몰려 있었다. 그러니까 지금 프로젝트는 "그래프 점수 체계를 어떻게 바꿀 것인가"보다도, 이미 바꿀 수 있게 만든 체계를 어떻게 덜 번거롭게 비교할 것인가가 다음 병목이었다. 실제로 지난 단계에서 scoring profile을 JSON으로 분리해 놓고도 비교 과정은 꽤 수동적이었다. query 하나를 던지고 결과를 보고, config를 바꾸고 다시 던진 뒤, top-1 제목과 edge score를 눈으로 대조해야 했다. query가 두세 개만 넘어가도 메모가 흐트러지고, 나중에 다시 봤을 때 왜 이 profile을 남겼는지 근거가 약해졌다.
그래서 이번에는 retrieval 품질 자체를 한 번 더 크게 건드리기보다, profile tuning을 반복 가능한 실험 루프로 바꾸는 쪽에 시간을 썼다. 내가 붙인 건 `profile_eval` CLI다. 역할은 단순하다. 같은 dataset과 query 세트를 기준으로 default profile과 custom scoring profile을 순서대로 돌리고, 각 profile에서 top-1이 무엇이었는지, 점수와 coverage가 어떻게 달라졌는지, edge score 변화가 있었는지를 한 번에 요약해 준다. 기능 자체는 작지만, 이런 작은 도구가 생겨야 다음 실험에서 "이번 조정이 실제로 retrieval 성격을 바꿨는가"를 훨씬 빨리 읽을 수 있다.
1. 왜 이 단위를 먼저 붙였나
최근 GraphRAG 쪽 흐름을 보면, 구현의 무게중심이 "더 큰 검색기 붙이기"가 아니라 비교 기준선을 촘촘하게 만드는 일에 있다. explainable retrieval, trace snapshot, trace diff, relation weighting, scoring profile까지 왔는데, 정작 profile 여러 개를 같은 질문 묶음에 올려놓고 비교하는 도구가 없으면 마지막 한 걸음이 비어 있는 셈이었다. 결국 튜닝 실험이 코드보다 메모 습관에 의존하게 된다.
나는 이런 상태를 오래 두면 두 가지 문제가 생긴다고 본다. 첫째, profile을 바꾼 뒤 결과가 좋아졌는지 나빠졌는지 체감은 남는데 기록은 흐려진다. 둘째, 나중에 trace snapshot을 봐도 "왜 이 snapshot을 남겼는지"가 약해진다. 그래서 이번 단계에서는 새 retrieval 아이디어를 더 넣는 대신, config diff → query 세트 평가 → 필요할 때 trace 확인이라는 루프를 먼저 붙이는 편이 맞다고 판단했다.
- default profile을 항상 baseline으로 먼저 평가
- custom profile은 이름과 경로를 함께 남겨 실험 출처 고정
- query 세트는 JSON 파일로 분리해 실험 입력을 코드 밖으로 이동
- 출력은 우선 top-1 중심으로 요약해 변화 감지를 빠르게 만듦
2. 이번에 실제로 바꾼 것
핵심은 세 군데다. 먼저 query 세트 로더를 만들었다. 문자열 배열도 읽고, {label, query} 형태도 읽는다. 처음에는 label까지 강제할까 고민했는데, 실험 초반에는 메모처럼 query만 빨리 쌓는 경우가 많아서 문자열 배열도 그대로 받게 했다. 대신 내부에서는 `query-1`, `query-2` 같은 기본 label을 채워 넣는다.
그다음은 profile 비교 함수다. 여기서는 default profile을 무조건 먼저 실행하게 했다. 이건 작은 강제지만 꽤 중요하다. custom profile만 돌리다 보면 baseline 없는 결과가 쉽게 쌓이는데, 그 순간부터 "무엇이 달라졌는지"보다 "지금 상태가 어떤지"만 보게 된다. 지금 프로젝트 단계에서는 absolute score보다 baseline 대비 변화 방향이 더 중요하다고 보고, 비교 함수 자체에 그 순서를 박아 넣었다.
마지막은 텍스트 요약 출력이다. JSON만 있으면 후처리는 쉽지만, 반복 작업에서는 먼저 사람 눈에 바로 들어오는 요약이 필요했다. 그래서 profile별 평균 top score, 평균 coverage, 각 query의 top-1 제목과 edge score, 누락 엔티티를 한 번에 읽히는 형식으로 정리했다. trace snapshot처럼 깊게 파고드는 도구가 이미 있으니, 이번 도구는 반대로 빠른 1차 판독에 초점을 맞췄다.
default -> dense profile 순서로 같은 query 세트 평가
각 query의 top-1 title / final score / edge score / coverage 출력
필요하면 그다음 단계에서 trace snapshot으로 내려감
3. 샘플 평가에서 바로 보인 차이
샘플 query 세트 세 개로 돌려 보니, 결과 해석이 꽤 또렷했다. default와 dense profile 모두 top-1은 Incident resolution로 유지됐다. 그러니까 이번 dense profile은 랭킹을 뒤집는 공격적인 설정이라기보다, 기존 top-1을 유지하면서 direct relation 쪽 edge contribution을 더 밀어 주는 성격에 가깝다. 실제로 edge score가 3.0000에서 3.1500으로 올라갔고, 평균 top score도 소폭 상승했다.
| profile | avg top score | avg coverage | 읽은 포인트 |
|---|---|---|---|
| default | 5.1701 | 100% | 현재 baseline. 세 query 모두 top-1은 안정적으로 유지됐다. |
| relation_weight_dense | 5.2367 | 100% | top-1을 유지한 채 edge contribution을 조금 더 강하게 만들었다. |
이 표에서 내가 중요하게 본 건 "dense가 이겼다"가 아니다. 지금은 오히려 무엇이 안 바뀌었는지를 아는 게 더 중요하다. top-1 제목은 유지되고 coverage도 그대로인데 edge score만 미세하게 올라갔다면, 이 profile은 retrieval 방향 전체를 흔드는 설정이 아니라 그래프 근거를 더 강하게 반영하는 미세 조정이라고 읽을 수 있다. 이런 해석이 있어야 다음에 coverage bonus를 건드릴지, relation weight를 더 벌릴지, 아니면 trace diff로 supporting edge 수준까지 내려갈지 판단이 쉬워진다.
4. 구현보다 더 오래 남는 건 비교 습관
이번 작업을 하면서 다시 느낀 건, GraphRAG처럼 점수 축이 여러 개 섞인 프로젝트에서는 모델을 하나 더 붙이는 일만큼이나 비교 습관을 코드로 고정하는 일이 중요하다는 점이다. relation weight를 JSON으로 뽑아내는 순간부터 실험은 더 쉬워지지만, 동시에 조합 수도 늘어난다. 그때 비교 루프가 손으로 굴러가면 실험 속도보다 기록 품질이 먼저 무너진다.
그래서 이번 단계는 기능 추가라기보다 실험 운영 개선에 가깝다. query 세트를 파일로 두고, baseline을 항상 같이 돌리고, top-1과 score breakdown을 한 번에 읽게 만드는 것만으로도 다음 반복이 훨씬 덜 흐트러진다. 특히 지금처럼 아직 실제 대규모 문서셋으로 가기 전의 작은 MVP 단계에서는, 이런 정리 습관이 나중에 품질 해석의 기준선으로 그대로 남는다.
- config를 바꾼 뒤 바로 query 세트 전체를 비교할 수 있게 됐다.
- baseline 없는 실험 결과가 쌓이는 문제를 줄였다.
- trace snapshot은 2차 깊이 분석 도구로 더 자연스럽게 이어지게 됐다.
5. 다음에 바로 해볼 것
다음 단계는 이미 꽤 선명하다. 첫째, profile_eval 결과와 trace snapshot 파일명을 연결해서, 특정 query와 profile 조합에서 순위가 흔들리면 바로 before/after diff를 열 수 있게 만들고 싶다. 둘째, 지금은 top-1 중심인데 여기에 top-k 겹침 정도나 rank movement를 붙이면 더 미세한 변화도 읽을 수 있다. 셋째, 샘플 데이터셋을 넘어서 실제 문서셋용 query 세트를 만들어야 한다. 그래야 지금 만든 평가 루프가 장난감 검증이 아니라 실제 retrieval tuning 루프로 넘어간다.
오늘 작업은 겉으로 보면 작은 도구 하나 추가한 정도다. 그런데 이런 반복 도구는 한 번 붙여 두면 이후의 점수 실험 전체에 영향을 준다. 지금 GraphRAG 트랙에서 내가 만들고 싶은 것도 그런 종류의 바닥 작업이다. flashy한 모델 이름보다, 바뀐 점수를 같은 질문 묶음 위에서 안정적으로 읽어낼 수 있는 구조를 먼저 쌓아 두는 쪽이 다음 단계의 시행착오를 훨씬 줄여 준다.
'[개발 일기]' 카테고리의 다른 글
| GNN | Hybrid ablation multi-seed 요약 (0) | 2026.04.11 |
|---|---|
| GNN 프로젝트: Propagation Profile 진단값 추가 (0) | 2026.04.06 |
| GNN Baseline: Propagated Cosine (1) | 2026.04.05 |
| 발행 직전에 RSS를 한 번 더 보는 이유 (0) | 2026.04.05 |
| GNN Baseline: Supervised Scoring 추가 (0) | 2026.04.04 |