GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

raw 폰 영상 아홉 개와 요청 한 문단.
약 4분 뒤, 내레이션이 붙은 세로 숏폼 하나.
그 사이의 모든 판단을 맥 한 대 안에서, 이 결과물이 나오게 됐습니다.

 

 

이 글은 그 파이프라인을 만든 7편의 기록에 대한 요약입니다.


"유기견 입양에 AI를 붙였다"가 가리는 것

보통은 이렇게 떠올립니다. 모델에 영상을 던지면, 알아서 편집되고, 그럴듯한 게 나온다. 데모 하나는 그럭저럭 나옵니다. 틀린 말은 아닙니다.

하지만 그 출발점은 정작 중요한 두 질문을 가립니다.

  • 내일 똑같이 다시 뽑을 수 있는가? (재현)
  • AI가 지어낸 것과 사람이 정한 것이 섞이지 않는가? (신뢰)

단순한 LLM 데모는 이 둘에 답하지 않습니다. 이 시리즈는 이 두 질문에 답하려고 내린 결정들의 기록입니다.


한 문단으로

  • LLM은 레시피만 씁니다. 실행은 코드가 합니다. 그래서 LLM이 환각을 뱉어도 실행 단계에서 격리됩니다. (temperature 0 + 스키마 강제 + 허용셋 밖 라벨은 거부)
  • 모델은 갈아끼우는 부품, 골든셋은 그 부품을 채점하는 기준입니다. 눈으로 하는 데모 비교 대신, 정답을 손으로 계산해 대조하는 단위테스트 130여 개 위에서 결정을 내렸습니다. 실제로 당연히 먹힐 것이라고 여겼던 개선안들이 골든셋 채점에서 두 번이나 기각 당했습니다.
  • 가능한 많은 것을 외부 API가 아니라 맥 한 대 안에 둡니다. 데이터가 매 호출마다 밖으로 나가지 않고, 호출 비용이 없고, 통제권이 제 손에 남습니다.
  • 자동화가 못 믿는 딱 그 지점에만 사람을 남깁니다. 재식별은 90%를 자동으로 하고, 애매한 나머지는 카드 한 장·탭 한 번(영상당 1.75탭 실측)으로 끝냅니다. "AI가 다 한다"보다 정직하고, 실제로 더 잘 작동했습니다.

이건 강아지 영상 이야기가 아닙니다

도메인은 강아지였지만, 만든 것은 신뢰할 수 있는 로컬 AI 파이프라인을 세우는 방법론입니다. 골든셋으로 채점하는 법, LLM과 코드의 권한을 가르는 법, 환각을 격리하는 법, 빠르게 만들 때조차 결과 동일성부터 지키는 법. 이 규칙들은 영상 도메인에 묶여 있지 않습니다. 입력이 픽셀이든 문서든 로그든, 옮겨 붙습니다.

그래서 이 글은 두 부류를 위한 것입니다. 데모가 아니라 내일도 똑같이 도는 시스템을 만들어야 하는 사람. 그리고 no-code 위에서 뭔가 만들었는데 왜 두 번째 입력에서는 안 되는지 답답한 사람.


7편 링크

  1. 맥 로컬 LLM 구축, 무엇을 내 손 안에 두고, 무엇만 밖에 맡길 것인가. 노트북 한 대에 세운 환경.
  2. 로컬 LLM 아키텍처, 왜 로컬인지, 왜 이 구성인지. 선택의 이유.
  3. YOLO11 해상도 함정, 정답지부터 만들고 모델을 붙인다. 해상도를 올렸더니 오히려 나빠진 이야기.
  4. 결정론적 AI 파이프라인 설계, 같은 입력이면 같은 출력. 창작만 예외로 두되, 그 창작에도 가드를 건다.
  5. 로컬 TTS로 타임라인 컨트롤, 대본을 음성으로, 음성을 영상 타임라인에 맞추기.
  6. LLM은 지시만, 프롬프트에 내용을 넣으면 출력으로 샌다. 같은 문제를 네 번 고치고 얻은 규칙.
  7. 스레드보다 자원, 8분을 4분으로. 가장 큰 배수는 멀티스레딩이 아니라, 놀고 있던 GPU를 깨운 한 줄에서 나왔습니다.

정직하게 남겨둘 것

이 검증들은 서로 다른 다섯 개의 영상으로 만든 골든셋에서 이뤄졌습니다. 도메인은 하나, 혼자서 진행했습니다. 그래서 이 기록은 이 방법이 모든 곳에서 최고라는 것이 아닙니다. 이 방법이면 만든 것을 믿을 수 있고, 부품을 갈아끼워도 기준이 흔들리지 않는다는 것입니다. 방법론의 엄밀함이지, 규모의 검증은 아닙니다. 그 경계를 넘는 건 다음 도메인의 몫입니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


이전글

 

스레드보다 자원: Mac GPU(MPS)로 3.5배 빠르게 - Resource First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com파이프라인을 빠르게 만드는 첫 질문은"스레드를 몇 개 띄울까?"가 아니라"어떤 자원이 어디서 놀고

chessire.tistory.com

 

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

파이프라인을 빠르게 만드는 첫 질문은
"스레드를 몇 개 띄울까?"가 아니라
"어떤 자원이 어디서 놀고 있나?"입니다.

 

"느리면 그냥 멀티스레딩 돌리면 되는 거 아냐?"

 

이번 함정은 그 직관이 절반만 맞다는 데 있습니다. 스레드를 늘려도 공유 자원 앞에서는 줄만 서기 때문에 안 빨라지는 구간이 있습니다. 스레드와 무관하게 놀고 있던 자원(GPU)도 있습니다. 심지어 빨라졌는지 판정하는 것조차 같은 코드 실행에도 실행 속도가 2배 차이가 될 수 있는 함정이 있습니다. 이번 최적화로 자동 영상 편집에 8분 걸리던 파이프라인을 4분으로 줄였는데 결론부터 말하면 가장 큰 배수는 멀티스레딩이 아닌 한 줄 수정에서 나왔습니다.

 

스레드보다 먼저 자원의 지도를 그린다.
겹칠 수 있는 것만 겹치고,
결과는 완전히 동일해야 한다.

 

이번 편은 실사용 병목 3구간(전처리+검출, 관찰 프로필, 동작 판정)의 속도 최적화 기록입니다. 원칙은 하나로 요약됩니다. 단계 간 직렬은 유지하고, 단계 안에서만 병렬화한다. 파이프라인의 순서와 게이트 구조는 건드리지 않았습니다.

 

Multi-Threading vs Resources

16. 스레드보단 자원을 살펴보자 - 다섯 개의 자원

병렬화를 시작하기 전에 파이프라인이 쓰는 자원부터 그렸습니다. 다섯입니다. Ollama(Gemma, GPU), MLX(TTS, GPU), MPS(YOLO, GPU), ffmpeg(CPU 서브프로세스), OpenCV / Torch(CPU). 지도를 그리고 나니 제일 중요한 사실이 보였습니다. Ollama는 서버가 요청을 직렬로 처리하는 공유 자원입니다. 클라이언트에서 스레드를 아무리 늘려도 서버 앞에서 줄만 섭니다.

 

그래서 이번 최적화의 이득은 전부 한 곳에서 나왔습니다. Ollama 작업과 비-Ollama 작업을 겹치는 것. LLM이 크롭 하나를 판정하는 동안 CPU는 다음 영상의 모션 곡선을 계산하고, ffmpeg는 다음 영상을 정규화합니다. LLM을 빠르게 만든 게 아니라, LLM이 일하는 동안 다른 자원이 놀지 않게 만든 겁니다.

 

병렬화에도 안전을 위한 규칙을 먼저 세웠습니다. 병렬 구간은 meta파일을 만지지 않고 쓰기는 오케스트레이터가 루프 밖에서 한 번, 판정 로직은 입력이 전부 모인 뒤의 직렬 흐름 그대로 두고, 완료 순서는 비결정이어도 조립은 인덱스로 고정합니다. 그리고 합격 기준은 속도가 아니라 결과 동일성입니다. 빨라졌는데 결과가 달라졌다면 그건 최적화가 아니라 버그니까요.


17. 최대 병목은 병렬화가 아니었다 - 놀고 있는 GPU

전처리(ffmpeg)와 검출(YOLO)을 파이프라이닝했습니다. 영상 i를 검출하는 동안 영상 i+1을 정규화하는 구조는 교과서적인 이종 자원 오버랩입니다. 그런데 체감이 없다는 피드백이 왔습니다.

 

구간별 비중을 실측해 보니 범인은 따로 있었습니다. 검출 모델이 내내 CPU로 돌고 있었습니다. Mac에서 이 라이브러리의 디바이스 자동 선택은 GPU가 아니라 CPU였습니다. Apple GPU(MPS)를 쓰려면 명시해야 했습니다. 그래서 한 줄을 고쳤습니다. 157.9초 → 45.4초, 3.5배. 이번 최적화 전체에서 가장 큰 배수가 스레드 하나 없이 나왔습니다.

 

