Skip to content

#467 [Feat] 배틀 대본 붙여넣기 파서 (배틀·시나리오 자동 등록) - #468

Merged
si-zero merged 11 commits into
devfrom
feat/#467
Sep 11, 2026
Merged

#467 [Feat] 배틀 대본 붙여넣기 파서 (배틀·시나리오 자동 등록)#468
si-zero merged 11 commits into
devfrom
feat/#467

Conversation

@si-zero

@si-zero si-zero commented Sep 11, 2026

Copy link
Copy Markdown
Member

#️⃣ 연관된 이슈

📝 작업 내용

기획자가 구글독스에 쓴 배틀 대본을 어드민에 붙여넣으면 서버가 파싱해 배틀/시나리오 등록 페이로드를 만들어 돌려준다(DB 저장 X, 미리보기용). #455 인프라(Tone / philosopher_voice) 위에서 진행.

✨ Feat

내용 파일
파싱 결과 DTO (battlePayload / scenarioPayload / speakerNames / warnings) AdminBattleParseRequest, AdminBattleParseResponse, ParseWarning
대본 → 노드/메타데이터 분해 (헤더·섹션 정규화, 플랫 본문 자동 분할, 👉 [선택의 시간] A/B 옵션·발화자, [분기 …]분기_A/분기_B, 메타데이터 표) BattleScriptDocument, BattleScriptDocumentParser
줄별 톤 태그 추출(첫 번째만, text 에서 제거) + 효과 태그 표기 정규화 ScriptToneExtractor, TtsTextNormalizer#normalizeEffectTags
감정 톤·발화자 A/B LLM 배치 분류 (OpenAI 재사용, openai.emotion.model 기본 gpt-4o-mini, response_format: json_object, 실패 시 전부 NEUTRAL + warning) EmotionClassifier, OpenAiEmotionClassifier, application.yml
조립: 태그 이름→ID(TagRepository), 철학자→보이스(PhilosopherVoiceService), 성향 지표 12문자열 검증(ValueAxis), 철학자 키워드 10인 검증(PhilosopherType), 노드 autoNext 체인/분기 연결, voiceSettings 채움, POST /api/v1/admin/battles/parse BattleScriptParseService, AdminBattleService, AdminBattleController

🐛 Fix — 실제 기획자 대본 포맷 보정

샘플 외에 실제 대본(008 — 첫 데이트 국밥…)으로 검증하니 포맷이 꽤 달라 파서를 보정함:

실제 문서 표기 보정 내용 파일
헤더 008 — 제목 (분기 O/X 버전 없음) {라벨} — {제목} 도 허용. interactive본문에 선택/분기가 있으면 자동 승격 BattleScriptDocumentParser
사전 투표 + "유죄다 (플라톤)" vs "무죄다 (마르크스)" 파싱해서 OptionMeta.primarySpeaker 로 저장 → A/B 발화자 바인딩 힌트(선택의 시간 명시가 없을 때 LLM 앞단계로 사용) BattleScriptDocument, BattleScriptDocumentParser, BattleScriptParseService
1라운드: "부제" vs "부제" 라운드 헤더에 :부제 허용 BattleScriptDocumentParser
플라톤 (유죄): "…" 발화자명 뒤 (…) 주석 제거 → 플라톤 BattleScriptDocumentParser
대사 앞뒤 "…" 바깥 따옴표 제거 BattleScriptDocumentParser
👉 [선택의 시간: 당신의…] (콜론 포함) 콜론 있어도 선택 섹션으로 인식, A:/B: 옵션 줄만 예외 처리 BattleScriptDocumentParser
[분기 시나리오 A / B] + [A 선택 시] 플라톤: "…" 컨테이너 헤더 무시 + [A 선택 시] 인라인 프리픽스를 분기_A 노드 전환 신호로, 나머지는 대사로 BattleScriptDocumentParser
철학자 키워드 #플라톤 #칸트 #아리스토텔레스 (공백 구분) 공백·쉼표 모두 구분자로 BattleScriptDocumentParser

📌 공유 사항

  1. scenarioPayload.battleId 는 null 이다 — 배틀이 아직 없어서. 어드민이 battlePayload 로 배틀을 먼저 만들고 받은 id 를 채워 시나리오를 등록한다.
  2. 이미지(썸네일/옵션), targetDate, publishAt 은 문서에 없어 null. 미리보기에서 수동 입력. status 는 항상 PENDING.
  3. MISSING_VOICE / INCOMPLETE_SPEAKER_BINDINGblocking=true warning 이 있으면 발행 전에 미리보기에서 해결해야 한다.
  4. 감정 큐 하이브리드: 기획자가 쓴 톤 태그 우선, 빈 줄만 LLM. 효과 태그는 text 인라인 유지.
  5. CATEGORY 태그: 실제 대본의 관계·사랑 처럼 새 카테고리 문자열이 운영 DB tags 에 없으면 UNKNOWN_CATEGORY_TAG warning 으로 빠지고 미리보기에서 선택하게 된다.
  6. 설계 문서: 배틀_발행_시나리오_현행_vs_개선.md 2.4 / 2.5 / 2.6 / 4.

✅ 체크리스트

  • 컨벤션에 맞는 Type을 선택했나요?
  • 로컬에서 실행했을 때 에러가 발생하지 않나요?
  • Merge 하려는 브랜치가 올바르게 설정되어 있나요? (dev)

📸 스크린샷

샘플 3건(sample-linear.txt, sample-interactive.txt, sample-real-1.txt) 파싱 → BattleScriptDocumentParserTest(9) + BattleScriptParseServiceTest(8) 통과. Tone/ScriptToneExtractor/TtsTextNormalizer 단위 테스트 포함.

💬 리뷰 요구사항

  1. 문서 파서 정규식이 샘플 3건 + 실제 대본 1건 기준입니다. 더 다양한 포맷은 warnings 로 빠지고 미리보기에서 보정하는 구조인데, 이 안전망이 충분한지 봐주세요.
  2. LLM 호출은 openai.emotion.model 로 분리했고 실패해도 parse 는 성공합니다. 재시도/타임아웃(30s) 정책 의견 부탁드립니다.

@si-zero si-zero self-assigned this Sep 11, 2026
@si-zero si-zero added the ✨ Feat (기능 추가) 새로운 기능을 추가할 때 사용합니다. label Sep 11, 2026
@si-zero
si-zero merged commit 291918c into dev Sep 11, 2026
1 check passed
@si-zero
si-zero deleted the feat/#467 branch September 11, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feat (기능 추가) 새로운 기능을 추가할 때 사용합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant