전체 질문을 넣어도 모델이 건수를 정확히 세지는 않는다

AI Tutor 질문 분석의 기존 STEP1은 모든 질문을 문자열로 보내고 모델이 유형별 건수를 출력하게 했다. 샘플만 분석하던 경로는 아니었다. 설계 기록은 큰 입력에서 모델이 건수를 어림잡아 실제 질문 수와 다른 결과를 내는 문제를 다룬다.

질문 분석 기능의 당시 구현 사례다. 제품 통합 전의 동작이며, 현행 보고서의 정확성이나 배포 결과를 설명하는 사례로 넓힐 수 없다. 질문 개요 집계가 학생 구간·분모·날짜를 코드로 고정했다면, 후속 변경은 질문 내용의 유형별 집계를 도구에 맡기는 다음 단계다.

buildQuestionsJsonl은 질문 한 건을 JSON 한 줄로 만들고 학생 로그인과 대화 ID를 S1, C1 같은 별칭으로 바꾼다. STEP1은 이 파일을 업로드해 code_interpreter의 file_ids로 전달한다. 질문 원문은 파일에 남기므로 별칭 치환만으로 개인정보가 모두 제거되지는 않는다. STEP2의 질문 목록은 여전히 문자열 입력이다.

합계 검사는 누락 없는 분류의 증명이 아니다

구조화 출력과 Zod 검증 뒤에는 유형별 count의 합이 입력 질문 수와 같은지 확인한다. 다음은 실제 합계 계산과 조건이며 오류 메시지는 줄였다.

const sum = data.type_distribution.reduce((acc, t) => acc + t.count, 0);
if (sum !== questionCount) {
	throw new Error('유형별 합계와 질문 수 불일치');
}

총 100건을 넣었는데 유형별 합계가 92건이면 이 조건이 거절한다. 하지만 질문 하나를 누락하고 다른 질문 하나를 두 번 세어도 합은 100이 될 수 있다. 질문별 분류가 맞는지, 중복 없이 모두 처리했는지, 토픽별 건수와 학생 수가 맞는지는 이 합계만으로 알 수 없다.

질문별 라벨을 반환받아 앱에서 집계하면 질문 ID별 포함 여부를 더 직접 확인할 수 있다. 당시 설계는 질문 수에 비례하는 출력 토큰을 이유로 이 대안을 채택하지 않았다. 결과 파일을 회수해 다시 검사하는 대안도 연결할 단계가 늘어나 보류했다. 따라서 도구 추가의 의미는 집계 방법과 검사를 강화한 것이며, 분류 정확도 개선을 수치로 입증한 결과는 아니다.

응답 검증 실패만 같은 파일로 다시 요청한다

파일은 반복문 전에 한 번 업로드한다. 응답 파싱, 스키마, 합계나 usage 검증이 실패하면 같은 파일로 최대 세 번 요청한다. SDK 재시도는 maxRetries=0, 요청 timeout은 20분이다. createResponse 자체의 reject는 내부 검증 catch 밖에 있어서 이 반복문이 세 번 다시 호출하지 않는다.

finally는 업로드 파일을 삭제하고 삭제 실패는 warning으로 남긴다. 파일의 7,200초 TTL은 명시 삭제에 실패했을 때의 안전망이다. 결과를 파일 삭제 오류로 바꾸지는 않지만, 파일 업로드·도구 호출·삭제라는 외부 단계가 추가된다.

테스트는 업로드 파일 ID와 도구·스키마 payload, prompt에 원문이 없는지, 합계 불일치 재시도와 성공·실패 뒤 삭제를 확인한다. JSONL의 별칭 매핑·줄 수·유효 JSON도 검사한다. API와 도구 응답을 mock한 검사는 실제 도구 실행, 질문별 정확한 분류와 전수 처리까지 확인하지 않는다.