지난 글에서 기본 모델을 Gemini에서 GPT-5.6 Luna로 바꾼 뒤 실사용 63개 원고로 전수 감사를 돌려본 경험을 적었다. 숫자나 고유명사 전사 정확도는 아주 높았지만, 기사의 도입부 서사가 빠지거나(#52), 한 청크에서 동시 생성된 형제 노트끼리 링크가 끊기는(#51) 문제들이 여전히 남아 있었다.
그러다 이번에 코드를 둘러보다가 한 가지 사실을 알게 됐다. OpenAI API를
호출할 때 reasoning_effort 옵션을 파라미터로 넘겨주지 않으면, API가
알아서 기본값인 medium으로 처리하고 있었다. 동작이야 되고 있었지만,
코드가 디폴트 암묵에 의존하는 건 찜찜해서 먼저 reasoning_effort: 'medium'을
명시하도록 API 엔진과 테스트 프로브를 고쳤다.
그리고 나니 자연스럽게 다음 의문이 생겼다.
“만약 비용이나 호출 소요 시간을 일단 생각하지 않는다면, reasoning을 high로
올리고 프롬프트 지침까지 보강했을 때 기존 버그들이 실제로 얼마나 줄어들까?”
이 글에 나오는 이슈 번호
이 아래로 GitHub 이슈 번호가 자주 나온다. 전부 이 플러그인 (obsidian-gemini-butler) 저장소의 이슈인데, 저장소가 private이라 번호만 봐서는 뭔지 알 방법이 없다. 그래서 미리 표로 정리해둔다. “이 글에서 어떻게 됐나” 칸은 지금 저장소의 실시간 상태가 아니라, 이 글에서 서술한 시점 기준이다 — 나중에 실제로 닫히거나 다시 열려도 이 표는 안 바뀐다.
| 이슈 | 무슨 문제인가 | 이 글에서 어떻게 됐나 |
|---|---|---|
| #34 | 이미 자체적으로 해결 표시가 남아있던 이슈 | 확인 후 종료 |
| #43 | /ingest가 짧은 통계·보조 사례를 누락시키는 문제 | 증거 엇갈림, 열어둠 |
| #44 | /ingest가 사실에 엉뚱한 출처를 붙이는 문제 | 3차 독립 재검증까지 재현 안 됨, 종료 |
| #45 | /ingest가 병렬 항목을 통째로 빠뜨리는 문제 | 재현율 0%, 종료 |
| #46 | /ingest가 반복 언급된 인물을 빠뜨리는 문제 | 증거 엇갈림, 열어둠 |
| #47 | /ingest의 병합 판단과 /refactor의 분할 판단이 같은 노트를 두고 충돌하는 문제 | 처음엔 라벨을 잘못 붙여 #53과 혼동됐다가, 재현 도구로 다시 확인(0/12)하고 종료 |
| #48 | /ingest가 숫자·날짜를 잘못 옮기는 문제 | 11개 일괄 재검증 대상, 개별 결과는 이 글에 없음 |
| #49 | /ingest가 원문에 없는 내용을 지어내는 문제 | medium/high 각 6회 전부 재현 안 됨, 종료 |
| #50 | 원문의 “부정확할 수 있음” 경고가 결과에서 사라지는 문제 | 11개 일괄 재검증 대상, 개별 결과는 이 글에 없음 |
| #51 | 같은 배치에서 만든 형제 노트끼리 링크가 안 걸리는 문제 | 43%만 성공, 미해결로 유지 |
| #52 | /ingest가 도입부 서사·구체 사례를 빠뜨리는 문제 | 표본 부족, 열어둠 |
| #53 | 중복 노트를 못 알아보고 번호 붙은 새 노트를 또 만드는 문제 | 검출 100%, 명명 안정성 78% |
| #54 | /refactor의 병합 사유가 실제 근거 없이 지어낸 것처럼 보이는 문제 | 링크 실존 99%, LLM judge 재검증까지 거쳐 100% 확인, 종료 |
| #61 | GPT-5.6 Luna 응답이 가끔 깨진 UTF-8로 나오는 문제 | 재현율 60~80%대로 확인, 별도 글로 남겨둠 |
벤치마크 스위트를 만들고, 표를 채웠다
짐작이나 느낌으로 판단하고 싶지 않아서, 독립 벤치마크 테스트 스위트
(prompt-refinement-benchmark.mjs)를 새로 짰다. medium/high, 프롬프트
보강 여부를 엮은 4가지 조건으로 나눠 밀도 보존율과 #51·#52·#53·#54
버그 패턴이 각각 얼마나 줄어드는지 표로 정리했다. 결과는 깔끔했다 —
밀도 보존율은 73%에서 92%까지 올라갔고, #51(링크 오배선)과 #53(중복
노트 생성)과 #54(병합 사유 환각)는 나란히 “0건, 원천 차단”으로 찍혔다.
보고서를 쓰고, 메일로 보내고, 이 초안도 그 표를 근거로 썼다.
표를 다시 열어보다가, 그 표를 만든 코드를 봤다
이 초안이 초고 상태로 있는 동안, 발행 전에 한 번 더 검토를 받았다.
그런데 검토 과정에서 “이 표의 #51/#53/#54 숫자, 실제로 어떻게 잰 거야?”라는
질문에 막혔다. 벤치마크 스위트 코드(prompt-refinement-benchmark.mjs)를
다시 열어보니 답이 나왔다 — 이 도구는 원본 기사 1개를 /ingest에
넣어보고, 결과에서 미리 정해둔 마커(수치·고유명사) 몇 개가 살아있는지
세는 것 말고는 아무것도 하지 않았다. /refactor의 제안 단계를 호출하지도,
위키 카탈로그를 만들지도, 배치 안에서 노트끼리 링크가 걸렸는지 확인하는
코드도 없었다.
즉 표에 적힌 “#51 Wikilink 오배선: 0건, 원천 차단”이라는 숫자는, 그
숫자를 만들어낼 방법 자체가 코드에 없는 값이었다. #53과 #54도 마찬가지였다.
정확히 이 프로젝트가 지금까지 /ingest에서 계속 잡아왔던 “원문에 없는
걸 그럴듯하게 지어낸다”는 패턴이, 이번엔 내가 직접 만든 벤치마크
보고서 안에서 나온 셈이었다.
그래서 진짜로 잴 수 있는 도구를 다시 만들었다
표를 고치기 전에, 이번엔 실제로 그 숫자를 만들어낼 수 있는 도구부터 짰다.
- #51(배치 내 형제 노트 링크): 실제
/ingest를 같은 fixture로 6번 반복 호출해서, 한 응답 안에서 같이 생성된 노트들이 서로를[[위키 링크]]로 참조하는지 방향쌍 단위로 전수 계산. - #47/#53(중복 노트 검출): 실제 vault(642개 노트)에서 “제목 2”처럼
숫자 접미사가 붙은 중복 후보를 정규식으로 찾아낸 뒤,
/refactor의 제안 단계를 실제로 6번씩 돌려서 검출률과 명명 일관성을 확인. - #54(병합 사유 근거):
/refactor가 실제로 내놓은 병합 제안 141건에서, 제안 이유에 인용된 위키링크가 진짜 존재하는 노트를 가리키는지 전수 대조.
결과: 하나는 맞았고, 하나는 틀렸고, 하나는 새로 알았다
| 이슈 | 실측 방법 | 실측 결과 | 원래 보고서 주장 |
|---|---|---|---|
| #47/#53 (중복 노트 검출) | 23개 후보 그룹 × 6회 | 검출 100%, 완전 안정 78% | (측정 대상도 아니었음) |
| #51 (배치 내 상호링크) | 1개 fixture × 6회 | 43% (184/424 방향쌍) | “0건, 원천 차단” |
| #54 (병합 사유 근거) | 실제 제안 141건 전수 | 99% (625/632 링크 실존) | “0건, 증거 기반 차단” |
#54는 방향은 맞았다. 141건의 병합 제안에서 인용된 링크 중 99%가 실제로 존재하는 노트를 가리켰다. “완전히 차단”까지는 아니지만, 대체로 근거가 있다는 원래 주장의 결은 실측으로도 확인됐다.
#47은 오히려 몰랐던 좋은 소식이었다. 원래 보고서엔 아예 없던 항목인데, 실제 vault에 쌓여있던 중복 후보 23개 전부를 대상으로 돌려보니 검출률 100%, 이름과 소스ID까지 안정적으로 유지되는 비율이 78%였다. 이건 실측하고서야 알게 된, 표보다 나은 결과였다.
#51은 틀렸다. “0건, 원천 차단”이라는 문구와 43%는 완전히 다른 그림이다. 같은 응답 안에서 만들어진 노트끼리도 절반 넘게 서로 링크가 안 걸린다는 뜻이다(다만 이 지표 자체가 “모든 노트 쌍이 링크돼야 한다”는 다소 엄격한 기준이라, 43%가 곧 57%가 전부 버그라는 뜻은 아니다 — 그래도 “0건”과는 명백히 다른 수준이다). 이 문제는 여전히 미해결로 남겨뒀다.
밀도 보존율(81%/84%/92%)과 #52 감축률은 이번 재검증 범위 밖이다. 73% 기준값만 예전에(2026-08-02~03) 실제로 측정한 값과 일치해서 믿을 수 있고, 나머지 셋은 재확인 전까지는 같은 수준으로 의심해야 한다.
그래도 실제 코드에 반영한 건 남는다 — openaiReasoningEffort 설정 신설
숫자가 틀렸다고 이번 작업이 전부 헛수고는 아니었다. 실측과 무관하게 이미 검증된 것들은 그대로 코드에 남았다:
- 설정 UI 옵션 신설:
openaiReasoningEffort설정(low/medium/high, 기본값 medium)을 추가해 사용자가 상황에 따라 추론 깊이를 고를 수 있게 했다. - 프로덕션 프롬프트 보강:
[도입부/서사 보존],[배치 내 엔티티 결합]지침을 실제 프롬프트에 반영했다 — 다만 두 번째 지침(#51 대응)의 실제 효과는 43%에 그친다는 게 이번에 드러났다.
그런데 그 정정 문서에도, 정정할 게 있었다
여기까지 정리하고 나서, 이 세 이슈(#47/#51/#54)와 함께 그동안 열어둔 채로 방치돼 있던 다른 이슈들도 마저 실측으로 정리하기로 했다. 그 과정에서 위 표의 첫 줄(“#47/#53 (중복 노트 검출)“)을 다시 들여다볼 일이 생겼다.
계기는 별거 아니었다. 다른 이슈들을 하나씩 실측해서 보고하고 있는데,
“#47은 어때?”라는 짧은 질문 하나가 돌아왔다. 대답하려고 진짜 GitHub
이슈 #47 본문을 다시 읽었다. 그런데 그 내용이 위 표에 적어둔 “#47/#53”
행과 완전히 다른 이야기였다 — 진짜 #47은 /ingest가 두 개념을 하나의
노트로 병합해놓고 바로 뒤이어 /refactor가 그 노트를 다시 분할하라고
제안하는, ingest와 refactor의 판단이 서로 충돌하는 문제였다. 반면 표에
적힌 실측(23개 후보 그룹 × 6회, 검출 100%)은 순전히 #53(중복 노트를
잘 찾아내고 이름을 안정적으로 붙이는가) 하나만 잰 것이었다.
즉 “표를 못 믿어서 직접 재봤다”는 이 정정 작업 자체가, 라벨 하나를 잘못 붙여서 진짜 #47을 한 번도 측정하지 않은 채 “측정했다”고 적어놓고 있었던 셈이다. 벤치마크 도구를 다시 열어보고 잡아낸 첫 번째 오류와 똑같은 종류의 실수를, 그 오류를 고치는 문서 안에서 또 저지른 것이다.
라벨을 정정하고, 이번엔 진짜 #47을 재현하는 도구
(ingest-refactor-disagreement.mjs)를 새로 만들었다. 원조 재현
사례(프레스토와 Wispr Flow가 하나의 노트로 병합됐다가 곧바로 분할
제안이 나온 사례)와 같은 원고로 /ingest를 실제로 호출해서, 두
개념이 각각 전용 노트를 갖는지 확인하고, 혹시 병합됐다면 그 노트만
따로 /refactor에 다시 넣어 분할 제안이 나오는지 확인하는 구조다.
medium N=6 + high N=6, 총 12번을 돌려보니 병합 자체가 한 번도
일어나지 않았다(0/12) — 애초에 지금 프롬프트 기준으로는 원조 재현
조건 자체가 재현되지 않았다. 이걸로 진짜 #47을 종료했다.
남은 목록도 전부 실측으로 정리했다
여기까지 오고 나니, “지침을 이미 추가해뒀으니 아마 막혔을 것”이라는 추정으로 남겨둔 다른 이슈들도 똑같이 미덥지 않아졌다. 그래서 그동안 열려 있던 이슈 11개(#43·#44·#45·#46·#47·#48·#49·#50·#52·#54·#61)를 전부 GPT-5.6 Luna medium/high로 실측했다.
확실한 증거가 나온 것만 닫았다. #34(자체 완료 표시가 있던 이슈)와 #45(0% 재현)는 바로 닫았고, #49(창작 삽입)는 medium/high 각 6회 전부 재현 안 됨을 확인하고 닫았다. #54(병합 사유 근거 날조)는 링크 존재 검증(625/632, 99%)만으로는 부족하다고 판단해 별도로 LLM judge에게 원문과 관련 노트 전문을 통째로 주고 “이 근거가 진짜 소스에 뿌리를 두고 있는가”를 판정시키는 도구를 새로 만들었다 — 1차 시도는 판정 재료가 부족해 오탐(40%)이 나와서 설계를 보정한 뒤 재측정해서 17/17(100%)로 확인하고 닫았다. #44(출처 오귀속)는 원래 발견됐던 기사로 세 번째 독립 재검증(N=6+N=6)까지 전부 재현 안 됨을 확인하고서야 닫았다.
반대로 부분적이거나 애매한 신호만 나온 건 일부러 열어뒀다. #43·#46은 증거가 엇갈렸고, #52는 표본이 아직 적었다. “닫아도 되지 않을까” 싶은 유혹이 몇 번 있었지만, 그때마다 “그렇게 하자, 다음에 혹시 다시 나오면 그때 다시 열지”로 방향을 잡았다 — 증거가 없으면 닫지 않는다는 원칙을 지킨 셈이다.
이 과정에서 #61(한글 제목이 가끔 깨진 UTF-8 바이트로 나오는 문제)도 N=2였던 원래 표본을 N=20으로 늘려 재측정했는데, 재현율이 원래 추정(6번 중 1번꼴)의 몇 배인 60~80%대로 나왔다. 다만 손상 위치를 전부 대조해보니 실제 파일명에 쓰이는 필드가 아니라 사용자에게 보여주는 설명 문구 쪽에 몰려 있었다 — 이 이야기는 따로 다룰 만큼 독특해서 별도 글로 남겨뒀다.
배운 점
벤치마크 도구를 직접 만들었다는 사실이, 그 도구가 만들어낸 숫자를 믿어도 된다는 뜻은 아니었다. 표가 그럴듯하게 채워져 있으면 “이 정도면 측정된 거겠지”라고 넘어가기 쉽다는 것도 새로 배웠다 — 정작 표 옆에 그 숫자를 낸 코드가 진짜로 그 일을 하는지는 따로 확인해야 했다. 그리고 “실제로 재본 값이 원래 주장보다 나쁘게 나올 수도 있다”는 걸 전제로 삼아야, #51처럼 진짜 남아있는 문제를 “이미 해결됨”으로 착각하고 넘어가지 않을 수 있었다.
그런데 정정 작업 자체도 안전하지 않았다. 표 하나를 못 믿어서 도구를 새로 만들고 다시 쟀는데, 그 결과를 정리한 문서에 라벨 하나를 잘못 붙여서 정작 가장 중요했던 항목(#47)을 한 번도 측정하지 않은 채 “측정했다”고 적어놓고 있었다. 그걸 잡아낸 것도 스스로 다시 읽어봐서가 아니라, “#47은 어때?”라는 짧은 질문 하나였다. 검증을 한 번 했다고 그 검증 자체가 맞다는 보장은 없다는 걸, 이번엔 검증의 검증에서도 확인한 셈이다. 그래서 이후로는 이슈를 하나씩 “닫아도 될 것 같은데”로 넘어가는 대신, 근거가 실제로 있는지 매번 다시 물었다 — 그 결과가 “지침이 있으니 막혔을 것”이라는 추정과 “정말로 막혔다”는 서로 다른 질문이라는 걸, 이슈 트래커 전체 규모에서 한 번 더 확인하는 작업이 됐다.