물론 검증은 시리즈의 규칙대로입니다. 골든셋 5영상을 GPU로 재검출해 재채점하여 박스 수, 트랙 수, miss/IDF1을 포함한 모든 지표가 소수 4자리까지 완전 동일했고, 게이트 판정도 그대로였습니다(예외 처리된 나쁜 footage의 FAIL까지 똑같이). 디바이스가 바뀌어도 자는 휘지 않았다는 확인입니다.

 

비슷한 사례가 하나 더 있었습니다. 모션 곡선이 프레임마다 랜덤 시크로 영상을 읽고 있었는데 랜덤 시크는 매번 키프레임부터 다시 디코드했습니다. 순차 읽기로 바꾸자 영상별로 4.6 -> 0.8초, 11.9 -> 1.7초, 약 6배. 이것도 병렬화가 아니라 직렬 최적화입니다. 교훈을 한 문장으로 정리하자면, [구조를 바꾸기 전에, 자원이 제대로 쓰이고 있는지부터 확인하라.] 였습니다.


18. 겹치기의 산수 - 비용을 서로의 뒤에 숨긴다

자원 지도가 그려지고 GPU를 활용한 뒤에야 오버랩이 값을 하기 시작했습니다. 세 구간입니다.

  • 전처리 || 검출 — ffmpeg(CPU)가 다음 영상을 정규화하는 동안 YOLO(GPU)로 현재 영상을 검출했습니다. 전처리 비용이 검출 뒤에 숨어, 준비 구간이 50.7초에서 17.1초(3배)로 줄었습니다.
  • 관찰 프로필 — 영상마다 캡션(Ollama, 직렬)과 센서 4축(오디오, 배경, 장소, 휘도, CPU)을 뽑는데 센서를 영상 간 병렬로 팬아웃시켰고 총시간이 캡션 합 + 센서 합에서 max(캡션 합, 센서 합)으로 바뀌면서 센서 비용을 통째로 은닉시켰습니다.
  • 동작 판정 — 모션 계산(CPU)을 팬아웃하고 LLM 판정만 직렬로 했습니다. 모션의 비용이 판정 뒤로 숨었습니다.

스레드가 들어오면서 따라오는 숙제도 처리했습니다. 모델 지연 로딩 3곳에 이중 확인 락(같은 모델을 두 번 로드해 메모리가 2배가 되는 사고 방지), 병렬 워커는 영상별 산출물 파일만 쓰기, 임시파일명에 인덱스 포함. 기존 테스트 102개는 전부 그대로 통과하였고 CLI와 같은 인터페이스는 하나도 안 바뀌었습니다. 병렬화는 전부 내부 구조입니다.


19. 단순 측정을 믿지 마라 - 분산부터 통제

마지막 함정은 측정 그 자체였습니다. 동작 판정 구간을 같은 입력으로 세 번 돌렸더니 28.1초, 22.9초, 14.3초. 같은 코드가 2배 차이 났습니다. Ollama 응답 시간의 분산이 구간 전체를 지배해서 한 번씩 재는 A / B 비교가 무의미해졌습니다.

 

실제로 두 번 속았습니다. 새 코드의 첫 실행이 옛 코드보다 느리게 나왔는데(9.7초 vs 10.7초) 첫 실행에 모델 로드와 Ollama 웜업이 섞여 있었습니다. 웜 상태로 다시 재니 7.8초(−20%)가 나왔습니다. 또 한 번은 22.9초(구코드)에서 28.1초(신코드)로 느려졌나 싶었는데 재실행에서 14.3초. 실행할 때마다 측정 결과가 달랐습니다.

 

그래서 판정 방법을 갈랐습니다. 결정론 구간(모션 곡선, 검출)은 조건을 맞춘 단발 A / B로 숫자를 재고, LLM이 섞인 구간은 단순 측정 대신 출력 동일성과 구조적 은닉(직렬 합이 max로 바뀌었다는 산수)으로만 판단합니다. 시리즈 첫 편에서 "자가 휘어 있으면 측정이 무의미하다"고 했는데, 마지막 편에서 하나 더 보탭니다. 분산을 통제하지 않은 단순 측정도 휜 자입니다.


맺음말

자동 영상 편집 1회 라이프사이클 기준, 총 8분이 4분이 됐습니다.

  • 자원 지도를 먼저 그려 공유 자원(Ollama)과 겹칠 수 있는 자원을 가르고,
  • 최대 배수(3.5배)는 스레드가 아니라 놀고 있던 GPU를 깨운 한 줄에서 얻고,
  • 오버랩으로 전처리, 센서, 모션의 비용을 LLM 작업 뒤에 숨기고,
  • 단순 측정의 분산을 통제해, 빨라졌다는 느낌 자체를 측정답게 만들었습니다.

그리고 이 모든 변경의 합격 기준은 속도가 아니라 결과 동일성이었습니다. 골든셋 지표 소수 4자리까지.

 

남은 부분도 있습니다. Ollama 서버 자체의 동시성, TTS와 렌더의 파이프라이닝. 하지만 raw 폰 영상 아홉 개와 요청 한 문단이 4분 만에 내레이션 붙은 숏폼이 되는 지금, 개발기로서의 이야기는 여기서 일단락하려 합니다. 골든셋으로 채점하고, 측정할 수 있는 것은 측정에게 맡기고, 소리도 채점하고, 구조의 주인을 하나로 세우고, 빠르게 만들 때조차 자부터 검증한다. 이 시리즈가 남긴 문장들입니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

pgvector와 bge-m3로 로컬 RAG 구축 - Provenance First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com임베딩은 진위를 가리지 않고,유사도를 잽니다.검수는 검색 뒤가 아니라,저장 앞에 세웠습니다. 영

chessire.tistory.com

이전글

 

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

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

요청이 짧다고 영상까지 짧아지면 안 됩니다.
빈칸은 LLM의 저작으로 채우고,
채워진 칸은 아무도 건드리지 않습니다.

 

우선 작업 결과부터 보고가겠습니다.

프롬프트

우리 토리 소개 영상 만들어줘

(애견카페 장면을 집에서 쉬는 장면으로 오해한 할루시네이션이 포함됨)

 

프롬프트

우리 토리 소개 영상 만들어줘 뛰어노는 모습 좀 나오다가 산책하고 밤에도 달리고 마지막에 하이파이브로 끝나면 딱이겠다

 

'소개 영상 만들어줘' 한 줄이면 알아서 잘 나와야 하는 것 아냐?

 

이번 함정은 파이프라인이 아니라 요청 쪽에 있었습니다. 한 줄 요청에는 구조 정보가 없습니다. 몇 블록으로, 어떤 순서로, 자막은 뭐라고, 아무것도 없죠. 지난 편까지의 파이프라인은 이 요청에 성실하게 답했습니다. 기본값 블록 하나, 소스당 한 컷씩 26초. 하지만 "소개 영상 만들어줘"라는 요청에는 소개 영상이 아니라 영상의 나열이 나왔습니다. 번역할 문장이 없으면 번역기는 구성을 못 합니다.

 

유저가 정하지 않은 것만 LLM이 저작하고,
유저가 정한 것은 LLM이 마음대로 건드리지 않는다.

 

잠시 배경 하나를 설명드리면 지난 편 이후 남았던 수동 단계 둘은 자동화로 풀었습니다. 영상마다 기계가 "무엇이 찍혔는지" 적어두는 관찰 프로필, 그리고 강아지 사진 몇 장으로 여러 마리 중 주인공을 확정하는 프레임 앵커. 덕분에 영상과 사진만 넣으면 결과가 나옵니다. 이번 편은 고정 형태의 파이프라인을 잘 만드는 파이프라인으로 다듬은 기록입니다. 한 줄 요청을 구성으로 바꾸는 저작 모드. 요청 디테일 천차만별에 대응하는 소유권 원칙. 그리고 실사용이 낸 숙제들. 결론부터 말하면 이번 구간에서 같은 문제를 네 번 고치고 나서야 규칙 하나를 얻었습니다. 프롬프트에는 지시만 적는다. 내용은 금지.

 

Two decisions, one pipeline


12. 한 줄 요청을 구성으로 - 저작 모드

편집의 구조가 요청에 없다면 어떻게 만들어야 할까요. 소재와 목적. 무엇이 찍혀 있는지(관찰 프로필)와 무엇을 원하는지(요청의 느낌)를 알면 구성은 만들 수 있습니다. 그래서 번역기 앞에 LLM을 다시 세웠습니다. 간단한 요청이 들어오면 LLM이 요청 원문과 영상별 관찰 프로필, 그리고 소재 길이를 보고 블록을 직접 구성합니다. 어떤 영상을 어떤 순서로, 몇 초씩, 자막은 뭐라고 할지 말이죠.

 

소재 길이를 입력에 넣은 건 실측 때문입니다. 길이를 모르는 LLM의 저작은 2.3초짜리 영상을 인트로와 엔딩 두 블록에 배치하는 실수를 했습니다. LLM에게도 재료의 속성은 알려줘야 합니다.

 

