본문으로 건너뛰기
Chandler Nguyen
AI7분 읽기

AI 팟캐스트 호스트가 'Trời ơi'를 13번 외친 후 배운, 실제로 지켜지는 시스템 프롬프트 작성법

AI 팟캐스트 호스트를 위해 며칠 동안 시스템 프롬프트를 정성껏 작성했습니다. 그들은 모든 단어를 읽고 거의 전부를 무시했습니다. 해결책은 더 나은 단어가 아니었습니다 — LLM이 파이프라인 내에서 규칙이 어디에 위치하느냐에 따라 규칙을 완전히 다르게 처리한다는 점을 이해하는 것이었습니다.

지난주, 좀 부끄러운 발견을 했어요.

제 AI 팟캐스트 파이프라인에는 며칠 동안 공들여 만든 정교한 시스템 프롬프트——"대화 가이드"——가 있어요. 두 명의 AI 축구 해설자(호스트)에게 서로 어떻게 대화해야 하는지 정확히 지시하는 거죠. 같은 오프닝을 반복하지 말 것. 세그먼트마다 구조를 바꿀 것. 시그니처 멘트(입버릇)는 절제해서 사용할 것. 통계를 절대 조작하지 말 것.

LLM은 모든 단어를 읽었어요. 그리고 거의 전부를 무시했죠.

베트남 호스트는 한 에피소드에서 "Trời ơi"(오 마이 갓)를 13번이나 말했어요. 일본 호스트는 모든 세그먼트를 "いやー、健一さん!"(이야, 겐이치 씨!)로 열었죠. 영어 호스트는 자신만만하게 xG 0.35, 점유율 65%를 인용했는데——그 숫자는 제 데이터베이스 어디에도 존재하지 않았어요.

프롬프트는 정중하게 "알겠습니다" 하고 받아들여졌어요. 그리고 무시당했죠.

이게 제가 프롬프트 엔지니어링에 대해 배운 가장 유용한 교훈이에요: LLM에는 두 가지 읽기 모드가 있고, 이 둘은 완전히 다르게 취급됩니다.


두 가지 읽기 모드 문제

제 원래 대화 가이드는 이랬어요. 구조가 동일하므로 베트남어 버전을 영어로 번역한 기준으로 보여드릴게요:

STYLE GUIDELINES:

HOST INTERACTION PATTERN: Emotional Host + Analytical Host.

STRICT RULES FOR EVERY SEGMENT:

0. NEVER REPEAT THE SAME OPENING. Vary which host initiates, 
   which match moment they react to first.

1. EMOTIONAL START (mandatory): Every segment begins with 
   the emotional host's pure reaction.

2. DATA-GROUNDING RULE (mandatory): Every claim must cite 
   specific numbers. Not "they dominated" but "61% possession, 
   3.17 xG to 0.65."

3. CATCHPHRASES:
   Emotional host: "You see that?!" "Wait wait wait..."
   Analytical host: "There's a deeper reason..." "Let me tell you."

꽤 철저해 보이죠? 진짜 제대로 했다고 생각했어요.

하지만 실제 생성된 스크립트를 읽어보니, 결과는 이랬어요:

규칙가이드의 지시 내용LLM의 실제 동작
규칙 0 (반복 금지)오프닝에 변화를 줄 것모든 세그먼트가 감정 호스트의 입버릇으로 시작
규칙 2 (데이터)구체적인 숫자를 인용할 것완전히 조작된 숫자를 당당하게 인용
규칙 3 (입버릇)시그니처 멘트 목록각 멘트를 에피소드당 5~13회 사용

규칙은 분명히 있었어요. LLM은 읽었고요. 하지만 요구사항이 아니라 제안 정도의 힘밖에 없었어요.

이때 깨달았어요: LLM이 제 규칙을 "강제 가능한 제약"이 아니라 "스타일 문맥 정보"로 처리하고 있었던 거예요.


패턴: 컨텍스트 vs. 커맨드

여러 종류의 텍스트를 어떻게 읽는지 생각해 보세요. 레스토랑 메뉴를 받으면, 행동할 정보로 읽어요. 파스타의 역사에 관한 위키피디아 문서를 받으면, 배경 지식으로 읽죠.

LLM도 프롬프트에 대해 비슷한 걸 해요. 규칙이 "STYLE GUIDELINES"나 "HOST INTERACTION PATTERN"이나 "RULES FOR SEGMENTS" 같은 라벨이 붙은 섹션에 있으면, 모델은 그걸 주변 정보——응답에 색을 입히는 문맥일 뿐 제약하지는 않는——로 취급해요.

