fix: Seed-X 프롬프트 형식과 티어별 용어집 전략 수정
4티어(Seed-X-PPO-7B) 번역 경로가 모델 카드 요구사항을 어기고 있었다. - 프롬프트 끝의 `<ko>` 등 대상 언어 태그가 빠져 있었다. PPO 학습에 쓰인 신호라 없으면 번역 품질이 흔들린다. - Seed-X 는 chat template 없는 번역 전용 completion 모델인데 "구어체로 자연스럽게" 같은 지시문 래퍼를 씌우고 있었다. 학습 분포를 벗어난다. - 그 결과 MTSpec.supports_prompt_glossary=True 가 사실과 달랐다. Seed-X 는 용어집 지시문을 이해하지 못하므로 플레이스홀더 치환을 써야 한다. 수정 - PromptStyle(NONE/SEEDX/INSTRUCT) 도입, supports_prompt_glossary 를 prompt_style 에서 파생시켜 둘이 어긋날 수 없게 함 - build_seedx_prompt() 로 모델 카드 형식을 분리 (지시문 주입 불가) - INSTRUCT 경로는 chat template 사용, Qwen3 thinking 모드는 끔 - LANGUAGES 에 seedx 태그 명시 - finetune_mt.py 가 티어의 prompt_style 을 따라가게 해 학습/추론 프롬프트 일치 - AWQ Int4 로드 실패 시 autoawq 설치 안내를 담은 오류 메시지 검증: pytest 52개 통과 (프롬프트 회귀 테스트 13개 추가), ruff clean 근거: https://huggingface.co/ByteDance-Seed/Seed-X-PPO-7B Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
82
tests/test_prompts.py
Normal file
82
tests/test_prompts.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""번역 프롬프트 형식 회귀 테스트.
|
||||
|
||||
Seed-X 는 chat template 없는 번역 전용 completion 모델이라 모델 카드가 정한
|
||||
형식을 한 글자도 벗어나면 안 된다. 특히 끝의 `<언어코드>` 태그는 PPO 학습에
|
||||
쓰인 것이라 빠지면 품질이 무너진다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from hearo.constants import LANGUAGE_CODES, LANGUAGES
|
||||
from hearo.models.glossary import Glossary, GlossaryEntry
|
||||
from hearo.models.tiers import TIERS, MTBackend, PromptStyle, get_tier
|
||||
from hearo.models.translator import build_seedx_prompt
|
||||
|
||||
|
||||
def test_seedx_prompt_matches_model_card_exactly():
|
||||
"""모델 카드 예시: "Translate the following English sentence into Chinese:\\nMay the force be with you <zh>" """
|
||||
assert build_seedx_prompt("May the force be with you", "en", "zh") == (
|
||||
"Translate the following English sentence into Chinese:\n"
|
||||
"May the force be with you <zh>"
|
||||
)
|
||||
|
||||
|
||||
def test_seedx_prompt_always_ends_with_target_language_tag():
|
||||
for target in LANGUAGE_CODES:
|
||||
prompt = build_seedx_prompt("hello", "en", target)
|
||||
assert prompt.endswith(f" <{target}>"), f"{target} 태그 누락"
|
||||
|
||||
|
||||
def test_seedx_prompt_has_no_extra_instructions():
|
||||
"""지시문을 끼워 넣으면 Seed-X 의 학습 분포를 벗어난다."""
|
||||
prompt = build_seedx_prompt("fall back now", "en", "ko")
|
||||
lowered = prompt.lower()
|
||||
for forbidden in ("casual", "output only", "glossary", "game or broadcast", "용어"):
|
||||
assert forbidden not in lowered
|
||||
assert prompt.count("\n") == 1 # 지시문 한 줄 + 본문 한 줄
|
||||
|
||||
|
||||
@pytest.mark.parametrize("code", LANGUAGE_CODES)
|
||||
def test_every_language_has_a_seedx_tag(code):
|
||||
assert LANGUAGES[code]["seedx"], f"{code} 에 seedx 태그가 없습니다"
|
||||
|
||||
|
||||
def test_seedx_tier_does_not_use_prompt_glossary():
|
||||
"""Seed-X 는 지시문을 못 알아들으므로 프롬프트 용어집을 쓰면 안 된다."""
|
||||
precision = get_tier("precision")
|
||||
assert precision.mt.prompt_style is PromptStyle.SEEDX
|
||||
assert precision.mt.supports_prompt_glossary is False
|
||||
|
||||
|
||||
def test_instruct_tier_uses_prompt_glossary():
|
||||
ultimate = get_tier("ultimate")
|
||||
assert ultimate.mt.prompt_style is PromptStyle.INSTRUCT
|
||||
assert ultimate.mt.supports_prompt_glossary is True
|
||||
|
||||
|
||||
def test_seq2seq_tiers_never_use_prompt_glossary():
|
||||
for tier in TIERS.values():
|
||||
if tier.mt.backend is MTBackend.CTRANSLATE2:
|
||||
assert tier.mt.prompt_style is PromptStyle.NONE
|
||||
assert tier.mt.supports_prompt_glossary is False
|
||||
|
||||
|
||||
def test_every_llm_tier_declares_a_prompt_style():
|
||||
for tier in TIERS.values():
|
||||
if tier.mt.backend is MTBackend.TRANSFORMERS:
|
||||
assert tier.mt.prompt_style is not PromptStyle.NONE, tier.key
|
||||
|
||||
|
||||
def test_glossary_survives_seedx_placeholder_path():
|
||||
"""Seed-X 경로에서 용어집이 프롬프트가 아니라 치환으로 동작하는지."""
|
||||
g = Glossary([GlossaryEntry("Nexus", {"ko": "넥서스"})])
|
||||
protected, repl = g.protect("push to the Nexus", "ko")
|
||||
|
||||
prompt = build_seedx_prompt(protected, "en", "ko")
|
||||
assert "Nexus" not in prompt # 모델이 건드릴 수 없게 가려짐
|
||||
assert prompt.endswith(" <ko>")
|
||||
|
||||
# 모델이 플레이스홀더를 그대로 통과시켰다고 가정
|
||||
assert Glossary.restore("⟦0⟧로 밀어", repl) == "넥서스로 밀어"
|
||||
Reference in New Issue
Block a user