Phase X-B-1,2 완료: Kei 유형 A/B 선택 + 검증기 완화

X-B-1: KEI_PROMPT에 유형 B 옵션 추가
- 유형 A: 기존 배경/본심/첨부/결론 (참조자료 있는 콘텐츠)
- 유형 B: 본심1(상단)+본심2(하단2분할)+결론 (본문만으로 구성)
- Kei가 콘텐츠 보고 A/B 선택, layout_template 필드로 반환
- 검증: 01번→A, 02번→B 정확히 선택

X-B-2: 검증기 완화
- 유형 A: 본심 필수 유지
- 유형 B: 결론(footer)만 필수, 자유 역할명 허용
- 섹션 수 차이 허용 확대 (유형 B: 4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 10:10:22 +09:00
parent c9677a69f8
commit bc7829b08b
3 changed files with 208 additions and 28 deletions

View File

@@ -171,22 +171,38 @@ def validate_stage_1a(
"instruction": f"weight 합이 1.0에 가깝도록 조정하라. 현재 합: {total_weight:.2f}",
})
# 본심 존재 + 본심 weight ≥ 0.3
core_info = page_struct.get("본심", {})
if not core_info or not isinstance(core_info, dict):
errors.append({
"severity": "RETRYABLE",
"field": "page_structure.본심",
"localization": "본심 역할이 page_structure에 없음",
"instruction": "page_structure에 본심 역할을 추가하라. 본심은 슬라이드의 핵심 콘텐츠이다.",
})
elif core_info.get("weight", 0) < 0.3:
errors.append({
"severity": "RETRYABLE",
"field": "page_structure.본심.weight",
"localization": f"본심 weight {core_info['weight']:.2f} < 0.3",
"instruction": "본심은 슬라이드의 핵심. weight 0.3 이상 필요.",
})
# 유형에 따른 구조 검증
layout_template = analysis.get("layout_template", "A")
if layout_template == "A":
# 유형 A: 본심 필수
core_info = page_struct.get("본심", {})
if not core_info or not isinstance(core_info, dict):
errors.append({
"severity": "RETRYABLE",
"field": "page_structure.본심",
"localization": "본심 역할이 page_structure에 없음",
"instruction": "page_structure에 본심 역할을 추가하라. 본심은 슬라이드의 핵심 콘텐츠이다.",
})
elif core_info.get("weight", 0) < 0.3:
errors.append({
"severity": "RETRYABLE",
"field": "page_structure.본심.weight",
"localization": f"본심 weight {core_info['weight']:.2f} < 0.3",
"instruction": "본심은 슬라이드의 핵심. weight 0.3 이상 필요.",
})
elif layout_template == "B":
# 유형 B: 결론(footer) 필수, 나머지 자유
has_footer = any(
isinstance(info, dict) and info.get("zone") == "footer"
for info in page_struct.values()
)
if not has_footer and "결론" not in page_struct:
errors.append({
"severity": "RETRYABLE",
"field": "page_structure.footer",
"localization": "결론(footer) 역할이 없음",
"instruction": "유형 B에서도 결론 역할(zone: footer)은 필수이다.",
})
# 필수 필드 검증
for t in topics:
@@ -226,7 +242,9 @@ def validate_stage_1a(
if clean_text:
# 원본 ## 섹션 수 vs topic 수 비교
original_sections = re.findall(r"^## .+$", clean_text, re.MULTILINE)
if len(original_sections) > 0 and abs(len(topics) - len(original_sections)) > 2:
# 유형 B에서는 하나의 섹션을 여러 꼭지로 나눌 수 있으므로 허용 폭 확대
max_diff = 4 if layout_template == "B" else 2
if len(original_sections) > 0 and abs(len(topics) - len(original_sections)) > max_diff:
errors.append({
"severity": "RETRYABLE",
"field": "topics",