핵심 원칙
AI는 YouTube를 직접 수정할 수 없습니다. 곡 정보는 검증 후 로컬에 반영됩니다. 재생목록 ADD·REMOVE는 별도의 변경 미리보기와 사용자 승인이 필요합니다.
앱 내부 AI 작업
Android는 긴 Gemini 호출을 직접 유지하지 않고 Render에 Job을 맡긴 뒤 Neon에 저장된 진행률과 결과를 확인합니다.
결과 행 수가 요청 수와 달라도 전체 작업을 버리지 않습니다. 서버는 trackId로 매칭하고 성공한 곡은 보존하며 누락된 곡만 다시 처리합니다. 서버 응답의 곡 순서는 신뢰하지 않습니다.
외부 AI 작업
외부 AI Job을 이용하면 파일을 여러 개로 나누지 않아도 됩니다. 앱에서 분석할 곡을 고르고 Job을 만든 뒤, 앱이 생성한 지침을 외부 AI에 전달합니다.
GET /external/jobs/:id/instructionsGET /external/jobs/:id/next?limit=25POST /external/jobs/:id/resultsPOST /external/jobs/:id/completeJob token은 임시 자격 증명입니다. 해당 Job 읽기와 결과 쓰기에만 사용하며 공개 채널에 올리지 마세요. 다른 Job 접근과 YouTube 수정 권한은 없습니다.
이 Job의 instructions를 읽고 완료될 때까지 처리해: https://youtube-organizer-ai.onrender.com/external/jobs/<jobId>/instructions Authorization: Bearer <jobToken>
위의 <jobId>와 <jobToken>은 설명용 자리표시자입니다. 직접 복사해 보내지 말고 앱에서 발급된 실제 지침을 사용하세요. Job 유형은 TRACK_ANALYSIS와 RULE_INTERPRETATION이며 결과는 검토 전 staging에 저장됩니다.
Classification Schema v2
공식 분류 필드는 다음 여덟 개입니다. 모든 값은 비어 있지 않은 단일 JSON 문자열이어야 합니다.
| 필드 | 의미 | 예시 | 조건 |
|---|---|---|---|
contentType | 곡·믹스·라이브 등 형태 | track | 불확실하면 생략 |
genre | 음악 장르 | rock | canonical 값 |
mood | 분위기 | energetic | 추측 금지 |
activity | 어울리는 활동 | night_driving | lowercase snake_case |
source | 명시된 원작·출처 | game_ost | 명시적 근거 필수 |
languageRegion | 언어·지역 | japanese | 제목만으로 단정 금지 |
franchise | 게임·애니·미디어 | djmax | 명시적 근거 필수 |
voicebank | 보컬 합성 음원 | hatsune_miku | 명시적 근거 필수, 배열 금지 |
유효하지 않은 값
- 배열:
"voicebank": ["gumi"] - 객체:
"genre": {"value":"rock"} - 불리언·숫자: 문자열 필드에
true또는1 - 근거 없는 강한 속성: evidence 없이 source/franchise/voicebank 생성
PATCH와 명시적 삭제
- 결과에 없는 필드는 기존 값을 유지합니다.
null은 삭제가 아니라 미기재로 취급합니다.- 삭제는
clear: ["field"]로 명시합니다. - 적용 전후 diff와 이전 값 snapshot을 저장합니다.
- 사용자가 이후 직접 바꾼 값은 Undo가 덮어쓰지 않습니다.
{
"trackId": "youtube-video-id",
"attributes": { "genre": "rock" },
"clear": ["mood"],
"fieldConfidence": { "genre": 0.92 },
"fieldEvidence": {
"genre": ["공식 설명에 rock 장르가 명시됨"]
},
"confidence": 0.92,
"evidence": ["공식 설명 확인"]
}신뢰도와 근거
fieldConfidence는 필드별 0~1 숫자이고 fieldEvidence는 필드별 문자열 배열입니다. 곡 전체 confidence는 v1 호환용입니다.
0도 유효한 신뢰도 값이며 전송 실패와 구분합니다.- 필드별 신뢰도가 0.85 미만이거나 자동 재시도 한도에 도달한 곡은
LOW_CONFIDENCE_FINAL로 남아 검토 대상이 될 수 있습니다. - 동일 inputHash와 동일 버전의 저신뢰 결과를 무한 재분석하지 않습니다.
- input/schema/taxonomy/prompt가 바뀌거나 사용자가 명시적으로 재분석할 때만 다시 요청합니다.
자연어 규칙 해석
AI는 자연어를 공식 Rule Schema 후보로 바꿀 뿐입니다. 실제 곡 상태는 deterministic PlaylistPolicyEngine이 계산합니다.
{
"rule": {
"mode": "ALL",
"conditions": [
{ "field": "franchise", "operator": "EQUALS", "value": "djmax" }
],
"reason": "DJMAX 프랜차이즈 곡만 포함"
}
}mode는 ALL/ANY, operator는 EQUALS, NOT_EQUALS, CONTAINS, IN입니다. 규칙은 preview 후 적용합니다.
상태와 재시도
| 상황 | 처리 | 사용자 의미 |
|---|---|---|
| 정상 처리·polling | 주기적으로 상태 확인 | 분석 중 유지 |
| 429/503, timeout, 연결 오류 | 30초 → 1분 → 2분 → 4분 | 자동 재시도 |
| 400/401 등 영구 오류 | 즉시 실패 | 잘못된 요청을 반복하지 않음 |
| 일부 결과 누락 | 성공 checkpoint 유지, 누락 trackId만 재요청 | 완료 곡 재전송 없음 |
| Render 재시작 | Neon에 저장된 작업 상태와 성공 결과를 기준으로 복구 | 완료 곡을 처음부터 다시 처리하지 않음 |
단순 polling은 과부하가 아닙니다. “사용량이 많음”은 실제 429/503 같은 retryable 응답에만 사용합니다.
TRACK_ANALYSIS 결과 예시
{
"trackId": "youtube-video-id",
"attributes": {
"contentType": "track",
"genre": "synth_pop",
"languageRegion": "english"
},
"fieldConfidence": {
"contentType": 0.98,
"genre": 0.91,
"languageRegion": 0.95
},
"fieldEvidence": {
"contentType": ["공식 제목과 채널이 단일 음악 트랙을 식별"],
"genre": ["공식 발매 정보의 장르 설명"],
"languageRegion": ["가사의 주 언어 확인"]
},
"referenceMetadata": { "release": "official single" },
"confidence": 0.91,
"evidence": ["공식 채널 및 발매 정보 확인"]
}결과 배열 순서를 신뢰하지 않습니다. 입력 ID를 그대로 trackId에 복사해야 합니다.
JSON 파일로 결과 가져오기
앱의 외부 AI 요청 파일 만들기와 결과 가져오기도 계속 사용할 수 있습니다. 이 파일 흐름의 요청은 여전히 schemaVersion: 1입니다. 외부 AI Job의 v2 행 형식과 섞지 마세요.
track-classification-request.json의 instructions.allowedAttributes와 사용자 분류 기준을 확인합니다.youtubeId는 바꾸지 않습니다.첨부한 track-classification-request.json의 tracks만 분석하세요. instructions.allowedAttributes와 userClassificationCriteria를 우선하세요. 요청에 없는 youtubeId를 만들거나 원본 youtubeId를 변경하지 마세요. 근거가 부족한 속성은 추측하지 말고 생략하세요. 결과는 format이 youtube-music-organizer-classification-pack이고 schemaVersion이 1인 JSON 객체 하나만 반환하세요. 각 classifications 항목은 youtubeId, 비어 있지 않은 attributes, 0~1 숫자 confidence를 포함해야 합니다. voicebank 등 문자열 속성은 단일 문자열, vcScope/longForm은 JSON boolean입니다. YouTube 재생목록 변경 제안은 결과에 넣지 마세요. Markdown 코드블록이나 JSON 앞뒤 설명문을 넣지 마세요.
{
"format": "youtube-music-organizer-classification-pack",
"schemaVersion": 1,
"packVersion": "personal-2026-09-23-1",
"classifications": [
{
"youtubeId": "요청 파일의 youtubeId를 그대로 복사",
"attributes": { "genre": "electronic", "mood": "energetic" },
"confidence": 0.92,
"evidence": ["발매 정보와 공식 설명 확인"]
}
]
}예시 ID 그대로 가져오지 마세요. classifications[].youtubeId는 앱이 동기화한 실제 곡 ID여야 합니다. packVersion은 비어 있지 않은 100자 이하 문자열, 파일은 10MB 이하입니다.
v1 파일은 곡 단위 confidence를 사용하며 현재 앱의 자동 사용 기준은 0.85입니다. v1의 voice, scene, style, vcScope, longForm은 호환을 위해 읽지만 v2의 공식 여덟 필드와는 별개입니다. 새 Job은 v2의 필드별 신뢰도와 근거를 사용합니다. 기존 Room 분석 결과와 History는 유지됩니다.
기존 규칙 추천 파일
앱에서 내보낸 rule-analysis-request.json도 지원합니다. 결과는 format: youtube-music-organizer-rule-analysis-result, schemaVersion: 1, 원본 requestId, resultVersion, candidates를 포함해야 합니다. 조건과 실행은 요청 파일의 instructions에 있는 값만 사용하세요. 앱은 후보를 전체 보관함에서 다시 시험하며, 가져온 규칙을 자동 활성화하지 않습니다.
오류 해결
| 오류 | 원인 | 해결 |
|---|---|---|
| 속성 값은 문자열이어야 함 | 배열·객체·숫자 반환 | 공식 Attribute를 단일 문자열로 작성 |
| 결과 수가 요청 수와 다름 | 일부 trackId 누락 | 정상 결과는 저장하고 누락 ID만 처리 |
| 서버 AI 작업 실패 | 영구 요청 오류 또는 재시도 한도 초과 | Job 오류 확인 후 새 작업 생성 |
| 사용량이 많아 재시도 | 실제 429/503 응답 | 일정 간격으로 자동 재개 |
| 결과가 적용되지 않음 | preview 이후 사용자가 값을 변경 | 사용자 변경을 우선하고 충돌 검토 |