규칙이 "CRITICAL IMPROVEMENTS REQUIRED"나 "SPECIFIC PHRASE LIMITS" 같은 라벨이 붙은 섹션에 있으면, 모델은 그걸 강제 가능한 명령——검증하고 수정해야 하는 것——으로 취급해요.

이 인사이트의 핵심은 "CRITICAL"이나 "REQUIRED" 같은 단어가 아니에요. 규칙이 파이프라인의 어디에 위치하느냐예요.


해결책: 생성 단계가 무시하는 것을 리뷰 단계가 강제하게 만들기

제 파이프라인에는 두 번의 LLM 호출이 있어요:

  1. 생성 단계 —— 대화 가이드를 문맥으로 삼아 세그먼트별로 스크립트 작성
  2. 리뷰 단계 —— 완성된 전체 스크립트를 읽고 개선

처음에는 모든 규칙이 생성 단계에 있었어요. 리뷰 단계에는 완전히 영어 중심의 범용적인 반복 금지 규칙("'That's fascinating'을 두 번 말하지 말 것" 같은)만 있을 뿐, 제 호스트의 입버릇이나 구조적 요구사항에 대해서는 아무것도 몰랐죠.

수정은 어휘적인 게 아니라 구조적인 거였어요. 강제를 리뷰 단계로 옮겼어요——거기서는 전체 스크립트가 한 번에 보이니까요.

무엇이 바뀌었는가:

Before: 규칙이 생성 프롬프트에 있음 (무시됨)

STYLE GUIDELINES:
0. NEVER REPEAT THE SAME OPENING...
1. EMOTIONAL START...
2. DATA-GROUNDING...
3. CATCHPHRASES: "You see that?!" ...

After: 규칙을 추출해서 리뷰 프롬프트에 주입 (강제됨)

CRITICAL IMPROVEMENTS REQUIRED ACROSS THE ENTIRE SCRIPT:

2a. ENFORCE STYLE GUIDELINES' STRUCTURAL RULES:
     The STYLE GUIDELINES above contain specific requirements 
     about segment structure. These are REQUIREMENTS, not suggestions.
     - If the guidelines say "X of Y segments must vary," CHANGE 
       segments that violate this.
     - Check how EACH segment opens. If 4+ out of 5 open identically, 
       VARY at least 2.

3b. LOCALE-SPECIFIC PHRASE LIMITS (extracted from host profiles — 
    MAXIMUM 2 TIMES each across entire script):
    - "Trời ơi" / "Trời má" — max 4x combined
    - "Có một lý do sâu hơn" — max 2x
    - "Mày thấy chưa?!" — max 2x
    ...

여기서 두 가지 일이 일어났어요:

  1. 구조적 규칙이 컨텍스트에서 명령으로 승격되었어요——"이게 추천 패턴입니다"가 아니라 "이걸 강제하라", "이걸 위반한 세그먼트를 변경하라"로 다시 쓰였어요

  2. 입버릇이 호스트 프로필에서 추출되어, 제안 목록이 아닌 명시적인 제한으로 표시되었어요. "Catchphrases: here are some"이 "These phrases: MAXIMUM 2 TIMES each"로 바뀌었죠

두 변경 모두 생성 프롬프트가 아니라 리뷰 프롬프트에 있어요. 이게 핵심 아키텍처 결정이에요. 생성 단계는 스타일 가이드를 문맥으로 받아요——퍼스널리티의 풍미가 필요하니까요. 리뷰 단계는 규칙을 강제 명령으로 받아요——컴플라이언스 권한이 필요하니까요.


추출의 세부사항 (이게 7개 언어에서 작동하는 이유)

저는 언어별 구문 목록을 손으로 관리하고 싶지 않았어요. 호스트 프로필을 수정할 때마다 리뷰 프롬프트도 업데이트해야 한다면——그건 솔로 개발자의 제품을 죽이는 종류의 드리프트예요.

대신, 리뷰 시점에 호스트 프로필을 파싱하는 작은 추출 함수를 작성했어요. 각 로케일의 프로필에는 표준 레이블이 붙은 입버릇 섹션이 있어요:

  • 베트남어: Câu cửa miệng:
  • 일본어: 口癖:
  • 스페인어: Latiguillos:
  • 프랑스어: Phrases fétiches:
  • 한국어: 입버릇:
  • 중국어: 口头禅:

이 함수는 그 레이블을 찾고, 그 뒤에 오는 인용부호로 감싸진 문자열을 추출해서, 강제 가능한 제한으로 포맷해요:

