가사 숫자 한글화 — Suno 오독 방지
- 프롬프트에 아라비아 숫자 금지 규칙+예시 추가 - 안전망 hangulize_numbers: 한자어 수사(일승·칠십팔승·이차전), 게임·경기는 고유어(일곱 게임), 스코어 5:3→오 대 삼, 소수 타율 영점삼일이. 제목도 적용 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6770543318
commit
579ab768bf
@ -466,6 +466,9 @@ def _lyrics_prompt(team_name: str, context: str, league: str = "kbo") -> str:
|
||||
"- 경기장에서 수만 관중이 떼창하는 웅장한 스타디움 앤섬\n"
|
||||
"- 오늘 경기 맥락(어제/최근 결과 설욕·기세, 순위 싸움, 연전 차수, 선발·라인업)을 "
|
||||
"가사에 구체적으로 녹일 것 — 선수 실명 사용 가능\n"
|
||||
"- 가사에 아라비아 숫자를 절대 쓰지 말 것 — 모두 한글 발음으로 표기 "
|
||||
"(1승→일승, 1위→일위, 78승→칠십팔승, 2차전→이차전, 1번→일번, "
|
||||
"7게임→일곱 게임, 15호→십오호, 5:3→오 대 삼)\n"
|
||||
"- 괄호로 관중 콜앤리스폰스 파트 표기, 섹션 태그에 연주 지시 포함\n"
|
||||
"- 분량은 1분 30초~2분 (Verse 2개 + Chorus 반복 + Bridge + Final Chorus)\n\n"
|
||||
f"[가사 형식 레퍼런스 — 구조와 톤만 참고, 내용은 오늘 경기에 맞게 새로 쓸 것]\n{_REFERENCE}\n\n"
|
||||
@ -475,6 +478,62 @@ def _lyrics_prompt(team_name: str, context: str, league: str = "kbo") -> str:
|
||||
)
|
||||
|
||||
|
||||
# ── 가사 숫자 한글화 (Suno 가 아라비아 숫자를 잘못 읽는 문제 방지) ──
|
||||
_SINO = "영일이삼사오육칠팔구"
|
||||
_NATIVE = {
|
||||
1: "한", 2: "두", 3: "세", 4: "네", 5: "다섯", 6: "여섯", 7: "일곱",
|
||||
8: "여덟", 9: "아홉", 10: "열", 11: "열한", 12: "열두", 13: "열세",
|
||||
14: "열네", 15: "열다섯", 16: "열여섯", 17: "열일곱", 18: "열여덟",
|
||||
19: "열아홉", 20: "스무",
|
||||
}
|
||||
|
||||
|
||||
def _sino(n: int) -> str:
|
||||
"""한자어 수사 — 78 → 칠십팔 (0~9999)."""
|
||||
if n == 0:
|
||||
return "영"
|
||||
parts = []
|
||||
for unit, name in ((1000, "천"), (100, "백"), (10, "십")):
|
||||
d, n = divmod(n, unit)
|
||||
if d:
|
||||
parts.append(("" if d == 1 else _SINO[d]) + name)
|
||||
if n:
|
||||
parts.append(_SINO[n])
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
def _native(n: int) -> str:
|
||||
"""고유어 수사 — 7 → 일곱 (범위 밖은 한자어 폴백)."""
|
||||
return _NATIVE.get(n) or _sino(n)
|
||||
|
||||
|
||||
def hangulize_numbers(text: str) -> str:
|
||||
"""가사 속 아라비아 숫자를 전부 한글 발음으로 치환.
|
||||
|
||||
1승→일승, 78승→칠십팔승, 7게임→일곱 게임, 5:3→오 대 삼, 0.312→영점삼일이.
|
||||
"""
|
||||
# 소수(타율 등): 0.312 → 영점삼일이 (소수부는 자리별 낭독)
|
||||
text = re.sub(
|
||||
r"(\d)\.(\d+)",
|
||||
lambda m: _sino(int(m.group(1))) + "점" + "".join(_SINO[int(c)] for c in m.group(2)),
|
||||
text,
|
||||
)
|
||||
# 스코어: 5:3 / 5대3 → 오 대 삼
|
||||
text = re.sub(
|
||||
r"(\d+)\s*[:대]\s*(\d+)",
|
||||
lambda m: f"{_sino(int(m.group(1)))} 대 {_sino(int(m.group(2)))}",
|
||||
text,
|
||||
)
|
||||
# 게임·경기 수는 고유어 수사 (7게임 → 일곱 게임)
|
||||
text = re.sub(
|
||||
r"(\d+)\s*(게임|경기)",
|
||||
lambda m: f"{_native(int(m.group(1)))} {m.group(2)}",
|
||||
text,
|
||||
)
|
||||
# 남은 모든 숫자는 한자어 수사 (1위→일위, 2차전→이차전, 15호→십오호)
|
||||
return re.sub(r"\d+", lambda m: _sino(int(m.group(0))), text)
|
||||
|
||||
|
||||
def _parse_json(text: str) -> dict:
|
||||
t = text.strip()
|
||||
t = re.sub(r"^```(?:json)?\s*|\s*```$", "", t)
|
||||
@ -549,6 +608,8 @@ async def _start_one(db, m: Match, side: str, lineups: dict | None) -> bool:
|
||||
lyrics = str(piece.get("lyrics") or "").strip()
|
||||
if not lyrics:
|
||||
raise RuntimeError("LLM 가사 비어있음")
|
||||
lyrics = hangulize_numbers(lyrics) # 프롬프트를 뚫고 남은 숫자 안전망
|
||||
title = hangulize_numbers(title) # 제목도 Suno 가 부르는 경우 대비
|
||||
|
||||
task_id = await suno.start_generation(title, style, lyrics)
|
||||
upgrade = bool(row and row.status == "complete")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user