agent/ 아래에 자세 분석 에이전트 구현을 시작했다. 설계 단계에서 세웠던 가정 여러 개가 실제 환경에서 깨져 모델과 서빙 방식을 바꿨다.
필수 투입자원에 맞춰 스택 변경
투입자원 목록에 MediaPipe와 Ollama가 없고 OpenCV·Transformers가 지정돼 있어 두 군데를 바꿨다.
- 포즈 추정: MediaPipe → OpenCV 디코딩 + Transformers(RT-DETR + ViTPose)
- 판정 서빙: llama.cpp/GGUF → Transformers 직접 적재
환경에서 걸린 것들
CUDA 드라이버 불일치. 기본 PyPI torch가 cu130 빌드로 설치되는데 WSL2 드라이버는 CUDA 12.6(560.94)이라 GPU를 아예 인식하지 못했다. WSL2는 Windows 호스트 드라이버를 따르므로 드라이버를 올릴 수 없다. pyproject.toml에서 cu126 휠 인덱스를 지정해 고정했다.
EXAONE 3.5를 쓰지 못하게 됐다. VRAM 문제가 아니라 의존성 문제다. 3.5는 trust_remote_code로 자체 모델링 코드를 받는데, 그 코드가 특정 transformers 버전 창에만 맞는다. 4.x에서는 RopeParameters import 실패, 5.15에서는 create_causal_mask() 인자명이 input_embeds→inputs_embeds로 바뀌어 TypeError가 났다. 버전을 고정하면 쓸 수 있지만 팀 전원이 재현해야 하는 환경에서 상류 변경에 깨지는 경로는 피하기로 했다.
EXAONE 4.0/4.5는 transformers에 네이티브 통합돼 있다. 원격 코드가 필요 없다. 8GB에 들어가면서 네이티브인 것은 4.0 1.2B뿐이라 이걸로 결정했다.
실측으로 뒤집힌 판단 두 가지
양자화가 손해였다. 1.2B를 4bit로 올릴 이유가 없었다. bf16으로도 2.4GB뿐이고, 4bit는 bitsandbytes 역양자화 오버헤드 때문에 적재가 6배(44.2초 → 7.5초), 생성이 2배(11.5 → 24.4 tok/s) 느렸다. 작은 모델에서는 양자화하지 않는다.
스키마 필드 구성이 판정 정확도를 바꿨다. 이게 오늘 가장 큰 발견이다. 제약 디코딩은 스키마 순서대로 토큰을 생성하므로 필드 배치가 곧 사고 순서다.
| 스키마 | 결과 |
|---|---|
grade 먼저 | 0등급 (오답) |
evidence → grade 순서만 변경 | 0등급 (오답) |
comparison(대조 추론) 필드 추가 | 1등급 (정답) |
같은 모델이 평문 생성에서는 “171.3도는 165도 초과 → 1등급”으로 맞게 추론하는데, grade를 먼저 뱉게 하면 틀렸다. 순서만 바꾸는 것으로는 부족했고 — evidence가 수치를 되풀이할 뿐 대조를 안 했다 — 등급별 기준을 하나씩 대조하는 필드를 명시하고 나서야 정답이 나왔다. metric_ref도 그때서야 정확해졌다(그전에는 무관한 impact_frame을 근거로 지목).
오늘 확인한 것
- 5개 항목 전 구간 판정 동작
- 재현성 실측: 3회 반복 전부 80점, 표준편차 0.00 (목표 3점 이내)
- 항목별 근거 지표 정확히 지목
- 검수 전 루브릭이므로 결과에
provisional: true표기 - 테스트 20개 통과 (점수 합산 12, 특징 추출 8)
루브릭과 특징 추출의 지표 이름이 어긋난 것도 계약 테스트가 잡았다 (swing_knee_angular_velocity_peak → ..._frame_offset). 두 파일을 따로 수정하면 반드시 어긋나므로 이 테스트는 유지한다.
다음 할 일
- 실제 슈팅 영상 확보 — 포즈 파이프라인이 아직 합성 데이터로만 검증됐다
- 지도자 검수로 루브릭 각도 임계값 확정 (현재 임시값)
- 골든셋 라벨링 후 QWK 측정
- FastAPI 래핑, MySQL 적재