Mobidoo

모범 사례

이 문서는 영문 원고를 AI로 번역한 내용이라 표현이 어색할 수 있습니다.

모비두 백엔드 연동용 사례 하나입니다. create → receipt → 클립 재시도가 어떻게 이어질 수 있는지 보여주는 예이며, 계약서가 아닙니다.

팀 워크스페이스 API 키: 팀 Format에는 팀 키가 필요합니다.

API 표면: 라이브 커머스 API.
input과 typed receipt 깊이: Format 호출하기 — 요청 본문, 구조화 출력.

1. Create 예시

instruction + 편한 형태의 input + 파트너 output_schema(typed receipt — 완성본과 씬별 클립). 탭은 같은 body의 cURL / TypeScript / JavaScript / Python입니다.

Example create — AHC 30 s live cut

POST /v1/formats/{handle}/{slug}/runs

create body에서 반드시 맞춰야 하는 것

이 body에서 실제로 하중을 받는 것은 네 가지입니다. 위 탭이 보내는 body를 그대로 펼친 것입니다 — 이 호출은 34.72초 컷, 씬 7개, $7.02로 돌아왔습니다. 긴 문자열만 길이 때문에 줄였습니다:

output_schema는 객체 전체입니다{ name, strict, schema }이고 JSON Schema를 그 안에 그대로 실어야 합니다. 등록된 이름만 보내는 것으로는 부족합니다. primary_output_key와 함께 보내고, 적용됐는지 확인하세요. create 응답이 output_schema.source를 돌려주는데, 보낸 스키마가 적용됐다면 request_override입니다.

generation_spend_cap_usd는 0이 아니어야 하고 예상치보다 넉넉해야 합니다. 0이거나 빠뜨린 것은 "무제한"이 아닙니다. 위 실행의 cap은 $20이었고 실제 청구는 $7.02였습니다.

input은 평평한 snake_case입니다product_url, product_name, on_card_name, host_image_url, vo_language, price, highlights, script.segments. 래퍼 키 아래로 감싸지도, camelCase로 바꾸지도 마세요.

파트너 쪽 대문자 태그가 그대로 동작합니다. INTRO / MID / FIN을 보내기 전에 변환할 필요가 없습니다. receipt는 acttags에서 Intro / Mid / Fin으로 정규화해 돌려줍니다. 비교는 보낸 표기가 아니라 receipt의 표기를 기준으로 하세요.

인식되는 input 키가 없는 요구사항은 전부 instruction으로 갑니다. 위 실행도 "segments를 그대로 사용", "대본을 늘리지 말 것", "무BGM·무자막·9:16"을 거기에 넣었습니다.

길이를 정하는 것은 대본 분량입니다

길이 파라미터는 없습니다. 완성본 길이는 대본을 읽는 데 걸리는 시간이고, 따라서 길이를 조절하는 유일한 손잡이는 script.segments에 넣는 글자 수입니다. 이 자료에서 한국어 기준으로 유지된 비율은 초당 약 8.2자였습니다 — 283자가 34.72초 컷으로 돌아왔습니다. 대본을 쓰기 전에 분량을 가늠하는 용도로 쓰세요. SLA가 아니라 기획용 숫자입니다.

run이 돌아옵니다:

terminal까지 기다리세요 — status_url / result_url 폴링이나 run 웹훅(format.run.terminal)입니다. 웹훅은 씬마다가 아니라 턴마다 한 번입니다.

받은 뒤에는 운영자에게 보여주기 전에 receipt가 보낸 대본을 실제로 담고 있는지 확인하세요. middle: []closing: []도 스키마상 유효하기 때문에, 대본 일부만 읽은 실행이 오류 없이 completed로 돌아올 수 있습니다. 돌아온 행의 script 텍스트를 보낸 segments와 대조하는 것이 값싼 방어선입니다.

2. Receipt 예시

위 스니펫은 값을 채워 쓰는 템플릿입니다. 여기서부터는 전부 실제 프로덕션 실행을 그대로 옮긴 것입니다 — Format v5의 arun_42ed6a48b3d44092, 한국어 선크림 컷, 씬 11개, 73.36초, $14.96. 그래서 아래 media.sume.com URL은 지금 열리는 실제 파일입니다. 길이 때문에 씬 11개 중 3개만 남겼습니다(sc_1sc_3, sc_5, sc_6, sc_8sc_10 생략). 스키마 세부: 구조화 출력.

“이 클립 다시 만들기”를 붙이려면 이 턴에서 네 가지를 저장해 두세요:

저장어디서쓰임
run.idcreate 응답재시도의 previous_run_id가 됩니다
thread_idcreate 응답한 제작물의 모든 턴을 묶습니다 — 읽기 전용, 요청 body에는 절대 넣지 않습니다
scene.idreceipt의 각 행재시도할 클립을 지목합니다 — 제작물 동안 고정이므로 배열 인덱스로 잡지 마세요
scene.statusreceipt의 각 행stand-in / failed이 재시도 버튼을 켜는 신호입니다

scene.id불투명한 문자열로 저장하세요. 이 실행은 sc_0sc_10을 돌려줬지만, 이전 Format 버전의 제작물은 다른 모양을 돌려줬습니다. receipt가 주는 값을 그대로 저장하고, 파싱하거나 직접 만들어 쓰지 마세요.

이 실행은 모든 씬이 succeeded로 돌아왔습니다. 그래서 아래 재시도는 실패 복구가 아니라 운영자가 다른 테이크를 요청한 경우입니다. 다른 트리거는 재시도 대상으로 돌아온 행입니다 — 대시보드는 아래 둘 중 하나에서 “이 클립 다시 만들기”를 켭니다:

status는 설명용 예시입니다 — 이 제작물은 11/11 succeeded였습니다.

3. Retry 예시

대시보드 루프: 운영자가 클립을 고름 → 백엔드는 이미 previous_run_id(create run id)와 scene_id(receipt 행)를 갖고 있음. 같은 Format 엔드포인트에 input.scene_id + **create와 같은 output_schema**를 실어 보냅니다. typed receipt 모양이 그대로 유지됩니다.

thread_id는 body에 넣지 마세요. 응답 전용 필드이며, 보내면 unknown_parameter로 거절됩니다. 대화를 잇는 키는 previous_run_id 하나뿐입니다.

남는 것은 instruction이고, 무엇이 돌아올지를 결정하는 필드가 바로 이것입니다.

씬별 구체 피드백을 넣으세요

운영자가 테이크가 마음에 들지 않아 “이 클립 다시 만들기”를 누른 경우라면, 그 이유를 물어보고 그 답을 instruction에 실어 보내세요 — 어느 씬인지, 무엇이 잘못됐는지, 어떻게 바꿔야 하는지. 실제 피드백을 담은 재시도가 권장 형태이고, 아래의 고정 템플릿은 기본값이 아니라 대체 수단입니다.

instruction에 넣을 네 부분입니다. 제작물이 돌아가는 언어 그대로 쓰면 됩니다:

  1. 씬 — scene_id와 role을 산문으로도 한 번 더 적어 지목을 분명히
  2. 관찰된 결함 — 운영자가 이 테이크에서 실제로 본 것
  3. 구체적인 수정 — 형용사가 아니라 지시로, 대신 무엇을 할지
  4. 가드레일 — VO 스파인·대본 텍스트·인물·의상·세트 유지, 세로 9:16, 무BGM, 무자막

아래 두 instruction 예시는 같은 레시피의 다른 제작물에서 그대로 가져온 것입니다. 위 receipt들과는 다른 스레드라 previous_run_id가 §2와 다릅니다. receipt가 아니라 요청 body로 보세요.

첫 번째는 빈 공간에 제품만 떠 있는 패키지샷으로 돌아온 broll 씬입니다. 배너 규칙을 눈여겨보세요 — 화면 안 카드/배너 타이포는 프레임 높이 상단 40% 안에서 끝나야 합니다. 하단은 라이브 UI가 덮기 때문입니다.

Example retry — scene note in the instruction

POST /v1/formats/{handle}/{slug}/runs

두 번째 talk 씬도 같은 방식입니다 — 같은 네 부분, 다른 결함. 인사 구간에서 호스트의 시선이 떨어지고 눈이 감기는 프레임이 있었던 경우입니다:

왜 이렇게까지 하냐면: 고정 템플릿으로 재시도한 talk 씬이 운영자가 거절한 바로 그 테이크와 바이트 단위로 동일하게 돌아온 적이 있고, 같은 씬을 위와 같은 피드백과 함께 재시도했을 때는 다시 렌더링되어 시선이 고쳐졌습니다. API 보장이 아니라 연동 가이드로 받아들이세요 — 고정 템플릿 재시도가 항상 동일하게 돌아온다는 보장도, 구체 피드백이 항상 바뀐다는 보장도 없습니다. 통제할 수 있는 것은 instruction이 얼마나 많이 알려주는가입니다. §5에서 이런 재시도를 끝까지 측정했습니다 — 가격 카드에 지시한 여덟 가지 수정이 전부 반영됐고, 나머지 여섯 클립은 그대로였습니다.

기본 템플릿은 기계적 재시도용입니다

stand-in / failed로 돌아온 행에는 설명할 것이 없습니다 — 운영자가 거절할 테이크 자체를 본 적이 없으니까요. 이 경우에는 고정 템플릿이 맞고, 단순히 “한 번 더 돌려”인 경우에도 정직한 기본값입니다:

Example retry — selected scene on the same thread

POST /v1/formats/{handle}/{slug}/runs

새 run, 같은 thread:

여러 클립을 한 번에 재시도하는 것도 같은 호출입니다 — scene_ids와 복수형 instruction만 바뀝니다:

클립 재시도는 씬이 어떻게 보이는지를 다시 렌더링합니다. 무엇을 말하는지 — 문구, 가격 낭독, 길이 — 를 바꾸는 것은 새 씬 id가 붙는 새 제작입니다. 클립 재시도로 바꿀 수 있는 것과 없는 것을 보세요.

버튼을 붙이기 전에 알아둘 두 가지

한 스레드의 재시도는 직렬로. 같은 previous_run_id로 재시도를 두 개 동시에 보내지 마세요 — 제작 스레드 하나에 진행 중인 턴 하나입니다. 첫 재시도가 terminal에 도달할 때까지 두 번째는 큐에 넣어 두세요. 한 턴에서 여러 씬을 고치는 용도로는 scene_ids가 있습니다.

previous_run_id는 스레드를 고르는 것이지 스냅샷을 고르는 것이 아닙니다. 예전 run id를 넣어도 그 시점 receipt에서 분기되지 않습니다. 새 receipt는 스레드의 최신 씬 상태로 조립되므로, 앞선 턴이 이미 바꾼 씬은 바뀐 채로 남습니다. 재시도를 비교할 때는 create가 아니라 직전 턴의 receipt와 비교하세요.

4. Retry receipt 예시

다시 전체 receipt입니다 — opening / middle / closing 목록 전체이며, 패치도 변경분도 아닙니다. sc_7만 새 미디어 URL을 갖고, 나머지 씬은 갖고 있던 URL을 그대로 유지하며 full_video가 다시 조립됩니다. 눈으로 비교할 수 있게 위와 같은 세 행을 실었습니다:

두 receipt가 실제로 증명한 것:

Create arun_42ed6a48b3d44092Retry arun_808e4eb3e6c04cf7
thread_idthr_96012fce-…동일
돌아온 씬 행 수1111 — 패치가 아니라 전체 목록
나머지 씬 URL11개 중 10개가 바이트 단위로 동일
sc_7 URLartf_nlQMSVkb…새 URL — artf_Hy18xtqP…
sc_7 길이17.76초17.76초 — VO 스파인 안 움직임
full_video73.360초, 12,049,030 B재조립, 73.360초, 12,080,322 B
청구액$14.959638$0.742530 — 약 20분의 1
소요 시간11분 08초2분 53초

재시도 뒤에도 이전 sc_7 URL은 여전히 200으로 열립니다. 이전 턴의 아티팩트는 변경되지 않으므로, 이미 저장해 둔 create receipt는 계속 유효합니다. 두 테이크를 모두 보관하고 운영자가 고르게 해도 됩니다.

다시 만든 클립은 재인코딩이 아니라 새 생성입니다. 프레임 안의 생성물은 테이크마다 달라질 수 있습니다. 위 사례에서도 구조화된 오버레이 사실(가격, 할인 표기, SPF 등급, 구성)은 정확히 유지됐지만, 제품 아트에 렌더링된 장식용 패키지 문구는 표현이 바뀌었습니다. 운영자에게 “한 군데만 고쳐진 똑같은 프레임”을 약속하지 마세요. 약속할 수 있는 것은 다른 테이크입니다.

5. 더 짧은 한 쌍, 측정값

