Phase N+O: 컨테이너 기반 레이아웃 + Step B 제거 + 전면 정리

- Phase N: catalog 개선, fallback 전면 제거, Kei API 무한 재시도, topic_id 버그 수정
- Phase O: 컨테이너 스펙 계산(비중→px), 블록 스펙 확정, 렌더러 container div
- Step B(Sonnet) 제거: Kei(A-2)+코드로 대체. STEP_B_PROMPT/fallback/DOWNGRADE_MAP 삭제
- Selenium: container div 감지 추가
- catalog.yaml: ref_chars 구조 변환 + FAISS 재빌드
- 문서 전면 갱신: README, PROGRESS, IMPROVEMENT, Phase I~O md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 15:20:51 +09:00
parent ffad1ba82a
commit b0bcffc0f6
28 changed files with 8450 additions and 1530 deletions

487
README.md
View File

@@ -1,374 +1,251 @@
# Kei Design Agent
콘텐츠를 시각적으로 구조화된 슬라이드 HTML로 변환하는 독립 에이전트.
콘텐츠를 시각적으로 구조화된 슬라이드 HTML(1280×720px, 16:9)로 변환하는 AI 파이프라인.
## 개요
텍스트/MDX 콘텐츠를 입력하면, AI가 정보 구조를 파악하고 적합한 레이아웃과 블록을 선택하여 깔끔한 1페이지(또는 다중 페이지) 슬라이드를 생성합니다.
텍스트/MDX 콘텐츠를 입력하면 Kei 실장(Opus)이 정보 구조와 비중을 판단하고, 그 비중대로 컨테이너를 확정하고, 블록을 선택하고, 텍스트를 편집하여 슬라이드를 생성다.
## 아키텍처 (5단계 파이프라인)
**핵심 특징:**
- 콘텐츠마다 비중이 동적으로 변한다 (본심 60% / 배경 20% 등 — Kei가 매번 판단)
- 비중이 컨테이너 px를 확정 → 블록과 텍스트가 컨테이너에 맞춰진다
- Kei API 필수. fallback 없음. 성공할 때까지 무한 재시도.
---
## 파이프라인 (6단계)
```
텍스트 입력 (+ 이미지 폴더 경로)
텍스트 입력
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1단계] Kei 실장 — 콘텐츠 분석 + 스토리라인 설계
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 사용 AI: Kei API (Opus)
│ fallback: manual_classify() (최소 구조 생성)
│ 1-A: 정보 구조 파악 + 꼭지 추출
│ - 핵심 메시지(core_message) 도출
│ - 본문 흐름(flow) vs 참조 정보(reference) 분리
│ - 각 꼭지의 레이어/강조/배치 방향 판단
│ - 이미지 판단 (개수/소속/핵심·보조/텍스트 포함 여부)
│ - 표 판단 (행/열 규모, 1페이지 표시 가능 여부)
│ - purpose 부여 (문제제기/근거사례/핵심전달/용어정의/결론강조/구조시각화)
│ 1-B: 각 꼭지 컨셉 구체화
│ - relation_type (비교/포함/계층/인과 등)
│ - expression_hint (표현 방향)
│ - source_data (원본에서 추출할 데이터)
│ 제목 중복 검증 (I-6)
│ - 슬라이드 제목 ↔ 첫 꼭지 제목 유사도 70% 초과 시 자동 교정
│ 이미지 크기 측정 (Pillow)
│ - base_path 있으면 이미지 파일 크기 측정 → analysis에 포함
[1단계] Kei 실장 — 꼭지 추출 + 비중 판단 (Kei API / Opus)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[2단계] 디자인 팀장 — 레이아웃 설계 + 블록 매핑
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ Step A-1: 레이아웃 프리셋 자동 선택 (규칙 기반, LLM 불필요)
│ - sidebar-right / two-column / hero-detail / single-column
│ - grid는 코드가 프리셋에서 강제 (AI가 변경 불가)
│ Step A-2: Opus(Kei API) 블록 추천 (FAISS 검색 결과 기반)
│ 사용 AI: Kei API (Opus)
│ - 도메인 지식 + 콘텐츠 성격 기반 블록 추천
│ - fallback: 추천 없이 Step B로
│ Step B: 블록 매핑 + 글자 수 가이드 (Sonnet)
│ 사용 AI: Anthropic API (Sonnet)
│ - Opus 추천 참고하되 최종 선택은 팀장 판단
│ - 컨테이너 예산(zone별 높이 px) 기반 블록 선택
│ - purpose 기반 블록 선택 가이드 참고
│ - 각 블록에 char_guide(글자 수 가이드) 부여
│ 블록 검증 (코드):
│ - 미등록 블록 → purpose 기반 fallback (PURPOSE_FALLBACK)
│ - 잘못된 zone → 기본 zone 자동 매핑
│ - conclusion 꼭지 → footer zone 강제
│ - compare-pill-pair 단독 사용 → comparison-2col 교체 (I-7)
│ - 금지 블록(section-title-with-bg) → body/sidebar에서 교체
│ 높이 예산 검증 (I-9):
│ - zone별 블록 높이 합산 vs budget_px 비교
│ - 초과 시 → overflow 정보 수집 (블록 자동 교체 안 함)
↓ (overflow 있으면)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[2.5단계] Kei 실장 — 넘침 판단 (I-9)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 사용 AI: Kei API (Opus)
│ 조건: 2단계에서 overflow 발생 시에만 실행
│ Kei에게 전달: 어떤 zone이 얼마나 초과, 블록/콘텐츠 요약, 대형 테이블/이미지 정보 (I-8)
│ Kei가 판단:
│ Option 1 "trim" → 텍스트 분량 제약 (char_guide 축소) → 3단계에서 반영
│ Option 2 "restructure" → 핵심 재구성 + 상세는 팝업(detail page) 분리
│ → detail_target 설정 후 2단계 재실행
│ Kei API 실패 시: DOWNGRADE_MAP 비상 작동 (기계적 블록 교체)
[1.5단계] Kei 실장 — 컨셉 구체화 (Kei API / Opus)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[3단계] Kei 텍스트 편집자 — 도메인 전문가로서 텍스트 정리
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 사용 AI: Kei API (Opus + RAG + 도메인 지식)
│ Sonnet fallback 없음 (Kei API만 사용)
│ - 각 블록의 슬롯에 맞게 텍스트 정리
│ - 슬롯 의미 설명(slot_desc) 참고하여 정확한 데이터 배치 (I-4, I-5)
│ - 글자 수 가이드 참고, 내용 의미 우선
│ - 2.5단계에서 trim 제약이 있으면 반영
│ - 원본 텍스트 최대 보존, 출처 보존, 개조식, 날조 금지
│ - detail_target 꼭지: summary + detail 두 버전 작성
[컨테이너 계산] 비중 → px 확정 (코드, 결정론적)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[4단계] 디자인 실무자 — 디자인 조정 + HTML 조립
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 사용 AI: Anthropic API (Sonnet) — CSS 변수 override 결정
│ 렌더링: Jinja2 + CSS Grid
│ - Sonnet이 텍스트 양에 맞게 CSS 변수 override 결정
│ (--font-body, --font-subtitle, --spacing-inner, --spacing-block 등)
│ - Jinja2로 블록 템플릿 렌더링
│ - CSS 변수 cascade로 area별 자동 적용
│ - SVG 시각화 블록: 좌표 사전 계산 (svg_calculator.py)
│ - 이미지 base64 인라인 삽입 (다운로드 HTML에서도 표시)
[2단계] 블록 확정 + 배치 (Kei API + Sonnet)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[5단계] 디자인 팀장 — 전체 재검토 (최대 2회 루프)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 사용 AI: Anthropic API (Sonnet) — HTML 기반 균형 점검
│ 점검 항목:
│ - 빈 블록 감지
│ - 채움 불균형 (한 블록은 빽빽, 다른 블록은 비어있음)
│ - 이미지/표 크기 적절성
│ - 전체 정보량 (페이지당 너무 많거나 적은지)
│ 조정 필요 시:
│ - expand: 텍스트 늘림 (target_ratio, 예: 1.3 = 30% 증가)
│ - shrink: 텍스트 줄임 (target_ratio, 예: 0.7 = 30% 감소)
│ - rewrite: 텍스트 재작성 (방향 명시)
│ → 3단계(Kei 편집자) 재호출 → 4단계 재렌더링 → 재검토
│ 조정 불필요 또는 2회 완료 시 확정
[블록 스펙 확정] 항목수/글자수/폰트 (코드, 결정론적)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
미리보기 + HTML 다운로드
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[3단계] Kei 편집자 — 텍스트 정리 (Kei API / Opus)
[4단계] 디자인 실무자 — CSS 조정 + HTML 조립 (Sonnet + Jinja2)
[Phase L] Selenium 렌더링 측정 → 피드백 루프
[5단계] Kei 실장 — 최종 검수 (스크린샷) (Opus 멀티모달)
완성 슬라이드 HTML
```
### 단계별 AI 담당
### 단계별 상세
| 단계 | 담당 | AI | session_id |
|------|------|-----|-----------|
| 1-A | Kei 실장 | Kei API (Opus) | `design-agent` |
| 1-B | Kei 실장 | Kei API (Opus) | `design-agent-refine` |
| 2 A-2 | Kei 실장 | Kei API (Opus) | `design-agent-opus` |
| 2 B | 디자인 팀장 | Anthropic (Sonnet) | — |
| 2.5 | Kei 실장 | Kei API (Opus) | `design-agent-overflow` |
| 3 | Kei 편집자 | Kei API (Opus) | `design-agent-editor` |
| 4 | 디자인 실무자 | Anthropic (Sonnet) | — |
| 5 | 디자인 팀장 | Anthropic (Sonnet) | |
| 단계 | 담당 | AI | 역할 |
|------|------|-----|------|
| **1A** | Kei 실장 | Kei API (Opus) | 핵심 메시지, 꼭지 추출, page_structure(비중), purpose 부여 |
| **1B** | Kei 실장 | Kei API (Opus) | relation_type, expression_hint, source_data |
| **컨테이너** | 코드 | — | Kei 비중 → 역할별 컨테이너 px 확정, height_cost 제약, 블록 스펙 |
| **2 A-2** | Kei 실장 | Kei API (Opus) | 컨테이너 제약 보고 블록 확정 (FAISS 후보 기반) |
| **2 B** | 디자인 팀장 | Sonnet | zone 배치 + char_guide만 (블록 타입 변경 불가) |
| **블록 스펙** | 코드 | — | 컨테이너 크기 → 항목수/글자수/폰트/패딩 확정 |
| **3** | Kei 편집자 | Kei API (Opus) | 텍스트 편집 (컨테이너 제약 준수, 원본 보존) |
| **4** | 디자인 실무자 | Sonnet | CSS 변수 override + Jinja2 렌더링 |
| **Phase L** | 코드 | Selenium | 렌더링 측정 → overflow 감지 → 편집자 재호출 |
| **5** | Kei 실장 | Opus | 스크린샷 보고 최종 검수 (멀티모달) |
### 핵심 원칙
- **비중 → 컨테이너 → 블록 → 콘텐츠** 순서. 비중이 모든 것을 결정
- **Kei API 필수.** fallback 없음. 기본값 없음. 성공할 때까지 무한 재시도
- **Sonnet은 zone 배치 + CSS 조정만.** 블록 선택/콘텐츠 판단 금지
- **블록 선택은 Kei가 확정 → 코드가 강제.** Sonnet이 변경 불가
- **텍스트가 기준.** 디자인이 텍스트에 맞춤. CSS로 사후 자르기 금지
---
## 컨테이너 시스템 (Phase O)
Kei가 판단한 비중이 시각적 레이아웃에 정확히 반영되는 구조.
```
슬라이드 1280×720px
├── header: 제목 (~60px 고정)
├── body (65%): 490px
│ ├── 배경 컨테이너: 490 × 20% = 98px ← Kei 비중으로 확정
│ │ └── 문제제기 + 근거사례 (compact 블록만)
│ └── 본심 컨테이너: 490 × 60% = 294px ← Kei 비중으로 확정
│ └── 핵심전달 (large/xlarge 블록 가능)
├── sidebar (35%): 490px
│ └── 첨부 컨테이너: 490px 전체
│ └── 용어 정의 (여유 있게)
└── footer: 결론 (~60px 고정)
└── banner-gradient (핵심 메시지 한 줄)
```
- 컨테이너 높이(px)가 블록의 height_cost를 제약
- 컨테이너 크기에서 항목수/글자수/폰트/패딩이 자동 계산
- 편집자에게 컨테이너 제약이 전달되어 텍스트 분량이 맞춰짐
---
## 개선 이력
| Phase | 내용 | 상태 |
|-------|------|------|
| **A~D** | 슬라이드 품질 핵심 (디자인 조정, overflow 방지, 이미지 처리) | 완료 |
| **G** | Kei API 통신 정상화 (SSE 스트리밍, Sonnet fallback 제거, GPU 분리) | 완료 |
| **H** | 스토리라인 설계 기반 전환 (core_message, purpose, source_hint) | 완료 |
| **I** | 전수 정합성 복구 + 넘침 처리 패러다임 전환 (14건) | 완료 |
| **J** | 블록 선택 권한 구조 재정의 + 최종 검토 Kei 전환 | 완료 |
| **K** | communicative role 기반 시각적 위계 + purpose별 분량 제약 | 완료 |
| **K-1** | 파이프라인 스텝별 중간 산출물 로컬 저장 (`data/runs/`) | 완료 |
| **L** | 렌더링 측정 에이전트 (Selenium headless) + 피드백 루프 | 완료 |
| **M** | Kei 비중 시스템 (page_structure weight) + 원본 보존 강화 | 완료 |
| **N** | 4대 핵심 문제 해결 — catalog 개선, fallback 전면 제거, topic_id 버그 수정, 무한 재시도 | 완료 |
| **O** | 컨테이너 기반 레이아웃 시스템 — 비중→px→블록제약→콘텐츠제약 | **진행 중** |
---
## 중간 산출물
파이프라인 실행마다 `data/runs/{timestamp}/`에 단계별 결과가 저장된다.
| 파일 | 단계 | 내용 |
|------|------|------|
| `step1_analysis.json` | 1A | 꼭지 추출, page_structure(비중), core_message |
| `step1b_concepts.json` | 1B | relation_type, expression_hint, source_data |
| `step1c_containers.json` | O-1 | 역할별 컨테이너 스펙 (height_px, width_px, max_height_cost) |
| `step2_layout.json` | 2 | 블록 배치 (area, type, purpose, reason) |
| `step2c_block_specs.json` | O-3 | 블록별 스펙 (_max_items, _max_chars, _font_size_px) |
| `step3_filled_blocks.json` | 3 | 텍스트 편집 결과 (data, char_count) |
| `step4_css_adjustment.json` | 4 | CSS 변수 override |
| `step4_rendered.html` | 4 | 렌더링된 HTML |
| `step4_measurement_round*.json` | Phase L | Selenium 측정 (scrollHeight, overflow) |
| `step5_review_round*.json` | 5 | Kei 검수 결과 |
| `final.html` | 최종 | 완성 슬라이드 |
| `report.html` | 리포트 | 전 단계 시각화 리포트 |
리포트 생성: `python scripts/generate_run_report.py`
---
## 블록 라이브러리 (38개)
```
templates/blocks/
├── INDEX.md 전체 인덱스
├── headers/ (5개) 타이틀, 꼭지 헤더
│ ├── section-title-with-bg.html 배경 이미지 + 영문/한글
│ ├── section-header-bar.html 파란 배경 바 + 제목
│ ├── topic-left-right.html 좌:제목 + 우:설명
│ ├── topic-center.html 중앙 정렬 제목
│ └── topic-numbered.html 번호 + 제목 + 설명
├── cards/ (9개) 카드 계열
│ ├── card-image-3col.html 이미지 카드 3열
│ ├── card-dark-overlay.html 다크 오버레이 카드
│ ├── card-tag-image.html 태그 + 이미지 카드
│ ├── card-icon-desc.html 아이콘 + 설명 카드
│ ├── card-compare-3col.html 비교 카드 3열
│ ├── card-step-vertical.html 세로 단계 카드
│ ├── card-image-round.html 원형 이미지 카드
│ ├── card-stat-number.html 큰 숫자 KPI 카드
│ └── card-numbered.html 번호 리스트 카드
├── tables/ (3개) 비교 테이블
│ ├── compare-3col-badge.html A|VS배지|B 3단 비교
│ ├── compare-2col-split.html 좌우 분할 비교
│ └── table-simple-striped.html 줄무늬 일반 테이블
├── visuals/ (6개) 다이어그램, 관계도 (SVG)
│ ├── venn-diagram.html 벤 다이어그램 (N개 동적)
│ ├── circle-gradient.html 그라데이션 원 + 텍스트
│ ├── compare-pill-pair.html 둥근 박스 2개 + VS
│ ├── process-horizontal.html 가로 단계 흐름
│ ├── flow-arrow-horizontal.html 가로 화살표 흐름
│ └── keyword-circle-row.html 키워드 원형 나열
├── emphasis/ (10개) 강조, 인용, 결론
│ ├── quote-big-mark.html 큰 따옴표 인용
│ ├── quote-question.html 질문형 강조
│ ├── comparison-2col.html 2단 비교
│ ├── banner-gradient.html 그라데이션 배너
│ ├── dark-bullet-list.html 다크 배경 불릿 리스트
│ ├── highlight-strip.html 하이라이트 스트립
│ ├── callout-solution.html 솔루션 콜아웃
│ ├── callout-warning.html 경고 콜아웃
│ ├── tab-label-row.html 탭 라벨 행
│ └── divider-text.html 텍스트 구분선
└── media/ (5개) 이미지/미디어
├── image-row-2col.html 이미지 2장 나란히
├── image-grid-2x2.html 이미지 2x2 그리드
├── image-side-text.html 이미지 + 텍스트
├── image-full-caption.html 전체 너비 이미지 + 캡션
└── image-before-after.html Before/After 비교
```
6개 카테고리, 38개 블록. 각 블록은 `catalog.yaml`에 용도(when), 금지(not_for), purpose_fit이 정의됨.
## FAISS 블록 검색
| 카테고리 | 개수 | 용도 |
|---------|------|------|
| **headers** | 5 | 타이틀, 꼭지 헤더 |
| **cards** | 9 | 항목 나열, 카드 그리드 |
| **tables** | 3 | 비교표, 데이터 테이블 |
| **visuals** | 6 | SVG 다이어그램, 관계도 |
| **emphasis** | 10 | 강조, 인용, 결론, 불릿 |
| **media** | 5 | 이미지/사진 |
38개 블록 전체를 프롬프트에 넣는 대신, FAISS로 꼭지별 관련 블록만 검색하여 전달합니다.
FAISS 블록 검색: bge-m3 1024차원 임베딩 → 꼭지별 관련 블록 후보 추출
```
꼭지 "A vs B 비교" → FAISS 검색 → comparison-2col, compare-pill-pair, compare-2col-split
꼭지 "연도별 로드맵" → FAISS 검색 → process-horizontal, flow-arrow-horizontal, card-step-vertical
```
- 임베딩 모델: BAAI/bge-m3 (1024차원, 한국어 최적화)
- 인덱스 빌드: `python scripts/build_block_index.py`
- fallback: 인덱스 없으면 catalog.yaml 전문 전달 (기존 방식)
## 레이아웃 프리셋
| 프리셋 | 조건 | CSS Grid | zone 예산 |
|--------|------|----------|----------|
| `sidebar-right` | reference 꼭지 있음 | 65:35 좌우 분할 | body 490px, sidebar 490px |
| `two-column` | 대등한 비교 | 50:50 균등 | left 490px, right 490px |
| `hero-detail` | 고강조 1개 + 보조 | hero 영역 + detail | hero 310px, detail 155px |
| `single-column` | 순차적 flow만 | 1열 | body 490px |
grid는 코드(Step A)가 결정. Sonnet은 blocks만 출력. grid 변경 불가.
---
## 기술 스택
| 역할 | 도구 |
|------|------|
| 서버 | FastAPI + uvicorn (포트 8001) |
| AI (1단계 실장) | Kei API (Opus) → fallback: Sonnet |
| AI (2단계 A-2) | Kei API (Opus) — 블록 추천 |
| AI (2단계 B) | Anthropic API (Sonnet) — 블록 매핑 |
| AI (3단계 편집자) | Kei API → fallback: Sonnet |
| AI (4단계 실무자) | Anthropic API (Sonnet) — CSS 조정 |
| AI (5단계 재검토) | Anthropic API (Sonnet) — 균형 점검 |
| 블록 검색 | FAISS + bge-m3 (38개 블록 인덱스) |
| 템플릿 | Jinja2 (카테고리별 블록 조합) |
| 렌더링 | CSS Grid + 디자인 토큰 (16:9, 1280×720) |
| SVG 시각화 | svg_calculator.py (cos/sin 좌표 계산, N개 동적) |
| 이미지 처리 | Pillow (크기 측정) + base64 인라인 |
| 폰트 | Pretendard Variable (한국어) |
| AI (Kei 실장/편집자) | Kei API Opus (localhost:8000) |
| AI (디자인 팀장/실무자) | Anthropic API → Sonnet |
| AI (최종 검수) | Anthropic API → Opus (멀티모달) |
| 블록 검색 | FAISS + bge-m3 |
| 템플릿 | Jinja2 |
| 렌더링 | CSS Grid + 디자인 토큰 (1280×720) |
| 렌더링 측정 | Selenium headless Chrome |
| SVG 시각화 | svg_calculator.py (N개 동적 배치) |
| 이미지 | Pillow (크기 측정) + base64 인라인 |
| 폰트 | Pretendard Variable |
| 공간 계산 | space_allocator.py (결정론적) |
---
## 설치 및 실행
### 설치
```bash
# 설치
cd design_agent
python -m venv .venv
.venv/Scripts/activate # Windows
pip install -e .
```
### FAISS 인덱스 빌드
```bash
# FAISS 인덱스 빌드 (블록 추가/수정 시)
python scripts/build_block_index.py
```
### 환경 변수
`.env` 파일:
```env
# .env 설정
ANTHROPIC_API_KEY=sk-ant-...
KEI_API_URL=http://localhost:8000
LOG_LEVEL=DEBUG
```
### 실행
```bash
# 터미널 1: Kei 백엔드 (Opus 실장 + 편집자 역할)
# 터미널 1: Kei API (필수)
cd D:\ad-hoc\kei\persona_agent
uvicorn backend.main:app --reload --host 127.0.0.1 --port 8000
python -m uvicorn backend.main:app --host 127.0.0.1 --port 8000
# 터미널 2: Design Agent
cd D:\ad-hoc\kei\design_agent
uvicorn src.main:app --reload --host 127.0.0.1 --port 8001
python -m uvicorn src.main:app --host 127.0.0.1 --port 8001 --reload
```
접속: http://localhost:8001
---
## 프로젝트 구조
```
design_agent/
├── CLAUDE.md 프로젝트 규칙 + 5단계 프로세스
├── PLAN.md 태스크 계획
├── PROGRESS.md 진행 상황
├── IMPROVEMENT.md 개선 계획 (Phase A~F)
├── IMPROVEMENT-PHASE-{A~D}.md 각 Phase 실행 상세
├── README.md 이 파일
├── pyproject.toml
├── .env API 키
├── src/ 파이프라인 코드
│ ├── main.py FastAPI 서버 (포트 8001)
│ ├── config.py 설정 (pydantic-settings)
│ ├── kei_client.py 1단계: Kei API → 꼭지 추출
── design_director.py 2단계: 프리셋 선택 + Opus 추천 + 블록 매핑
│ ├── content_editor.py 3단계: Kei API → 텍스트 정리
│ ├── pipeline.py 5단계 파이프라인 (디자인 조정 + 재검토 루프)
│ ├── renderer.py 4단계: HTML 조립 (SVG 전처리 + CSS 변수 override)
│ ├── block_search.py FAISS 블록 검색 모듈
│ ├── svg_calculator.py SVG 좌표 계산 (cos/sin N개 배치)
│ └── image_utils.py 이미지 크기 측정 + base64 삽입
├── scripts/
│ └── build_block_index.py FAISS 인덱스 빌드 스크립트
├── src/
│ ├── main.py FastAPI 서버 (포트 8001)
│ ├── config.py 설정 (pydantic-settings)
│ ├── pipeline.py 파이프라인 오케스트레이션 (6단계)
│ ├── kei_client.py Kei API 클라이언트 (1A, 1B, 검수, 넘침 판단)
│ ├── design_director.py 2단계: 프리셋 + Kei 블록 확정 + Sonnet zone 배치
│ ├── content_editor.py 3단계: Kei API 텍스트 편집
├── renderer.py 4단계: HTML 조립 (컨테이너 grid + Jinja2)
├── space_allocator.py 컨테이너 스펙 계산 + 블록 스펙 확정 (Phase O)
├── slide_measurer.py Selenium 렌더링 측정 + 스크린샷 (Phase L/N)
│ ├── block_search.py FAISS 블록 검색
│ ├── svg_calculator.py SVG 좌표 계산 (N개 동적 배치)
│ ├── image_utils.py 이미지 크기 측정 + base64 삽입
── sse_utils.py SSE 스트리밍 유틸
├── templates/
│ ├── slide-base.html 슬라이드 베이스 (다중 페이지 + 인쇄 JS)
│ ├── catalog.yaml 블록 카탈로그 (38개, height_cost 포함)
│ └── blocks/ 블록 라이브러리 (6 카테고리, 38개)
│ ├── INDEX.md 전체 인덱스
│ ├── headers/ (5) 타이틀, 꼭지 헤더
│ ├── cards/ (10) 카드 계열
│ ├── tables/ (3) 비교 테이블
│ ├── visuals/ (10) 다이어그램, 관계도 (SVG)
│ ├── emphasis/ (13) 강조, 인용, 결론, 자세히보기
│ ├── media/ (5) 이미지/미디어
│ └── media/ (5) 이미지/미디어
│ ├── slide-base.html 슬라이드 베이스
│ ├── catalog.yaml 블록 카탈로그 (38개, when/not_for/purpose_fit)
│ └── blocks/ 블록 라이브러리 (6 카테고리)
├── static/
│ ├── index.html 프론트엔드 (이미지 경로 입력 팝업 포함)
── tokens.css 디자인 토큰
│ └── base.css 기본 슬라이드 스타일
├── scripts/
│ ├── build_block_index.py FAISS 인덱스 빌드
── generate_run_report.py 실행 리포트 생성
├── data/ 로컬 데이터 (gitignored)
│ ├── block_index.faiss FAISS 벡터 인덱스
│ └── block_metadata.json 인덱스 메타데이터
├── static/ 프론트엔드 (index.html, CSS)
├── data/ 로컬 데이터 (runs/, FAISS 인덱스)
├── docs/ 기술 조사, Figma 분석
├── docs/
│ ├── RESEARCH.md 기술 조사
│ ├── PHASE2-PLAN.md Phase 2 계획
│ ├── PHASE2-PROCESS.md Phase 2 실행 프로세스
│ ├── PHASE2-TECH-REVIEW.md Phase 2 기술 검토
│ ├── figma-screenshots/ Figma 스크린샷 (16장)
│ ├── figma-assets/ Figma 에셋
│ ├── figma-analysis/ 노드 구조 분석
│ └── block-tests/ 블록 테스트 HTML
└── tests/
├── IMPROVEMENT.md 개선 계획 총괄 (Phase A~O)
├── IMPROVEMENT-PHASE-*.md 각 Phase 상세
└── PROGRESS.md 진행 상황 추적
```
## 핵심 원칙
- **모든 판단은 AI 사고. 하드코딩 없음**
- 텍스트가 기준. 디자인이 텍스트에 맞춤 (텍스트를 자르지 않음)
- 이미지 원본 그대로, 크기만 조절 (object-fit: contain)
- 컨테이너 예산(zone별 높이 px) 안에서 블록 배치
- grid는 코드가 결정. Sonnet은 blocks만 판단
- Kei API 1차 → Sonnet fallback (1단계, 3단계)
- Kei Persona Agent 코드를 수정하지 않음
---
## Kei Persona와의 관계
```
Kei Persona (본체) — localhost:5173/8000
대화/생성/피드백/실행 모드
Opus + RAG (bge-m3 + FAISS)
독립적으로 동작
Kei Persona Agent (localhost:8000)
── Opus + RAG + 세션 컨텍스트
── 도메인 지식 (건설/DX/BIM)
── 대화/생성/피드백/실행 모드
Design Agent (이 프로젝트) — localhost:8001
├ 슬라이드 생성 전용
├ Kei API로 실장(1단계) + 편집자(3단계) + 블록 추천(2단계 A-2) 호출
FAISS 블록 검색 (bge-m3, Kei와 동일 모델)
독립적으로 동작 (Kei 없이도 Sonnet fallback)
Design Agent (localhost:8001, 이 프로젝트)
── 슬라이드 생성 전용
── Kei API로 실장(1단계) + 편집자(3단계) + 블록 확정(2단계) 호출
── 최종 검수(5단계)는 Opus 직접 호출 (멀티모달 스크린샷)
── 두 프로젝트는 독립. 코드 공유 없음. API 연동만.
```
두 프로젝트는 완전히 독립. 코드 공유 없음. API 연동만.