Files
C.E.L_Slide_test2/PIPELINE.md

305 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design Agent 파이프라인 현황
> **최종 갱신:** 2026-04-13
> **목적:** 새 세션의 AI가 이 문서만 읽으면 파이프라인 전체를 이해하고 작업할 수 있도록 한다.
---
## 1. 전체 흐름 요약
```
MDX 입력
[Stage 0] MDX 정규화 (코드)
[Stage 1A] Kei 실장 — 꼭지 추출 (AI: Opus)
→ layout_template: A 또는 B 선택
[Stage 1B] 컨셉 구체화 (AI: Opus)
→ relation_type, expression_hint, source_data
[Stage 1B-ST] 구조화 텍스트 생성 (AI: Opus)
→ structured_text per topic
[Stage 1.5a] 컨테이너 계산 (코드: 결정론적)
→ FontHierarchy, ContainerSpec, Preset
[Stage 1.7] 블록 레퍼런스 선택 (코드 + AI 1회)
→ relation_type → 카테고리 → 필터 → 블록 결정
[Stage 1.8] 적합성 검증 + 보강 (코드 + Selenium + AI)
→ overflow 감지 → Kei 에스컬레이션 → 재배분
[Stage 1.5b] 디자인 예산 계산 (코드)
[Stage 2] HTML 생성 (Type에 따라 다름)
→ Type B/B'/B'': block_assembler (코드)
→ Type A: Sonnet 재구성 (AI, 미완성)
[Stage 3] 렌더링 조립 (Type A만, Jinja2)
[Stage 4] 검증 (Selenium + Opus Vision)
→ overflow 측정 + 스크린샷 품질 평가
최종 HTML 출력 (data/runs/{id}/final.html)
```
---
## 2. 레이아웃 유형 (Type A / B / B' / B'')
### 2.1 Kei가 선택하는 유형: A와 B
Kei 프롬프트(`src/kei_client.py:34-46`)에서 A 또는 B를 선택한다.
| 유형 | 조건 | Zone 구조 |
|------|------|-----------|
| **Type A** | 참조자료(용어 정의, 부록 등)가 별도로 존재 | body(배경+본심) + sidebar(첨부) + footer(결론) |
| **Type B** | 본문 흐름만. 배경/첨부가 없거나 억지로 만들어야 하면 | top + bottom_left + bottom_right + footer |
### 2.2 Type B 변형: B'과 B''
B'과 B''은 **Kei가 선택하지 않는다.** 특정 MDX 테스트 과정에서 하드코딩한 변형이다.
| 변형 | 생성 경위 | 차이점 | 코드 위치 |
|------|----------|--------|----------|
| **B** | 범용 | 상단(전체폭 텍스트+이미지) + 하단 2분할 + 결론 | `block_assembler.py:461` `_assemble_slide_html_type_b()` |
| **B'** | 03번 MDX 테스트 중 생성 | 상단이 세로 카드 형태 + 하단에 표 렌더링 + 불릿 전용 | `block_assembler.py:885` `_assemble_slide_html_type_b_prime()` |
| **B''** | B'에서 스타일 변형 | border/gradient 없음. 색상바+여백으로 구분 | `block_assembler_b2.py:9` `_assemble_slide_html_type_b_double_prime()` |
**분기 코드** (`block_assembler.py:370-378`):
```python
if ctx.analysis.layout_template == "B":
return _assemble_slide_html_type_b(ctx, title_text)
if ctx.analysis.layout_template == "B'":
return _assemble_slide_html_type_b_prime(ctx, title_text)
if ctx.analysis.layout_template == "B''":
return _assemble_slide_html_type_b_double_prime(ctx, title_text)
```
### 2.3 향후 방향
B'/B''은 **범용화가 필요하다.** 현재는 03번 콘텐츠 구조(카드형+표)를 B 조립 함수가 커버하지 못해서 만든 땜질이다. 궁극적으로는 B 하나로 다양한 콘텐츠 구조를 커버하거나, AI가 서브타입을 판단하게 해야 한다.
---
## 3. MDX 샘플 ↔ 유형 매핑
| MDX | 파일 | 콘텐츠 성격 | 선택 유형 | 상태 |
|-----|------|-----------|----------|------|
| **01번** | `samples/mdx/01. 건설산업 DX의 올바른 이해(0127).mdx` | 용어 혼용 문제 + 용어 정의(참조) | **Type A** | ⚠ Stage 2 미완성 (Sonnet 의존) |
| **02번** | `samples/mdx/02. DX의 시행 목표 및 기대효과.mdx` | 본문 흐름 (3대 목표) | **Type B** | ✅ 동작 |
| **03번** | `samples/mdx/03. DX 시행을 위한 필수 요건 및 혁신 방안.mdx` | 카드형 구조 + 표 + 불릿 | **Type B'** | ✅ 동작 (하드코딩) |
---
## 4. 단계별 상세
### Stage 0: MDX 정규화
- **파일:** `src/mdx_normalizer.py``normalize_mdx_content()`
- **입력:** raw MDX 텍스트
- **출력:** `NormalizedContent` (sections, images, tables 분리)
### Stage 1A: Kei 실장 — 꼭지 추출
- **파일:** `src/kei_client.py``classify_content()`
- **AI:** Opus (Kei API)
- **입력:** 정규화된 텍스트
- **출력:** `Analysis` (title, core_message, layout_template, total_pages, page_structure, topics)
- **핵심 판단:**
- 꼭지 5개 이내 추출
- 각 꼭지에 purpose, layer, role, emphasis, direction 부여
- layout_template = "A" 또는 "B" 선택
- page_structure에 역할별 weight(비중) 배정
### Stage 1B: 컨셉 구체화
- **파일:** `src/kei_client.py``refine_concepts()`
- **AI:** Opus
- **출력:** topics에 relation_type, expression_hint, source_data 추가
### Stage 1B-ST: 구조화 텍스트 생성
- **파일:** `src/kei_client.py``generate_structured_text()`
- **AI:** Opus
- **출력:** topic별 structured_text (마크다운 형태)
### Stage 1.5a: 컨테이너 계산 (결정론적)
- **파일:** `src/space_allocator.py`
- **함수:**
- Type A → `calculate_container_specs()`
- Type B/B'/B'' → `build_containers_type_b()`
- 공통 → `calculate_font_hierarchy()`, `select_preset()`
- **입력:** page_structure의 weight, slide 크기(1280×720)
- **출력:** 역할별 `ContainerSpec` (width_px, height_px, zone)
- **로직:** weight × available_px = 각 zone px 확정
### Stage 1.7: 블록 레퍼런스 선택
- **파일:** `src/block_reference.py``select_and_generate_references()`
- **로직 (코드 결정론적 + AI 1회):**
1. relation_type → 블록 카테고리 매핑
2. expression_hint 키워드 매칭
3. 컨테이너 크기 적합성 필터
4. role/zone 제약 (sidebar → visuals/media 제외)
5. catalog.yaml 존재 검증 (유령 블록 차단)
6. 후보 2-3개 → Kei 1회 호출로 최종 선택
- **출력:** 역할별 `BlockReference` (block_id, design_reference_html)
### Stage 1.8: 적합성 검증 + 보강
- **파일:** `src/fit_verifier.py``calculate_fit()`
- **로직:**
1. 텍스트 분량 vs 할당 공간 계산
2. Selenium으로 실제 높이 측정 (3회 루프)
3. overflow 시 → Kei 에스컬레이션 (`call_kei_fit_escalation()`)
- 팝업 분리 판단, zone 간 재배분
4. 보강 제안: bold 키워드, 팝업 요약 등
### Stage 2: HTML 생성
**Type B/B'/B'' (코드 조립):**
- **파일:** `src/block_assembler.py``assemble_slide_html()`
- 역할별 `assemble_role_html()` 호출 → 블록 HTML 조립
- structured_text + design_reference_html 결합
- 이미지/팝업 embed
- **즉시 완성 HTML 반환**
**Type A (AI 재구성, 미완성):**
- **파일:** `src/content_verifier.py``generate_with_retry()`
- Sonnet에 phase_t_context 전달 → CSS + 레이아웃 생성
- **현재 검증 불완전**
### Stage 3: 렌더링 조립 (Type A만)
- **파일:** `src/renderer.py``render_slide_from_html()`
- Type B는 Stage 2에서 완전한 HTML이므로 스킵
### Stage 4: 검증
- **파일:** `src/slide_measurer.py`
- `measure_rendered_heights()` — Selenium 실측
- `capture_slide_screenshot()` — 스크린샷 캡처
- **파일:** `src/kei_client.py``vision_quality_gate()`
- Opus 멀티모달: 스크린샷 보고 시각 품질 평가
---
## 5. 핵심 파일 맵
```
src/
├── main.py ← FastAPI 서버, POST /api/generate
├── pipeline.py ← 파이프라인 오케스트레이터 (generate_slide)
├── pipeline_context.py ← PipelineContext 데이터 클래스
├── config.py ← 설정 (API key, 슬라이드 크기)
├── mdx_normalizer.py ← Stage 0: MDX → NormalizedContent
├── kei_client.py ← Stage 1A/1B/1B-ST: Kei API 호출 + 프롬프트
├── space_allocator.py ← Stage 1.5a: 컨테이너 px 계산
├── block_reference.py ← Stage 1.7: 블록 선택 (relation_type 기반)
├── fit_verifier.py ← Stage 1.8: 적합성 검증 + Selenium 루프
├── block_assembler.py ← Stage 2: Type B/B' HTML 조립
├── block_assembler_b2.py ← Stage 2: Type B'' HTML 조립
├── content_verifier.py ← Stage 2: Type A HTML (Sonnet, 미완성)
├── renderer.py ← Stage 3: Jinja2 렌더링 (Type A만)
├── slide_measurer.py ← Stage 4: Selenium 측정 + 스크린샷
├── validators.py ← Kei 응답 검증 (A/B별 구조 확인)
├── image_utils.py ← 이미지 크기 측정 + data URI 변환
├── svg_calculator.py ← SVG 다이어그램 좌표 계산
└── sse_utils.py ← SSE 스트리밍 유틸
templates/
├── slide-base.html ← 슬라이드 기본 구조 (Jinja2, CSS Grid)
├── catalog.yaml ← 블록 라이브러리 정의 (50+개)
└── blocks/ ← 블록 HTML 템플릿
├── headers/ (8개)
├── cards/ (17개)
├── emphasis/ (12개)
├── tables/ (8개)
├── visuals/ (6개)
├── media/ (4개)
└── BEPs/ (6개)
```
---
## 6. 데이터 흐름 (PipelineContext)
```
PipelineContext:
raw_content ← 원본 MDX
normalized ← NormalizedContent (sections, images, tables)
analysis ← Analysis (title, core_message, layout_template, page_structure, topics)
topics ← list[Topic] (relation_type, expression_hint, structured_text 포함)
page_structure ← PageStructure (roles → {topic_ids, weight, zone})
containers ← dict[role → ContainerSpec(width_px, height_px)]
font_hierarchy ← FontHierarchy (key_msg, core, bg, sidebar 폰트 크기)
references ← dict[role → list[BlockReference]]
sub_layouts ← dict[role → SubLayout]
fit_result ← 역할별 fit_status, 재배분값
enhancement_result ← bold_keywords, popup_summaries 등
generated_html ← Stage 2 출력
rendered_html ← Stage 3 출력 (완전 HTML)
measurement ← Selenium 측정값
quality_score ← 0-100
```
---
## 7. 현재 구현 상태 (Phase Y-11~13, 2026-04-15)
> Phase Y: slide-base 기반 파이프라인 재설계. 상세: `docs/history/PHASE-Y-PLAN.md`
### 파이프라인 흐름 (현재)
```
[Stage 0] MDX → normalized.sections (source of truth)
[Stage 1A] Kei 꼭지 추출 (영역/zone 판단 안 함)
[Phase Y] 코드: normalized → 대목차 추출 → group schema 분류 → 블록 매칭 → 영역 확정
[Stage 1.5a] space_allocator: weight → zone px (% 기반)
[Stage 1.7] block_reference: tag_match → schema_match → fallback 순서
[Stage 1.8] assembler(measure_mode) → Selenium 측정 → fit 루프
[Stage 2] assembler(slide-base + 블록) → final HTML
[Stage 4] Selenium overflow + 비전 (-1 미평가)
```
### MDX별 상태
| MDX | 상태 | 비고 |
|-----|------|------|
| **03** | ✅ 동작 | prerequisites-3col + pp2. 텍스트 누락 없음. 회귀 기준. |
| **02** | ⚠ schema 1차 | top: parallel_3_with_image. bottom: 분류 정교화 필요. |
| **01** | ⬜ 미착수 | Type A. 별도 작업. |
### 핵심 원칙 (확립됨)
- source of truth = normalized.sections (Stage 0)
- 영역 = 코드가 결정 (Kei 아님). sub_titles 기반 + group schema.
- 블록 CSS에 최종 고정값. slide_font_css는 공통 레이아웃 계약만.
- zone = % 기반, block = height:100%.
- 글씨 크기 고정. fit은 padding → 내용량 → font 1단계(responsive tier).
- 기존 경로 삭제 금지. 새 schema 점진적 추가. MDX 03 회귀 기준.
- 하드코딩 금지. 프로세스가 결과를 만드는 구조.
3. **블록 글씨 크기 하드코딩 (px 고정)**
- 블록 CSS에 font-size가 Figma 원본 px로 고정
- 컨테이너 크기에 따라 조정 불가 → overflow 원인
- CSS 변수(`var(--block-font-heading)`)로 전환 → assembler가 zone 크기에 따라 계산
### 미해결 프로세스
1. **overflow 시 font 조정 루프** — 재배분만으로 부족할 때 font/padding 줄이기 (Y-5)
2. **Sonnet redesign 경로** — tag 매칭 실패 시 블록 단위 redesign → 저장 (Y-6)
---
## 8. 검증 계획
업데이트된 템플릿이 파이프라인에서 제대로 동작하는지 확인한다.
| MDX | 유형 | 검증 포인트 |
|-----|------|-----------|
| **01번** | Type A | 업데이트된 블록 + slide-base.html로 조립 정상 동작 |
| **02번** | Type B | 업데이트된 블록 선택 + 조립 + overflow 없음 |
| **03번** | Type B' | 카드/표 구조가 업데이트된 템플릿으로 정상 렌더링 |
### 검증 방법
1. 각 MDX를 파이프라인에 투입
2. 중간 산출물(step1_analysis.json 등) 확인 — 블록 선택이 의도대로인지
3. 최종 HTML(final.html) 렌더링 — overflow, 시각 품질 확인
4. 업데이트 전/후 비교