GitHub - chessire/shelter-puppy
Contribute to chessire/shelter-puppy development by creating an account on GitHub.
github.com
목소리를 얹는 순간,
화면이 음성에 맞춰 늘어나고,
타임라인은 음성이 컨트롤합니다.
우선 작업 결과부터 보고 가겠습니다.
프롬프트
우리 토리 소개 영상 만들자. 먼저 토리가 가만히 있는 장면 하나를 가지고 토리 얼굴을 천천히 클로즈업으로 5초 보여주고 아래에 텍스트로 우리 토리를 소개합니다를 띄워주면서 읽어줘. 그 다음 애견카페에서 뛰어 노는 모습 10초 편집해서 아래에 텍스트로 똥꼬발랄한 우리 토리 띄워주고 마찬가지로 읽어줘. 그 다음에는 산책하고 비오는 날 뛰고 밤에 달리는 신나게 뛰어노는 모습을 10초 보여주면서 산책도 좋아하는 우리 토리를 아래에 텍스트로 띄워주면서 읽어줘. 그리고 마지막으로 하이파이브 하는 장면으로 우리 예쁜 토리입니다 텍스트를 아래에 띄우고 읽어주면서 끝내줘.
"TTS면 대본 넣고 음성 뽑아서 영상 위에 깔면 끝 아냐?"
이번 함정은 세 겹입니다. 어떤 TTS가 좋은지는 누가 판정하는가. 어느 문장이 몇 초에 시작해야 화면과 맞는가(싱크). 그리고 TTS가 이름을 엉뚱하게 읽으면 누가 알아차리는가(검증). 셋 다 [깔면 끝]이라는 명제에는 없는 문제들입니다.
목소리는 귀로 채점하고,
경계는 산수로 박고,
결과는 기계로 듣는다.
영상 작업을 1차로 마무리 짓고, 건너뛴 다섯번째 단계(TTS 내레이션)로 돌아왔습니다. 이번 편은 그 다섯번째 단계의 구현, 편집 / 내레이션 두 모드의 자동 판정, 그리고 2×2 통합 테스트까지 다룹니다. 결론부터 말하면 이번에도 채점이 직관을 뒤집었습니다. 1라운드에서 탈락시킬 뻔했던 엔진이 최종 승자가 됐거든요.

