diff --git a/IMPROVEMENT-PHASE-P.md b/IMPROVEMENT-PHASE-P.md new file mode 100644 index 0000000..f4de0de --- /dev/null +++ b/IMPROVEMENT-PHASE-P.md @@ -0,0 +1,176 @@ +# Phase P: 블록 재구성 + 실제 렌더링 비교 선택 + +> 작성일: 2026-03-27 +> 상태: 계획 수립 중 (사용자 승인 대기) +> 선행 완료: Phase O (컨테이너 기반 레이아웃) + +--- + +## 핵심 원칙 + +**"블록을 컨테이너에 맞게 재구성하고, 실제 렌더링해보고, Kei가 목적에 맞는 것을 고른다."** + +``` +컨테이너 58px 확정 (Phase O) + ↓ +후보 3개 선택 (FAISS 2개 + Opus 1개) + ↓ +3개 블록을 각각 58px에 맞게 재구성 +(폰트, 패딩, 항목 수, 레이아웃 변형) + ↓ +재구성된 3개를 실제 렌더링 (Selenium) + ↓ +Kei가 "당초 목적에 가장 적합한 것은?" 선택 → 최종 1개 +``` + +--- + +## 이것이 해결하는 문제들 + +| 문제 | 왜 해결되나 | +|------|-----------| +| P-1: 키워드 반응으로 잘못된 블록 선택 | 3개를 실제 렌더링해서 Kei가 목적 기준으로 최종 선택. 키워드 반응으로 골라도 목적에 안 맞으면 탈락 | +| P-4: 블록이 콘텐츠 의미 왜곡 | compare-pill-pair에 "혼용 문제"를 넣어봤더니 의미가 안 맞으면 Kei가 탈락시킴 | +| P-5/6/7: height_cost 부정확 | catalog의 height_cost를 믿지 않음. 실제 렌더링으로 확인 | +| P-8: 58px에 콘텐츠 전달 불가 | 블록을 58px에 맞게 재구성하므로 어떤 블록이든 넣을 수 있음 | +| P-14: 피드백 루프 무력 | 처음부터 맞는 걸 고르니까 피드백 필요성 감소 | + +--- + +## 단계별 상세 + +### P-Step 1: 후보 선택 + +각 topic에 대해 후보 3개를 뽑는다. + +**FAISS 2개:** +- 기존 `search_blocks_for_topics()`로 검색 +- topic의 purpose, relation_type, expression_hint를 쿼리에 포함 (이미 Phase M에서 구현) +- 상위 2개 선택 + +**Opus 1개:** +- 기존 `_opus_block_recommendation()`에서 Kei가 추천한 블록 +- 도메인 지식 + 콘텐츠 성격 기반 + +**중복 제거:** FAISS 결과에 Opus 추천이 이미 포함되어 있으면 FAISS 3번째를 올림. 항상 서로 다른 3개. + +### P-Step 2: 블록 재구성 + +각 후보 블록을 해당 topic의 **컨테이너 크기에 맞게 재구성**한다. + +**재구성 항목:** +- 폰트 크기: 컨테이너 높이에 따라 조정 (Phase O `_determine_typography()` 사용) +- 패딩: 컨테이너 높이에 따라 조정 +- 항목 수: 컨테이너에 들어가는 만큼만 (Phase O `_calculate_block_constraints()` 사용) +- 글자 수: 항목당 최대 글자 수 계산 +- 레이아웃: 세로 → 가로 전환 등 (컨테이너 가로/세로 비율에 따라) + +**이 단계에서 텍스트도 채운다:** +- 각 후보 블록의 슬롯에 맞게 Kei 편집자가 텍스트를 채움 +- 3개 블록 × 같은 원본 텍스트 → 3개 다른 편집 결과 +- 또는: 1회 편집 후 3개 블록 슬롯에 재배치 (API 호출 절약) + +### P-Step 3: 실제 렌더링 + +재구성된 3개 블록을 각각 Selenium으로 렌더링한다. + +**측정 항목:** +- 실제 높이(scrollHeight) vs 컨테이너 높이 +- overflow 여부 +- 렌더링 결과 스크린샷 (base64 PNG) + +**렌더링 방법:** +- `render_standalone_block(block_type, data)`로 각 블록 단독 렌더링 +- 컨테이너 크기를 감싼 div 안에서 렌더링 +- `slide_measurer.py`의 기존 Selenium 인프라 재사용 + +### P-Step 4: Kei 최종 선택 + +렌더링된 3개 결과를 Kei(Opus)에게 보여주고 최종 선택. + +**Kei에게 전달하는 정보:** +- 이 topic의 원래 목적 (purpose, relation_type, expression_hint) +- 3개 렌더링 결과 (스크린샷 이미지 또는 HTML 요약) +- 각 블록의 overflow 여부 +- 원본 콘텐츠 + +**Kei 판단 기준:** +1. 당초 목적에 적합한가? (문제 제기인데 비교 블록이면 부적합) +2. 콘텐츠 의미가 왜곡되지 않는가? +3. 컨테이너에 맞게 렌더링되었는가? + +**Kei API 호출:** 1회. 3개를 한꺼번에 보여주고 1개 선택. + +--- + +## 파이프라인 흐름 변경 + +``` +현재: +1A → 1B → 컨테이너 → A-2(Kei 1개 확정) → 블록 스펙 → 3(편집) → 4(렌더링) → 측정 → 5(검수) + +Phase P 후: +1A → 1B → 컨테이너 + → 각 topic마다: + 후보 3개 (FAISS 2 + Opus 1) + → 3개 재구성 (컨테이너에 맞게) + → 3개 렌더링 (Selenium) + → Kei 최종 선택 (목적 적합성) + → 선택된 블록으로 전체 슬라이드 조립 + → 4(CSS 조정 + 최종 렌더링) + → 측정 + → 5(검수) +``` + +--- + +## 기존 코드 영향 + +| 파일 | 변경 | +|------|------| +| `pipeline.py` | Step A-2 단일 선택 → topic별 3후보 + 렌더링 + Kei 선택 루프로 변경 | +| `design_director.py` | `_opus_block_recommendation()` — 기존 유지. 추가로 단일 topic 추천 함수 필요 | +| `block_search.py` | 기존 유지. topic별 상위 2개 추출 함수 추가 | +| `renderer.py` | `render_standalone_block()` — 기존 유지. 컨테이너 감싼 렌더링 함수 추가 | +| `slide_measurer.py` | 기존 유지. 단일 블록 높이 측정 함수 추가 | +| `space_allocator.py` | `finalize_block_specs()` — 기존 유지. 후보별 스펙 계산에 재사용 | +| `content_editor.py` | 기존 유지. 후보별 텍스트 채우기에 재사용 | +| `kei_client.py` | 3후보 비교 선택 프롬프트 함수 신규 | + +--- + +## 하드코딩 검증 + +| 항목 | 하드코딩? | 근거 | +|------|---------|------| +| 후보 수 3개 (FAISS 2 + Opus 1) | 구조적 설계 | 블록 추가해도 변경 불필요. 3개는 비교에 적절한 수 | +| 블록 재구성 (폰트/패딩/항목수) | 동적 계산 | Phase O `_determine_typography()`, `_calculate_block_constraints()` 사용 | +| 최종 선택 | Kei 판단 | 코드가 선택하지 않음 | +| 컨테이너 크기 | Kei 비중에서 동적 계산 | Phase O `calculate_container_specs()` | + +--- + +## 비용 분석 + +| 항목 | 현재 | Phase P 후 | +|------|------|-----------| +| Kei API 호출 | 1A + 1B + A-2 + 3(편집) + 5(검수) = 5회 | + topic별 최종 선택 1회 × 5topics = +5회. 총 ~10회 | +| Selenium 렌더링 | 1회 (전체 슬라이드) | + topic별 3후보 × 5topics = +15회. 단독 블록이라 빠름 (~50ms/회) | +| Sonnet 호출 | 4단계 CSS 1회 | 변경 없음 | +| 총 시간 증가 | — | Selenium +750ms, Kei API +5회 (각 ~30초) ≈ +2.5분 | + +--- + +## 미해결 사항 (사용자 논의 필요) + +1. **3개 후보에 텍스트를 어떻게 채우나?** + - 방법 A: Kei 편집자를 3회 호출 (각 블록 슬롯에 맞게) — 정확하지만 느림 + - 방법 B: 1회 편집 후 3개 블록 슬롯에 기계적 재배치 — 빠르지만 슬롯 구조가 다르면 부정확 + - 방법 C: Kei 편집자 1회 호출에 "3개 블록 각각의 슬롯에 맞게" 한꺼번에 요청 — 중간 + +2. **Kei 최종 선택에 스크린샷을 보여줄까, HTML 요약을 보여줄까?** + - 스크린샷: Opus 멀티모달로 실제 렌더링을 보고 판단 — 정확 + - HTML 요약: 텍스트 기반 — 빠르지만 시각 판단 불가 + +3. **3개 후보가 전부 목적에 안 맞으면?** + - Kei가 "없음"을 선택할 수 있게 할까? 그러면 추가 후보를 어디서?