- 루트의 IMPROVEMENT-PHASE-*.md, PHASE-*.md 등 45개 → docs/history/로 이동 - docs/block-tests/ 오래된 블록 테스트 HTML 삭제 (figma_to_html_agent로 대체) - docs/figma-analysis/, docs/figma-assets/, docs/figma-screenshots/ 정리 - docs/test-*.html 등 초기 테스트 파일 정리 - 참고 페이지/ 스크린샷 정리 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
Phase W — 실행 계획 (Task별 방향 + 방법)
작성일: 2026-04-03 상위 문서: PHASE-W.md
W-1: space_allocator — weight 비율 초기 배정
W-1-1: zone_budget을 weight 비율로 계산
현재: zone_budget = zone_info.get("budget_px") → 프리셋 490px 고정
방향: zone_budget = total_available × zone_weight_sum / all_weight_sum
파일: src/space_allocator.py — calculate_container_specs() 내부
방법:
- 전체 가용 높이 = slide_height - padding×2 - gap×2 - header
- 각 zone의 weight 합을 구함 (body zone = 배경+본심 weight, sidebar = 첨부 weight 등)
- 전체 weight 합 대비 비율로 zone_budget 계산
- 이전에 구현했던 코드를 다시 적용 (173113 run에서 동작 확인됨) 검증: weight 합 1.0일 때 모든 컨테이너 높이 합 출력하여 전체 가용의 95% 이상인지 확인
W-1-2: 전체 공간 100% 사용
방향: W-1-1이 해결되면 자동으로 해결
검증: stage_1_5a_context.json에서 모든 컨테이너 height_px 합산 ≥ 전체 가용 × 0.95
W-1-3: 시선 흐름 배치 좌표
현재: _calc_coords()가 배경→상단좌, 본심→중앙좌, 첨부→우측, 결론→하단으로 배치
방향: 현재 코드 유지 (이미 올바름)
검증: stage_1_8_filled.html에서 배경이 상단, 본심이 중앙, 첨부가 우측, 결론이 하단에 위치
W-2: block_assembler — 공통 조립 함수 완성
W-2-1: font_hierarchy override
현재: _override_font() 함수가 블록 CSS의 font-size를 font_hierarchy로 조정
방향: 현재 코드 유지
검증: filled HTML에서 첨부 영역의 font-size가 sidebar 값(9-11px)을 초과하지 않음
W-2-2: 팝업 링크 인접 배치
현재: _parse_structured_text()에서 [팝업: 제목]을 이전 불릿 텍스트에 [제목→]으로 붙임
방향: 현재 코드 유지
검증: filled HTML에서 [혼용 대표 사례→]가 별도 줄이 아니라 텍스트 옆에 붙어있음
W-2-3: sidebar 상단 라벨
현재: _assemble_card_numbered()에서 topic.title을 라벨로 추가
방향: 현재 코드 유지
검증: filled HTML의 첨부 영역 상단에 꼭지 title이 보임
W-2-4: 카드 indent 파싱
현재: _assemble_card_numbered()에서 indent=0만 카드 제목, indent=1은 설명
방향: 현재 코드 유지
검증: 첨부에 건설산업/BIM/DX 3개 카드가 분리되고, 하위 설명이 각 카드 안에 있음
W-2-5: 카드 불릿 간격
현재: CSS override에서 white-space: pre-line → normal 변환
방향: 현재 코드 유지
검증: 첨부 카드 내 불릿과 불릿 사이에 빈 줄(엔터)이 없음
W-2-6: 실제 이미지 사용
현재: has_real_image 분기로 실제 이미지 있으면 SVG 레이아웃, 없으면 텍스트만
방향: 수정 필요 — 현재 _assemble_svg_layout()이 design_reference_html에서 SVG를 추출. 이걸 ctx.slide_images의 실제 이미지(base64)로 교체
파일: src/block_assembler.py — _assemble_svg_layout()
방법:
ctx.slide_images에서 해당 이미지의 base64 데이터를 가져옴<img src="data:image/png;base64,{b64}" />형태로 삽입- SVG viewBox/gradient 하드코딩 대신 실제 이미지 사용
검증: filled HTML에
<img src="data:image/png;base64,패턴이 있고, SVG 태그가 없음
W-2-7: filled/assembled 통일
현재: _gen_stage_1_8_filled()가 assemble_slide_html() 호출
방향: assembled(stage_2_code_assembled)도 같은 함수 호출하도록 assemble_stage2.py 수정 또는 제거
검증: filled와 assembled가 같은 HTML 구조를 가짐 (diff로 확인)
W-2-8: 팝업 마크다운 테이블 변환
현재: mdx_normalizer.py의 _extract_popup()에서 _convert_md_table_to_html() 호출
방향: 현재 코드 유지
검증: stage_0_context.json의 popups에서 "DX와 BIM의 구분" 팝업에 <table> 태그가 있고 | 마크다운이 없음
W-3: filled → Selenium 측정 연결
W-3-1: .slide 클래스
현재: assemble_slide_html()의 최외곽 div에 class="slide" 있음
방향: 현재 코드 유지
검증: filled HTML에서 class="slide" 존재 확인
W-3-2: area-* 클래스
현재: 각 역할 컨테이너에 area-body, area-sidebar, area-footer 클래스 있음
방향: 현재 코드 유지
검증: filled HTML에서 area-body, area-sidebar, area-footer 존재 확인
W-3-3: Selenium 측정 정상 동작
현재: 173113 run에서 {'error': 'slide not found'} 발생 (당시 .slide 클래스 없었음)
방향: W-3-1, W-3-2가 해결되면 자동 해결
방법: filled HTML을 measure_rendered_heights()에 넣고 정상 결과 반환 확인
검증: 반환값에 zones.sidebar.scrollHeight, zones.body.scrollHeight 등이 있고 error 키가 없음
W-3-4: 시각화 순서 (before → filled → after)
현재: step_visualizer.py의 dispatch에서 blocks → filled → fit_before → fit_after 순서
방향: before(빈 컨테이너 크기) → filled(블록+텍스트 채운 상태) → after(조정된 크기) 순서
파일: src/step_visualizer.py — generate_step_html()
방법:
stage_1_8dispatch 순서를fit_before → filled → fit_after로 변경- fit_before는 빈 컨테이너 크기만 보여줌 (부족/여유 판단 없이)
- filled는 블록+텍스트 채운 상태
- fit_after는 조정 후 컨테이너 크기 검증: steps 폴더에 3개 파일이 순서대로 있고, before의 크기 → filled의 넘침 → after의 변경이 시각적으로 확인 가능
W-4: 측정 결과 기반 조정 판단
W-4-1: sidebar overflow → 확장
현재: pipeline.py Stage 1.8에 sidebar 확장 코드 있음
방향: 현재 코드 유지 (Selenium 측정이 동작하면 자동으로 발동)
검증: sidebar scrollHeight > clientHeight일 때 stage_1_8_context.json의 첨부 height_px가 scrollHeight 이상으로 증가
W-4-2: body overflow → 재배분
현재: redistribute() 함수가 body zone 내에서 배경↔본심 재배분
방향: 현재 코드 유지
검증: body overflow 시 배경 또는 본심의 height_px가 변경됨
W-4-3: 재배분 후에도 overflow → Kei 에스컬레이션
현재: needs_escalation=True일 때 call_kei_fit_escalation() 호출
방향: 현재 코드 유지
검증: enhancement_result.kei_decisions에 Kei 응답이 저장됨
W-4-4: Kei trim/popup 결정 실제 적용
현재: Kei 결정을 받지만 실제 반영 안 됨
방향: 새로 구현
파일: src/pipeline.py Stage 1.8 내부 + 새 함수
trim 구현 방법:
- Kei가
{"action": "trim", "detail": "150자로 축약"}을 반환하면 - 해당 role의 topic structured_text를 Kei/Sonnet에게 축약 요청 (AI 판단 — 어떤 문장이 덜 중요한지는 AI만 알 수 있음)
- 프롬프트: "다음 텍스트를 N자 이내로 축약하라. 불릿 구조 유지. 핵심 85% 보존."
- 축약된 텍스트로 structured_text 교체
- 하드코딩 없음 — 어떤 콘텐츠든 AI가 판단
- 도구: anthropic SDK (이미 있음), Kei API /api/direct
popup 구현 방법:
- Kei가
{"action": "popup", "detail": "상세 정의를 팝업으로"}를 반환하면 - 해당 role의 structured_text를 Kei/Sonnet에게 분리 요청 ("요약 vs 상세" 판단)
- 프롬프트: "다음 콘텐츠를 슬라이드 요약(2-3줄)과 팝업 상세로 분리하라."
- 요약은 structured_text에, 상세는 별도 팝업 HTML로 저장
- 슬라이드에는 요약 +
[상세보기→]링크 - 하드코딩 없음 — 어떤 콘텐츠든 AI가 요약/상세를 판단
- 도구: anthropic SDK, 팝업 HTML 템플릿 (pipeline.py Stage 5에 이미 있음)
검증: trim 후 structured_text 길이가 줄어들고, popup 후 팝업 HTML 파일이 생성됨
W-4-5: Kei restructure → 컨테이너 직접 변경
현재: redistribute() 재실행만 됨
방향: Kei가 "본심에 363px 보장"하면 직접 height_px 변경
파일: src/pipeline.py Stage 1.8 내부
방법:
- Kei 결정에서 구체적 px 값을 파싱 (정규식으로 숫자 추출)
- 해당 role의 height_px를 직접 설정
- 다른 role에서 부족분을 weight 역비례로 차감 (중요도 낮은 곳에서 더 많이)
- 최소 높이(60px) 보장
- 총합이 전체 가용 초과하지 않도록 검증
- 하드코딩 없음 — 순수 산술, 어떤 role이든 동작
- 도구: Python 산술 (외부 라이브러리 불필요)
검증: restructure 후 해당 role의 height_px가 Kei가 지정한 값으로 변경되고, 총합이 전체 가용 이하
W-4-6: after 컨테이너 저장
현재: stage_1_8_context.json에 containers 저장됨
방향: 현재 코드 유지 (W-4-1~5의 결과가 containers에 반영되면 자동 저장)
검증: stage_1_8_context.json의 containers가 before와 다름
W-4-7: Kei 보강 검토 호출
현재: call_kei_enhancement_review() 함수 있고 pipeline.py에서 호출
방향: 현재 코드 유지
검증: enhancement_result에 Kei 보강 검토 결과가 저장됨 (approve/modify/reject)
W-5: after 기반 최종 조립 + 검증
W-5-1: stage_2가 after 컨테이너 사용
현재: stage_2_context.json의 containers == stage_1_8_context.json의 containers (확인됨) 방향: 현재 코드 유지 검증: 두 JSON의 containers 비교 — 일치
W-5-2: overflow 없음 확인
현재: Stage 4에서 Selenium 측정. Vision 모델 ID 404 에러
방향: Vision 모델 ID를 claude-sonnet-4-20250514로 변경 (vision 지원, 비용 효율)
파일: src/kei_client.py — 3곳
방법: 모델 ID 문자열 교체
검증: Stage 4에서 모든 zone의 excess_px ≤ 0
W-5-3: 텍스트 85% 보존 검증
현재: 검증 로직 없음
방향: 새로 구현
파일: src/pipeline.py Stage 4 또는 Stage 5
방법:
- final.html에서 HTML 태그 제거하여 순수 텍스트 추출 (Python stdlib
html.parser) - 각 role의 structured_text와 문자 3-gram 겹침 비교
- 85% 이상이면 PASS
- 하드코딩 없음 — 문자열 비교만, 어떤 콘텐츠든 동작
- 도구: Python stdlib만 (html.parser, re). 외부 NLP 불필요
검증: 검증 함수가 각 role별 보존율을 반환하고, 모든 role이 85% 이상
의존 관계
W-1-1 → W-1-2 (자동)
W-1 + W-2 → W-3 (filled 생성 + 측정)
W-3 → W-4 (측정 결과로 판단)
W-4 → W-5 (after 기반 최종)
W-2 내부: 1~8 독립적으로 병행 가능
W-4 내부: 1→2→3→4/5 순차, 6/7 독립
필요 도구/라이브러리
| 도구 | 용도 | 상태 |
|---|---|---|
| Selenium + Chrome headless | filled 측정 (W-3) | ✅ 설치됨, 동작 확인 |
| anthropic SDK | Kei trim/popup (W-4-4), Vision (W-5-2) | ✅ 설치됨 |
| httpx | Kei API 호출 | ✅ 설치됨 |
| Kei API (localhost:8000) | 에스컬레이션, 보강 검토 | ✅ 동작 확인 |
| Python stdlib (html.parser, re) | 텍스트 보존 검증 (W-5-3) | ✅ 내장 |
| Jinja2 | 블록 템플릿 렌더링 | ✅ 설치됨 |
추가 설치 필요 없음.
실행 순서
Phase 1: W-1 (weight 비율) — 기반
Phase 2: W-2 (공통 조립 함수) — W-1과 병행 가능
Phase 3: W-3 (Selenium 연결) — W-1 + W-2 필요
Phase 4: W-4 (판단 로직) — W-3 필요
Phase 5: W-5 (최종 검증) — W-4 필요
각 Phase 완료 후 파이프라인 실행하여 검증.