bc7829b08bb2234b7e52edf247a260a6c66703f5
X-B-1: KEI_PROMPT에 유형 B 옵션 추가 - 유형 A: 기존 배경/본심/첨부/결론 (참조자료 있는 콘텐츠) - 유형 B: 본심1(상단)+본심2(하단2분할)+결론 (본문만으로 구성) - Kei가 콘텐츠 보고 A/B 선택, layout_template 필드로 반환 - 검증: 01번→A, 02번→B 정확히 선택 X-B-2: 검증기 완화 - 유형 A: 본심 필수 유지 - 유형 B: 결론(footer)만 필수, 자유 역할명 허용 - 섹션 수 차이 허용 확대 (유형 B: 4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kei Design Agent
콘텐츠를 시각적으로 구조화된 슬라이드 HTML(1280×720px, 16:9)로 변환하는 AI 파이프라인.
개요
텍스트/MDX 콘텐츠를 입력하면:
- Kei 실장(Opus)이 정보 구조와 비중을 판단하고
- 코드가 컨테이너 크기를 계산하고
- 블록을 선택하고
- 콘텐츠-컨테이너 적합성을 검증하고
- AI(Sonnet)가 블록 디자인을 참고하여 HTML을 생성하고
- 코드가 슬라이드 프레임에 조립하고
- 측정+비전 모델로 검증합니다
파이프라인 (10단계)
MDX 원본
↓
[Stage 0] MDX 정규화 (코드)
↓
[Stage 1A] 꼭지 추출 + 영역 배정 (Kei API / Opus)
↓
[Stage 1B] 컨셉 구체화 (Kei API / Opus)
↓
[Stage 1.5a] 컨테이너 초기 계산 (코드)
↓
[Stage 1.7] 블록 선택 (코드)
↓
[Stage 1.8] 적합성 검증 + 재배분 + 보강 (코드 + Kei 에스컬레이션)
↓
[Stage 1.5b] 디자인 예산 재계산 (코드)
↓
[Stage 2] HTML 생성 (영역별 개별 호출) (Claude Sonnet)
↓
[Stage 3] 렌더링 조립 + 후처리 (코드)
↓
[Stage 4] 측정 + 품질 검증 (Selenium + Opus Vision)
↓
검증 통과 시 → final.html 저장 + 팝업 분리 (파일 출력)
※ 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 | 측정 결과 + 품질 점수 |
핵심 원칙
- 콘텐츠가 구조를 결정 — 블록 CSS는 참고만. AI가 콘텐츠 전달 의도를 보고 HTML 구조 결정 (Phase R')
- 하드코딩 금지 — font-size 외 모든 수치는 동적 계산. 어떤 MDX가 들어와도 동일하게 동작
- 스크롤 절대 금지 — overflow:auto/scroll 어떤 영역에서도 불허
- Kei API 필수 — fallback 없음. 성공할 때까지 무한 재시도
- AI가 옵션 생성, Kei가 결정 — 공간 부족 시 하드코딩 대응이 아니라 Kei 판단 요청
- 계산 먼저, AI 판단 나중에, 렌더링은 검증만
- 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 (결정론적) |
설치 및 실행
# 설치
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
# 터미널 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
개선 이력
| 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 연동만.
Description
Languages
Python
86.6%
HTML
12.7%
CSS
0.7%