-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] : autoTagSong 태그 프롬프트 외부 주입으로 변경 (#188) #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,14 +16,10 @@ interface Tag { | |
| category: string; | ||
| } | ||
|
|
||
| let cachedTagsPrompt: string | null = null; | ||
|
|
||
| /** | ||
| * DB에서 전체 태그 목록을 읽어와 AI 프롬프트용 텍스트로 변환한다. | ||
| */ | ||
| const getTagsForPrompt = async (): Promise<string> => { | ||
| if (cachedTagsPrompt) return cachedTagsPrompt; | ||
|
|
||
| export const getTagsForPrompt = async (): Promise<string> => { | ||
| const supabase = getClient(); | ||
| const { data: tags, error } = await supabase | ||
| .from('tags') | ||
|
|
@@ -36,34 +32,51 @@ const getTagsForPrompt = async (): Promise<string> => { | |
| } | ||
|
|
||
| // AI가 읽기 편하게 "ID: 이름 (카테고리)" 형식으로 변환 | ||
| cachedTagsPrompt = tags.map((tag: Tag) => `${tag.id}: ${tag.name} (${tag.category})`).join('\n'); | ||
| return cachedTagsPrompt; | ||
| return tags.map((tag: Tag) => `${tag.id}: ${tag.name} (${tag.category})`).join('\n'); | ||
| }; | ||
|
|
||
| /** | ||
| * AI를 활용해 노래에 적절한 태그 ID들을 추출한다. | ||
| */ | ||
| export const autoTagSong = async (title: string, artist: string): Promise<number[]> => { | ||
| export const autoTagSong = async ( | ||
| title: string, | ||
| artist: string, | ||
| tagsPrompt: string, | ||
| ): Promise<number[]> => { | ||
| try { | ||
| // 1단계: 프롬프트용 태그 리스트 준비 | ||
| const tagsPrompt = await getTagsForPrompt(); | ||
| if (!tagsPrompt) return []; | ||
|
|
||
| // 1단계: 정규식을 이용한 문자열 사전 분석 (Harness) | ||
| const hasHangul = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/.test(title + artist); | ||
| const hasKana = /[ぁ-んァ-ヶ]/.test(title + artist); | ||
|
|
||
| // LLM에게 줄 강력한 힌트 생성 | ||
| const languageHints = ` | ||
| - [Detected Script] Hangul Present: ${hasHangul}, Japanese Kana Present: ${hasKana} | ||
| `.trim(); | ||
|
|
||
| // 2단계: OpenAI API 호출 | ||
| const response = await client.chat.completions.create({ | ||
| model: 'gpt-4o-mini', // 가성비가 좋은 모델 사용 | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: ` | ||
| You are a music database expert. Based on the song title and artist, categorize the song by selecting appropriate tag IDs from the provided list. | ||
| You are a music database expert specializing in global artist categorization. | ||
|
|
||
| [Language Selection Strategy] | ||
| - **Do NOT** assume a song is 102 (팝송) solely based on English/Latin characters. | ||
| - If title/artist are in English, research the **artist's origin and primary market**. | ||
| - Priority Logic: | ||
| 1. If Hangul is detected OR the artist is a K-Pop artist: Select 100 (한국노래). | ||
| 2. If Kana is detected OR the artist is a J-Pop/Japanese artist: Select 101 (일본노래). | ||
| 3. Select 102 (팝송) ONLY if the artist is primarily from Western/English-speaking regions. | ||
| 4. For all other cases or truly global/mixed origins, use 103 (글로벌). | ||
|
|
||
| [Selection Rules] | ||
| - Language Slot (100-199): EXACTLY 1 tag. | ||
| - Genre Slot (200-299): EXACTLY 1 tag. | ||
| - Origin Slot (300-399): 1 to 2 tags, sorted by relevance. | ||
|
|
||
| Guidelines: | ||
| 1. Select at least one tag, but no more than 4. | ||
| 2. Prioritize Language (100s), then Genre (200s), then Origin (300s). | ||
| 3. If it's Japanese music, ALWAYS include 101 (J-POP). | ||
| 4. Be precise. If it's from an Anime, use 302 (애니메이션). | ||
| 5. Return only JSON: {"tag_ids": [number, number, ...]} | ||
| [Contextual Hints] | ||
| ${languageHints} | ||
|
|
||
| Allowed Tags List: | ||
| ${tagsPrompt} | ||
|
Comment on lines
60
to
82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Ambiguous llm json contract autoTagSong() still parses the response as {"tag_ids": number[]} but the updated system prompt no
longer instructs the model to return a tag_ids field, so valid JSON responses without that key will
make the function return undefined and cause repeated per-song failures.
Agent Prompt
|
||
|
|
@@ -75,14 +88,12 @@ export const autoTagSong = async (title: string, artist: string): Promise<number | |
| }, | ||
| ], | ||
| response_format: { type: 'json_object' }, | ||
| temperature: 0, // 결과의 일관성을 위해 0으로 설정 | ||
| max_tokens: 50, // 결과가 짧으므로 토큰 제한 | ||
| temperature: 0, | ||
| }); | ||
|
|
||
| const content = response.choices[0].message.content; | ||
| if (!content) return []; | ||
|
|
||
| // 3단계: 결과 파싱 및 반환 | ||
| const result: { tag_ids: number[] } = JSON.parse(content); | ||
| return result.tag_ids; | ||
| } catch (error) { | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. Llm json 스키마 불일치
🐞 Bug≡ CorrectnessautoTagSong은 응답을 {tag_ids:number[]}로 파싱해 result.tag_ids를 그대로 반환하지만, 새 system 프롬프트에는 tag_ids 키를 반드시 포함하라는 출력 스키마 지시가 없어 json_object 모드에서 다른 키로 응답할 경우 tagIds가 undefined가 됩니다. 그 결과 taggingSongs에서 tagIds.length 접근 시 TypeError가 발생해 해당 곡 태깅이 실패합니다.Agent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools