선택오답 활동 — 대화시작 고정 멘트 어긋남 원인 분석
마지막 업데이트 2026-10-14
원본 사건 + 운영 재현 2회원인 일부 유력 · 수정 미적용
판정
확정: 올바른 선택오답 활동과 지침이 적용됐지만, 대화시작에 대한 첫 AI 응답이 지정 멘트와 달랐다. 원본 사건에서는 멘트가 누락됐고, 두 운영 재현에서는 각각 앞말 추가와 문구 재작성이 발생했다.
추가 멘트의 설명: #{상대발화수용_흐름진행규칙}은 고정 발화 턴에도 예외 없이 먼저 반응한 뒤 지정 멘트를 붙이라고 요구한다. 대화시작을 일반 사용자 입력으로 보내는 현재 경로에서 이 지시는 “지정 멘트만 말한다”는 기대와 충돌한다.
유력: 원문 복사 지시, 선행 반응 지시, 어절·문장 제한이 충돌하고, 첫 턴의 원문 출력 보장이 없어 모델이 다른 형태의 응답을 생성한다. 재작성에 어느 규칙이 결정적으로 작용했는지는 미확정이다.
1. 문제와 기대 결과
앞선 주도_s2e01_중재_04_선택형_상황연습에서 아동이 2번 오답을 선택한 뒤, 주도_s2e01_중재_05_선택오답의 “오답 선택” 스텝에 진입한다. 자동 전송된 대화시작에 대해 아래 멘트만 원문 그대로 발화하는 것이 기대 결과다.
어어? 근데 이 말엔 따지기 가시가 박혀 있는거 같지 않아? 물론 맞는 말이긴 한데, 잘못을 콕콕 따지는 말이라 진우 기분이 상할 수 있어!
원본 활동 ID: activity-729a5901-f85d-4856-871b-cde150ec08a4. 별도 백로그 번호는 미지정이다. 운영 재현은 같은 콘텐츠를 복제한 테스트 활동(…1e78ae88)에서 진행했다.
지정 멘트는 공백 기준 23어절, 78자다. 문장부호 기준으로 어어? / 근데 … 않아? / 물론 … 있어!의 3문장이다. 따라서 “최대 3문장”은 넘지 않지만, 활동의 별도 “두 문장 이내” 지시와는 충돌한다.
2. 원본 사건과 재현 결과
아래 시각은 모두 KST다. 원본 JSON의 epoch 밀리초와 Chrome 콘솔의 UTC 시각을 한국시간으로 변환했다.
| 사례 | 시각 | 직접 확인한 사실 |
|---|---|---|
| 10/12 원본 | 19:11:26.410 | choiceIndex:1 → targetIndex:17. 정확한 선택오답 활동으로 분기. |
| 10/12 원본 | 19:11:26.425–27.849 | 이전 세션 종료 → 새 LiveKit 세션 생성. 에이전트 지침 SHA-256이 첨부 프롬프트의 참여자 변수 치환 결과와 정확히 일치. |
| 10/12 원본 | 19:11:27.853 | 대화시작 전송. 첫 발화 한국어 유도 꺼짐, 추가 응답 지침 길이 0. |
| 10/12 원본 | 19:11:30.630 | 첫 응답: “오! 시작해보자! 준비됐어? 뭐든 말해봐!” 지정 멘트 누락. |
| 10/12 원본 | 19:11:46.832 | 첨부 대화 화면에서 아동의 오답 음성 발화 이후, 같은 세션의 response-2가 지정 멘트 78자를 정확히 생성. |
| 10/13 재현 1 | 20:18:59.563 | 새 세션에 대화시작 전송. responseInstructionsLength:0. |
| 10/13 재현 1 | 20:19:03.193 | 첫 응답: 11어절 추가 반응 + 지정 멘트 23어절 = 총 34어절. 고정 멘트 포함으로 자동 종료 매칭. |
| 10/13 재현 2 | 20:20:41.628 | 같은 선택오답 콘텐츠에 새 세션 생성. 지침 SHA-256은 재현 1과 동일, 추가 응답 지침 없음. |
| 10/13 재현 2 | 20:20:44.661 | 첫 응답을 21어절로 재작성. 지정 원문 누락, 이 응답의 자동 종료 매칭 이벤트 없음. |
| 10/13 재현 2 | 20:21:00.103–12.553 | 같은 세션의 response-2가 지정 원문 23어절을 생성. 이후 자동 종료·다음 활동 이동. 이 두 번째 응답의 정확한 입력 내용은 수집 콘솔에서 확인하지 못함. |
재현 1 — 앞말 추가
오, 시작하는 거야? 좋아! 근데 방금 말한 거 한번 같이 살펴볼까? 어어? 근데 이 말엔 따지기 가시가 박혀 있는거 같지 않아? 물론 맞는 말이긴 한데, 잘못을 콕콕 따지는 말이라 진우 기분이 상할 수 있어!
재현 2 — 원문 재작성
헉, 방금 고른 말! 뭔가 따지는 느낌이 살짝 있었던 것 같아. 물론 틀린 건 아닌데, 진우가 기분 나쁠 수도 있지 않을까?
재현 1·2의 instructionSha256은 모두 a286714d6cc5bbf2f549c2672f6d85d44e0ae3fa748048484f90724ca6a0a55b, instructionCharCount:11885, instructionBytes:25098이다. 다른 참여자를 사용한 원본 사건의 해시와는 구분한다.
3. 실제 프롬프트에서 확인한 충돌
운영 ppi-activity2-prod의 원본 활동과 ppi-prompt-variable-prod의 변수 정의를 읽기 전용 조회했다.
| 정의 위치 | 실제 지시 | 문제와 해석 |
|---|---|---|
활동 Conversation Flow | “MUST: Variables의 시작 값을 그대로 복사해서 말한다.” | 원문 복사를 요구하지만, 첫 입력 대화시작의 별도 처리나 추가 문장 금지는 명시하지 않음. |
상대발화수용_흐름진행규칙 | “지정된 발화가 있는 턴 … 에도 예외 없이, 상대의 직전 발화에 먼저 유의미하게 반응한 뒤 그 발화를 이어붙인다.” | 추가 멘트를 요구. 활동의 Flow 서문도 이 규칙을 대전제로 삼음. 재현 1의 추가 반응과 일치. |
티키_Variety / Length | “말할 때는 최대 3문장, 총 20어절 이내로 말해.” | 지정 원문 자체가 23어절. 길이 지시와 원문 복사를 동시에 만족할 수 없음. |
활동 Tone | “반말, 느낌표를 자주 쓰는 밝은 텐션. 두 문장 이내.” | 문장부호 기준 3문장인 지정 원문과 충돌. |
티키_Variety / Variety | “매번 다른 표현으로 응답해.”와 “Variables 변수값은 지정 멘트만 원본 그대로 써.”가 공존 | 원문 예외도 존재하므로 표현 다양화만을 단독 원인으로 단정할 수 없음. 길이 제한에는 해당 예외가 없음. |
티키_공통_룰 | 대화종료의 원문 출력 규칙은 있지만 대화시작의 대응 규칙은 없음. | 시작 신호가 일반 대화 입력으로 해석되지 않도록 하는 별도 규칙이 빠져 있음. |
4. 영향을 받은 코드 경로
오답 선택 → 선택오답 활동 분기 → 이전 세션 종료 → 새 LiveKit 세션 → 서버 buildActivityInstruction: 활동 + 공통 변수 + 참여자 변수 치환 → agent_prompt_state: 적용된 지침의 길이·SHA-256 보고 → 자동 시작: startMent 또는 기본 대화시작 텍스트 → 한국어 유도 꺼짐: responseInstructions 없음 → ppi.user_text_message → generate_reply(user_input="대화시작") → AI가 텍스트 생성 → 음성 출력·transcript 수신 → 자동 종료 문구 확인
| PPI 저장소 상대 경로 | 확인한 역할 |
|---|---|
apps/web/entities/guest-session/model/use-ai-session.ts | startMentRef.current || AUTO_START_MESSAGE로 시작 입력 선택, resolveAutoStartResponseInstructions 결과와 함께 텍스트 전송. |
apps/web/entities/guest-session/lib/auto-start-message.ts | guideEnabled:false이면 즉시 responseInstructions:undefined 반환. 지정 시작 문구를 추가 지침으로 만드는 기능이 한국어 유도 옵션에 묶여 있음. |
apps/web/entities/guest-session/lib/session-start/create-voice-session.ts | LiveKit 경로에서 사용자·활동 식별자로 세션 생성 요청. 서버가 활동을 다시 조회하고 지침 구성. |
apps/web/app/api/livekit/call/route.tsapps/web/lib/openai-create-call.ts | buildActivityInstruction으로 지침 구성 후 세션 설정에 전달. |
apps/livekit-agent/agent.py | _attach_ppi_text_transport에서 텍스트를 user_input으로 전달. 추가 지침이 있을 때만 instructions 인자 포함. 이 수신 분기에는 지정 원문 강제 출력이 없음. |
자동 종료 문구 매칭은 이미 생성된 응답을 검사한다. 지정 문구가 포함된 재현 1은 추가 멘트가 있어도 진행됐으므로, 자동 종료 성공은 첫 멘트가 정확했다는 검증이 아니다.
5. 검증한 것과 배제·미확정 사항
- 분기 정상: 오답 선택의
choiceIndex:1이 선택오답 활동을 가리켰다. - 새 세션 확인: 이전 세션을 종료하고 새 에이전트 세션의
response-1이 생성됐다. 이전 활동 응답 혼입을 뒷받침하지 않는다. - 지침 전달 정상: 원본 사건의 지침 해시가 첨부 확장 프롬프트의 참여자 변수 치환 결과와 정확히 일치한다. 원본 값은
11879자 / 25100바이트 / ff16f67d2c760c2a6fd223a043cf0e73f98f7382c82b6134a50a1a4cb6ac4c52다. - 동일 지침에서도 두 형태의 실패: 오늘 두 재현의 해시가 동일했으므로, 지침 내용 변경으로 결과 차이를 설명할 수 없다.
- 관련 단위 테스트:
pnpm exec vitest run entities/guest-session/lib/auto-start-message.test.ts를apps/web에서 실행, 5개 통과. 옵션 꺼짐 시 추가 지침이 없다는 현재 동작을 검증한다. 모델의 원문 준수율을 검증하는 테스트는 아니다. - 미확정: 길이 제한·선행 반응·표현 다양화 각각의 기여도와 모델 내부 판단. 변수 제거 비교 실험 및 40어절 시작 멘트 실험은 수행하지 않았다.
- 관측 한계: Chrome 콘솔의 생성 transcript와 자동 종료 이벤트를 기준으로 판정했다. 별도 오디오 캡처로 실제 스피커 출력을 검증하지 않았다.
검토한 애플리케이션 코드 기준: 0d36d2c66f6dc9604a45bf402dce44a52e72a335. 운영 배포 코드 SHA는 별도로 연결하지 않았으며, 해당 경로의 실행은 수집된 콘솔 이벤트로 확인했다.
6. 가장 빠른 확인 방법 — 테스트 사본에서 두 변수 제거
빠른 진단: 해당 활동의 테스트 사본에서 #{상대발화수용_흐름진행규칙}과 #{티키_Variety} 참조를 제거하고, 매번 새 AI 세션을 생성해 오답 선택 → 대화시작을 여러 번 반복한다. 지정 멘트의 원문 일치 여부, 추가 문장 여부, 자동 종료를 각각 확인한다.
Tone: 두 문장 이내 지시는 남으므로, 실패해도 두 변수의 영향이 없다고 단정할 수 없다. 티키_Variety에는 언어·Response Guard·다양성 규칙도 들어 있으므로, 전체 삭제를 그대로 운영 수정안으로 삼지 않는다.| 비교 단계 | 확인 목적 |
|---|---|
| 원본 설정 | 같은 입력·멘트·참여자·모델로 기준 성공률 기록. |
| 두 변수 참조 제거 | 공통 지침 묶음이 고정 멘트 준수에 영향을 주는지 빠르게 확인. |
| 선행 반응 변수만 복구 | 추가 멘트·일반 시작 반응이 다시 생기는지 확인. |
| 다시 둘 다 제거한 뒤 Variety만 복구 | Length·Variety 묶음의 영향 분리. 원인 확인이 필요하면 Length만 바꾸는 추가 실험 수행. |
기록 항목: 지침 SHA-256, 시작 입력, 추가 응답 지침 유무, 첫 응답 원문, 어절 수, 원문 일치, 자동 종료 이벤트. 여러 회 반복의 실패·성공 횟수를 함께 남긴다.
7. 수정 방향과 남은 작업 — 아직 미적용
- 활동의 시작 턴 계약 명시:
대화시작을 운영 신호로 정의하고, 지정 멘트만 원문 그대로 출력하도록 한다. 이 턴은 선행 반응·길이·문장 수·표현 다양화 지시보다 우선하도록 명시한다. - 공통 지침의 예외 정리: 고정 발화 턴에는 선행 반응과 길이 제한을 적용하지 않도록 한다. 공통 변수 전체를 수정하면 다른 활동에도 영향을 주므로 활동 사본에서 먼저 검증한다.
- 앱의 시작 멘트 전달 분리 검토: 원문 시작 멘트 지침을 한국어 유도 옵션과 독립시키는 개선 백로그를 검토한다. 모델 지시 강화만으로 항상 원문 일치가 보장되는 것은 아니다.
- 원문 일치가 필수인 경우: 모델에게 해당 문장을 생성시키는 대신 지정 문장을 직접 TTS에 전달하는 경로를 검토한다. 녹음·transcript·자동 종료·이후 대화 문맥까지 일관되게 처리하는 설계가 필요하다.
시작 턴 규칙 예시 (제안 · 미적용) 입력이 정확히 '대화시작'이면 Variables의 '시작' 값을 원문 그대로 한 번만 발화한다. 다른 반응, 인사, 질문, 설명을 앞뒤에 추가하지 않는다. 이 턴에는 선행 반응, 어절·문장 수 제한, 표현 다양화 규칙을 적용하지 않는다. 시작 턴에 대해서는 이 규칙이 해당 공통 지시보다 우선한다.
적용된 변경: 분석 문서와 문서 목록·관계 그래프·작업 로그만 추가한다. 애플리케이션 코드와 운영 활동·프롬프트 변수는 변경하지 않았다. 배포되는 것은 문서이며, 제품 수정은 아니다.
8. 근거와 관련 문서
- 사용자가 제공한 사건 메모, 아동·진행자 LogRocket JSON, 대화로그 화면
image.png,prompt-주도_s2e01_중재_05_선택오답-expanded.md. 사건 판단은 아동 로그와 대화 화면 중심이며, 진행자 로그에는 지침 원문이 없었다. - 2026-10-13 운영 Chrome 콘솔 캡처
ppi-startment-console-20261013.jsonl. 세션 구분: 재현 1…b13d:response-1, 재현 2…ab95:response-1/response-2. 원본 로그는 개인정보가 포함될 수 있어 문서 사이트에 업로드하지 않는다. - 운영 DynamoDB 읽기 전용 조회: 원본 활동의
instruction, 공통 변수티키_Variety·상대발화수용_흐름진행규칙·티키_공통_룰. - 공통 룰 프롬프트 변수가 활동 지침을 무효화한 선행 분석 — 유사한 규칙 충돌 사례. 그 문서의 수정안이 이번 활동에 검증·적용됐다는 의미는 아니다.
- 프롬프트 변수 시스템 — 치환과 코드 경로
- 문서 목록 · 문서 관계 그래프