재미있는 결정 하나가 있습니다. LLM의 저작은 이 파이프라인 전체에서 유일하게 의도된 비결정입니다. 지금까지 "같은 입력 = 같은 출력"을 그렇게 지켜놓았지만 저작은 정답이 없는 주관 축이기 때문입니다. 같은 요청에서 매번 다른 구성이 나와도 되고, 마음에 안 들면 다시 뽑으면 됩니다(빠른 재추첨 옵션 추가). 결정론은 정답이 있는 곳의 규칙이지, 창작을 막으면 안되니까요.

 

다만 창작에도 결정론은 붙습니다. 작가가 지어낸 소재 이름은 실제 목록과 대조해 환각을 제거하고, 블록 길이와 개수는 Clamp하고, 배속은 [요청이 명시한 경우만]이라는 가드를 유지합니다. 실측 샘플로 "문틈 사이로 빼꼼! 우리 토리 등장!" 같은 자막이 나왔는데, 해당 영상의 관찰 프로필에 실제로 문틈 장면이 기록돼 있었습니다. 창작이되, 근거 있는 창작입니다.


13. 소유권은 이진, 빈칸은 Gradient - 요청 디테일 대응

실제 요청은 한 줄만 오지 않습니다. "소개 영상 만들어줘"부터, "뛰어노는 모습 나오다가 산책하고 밤에 달리고 하이파이브로 끝" 같은 스케치, 블록별 초와 자막까지 박은 풀 스펙까지. 디테일이 천차만별입니다. 처음엔 [요청이 얼마나 구조적인가]를 점수로 재려 했습니다. 잘못된 접근이었습니다. 구조는 결국 하나로 귀결돼야 합니다. 유저가 뼈대를 줬으면 구조는 유저 것이고, 안 줬으면 LLM이 저작합니다. Gradient인 건 구조가 아니라 빈 필드의 수입니다.

요청 수준 결과
"소개 영상 만들어줘" 전체 저작 작가가 구성 창작 (4~5블록)
"뛰어놀다가 → 산책 → 밤 달리기 → 하이파이브 엔딩" 부분 저작 유저 4박자 보존 + 빈칸(자막·초·소재)만 채움
블록별 스펙 완비 저작 0호출 빈칸 없음 → 작가 미출동

 

병합은 결정론입니다. LLM의 저작에서 유저가 명시한 필드는 아예 읽지 않습니다. "덮어쓰지 않도록 주의한다"가 아니라 읽는 코드 자체가 없는 구조적 불변입니다. 대본 스킵, 복창 사고와 같은 문제들이 재발하지 않도록 원천 차단한 겁니다.

 

텍스트는 한 단계 더 엄격합니다. 요청에 자막 지시가 하나라도 있으면 자막은 통째로 유저의 소유입니다. LLM은 빈 블록의 자막조차 채우지 않습니다. 절반의 자막을 유저가 썼는데 나머지 절반을 LLM이 채우면 유저의 의도가 사라질 수 있습니다. 덤으로 잠재돼있던 버그도 하나 잡았습니다. [자막 없이]라는 요청에 [자막]이라는 단어가 포함돼 있어서 오히려 자막을 채우던 것입니다. 부정 표현이 긍정 표현보다 우선한다는 원칙으로 수정했습니다.


14. 프롬프트에는 지시만 - 같은 문제만 네 번

이번 구간에서 제일 어려웠던 문제였습니다. 자막에 요청 전문을 그대로 발화하는 사고가 났습니다("…만들어줘"까지 통째로). 1차 수정은 결정론으로 필터링 했습니다. 출력에서 요청 문자열을 지우는 필터였습니다. 그런데 이건 단순히 표면적으로 문제를 지웠습니다. 실질적 문제는 프롬프트에 있었는데 말입니다. "요청의 말투를 그대로 살려서"라는 지시가 사실상 인용의 원인이었던 겁니다. 프롬프트를 "요청은 지시문이고, 자막은 시청자에게 말하는 새 문장"으로 고치자 4회 배터리에서 복창 0건(수정 전 2/4)으로 수정되었습니다.

 

같은 문제가 또 발생했습니다. 요청에 없는 캠페인성 문구가 자막에 붙어 나왔는데 추적해 보니 프롬프트에 박아둔 페르소나(~홍보 영상 편집기)가 요청에 없는 목적을 주입하고 있었습니다. 프롬프트 6곳을 전부 중립적인 "강아지 영상"으로 고쳤습니다. 목적과 톤은 요청에서만 옵니다.

 

패턴을 정리하면 네 번 모두 같은 문제였습니다. 코드나 프롬프트에 박힌 내용(어휘, 예시 문구, 프레이밍)이 출력으로 샌 것입니다. 그래서 규칙을 정리했습니다. 프롬프트에는 지시만, 내용은 금지. 결정론은 폐기하지 않고 가드로 강등했습니다.


15. 실사용이 낸 숙제들 - 폴리싱과 권한

실사용 데모를 돌리며 자잘한 숙제들이 쏟아졌습니다. 영상이 12.2초로 짧게 나오고(LLM이 길이 필드를 안 적는 습성. 스키마에서 필수로 강제), 0.8초짜리 컷이 화면에서 번쩍하고 지나가고(표시 하한 1.5초 규칙 추가), 제목과 자막이 한 자리에 겹쳐 찍혔습니다. 자막 겹침은 8방위 텍스트 영역 시스템으로 풀었습니다. 상단, 하단, 좌우의 네 구석에 텍스트 자리를 정의하였고, 상단 행은 AI 표시 배지를 침범하지 않게 그 아래에서 시작하며, 긴 자막은 자동 개행합니다. 위치와 표시 타이밍도 소유권 원칙 그대로 유지했습니다. 유저가 명시하면("왼쪽 아래에 자막") 번역기가 받아 적고, 아니면 LLM의 연출 재량입니다.

 

인프라 쪽에서는 무서운 버그를 하나 잡았습니다. 전처리 산출물이 깨졌을 때 파일이 존재한다는 이유로 재사용되면서 깨진 캐시가 계속 사용되는 버그였습니다. 영상 하나의 분석이 통째로 증발했습니다. 재사용 조건에 내용 검증을 추가했습니다. TTS에서도 하나, 첫 구절 앞에 "흠" 하는 발성이 붙은 걸 Whisper 왕복과 파형으로 문제를 파악하고 해당 구절만 재합성해 지웠습니다.

 

마지막이 이번 편의 하이라이트입니다. 사진으로 지정한 주인공이 안 나오는 장면이 렌더에 들어왔습니다. 고양이만 걸어다니는 복도 컷이었죠. 문제를 추적하니 LLM이 소스를 직접 지정하면서 시스템이 이를 확실한 선택으로 취급해 Uncertain 구간임에도 영상으로 들어왔습니다. LLM의 선택은 영상 단위일 뿐인데 그것이 주인공이 없는 구간을 선택해버렸습니다. Uncertain 구간이 영상 단위 선택을 타고 그대로 들어온 겁니다.

 

수정은 권한의 분리였습니다. 유저의 프롬프트는 "그 장면을 원한다"는 보증이라 Uncertain 면제 + 소스 예약을 다 받지만, LLM의 지정은 "그 영상이 어울릴 것 같다"는 판단일 뿐이라 예약만 받았습니다. 여기에 프레임 앵커에 있는 확정된 주인공 박스로 실제 존재하는 구간인지 교차해서 클립을 뽑게 했습니다. 재렌더에서 고양이 컷은 사라졌고 모든 클립에 주인공이 있는 걸 프레임 단위로 확인했습니다.

 


맺음말

여기까지로 파이프라인이 나오는 것을 넘어 만드는 쪽까지 넘어왔습니다.

  • 저작 모드는 한 줄 요청에서 소재와 목적을 근거로 구성을 창작하고(파이프라인 유일의 의도된 비결정),
  • 소유권 원칙은 구조를 하나로 귀결시켜 유저가 정한 필드는 LLM의 저작이 아예 간섭하지 못하게 하고,
  • 프롬프트는 같은 문제 네 번 끝에 [지시만, 내용 금지]로 확립하고,
  • 권한 분리는 유저의 보증과 LLM 저작의 보증에 다른 크기의 권한을 줬습니다.

시리즈의 문장들이 이번 편에서 하나 더 늘었습니다. 정답지로 채점하고, 측정할 수 있는 건 측정에게 맡기고, 소리도 채점하고. 이것에 더해 이번엔 하나의 구조로 귀결시켜라. 구조든 텍스트든 권한이든 구조가 나뉘어지니 문제가 많아졌습니다.

 

남은 것은 저작 품질의 평가입니다. 구성이 좋은지는 골든셋으로 채점할 수 없는 주관 축이라, 지금은 재추첨 복권이 UX의 전부입니다. 저작 결과를 고객이 미리 보고 고르는 카드 UI, 그리고 이 파이프라인을 제품으로 감싸는 일. 다음 편에서 이어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

스레드보다 자원: Mac GPU(MPS)로 3.5배 빠르게 - Resource First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com파이프라인을 빠르게 만드는 첫 질문은"스레드를 몇 개 띄울까?"가 아니라"어떤 자원이 어디서 놀고

chessire.tistory.com