@staticmethod
def _extract_catchphrase_limits(host_profiles: str) -> str:
    patterns = [
        (r'Câu cửa miệng:?', r'"([^"]+)"'),       # Vietnamese
        (r'口癖:?',             r'「([^」]+)」'),    # Japanese
        (r'Latiguillos:?',      r'"([^"]+)"'),       # Spanish
        # ... etc for all 7 languages
    ]
    limits = []
    for marker_pattern, quote_pattern in patterns:
        match = re.search(marker_pattern, host_profiles)
        if match:
            phrases = re.findall(quote_pattern, host_profiles[match.end():])
            for phrase in phrases:
                limits.append(f'   - "{phrase}" — max 2x')
    return "\n".join(limits)

이제 호스트 프로필을 편집해서 입버릇을 바꾸면, 리뷰 프롬프트가 자동으로 업데이트돼요. 드리프트 없음. 잊힌 언어 없음.


이게 실제로 고친 것 (숫자로)

수정 전과 수정 후에 생성된 에피소드 간의 변화는 이래요:

지표수정 전수정 후
"Trời ơi" 횟수 (할당량: 최대 4회)13회vi에서는 아직 미테스트, 하지만 ja에서는 모든 입버릇 4개가 할당량 내
"Có một lý do sâu hơn" (할당량: 최대 2회)5회동등한 ja 테스트에서 할당량 내
조작된 통계 (xG, 점유율, 포메이션 폭)4개의 조작된 숫자조작된 숫자 제로
동일 패턴의 세그먼트 오프닝6/6 동일ja에서는 여전히 5/5 — 아래 참조
베트남/일본 연결성 유지1/4만 생존 (리뷰가 3개 삭제)11/11 생존
호스트 목소리 혼재양쪽이 서로의 멘트 사용명확히 분리

입버릇 할당량, 조작 방지 규칙, 로컬라이제이션 보존은 모두 작동했어요. 호스트 프로필이 이제 각 호스트의 개성을 충분히 명확하게 지시해서, LLM이 더 이상 누가 무슨 말을 해야 하는지 헷갈리지 않아요.


아직 깨진 것

한 가지 규칙은 여전히 실패하고 있어요: 구조적 다양성. 대화 가이드는 "5개 세그먼트 중 최소 2개는 다른 구조적 진입을 사용해야 한다"고 말해요. 수정 후에도, 일본어 에피소드는 모든 세그먼트를 동일하게 열었어요: "いやー、健一さん!" —— 한 에피소드에 다섯 번.

이건 프롬프트 강제의 문제가 아니에요. 아키텍처의 문제예요.

생성 단계는 각 세그먼트를 독립적으로 생성해요. 세그먼트 3은 세그먼트 1과 2가 감정 호스트의 입버릇으로 시작했다는 걸 모르죠. 각 세그먼트는 동일한 대화 가이드로 격리된 상태에서 생성되기 때문에, 각각 동일한 "감정 호스트로 시작하자"는 결정에 도달해요.

리뷰 단계는 이걸 잡을 수 있어야 해요——전체 스크립트를 한 번에 보니까요. 그리고 오프닝을 다양화하라는 명시적인 강제 언어도 추가했어요. 하지만 리뷰를 실행하는 Gemini 3.5 Flash는 입버릇을 자르거나 조작된 주장을 부드럽게 하는 것보다 세그먼트 오프닝을 재구성하는 게 확실히 더 어려운 모양이에요. 오프닝 다양성은 단순히 다듬거나 제거하는 게 아니라 콘텐츠의 일부를 재생성해야 하니까요. 이건 모델에게 더 무거운 작업이에요.

아직 이 문제를 작업 중이에요——아마 더 뛰어난 리뷰 모델을 쓰거나, 세그먼트가 오프닝 상태를 공유하는 파이프라인 재구조화가 필요할 거예요. 더 넓은 포인트는: 프롬프트 강제로는 아키텍처 문제를 고칠 수 없다는 거예요. 파이프라인 구조가 규칙을 지키는 걸 불가능하게 만든다면, 프롬프트에 "CRITICAL"이나 "MANDATORY"를 아무리 써도 구원받지 못해요.


프레임워크: 모든 규칙에 대한 세 가지 질문

이 경험 이후로, 이제 모든 시스템 프롬프트를 작성할 때 세 가지 질문으로 감사해요:

1. 이 규칙은 어디에 위치하는가——생성인가 리뷰인가? 퍼스널리티와 톤에 관한 규칙은 생성에 속해요. 컴플라이언스, 일관성, 구조에 관한 규칙은 리뷰에 속해요. 둘 다 걸치는 규칙(입버릇처럼——퍼스널리티지만 제한이 필요)은 양쪽에 둬요.