§2–§4는 73초, 씬 11개짜리 제작물입니다. §1에 나온 34.72초 컷에서 같은 루프를 한 번 더 볼 만합니다. 한 씬만 고치는 재시도가 전체를 다시 만드는 것에 비해 얼마나 싼지 숫자로 나오기 때문입니다:

Create arun_206837aa88d34c8d지시형 retry arun_0c315b373e24433a
threadthr_45c64858-…동일
previous_run_idnullarun_206837aa88d34c8d
보낸 대본한국어 283자, INTRO / MID / FIN
결과34.72초, 씬 7개 전부 succeeded34.72초, 씬 7개 중 하나만 이동
청구액$7.0221$0.7361
소요 시간10분 08초2분 24초

retry body는 §3의 모양 그대로이고 그 이상은 없습니다 — previous_run_id, input: { "scene_id": "sc_5" }, create와 동일한 output_schema, 그리고 씬·결함·수정을 지목하는 instruction. sc_5는 클로징 가격 카드로, 제품명과 가격만 얹힌 under_banner 씬이었습니다. 줄여서 옮기면:

고칠 씬: sc_5 (under_banner / Fin, 클로징 가격 카드, 9.44초).

지금 무엇이 잘못됐는지: 카드에 제품명과 가격만 있고, 이 상품의 핵심 소구점인 자외선 차단 지수(SPF50+ PA++++)가 빠져 있습니다. … "30% 할인"이 카드에서 가장 크게 잡혀 있고, 정작 소비자가 결제하는 금액인 23,030원이 그보다 작게 보입니다. … 제품 이미지 위쪽에 빈 여백이 크게 남아 카드 상단이 비어 보입니다.

어떻게 바꿀지: 제품명 바로 아래에 "SPF50+ PA++++" 한 줄을 배지 형태로 추가해 주세요. … 판매가 23,030원을 카드에서 가장 큰 요소로 만들어 주세요. "30% 할인"은 그보다 작게, 보조 배지로 내려 주세요. 정가 32,900원은 취소선을 유지해 주세요. … 배경 톤, 카드 색감, 제품 사진 구도(박스 1개 + 튜브 2개)는 지금 그대로 두세요.

형용사가 아니라 편집 지시로 된 요구 여덟 개, 그리고 그 뒤에 바꾸지 말 것 목록. 여덟 개가 모두 반영됐고, sc_5 밖은 아무것도 움직이지 않았습니다:

변경
sc_0sc_4, sc_6없음 — 여섯 개 모두 아티팩트 URL 동일
sc_5있음 — artf_psot5zRM…/images.pngartf_GWQTB6n2…/timeline.mp4
full_video있음 — 재조립, 34,720 ms 그대로, 바이트는 다름

full_video가 아니라 씬별 URL을 비교하세요. full_video는 턴마다 다시 조립되므로 클립이 실제로 다시 렌더링됐는지와 무관하게 URL이 바뀝니다. 그것만으로는 아무것도 증명하지 못합니다. 신호는 씬별 URL이고, talk 씬은 바이트 해시까지 비교하세요 — talk 재시도가 새 URL에 바이트 단위로 동일한 미디어를 돌려준 사례가 있습니다.

scene.video가 항상 영상인 것은 아닙니다. create receipt에서 sc_5.video는 PNG 스틸이었습니다 — "type": "image", "content_type": "image/png"이고 width·height·duration_ms가 전부 null, 길이는 행의 duration_seconds: 9.44에만 있었습니다. 재시도 뒤 같은 씬 id는 MP4를 들고 있었습니다 — "type": "video", 720×1280, duration_ms: 9440. 영상 엘리먼트가 재생할 수 있다고 가정하지 말고 행마다 typecontent_type을 읽고, 길이는 duration_seconds에서 가져오세요. 같은 씬 id에서도 턴이 바뀌면 타입이 바뀔 수 있습니다.

한 씬만 고치는 재시도가 여기서는 재생성의 약 10분의 1이었습니다 — $7.02 대비 $0.74, 10분 08초 대비 2분 24초. 운영자가 클립 하나를 바꾸고 싶어 한다면 그 클립을 재시도하세요. 제작물을 다시 돌리는 것은 같은 수정을 비싸게 얻는 방법입니다.

루프는 여기까지입니다. 세부는 라이브 커머스 API구조화 출력에 있습니다.