이전글

 

로컬 TTS(mlx-audio)로 영상 타임라인 컨트롤 - Narration First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com목소리를 얹는 순간,화면이 음성에 맞춰 늘어나고,타임라인은 음성이 컨트롤합니다. 우선 작업 결과

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

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라운드에서 탈락시킬 뻔했던 엔진이 최종 승자가 됐거든요.

TTS Engine with LLM

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

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

LLM은 영상을 만들지 않습니다.
영상은 결정론 코드가 만들고,
LLM은 사람의 말을 코드로 번역할 뿐입니다.

 

우선 작업 결과부터 보고 가겠습니다.

프롬프트

우리 토리 소개 영상 만들자. 먼저 토리가 가만히 있는 장면 하나를 가지고 토리 얼굴을 천천히 클로즈업으로 5초 보여주고 아래에 텍스트로 우리 토리를 소개합니다를 띄워줘. 그 다음 애견카페에서 뛰어 노는 모습 10초 편집해서 아래에 똥꼬발랄한 우리 토리 띄워주고 산책하고 비오는 날 뛰고 밤에 달리는 신나게 뛰어노는 모습을 10초 보여주면서 산책도 좋아하는 우리 토리를 아래에 텍스트로 띄워줘. 그리고 마지막으로 하이파이브 하는 장면으로 우리 예쁜 토리입니다. 텍스트를 아래에 띄우면서 끝내줘.



"영상 편집 AI면, LLM한테 영상이랑 요청을 통째로 주면 되는 거 아냐?"

 

매번 같은 질문을 하게 됩니다. 요즘 멀티모달 모델은 영상도 보고 말도 알아듣습니다. 하지만 이 출발점에는 함정이 있습니다. 같은 요청을 두 번 돌리면 다른 결과가 나오고, 프레임 단위 타이밍은 어긋나고, 왜 그렇게 잘렸는지 아무도 설명할 수 없습니다. 그래서 역할을 분리했습니다.

 

LLM에게는 '의미'만 맡긴다.
픽셀, 프레임, 타이밍은 전부 결정론 함수가 만진다.

 

지난 편에서 강아지를 찾고(검출), 다시 알아보고(재식별), 움직임을 재는(모션) 첫 레이어를 세웠습니다. 이번 편은 그 위에 올라가는 나머지, 동작 판별(네번째 단계), 편집 실행(여섯번째 단계), 그리고 자연어 한 문단이 완성 숏폼이 되는 통합 테스트까지 다룹니다. 다섯번째 단계(내레이션)를 왜 건너뛰었는지도요. 결론부터 말하면 이번 구간에서 배운 가장 값진 교훈은 이것입니다. LLM은 못 하는 일에서 아주 자신있게 틀립니다.

LLM Interpreter

4. 동작을 판별하다 - LLM에게 오지선다를

네번째 단계는 동작 판별입니다. 모션 곡선이 "움직인다"까지는 말해주지만, 그게 달리기인지 걷기인지 점프인지는 모릅니다. 여기서 처음으로 로컬 LLM(Gemma)이 등장합니다.

 

방식이 재미있었습니다. LLM에게 "이 강아지 뭐 하고 있어?"라고 서술형으로 묻지 않습니다. 오지선다 객관식(점프·달리기·걷기·앉기·엎드림)으로 묻고, 답의 첫 토큰 확률(logprob)을 읽습니다. 동적 동작들의 확률 합과 정적 동작들의 확률 합을 비교해 "동적인가 정적인가"부터 확정합니다. 서술형 답변은 파싱도 채점도 어렵지만, 객관식 확률은 숫자라서 파이프라인이 다룰 수 있습니다.

 

여기에 안전장치를 겹칩니다. LLM 판정이 모션 곡선과 일치하고 확신도 높을 때만 확정(commit), 아니면 모름(uncertain)으로 폴백. 네번째 단계는 혼자 다 맞혀야 하는 시험이 아니라, 애매하면 모르겠다고 말해도 되는 과정입니다. 모호함은 상위 레이어가 흡수합니다.

 

정답 라벨은 이번에도 사람이 만들었습니다. 모션 곡선으로 구간 후보를 자동으로 잘라 사람은 동작 이름만 채우게 해 품은 줄였지만 라벨 자체를 AI에게 맡기지는 않았습니다. 모델이 모델을 채점하는 자기참조가 되니까요.

 

결과는 확정된 구간의 동적/정적 정확도 1.00, 모름 비율 0.15(나쁜 footage 제외 시 0.07). 게이트(정확도 >= 0.90, 모름 <= 0.25) 통과입니다. 정직하게 적어두면 100%는 테스트 영상의 경계가 뚜렷했던 덕도 있습니다. 쉬운 건 확정하고 애매한 건 물러선다. 설계가 의도대로 작동했습니다.


5. 다섯번째 단계를 건너뛰다 - 완결된 경로부터

원래 설계에는 저작 모드가 두 개 있습니다.

  • 모드 A (편집만) - "인트로 몇 초, 다음에 노는 장면…" 같은 편집 요청만으로 자막 숏폼을 만듭니다. TTS가 필요 없습니다.
  • 모드 B (내레이션) - 대본을 쓰면 TTS 음성이 깔리고, 영상이 거기에 맞춰 편집됩니다. 다섯번째 단계(TTS)가 필요합니다.

그래서 일단 다섯번째 단계는 스킵하였습니다. 우선순위에서 뺐습니다. 이유는 단순합니다. 모드 A만으로도 처음부터 끝까지 완결된 제품 경로 하나가 성립하기 때문입니다. 부품을 하나 더 얹어 두 경로를 어설프게 구현하는 것보다 한 경로를 끝까지 뚫어 raw 폰 영상 -> 완성 숏폼을 증명하는 쪽이 먼저였습니다.


6. 자연어를 편집으로 - 번역가와 실행자

여섯번째 단계는 편집 실행입니다. 편집 요청 한 문단이 들어오면 LLM이 이를 블록 시퀀스로 번역합니다. "먼저 인트로, 그 다음 노는 장면 10초, 마지막에 하이파이브" — 블록마다 어떤 구간을(동적/정적), 몇 초로, 어떤 자막과 전환으로 쓸지가 담긴 편집 계획이 됩니다.

 

그리고 LLM의 일은 거기서 끝납니다. 계획을 받아 실제로 자르고 붙이고 줌하는 것은 전부 ffmpeg와 OpenCV, 즉 결정론 코드입니다. 같은 입력이면 같은 출력이 나옵니다.

 

실행 쪽에서 제일 고생한 건 줌이었습니다. 점차 확대되는 줌을 ffmpeg 내장 필터로 만들면 정수 반올림 때문에 화면이 부들부들 떨렸습니다. 세 번 갈아엎은 끝에 OpenCV로 float 보간 + 서브픽셀 렌더링을 직접 구현해 떨림을 잡았습니다. 한글 자막은 이 ffmpeg 빌드에 drawtext가 없어 PIL로 텍스트 이미지를 만들어 overlay로 태웠는데, 오히려 폰트와 스타일이 자유로워졌습니다. 출력은 분석용 저해상도가 아니라 원본 화질에서 다시 추출하고, 가로 / 세로가 섞인 폰 영상은 9:16 세로 + 블러 배경으로 통일했습니다.

 

편집 감각도 코드에 넣었습니다. 같은 장면을 잘게 쪼개 여기저기 흩뿌려진 영상이 나왔는데 어지러웠습니다. 소스당 한 컷, 길게. 이런 선호도 결정론 규칙이라 일관되게 지켜졌습니다.


7. 통합 테스트 - LLM의 자신감은 증거가 아니다

이제 전체를 처음부터 끝까지 돌립니다. 폰에서 갓 꺼낸 raw 영상 9개, 그리고 편집 요청 한 문단, "우리 토리를 소개합니다. 가만히 있는 인트로, 카페에서 노는 장면 10초, 산책, 비 오는 날, 밤에 달리기 합쳐서 10초, 하이파이브로 엔딩." 목표는 개발용 정답 라벨 없이 검출 결과만으로 도는 26.5초 세로 숏폼입니다.

 

당연히 한 번에 되지 않았습니다. 이 디버깅이 이번 편의 하이라이트입니다.

 

하이파이브가 사라졌습니다. 하이파이브는 앉아서 발을 드는 재주라, 오지선다(점프, 달리기, 걷기, 앉기, 엎드림)에는 자리가 없습니다. LLM은 가장 가까운 "앉기"로 분류했고, 동적 장면을 찾던 엔딩 블록은 빈손이 됐습니다. 수정은 라벨셋에 재주(묘기) 축을 추가하고, 편집 계획이 "묘기 장면"을 직접 고를 수 있게 했습니다. 이건 모델의 문제가 아니라 라벨셋 설계의 문제였습니다.

 

