Files
C.E.L_Slide_test2/IMPROVEMENT-PROCESS-REVIEW.md
kyeongmin ffad1ba82a Phase I 실행 완료 + 프로세스 재설계 (Stage 2.5 → Stage 5)
Phase I: 전수 정합성 복구 + 넘침 처리 패러다임 전환 (14개 항목)
- I-14: SSE 유틸 공통 추출 (src/sse_utils.py 신규, 3개 파일 중복 제거)
- I-13: dead code 3건 삭제 (_call_anthropic_direct, _extract_sse_text x2) + import anthropic 제거
- I-1: STEP_B_PROMPT purpose 가이드 미존재 블록 3개 → 실존 블록 교체
- I-2: catalog.yaml not_for 13건 미존재 블록 참조 교체/제거
- I-12: BLOCK_SLOTS 주석 개수 수정 (cards 9, visuals 6, emphasis 10)
- I-10: INDEX.md 38개 동기화 (삭제된 8개 블록 행 제거)
- I-11: README.md 38개 동기화 (_legacy 제거, 트리/개수 정리)
- I-3: PURPOSE_FALLBACK 상수 + purpose 기반 미등록 블록 교체
- I-7: compare-pill-pair 단독 사용 금지 검증
- I-4: 38개 블록 전체에 slot_desc 추가
- I-5: 편집자 프롬프트에 slot_desc 전달 로직
- I-6: 제목 유사도 70% 초과 시 자동 교정
- I-9: 넘침 판단 Kei API 호출 (KEI_OVERFLOW_PROMPT, call_kei_overflow_judgment)
- I-8: 대형 콘텐츠 정보 Kei overflow 프롬프트에 포함

프로세스 재설계:
- Stage 2.5 제거 → Stage 5에서 Sonnet 감지 + Kei 판단 통합
- _review_balance() 확장: zone 예산 + overflow_detected action 추가
- Stage 5 루프에 Kei 넘침 판단 호출 통합
- _apply_adjustments()에 kei_trim/kei_restructure action 추가
- _build_overflow_context(), _convert_kei_judgment() 헬퍼 함수 추가
- DOWNGRADE_MAP은 Kei API 실패 시 비상용으로만 잔존

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:06:21 +09:00

18 KiB

파이프라인 프로세스 재검토 — 검증 시점 문제 진단

Phase I 실행 완료 후 실제 구동 중 발견된 프로세스 구조 문제. Phase I의 코드 변경(14개 항목)은 유효하나, 검증이 배치된 시점이 부적절.


현재 프로세스 흐름 (as-is)

[1단계] Kei 실장 — 콘텐츠 분석 + 스토리라인 설계
  ├ 1-A: 꼭지 추출 (Kei API)
  ├ 1-B: 컨셉 구체화 (Kei API)
  ├ 제목 중복 검증 (코드)
  └ 이미지 크기 측정 (Pillow)
      ↓
[2단계] 디자인 팀장 — 레이아웃 + 블록 매핑
  ├ Step A: 프리셋 선택 (규칙 기반)
  ├ Step A-2: Opus 블록 추천 (Kei API)
  ├ Step B: Sonnet 블록 매핑
  └ 블록 검증 (코드): 미등록 교체, zone 교정, pill-pair, 높이 예산 체크
      ↓
[2.5단계] ⚠️ Kei 넘침 판단 — 예상 높이 기반
      ↓
[3단계] Kei 편집자 — 텍스트 채움 (Kei API)
      ↓
[4단계] 디자인 실무자 — CSS 조정 + HTML 렌더링 (Sonnet + Jinja2)
      ↓
[5단계] 디자인 팀장 — 재검토 + 조정 루프 (Sonnet, 최대 2회)
      ↓
미리보기 + HTML 다운로드

각 시점에서 알 수 있는 정보

시점 원본 텍스트 꼭지 분석 블록 배치 실제 텍스트 렌더링 HTML 실제 높이
1단계 후 O O - - - -
2단계 후 O O O - - 예상만
2.5단계 O O O 없음 없음 예상만
3단계 후 O O O O - -
4단계 후 O O O O O 측정 가능
5단계 O O O O O 측정 가능

문제 진단 (6건)

문제 1: 내용 없이 넘침 판단

위치: Stage 2.5 현상: Kei에게 "이 zone이 넘친다"고 전달하지만, 실제 텍스트가 없는 상태. 블록 타입의 예상 높이(medium=150px, large=250px)만으로 판단 요청. 문제: Kei가 "trim할까 restructure할까"를 결정하려면 실제 콘텐츠를 봐야 하는데 볼 수 없음. 판단 근거가 부족한 상태에서 판단을 요청.


문제 2: 예상 높이 초과 → 판단 주체 잘못됨

위치: Stage 2.5 현상: Sonnet에게 이미 "zone 예산 490px, height_cost 확인해서 초과하지 마라"고 프롬프트로 지시함. 그런데도 예상 높이가 초과하면 그건 Sonnet이 지시를 안 따른 것. 문제: Sonnet의 지시 불이행을 Kei에게 물어볼 문제가 아님. Sonnet을 다시 호출하거나 프롬프트를 개선할 문제. 판단 주체와 해결 주체가 불일치.


문제 3: 실제 HTML이 있는데 넘침을 안 봄

위치: Stage 5 현상: 렌더링된 HTML이 있고, 각 블록의 실제 텍스트 양도 알 수 있는 시점. 그러나 현재 Stage 5의 점검 항목은 "빈 블록, 채움 불균형, 정보량, HTML 구조"만. 문제: 정작 "컨테이너에 실제로 넘치는가"는 점검 항목에 없음. 넘침을 확인할 수 있는 최적의 시점에서 확인하지 않음.


문제 4: 넘침 판단에 Kei가 없음

위치: Stage 5 현상: Stage 5 재검토는 Sonnet이 단독으로 수행. 조정도 expand/shrink/rewrite를 Sonnet이 결정. 문제: 넘침 발생 시 "뭘 줄이고 뭘 팝업으로 분리할지"는 콘텐츠 중요도 판단 — Kei가 해야 할 일. 현재 Stage 5에 Kei 참여 경로가 없음.


문제 5: 실제 렌더링 높이 측정 수단 없음

위치: 전체 파이프라인 현상: 파이프라인 어디에서도 렌더링된 HTML의 실제 px 높이를 측정하지 않음.

  • Stage 2: 블록 타입 기반 예상 높이 (HEIGHT_COST_PX: compact=70, medium=150, large=250, xlarge=400)
  • Stage 5: Sonnet이 HTML 코드를 읽고 눈대중으로 판단 문제: 예상 높이와 실제 높이는 다를 수 있음. 텍스트 양, CSS 조정, 폰트 크기에 따라 실제 높이가 달라지는데 이를 측정하는 코드가 없음.

문제 6: 넘침이 재검토 루프에 포함 안 됨

위치: Stage 5 루프 현상: Stage 5는 재검토 → 조정 → fill_content(Stage 3) → render(Stage 4) → 재검토 루프가 있음 (최대 2회). 문제: 이 루프 안에 넘침 판단이 없음. 조정 후에도 여전히 넘칠 수 있는데, expand 조정으로 텍스트가 늘어나서 오히려 더 넘칠 수도 있음. 루프가 넘침을 감지하지 못함.


문제 요약 매트릭스

# 문제 위치 핵심 원인 영향
1 내용 없이 넘침 판단 2.5 텍스트 채움 전에 판단 Kei 판단 근거 부족 → 부정확한 결정
2 예상 높이 초과 → Kei에게 물음 2.5 판단 주체 잘못됨 Sonnet 지시 불이행을 Kei가 해결할 수 없음
3 HTML 있는데 넘침 안 봄 5 점검 항목 누락 실제 넘침 감지 못함
4 넘침 판단에 Kei 없음 5 Sonnet만 참여 콘텐츠 중요도 무시한 조정
5 실제 높이 측정 없음 전체 측정 수단 부재 예상과 실제의 차이 감지 불가
6 넘침이 루프에 없음 5 루프 넘침 체크 미포함 조정 후 넘침 악화 가능

원인 관계

근본 원인: Stage 2.5의 넘침 판단 위치가 기존 DOWNGRADE_MAP 위치를 그대로 따름
    ↓
메커니즘만 변경(DOWNGRADE → Kei), 시점은 재검토 안 함
    ↓
내용 없이 판단(문제 1) + 주체 잘못됨(문제 2)
    ↓
실제 넘침이 감지되는 시점(Stage 4 이후)에는 검증 없음(문제 3, 4, 6)
    ↓
애초에 실제 높이 측정 수단도 없음(문제 5)

해결 방안 조사 결과

방안 1: 실제 렌더링 높이 측정 (문제 5 해결)

현재 파이프라인에는 렌더링된 HTML의 실제 px 높이를 측정하는 수단이 없음.

도구 정확도 속도 CSS Grid CSS 변수 커스텀 폰트 설치 상태
Playwright 픽셀 정확 20~50ms/요소 완전 지원 완전 지원 완전 지원 미설치
Selenium 픽셀 정확 50~150ms/요소 완전 지원 완전 지원 완전 지원 설치됨 (4.34.0)
WeasyPrint 제한적 200~500ms 부분 지원 제한적 지원 설치됨 (65.1)
텍스트 추정 ±15~30% 오차 <1ms 불가 불가 불가

권장: Playwright — 가장 정확하고 빠름. 비동기 지원. headless Chromium 자동 설치. 차선: Selenium — 이미 설치됨. 동기식이라 약간 느리지만 충분히 사용 가능.

측정 방식:

# Playwright 예시
async with async_playwright() as p:
    browser = await p.chromium.launch()
    page = await browser.new_page(viewport={"width": 1280, "height": 720})
    await page.set_content(html)

    # 각 zone의 실제 높이 측정
    body_box = await page.locator("[data-zone='body']").bounding_box()
    actual_height = body_box["height"]  # 실제 렌더링 px

    # overflow 감지: scrollHeight > clientHeight
    overflow = await page.evaluate("""
        el => el.scrollHeight > el.clientHeight
    """, await page.query_selector("[data-zone='body']"))

방안 2: Stage 2.5 → Stage 5로 이동 (문제 1, 2, 3, 4, 6 해결)

현재: Stage 2.5에서 텍스트 없이 Kei 판단 → 근거 부족 개선: Stage 4(렌더링) 이후, Stage 5(재검토) 안에서 넘침 판단

현재:
  Stage 2 → [2.5 Kei 넘침 판단] → Stage 3 → Stage 4 → Stage 5(Sonnet만)

개선:
  Stage 2 → Stage 3 → Stage 4 → Stage 5(Sonnet 감지 + Kei 판단)

Stage 5 역할 확장:

  1. Sonnet이 감지: 렌더링된 HTML + zone 예산 정보를 보고 넘침 여부 판단
  2. 넘침이면 Kei에게 전달: 실제 콘텐츠가 있는 상태에서 Kei가 판단
  3. Kei가 결정: trim(텍스트 축약) 또는 restructure(팝업 분리)
  4. Sonnet이 실행: CSS 조정 + 재렌더링

Sonnet + Kei 협업 모델:

Sonnet: "body zone이 520px인데 예산 490px. 30px 초과."
    ↓
Kei: "꼭지 3의 부연 설명을 축약하면 됨. 핵심은 유지."  (trim)
  또는
Kei: "12행 비교표는 팝업으로 분리. 슬라이드엔 요약만."  (restructure)
    ↓
Sonnet: CSS 조정 + 재렌더링

방안 3: Stage 2 구조적 검증은 유지하되 역할 한정 (문제 2 해결)

Stage 2의 _validate_height_budget()구조적 검증만 담당:

  • 금지 블록 교체 (BODY_FORBIDDEN_MAP) — 유지
  • pill-pair 단독 금지 (I-7) — 유지
  • 예상 높이 초과 — 경고만 (Kei 호출 안 함, Stage 5에서 처리)
# Stage 2: 경고만 출력, overflow 정보는 Stage 5에서 활용
if total > budget:
    logger.warning(f"[예상 높이 초과] {area}: {total}px > {budget}px (Stage 5에서 검증)")
    # Kei 호출 안 함. 실제 렌더링 후 Stage 5에서 정확히 감지.

Sonnet 프롬프트(STEP_B_PROMPT) 개선:

  • 현재: height_cost 매핑을 설명하지만 구체적 예시 없음
  • 개선: 계산 예시 추가 + "초과 시 reason 필드에 설명" 명시

방안 4: 넘침을 Stage 5 재검토 루프에 통합 (문제 6 해결)

현재 Stage 5 루프:

재검토(Sonnet) → 조정(expand/shrink/rewrite) → 재편집(Kei 편집자) → 재렌더링 → 재검토

개선 Stage 5 루프:

재검토(Sonnet, 넘침 포함)
  → 넘침 있으면: Kei 판단(trim/restructure)
  → 조정 적용(expand/shrink/rewrite/trim/restructure)
  → 재편집(Kei 편집자) → 재렌더링 → 재검토

Stage 5 프롬프트에 추가할 점검 항목:

6. 높이 제약: 각 zone이 예산을 초과하는가?
   - 자동 조정(shrink)으로 해결 가능 → shrink
   - 불가능 → overflow_detected (Kei 판단 필요)

_apply_adjustments()에 추가할 action:

  • overflow_detected → Kei API 호출 → trim/restructure 적용

해결 방안 매트릭스

방안 해결하는 문제 필요 기술 구현 난이도
1. 실제 높이 측정 문제 5 Playwright 또는 Selenium 중 (의존성 추가)
2. 넘침 판단 Stage 5로 이동 문제 1, 2, 3, 4 코드 리팩토링 중 (Stage 2.5 제거, Stage 5 확장)
3. Stage 2 경고만 문제 2 코드 수정 소 (Kei 호출 제거, 경고만)
4. 넘침을 루프에 통합 문제 6 Stage 5 프롬프트 + 코드 중 (새 action + Kei 연동)

방안 1은 선택적 — Playwright/Selenium 없이도 Sonnet이 HTML을 읽고 넘침을 추정할 수 있음. 정확도는 떨어지지만 현실적. 방안 2+3+4는 필수 — 프로세스 구조 자체의 문제이므로 반드시 수정.


실행 계획: 프로세스 재설계 (방안 2+3+4)

충돌/회귀/오류 검토 완료. Phase I 산출물 전부 재사용. 변경 파일 pipeline.py만. Sonnet 신규 투입 0건. Kei API 호출 위치만 이동. 하드코딩/단발성 없음.

변경 전후 프로세스 비교

[변경 전]
  Stage 1 → Stage 2 → [2.5 Kei 넘침 판단 ⚠️] → Stage 3 → Stage 4 → Stage 5(Sonnet만)

[변경 후]
  Stage 1 → Stage 2(경고만) → Stage 3 → Stage 4 → Stage 5(Sonnet 감지 + Kei 판단)

변경 상세 (5건, pipeline.py만)

P-1: Stage 2.5 제거

위치: pipeline.py 91~136행 (46행) 작업: 전체 삭제 영향: 없음. overflow 키는 layout_concept에 남아 Stage 5에서 참고.

Phase I 회귀 검토:

  • call_kei_overflow_judgment() — 함수 삭제 안 함. 호출 위치만 Stage 5로 이동.
  • _downgrade_fallback() — 삭제 안 함. Stage 5에서 비상용.
  • KEI_OVERFLOW_PROMPT — 삭제 안 함. Stage 5에서 사용.

P-2: _review_balance() 시그니처 + 프롬프트 확장

위치: pipeline.py 297~363행 작업:

  1. 시그니처: (html, layout_concept, content)(html, layout_concept, content, analysis) 추가
  2. 프롬프트에 zone 예산 정보 + overflow 힌트 추가
  3. 점검 항목 6번 추가: "높이 초과 — overflow_detected"
  4. 출력 format에 overflow_detected action 추가

변경 내용:

# 시그니처 변경
async def _review_balance(
    html: str,
    layout_concept: dict[str, Any],
    content: str,
    analysis: dict[str, Any],  # 추가
) -> dict[str, Any] | None:

# 프롬프트 추가
# 1. zone 예산 정보 (select_preset + LAYOUT_PRESETS에서)
preset_name = select_preset(analysis)
preset = LAYOUT_PRESETS.get(preset_name, {})
zone_budget_lines = [
    f"- {name}: ~{z['budget_px']}px (너비 {z['width_pct']}%)"
    for name, z in preset.get("zones", {}).items()
]

# 2. Stage 2 예상 overflow 힌트 (있으면)
overflow_hint = layout_concept.get("overflow", [])

# 3. 점검 항목 6번
"6. 높이 초과: 각 zone의 블록+텍스트가 예산을 초과하는가?\n"
"   - shrink로 해결 가능 → shrink\n"
"   - 불가능 (콘텐츠가 본질적으로 큼) → overflow_detected\n"

# 4. action 추가
"- overflow_detected: 높이 초과로 Kei 판단 필요. 해당 zone과 초과 블록 명시.\n"

충돌: 기존 5개 점검 + 3개 action 변경 없음. 추가만. Sonnet 역할: 넘침 감지만. 판단은 Kei.


P-3: Stage 5 루프에 Kei 넘침 판단 통합

위치: pipeline.py 155~180행 작업: 루프 내에서 overflow_detected 시 Kei 호출 추가

