Files
C.E.L_Slide_test2/IMPROVEMENT-PHASE-R-PRIME.md
kyeongmin 0e4b8c091c Phase S: Claude HTML 직접 생성 + 독립 검증 시스템 도입
블록 선택 방식(Phase P/Q/R) 폐기 → Claude Sonnet이 영역별 HTML 직접 생성.
생성-검증 분리: content_verifier.py로 텍스트 보존/금지 콘텐츠/구조를 코드 검증.

주요 변경:
- src/html_generator.py: 4개 프롬프트 템플릿(BG/CORE/SIDEBAR/FOOTER) + 영역별 Claude 호출
- src/content_verifier.py: L1 텍스트 보존, L2 금지 콘텐츠, L3 구조 검증 + 재시도 루프
- src/html_validator.py: 보안 검증(script/iframe 제거)
- src/renderer.py: render_slide_from_html() 추가, area div overflow:hidden
- scripts/test_phase_s.py: generate_with_retry() 통합, step2b_verification 결과 저장
- 배경 라이트 디자인(#f8fafc), 개조식 어미 변환, 축약 금지 규칙

다음 과제: 폰트 위계(핵심14>본문12>배경10-12>첨부9-11) + 동적 컨테이너 계산

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 08:37:05 +09:00

18 KiB

Phase R': 접근 C 기반 — 블록 CSS 참고 + AI 구조 결정

작성일: 2026-03-30 상태: 설계 선행: Phase P(20점), Q(블록 선택 개선), R(실패 — 기존 구조 위에 패치) 근거: C_reference.png + hybrid_simulation.png 시뮬레이션 검증


1. Phase P, Q, R에서 무엇이 문제였는가

P, Q, R 전부 같은 구조:

블록을 선택한다 → 그 블록의 슬롯에 텍스트를 채운다

이 구조의 근본 한계:

  • 블록이 구조를 결정한다. 콘텐츠가 아니라 블록 템플릿이 HTML 구조를 고정.
  • topic 1개 = 블록 1개. 합침/분리 불가.
  • 38개 고정 템플릿에 없는 구조(포함 관계, Before→After 등)는 표현 불가.
  • 콘텐츠 전달 의도(expression_hint)가 HTML 구조에 반영되지 않음.

Phase Q에서 블록 선택 정확도를 올렸고, Phase R에서 variant를 추가했지만, 근본 구조("블록 선택 → 슬롯 채우기")는 P = Q = R 동일. 결과물도 동일 수준.


2. 접근 C: 무엇이 달라지는가

핵심 전환

현재 (P=Q=R):
  블록이 구조를 결정 → 콘텐츠를 슬롯에 채움
  (블록 중심)

접근 C:
  콘텐츠가 구조를 결정 → 블록 CSS를 참고하여 HTML 생성
  (콘텐츠 중심)

"블록을 참고하여 만든다"의 정확한 의미

블록을 버리는 것이 아니다. 블록을 "선택"하는 것도 아니다.

현재 (블록 선택) 접근 C (블록 참고)
블록의 역할 HTML 구조를 결정하는 템플릿 CSS 스타일(색상, 폰트, 배경, radius)의 참고 자료
누가 구조를 결정 블록 템플릿 (Jinja2) AI가 콘텐츠 전달 의도를 보고 결정
topic 합침/분리 불가 (1 topic = 1 블록) 가능 (AI가 판단)
포함 관계 시각화 해당 블록이 없으면 불가 AI가 큰 박스 안에 카드를 넣는 구조를 직접 생성
CSS 일관성 블록 템플릿이 보장 디자인 토큰 + 블록 CSS 참고로 보장

C_reference.png에서 증명된 것

제가 수동으로 했던 판단:

  1. topic 1(문제제기) + topic 2(사례) → 1영역에 통합, dark-bullet-list의 CSS 색상 사용
  2. 사례 2건 → 가로 2열 카드, 같은 구조로 나란히
  3. topic 3(핵심) → DX 큰 박스 안에 GIS/BIM/DT 카드 = 포함 관계 시각화
  4. "BIM ≠ DX" → 별도 강조 박스 (topic에 없는 요소를 추가)
  5. sidebar 용어 → 풀 정의 + 출처, card-numbered의 CSS 스타일 참고

이 판단들을 AI가 하는 것이 접근 C.


3. 프로세스 설계

기존 프로세스 (P=Q=R)

1단계: Kei 분석 (topics, relation_type, expression_hint, page_structure)
1.5단계: Kei 컨셉 구체화 (source_data)
컨테이너 계산 (비중 → px)
프리셋 선택 (sidebar-right 등)

2단계: 블록 선택 (block_selector → catalog → Kei 선택)     ← 여기가 문제
3단계: 텍스트 채우기 (fill_candidates → 슬롯에 텍스트)      ← 여기가 문제

4단계: CSS 조정 + 렌더링
검증: Selenium 측정
품질 게이트: 비전 모델

접근 C 프로세스

1단계: Kei 분석 (동일)
1.5단계: Kei 컨셉 구체화 (동일)
컨테이너 계산 (동일)
프리셋 선택 (동일)

2-3단계 통합: AI가 HTML 구조를 직접 생성                    ← 여기가 바뀜
  입력: Kei 분석 결과 + 원본 텍스트 + 디자인 토큰 + 블록 CSS 참고 + 컨테이너 스펙
  출력: 각 컨테이너 영역의 HTML (슬라이드 body, sidebar, footer)

  AI가 결정하는 것:
    - 각 topic을 어떤 구조로 보여줄지 (불릿? 비교? 포함관계? 카드?)
    - topic을 합칠지 분리할지
    - 핵심 메시지를 별도 강조할지
    - 텍스트를 어떻게 배치할지

  AI가 참고하는 것:
    - 디자인 토큰 (CSS 변수 — 색상, 폰트, 간격)
    - 기존 블록의 CSS 패턴 (다크 배경, 카드 스타일, 배너 스타일 등)
    - 컨테이너 크기 (px)
    - expression_hint ("포함 관계 시각화", "프로세스 변화")
    - 예시 슬라이드 (few-shot)

4단계: 렌더링 (AI가 생성한 HTML을 슬라이드 프레임에 삽입)
검증: Selenium 측정 (동일)
품질 게이트: 비전 모델 (동일)

변경되는 것 / 변경되지 않는 것

항목 변경 여부
1단계 Kei 분석 변경 없음
1.5단계 컨셉 구체화 변경 없음
컨테이너 계산 (space_allocator) 변경 없음
프리셋 선택 변경 없음
2단계 블록 선택 (block_selector) 제거 — AI가 직접 구조 결정
3단계 슬롯 채우기 (fill_candidates) 제거 — AI가 HTML에 텍스트 직접 포함
4단계 CSS 조정 변경 — AI 생성 HTML을 프레임에 삽입
Selenium 측정 변경 없음
비전 모델 품질 게이트 변경 없음
slide-base.html 변경 없음
tokens.css, base.css 변경 없음

4. AI HTML 생성의 구체적 설계

4-1. AI에게 주는 입력

1. 원본 콘텐츠 (MDX 텍스트 전체)
2. Kei 분석 결과:
   - topics[] (id, title, purpose, relation_type, expression_hint, source_data)
   - page_structure (본심/배경/첨부/결론 비중)
   - core_message
3. 컨테이너 스펙:
   - 각 역할별 높이(px), 너비(px)
   - 프리셋 (sidebar-right, two-column 등)
4. 디자인 토큰 (tokens.css 전문)
5. 블록 CSS 패턴 참고 (주요 블록의 CSS만 발췌):
   - 다크 배경 패턴 (.block-dark-bullets의 색상/배경)
   - 카드 패턴 (.cid-card의 border/radius/padding)
   - 배너 패턴 (.block-banner-grad의 gradient)
   - 비교 패턴 (.block-comparison의 좌우 분할)
   - 테이블 패턴 (.block-table-striped의 헤더/행)
6. 예시 슬라이드 2-3개 (few-shot):
   - C_reference.png의 HTML
   - hybrid_simulation의 HTML
   - (다른 콘텐츠 예시)

4-2. AI에게 요구하는 출력

각 영역(body, sidebar, footer)의 HTML 조각.
슬라이드 프레임(slide-base.html)에 삽입할 수 있는 형태.

{
  "body_html": "<div class=\"container-배경\">...</div><div class=\"container-본심\">...</div>",
  "sidebar_html": "<div class=\"sidebar-label\">용어 정의</div><div class=\"def-item\">...</div>...",
  "footer_html": "<div class=\"block-banner-grad\"><div class=\"bg-text\">...</div></div>"
}

4-3. AI HTML 생성 규칙 (프롬프트에 포함)

## 규칙

1. 원본 텍스트를 그대로 사용한다 (자유도 15-20). 축약은 공간 부족 시에만.
2. 디자인 토큰(CSS 변수)만 사용한다. 하드코딩 색상/폰트 금지.
3. 블록 CSS 패턴을 참고하되, 구조는 콘텐츠에 맞게 자유롭게 구성한다.
4. 컨테이너 높이를 초과하지 않는다.
5. expression_hint를 반드시 반영한다:
   - "포함 관계" → 큰 박스 안에 작은 카드
   - "프로세스 변화" → Before→After 구조
   - "독립 사례 나열" → 가로 카드 비교
6. topic을 합치거나 분리할 수 있다 (page_structure의 역할 기준).
7. 핵심 메시지(core_message)는 별도 강조 요소로 만들 수 있다.

4-4. 품질 보장 (5중 방어)

Layer 1: 디자인 토큰 제약 (CSS 변수만 사용)
Layer 2: 컨테이너 크기 제약 (px 명시)
Layer 3: HTML 정화 (nh3 — script 태그 등 제거)
Layer 4: Selenium 측정 (overflow 감지)
Layer 5: 비전 모델 품질 게이트 (시각적 평가)

5. 구현 스텝

스텝 내용 파일 비고
R'-1 디자인 토큰 + 블록 CSS 패턴을 프롬프트용 텍스트로 추출 신규 src/design_tokens.py tokens.css + 주요 블록 CSS 발췌
R'-2 few-shot 예시 슬라이드 정리 (2-3개) data/examples/ C_reference.html, hybrid_simulation.html
R'-3 AI HTML 생성 함수 구현 신규 src/html_generator.py Kei 분석 + 토큰 + 예시 → HTML 생성
R'-4 pipeline.py 2-3단계를 html_generator로 교체 src/pipeline.py block_selector, fill_candidates 호출 제거
R'-5 렌더러에 AI 생성 HTML 삽입 함수 추가 src/renderer.py 기존 render_slide와 별도로 render_slide_from_html 추가
R'-6 HTML 정화 + 토큰 위반 검증 신규 src/html_validator.py nh3 + tinycss2
R'-7 테스트 (2개 콘텐츠로 검증) scripts/test_phase_r_prime.py DX 이해 + DX 목표

의존 관계

R'-1 (토큰 추출) ──→ R'-3 (HTML 생성)
R'-2 (예시 정리) ──→ R'-3
                     R'-3 ──→ R'-4 (파이프라인 교체) ──→ R'-7 (테스트)
                     R'-3 ──→ R'-5 (렌더러 추가)
R'-6 (검증) ←── 독립, R'-4와 병렬

6. pipeline.py 변경 상세 — 정확히 어디가 교체되는가

유지되는 코드 (1단계 ~ 컨테이너 계산: 줄 96~165)

# 줄 96~109: 1단계 Kei 분석 — 유지
analysis = await _retry_kei(classify_content, content)
_save_step(run_dir, "step1_analysis.json", analysis)

# 줄 111~120: 1.5단계 컨셉 구체화 — 유지
analysis = await refine_concepts(content, analysis)
_save_step(run_dir, "step1b_concepts.json", ...)

# 줄 122~140: 제목 중복 검증, 이미지 측정 — 유지

# 줄 142~164: 컨테이너 스펙 계산 — 유지
preset_name = select_preset(analysis)
preset = LAYOUT_PRESETS.get(preset_name, {})
container_specs = calculate_container_specs(...)
_save_step(run_dir, "step1c_containers.json", ...)

제거되는 코드 (2-3단계: 줄 166~339)

# 줄 166~207: Phase Q 블록 선택 — 전체 제거
#   block_selector.select_block_candidates()
#   select_fallback_candidates()
#   calculate_budgets_for_candidates()

# 줄 209~257: Kei 블록 선택 — 전체 제거
#   select_block_for_topics()
#   selected_blocks 딕셔너리 생성
#   finalize_block_specs()

# 줄 259~298: layout_concept 조립 — 전체 제거
#   final_blocks 리스트, sidebar label, 역할 순서 배치

# 줄 300~339: fill_candidates 호출 — 전체 제거
#   topic별 fill_candidates()
#   결과 검증

교체되는 코드 (R'-4에서 신규 작성)

# 2-3단계 통합: AI HTML 생성 (줄 166~ 교체)
yield {"event": "progress", "data": "2/5 슬라이드 HTML 생성 중..."}

from src.html_generator import generate_slide_html
from src.html_validator import validate_html

# AI가 HTML 직접 생성
generated = await generate_slide_html(
    content=content,
    analysis=analysis,
    container_specs=container_specs,
    preset=preset,
)

# HTML 정화 + 토큰 위반 검증
validated_html = validate_html(generated)

_save_step(run_dir, "step2_generated.json", {
    "body_html_length": len(generated.get("body_html", "")),
    "sidebar_html_length": len(generated.get("sidebar_html", "")),
    "footer_html_length": len(generated.get("footer_html", "")),
})

유지되는 코드 (4단계 이후: 줄 341~)

# 줄 341~350: 4단계 렌더링 — 유지하되 render_slide → render_slide_from_html로 변경
html = render_slide_from_html(generated, analysis, preset)
_save_step(run_dir, "step4_rendered.html", html)

# 줄 352~446: Selenium 측정, overflow 감지, 수학적 조정 — 유지
#   단, overflow 시 텍스트 압축(fill_content 호출, 줄 442)은 제거
#   대신 AI HTML 재생성 요청

# 줄 448~474: 비전 모델 품질 게이트 — 유지

# 줄 476~483: 이미지 삽입, final.html 저장 — 유지

_adjust_design (줄 490~589) — 제거 또는 변경

현재: layout_concept의 블록별 텍스트 양을 보고 CSS 변수 조정
R':  AI가 HTML 생성 시 이미 CSS를 포함하므로, 별도 CSS 조정 단계 불필요
     → 제거하거나, AI 생성 HTML에 대한 보조 조정으로 역할 축소

_review_balance, _apply_adjustments (줄 592~730) — 변경

현재: 블록 기반 layout_concept를 검수/조정
R':  AI 생성 HTML을 검수. 조정 시 block_selector/fill_candidates가 아닌 html_generator 재호출
     줄 729의 fill_content() 호출 → 제거

_build_overflow_context, _convert_kei_judgment (줄 733~800) — 변경

현재: layout_concept의 블록 데이터에서 overflow 컨텍스트 추출
R':  AI 생성 HTML에서 직접 추출하도록 변경

7. 다른 파일에 대한 영향 (충돌/회귀 체크)

영향 없음 (건드리지 않음)

파일 이유
src/kei_client.py — classify_content, refine_concepts 1단계에서 호출. R'에서 변경 없음
src/kei_client.py — vision_quality_gate 품질 게이트. R'에서 변경 없음
src/space_allocator.py — calculate_container_specs 컨테이너 계산. R'에서 변경 없음
src/design_director.py — select_preset, LAYOUT_PRESETS 프리셋 선택. R'에서 변경 없음
src/slide_measurer.py Selenium 측정. R'에서 변경 없음
src/config.py 설정. 변경 없음
src/sse_utils.py SSE 스트리밍. 변경 없음
src/image_utils.py 이미지 크기 측정. 변경 없음
src/main.py FastAPI 엔드포인트. generate_slide 호출은 동일
static/index.html 프론트엔드. 변경 없음
templates/slide-base.html 슬라이드 프레임. 변경 없음
static/tokens.css, static/base.css 디자인 토큰. 변경 없음 (프롬프트에서 읽기만 함)
templates/blocks/*.html 38개 블록. 변경 없음 (CSS 참고용으로만 유지)
templates/catalog.yaml 변경 없음 (프롬프트에서 CSS 패턴 인덱스로 참고만)

변경되는 파일

파일 변경 내용 기존 코드 영향
src/pipeline.py 줄 166~339 교체 (블록 선택+채우기 → html_generator 호출) 2-3단계만 교체, 나머지 유지
src/pipeline.py _adjust_design 제거 또는 축소 4단계 CSS 조정 불필요
src/pipeline.py _apply_adjustments에서 fill_content 호출 제거 overflow 시 html_generator 재호출
src/renderer.py render_slide_from_html() 신규 함수 추가 기존 render_slide() 변경 없음 (하위 호환)

신규 파일

파일 역할
src/html_generator.py 핵심 — Kei 분석 + 토큰 + 예시 → AI HTML 생성
src/design_tokens.py tokens.css + 블록 CSS 패턴을 프롬프트용 텍스트로 추출
src/html_validator.py nh3 정화 + CSS 토큰 위반 검증
data/examples/C_reference.html few-shot 예시 1
data/examples/hybrid_simulation.html few-shot 예시 2
scripts/test_phase_r_prime.py R' 테스트 스크립트

호출하면 안 되는 것 (자기 검증)

함수 위치 이유
select_block_candidates() block_selector.py 블록 선택 시스템 — R'에서 제거
select_fallback_candidates() block_selector.py 동일
select_block_for_topics() kei_client.py 블록 선택 AI — R'에서 제거
fill_candidates() content_editor.py 슬롯 채우기 — R'에서 제거
fill_content() content_editor.py 동일
finalize_block_specs() space_allocator.py 블록 스펙 — 블록 없으므로 불필요
calculate_budgets_for_candidates() space_allocator.py 블록 예산 — 블록 없으므로 불필요

호출해야 하는 것 (유지)

함수 위치 이유
classify_content() kei_client.py 1단계 Kei 분석
refine_concepts() kei_client.py 1.5단계 컨셉 구체화
calculate_container_specs() space_allocator.py 컨테이너 px 계산
select_preset() design_director.py 프리셋 선택
measure_rendered_heights() slide_measurer.py Selenium 측정
capture_slide_screenshot() slide_measurer.py 스크린샷 캡처
vision_quality_gate() kei_client.py 비전 모델 품질 게이트
generate_slide_html() html_generator.py (신규) AI HTML 생성
validate_html() html_validator.py (신규) HTML 정화+검증
render_slide_from_html() renderer.py (신규 함수) AI HTML → 슬라이드 프레임 삽입

8. 자기 검증 체크리스트

구현 시작 전 반드시 확인:

  • block_selector.py에서 블록을 "선택"하는 코드를 호출하고 있는가? → 호출하면 안 됨
  • fill_candidates/fill_content로 "슬롯에 텍스트를 채우는" 코드를 호출하고 있는가? → 호출하면 안 됨
  • finalize_block_specs/calculate_budgets_for_candidates를 호출하고 있는가? → 호출하면 안 됨
  • AI가 HTML 구조를 결정하고 있는가, 블록 템플릿이 구조를 결정하고 있는가? → AI가 결정해야 함
  • 기존 코드를 "수정"하는 것인가, "교체"하는 것인가? → 2-3단계는 교체
  • C_reference.png 수준의 결과가 나올 수 있는 구조인가? → topic 합침, 포함 관계, 핵심 메시지 분리가 가능해야 함
  • _adjust_design에서 블록 기반 로직을 사용하고 있는가? → 사용하면 안 됨
  • _apply_adjustments에서 fill_content를 호출하고 있는가? → 호출하면 안 됨

9. 기대 효과

지표 Phase R (실패) Phase R' (목표)
결과물 수준 34점 (블록 선택+슬롯 한계) C_reference.png 수준 (70점+)
구조 결정 주체 블록 템플릿 AI (콘텐츠 기반)
topic 합침/분리 불가 가능
포함 관계 시각화 해당 블록 없으면 불가 AI가 직접 구성
원본 텍스트 보존 편집자 의존 (축약됨) AI가 원본 직접 삽입
CSS 일관성 블록 템플릿 보장 디자인 토큰 + 블록 CSS 참고 + 검증

10. 회귀 방지 — Phase P=Q=R 반복 금지

이 Phase에서 절대 하면 안 되는 것

  1. block_selector.py의 select_block_candidates를 호출하면 안 됨 — 블록 "선택" 시스템 회귀
  2. content_editor.py의 fill_candidates/fill_content를 호출하면 안 됨 — "슬롯 채우기" 회귀
  3. catalog.yaml에서 블록을 매칭하면 안 됨 — 블록 매칭 회귀
  4. variant를 추가하면 안 됨 — Phase R의 실패 패턴 회귀
  5. "최소 변경"으로 기존 코드 위에 패치하면 안 됨 — P=Q=R 반복의 원인

이 Phase에서 반드시 해야 하는 것

  1. html_generator.py에서 AI가 HTML 구조를 직접 생성 — 콘텐츠가 구조를 결정
  2. 블록 CSS는 참고만 — "선택"이 아닌 "참고"
  3. pipeline.py 줄 166~339를 교체 — 패치가 아닌 교체
  4. C_reference.png와 동일 수준의 결과를 자동으로 생성 — 이것이 합격 기준