진짜 문제는 다음이었습니다. "가만히 있는 인트로"에 밤에 질주하는 영상이 들어왔습니다. 어둡고 흐릿한 밤 크롭을 LLM이 "정적"이라고 판단했습니다. 그것도 확신도 0.99로 판정한 겁니다. 처음엔 LLM과 모션 곡선의 판정을 확신도만큼 가중 투표시키는 절충안을 시도했다가 기각했습니다. LLM이 스스로 헷갈려할 때(확신 0.51)는 투표가 작동하지만, 확신하며 틀릴 때(0.99)는 투표가 오히려 오답에 힘을 실어줬습니다.

 

핵심 통찰은 이겁니다. LLM이 보는 건 정지된 크롭입니다. 흐릿하게 찍힌 강아지 사진 한 장으로는 뛰는 중인지 서 있는지 원리적으로 알 수 없습니다. 반면 모션 곡선(세번째 단계)은 카메라 움직임을 빼고 프레임 사이의 변화를 직접 잽니다. 그래서 절충 대신 역할을 완전히 분리했습니다.

축 담당 이유
동적/정적 (모션) 모션 곡선 전담 움직임의 직접 측정
동작 이름·재주 (의미) LLM 전담 선명한 한 장의 의미 판정

LLM은 밤 크롭을 여전히 오판합니다. 하지만 이제 그 오판이 결과를 흔들지 못합니다. 못 하는 일을 아예 맡기지 않았으니까요. 재테스트 결과는 인트로, 카페 두 영상, 밤 질주 포함, 하이파이브 엔딩까지 전부 정상. 서로 물고 물리던 문제들이 역할 분리 하나로 동시에 풀렸습니다.

 


맺음말

여기까지로 자연어 한 문단에서 완성 숏폼으로 파이프라인이 생겼습니다.

  • 동작 판별은 오지선다 확률로 묻되, 애매하면 물러서게 하고,
  • 내레이션은 완결 경로를 먼저 증명하기 위해 뒤로 미루고,
  • 편집 실행은 LLM이 번역한 계획을 결정론 코드가 재현 가능하게 렌더하고,
  • 통합 테스트에서 LLM이 못 하는 축(모션)을 측정에게 완전히 넘겼습니다.

지난 편의 교훈이 눈보다 정답지였다면 이번 편은 그 연장입니다. LLM의 확신이 보는 것과 같이 증거가 아닙니다. 측정할 수 있는 것은 측정으로 판정하고, LLM에게는 의미의 번역만 맡긴다. 이러한 설계가 흐릿한 밤에 질주하는 영상 하나에서 실제로 값을 했습니다.

 

남은 것은 뒤로 미룬 다섯번째 단계(TTS 내레이션), 수동으로 적고 있는 장면 태그의 자동화, 그리고 이 파이프라인을 제품으로 감싸는 일입니다. 다음 편에서 이어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

로컬 TTS(mlx-audio)로 영상 타임라인 컨트롤 - Narration First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com목소리를 얹는 순간,화면이 음성에 맞춰 늘어나고,타임라인은 음성이 컨트롤합니다. 우선 작업 결과

chessire.tistory.com

이전글

 

YOLO11 해상도 함정: 골든셋으로 검증 - Measurement First AI

좋은 모델이 좋은 파이프라인을 만드는 게 아닙니다.좋은 채점 기준이,모델을 갈아끼울 수 있게 해줍니다. "AI 영상 분석이면 일단 모델부터 돌려보는 거 아냐?" 틀린 말은 아닙니다. YOLO를 붙이

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

좋은 모델이 좋은 파이프라인을 만드는 게 아닙니다.
좋은 채점 기준이,
모델을 갈아끼울 수 있게 해줍니다.

 

"AI 영상 분석이면 일단 모델부터 돌려보는 거 아냐?"

 

틀린 말은 아닙니다. YOLO를 붙이면 박스가 강아지를 따라다니고, 뭔가 되는 것처럼 보입니다. 하지만 이 출발점은 정작 중요한 질문을 가립니다. 모델을 바꿨을 때 정말 좋아진 건지, 그냥 좋아 보이는 건지, 눈으로 데모 몇 개를 비교하는 방식으로는 영영 답할 수 없습니다.

 

"모델을 채점할 '정답지'부터 만들고 그 다음에 모델을 붙인다."

 

지금 만들고 있는 것은 영상 속에서 강아지 한 마리를 찾아내고, 계속 따라가고, 언제 움직이고 언제 쉬는지 이해하는 파이프라인입니다. 이번 편은 그 첫 레이어(검출, 재식별, 모션)를 위 순서로 세운 과정만 다룹니다. 결론부터 말하면, 네 번의 큰 결정 중 두 번은 정답지가 제 직관을 기각했습니다.

 

Model vs Golden-Set

0. 골든셋을 세우다 - 채점기 검증

골든셋(golden set)은 "정답이 확정된 표준 데이터셋"입니다. 한 번 만들어두면 어떤 모델을 넣든 어떤 파라미터를 바꾸든 항상 같은 표준으로 채점해 객관적으로 비교할 수 있습니다.

 

이 단계에서 만든 것이 정확히 이것입니다.

  • fail-case가 서로 다른 영상 5개를 골라 CVAT로 라벨링한 정답(GT)
  • 단계별 메트릭 라이브러리 (IDF1, MOTA, ID-switch, miss rate …)
  • 언제든 한 줄로 재채점하는 eval / report CLI
  • 그리고 채점기 자체를 검증하는 합성 단위테스트 30개

마지막 항목이 핵심입니다. 메트릭이 맞아야 모듈을 검증할 수 있습니다. 손으로 계산한 답과 채점기 출력이 일치하는지부터 확인했습니다. 자가 휘어 있으면, 그 뒤의 모든 측정이 무의미하니까요.

 

비유하면 골든셋은 운전면허 시험 코스입니다. 정답이 정해진 코스에서 합격하면 도로에서는 시험관 없이 달립니다. 골든셋은 "이 자동화를 믿어도 되는가?"를 증명하는 일회성 관문이지 운영 중에 반복하는 절차가 아닙니다.


1. 검출을 올리다 - 직관 검증

첫번째 단계는 YOLO + ByteTrack 기반의 검출, 추적입니다. 베이스라인(yolo11n)을 골든셋으로 채점해 보니 병목은 검출 누락이었습니다. 개선점은 두 군데 였습니다. 모델 체급을 올리거나(11n -> 11m), 입력 해상도를 올리거나(1536).

 

직관적으로는 둘 다 도움이 될 것 같습니다. 결과는 달랐습니다.

골든셋 채점 결과판정
체급 ↑ (11n→11m) miss rate 0.40→0.27, ID-switch 48→23, IDF1 0.44→0.54 채택
해상도 ↑ (1536) 오히려 악화 기각

해상도를 올렸는데 왜 나빠질까요? 640으로 학습된 YOLO는 1536 입력에서 크고 가까운 물체를 오히려 놓쳤습니다. 학습 분포와 추론 분포가 어긋나기 때문입니다. "해상도가 높을수록 잘 보인다"는 사람이 보는 눈의 이야기일 뿐, 모델의 이야기가 아니었습니다.

 

골든셋이 없었다면 어땠을까요. 데모 영상 몇 개를 보고 "1536이 더 선명한데?"라며 역효과 나는 설정을 채택했을 가능성이 큽니다. 눈으로 하는 비교는 확증 편향일 뿐입니다.

 

또 하나의 발견은 역할 분리입니다. 검출 누락은 이 단계에서 잡되 같은 강아지를 계속 같은 강아지로 보는 정체성 문제는 다음 단계의 몫으로 넘겼습니다. 한 모듈에서 모든 걸 해결하려 하지 않았습니다.


2. 그 강아지를 다시 알아보다 - 애매하면 유저에게

두번째 단계는 re-ID입니다. 영상에 여러 마리가 나올 때, 그 중 주인공 강아지 한 마리를 화면에서 사라졌다 돌아와도 계속 같은 강아지로 인식해야 합니다.

 

구성은 세 가지입니다.

  1. DINOv2 임베딩 - 강아지의 외형을 벡터로.
  2. 시간 상호배제 - 같은 프레임에 동시에 등장하면 서로 다른 강아지라는 논리 제약. 임베딩이 헷갈려도 물리적으로 확정되는 정보입니다.
  3. 유저 1탭 - 애매하면 유저에게 묻습니다.

세 번째가 설계의 갈림길입니다. 생김새가 비슷한 두 마리는 임베딩으로 완벽하게 구분할 수 없었습니다 (실측 오부착 1/12). 이걸 모델 개선으로 끝까지 밀어붙이는 대신, 애매한 케이스는 카드 한 장을 보여주고 탭 한 번으로 확정받는 구조를 택했습니다. 목표는 영상당 2탭 미만, 실측은 1.75탭이었고 통과였습니다.

 

"AI가 다 한다"보다, "AI가 90%를 하고 나머지는 유저가 탭 한 번으로 끝낸다"가 더 정직하고, 실제로 더 잘 작동했습니다.

 

이 단계에서도 골든셋이 직관을 한 번 더 기각했습니다. 레퍼런스 이미지를 여러 장 쓰는 멀티레퍼런스 매칭이 당연히 나을 거라 가정했는데 채점 결과 단순 평균 임베딩이 최고였습니다. 가설을 세우고, 채점하고, 데이터가 기각하면 접는다. 이 사이클이 두 단계 연속으로 "당연해 보이는 개선안"을 걸러냈습니다.

 