2. 이 규칙은 컨텍스트로 쓰였는가, 명령으로 쓰였는가? "여기 입버릇입니다"는 컨텍스트예요. "최대 각 2회"는 명령이에요. 같은 규칙에 둘 다 필요해요——생성 단계는 입버릇이 무엇인지 알아야 하고, 리뷰 단계는 그게 얼마나 자주 나오는지 강제해야 하니까요.

3. 파이프라인이 실제로 이 규칙을 따를 수 있는가? 세그먼트별 생성은 세그먼트 간 다양성을 강제할 수 없어요. 어떤 프롬프트도 이걸 고칠 수 없어요. 규칙을 리뷰 단계로 옮기거나(전체 스크립트가 보이니까), 세그먼트가 상태를 공유하도록 파이프라인을 재구조화해야 해요.

이 프레임워크는 팟캐스트 생성에만 해당되는 게 아니에요. 콘텐츠 생성, 코드 리뷰, 문서 요약 등 생성과 품질 관리 패스가 분리된 모든 멀티스텝 LLM 파이프라인에 적용돼요.


저는 아직 이걸 반복하고 있지만, 만약 첫 멀티스텝 LLM 파이프라인을 만들고 있다면: 최소 두 단계로 나누세요. 첫 번째 단계는 퍼스널리티와 자유로움을 가지고 생성해요——문맥, 예시, 톤 가이드를 주세요. 두 번째 단계는 정밀함과 강제력을 가지고 리뷰해요——명시적인 제한, 컴플라이언스 체크, 그리고 변경할 권한을 주세요.

생성 단계는 크리에이티브 협력자에게 브리핑하는 느낌이어야 해요. 리뷰 단계는 편집자에게 체크리스트를 건네는 느낌이어야 해요.

그리고 두 가지를 다 했는데도 규칙이 계속 무시된다면, 먼저 아키텍처 질문을 해보세요. 프롬프트의 문제인가, 파이프라인의 문제인가?


자주 묻는 질문

단일 프롬프트 설정(파이프라인 없음)에도 적용되나요?

네, 하지만 제한적으로요. 단일 프롬프트에서는 같은 모델에게 창의적이면서도 동시에 컴플라이언트하라고 요구하게 돼요——둘이 충돌할 때는 항상 창의성이 컴플라이언스를 이겨요. 단일 프롬프트에서도 지시사항을 "컨텍스트" 섹션과 명시적인 "제약" 섹션으로 나누는 건 도움이 돼요. 하지만 한계는 있어요. 서로 다른 지시사항을 가진 두 번의 분리된 호출이 거의 항상 한 번을 능가해요.

왜 그냥 생성 프롬프트의 규칙을 더 강하게 쓰지 않았나요?

해봤어요. "MANDATORY", "CRITICAL", "NEVER" 그리고 대문자로 다 써넣었어요. 생성 단계에는 경쟁하는 요구사항이 너무 많아요——매력적이고, 자연스럽게 들리고, 다양하고, 로컬에 진정성 있고, 컴플라이언트하기까지——이 모든 걸 동시에 하려고 해요. 모델이 "흥분한 베트남 축구팬처럼 들리기"와 "'Trời ơi'는 두 번만" 사이에서 선택해야 할 때, 퍼스널리티가 항상 이겨요. 리뷰 단계에는 그런 충돌이 없어요——유일한 일은 강제니까요.

리뷰 단계에 어떤 모델을 사용하나요?

Gemini 3.5 Flash(비용 때문에——리뷰는 7개 언어 모든 에피소드에 실행돼요). 입버릇 자르기나 조작된 통계 부드럽게 하기 같은 단순한 강제는 잘 처리해요. 세그먼트 오프닝 재구성처럼 콘텐츠 재생성이 필요한 작업에는 어려움을 겪어요. 더 강력한 모델이 도움이 되겠지만, 아직 비용을 정당화하지 못했어요.


저는 DIALØGUE(다이얼로그)——AI 팟캐스트 플랫폼——를 저녁과 주말에 혼자 만들고 있어요. 입버릇 추출과 리뷰 프롬프트 변경의 코드는 공개되어 있어요——전체 시스템 프롬프트와 강제 로직은 팟캐스트 엔진 리포지토리에서 읽을 수 있어요. 그 과정에서 배운 걸 여기에 쓰고 있어요.

비슷한 문제——읽히지만 강제되지 않는 규칙——를 겪은 적이 있다면 궁금해요: 그 규칙은 무엇이었고, 파이프라인의 어디에 있었나요?

여기까지입니다.

감사합니다, Chandler