"추측을 넣어 놓고 되무를 순 없어. 그러니 더 이상 추측이 아닌 부분만 넣어."
불일치
Whisper 계열 모델은 근본적으로 배치 야. 오디오 덩어리를 주면 전사를 받아. 그런데 좋은 받아쓰기는 스트리밍 처럼 느껴져. 말하는 대로 단어가 나타나니까. 순진하게 다리를 놓으면, 그러니까 '30초 녹음하고, 한 번 전사하고, 붙여넣기' 하면 기술적으론 되지만 죽은 느낌이야. 말하는 동안 아무 반응이 없고, 끝나고 한참 멈춰 있거든. 라이브한 느낌을 내려면 배치 모델 위에 스트리밍을 흉내 내야 해. 조심해서. 배치 모델은 뒤를 더 들으면서 앞 단어에 대한 생각을 계속 바꾸니까.
LocalAgreement
비결은 엔진 위에 앉는 스트리밍 컨트롤러야. 롤링 버퍼를 유지하고, 오디오가 커지면서 배치 모델을 반복 돌리고, 각 새 가설을 이전 것과 비교해. 연달아 돌린 두 패스가 앞부분에서 일치하면, 그 앞부분을 커밋해. 최종으로 보여줘도 될 만큼 안정됐다는 뜻이니까. 아직 안 맞는 꼬리는 프리뷰로만 보여 주고 절대 최종으로 취급 안 해. 한번 커밋된 텍스트는 확정이야. 새 패스는 커밋된 텍스트 전체로 시작할 때만 커밋해. 그러면 그 뒤에 말한 걸 바로 전 패스가 그 뒤에 말한 것과 비교해서, 일치한 부분을 커밋하고. 커밋된 단어를 하나라도 고쳐 쓴 패스는 아무것도 커밋 안 해. 단어만 봐서는 그 패스 안에서 커밋된 텍스트가 어디서 끝나는지 알 수 없고, 짐작하다 보면 스케치가 같은 단어를 두 번 커밋하거든. 그런 패스도 커밋된 텍스트와 겹치는 제일 긴 앞부분 뒤의 단어는 프리뷰로 보여 줘. 그래서 새 단어가 안 빠지고 프리뷰도 안 비어. 패스들이 다시 커밋된 텍스트로 시작하고 서로 일치하면 커밋도 다시 흘러. 실제 구현은 단어 타임스탬프로 맞추고, 그래서 고쳐 쓴 패스에서도 계속 커밋할 수 있어. 이 스케치는 가진 게 단어뿐이라 보수적으로 가. 그게 LocalAgreement 아이디어야. 연달아 돌린 패스가 일치한 다음에만 텍스트를 확정하고, 아직 확정 안 된 꼬리는 보여만 주고, 안전할 때 문장 경계에서 버퍼를 다듬는 거지.
패스 N: "보고서를 보내"
패스 N+1: "보고서를 화요일에 보내" 앞부분 일치 ->
^^^^^^^^ 커밋됨 "보고서를"
^^^^^^^^^^ 아직 꼬리 -> 프리뷰만
# 일치한 앞부분만 커밋. 흔들리는 꼬리는 절대 삽입 안 함.
MVP 규율
말하는 동안 라이브로 타이핑하는 게 어려운 버전이고, 거기서 시작하는 건 함정이야. 솔직한 MVP 는 부분 전사를 오버레이에 프리뷰로 보여 주다가 키를 놓을 때 한 번만 삽입해. 녹음 전체를 마지막으로 한 번 더 돌린 결과를, 아직 확정 안 된 꼬리까지 통째로. 그래야 말한 게 하나도 안 빠져. 그러면서도 MVP 안에서 '커밋됨' 과 '아직 아님' 은 내부적으로 갈라 놔. 그래야 나중에 진짜 라이브 타이핑을 붙이는 날, 기계는 이미 다 있고 커밋된 텍스트가 어디로 갈지만 바꾸면 되거든. 오버레이 대신 앱으로. 데이터 모델은 어려운 버전에 맞춰 짓고, 배포는 쉬운 버전부터 해.