공통_룰 프롬프트 변수가 활동 지침을 무효화한 원인과 v3 수정 원인 확정 수정안 검증됨 운영 반영 대기

마지막 업데이트 2026-09-12

쉬운 설명 한 장 보기 · 사전지식 없이 읽는 요약

카테고리 bugs 2026-08-26 대상 프롬프트 변수 공통_룰 (ppi-prompt-variable-prod) 코드 변경 없음

결론

특정 문장 하나가 아니라, 공통 변수가 활동 규칙보다 뒤에 붙으면서 "다른 모든 규칙보다 우선"을 선언하고, 활동이 시키는 동작을 금지하는 문장을 함께 담고 있었던 것이 원인이다. 여기에 활동과 무관한 참조 목록(잼민백과사전 4,788 tok)까지 더해져 규칙 65개가 서로 충돌했고, 모델이 충돌 해소에 실패해 활동 지침 전반을 무시했다.

토큰 한도 초과는 아니다. 확장 후 시스템 프롬프트는 16,969 tok으로 gpt-realtime 컨텍스트(32,768 tok)의 52% 수준이고, 전달 경로에도 절단 지점이 없다.

증상

인과 흐름

#{공통_룰}이 활동 규칙 뒤(Conversation Flow 직전)에 삽입
  → 공통_룰 안에 "위 NEVER ECHO의 모든 항목은 다른 모든 규칙보다 우선해" 선언
  → 그 아래 4개 규칙이 활동이 시키는 동작을 금지
      · 상대가 말한 단어 금지  → 활동의 "받기(되짚기)" 불가
      · 숫자 절대 금지        → "하나 둘 셋" 고정 멘트 불가
      · 이전 맥락 참고 금지    → Context·STEP·턴 수 추적 불가
      · cs<0.86이면 내용 언급 금지 → 아동 STT 저신뢰 구간에서 알맹이 소실
  → 여기에 잼민백과사전 4,788 tok(전체 29%)이 규칙 밀도를 희석
  → MUST/NEVER 65개가 상호 모순 → 모델이 활동 지침 전반을 무시

측정 — 길이는 원인이 아니다

항목
확장 후 시스템 프롬프트26,048자 / 16,969 tok (o200k_base)
공통_룰 기여분5,392자 / 3,136 tok (전체의 19%)
잼민백과사전(26.06)6,642자 / 4,788 tok (전체의 29%)
gpt-realtime 컨텍스트32,768 tok → 52% 사용, 한도 미달
전달 경로 상한Redis 스냅샷 1MB (livekit-session-config-store.ts:6,193), 실제 modelConfig ~55KB

절단이 있었다면 instructions 키가 통째로 빠져 agent가 범용 어시스턴트 프롬프트로 폴백한다(apps/livekit-agent/agent.py:2875). 그 경우 캐릭터 자체가 사라지는데, 실제 증상은 캐릭터는 유지되고 규칙만 안 지켜지는 형태였다.

확인 방법: 로컬 dev에서 변수 치환 완료본을 파일로 덤프해 실제 입력을 직접 확인했다 (apps/web/lib/dev-instruction-dump.ts, dev 전용·로컬 적용).

원인 문장

공통_룰 (5줄)

원문막는 활동 동작
위 NEVER ECHO의 모든 항목은 다른 모든 규칙보다 우선해활동 규칙 전체를 덮어쓰는 스위치. 아래 4개를 강제 조항으로 승격
상대가 최근에 말한 단어/표현은 응답에 넣지 마① 받기: 상대 말 되짚기(상대가 말한 단어 넣기) — 활동의 핵심 턴 유형
숫자가 있으면 절대 쓰지 말고{{돌리기_요청}} "하나 둘 셋 해줘!", 대신 세기 등 고정 멘트
NIR: 이전 대화 맥락은 참고하지 마Context(지난 만남·앨범·1·2번 질문) + STEP 순서·6턴 카운트
cs<0.86이면 내용을 일절 언급하지 마그 턴에 하기로 된 받기·장면·고정 멘트

뉴핑퐁세계관 (1곳)

### 김하랑 (너) — 하랑 캐릭터용으로 만든 변수를 우주 활동에도 물려 써서, 우주 프롬프트가 "너 = 김하랑"이라고 선언한다. ## 모카에 대해가 활동 Context와 중복해 두 번 나오는 것도 같은 이유.

기타 충돌

검증 — 세 번의 실험

버전크기MUST/NEVER결과
원본16,969 tok65깨짐
최소수정 (공통_룰 5줄 + 정체성 1곳)16,969 tok65여전히 깨짐
재작성 (분량 절반·충돌 제거·우선순위 명시)8,635 tok22안 깨짐
2번이 실패한 것이 핵심이다. 문제 문장 몇 개를 고쳐도 나머지 규칙 더미가 그대로면 동일하게 무시된다. 원인은 개별 문장이 아니라 규칙 밀도·충돌 구조다.

공통_룰 v3 수정 내역

3,136 tok → 1,872 tok (60%). UT·UA·CR·NIR·OC·화자·진행자 프리셋은 모두 유지하고, 중복·점수계산·장황한 예시만 압축했다.

원본v3
"NEVER ECHO가 다른 모든 규칙보다 우선"맨 앞 ## 이 규칙의 자리 — 활동 프롬프트가 언제나 우선
최근에 말한 단어/표현 금지문장째 따라 하기 금지 + 받기·되짚기 턴 단어 1개 예외
숫자 절대 금지금지 + 고정 멘트·"하나 둘 셋" 세기 예외 명시
"이전 대화 맥락은 참고하지 마"삭제. "Context·STEP·턴 수는 계속 기억한다"로 대체
cs 0.86 점수 계산 → 내용 일절 언급 금지점수 계산 삭제. "안 들린 말은 언급하지 말되 그 턴에 하기로 된 건 그대로"
'대화종료' → {{종료}}고정 인사말로 대체 (활동의 {{종료}}가 마무리 인사가 아닐 수 있음)
# Reference Pronunciations 최상위 heading## 숫자 말하기로 강등 — 활동 섹션 구조 유지
@{아동나이} 하드코딩"네 나이는 활동 프롬프트 값만 쓴다" — 나이 충돌 제거
"그렇구나~" 지시 ↔ "그렇구나 금지" 자기모순"아 그래?"로 통일
적용 방법: 프롬프트 변수에 v3를 등록하고, 활동에서 # Instructions & Rules 바로 아래(활동 규칙보다 앞)에 배치한다. 기존 공통_룰은 다른 활동들이 공유하므로 새 변수로 A/B 하는 것이 안전하다.

재발 방지 규칙

  1. 프롬프트 앞에 # 우선순위 블록을 두고, 어떤 공통 변수도 활동의 STEP·고정 멘트를 이기지 못하게 한다
  2. 공통 변수에 "다른 모든 규칙보다 우선" 같은 절대 선언을 넣지 않는다
  3. 공통 변수는 캐릭터 중립으로 쓴다 — (너) 같은 정체성 표기, 특정 나이 하드코딩 금지
  4. 활동에서 안 쓰이는 참조 목록(간식·포켓몬·아이돌 등)은 붙이지 않는다. 규칙 대비 잡음 비율이 준수율을 좌우한다
  5. 공통 변수가 활동 규칙을 금지할 소지가 있으면 예외 문구를 같은 줄에 명시한다 ("단, 활동이 지시한 턴에서는 …")

미확정

관련 문서