부수입도 있었습니다. 두번째 단계의 정답은 첫번째 단계의 골든셋에서 자동으로 도출됐습니다. 추가 라벨링 0. 정답지를 제대로 만들어두면 이렇게 재활용됩니다.

(추후에는 이미지 검증 프로세스도 추가해 유저의 탭도 제거하게 됩니다.)


3. 움직임을 숫자로 - 카메라 보정

세번째 단계는 모션 곡선입니다. 주인공 강아지의 박스 안에서 프레임 차이를 계산해, 동적/정적 구간을 나눕니다.

 

가장 재미있던 문제는 강아지가 아니라 카메라였습니다. 손으로 찍은 영상은 손떨림과 팬(pan)이 섞여 있어서, 가만히 앉아 있는 강아지도 화면상으로는 계속 움직인다고 판정했습니다. 해법은 배경 특징점으로 카메라의 움직임을 추정해 빼는 것. 카메라를 빼고 나면 강아지의 움직임만 남습니다.

 

검증 결과, 앉아 있는 강아지는 모션 값 0.8(정적), 노는 강아지는 13.8(동적), 앉아 있다가 화면 밖으로 나가는 전환도 잡았습니다.

 

다만 정직하게 적어두면, 세번째 단계는 정식 게이트가 아니라 눈검증으로 진행했습니다. 동적/정적 분리가 육안으로 명확해서 v1은 이 수준으로 갈음했습니다. 모든 단계에 같은 무게의 검증을 거는 건 오버엔지니어링입니다. 검증 강도는 리스크에 비례해서 정했습니다.

 

마지막으로 두번째 단계와 세번째 단계를 합쳤습니다. 모션을 전체 화면이 아니라 두번째 단계의 주인공으로 묶은 박스 기준으로 계산했습니다. 같은 영상이라도 주인공이 누구냐에 따라 답이 달라집니다. dog0을 주인공으로 잡으면 "정지", dog1로 잡으면 "화면 밖으로 나감." 박스가 그 강아지를 프레임 내내 따라간다는 증거입니다.


맺음말

여기까지가 첫 레이어입니다.

  • 골든셋 하나로 이후 모든 결정을 채점하고,
  • 검출에서 직관 대신 숫자로 레버를 고르고,
  • 재식별은 애매한 나머지를 유저의 탭 한 번에 맡기고,
  • 모션은 카메라를 빼고 강아지만 남겼습니다.

겉으로는 그저 모델 네 개의 나열처럼 보입니다. 하지만 이 배치에는 일관된 선택이 하나 깔려 있습니다. 모델은 갈아끼우는 부품이고, 골든셋은 그 부품을 채점하는 기준이라는 것. 부품은 계속 바뀝니다. 기준을 먼저 세운 쪽만 부품을 바꿀 수 있습니다.

 

다음 단계는 검출한 후, 추적된 강아지가 무엇을 하고 있는지(앉기, 뛰기, 놀기)를 판별하는 일입니다. 여기서부터 로컬 LLM이 등장합니다. 픽셀의 세계에서 의미의 세계로 넘어가는 경계인데, 그 경계선을 어디에 긋는지는 다음 편에서 풀어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

결정론적 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 아키텍처 - Local First AI

GitHub - chessire/shelter-puppyContribute to chessire/shelter-puppy development by creating an account on GitHub.github.com1. 강아지를 입양했다기술을 먼저 정하고 문제를 찾은 게 아닙니다.한 마리를 데려오고 나서야풀어

chessire.tistory.com

 

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

1. 강아지를 입양했다

기술을 먼저 정하고 문제를 찾은 게 아닙니다.
한 마리를 데려오고 나서야
풀어야 할 문제가 보였습니다.

 AI로 무언가를 만들어보려던 참이었습니다. 도구는 손에 있는데 무엇을 풀지가 없던 흔한 상태였습니다.

 

 최근에 강아지를 입양해왔습니다. 5월 초부터 마음을 정해두고, 아내가 매일같이 보호 글을 들여다보며 인연을 찾았습니다. 그렇게 5월 24일, 강릉의 한 보호소와 연계된 임시보호자님에게 있던 아기 강아지를 데려왔습니다.

 

 이 아이의 사정은 이랬습니다. 떠돌이 어미가 낳은 새끼들 중 하나였고, 새끼들만 구조되었고 어미는 끝내 구조되지 못했다고 들었습니다. 세 마리 중 둘은 먼저 가족을 찾았고, 가장 오래 떠나지 못하고 남아 있던 아이가 우리에게 왔습니다.

 

 데려오는 과정에서 예상하지 못한 걸 봤습니다. 임시보호자와 입양 희망자 사이에서 오가는 소통, 정보, 그 사이의 불편함이 분명히 존재했습니다. 사람 손만으로는 메우기 버거운 자리였습니다.

 

 그 순간 두 가지가 겹쳤습니다. 풀 문제를 찾던 나, 그리고 눈앞에 보인 진짜 불편함. 마침 AI를 만들고 싶었던 저는 그 틈을 AI로 메울 수 있겠다 싶었습니다. 그래서 결심했습니다. 이 틈을 내 능력으로 메워보자고요.

 

 이 글은 그 결심에서 출발한 기록입니다. 그리고 다음 이야기들은 "왜 로컬인지", "왜 이렇게 도구를 쪼갰는지"를 다뤄보고자 합니다.

 

귀여운 토리


2. 영상 - 왜 전부 LLM에 맡기지 않을까?

"AI한테 영상 던지고 '이 강아지 뭐 해?' 물어보면 되잖아."
이 방법은 가장 비싸고 가장 느리게 도착하는 방법입니다.

 

 보호소 강아지의 영상들을 가지고 입양 공고 콘텐츠로 만든다고 해봅시다. 멀티모달 LLM에 영상을 통째로 밀어 넣으면 끝날 것 같습니다. 하지만 이 출발점은 비용도 속도도 가장 안 좋은 형태로 시작됩니다.

 

 여기서 짚어야 할 게 있습니다. "AI"는 한 덩어리가 아니라 파이프라인입니다. 영상 한 편을 처리하는 데에도 성격이 다른 셋이 섞입니다.

  1. 고전 알고리즘 (OpenCV) - 학습이 없습니다. 수학과 규칙으로 돕니다. 빠르고, 싸고, 결과가 일정합니다.
  2. 학습된 전용 신경망 (YOLO) - AI지만 "검출" 한 가지에 특화돼 있습니다. 프레임마다 강아지의 박스와 위치를 찾습니다.
  3. 생성형 LLM (gemma) - 의미를 판단합니다. "이 강아지는 지금 사람을 반기는 중"이라는 해석. 강력하지만 느리고, 비싸고, 가끔 틀립니다.

 만약 강아지가 화면 어디 있는지 박스 좌표를 찾는 일까지 LLM에 시키면 어떻게 될까요? 느리고, 부정확하고, 비용만 듭니다. 그건 YOLO가 훨씬 잘하고 싸게 하는 일입니다.

 

 그래서 설계의 핵심은 이렇게 정리됩니다.

 

 잘 만든 설계는 무거운 생성형 LLM을 '꼭 필요한 한 스텝'에만 씁니다. 나머지는 싸고 빠른 도구로 분리합니다.

 

 실제 영상 한 편은 이렇게 흘러갑니다.

  • 프레임 자르기·인코딩·자막 입히기 -> ffmpeg (그냥 도구)
  • 강아지가 어디 있나(박스) -> YOLO (검출)
  • 그 강아지를 영상 내내 같은 ID로 유지 -> ByteTrack (추적)
  • 마지막에 딱 한 번, "이 장면을 입양 공고용으로 어떻게 설명할까"를 저작 -> gemma (의미·생성)
  • 대본을 나레이션 음성으로 입힌다 -> mlx-audio (TTS)

 작은 비영리에서 이건 취향이 아닌 비용 절감입니다. LLM 호출 한 번이 곧 비용이고 솔로 운영에서 느린 파이프라인은 곧 굴리지 못하는 파이프라인이 됩니다. 무거운 도구를 아껴 쓰는 설계가 비영리 사업을 지속 가능하게 만드는 조건이 될 수 있습니다.

 

 이 "계층 분리"는 영상에만 해당하는 이야기가 아닙니다. 사실 이 프로젝트 전체를 관통하는 질문이고 다음 DB 이야기도 똑같은 자리에서 출발합니다.


3. DB - 왜 Postgres와 벡터DB를 같이 둘까?

하나는 "정확히 일치하는 것"을 찾고
다른 하나는 "비슷한 의미"를 찾습니다.

 

 DB를 두 개나 쓴다고 하면 과해 보입니다. "그냥 데이터베이스 하나면 되지 않나?" 하지만 이건 중복이 아니라 2번에서 본 것과 똑같은 역할 분리입니다. 두 DB는 애초에 답하는 질문이 다릅니다.

  1. PostgreSQL
    "정확히 일치하는 것"을 찾는다. 회원, 강아지 공고, 입양 신청처럼 행과 열로 떨어지는 데이터입니다. "보호 중이고, 중성화 완료이고, 3살 이하인 강아지" — 조건이 명확하고, 답도 딱 떨어집니다. 이러한 과정은 관계형 DB가 최적입니다.
  2. 벡터DB(chromadb)
    "비슷한 의미"를 찾는다. "사람을 잘 따르고 아파트에서도 키울 만한 순한 아이" 같은 질문은 다릅니다. 어느 칸에도 그렇게 적혀 있지 않습니다. 이건 의미가 가까운 데이터를 찾는 일이고 텍스트를 숫자 벡터로 바꿔(임베딩) 거리로 비교해야 됩니다. 관계형 DB의 WHERE 조건으로는 풀기 어려운 문제입니다.

 그래서 둘은 대체재가 아닙니다. 하나는 사실을 정확히 찾고, 하나는 의미를 비슷하게 찾습니다. 응대봇이 "이 조건에 맞는 강아지"(Postgres)와 "이 느낌의 강아지"(벡터DB)를 동시에 답하려면 둘 다 필요합니다.

 

 이게 2번의 영상 이야기와 똑같은 자리인 이유가 여기 있습니다. "만능 도구 하나로 다 해결한다"가 아니라 "질문의 성격에 맞는 도구를 선택한다". LLM에 전부 안 시키듯, DB도 한 종류에 전부 안 맡깁니다.

 

 다만 솔직히 둘 다 끌고 가는 건 비용입니다. 운영할 DB가 둘이 되니까요. 그래서 지금은 명확히 분리해 두되, 규모가 커지면 한쪽으로 합치는 길도 열어둡니다. PostgreSQL에 벡터 검색 확장(pgvector)을 얹어 한 DB에서 사실과 의미를 같이 다루는 선택지입니다. 초기에는 역할을 선명하게 분리하고, 규모가 커지면 운영 부담을 줄이는 쪽으로 통합하려 합니다.


4. 응대봇 - 왜 로컬이며, 왜 이 모델일까?

똑똑한 모델 하나를 고르는 문제가 아닙니다.
"무엇을 사용해야 하는가"와
"어떤 일에 어떤 모델을 쓰는가"입니다.

 

 입양 문의에 답하는 봇을 만든다고 해봅시다. 가장 쉬운 길은 외부 API(예: GPT)를 부르는 겁니다. 그런데 그 길을 택하는 순간, 1편 첫머리에서 던졌던 질문이 그대로 돌아옵니다. 무엇을 로컬에 두고 무엇을 밖에 맡길 것인가.

 

 왜 로컬(Ollama)인가. 세 가지 이유입니다.

  • 데이터. 강아지 정보, 보호 기록, 입양 신청자 문의가 매 호출마다 외부 서버로 나갑니다. 명분 사업에서 보호 동물과 신청자의 데이터를 남의 서버에 흘리는 것은 가볍지 않습니다. 로컬은 그 정보들이 내 기기 밖으로 안 흘러나갑니다.
  • 비용. 외부 API는 호출당 과금입니다. 응대봇은 많이 부를수록 가치가 큰데 많이 부를수록 그대로 토큰 값이 됩니다. 로컬 모델은 전기값 외에 호출당 비용이 0입니다.
  • 통제. 모델이 갑자기 바뀌거나 가격이 오르거나 정책으로 막히는 일에서 자유롭습니다. 예측 가능함이 곧 안정성입니다.

왜 이 모델 조합인가. 여기서도 2번의 "계층 분리"가 그대로 반복됩니다. 응대봇은 모델 하나가 아니라 역할이 다른 둘로 작동합니다.

  • bge-m3 (임베딩) — 대화하지 않습니다. 텍스트를 숫자 벡터로 바꾸는 일만 합니다. "이 강아지 설명"과 "사용자 질문"을 같은 좌표 공간에 올려 의미가 가까운지를 잴 수 있게 만듭니다. 한국어 성능이 좋아 강아지 데이터에 적합합니다.
  • gemma (생성) — 답을 만듭니다. 검색해 온 데이터를 읽고, 영상을 해석하며, 사람에게 건넬 문장으로 풀어냅니다.

이 둘이 3번의 두 DB와 맞물려 하나의 흐름이 됩니다. 이게 RAG입니다.

  1. 사용자가 묻습니다. "사람 잘 따르고 아파트에서도 키울 만한 아이 있어요?"
  2. bge-m3가 질문을 벡터로 바꾸고 벡터DB에서 의미가 가까운 강아지를 꺼냅니다. (동시에 Postgres로 "보호중, 입양가능" 같은 사실 조건도 거릅니다.)
  3. 그렇게 추려진 진짜 데이터를 gemma에게 건네며 답을 만들게 합니다.

 RAG의 핵심은 gemma가 아는 척 지어내지 않게 만드는 겁니다. 모델의 메모리가 아니라 우리 DB의 실제 강아지 데이터를 근거로 답하니까요. 있지도 않은 강아지를 추천하는 환각은 단순 오류가 아니라 신뢰 문제입니다. RAG는 그 위험을 구조로 막습니다.

 

모델 사양의 선택. gemma는 26B 규모지만 MoE 구조라 토큰당 약 4B만 활성화됩니다. 전체는 메모리에 올라가되 속도는 작은 모델처럼 나오는 절충입니다. 거기에 4비트 양자화(q4_K_M)를 더해 메모리를 줄여 워크스테이션이 아닌 한 대의 맥 안에 들어오게 맞췄습니다. "가장 큰 모델"이 아니라 "이 기기에서 굴러가는 충분히 똑똑한 모델"을 고른 겁니다.

 

 결국 이 섹션도 같은 자리로 돌아옵니다. 강력한 모델 하나로 다 하는 게 아니라 일을 쪼개 각자에게 맞는 도구를 붙이고, 그 전부를 로컬에 두는 것입니다.


5. 프론트엔드와 백엔드 - 왜 Django며 왜 Vite에서 Remix일까?

지금 필요한 것과 나중에 필요할 것은 다릅니다.
좋은 선택은 지금을 빠르게 해결하고
나중의 선택지도 닫지 않습니다.

 

 플랫폼엔 두 축이 있습니다. 데이터를 다루는 백엔드와 화면을 그리는 프론트엔드입니다. 둘 다 선택지가 넘치는데 기준은 하나였습니다. 지금 단계에서 가장 적은 공수로 가장 멀리 가는 길.

왜 FastAPI가 아니라 Django인가.

 파이썬 백엔드라면 가벼운 FastAPI도 강력한 후보입니다. 그런데 이 프로젝트는 CRUD가 많은 플랫폼으로 예상됩니다. 강아지 공고 등록과 수정, 임보자 관리, 입양 신청 처리에 데이터를 넣고 빼는 화면이 끝없이 필요합니다.

 

 Django는 여기서 결정적인 걸 공짜로 줍니다.

  • Admin 패널 내장. 공고·신청·회원 관리 화면을 자동으로 생성해줍니다. 프론트를 한 줄도 안 짜도 데이터를 넣고 바로 운영·테스트할 수 있습니다. 솔로 운영에서 이건 몇 주를 아끼는 차이입니다.
  • ORM과 마이그레이션, 인증 기본 제공. FastAPI라면 직접 골라 붙여야 할 조각들이 처음부터 한 묶음으로 옵니다.

 무엇보다 4번까지 만든 AI 코드(Ollama, RAG, YOLO)가 전부 파이썬입니다. Django도 파이썬이라 같은 백엔드 안에서 AI와 플랫폼 로직이 함께 돕니다. 언어가 갈리지 않는다는 것은 곧 개발 속도를 빠르게 합니다.

 

 FastAPI의 가벼움이 빛나는 자리도 분명 있습니다. 다만 CRUD가 많은 플랫폼, 1인 운영이라는 이 조건에선 덜어주는 일의 양이 Django 쪽이 압도적이었습니다.

왜 한 번에 안 가고 단계적으로 Vite에서 Remix인가.

프론트는 처음부터 정답을 고르지 않았습니다. 단계마다 필요가 다르기 때문입니다.

  • MVP 단계 = Vite + React. 지금 필요한 건 빠른 출시입니다. Vite는 가볍고 빠른 개발 환경으로 React 화면을 즉시 띄웁니다. 단점도 분명합니다. 순수 SPA라 SSR이 없어 검색 노출(SEO)과 링크 미리보기가 약합니다. MVP에선 감수할 수 있는 약점입니다. 아직 공개 공고를 검색에 태울 단계가 아니니까요.
  • 프로덕트 단계 = Remix(SSR). 공개 입양 공고가 검색에 잡히고, 카톡·인스타에 링크를 붙였을 때 미리보기가 떠야 하는 순간이 옵니다. 그때 SSR이 필요해지고, Remix로 넘어갑니다. Django API 백엔드와 역할이 겹치지 않아 조합도 깔끔합니다.

 그래서 핵심은 단계 전략입니다. 지금은 발견성(SEO)을 포기하고 출시 속도를 빠르게 합니다. 나중에 발견성이 필요해지면 그때 SSR로 갈아탑니다. 처음부터 Remix로 시작해 무겁게 가는 대신 지금의 문제를 가장 빨리 풀면서 나중의 선택지도 닫지 않습니다.