for review_round in range(MAX_REVIEW_ROUNDS):
    review_result = await _review_balance(html, layout_concept, content, analysis)

    if not review_result or not review_result.get("needs_adjustment"):
        break

    # overflow_detected가 있으면 Kei에게 판단 요청
    overflow_adjs = [
        adj for adj in review_result.get("adjustments", [])
        if adj.get("action") == "overflow_detected"
    ]
    if overflow_adjs:
        # 실제 콘텐츠가 있는 상태에서 Kei 판단
        overflow_context = _build_overflow_context(layout_concept, overflow_adjs)
        kei_judgment = await call_kei_overflow_judgment(
            overflow_context, content, analysis
        )

        if kei_judgment is None:
            logger.warning("[DOWNGRADE 비상] Kei API 실패")
            for page in layout_concept.get("pages", []):
                _downgrade_fallback(page.get("blocks", []), overflow_context)
        else:
            # Kei 판단을 adjustments에 반영 (overflow_detected → kei_trim/restructure)
            _convert_kei_judgment(review_result, kei_judgment, analysis)

    # 모든 조정 적용 (기존 expand/shrink/rewrite + 신규 kei_trim)
    layout_concept = await _apply_adjustments(layout_concept, review_result, content)
    html = render_slide(layout_concept)

호출되는 함수: 모두 Phase I에서 만든 것 재사용

  • call_kei_overflow_judgment() — kei_client.py (변경 없음, Kei API만 사용)
  • _downgrade_fallback() — design_director.py (변경 없음)

신규 헬퍼 함수 2개:

  • _build_overflow_context() — overflow_adjs + layout_concept에서 실제 블록 데이터 추출
  • _convert_kei_judgment() — Kei의 trim/restructure 결정을 review_result.adjustments에 반영

P-4: _apply_adjustments() — kei_trim action 추가

위치: pipeline.py 366~410행 작업: 기존 elif 체인에 kei_trim 분기 추가

# 기존 expand/shrink/rewrite 로직 변경 없음
# 아래 elif만 추가:

elif action == "kei_trim":
    max_chars = adj.get("max_chars", 200)
    if "char_guide" not in block:
        block["char_guide"] = {}
    for key in block.get("char_guide", {}):
        block["char_guide"][key] = min(block["char_guide"][key], max_chars)
    if not block["char_guide"]:
        block["char_guide"] = {"text": max_chars}
    logger.info(f"조정: {area} → kei_trim max_chars={max_chars}")

elif action == "kei_restructure":
    block["detail_target"] = True
    if "data" in block:
        del block["data"]
    block["reason"] = f"재구성: {adj.get('detail', 'Kei 판단 팝업 분리')}"
    logger.info(f"조정: {area} → kei_restructure (detail_target)")

충돌: 없음. 기존 3개 action 변경 0행. 새 elif 추가만.


P-5: 호출부 수정

위치: pipeline.py 156행

# 현재:
review_result = await _review_balance(html, layout_concept, content)
# 변경:
review_result = await _review_balance(html, layout_concept, content, analysis)

영향: 이 함수의 호출부는 pipeline.py 156행 1곳만. 다른 파일에서 호출하지 않음.


변경 파일 총괄

파일 변경 Phase I 코드 영향
pipeline.py Stage 2.5 제거 + Stage 5 확장 + 헬퍼 2개 + action 2개 Phase I 함수 재사용, 삭제 0건
design_director.py 변경 없음
kei_client.py 변경 없음
content_editor.py 변경 없음
sse_utils.py 변경 없음

검증 매트릭스

항목 결과
Phase I 회귀 없음 — I-1~I-14 전부 유지, 함수/상수 삭제 0건
Kei API 사용 유지call_kei_overflow_judgment() 호출 위치만 Stage 5로 이동
Sonnet이 Kei 역할 대체 없음 — Sonnet은 감지만, 판단은 Kei만
하드코딩 없음 — trim max_chars는 Kei가 결정
단발성 수정 없음 — 범용 구조 (어떤 overflow에도 동작)
기존 코드 충돌 없음 — overflow 키가 중간 단계에서 무시되는 것 확인
DOWNGRADE 비상용 유지 — Stage 5에서 Kei 실패 시 동일하게 작동

실행 순서

  1. P-1: Stage 2.5 제거 (pipeline.py 91~136행 삭제)
  2. P-2: _review_balance() 시그니처 + 프롬프트 확장
  3. P-3: Stage 5 루프에 Kei 연동 + 헬퍼 함수 2개
  4. P-4: _apply_adjustments() kei_trim/kei_restructure action 추가
  5. P-5: 호출부 analysis 파라미터 추가

이력

날짜 내용
2026-03-26 Phase I 실행 완료 후 프로세스 검증 중 발견. 6개 문제 진단.
2026-03-26 해결 방안 4개 조사. Playwright 높이 측정 + Stage 5 넘침 통합 방향 도출.
2026-03-26 실행 계획 확정. 충돌/회귀/오류 검토 완료. P-1~P-5 5건, pipeline.py만 변경. Phase I 산출물 전부 재사용.