본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

호출 하나에 언어 하나, 빌드가 강제해

~12 min · tts, constraints, hard-checks, provider-behavior

Level 0불 꺼진 작업장
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

파이프라인을 다시 빚어놓은 공급자 동작

내레이션은 설계상 두 언어를 써. 한쪽이 논증을 싣고 다른 쪽이 관계를 실어. 편집상의 결정인데, 음성 합성에 관한 기술적 사실 하나랑 대번에 부딪혀. 한 요청에 두 언어를 넣으면 외국어 단어가 일정 확률로 잘못된 발음으로 돌아와. 항상도 아니고 절대도 아냐. 예측이 안 돼. 모형들이 섞인 텍스트로 학습된 방식이 그 읽기를 도박으로 만들어놨거든.

세 선택지 중 예측 불가가 제일 나빠. 항상 틀리면 우회하면 되고, 절대 안 틀리면 처리할 게 없어. 여러 테이크 중 하나쯤에서 나타나는 실패는, 안 들어보고는 어떤 테이크도 믿을 수 없다는 뜻이야. 다시 요청해서 고칠 수도 없고. 같은 요청이 멀쩡하게 돌아왔다가 나중에 깨질 수 있으니까.

해법은 조심하기가 아니라 구조 바꾸기

대응은 합성의 단위가 뭔지를 바꾸는 거였어. 원소는 문장도 비트도 아냐. 언어가 하나인 구간이야. 모든 요청이 정확히 한 문자 체계만 담으니까, 도박을 만들어내는 상황 자체가 성립을 못 해.

그다음 규칙을 기억이 아니라 강제로 붙들어. 두 문자 체계를 담은 구간이 있으면 빌드 검사가 실패시켜. 숫자랑 문장부호는 예외로 두고. 이 부분이 훔쳐 갈 값어치가 있어. 누구 머릿속에 사는 규칙은 누가 급해지는 날까지만 버티는 규칙이고, 이건 조용히 그리고 띄엄띄엄 실패했을 거야. 규율로 지키는 규칙한테 최악의 조합이지.

외국어 단어 하나를 끼워 넣지 마

뻔한 우회는 외국어 단어만 따로 합성해서 끼워 넣는 거야. 검토했고 거절당했어. 한 줄에 단어 하나를 삽입하면 인용처럼 안 들리거든. 딸꾹질처럼 들려. 인용문 한 문장 앞뒤에서 숨으로 읽히는 그 틈이, 단어 하나 앞뒤에선 결함으로 읽혀.

그래서 규칙은 그 상황을 안 만드는 거야. 한 언어로 된 구간은 외국어 용어를 자기 문자 체계로 옮겨 적어. 그 줄이 진짜로 원래 형태의 외국어 용어를 필요로 하면, 그 줄 자체가 그 언어야. 전환은 뜻의 경계에서 일어나거나 아예 안 일어나거나 둘 중 하나고.

띄엄띄엄 나는 실패는 조심해서 피할 게 아니라 구조적으로 불가능하게 만들어야 해. 가끔 나타나는 결함은 테스트도 규율도 이겨. 테스트는 통과하고, 검토자는 멀쩡한 테이크를 듣고, 실패는 나중에 나가. 하나 발견하면 쓸 만한 수는 거의 항상 연산의 형태를 바꿔서 실패하는 경우를 만들 수 없게 하는 거야. 경계심을 더 붙이는 게 아니라.

Code

규칙을 잊을 수 없게 만드는 검사·python
HANGUL = re.compile(r"[\uac00-\ud7a3]")
LATIN  = re.compile(r"[A-Za-z]")

def check_segment_language_purity(segments: list[Segment]) -> list[str]:
    """A segment carrying two scripts is a build failure.

    Digits and punctuation are exempt - they are read the same way
    by either voice, so they do not trigger the mispronunciation.
    """
    errors = []
    for seg in segments:
        has_hangul = bool(HANGUL.search(seg.text))
        has_latin = bool(LATIN.search(seg.text))
        if has_hangul and has_latin:
            errors.append(
                f"{seg.key}: mixed script in one synthesis call -"
                f" split at the meaning boundary or transliterate."
                f"  text={seg.text!r}"
            )
    return errors


# Note what this is NOT: a warning, a lint suggestion, a note in
# the style guide. It fails the build, because the defect it
# prevents is intermittent and would otherwise ship.
외국어 용어를 다루는 세 방법, 순위대로·text
BAD    one request, both scripts
       "그 사람이 pipeline 이라고 불렀어"
       -> pronunciation is a dice roll on every synthesis

WORSE  splice a one-word foreign call into the line
       [ko run] + [1-word en call] + [ko run]
       -> the gap reads as a hiccup, not a quotation

GOOD   transliterate into the run's own script
       "그 사람이 파이프라인이라고 불렀어"
       -> one script, one call, no gamble

ALSO GOOD   if the term must stay in its original form,
            the whole line is in that language, and the
            switch happens at a meaning boundary.

External links

Exercise

네 시스템에서 띄엄띄엄 나는 실패를 하나 찾아봐. 적은 확률로 실패하는데 재시도나 경고나 '조심하세요' 메모로 처리되고 있는 거. 어떤 형태 변경이 그 실패 경우를 '드물게'가 아니라 '만들 수 없게' 만드는지 물어봐. 그다음 그 변경 비용을, 그게 대신하는 경계심의 지속 비용이랑 견줘봐.
Hint
재시도 로직은 형태 문제가 해결되는 대신 관리되고 있다는 흔한 신호야. 재시도는 띄엄띄엄한 실패를 대부분의 경우 느린 성공으로 바꿔줘. 진짜 유용하고, 동시에 누군가 밑바닥 형태를 고치게 만들었을 압력을 없애버려.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.