M5 계획 끝에 이렇게 적었다. 만드는 것이 가장 적은 슬라이스를 먼저 하는 이유는, 그 뒤의 모든 비교가 거기서 시작하기 때문이다. 이번 PR #22가 그 슬라이스, M5-0 기준선 동결이다. 판정과 추출과 저장 로직은 바뀌지 않았다. 바뀐 것은 평가 실행기의 비교 규칙 하나, 커밋된 보고서 하나, 문서 한 장이다.

지난 글의 호스트 확인은 아직 실행 전이다. 그 결과는 이 기준선 페이지의 한 줄을 채우게 된다. 순서가 뒤집힌 것처럼 보이지만, 페이지에 “not verified”라고 적을 자리를 먼저 만드는 것도 기준선의 일이다.

비교기가 비교를 끊는 조건

M4-A에서 정책 평가 보고서에 버전과 해시를 넣고 이전 보고서와 비교하는 옵션을 만들었다. 그때 비교기는 네 가지가 다르면 비교를 거부했다. 보고서 형식 버전, 지표 정의 버전, 실행 모드, 그리고 공유 스키마 파일의 해시.

넷째가 문제였다. 공유 스키마는 판정 결과와 근거와 지원 기록의 모양을 정의하는 파일인데, M5 계획의 A 슬라이스는 근거 조각의 스키마를 그 파일에 추가하고, B 슬라이스는 인용을 위해 두 스키마를 넓힌다. 즉 기준선을 동결한 직후의 첫 코드 변경이 기준선을 비교 불가로 만든다. 계획서를 검토하며 알았고, 계획서에 M5-0의 유일한 코드 변경으로 적어 두었다.

M4-A 때와 같은 모양의 문제다. 그때는 사람이 사례에 검토 완료 표시를 하는 정상적인 작업이 비교를 끊었고, 해시를 조건과 주석으로 나눠서 풀었다. 이번에는 스키마를 추가하는 정상적인 작업이 비교를 끊는다. 풀이의 방향도 같다. 무엇이 바뀌었을 때 비교를 이어가도 되는지의 기준을 옮기는 것이다. 스키마 해시 차이를 거부 사유에서 빼고, 정책 변경이나 데이터셋 변경처럼 schemaChanged로 보고한다. 비교해도 되는지는 사례마다 붙어 있는 계약 해시가 정한다. 스키마 변경이 어떤 사례의 입력이나 기대값을 실제로 바꿨다면 그 사례는 계약 해시가 달라져 “수정됨”으로 빠지고, 바꾸지 않았다면 비교된다. 거부 사유로는 형식 버전과 지표 버전과 모드, 셋이 남는다.

테스트는 두 방향으로 넣었다. 스키마 해시만 다른 보고서 둘을 비교하면 28건이 전부 비교되고 회귀는 없어야 한다. 스키마 해시가 다르면서 판정 함수를 일부러 망가뜨린 보고서를 비교하면 회귀가 그대로 잡혀야 한다. 스키마 변경을 보고하는 것이 회귀를 숨기는 통로가 되면 안 되기 때문이다. 보고서 형식 버전은 3으로 올렸다. 같은 파일 모양이지만 “비교 가능”의 뜻이 달라졌으니 이전 버전과 섞이지 않게 했다.

커밋을 둘로 나눈 이유

보고서에는 어느 커밋에서 만들었는지 codeSha가 실린다. 그런데 보고서는 만든 뒤에야 커밋할 수 있으니, 보고서가 가리키는 SHA에 보고서 자신이 들어 있을 수는 없다. 순서를 정했다. 첫 커밋에 비교 규칙 변경과 테스트를 넣고, 그 커밋의 깨끗한 체크아웃에서 보고서를 만들고, 둘째 커밋에 보고서와 페이지를 넣었다. 보고서의 SHA는 첫 커밋을 가리키고, 작업 트리가 깨끗했다는 표시도 함께 실린다. 기준선 페이지는 그 SHA를 첫 줄에 적는다.

사소한 순서지만, 이렇게 나누면 보고서가 실제 존재하는 코드 커밋과 깨끗한 작업 트리를 가리키게 할 수 있다.

표를 쓰다 보면 “모른다”가 늘어난다

기준선 페이지의 본체는 기능별 확인 상태를 적은 표다. 열다섯 줄, 라벨은 셋뿐이다. 합성 입력으로 확인했는지, 실제 제공자로 확인했는지, 확인하지 않았는지. 실제 제공자로 확인한 줄에는 제공자와 모델과 프롬프트 버전을 같이 적었다. 한 엔드포인트의 수치는 다른 엔드포인트에 대해 아무것도 말해 주지 않는다는 것이 M4부터의 규칙이다.

쓰다 보니 “not verified”가 늘었다. 실제 판정 결과를 얻은 근거는 사용 확인 1차의 무료 모델 하나에 한정되고, 그것도 이전 프롬프트 버전과 #19 이전 계약이다. 제공자 어댑터 행에는 다른 모델에서 관찰한 구조화 출력 실패도 함께 기록했다. #19가 바꾼 세 가지, 고용주 없음과 정수 점수와 추론 강도 설정은 실제 모델로 확인한 적이 없다. 추천 배치와 OpenAI 경로와 ChatGPT 호스트도 그렇다. 표에는 그렇게 적었다. 사용 확인 1차의 호출별 지연도 수치만 옮겼다. 답한 모델의 판정 호출이 80.5초와 66.9초, 답하지 못한 첫 공고의 추출 호출이 도구 제한 300초를 넘겨 515초에 끝난 것까지. 그 515초는 취소가 전달되지 않던 실행기의 기록이라, 표에는 그 사실도 적었다.

이 표에 “not verified”가 많은 것은 나쁜 소식이 아니다. 다음 슬라이스마다 어느 줄이 바뀌는지, 무엇이 그 줄을 바꿨는지 적을 자리가 생긴 것이다.

리뷰가 잡은 두 가지

둘 다 문서였고, 둘 다 표를 정확하게 만드는 일이었다.

해시를 어떻게 계산했는지. 페이지에 정책 파일과 스키마 파일의 해시를 “파일의 SHA-256”이라고 적었다. 리뷰어가 직접 계산해 보니 파일 바이트의 해시는 다른 값이었다. 실행기는 파일 내용을 문자열로 읽어 JSON으로 직렬화한 뒤 SHA-256으로 해시한다. 구조화된 평가 입력과 같은 정규화 함수를 파일 내용에도 쓰기 때문이다. 보고서 값은 맞았고 설명이 틀렸다. 재현하려는 사람이 파일 해시를 계산했다면 보고서와 다른 값을 얻고 무엇이 잘못됐는지 찾아 헤맸을 것이다. 계산 방법을 그대로 적었다.

어느 실행기가 실제로 돌았는지. 사용 확인 실행기 줄에 1차에서 실제로 사용했다고 적었다. 그런데 1차를 돌린 것은 취소 전달을 고치기 전의 실행기다. 같은 페이지의 앞 문단에서는 현재 SHA의 사용 확인을 “not verified”로 남긴다고 해 놓고, 표에서는 같은 실행기를 실제로 썼다고 적은 셈이다. 현재 실행기는 합성 입력으로만 확인했고, 1차 실행은 수정 전 실행기가 만들었으며, 그 뒤 실제 실행이 없었다고 나눠 적었다.

기준선 페이지는 “무엇을 어디까지 확인했는가”를 적는 문서라, 이 문서가 부정확하면 뒤의 모든 비교가 잘못된 출발점을 갖는다. 리뷰가 잡은 두 곳 모두 그 출발점에 관한 것이었다.

확인한 것과 남은 것

확인한 것은 합성 입력뿐이다. 정책 평가 28건 통과, 기준선 비교 28건에 회귀 0, 스키마 해시만 바꾼 기준선 사본과의 비교가 통과하면서 변경으로 보고되는 것. 모델 호출은 없었다.

남은 것은 둘이다. 같은 공고 세 개로 사용 확인을 다시 돌려 #19 이후의 동작을 표에 채우는 일. 이것은 승인된 호출이 필요해 내가 정하는 단계다. 그리고 M5-A, 합성 코퍼스와 어휘 검색이다. 그 슬라이스가 공유 스키마에 첫 스키마를 추가할 때, 이 기준선이 비교를 거부하지 않고 “스키마 바뀜”이라고 보고하는지가 M5-0이 제대로 됐는지의 첫 확인이 된다.

기준선 페이지 전문은 저장소의 MILESTONE_5_BASELINE.md에 있다. SQLite 스키마 버전은 PRAGMA user_version으로, 위젯 주소와 도구 아홉 개는 서버 코드에서 그대로 옮겼다.