맺음말

 다섯 개의 질문은 사실 하나인 듯합니다. 영상도, DB도, 응대봇도, 프론트엔드도, 백엔드도 전부 같은 자리로 수렴했습니다. 강력한 도구 하나로 다 풀지 않는다. 일을 쪼개 각자에게 맞는 도구를 붙인다. 그리고 가능한 많은 것을 로컬에 둔다는 것입니다.

 

 이건 취향이 아니었습니다. 개인이 굴리는 작은 비영리에서 무거운 것을 아껴 쓰는 설계만이 지속 가능하니까요. 가장 비싼 도구를 가장 적게 쓰는 법을 아는 것, 그것이 노하우가 됩니다.

 

도구는 이렇게 깔았고(1편), 왜 그렇게 골랐는지도 풀었습니다(2편). 다음은 이걸로 실제로 무엇을 만드는가입니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

YOLO11 해상도 함정: 골든셋으로 검증 - Measurement First AI

좋은 모델이 좋은 파이프라인을 만드는 게 아닙니다.좋은 채점 기준이,모델을 갈아끼울 수 있게 해줍니다. "AI 영상 분석이면 일단 모델부터 돌려보는 거 아냐?" 틀린 말은 아닙니다. YOLO를 붙이

chessire.tistory.com

이전글

 

맥 로컬 LLM 구축 - Local First AI

AI는 한 덩어리가 아닙니다.역할이 다른 도구들을,한 대의 맥 위에 포개는 일입니다."유기견 입양에 AI를 붙인다"고 하면, 보통 이렇게 떠올립니다."그냥 ChatGPT API 부르면 되는 거 아냐?" 틀린 말은

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com

 

 

GitHub - chessire/shelter-puppy

Contribute to chessire/shelter-puppy development by creating an account on GitHub.

github.com

AI는 한 덩어리가 아닙니다.
역할이 다른 도구들을,
한 대의 맥 위에 포개는 일입니다.

"유기견 입양에 AI를 붙인다"고 하면, 보통 이렇게 떠올립니다.
"그냥 ChatGPT API 부르면 되는 거 아냐?"

 

틀린 말은 아닙니다. 하지만 이 출발점은 정작 중요한 걸 가립니다. 강아지 정보와 영상 같은 데이터가 매 호출마다 외부로 나가고, 호출량만큼 비용이 붙고, 모델·데이터의 통제권은 내 손을 떠납니다. 작은 비영리 명분 사업에서 이 셋은 가볍지 않은 비용입니다.

그래서 질문을 바꿨습니다.

 

"무엇을 내 손 안(로컬)에 두고, 무엇만 밖에 맡길 것인가?"

 

이 글은 그 경계를, 노트북 한 대 위에 세우는 과정입니다. 선택의 이유는 다음 편으로 미루고, 이번 편은 "무엇을 어떻게 깔았는가"만 다룹니다.


1. AI 플랫폼을 설계하다 - 한 대의 맥에 무엇이 올라가나

세울 것은 세 개의 계층입니다.

  1. Python 계층 — AI 코드(LLM·RAG·영상)와 백엔드(Django)가 같은 언어로 도는 곳.
  2. 데이터 계층 — 표 형태 데이터를 담는 PostgreSQL.
  3. Node 계층 — 화면(React)을 띄우는 프론트엔드 런타임.

핵심은, 이질적으로 보이는 이 조각들이 대부분 파이썬 하나로 묶인다는 점입니다. 강아지 공고 관리도, 응대봇도, 영상 분석도 전부 파이썬 위에서 돕니다. 그래서 환경설정의 무게중심도 자연히 파이썬에 실립니다.

AI Pipeline


2. Python 계층 - AI와 백엔드가 같은 언어로 돈다

맥 기본 파이썬은 버전이 낡아 개발용으로는 권장되지 않습니다. brew로 최신을 깔고, 프로젝트 전용 가상환경(venv)을 만듭니다.

brew install python
python3 --version          # 3.12 / 3.13 나오면 OK
which python3              # /opt/homebrew/bin/python3 이면 brew 거 잡힌 것

# 프로젝트 전용 가상환경
python3 -m venv ~/ai/venv
source ~/ai/venv/bin/activate   # 프롬프트에 (venv) 뜨면 활성
pip install -U pip

그 위에 세 묶음을 올립니다. LLM·RAG 스택, 영상 파이프라인, 그리고 백엔드(Django).

# LLM·RAG — 로컬 모델 클라이언트 + 벡터DB
pip install ollama chromadb

# 영상 파이프라인
brew install ffmpeg                            # 영상 자르기·인코딩·자막 입히기
pip install opencv-python                      # 프레임 추출
pip install ultralytics                        # YOLO(검출) + ByteTrack(추적)
pip install mlx-audio soundfile sounddevice    # TTS — 대본을 나레이션 음성으로 (애플 실리콘 최적)

# 백엔드 — Django + REST + DB 드라이버
pip install django djangorestframework django-cors-headers psycopg2-binary
django-admin startproject config .        # 현재 폴더에 프로젝트 생성
python manage.py runserver                # http://127.0.0.1:8000 뜨면 OK

로컬 LLM 본체인 Ollama는 코드 라이브러리가 아니라 독립 서버라 brew로 깝니다. 위에서 깐 ollama(pip)는 그 서버를 파이썬에서 부르는 리모컨이고, 지금 까는 본체가 TV입니다.

brew install ollama
brew services start ollama        # 재부팅해도 자동으로 뜨는 백그라운드 서비스

ollama pull bge-m3                 # 임베딩 모델 (작고 빠름, 첫 성공)
ollama pull gemma4:26b-a4b-it-q4_K_M   # 멀티모달 본체 (비전+텍스트)

ollama run gemma4:26b-a4b-it-q4_K_M "안녕, 한국어 돼?"   # 응답 나오면 /bye

이 한 덩어리만으로 "텍스트를 벡터로 바꾸고(bge-m3), 의미가 비슷한 데이터를 꺼내(chromadb), 그걸 보고 답하는(gemma)" 응대봇의 뼈대가 한 대의 맥 안에 들어옵니다.


3. PostgreSQL - 구조화 데이터의 자리

회원, 강아지 공고, 입양 신청처럼 행과 열로 떨어지는 데이터는 관계형 DB에 둡니다.

brew install postgresql
brew services start postgresql    # ollama처럼 백그라운드 서비스로

조금 전 깐 chromadb와는 역할이 다릅니다. postgres는 구조화 데이터, chromadb는 의미 검색용 벡터입니다. 대체재가 아니라 함께 씁니다. (왜 둘 다 필요한지는 다음 편에서.)


4. Node 계층 - 화면을 띄우다

프론트엔드(React)를 돌릴 런타임입니다.

brew install node          # node + npm 같이 들어옴
node --version             # 20 이상

npm create vite@latest     # React 선택 (MVP용 SPA)

MVP는 가볍고 빠른 Vite + React로 갑니다. 검색 노출(SEO)이 필요한 프로덕트 단계에서 SSR 프레임워크로 넘어갈 여지를 남겨두지만, 그 갈림길의 이야기도 다음 편 몫입니다. (참고로 create-react-app은 폐기되어 쓰지 않습니다.)


맺음말

여기까지가 골격입니다.

  • Python 하나에 AI와 백엔드를 포개고,
  • Postgres로 구조화 데이터를 받치고,
  • Node로 화면을 띄운다.

겉으로는 그저 brew install과 pip install의 나열처럼 보입니다. 하지만 이 배치에는 일관된 선택이 하나 깔려 있습니다. 가능한 많은 것을 외부 API가 아니라 내 손 안에 두는 것. 왜 굳이 로컬인지, 왜 FastAPI가 아니라 Django인지, 그리고 영상 분석을 왜 전부 LLM에 맡기지 않는지 — 이 환경을 이렇게 세운 이유는 다음 편에서 풀어가겠습니다.


이 글에서 다룬 고정 함수 분류층은 ProjectDavid의 일부입니다.

전체를 바닥부터 만드는 과정은 강의 3부작으로 정리하고 있고, 10월에 오픈 예정입니다.

→ 오픈 알림 받기


다음글

 

로컬 LLM 아키텍처 - Local First AI

1. 강아지를 입양했다기술을 먼저 정하고 문제를 찾은 게 아닙니다.한 마리를 데려오고 나서야풀어야 할 문제가 보였습니다. AI로 무언가를 만들어보려던 참이었습니다. 도구는 손에 있는데 무

chessire.tistory.com

관련글

 

오픈소스 LLM vs 빅테크 LLM API: AI 사업의 비용과 리스크

모델은 커모디티가 된다.마진은 판단에 남는다. 2025년 2월, OpenAI는 당시 가장 비싼 모델 GPT-4.5를 출시했습니다. 백만 토큰당 입력 75 달러, 출력 150 달러. 그리고 다섯 달이 지나기 전, API에서 제거

chessire.tistory.com