8. 귀에도 정답지를 - 골든이어셋 2라운드
TTS의 선택은 취향 문제처럼 보였습니다. 하지만 채점 문제로 바꿨습니다. 시리즈 첫 편의 골든셋을 소리로 옮긴 골든이어셋 — 함정 문장 10개를 고정해두고, 후보 엔진 3개가 합성한 음성을 블라인드로 들었습니다. 사람 귀(톤)와 기계(Whisper로 되읽혀 글자 오류율 측정), 그리고 속도(RTF)로 채점합니다.
1라운드에서 사고가 났습니다. 유력 후보였던 Qwen3-TTS가 날짜와 숫자를 뭉개 읽어 기계 채점에서 탈락권이었습니다. 그런데 숫자 읽기는 어차피 한국어 정규화 전처리기(7.2 -> 칠 점 이)가 파이프라인에서 제거할 축이었습니다. 실전에는 존재하지 않는 조건으로 채점하고 있었던 겁니다. 정규화된 입력으로 2라운드를 다시 돌리자 세 엔진 모두 만점, 승부는 톤 청취로 갈렸고 Qwen3-TTS가 이겼습니다. 톤 지시(발랄하게, 차분하게)가 되는 유일한 엔진이라는 점도 컸습니다.
첫 편에서 "자가 휘어 있으면 측정이 무의미하다"고 적었는데, 이번에 하나 더 배웠습니다. 채점 조건도 검증 대상이었습니다. 실전에 없는 조건으로 후보를 떨어뜨리면 그건 측정이 아니라 오심입니다.
목소리는 9종을 들어보고 하나로 정했고, 규칙도 하나 세웠습니다. 한 영상 안에서 내레이터는 바뀌지 않는다. 블록마다 분위기가 달라져야 하면 목소리를 바꾸는 게 아니라 톤 지시로 조절합니다. 중간에 내레이터가 바뀌는 건 연출이 아니라 편집 사고라고 생각했습니다.
9. 싱크는 측정이 아니라 산수 - 구절별 합성
가장 걱정했던 건 싱크였습니다. 대본 한 문단을 통째로 합성하면 두 번째 문장이 몇 초에 시작하는지 알아내기 위해 음성을 역으로 분석(forced alignment)해야 합니다. 측정이 하나 늘고, 오차도 따라옵니다.
그런데 발상을 바꾸면 이 문제는 사라집니다. 대본은 이미 LLM이 구절 단위로 분해해 줍니다. 그러면 구절별로 따로 합성해서 구절 사이를 무음으로 채워 조립하면 됩니다. 각 구절이 몇 초에 시작하는지는 측정할 대상이 아니라 우리가 정하는 파라미터가 됩니다. 실측 결과, 발화 시작이 블록 경계 0초, 5초, 15초, 25초에 오차 0으로 박혔습니다. 각 구절의 실제 길이를 재서 다음 경계를 다시 계산하는 산수라 누적 오차도 없습니다.
그리고 구절마다 합성 결과를 캐시하니 대본에서 문장 하나만 고치면 그 구절만 다시 합성합니다(나머지는 캐시에서 0.09초). 그리고 영상이 구절보다 짧으면 화면을 늘려서 맞춥니다. 내레이션이 타임라인을 컨트롤 하는 원칙의 구현입니다.
남는 문제가 검증입니다. TTS는 아주 가끔 이름을 엉뚱하게 읽습니다(실측: "토리"를 "프린"으로). 빈도가 낮아서 사람이 매번 다 들어볼 수는 없습니다. 그래서 합성 직후 Whisper로 되읽혀 원문과 대조하는 왕복 게이트를 달았습니다. 불일치하면 한 번 재합성합니다. 만든 쪽이 스스로 검사하는 채점기의 TTS판입니다.
10. 번역가의 실수 일곱 가지 - 검열이 아니라 복구
내레이션 모드에서 LLM의 번역 출력은 훨씬 길고 복잡해집니다. 그러자 실측에서 실패가 일곱 종류나 연쇄로 터졌습니다. 응답이 중간에 잘리고, 같은 문장을 무한 복창하고, 대본 전체를 한 블록에 삼키고, 요청에 없는 키워드를 전 블록에 입히고, [5초]나 [클로즈업] 같은 명시 지시를 놓치고…
고치면서 패턴이 보였습니다. 생성 사고(잘림과 복창)는 파라미터로 잡지만 구조적인 해법은 결국 하나였습니다. 호출을 잘게 쪼개 출력을 소형화 할 것. 대본 문장 추출과 문장별 속성 판정을 분리하고, 문장당 한 번씩 물어 선택지 위주로만 답하게 했습니다. 지난 편 오지선다와 같은 느낌입니다. 그리고 대본 텍스트 자체는 LLM 응답에서 받지 않고 파이썬이 직접 꽂습니다. LLM이 대본을 변형할 통로를 아예 없앤 겁니다.
[5초]나 [클로즈업] 같은 리터럴 누락은 결정론으로 복구했습니다. 요청 원문에서 해당 표현과 가장 가까운 문장에 귀속시키는 규칙입니다. "텍스트로 띄워줘"를 놓치면 내레이션 문장을 자막으로 쓰는 폴백도 달았습니다. 사용자가 명시한 것을 복구하는 방향이었습니다.
반대 사례가 하나 있었습니다. "소개 영상 만들자"라는 목적 문장을 제목 지정으로 오독해 화면에 제목을 박는 문제. 처음엔 결정론 가드(제목 키워드가 없으면 소거)를 검토했다가 기각했습니다. 의미 판단을 문자열 검열로 대신하면 고객이 프롬프트 작성법을 배워야 작동하는 명령어 체계가 됩니다. 자연어로 말하면 된다는 존재 이유를 배반하는 거죠. 이건 프롬프트 보강으로 풀었고, 테스트 배터리 7/7을 통과했습니다.
경계선을 정리하면 이렇습니다. 사용자 프롬프트의 복구와 쓰레기 차단은 결정론, 의미 해석은 프롬프트. 지난 편이 "LLM에게 모션을 맡기지 마라"였다면, 이번 편은 "결정론에게 의미 검열을 맡기지 마라"입니다. 경계선은 양쪽으로 지켜야 합니다.
11. 두 모드, 한 기계 - 2×2로 증명
이제 편집-Only(모드 A)과 편집 + 내레이션(모드 B)이 공존합니다. 어느 것인지는 누가 정할까요. 2단 판정입니다. 명시 표현이 있으면 결정론으로 확정하고, 없으면 LLM에게 2지선다 확률로 판정합니다. 동작 판별에서 검증한 레시피의 재사용입니다.
구조도 하나로 합쳤습니다. 편집 블록에 내레이션 필드 하나를 확장해 두 모드가 같은 편집 프로세스와 렌더 프로세스를 활용합니다. 내레이션 모드에서는 문장이 곧 블록입니다. 덧붙여 시청자에게 알리는 표시도 넣었습니다. 우상단에 [AI 편집] 배지가 상시로 뜨고, 음성이 있으면 [내레이션: AI 음성]이 한 줄 더 붙습니다.
마지막 검증은 같은 요청을 자막과 읽기의 유무로 가른 2×2 매트릭스입니다.
| 자막 | 읽기 | 모드 | 판정결과 |
| O | O | 편집 + 내레이션 (확신 1.00) | 27.6초, 자막+음성, 발화가 블록 경계 정각 |
| O | X | 편집-Only (확신 1.00) | 26.5초, 자막만, 무음 |
| X | O | 내레이션-Only (확신 1.00) | 26.5초, 음성만 — 자막 폴백이 정확히 침묵 |
| X | X | 편집-Only (확신 1.00) | 26.5초, 화면 텍스트 0 |
판정 4/4, 부작동 0. 특히 세 번째 줄이 마음에 듭니다. 자막 폴백이 있는데도 "자막 없이"라는 요청에서 자막을 제외했다는 것. 폴백이 명시 요청의 복구 장치이지, 시키지 않은 일을 하는 장치가 아니라는 증거니까요.
맺음말
여기까지로 두 모드가 모두 세팅되었습니다.
- TTS 선택은 취향이 아니라 골든이어셋 채점으로 정하고,
- 싱크는 측정 대신 구절별 합성 + 무음 조립의 산수로 박고,
- 번역의 실수는 명시된 것의 복구는 결정론, 의미 해석은 프롬프트로 갈라 고치고,
- 두 모드를 한 기계에 태워 2×2 매트릭스로 증명했습니다.
시리즈를 관통하는 문장이 하나로 모입니다. 감각을 믿지 말고 정답지로, LLM의 확신을 믿지 말고 측정으로, 그리고 이번엔 소리도 채점하라. 남은 수동 단계 두 개(영상마다 장면 태그를 적는 것, 여러 마리 중 주인공을 골라주는 것)는 이후 자동 태깅과 강아지 사진 몇 장으로 풀어 이제 영상과 사진만 넣으면 결과가 나옵니다. 그런데 그렇게 나온다고 끝이 아니었습니다. "소개 영상 만들어줘" 한 줄에 파이프라인이 내놓은 건 소개 영상이 아니라 영상의 나열이었거든요. 나오는 것과 잘 만드는 것 사이. 다음 편에서 풀어가겠습니다.
이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.
전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.
→ 오픈 알림 받기
다음글
LLM은 지시만: 프롬프트 오염을 막는 설계 - Ownership First AI
GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com요청이 짧다고 영상까지 짧아지면 안 됩니다.빈칸은 LLM의 저작으로 채우고,채워진 칸은 아무도 건드
chessire.tistory.com
이전글
결정론적 AI 파이프라인 설계 - Deterministic First AI
GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.comLLM은 영상을 만들지 않습니다.영상은 결정론 코드가 만들고,LLM은 사람의 말을 코드로 번역할 뿐입니
chessire.tistory.com
관련글
오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크
모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거
chessire.tistory.com
'AI > Pipeline' 카테고리의 다른 글
| 스레드보다 자원: Mac GPU(MPS)로 3.5배 빠르게 - Resource First AI (0) | 2026.07.27 |
|---|---|
| LLM은 지시만: 프롬프트 오염을 막는 설계 - Ownership First AI (0) | 2026.07.23 |
| 결정론적 AI 파이프라인 설계 - Deterministic First AI (0) | 2026.07.13 |
| YOLO11 해상도 함정: 골든셋으로 검증 - Measurement First AI (0) | 2026.07.08 |
| 로컬 LLM 아키텍처 - Local First AI (0) | 2026.07.06 |