Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26319f6bd9 | ||
|
|
edfc3f29df |
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cmd /c \"npx -y figma-developer-mcp --help\")",
|
||||
"Bash(npx -y figma-developer-mcp --version)",
|
||||
"mcp__Framelink_Figma_MCP__get_figma_data",
|
||||
"mcp__Framelink_Figma_MCP__download_figma_images",
|
||||
"Bash(start \"\" \"d:/ad-hoc/kei/design_agent/figma_to_html_agent/block-tests/prerequisites-3col.html\")",
|
||||
"Bash(python -c \"from selenium import webdriver; print\\('selenium OK'\\)\")",
|
||||
"Bash(python -c ':*)",
|
||||
"Bash(python)",
|
||||
"Bash(start \"\" \"d:/ad-hoc/kei/design_agent/figma_to_html_agent/block-tests/bim-goal-circles.html\")",
|
||||
"Bash(start \"\" \"d:/ad-hoc/kei/design_agent/figma_to_html_agent/block-tests/bg-shapes-only.html\")",
|
||||
"Bash(start \"\" \"d:/ad-hoc/kei/design_agent/figma_to_html_agent/block-tests/bim-figma-devmode.html\")",
|
||||
"Bash(claude mcp:*)",
|
||||
"Bash(curl -sS -o /dev/null -w \"mcp endpoint: HTTP %{http_code}\\\\n\" http://127.0.0.1:3845/mcp)",
|
||||
"Bash(curl -sS -o /dev/null -w \"sse endpoint: HTTP %{http_code}\\\\n\" http://127.0.0.1:3845/sse)",
|
||||
"Bash(curl -sS -o /dev/null -w \"root: HTTP %{http_code}\\\\n\" http://127.0.0.1:3845/)",
|
||||
"Bash(curl -s -o NUL -w \"%{http_code}\" http://127.0.0.1:3845/mcp)",
|
||||
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://127.0.0.1:3845/sse --max-time 3)",
|
||||
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://127.0.0.1:3845/mcp --max-time 3)",
|
||||
"Bash(curl -s -X POST http://127.0.0.1:3845/mcp -H \"Content-Type: application/json\" -H \"Accept: application/json, text/event-stream\" -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' --max-time 5)",
|
||||
"Bash(curl -v http://127.0.0.1:3845/mcp)",
|
||||
"Bash(curl -s -m 3 http://127.0.0.1:3845/sse)",
|
||||
"Bash(curl -s -m 3 -o /dev/null -w \"%{http_code}\\\\n\" http://127.0.0.1:3845/sse)",
|
||||
"mcp__figma-desktop__get_metadata",
|
||||
"mcp__figma-desktop__get_screenshot",
|
||||
"mcp__figma-desktop__get_design_context",
|
||||
"Bash(curl -sSo bg_texture.png \"http://localhost:3845/assets/849658071be46a26936e8666e3722b2dd548aee7.png\")",
|
||||
"Bash(curl -sSo arc_top.png \"http://localhost:3845/assets/f05ebf15a1125b6c5809f9ffa35b4e4e750687d3.png\")",
|
||||
"Bash(curl -sSo arc_side.png \"http://localhost:3845/assets/2f0f17507c681b7bc2fb109f3d4fafb9ff2f7ce0.png\")",
|
||||
"Bash(curl -sSo big_fill_productivity.svg \"http://localhost:3845/assets/21a89b8138bd47debcc6f12bb140ee63bbd9fdf2.svg\")",
|
||||
"Bash(curl -sSo big_ring_productivity.svg \"http://localhost:3845/assets/fbe84134d2e14bbf84b2c42516e9b85ffe6f7c1e.svg\")",
|
||||
"Bash(curl -sSo big_fill_safety.svg \"http://localhost:3845/assets/1f24875931dc3c36e2c841eaf5b94466fa035a48.svg\")",
|
||||
"Bash(curl -sSo big_ring_safety.svg \"http://localhost:3845/assets/c5aeccdfc884051848fc60f04abf2a9d367dd731.svg\")",
|
||||
"Bash(curl -sSo big_fill_trust.svg \"http://localhost:3845/assets/67ef527c29921d401d31032c02d6b3a0ae1d3050.svg\")",
|
||||
"Bash(curl -sSo acc_outer_speed.svg \"http://localhost:3845/assets/1391787caa4cb8241a1adadbb2c70aed3625e1b8.svg\")",
|
||||
"Bash(curl -sSo acc_inner_speed.svg \"http://localhost:3845/assets/eeb8e9bf8b1841215ae0253017512a7e4a6d5a95.svg\")",
|
||||
"Bash(curl -sSo acc_inner_profit.svg \"http://localhost:3845/assets/4885055cba20f72e83401be371fe74b9b43ec869.svg\")",
|
||||
"Bash(curl -sSo acc_outer_safety.svg \"http://localhost:3845/assets/688b5af1d813b16cd6410453e3d4d1f79c084222.svg\")",
|
||||
"Bash(curl -sSo acc_inner_safety.svg \"http://localhost:3845/assets/2fab268821fc763dbdff12e1dd65820dfa9b628e.svg\")",
|
||||
"Bash(ls block-tests/*.html block-tests/*.md)",
|
||||
"Bash(python scripts/gradient_math.py --test)",
|
||||
"Bash(python scripts/gradient_math.py --w 350 --h 350 --x1 110.833 --y1 18.2292 --x2 219.479 --y2 175 --stops \"0:#FDC69E,1:#E0782C\")",
|
||||
"Bash(python -c \"from scripts.gradient_math import svg_to_css; print\\(svg_to_css\\(W=350,H=350,x1=110.833,y1=18.2292,x2=219.479,y2=175,stops=[\\(0,'#FDC69E'\\),\\(1,'#E0782C'\\)]\\)\\)\")",
|
||||
"Bash(python render.py cards-3col-persona example)",
|
||||
"Bash(python render.py cards-3col-persona example-no-photos)",
|
||||
"Bash(python render.py cycle-3way-intersect example)",
|
||||
"WebFetch(domain:claude.com)",
|
||||
"WebFetch(domain:help.figma.com)",
|
||||
"Bash(curl -sSo \"527bd7809f4b2e5f3cd42f2e713ccbfb37537d82.png\" \"http://localhost:3845/assets/527bd7809f4b2e5f3cd42f2e713ccbfb37537d82.png\")",
|
||||
"Bash(ls \"d:/ad-hoc/kei/design_agent/figma_to_html_agent/block-tests/_renders/pill_flex_\"*)",
|
||||
"Bash(awk '/visual_diff:/{found=1} found && /^- id:/{print NR\": \"$0; found=0}' \"d:/ad-hoc/kei/design_agent/templates/catalog.yaml\")",
|
||||
"Bash(curl -s -o \"bg_slide_texture.png\" \"http://localhost:3845/assets/16a1b2ea5b64663a3ee44bfad24671a612952c29.png\")",
|
||||
"Bash(curl -s -o \"line_divider.svg\" \"http://localhost:3845/assets/01731a60f7d9d35816932c019149e301a3aae1a7.svg\")",
|
||||
"Bash(head -20 /d/ad-hoc/kei/design_agent/samples/mdx/01*.mdx)",
|
||||
"Bash(head -20 /d/ad-hoc/kei/design_agent/samples/mdx/02*.mdx)",
|
||||
"Bash(head -20 /d/ad-hoc/kei/design_agent/samples/mdx/03*.mdx)",
|
||||
"Bash(ls -la /d/ad-hoc/kei/design_agent/data/runs/20260413_*/)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"d:\\ad-hoc\\kei\\design_agent\\templates\\blocks\\new"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"figma-desktop": {
|
||||
"type": "sse",
|
||||
"url": "http://127.0.0.1:3845/sse"
|
||||
"Framelink Figma MCP": {
|
||||
"command": "cmd",
|
||||
"args": [
|
||||
"/c",
|
||||
"npx",
|
||||
"-y",
|
||||
"figma-developer-mcp",
|
||||
"--figma-api-key=figd_s23TfSDL0hS97DIialy0R2P6QsoZQHfuGx1l_t-k",
|
||||
"--stdio"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
# ⚠ DEPRECATED — 이 문서는 2026-03-27 기준 스냅샷입니다
|
||||
|
||||
> **최신 파이프라인 문서는 [`PIPELINE.md`](PIPELINE.md)를 참조하세요.**
|
||||
> 이 문서는 Type A/B 분기, B'/B'' 변형, Stage 1.7/1.8 등 현재 구조를 반영하지 않습니다.
|
||||
> 히스토리 참고용으로만 유지합니다.
|
||||
|
||||
---
|
||||
|
||||
# Design Agent 전체 구조 파악 리포트 (archived)
|
||||
# Design Agent 전체 구조 파악 리포트
|
||||
|
||||
**작성일:** 2026-03-27
|
||||
**목표:** design_agent의 아키텍처, 파이프라인, 코드 구조를 체계적으로 이해
|
||||
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
# Phase X: 템플릿 기반 동적 레이아웃
|
||||
|
||||
> 작성일: 2026-04-06
|
||||
> 상태: 계획 수립
|
||||
|
||||
---
|
||||
|
||||
## 배경
|
||||
|
||||
현재 파이프라인은 모든 MDX를 "배경/본심/첨부/결론" 4칸에 억지로 끼워넣는다.
|
||||
배경이 없는 콘텐츠도 배경을 만들어내고, 3분할이 적절한 콘텐츠도 2분할로 넣는다.
|
||||
Kei가 내용을 잘못 파악한 게 아니라, **"4칸을 채워라"는 지시가 잘못**된 것이다.
|
||||
|
||||
## 핵심 아이디어
|
||||
|
||||
**미리 정의된 레이아웃 템플릿 중 Kei가 콘텐츠에 맞는 것을 선택한다.**
|
||||
|
||||
- Kei가 자유롭게 구조를 만드는 것이 아님 (불안정)
|
||||
- 옵션을 주고 고르게 함 (안정적)
|
||||
- 하드코딩 아님 — 어떤 MDX가 와도 적절한 템플릿이 선택됨
|
||||
|
||||
## 고정 영역
|
||||
|
||||
모든 템플릿 공통:
|
||||
- **상단**: 슬라이드 제목 헤더 (항상 존재)
|
||||
- **하단**: 결론 footer (항상 존재)
|
||||
- **중간**: 템플릿에 따라 달라지는 영역
|
||||
|
||||
## 중간 영역 템플릿 옵션
|
||||
|
||||
### A. body + sidebar
|
||||
```
|
||||
┌──────────┬─────┐
|
||||
│ 본심1 │참조 │
|
||||
│ 본심2 │ │
|
||||
└──────────┴─────┘
|
||||
```
|
||||
적합: 참조자료(용어 정의 등)가 별도로 있는 콘텐츠
|
||||
예시: 01번 MDX (DX/BIM 용어 정립)
|
||||
|
||||
### B. 상단 wide + 하단 2분할
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 강조/핵심 │
|
||||
├────────┬────────┤
|
||||
│ 항목1 │ 항목2 │
|
||||
└────────┴────────┘
|
||||
```
|
||||
적합: 핵심 1개 + 두 가지 측면 비교/설명
|
||||
예시: 03번 MDX (필수요건 + 과정혁신/결과변화)
|
||||
|
||||
### C. 상단 wide + 하단 3분할
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 강조/핵심 │
|
||||
├─────┬─────┬─────┤
|
||||
│항목1│항목2│항목3 │
|
||||
└─────┴─────┴─────┘
|
||||
```
|
||||
적합: 핵심 1개 + 세 가지 항목 병렬
|
||||
예시: 02번 MDX (궁극적 목표 + 발주처/설계사/시공사)
|
||||
|
||||
### D. 2분할 (좌우 대등)
|
||||
```
|
||||
┌────────┬────────┐
|
||||
│ 항목1 │ 항목2 │
|
||||
│ │ │
|
||||
└────────┴────────┘
|
||||
```
|
||||
적합: 두 가지 비교/대비
|
||||
|
||||
### E. 단일 전체
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ │
|
||||
│ 본심 (전체) │
|
||||
│ │
|
||||
└─────────────────┘
|
||||
```
|
||||
적합: 하나의 흐름, 분할 불필요
|
||||
|
||||
### F. 상단 wide + 하단 2분할 + 보조 sidebar
|
||||
```
|
||||
┌─────────────┬───┐
|
||||
│ 강조/핵심 │참조│
|
||||
├──────┬──────┤ │
|
||||
│항목1 │항목2 │ │
|
||||
└──────┴──────┴───┘
|
||||
```
|
||||
적합: B + sidebar 조합
|
||||
|
||||
---
|
||||
|
||||
## 프로세스
|
||||
|
||||
```
|
||||
1. Kei가 MDX 원본을 읽고 내용 분석
|
||||
→ 핵심 메시지, 콘텐츠 구조 파악
|
||||
|
||||
2. Kei가 꼭지를 나눔 (개수와 역할명 자유)
|
||||
→ "핵심목표 1개, 주체별 기대효과 3개, 결론 1개"
|
||||
→ 역할명: 고정 4칸 아님. 콘텐츠에 맞는 이름 사용
|
||||
|
||||
3. Kei가 템플릿 선택
|
||||
→ "꼭지 구조를 보니 C템플릿이 맞다"
|
||||
→ 옵션 A~F 중 하나
|
||||
|
||||
4. Kei가 각 영역에 꼭지 배정 + weight
|
||||
→ 상단: 핵심목표(0.3)
|
||||
→ 하단좌: 발주처(0.2), 하단중: 설계사(0.2), 하단우: 시공사(0.2)
|
||||
→ footer: 결론(0.1)
|
||||
|
||||
5. 파이프라인이 템플릿대로 컨테이너 생성 → BEFORE
|
||||
|
||||
6. 콘텐츠 채움 → 측정 → 재배분 → FILLED → AFTER
|
||||
|
||||
7. 조립 → code_assembled / final
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 작업 리스트
|
||||
|
||||
### X-1: 템플릿 정의
|
||||
- 옵션 A~F의 구체적 컨테이너 구조 정의
|
||||
- 각 옵션의 zone 이름, 비율 계산 공식, 좌표 계산 로직
|
||||
- `src/design_director.py`에 `LAYOUT_TEMPLATES` 정의
|
||||
- 하드코딩 아님: 템플릿은 구조만 정의, 크기는 weight와 슬라이드 크기에서 동적 계산
|
||||
|
||||
### X-2: Kei 프롬프트 수정
|
||||
- `KEI_PROMPT`에 템플릿 A~F 옵션 제시
|
||||
- Kei가 콘텐츠를 보고 `layout: "C"` 선택
|
||||
- page_structure의 역할명이 자유 (배경/본심 고정 아님)
|
||||
- 각 역할에 zone 배정 (상단/하단좌/하단우 등)
|
||||
- 하드코딩 아님: Kei가 콘텐츠마다 다른 선택을 함
|
||||
|
||||
### X-3: space_allocator 템플릿 기반 컨테이너 생성
|
||||
- 선택된 템플릿에 따라 컨테이너 좌표/크기 생성
|
||||
- weight 비율로 각 영역 크기 결정
|
||||
- `select_preset()` → `build_layout_from_template()`
|
||||
- 하드코딩 아님: 템플릿 구조 + weight + 슬라이드 크기로 동적 계산
|
||||
|
||||
### X-4: block_assembler / assemble_stage2 동적 역할
|
||||
- `["배경", "본심", "첨부", "결론"]` 고정 루프 → `page_structure.keys()` 동적 루프
|
||||
- 좌표 계산은 X-3에서 생성한 컨테이너 정보 사용
|
||||
- 색상/폰트: 역할 수에 맞게 동적 배분
|
||||
- 하드코딩 아님: 역할 수가 3개든 5개든 동작
|
||||
|
||||
### X-5: 나머지 파일 동적화
|
||||
- step_visualizer: before/after 시각화에서 동적 역할 루프
|
||||
- fit_verifier: 4역할 고정 → 동적 역할
|
||||
- html_generator: Sonnet에게 동적 영역 수만큼 생성 요청
|
||||
- renderer: 동적 grid-template 생성
|
||||
- 하드코딩 아님: 모두 ctx.containers.keys() 기반
|
||||
|
||||
### X-6: 검증
|
||||
- 01번 MDX → A템플릿 → 기존과 동일하거나 더 나은 결과
|
||||
- 02번 MDX → C템플릿 → 상단 강조 + 하단 3분할
|
||||
- 03번 MDX → B템플릿 → 상단 요건 + 하단 2분할
|
||||
- 텍스트가 컨테이너 안에 있음
|
||||
- 공란 최소
|
||||
- 01번이 깨지면 롤백
|
||||
|
||||
---
|
||||
|
||||
## 주의사항
|
||||
|
||||
- 하드코딩 절대 금지: 특정 MDX에만 동작하는 코드 없음
|
||||
- 01번 보호: Phase X 전에 git commit 완료 (1f7579c). 깨지면 롤백
|
||||
- 점진적 진행: X-1 → X-2 후 Kei 응답 확인 → X-3~X-5 순차 진행
|
||||
- 각 단계마다 검증
|
||||
|
||||
---
|
||||
|
||||
## 관련 코드 (고정 역할 참조 현황)
|
||||
|
||||
| 파일 | 참조 수 | 수정 범위 |
|
||||
|------|---------|----------|
|
||||
| src/html_generator.py | 54건 | X-5 |
|
||||
| src/step_visualizer.py | 32건 | X-5 |
|
||||
| src/space_allocator.py | 26건 | X-3 |
|
||||
| scripts/assemble_stage2.py | 26건 | X-4 |
|
||||
| src/kei_client.py | 18건 | X-2 |
|
||||
| src/block_assembler.py | 17건 | X-4 |
|
||||
| src/fit_verifier.py | 16건 | X-5 |
|
||||
| src/pipeline.py | 15건 | X-3~X-5 |
|
||||
| src/renderer.py | 7건 | X-5 |
|
||||
| src/pipeline_context.py | 4건 | 필요 시 |
|
||||
| **합계** | **215건** | |
|
||||
-304
@@ -1,304 +0,0 @@
|
||||
# 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. 업데이트 전/후 비교
|
||||
@@ -393,36 +393,6 @@ P2-E (누락기능) ── 병렬 │
|
||||
|
||||
---
|
||||
|
||||
## Phase Y: MDX 외부 컴포넌트 인라인 삽입
|
||||
|
||||
> 근거: MDX에서 `import ... from '*.astro'`로 불러오는 외부 컴포넌트(표, 다이어그램 등)가 파이프라인에서 누락됨. import문은 제거되고 `<DxEffect />` 같은 태그는 사라져서 콘텐츠 손실 발생.
|
||||
|
||||
### Y-1: import문 파싱 — 컴포넌트명:파일경로 매핑
|
||||
- **파일:** `src/mdx_normalizer.py`
|
||||
- **내용:** `import Foo from '../../components/foo.astro'` → `{"Foo": 절대경로}` 매핑 추출
|
||||
- **의존성:** base_path (MDX 원본 파일 위치, pipeline.py에서 전달)
|
||||
- **완료 기준:** import문에서 컴포넌트명→절대경로 dict 반환
|
||||
|
||||
### Y-2: .astro 파일 파싱 — HTML + CSS 추출
|
||||
- **파일:** `src/mdx_normalizer.py`
|
||||
- **내용:** .astro 파일에서 `---` frontmatter 제거, HTML 본문 + `<style>` 블록 추출
|
||||
- **의존성:** Y-1
|
||||
- **완료 기준:** dx.astro → `<div class="table-wrapper">...</div>` + `<style>...</style>` 반환
|
||||
|
||||
### Y-3: 셀프클로징 태그 교체 — 인라인 삽입
|
||||
- **파일:** `src/mdx_normalizer.py`
|
||||
- **내용:** `<DxEffect />` 태그를 Y-2에서 추출한 HTML+CSS로 교체
|
||||
- **의존성:** Y-1, Y-2
|
||||
- **완료 기준:** MDX 정규화 결과에 외부 컴포넌트 HTML이 인라인으로 포함
|
||||
|
||||
### Y-4: Astro 특수 문법 정리
|
||||
- **파일:** `src/mdx_normalizer.py`
|
||||
- **내용:** Astro의 멀티라인 태그(`<td class="category-cell">텍스트</td>` 줄바꿈 패턴), `style="letter-spacing: -0.9px"` 등 인라인 스타일 정리
|
||||
- **의존성:** Y-2
|
||||
- **완료 기준:** 추출된 HTML이 브라우저에서 정상 렌더링
|
||||
|
||||
---
|
||||
|
||||
## 의존 관계
|
||||
|
||||
```
|
||||
|
||||
@@ -1,322 +1,303 @@
|
||||
# C.E.L. Slide Pipeline
|
||||
# Kei Design Agent
|
||||
|
||||
MDX 기반 콘텐츠를 분석해 1280x720 슬라이드 HTML로 변환하는 파이프라인입니다.
|
||||
콘텐츠를 시각적으로 구조화된 슬라이드 HTML(1280×720px, 16:9)로 변환하는 AI 파이프라인.
|
||||
|
||||
이 문서는 현재 코드 기준으로 이 프로젝트가 무엇을 하는지, 어떤 프로세스로 동작하는지, 현재 어디까지 와 있는지, 앞으로 무엇을 개선하려는지를 빠르게 파악하기 위한 개요 문서입니다.
|
||||
## 개요
|
||||
|
||||
## 무엇을 하는가
|
||||
텍스트/MDX 콘텐츠를 입력하면:
|
||||
1. Kei 실장(Opus)이 정보 구조와 비중을 판단하고
|
||||
2. 코드가 컨테이너 크기를 계산하고
|
||||
3. 블록을 선택하고
|
||||
4. 콘텐츠-컨테이너 적합성을 검증하고
|
||||
5. AI(Sonnet)가 블록 디자인을 참고하여 HTML을 생성하고
|
||||
6. 코드가 슬라이드 프레임에 조립하고
|
||||
7. 측정+비전 모델로 검증합니다
|
||||
|
||||
이 프로젝트는 MDX 문서를 입력으로 받아 다음 과정을 거쳐 슬라이드 결과물을 생성합니다.
|
||||
---
|
||||
|
||||
- 콘텐츠 정규화
|
||||
- 문서 의미 분석
|
||||
- 슬라이드 구조 해석
|
||||
- schema / recipe / block 선택
|
||||
- HTML 조립
|
||||
- fit / overflow 검증
|
||||
- 최종 산출물 저장
|
||||
## 파이프라인 (10단계)
|
||||
|
||||
최종 산출물은 보통 다음 형태로 저장됩니다.
|
||||
|
||||
- `final.html`
|
||||
- `final_context.json`
|
||||
- `steps/*.html`
|
||||
- popup / detail HTML
|
||||
|
||||
## 한눈에 보는 프로세스
|
||||
|
||||
```text
|
||||
MDX Input
|
||||
```
|
||||
MDX 원본
|
||||
↓
|
||||
Stage 0 Normalize
|
||||
[Stage 0] MDX 정규화 (코드)
|
||||
↓
|
||||
Stage 1A/1B AI Meaning Analysis
|
||||
[Stage 1A] 꼭지 추출 + 영역 배정 (Kei API / Opus)
|
||||
↓
|
||||
Phase Y Group / Schema / Recipe Parsing
|
||||
[Stage 1B] 컨셉 구체화 (Kei API / Opus)
|
||||
↓
|
||||
Stage 1.5a Space Allocation
|
||||
[Stage 1.5a] 컨테이너 초기 계산 (코드)
|
||||
↓
|
||||
Stage 1.7 Block Selection
|
||||
[Stage 1.7] 블록 선택 (코드)
|
||||
↓
|
||||
Stage 1.8 Fit / Measure / Adjustment
|
||||
[Stage 1.8] 적합성 검증 + 재배분 + 보강 (코드 + Kei 에스컬레이션)
|
||||
↓
|
||||
Stage 2 Assemble final slide HTML
|
||||
[Stage 1.5b] 디자인 예산 재계산 (코드)
|
||||
↓
|
||||
Stage 4 Validate with Selenium / Vision
|
||||
[Stage 2] HTML 생성 (영역별 개별 호출) (Claude Sonnet)
|
||||
↓
|
||||
Stage 5 Save final artifacts
|
||||
[Stage 3] 렌더링 조립 + 후처리 (코드)
|
||||
↓
|
||||
[Stage 4] 측정 + 품질 검증 (Selenium + Opus Vision)
|
||||
↓
|
||||
검증 통과 시 → final.html 저장 + 팝업 분리 (파일 출력)
|
||||
```
|
||||
|
||||
핵심 원칙은 다음과 같습니다.
|
||||
|
||||
- source of truth는 `normalized.sections`
|
||||
- 문서명 하드코딩보다 shape, schema, recipe, tag를 우선한다
|
||||
- 흐름의 우선순위는 `구조 -> payload -> layout -> fit`
|
||||
- popup / detail은 overflow를 덮는 임시 장치가 아니라 `메인 요약 + 상세 보기`의 표현 계약이다
|
||||
|
||||
## 타입 구조
|
||||
|
||||
현재 메인 타입 선택은 사실상 `Type A`와 `Type B`입니다.
|
||||
|
||||
### Type A
|
||||
|
||||
- 본문 외에 sidebar, reference, 부록성 영역이 함께 필요한 슬라이드
|
||||
- 현재는 Type B보다 덜 닫혀 있고, AI 생성 + renderer 경로 비중이 더 큼
|
||||
|
||||
### Type B
|
||||
|
||||
- top / bottom 같은 본문 zone 조합으로 해결되는 슬라이드
|
||||
- 현재 가장 안정적인 메인 경로
|
||||
- 최근 구조화 작업은 대부분 이 Type B 경로를 중심으로 진행됨
|
||||
|
||||
### Type B' / B''
|
||||
|
||||
- 역사적으로 실험/호환 과정에서 나온 변형 경로
|
||||
- 일부 legacy 코드와 과거 산출물에 흔적이 남아 있음
|
||||
- 현재 메인 1급 타입이라기보다, 과거 흐름과 검증된 표시 계약을 담고 있는 보조 경로에 가까움
|
||||
|
||||
## 단계별 파이프라인
|
||||
|
||||
| 단계 | 담당 | 주요 파일 | 하는 일 | 주요 산출물 |
|
||||
|---|---|---|---|---|
|
||||
| Stage 0 | 코드 | `src/mdx_normalizer.py` | MDX를 정규화하고 sections, tables, images, popups로 분리 | `NormalizedContent` |
|
||||
| Stage 1A | AI (Kei/Opus) | `src/kei_client.py` | title, core message, topic, 초기 layout 힌트 추출 | `Analysis`, `Topic[]` |
|
||||
| Phase Y | 코드 | `src/pipeline.py`, `src/section_parser.py` | `normalized.sections` 기반으로 group, schema, recipe, zone/page_structure 결정 | `PageStructure`, `mdx_sections` |
|
||||
| Stage 1B | AI (Kei/Opus) | `src/kei_client.py` | topic별 relation, source_data, 표현 힌트 보강 | 보강된 `Topic[]` |
|
||||
| Stage 1B-ST | AI (Kei/Opus) | `src/kei_client.py` | structured_text 생성 | `Topic.structured_text` |
|
||||
| Stage 1.5a | 코드 | `src/space_allocator.py` | zone / container 크기, preset, font hierarchy 계산 | `containers`, `font_hierarchy` |
|
||||
| Stage 1.7 | 코드 | `src/block_reference.py`, `templates/catalog.yaml` | tag_match, schema_match, fallback 기준으로 block 선택 | `references` |
|
||||
| Stage 1.8 | 코드 + Selenium + 일부 AI | `src/pipeline.py`, `src/slide_measurer.py` | fit 측정, overflow 확인, 재배분, 보정 | `fit_result`, `measurement` |
|
||||
| Stage 2 | 코드 중심 | `src/block_assembler.py` | Type B 기준 slide-base + block template + payload 조립 | `generated_html` |
|
||||
| Stage 3 | 코드 | `src/renderer.py` | Type A 쪽 Jinja/renderer 조립, Type B는 대체로 생략 | `rendered_html` |
|
||||
| Stage 4 | 코드 + Vision AI | `src/slide_measurer.py`, `src/kei_client.py` | Selenium overflow 측정, screenshot, vision quality 평가 | `measurement`, `quality_score` |
|
||||
| Stage 5 | 코드 | `src/pipeline.py` | `final.html`, `final_context.json`, popup/detail html 저장 | run 산출물 |
|
||||
|
||||
## 현재 메인 실행 경로
|
||||
|
||||
### Type B 메인 경로
|
||||
|
||||
지금 실전에서 가장 중요한 경로는 아래입니다.
|
||||
|
||||
1. Stage 0에서 MDX를 정규화
|
||||
2. Stage 1A / 1B에서 AI가 문서 의미와 topic 추출
|
||||
3. Phase Y에서 코드가 `normalized.sections`를 읽고 page_structure를 다시 생성
|
||||
4. Stage 1.7에서 block / recipe 후보 선택
|
||||
5. Stage 1.8에서 fit / overflow 검증
|
||||
6. Stage 2에서 `assemble_slide_html_final()`로 최종 HTML 조립
|
||||
7. Stage 4 / 5에서 측정과 산출물 저장
|
||||
|
||||
Type B의 핵심 파일은 아래입니다.
|
||||
|
||||
- `src/pipeline.py`
|
||||
- `src/section_parser.py`
|
||||
- `src/block_reference.py`
|
||||
- `src/block_assembler.py`
|
||||
- `src/space_allocator.py`
|
||||
- `templates/catalog.yaml`
|
||||
|
||||
### Type A 경로
|
||||
|
||||
Type A는 현재도 살아 있지만, Type B만큼 단단하게 닫힌 상태는 아닙니다.
|
||||
|
||||
- AI 생성 비중이 더 큼
|
||||
- `src/renderer.py` 의존도가 더 큼
|
||||
- sidebar / reference 구조를 포함하는 문서에서 의미가 큼
|
||||
|
||||
## schema -> recipe -> block
|
||||
|
||||
최근 구조화에서 가장 중요한 변화 중 하나는 `schema -> recipe -> block` 레이어입니다.
|
||||
|
||||
### schema
|
||||
|
||||
콘텐츠의 의미 구조입니다.
|
||||
|
||||
예:
|
||||
|
||||
- `parallel_cluster`
|
||||
- `parallel_cluster_plus_visual`
|
||||
- `compare_asymmetric_paired`
|
||||
- `sequence_plus_visual`
|
||||
- `single_block`
|
||||
|
||||
### recipe
|
||||
|
||||
block 이름이 아니라 표현 규칙입니다.
|
||||
|
||||
예:
|
||||
|
||||
- `single_block`
|
||||
- `two_col_text_visual`
|
||||
- `two_col_text_detail`
|
||||
- `stacked_summary_detail`
|
||||
|
||||
recipe는 보통 이런 계약을 가질 수 있습니다.
|
||||
|
||||
- left / right kind
|
||||
- top / bottom kind
|
||||
- ratio
|
||||
- vertical align
|
||||
- direct render 우선 여부
|
||||
|
||||
### block
|
||||
|
||||
실제 구현 템플릿 후보입니다.
|
||||
|
||||
예:
|
||||
|
||||
- `prerequisites-3col`
|
||||
- `process-product-2col`
|
||||
- `compare-detail-gradient`
|
||||
- `card-icon-desc`
|
||||
|
||||
즉, “무슨 문서냐”보다 “무슨 구조냐”를 먼저 읽고, 그 구조에 맞는 표현 규칙을 정한 뒤, 마지막에 구현 block을 고르는 방향으로 가고 있습니다.
|
||||
|
||||
## popup / detail 계약
|
||||
|
||||
popup은 현재 다음 철학으로 정리되는 중입니다.
|
||||
|
||||
- 메인 슬라이드에는 zone 크기에 맞는 요약만 남긴다
|
||||
- 큰 표, 시각 컴포넌트, 과다 bullet은 상세 popup으로 분리한다
|
||||
- 메인에서는 `자세히보기` 링크를 제공한다
|
||||
|
||||
현재 popup 관련 핵심은 아래입니다.
|
||||
|
||||
- `PopupItem` 모델로 popup 데이터를 명시적으로 다루기 시작함
|
||||
- `popup_id`와 `popup_file` 생애주기를 분리해 관리 중
|
||||
- 최종 목표는 popup 판단을 휴리스틱이 아니라 명시적 contract로 만드는 것
|
||||
|
||||
다만 아직 일부 구간에는 추측 로직과 이중 관리가 남아 있어, 이 부분은 계속 정리 중입니다.
|
||||
|
||||
## run 산출물 구조
|
||||
|
||||
각 실행은 `data/runs/{run_id}/` 아래에 저장됩니다.
|
||||
|
||||
주요 파일은 다음과 같습니다.
|
||||
|
||||
- `final.html`
|
||||
- `final_context.json`
|
||||
- `steps/*.html`
|
||||
- popup / detail HTML
|
||||
|
||||
### final.html
|
||||
|
||||
- 최종 렌더 결과
|
||||
- 실제 눈으로 보는 산출물
|
||||
|
||||
### final_context.json
|
||||
|
||||
- 각 단계 결과를 최종 context 형태로 저장
|
||||
- block 선택, page_structure, measurement, quality_score 등을 확인할 때 가장 중요
|
||||
|
||||
### steps/*.html
|
||||
|
||||
- 단계별 디버그 / 설명용 보드
|
||||
- 현재도 검토용으로 유용하지만, 일부 인코딩과 설명 품질은 더 다듬을 필요가 있음
|
||||
|
||||
## 자주 봐야 하는 파일
|
||||
|
||||
### 파이프라인 핵심
|
||||
|
||||
- [src/pipeline.py](src/pipeline.py)
|
||||
- [src/pipeline_context.py](src/pipeline_context.py)
|
||||
- [src/section_parser.py](src/section_parser.py)
|
||||
- [src/block_reference.py](src/block_reference.py)
|
||||
- [src/block_assembler.py](src/block_assembler.py)
|
||||
- [src/space_allocator.py](src/space_allocator.py)
|
||||
|
||||
### 템플릿 / 카탈로그
|
||||
|
||||
- [templates/catalog.yaml](templates/catalog.yaml)
|
||||
- [templates/blocks/new/prerequisites-3col.html](templates/blocks/new/prerequisites-3col.html)
|
||||
- [templates/blocks/redesign/process-product-2col.html](templates/blocks/redesign/process-product-2col.html)
|
||||
- [templates/blocks/cards/compare-detail-gradient.html](templates/blocks/cards/compare-detail-gradient.html)
|
||||
- `templates/blocks/slide-base.html`
|
||||
|
||||
### 검증 / 측정
|
||||
|
||||
- [src/slide_measurer.py](src/slide_measurer.py)
|
||||
- [src/validators.py](src/validators.py)
|
||||
- [src/step_visualizer.py](src/step_visualizer.py)
|
||||
|
||||
### 계획 / 히스토리
|
||||
|
||||
- [PIPELINE.md](PIPELINE.md)
|
||||
- [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md)
|
||||
|
||||
## 현재 상태
|
||||
|
||||
### 비교적 잘 닫혀가는 것
|
||||
|
||||
- Type B 메인 경로
|
||||
- `normalized.sections` 기반 구조 해석
|
||||
- schema / recipe 기반 block selection의 골격
|
||||
- redesign block 자산화
|
||||
- popup / detail 2단 표현 계약의 초안 연결
|
||||
|
||||
### 아직 정리 중인 것
|
||||
|
||||
- Type A 전체 안정화
|
||||
- popup을 완전한 source of truth로 정리
|
||||
- tag_match 와 schema_match의 완전한 동등 점수 비교
|
||||
- step 보드 인코딩 / 설명 품질
|
||||
- fit loop의 공간 재분배 고도화
|
||||
- legacy 경로와 문서 정리
|
||||
|
||||
### 최근 Type B에서 특히 중요해진 방향
|
||||
|
||||
- recipe direct render가 block 선택에 끌려가지 않도록 구조 계약을 더 강하게 만든다
|
||||
- Type B direct render가 Type B'의 검증된 표시 계약을 최대한 재사용하도록 정리한다
|
||||
- sample은 복제 대상이 아니라 evaluation rule의 기준으로 사용한다
|
||||
- validation은 “같아야 한다”보다 “어긋나면 안 된다”에 초점을 둔다
|
||||
|
||||
## 향후 개선 방향
|
||||
|
||||
현재 이후의 개선 방향은 아래 축으로 정리됩니다.
|
||||
|
||||
### 1. 구조 계약 강화
|
||||
|
||||
- top / bottom zone의 contract를 더 구체화
|
||||
- `parallel_cluster_plus_visual`, `full_text + detail_preview` 같은 recipe를 표현 범주 수준으로 강화
|
||||
- recipe가 block를 끌고 가는 것이 아니라 recipe가 block를 통제하도록 정리
|
||||
|
||||
### 2. 표시 계약 통합
|
||||
|
||||
- Type B direct render가 Type B'에서 이미 검증된 bullet / indent / body-text 구조를 재사용하도록 통합
|
||||
- `.rdr-*` 계열 신규 CSS를 계속 키우기보다, 기존 검증된 계약을 최대한 재사용
|
||||
|
||||
### 3. detail preview 개선
|
||||
|
||||
- popup source가 표면 `헤더 + 일부 행`
|
||||
- popup source가 리스트면 `앞부분 몇 개`
|
||||
- popup source가 컴포넌트면 구조화된 preview
|
||||
|
||||
즉 “링크만 있는 상세”가 아니라 “상세가 있다는 걸 바로 이해할 수 있는 preview”를 만드는 방향
|
||||
|
||||
### 4. fit loop 고도화
|
||||
|
||||
지금은 주로 overflow 대응 중심이지만, 앞으로는 아래까지 확장하려고 합니다.
|
||||
|
||||
- 빈 공간 감지
|
||||
- 이미지 확대
|
||||
- preview 행 수 조정
|
||||
- zone 비율 재배분
|
||||
|
||||
### 5. 검증판과 final 일치화
|
||||
|
||||
- `stage_4` 보드와 실제 `final_context.json` / `final.html`의 상태 차이를 줄이기
|
||||
- 검수 보드를 더 믿을 수 있는 상태로 정리
|
||||
|
||||
## 읽는 순서 추천
|
||||
|
||||
프로세스를 빠르게 파악하려면 아래 순서가 좋습니다.
|
||||
|
||||
1. 이 `README.md`
|
||||
2. [src/pipeline.py](src/pipeline.py)
|
||||
3. [src/section_parser.py](src/section_parser.py)
|
||||
4. [src/block_assembler.py](src/block_assembler.py)
|
||||
5. 최근 run의 `final_context.json`
|
||||
|
||||
히스토리와 설계 변화까지 보려면 아래 문서를 이어서 보면 좋습니다.
|
||||
|
||||
- [PIPELINE.md](PIPELINE.md)
|
||||
- [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md)
|
||||
※ Stage 4 이후의 파일 저장은 별도 Stage가 아닌 후처리입니다.
|
||||
|
||||
---
|
||||
|
||||
## 단계별 상세
|
||||
|
||||
### Stage 0: MDX 정규화
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 원본 MDX에서 JSX/frontmatter를 제거하고, 섹션/팝업/이미지/테이블로 분리 |
|
||||
| **적용기술** | 코드 (`normalize_mdx_content()`) |
|
||||
| **인풋** | 원본 MDX 문자열 |
|
||||
| **아웃풋** | `normalized` — clean_text, title, sections[], popups[], images[], tables[] |
|
||||
| **연계** | → Stage 1A가 clean_text를 Kei에게 전달 |
|
||||
|
||||
### Stage 1A: 꼭지 추출 + 영역 배정
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 콘텐츠에서 핵심 파트(꼭지)를 식별하고, 슬라이드의 어떤 영역(배경/본심/첨부/결론)에 배치할지 결정 |
|
||||
| **적용기술** | Kei API (`classify_content()`) |
|
||||
| **인풋** | normalized.clean_text |
|
||||
| **아웃풋** | `topics[]` (id, title, purpose, layer, relation_type, expression_hint), `page_structure` (role별 topic_ids, weight) |
|
||||
| **연계** | → Stage 1B가 각 꼭지를 구체화 |
|
||||
|
||||
### Stage 1B: 컨셉 구체화
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 각 꼭지에 실제 원본 텍스트(source_data)와 요약(summary)을 매핑 |
|
||||
| **적용기술** | Kei API (`refine_concepts()`) |
|
||||
| **인풋** | topics + clean_text |
|
||||
| **아웃풋** | `topics` 업데이트 — source_data, summary 추가 |
|
||||
| **연계** | → Stage 1.5a가 텍스트 양을 기반으로 컨테이너 비율 계산 |
|
||||
|
||||
### Stage 1.5a: 컨테이너 초기 계산
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 폰트 위계 확정 + 슬라이드 내 영역별 컨테이너 크기(px) 계산 + 프리셋 선택 |
|
||||
| **적용기술** | 코드 (`calculate_font_hierarchy()`, `calculate_dynamic_ratio()`, `calculate_container_specs()`) |
|
||||
| **인풋** | topics, page_structure (weight), preset |
|
||||
| **아웃풋** | `font_hierarchy` (key_msg/core/bg/sidebar px), `container_ratio` (71:29 등), `containers` (role별 width_px, height_px), `preset` |
|
||||
| **연계** | → Stage 1.7이 컨테이너 크기를 보고 블록 선택 |
|
||||
|
||||
### Stage 1.7: 블록 선택
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 각 꼭지의 relation_type + expression_hint + 컨테이너 크기로 적합한 블록 결정. 같은 영역 꼭지들의 layer가 다르면 주종관계 판단 (블록 1개로 합침) |
|
||||
| **적용기술** | 코드 (`select_and_generate_references()`) — catalog.yaml 기반 결정론적 매칭 |
|
||||
| **인풋** | topics, containers, page_structure |
|
||||
| **아웃풋** | `references` — role별 block_id, variant, design_reference_html, topic_id, is_hierarchical, supporting_topic_ids |
|
||||
| **연계** | → Stage 1.8이 선택된 블록+콘텐츠가 컨테이너에 맞는지 검증 |
|
||||
|
||||
### Stage 1.8: 적합성 검증 + 재배분 + 보강 + 서브 컨테이너
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 콘텐츠가 컨테이너에 들어가는지 검증 → 안 맞으면 재배분 → 여전히 안 되면 Kei 에스컬레이션 → 여유 공간에 보충 콘텐츠 → 서브 컨테이너 배치 계산 |
|
||||
| **적용기술** | 코드 (`calculate_fit()`, `redistribute()`, `analyze_enhancements()`, `apply_enhancements()`, `calculate_sub_layout()`) + Kei API (에스컬레이션 시 `call_kei_fit_escalation()`) |
|
||||
| **인풋** | topics, containers, references, font_hierarchy, normalized, core_message |
|
||||
| **아웃풋** | `containers` (재배분된 height_px), `fit_result` (role별 fit_status, redistribution), `enhancement_result` (V-7 subordinate_treatments, V-8 supplement_blocks, V-9 emphasis_blocks, V-10 bold_keywords, V-4 kei_decisions), `sub_layouts` (role별 서브 컨테이너 name/width/height, table_rows) |
|
||||
| **내부 흐름** | Step 1: 필요 높이 계산 → Step 2: 재배분 → Step 3: Kei 에스컬레이션 → Step 4-5: 보강 분석+적용 → Step 6: fit 재검증 → Step 7: 서브 컨테이너 배치 → Step 8: 확정 |
|
||||
| **연계** | → Stage 1.5b가 재배분된 크기로 디자인 예산 재계산, → Stage 2가 sub_layouts + enhancements를 프롬프트에 반영 |
|
||||
|
||||
### Stage 1.5b: 디자인 예산 재계산
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 재배분된 컨테이너 크기 + 선택된 블록 schema 기준으로 영역별 가용 공간 계산 |
|
||||
| **적용기술** | 코드 (`calculate_design_budget()`) |
|
||||
| **인풋** | containers (재배분 후), references (블록 schema) |
|
||||
| **아웃풋** | `containers` 업데이트 — design_budget (available_height_px, available_width_px, fits) |
|
||||
| **연계** | → Stage 2가 design_budgets를 프롬프트에 포함 |
|
||||
|
||||
### Stage 2: HTML 생성 (영역별 개별 호출)
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | page_structure에 존재하는 각 역할(배경/본심/첨부/결론)의 HTML을 **영역별 개별 Sonnet 호출**로 생성. 블록 디자인을 참고하되 콘텐츠가 구조를 결정 (Phase R' 방식) |
|
||||
| **적용기술** | Claude Sonnet API — 영역당 1회 호출 (`build_area_prompt()` → `_call_claude()`) |
|
||||
| **인풋** | raw_content, topics, containers, font_hierarchy, references (design_reference_html), sub_layouts (서브 컨테이너 치수), enhancements (V-4~V-10 지시), design_budgets |
|
||||
| **호출 흐름** | Sonnet(배경) → bg_html, Sonnet(본심) → core_html, Sonnet(첨부) → sidebar_html, Sonnet(결론) → footer_html. 해당 역할에 꼭지가 없으면 스킵. body_html = bg_html + spacer + core_html |
|
||||
| **아웃풋** | `generated_html` — body_html, sidebar_html, footer_html |
|
||||
| **프롬프트에 포함되는 것** | 서브 컨테이너 레이아웃 제약, 디자인 레퍼런스 HTML (블록 CSS 참고), Kei 에스컬레이션 결정, 종속 꼭지 처리 지시, 보충 블록 지시, 강조 문장, bold 키워드, 폰트/컨테이너 크기 제약 |
|
||||
| **연계** | → Stage 3이 영역별 HTML을 슬라이드 프레임에 배치 |
|
||||
|
||||
### Stage 3: 렌더링 조립 + 후처리
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | 생성된 HTML 조각을 CSS Grid 슬라이드 프레임에 삽입 + 후처리 (폰트 캡핑, overflow 제거, sidebar width 조정, bold 변환) |
|
||||
| **적용기술** | 코드 (`render_slide_from_html()`) |
|
||||
| **인풋** | generated_html, preset (grid_areas, grid_columns), font_hierarchy, container_ratio |
|
||||
| **아웃풋** | `rendered_html` → `final.html` 파일 저장 |
|
||||
| **연계** | → Stage 4가 렌더링 결과를 측정+검증 |
|
||||
|
||||
### Stage 4: 품질 검증
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목적** | Selenium으로 실제 브라우저 렌더링 후 overflow 측정 + Opus Vision으로 시각적 품질 평가 |
|
||||
| **적용기술** | Selenium (`measure_rendered_heights()`) + Claude Opus Vision (`vision_quality_gate()`) |
|
||||
| **인풋** | rendered_html |
|
||||
| **아웃풋** | `measurement` (zone별 clientHeight, scrollHeight, overflow, excess_px), `quality_score` |
|
||||
| **연계** | 파이프라인 완료. overflow 시 경고 포함하여 진행 |
|
||||
|
||||
---
|
||||
|
||||
## 중간 산출물
|
||||
|
||||
파이프라인 실행마다 `data/runs/{timestamp}/`에 단계별 결과가 저장된다.
|
||||
|
||||
### JSON Context (Stage별 누적 상태)
|
||||
| 파일 | Stage | 내용 |
|
||||
|------|-------|------|
|
||||
| `stage_0_context.json` | 0 | normalized (섹션, 팝업, 이미지) |
|
||||
| `stage_1a_context.json` | 1A | topics, page_structure |
|
||||
| `stage_1b_context.json` | 1B | topics (source_data 추가) |
|
||||
| `stage_1_5a_context.json` | 1.5a | font_hierarchy, containers, ratio |
|
||||
| `stage_1_7_context.json` | 1.7 | references (블록 선택 결과) |
|
||||
| `stage_1_8_context.json` | 1.8 | fit_result, enhancements, sub_layouts |
|
||||
| `stage_1_5b_context.json` | 1.5b | containers (design_budget 추가) |
|
||||
| `stage_2_context.json` | 2 | generated_html |
|
||||
| `stage_3_context.json` | 3 | (rendered_html은 final.html로 별도 저장) |
|
||||
| `stage_4_context.json` | 4 | measurement, quality_score |
|
||||
| `final_context.json` | 최종 | 전체 context |
|
||||
|
||||
### HTML 시각화 (`steps/` 폴더)
|
||||
| 파일 | Stage | 내용 |
|
||||
|------|-------|------|
|
||||
| `stage_0.html` | 0 | 섹션/팝업/이미지 목록 |
|
||||
| `stage_1a.html` | 1A | 꼭지 테이블 (purpose, layer, 영역) |
|
||||
| `stage_1b.html` | 1B | 꼭지 + source_data + summary |
|
||||
| `stage_1_5a.html` | 1.5a | 빈 컨테이너 (1280×720) |
|
||||
| `stage_1_5a_content.html` | 1.5a | 컨테이너에 콘텐츠 배치 |
|
||||
| `stage_1_5b.html` | 1.5b | 디자인 예산 (available height/width) |
|
||||
| `stage_1_7.html` | 1.7 | 블록 선택 표시 |
|
||||
| `stage_1_8_fit_before.html` | 1.8 | 적합성 (재배분 전) |
|
||||
| `stage_1_8_fit_after.html` | 1.8 | 재배분 후 + 보강 |
|
||||
| `stage_1_8_blocks.html` | 1.8 | SLOT 구조 + 블록 디자인 + 주종관계 (1280×720) |
|
||||
| `stage_2.html` | 2 | 영역별 Sonnet 출력을 실제 렌더링 (역할별 개별 확인) |
|
||||
| `stage_3.html` | 3 | 영역을 합쳐 슬라이드 프레임에 배치한 결과 (1280×720 실제 렌더링) |
|
||||
| `stage_4.html` | 4 | 측정 결과 + 품질 점수 |
|
||||
|
||||
---
|
||||
|
||||
## 핵심 원칙
|
||||
|
||||
1. **콘텐츠가 구조를 결정** — 블록 CSS는 참고만. AI가 콘텐츠 전달 의도를 보고 HTML 구조 결정 (Phase R')
|
||||
2. **하드코딩 금지** — font-size 외 모든 수치는 동적 계산. 어떤 MDX가 들어와도 동일하게 동작
|
||||
3. **스크롤 절대 금지** — overflow:auto/scroll 어떤 영역에서도 불허
|
||||
4. **Kei API 필수** — fallback 없음. 성공할 때까지 무한 재시도
|
||||
5. **AI가 옵션 생성, Kei가 결정** — 공간 부족 시 하드코딩 대응이 아니라 Kei 판단 요청
|
||||
6. **계산 먼저, AI 판단 나중에, 렌더링은 검증만**
|
||||
7. **overflow 상태에서 출력 금지** — Vision 모델 품질 게이트 통과 필수
|
||||
|
||||
---
|
||||
|
||||
## 블록 라이브러리 (38개)
|
||||
|
||||
6개 카테고리, 38개 블록. 각 블록은 `catalog.yaml`에 용도(when), 금지(not_for), purpose_fit, schema(슬롯 정의)가 있음.
|
||||
|
||||
| 카테고리 | 개수 | 용도 |
|
||||
|---------|------|------|
|
||||
| **headers** | 5 | 타이틀, 꼭지 헤더 |
|
||||
| **cards** | 9 | 항목 나열, 카드 그리드 |
|
||||
| **tables** | 3 | 비교표, 데이터 테이블 |
|
||||
| **visuals** | 6 | SVG 다이어그램, 관계도 |
|
||||
| **emphasis** | 10 | 강조, 인용, 결론, 불릿 |
|
||||
| **media** | 5 | 이미지/사진 |
|
||||
|
||||
---
|
||||
|
||||
## 기술 스택
|
||||
|
||||
| 역할 | 도구 |
|
||||
|------|------|
|
||||
| 서버 | FastAPI + uvicorn (포트 8001) |
|
||||
| AI (Kei 실장/편집자) | Kei API → Opus (localhost:8000) |
|
||||
| AI (HTML 생성) | Anthropic API → Claude Sonnet |
|
||||
| AI (품질 검증) | Anthropic API → Claude Opus Vision |
|
||||
| 블록 검색 | FAISS + bge-m3 |
|
||||
| 템플릿 | Jinja2 (블록 디자인 레퍼런스용) |
|
||||
| 렌더링 | CSS Grid + 디자인 토큰 (1280×720) |
|
||||
| 렌더링 측정 | Selenium headless Chrome |
|
||||
| SVG 시각화 | svg_calculator.py (N개 동적 배치) |
|
||||
| 이미지 | Pillow (크기 측정) + base64 인라인 |
|
||||
| 폰트 | Pretendard Variable |
|
||||
| 공간 계산 | space_allocator.py + fit_verifier.py (결정론적) |
|
||||
|
||||
---
|
||||
|
||||
## 설치 및 실행
|
||||
|
||||
```bash
|
||||
# 설치
|
||||
cd design_agent
|
||||
pip install -e .
|
||||
|
||||
# FAISS 인덱스 빌드 (블록 추가/수정 시)
|
||||
python scripts/build_block_index.py
|
||||
|
||||
# .env 설정
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
KEI_API_URL=http://localhost:8000
|
||||
LOG_LEVEL=DEBUG
|
||||
```
|
||||
|
||||
```bash
|
||||
# 터미널 1: Kei API (필수)
|
||||
cd D:\ad-hoc\kei\persona_agent
|
||||
python -m uvicorn backend.main:app --host 127.0.0.1 --port 8000
|
||||
|
||||
# 터미널 2: Design Agent
|
||||
cd D:\ad-hoc\kei\design_agent
|
||||
python -m uvicorn src.main:app --host 127.0.0.1 --port 8001 --reload
|
||||
```
|
||||
|
||||
접속: http://localhost:8001
|
||||
|
||||
---
|
||||
|
||||
## 개선 이력
|
||||
|
||||
| Phase | 내용 | 상태 |
|
||||
|-------|------|------|
|
||||
| A~D | 슬라이드 품질 핵심 | 완료 |
|
||||
| G~N | Kei API, 스토리라인, 정합성, 블록 선택, 비중, 측정 | 완료 |
|
||||
| O | 컨테이너 기반 레이아웃 | 완료 |
|
||||
| P | 다후보 렌더링 비교 | 완료 (20/100점 → 방향 전환) |
|
||||
| Q | 제약 기반 블록 선택 | 완료 |
|
||||
| R | 하이브리드 블록 (실패 — P=Q=R 동일 구조) | 실패 |
|
||||
| R' | 블록 CSS 참고 + AI 구조 결정 | 설계 확정 |
|
||||
| S | 검증 합격 프롬프트 + Claude HTML 생성 | 설계 확정 |
|
||||
| T | 11-Stage 파이프라인 + 디자인 레퍼런스 | 완료 (31/31 통과) |
|
||||
| V | 적합성 검증 + Kei 에스컬레이션 + 서브 컨테이너 | 완료 |
|
||||
| W | Stage 2 출력 품질 수정 (6건) | 진행 중 |
|
||||
|
||||
---
|
||||
|
||||
## Kei Persona와의 관계
|
||||
|
||||
```
|
||||
Kei Persona Agent (localhost:8000)
|
||||
├── Opus + RAG + 세션 컨텍스트
|
||||
├── 도메인 지식 (건설/DX/BIM)
|
||||
└── 대화/생성/피드백/실행 모드
|
||||
|
||||
Design Agent (localhost:8001, 이 프로젝트)
|
||||
├── 슬라이드 생성 전용
|
||||
├── Kei API로 꼭지 추출(1A) + 컨셉 구체화(1B) + 에스컬레이션(1.8) 호출
|
||||
├── Sonnet으로 HTML 생성(Stage 2)
|
||||
├── Opus Vision으로 품질 검증(Stage 4)
|
||||
└── 두 프로젝트는 독립. 코드 공유 없음. API 연동만.
|
||||
```
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# 45개 블록 BLOCK_SLOTS — design_director.py에 반영 필요
|
||||
# 다른 쪽 작업 완료 후 교체
|
||||
|
||||
BLOCK_SLOTS = {
|
||||
# headers/
|
||||
"section-title-with-bg": {"required": ["title_ko"], "optional": ["title_en", "breadcrumb", "bg_image"]},
|
||||
"section-header-bar": {"required": ["title"], "optional": ["subtitle"]},
|
||||
"topic-left-right": {"required": ["title", "description"], "optional": []},
|
||||
"topic-center": {"required": ["title"], "optional": ["subtitle", "description"]},
|
||||
"topic-numbered": {"required": ["number", "title"], "optional": ["description", "color"]},
|
||||
# cards/
|
||||
"card-image-3col": {"required": ["cards"], "optional": []},
|
||||
"card-text-grid": {"required": ["cards"], "optional": []},
|
||||
"card-dark-overlay": {"required": ["cards"], "optional": []},
|
||||
"card-tag-image": {"required": ["cards"], "optional": []},
|
||||
"card-icon-desc": {"required": ["cards"], "optional": []},
|
||||
"card-compare-3col": {"required": ["cards"], "optional": []},
|
||||
"card-step-vertical": {"required": ["steps"], "optional": []},
|
||||
"card-image-round": {"required": ["cards"], "optional": []},
|
||||
"card-stat-number": {"required": ["stats"], "optional": []},
|
||||
"card-numbered": {"required": ["items"], "optional": []},
|
||||
# tables/
|
||||
"compare-3col-badge": {"required": ["headers", "rows"], "optional": []},
|
||||
"compare-2col-split": {"required": ["left_title", "right_title", "rows"], "optional": []},
|
||||
"table-simple-striped": {"required": ["headers", "rows"], "optional": []},
|
||||
# visuals/
|
||||
"venn-diagram": {"required": ["center_label", "items"], "optional": ["center_sub", "description"]},
|
||||
"circle-gradient": {"required": ["label"], "optional": ["sub_label"]},
|
||||
"compare-pill-pair": {"required": ["left_label", "right_label"], "optional": ["left_sub", "right_sub"]},
|
||||
"process-horizontal": {"required": ["steps"], "optional": []},
|
||||
"flow-arrow-horizontal": {"required": ["steps"], "optional": []},
|
||||
"keyword-circle-row": {"required": ["keywords"], "optional": []},
|
||||
"layer-diagram": {"required": ["layers"], "optional": ["title"]},
|
||||
"timeline-vertical": {"required": ["events"], "optional": []},
|
||||
"timeline-horizontal": {"required": ["events"], "optional": []},
|
||||
"pyramid-hierarchy": {"required": ["levels"], "optional": []},
|
||||
# emphasis/
|
||||
"quote-left-border": {"required": ["quote_text"], "optional": ["source"]},
|
||||
"quote-big-mark": {"required": ["quote_text"], "optional": ["source"]},
|
||||
"quote-question": {"required": ["question"], "optional": ["description"]},
|
||||
"conclusion-accent-bar": {"required": ["conclusion_text"], "optional": ["label"]},
|
||||
"comparison-2col": {"required": ["left_title", "left_content", "right_title", "right_content"], "optional": ["left_subtitle", "right_subtitle"]},
|
||||
"banner-gradient": {"required": ["text"], "optional": ["sub_text"]},
|
||||
"dark-bullet-list": {"required": ["bullets"], "optional": ["title"]},
|
||||
"highlight-strip": {"required": ["segments"], "optional": []},
|
||||
"callout-solution": {"required": ["title", "description"], "optional": ["icon", "source"]},
|
||||
"callout-warning": {"required": ["title", "description"], "optional": ["icon"]},
|
||||
"tab-label-row": {"required": ["tabs"], "optional": []},
|
||||
"divider-text": {"required": ["text"], "optional": []},
|
||||
# media/
|
||||
"image-row-2col": {"required": ["images"], "optional": []},
|
||||
"image-grid-2x2": {"required": ["images"], "optional": []},
|
||||
"image-side-text": {"required": ["image_src"], "optional": ["image_alt", "title", "description", "bullets"]},
|
||||
"image-full-caption": {"required": ["src"], "optional": ["alt", "caption"]},
|
||||
"image-before-after": {"required": ["before_src", "after_src"], "optional": ["before_label", "after_label", "caption"]},
|
||||
}
|
||||
|
||||
# _apply_defaults 용
|
||||
BLOCK_DEFAULTS = {
|
||||
"section-title-with-bg": {"title_ko": "(제목)"},
|
||||
"section-header-bar": {"title": "(섹션)"},
|
||||
"topic-left-right": {"title": "(소제목)", "description": ""},
|
||||
"topic-center": {"title": "(제목)"},
|
||||
"topic-numbered": {"number": "1", "title": "(단계)"},
|
||||
"card-image-3col": {"cards": []},
|
||||
"card-text-grid": {"cards": []},
|
||||
"card-dark-overlay": {"cards": []},
|
||||
"card-tag-image": {"cards": []},
|
||||
"card-icon-desc": {"cards": []},
|
||||
"card-compare-3col": {"cards": []},
|
||||
"card-step-vertical": {"steps": []},
|
||||
"card-image-round": {"cards": []},
|
||||
"card-stat-number": {"stats": []},
|
||||
"card-numbered": {"items": []},
|
||||
"compare-3col-badge": {"headers": [], "rows": []},
|
||||
"compare-2col-split": {"left_title": "A", "right_title": "B", "rows": []},
|
||||
"table-simple-striped": {"headers": [], "rows": []},
|
||||
"venn-diagram": {"center_label": "관계도", "items": [], "center_sub": "", "description": ""},
|
||||
"circle-gradient": {"label": "(라벨)"},
|
||||
"compare-pill-pair": {"left_label": "A", "right_label": "B"},
|
||||
"process-horizontal": {"steps": []},
|
||||
"flow-arrow-horizontal": {"steps": []},
|
||||
"keyword-circle-row": {"keywords": []},
|
||||
"layer-diagram": {"layers": []},
|
||||
"timeline-vertical": {"events": []},
|
||||
"timeline-horizontal": {"events": []},
|
||||
"pyramid-hierarchy": {"levels": []},
|
||||
"quote-left-border": {"quote_text": "(인용)"},
|
||||
"quote-big-mark": {"quote_text": "(인용)"},
|
||||
"quote-question": {"question": "(질문)"},
|
||||
"conclusion-accent-bar": {"conclusion_text": "(결론)"},
|
||||
"comparison-2col": {"left_title": "A", "left_content": "-", "right_title": "B", "right_content": "-"},
|
||||
"banner-gradient": {"text": "(배너)"},
|
||||
"dark-bullet-list": {"bullets": []},
|
||||
"highlight-strip": {"segments": []},
|
||||
"callout-solution": {"title": "(솔루션)", "description": ""},
|
||||
"callout-warning": {"title": "(경고)", "description": ""},
|
||||
"tab-label-row": {"tabs": []},
|
||||
"divider-text": {"text": "구분"},
|
||||
"image-row-2col": {"images": []},
|
||||
"image-grid-2x2": {"images": []},
|
||||
"image-side-text": {"image_src": ""},
|
||||
"image-full-caption": {"src": ""},
|
||||
"image-before-after": {"before_src": "", "after_src": ""},
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 936 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user