Official public guide · Updated 2026-09-23

YouTube Music Organizer
AI 시스템 v2 가이드

AI는 곡의 의미 정보와 규칙 후보만 만듭니다. 실제 KEEP·ADD·REMOVE·PENDING_ANALYSIS 판단과 YouTube 변경은 앱의 로컬 정책 엔진과 사용자 승인이 담당합니다.

schemaVersion 2taxonomyVersion 1promptVersion 3apiVersion 2

설치된 APK가 이전 버전이라면 앱이 내보낸 요청 JSON의 instructions와 허용 필드를 우선하세요.

핵심 원칙

AI는 속성만 보강장르·분위기·출처 같은 의미 정보를 제안합니다.
정리는 로컬에서 결정PlaylistPolicyEngine이 원하는 상태를 계산합니다.
미리보기 후 적용ADD와 REMOVE는 사용자가 확인한 뒤에만 반영됩니다.
변경 이력과 Undo곡 정보의 이전 값을 기록하고, 이후 사용자 변경과 충돌하지 않을 때 복원합니다.

AI는 YouTube를 직접 수정할 수 없습니다. 곡 정보는 검증 후 로컬에 반영됩니다. 재생목록 ADD·REMOVE는 별도의 변경 미리보기와 사용자 승인이 필요합니다.

앱 내부 AI 작업

Android는 긴 Gemini 호출을 직접 유지하지 않고 Render에 Job을 맡긴 뒤 Neon에 저장된 진행률과 결과를 확인합니다.

대상 준비버전과 inputHash가 같고 confidence가 0.85 이상인 결과는 재사용합니다.
서버 Job 생성필요한 곡만 batch로 업로드합니다.
분석과 checkpoint성공 결과를 trackId 단위로 즉시 Neon에 저장합니다.
상태·결과 회수앱 재실행 후에도 저장된 serverJobId로 복원합니다.
PATCH와 재계산새 Attribute를 반영한 뒤 PlaylistPolicy를 로컬에서 다시 평가합니다.

결과 행 수가 요청 수와 달라도 전체 작업을 버리지 않습니다. 서버는 trackId로 매칭하고 성공한 곡은 보존하며 누락된 곡만 다시 처리합니다. 서버 응답의 곡 순서는 신뢰하지 않습니다.

외부 AI 작업

외부 AI Job을 이용하면 파일을 여러 개로 나누지 않아도 됩니다. 앱에서 분석할 곡을 고르고 Job을 만든 뒤, 앱이 생성한 지침을 외부 AI에 전달합니다.

지침 읽기GET /external/jobs/:id/instructions
다음 batch 받기GET /external/jobs/:id/next?limit=25
결과 제출POST /external/jobs/:id/results
완료 선언POST /external/jobs/:id/complete
앱 검토staging 결과를 preview한 뒤 적용합니다.

Job 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음악 장르rockcanonical 값
mood분위기energetic추측 금지
activity어울리는 활동night_drivinglowercase 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_INTERPRETATION 예시
{
  "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 행 형식과 섞지 마세요.

앱에서 곡 선택곡 분석 → 외부 AI 분석 → 곡 선택으로 요청 대상을 준비합니다.
요청 JSON 내보내기앱이 만든 track-classification-request.json의 instructions.allowedAttributes와 사용자 분류 기준을 확인합니다.
외부 AI에 전달요청 파일과 아래 지침을 함께 전달합니다. 요청의 youtubeId는 바꾸지 않습니다.
결과 가져오기JSON 객체 하나만 파일로 저장하거나 앱에 붙여넣습니다. 앱에서 결과를 검토합니다.
파일 방식 외부 AI 지침
첨부한 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 이후 사용자가 값을 변경사용자 변경을 우선하고 충돌 검토