af07d247f6dea16ba773e5bc231ebddd2a62e68f
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kei Design Agent
콘텐츠를 시각적으로 구조화된 슬라이드 HTML로 변환하는 독립 에이전트.
개요
텍스트/MDX 콘텐츠를 입력하면, AI가 정보 구조를 파악하고 적합한 레이아웃과 블록을 선택하여 깔끔한 1페이지(또는 다중 페이지) 슬라이드를 생성합니다.
아키텍처 (5단계 파이프라인)
텍스트 입력 (+ 이미지 폴더 경로)
↓
[1] Kei 실장 (Kei API → Opus) — 정보 구조 파악 + 꼭지 추출
- 본문 흐름(flow) vs 참조 정보(reference) 분리
- 각 꼭지의 레이어/강조/배치 방향 판단
- 이미지 판단 (개수/소속/핵심·보조/텍스트 포함 여부)
- 표 판단 (행/열 규모, 1페이지 표시 가능 여부)
- fallback: Anthropic Sonnet 직접 호출
↓
[2] 디자인 팀장 — 3-Step
Step A-1: 레이아웃 프리셋 자동 선택 (규칙 기반)
- sidebar-right / two-column / hero-detail / single-column
Step A-2: Opus(Kei API) 블록 추천 (FAISS 검색 결과 기반)
- 도메인 지식 + 콘텐츠 성격 기반 블록 추천
- fallback: 추천 없이 Step B로
Step B: 프리셋 안에서 블록 매핑 + 글자 수 가이드 (Sonnet)
- Opus 추천 참고하되 최종 선택은 팀장 판단
- 컨테이너 예산(zone별 높이 px) 기반 블록 선택
- grid는 코드가 프리셋에서 강제 (Sonnet은 blocks만 출력)
↓
[3] Kei 텍스트 편집자 (Kei API) — 도메인 전문가로서 텍스트 정리
- 글자 수 가이드 참고, 내용 의미 우선
- 출처 보존, 개조식, 날조 금지
- fallback: Anthropic Sonnet 직접 호출
↓
[4] 디자인 실무자 (Sonnet + Jinja2 + CSS Grid) — 디자인 조정 + HTML 조립
- Sonnet이 텍스트 양에 맞게 CSS 변수 override 결정 (폰트/여백/간격)
- Jinja2로 블록 템플릿 렌더링 + CSS 변수 cascade로 자동 적용
- SVG 시각화 블록: 좌표 사전 계산 (svg_calculator.py)
- 이미지 base64 인라인 삽입 (다운로드 HTML에서도 표시)
↓
[5] 디자인 팀장 (Sonnet) — 전체 재검토 (최대 2회 루프)
- HTML 전문 기반 균형 점검
- expand/shrink/rewrite 조정 (AI가 target_ratio 결정)
- 조정 후 재렌더링 → 재검토
↓
미리보기 + HTML 다운로드
블록 라이브러리 (46개 + _legacy 13개)
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/ (10개) 카드 계열
│ ├── card-text-grid.html 텍스트 카드 2~4열
│ ├── 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/ (10개) 다이어그램, 관계도 (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 키워드 원형 나열
│ ├── layer-diagram.html 레이어 다이어그램
│ ├── timeline-vertical.html 세로 타임라인
│ ├── timeline-horizontal.html 가로 타임라인
│ └── pyramid-hierarchy.html 피라미드 계층
├── emphasis/ (13개) 강조, 인용, 결론
│ ├── quote-left-border.html 좌측 라인 인용
│ ├── quote-big-mark.html 큰 따옴표 인용
│ ├── quote-question.html 질문형 강조
│ ├── conclusion-accent-bar.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 텍스트 구분선
│ └── details-block.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 비교
└── _legacy/ (13개) 이전 버전 (fallback)
FAISS 블록 검색
46개 블록 전체를 프롬프트에 넣는 대신, FAISS로 꼭지별 관련 블록만 검색하여 전달합니다.
꼭지 "A vs B 비교" → FAISS 검색 → comparison-2col, compare-pill-pair, compare-2col-split
꼭지 "연도별 로드맵" → FAISS 검색 → timeline-vertical, timeline-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 (46개 블록 인덱스) |
| 템플릿 | Jinja2 (카테고리별 블록 조합) |
| 렌더링 | CSS Grid + 디자인 토큰 (16:9, 1280×720) |
| SVG 시각화 | svg_calculator.py (cos/sin 좌표 계산, N개 동적) |
| 이미지 처리 | Pillow (크기 측정) + base64 인라인 |
| 폰트 | Pretendard Variable (한국어) |
설치 및 실행
설치
cd design_agent
python -m venv .venv
.venv/Scripts/activate # Windows
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 백엔드 (Opus 실장 + 편집자 역할)
cd D:\ad-hoc\kei\persona_agent
uvicorn backend.main:app --reload --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
프로젝트 구조
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 인덱스 빌드 스크립트
│
├── templates/
│ ├── slide-base.html 슬라이드 베이스 (다중 페이지 + 인쇄 JS)
│ ├── catalog.yaml 블록 카탈로그 (46개, height_cost 포함)
│ └── blocks/ 블록 라이브러리 (6 카테고리, 46개)
│ ├── INDEX.md 전체 인덱스
│ ├── headers/ (5) 타이틀, 꼭지 헤더
│ ├── cards/ (10) 카드 계열
│ ├── tables/ (3) 비교 테이블
│ ├── visuals/ (10) 다이어그램, 관계도 (SVG)
│ ├── emphasis/ (13) 강조, 인용, 결론, 자세히보기
│ ├── media/ (5) 이미지/미디어
│ └── _legacy/ (13) 이전 버전 (fallback)
│
├── static/
│ ├── index.html 프론트엔드 (이미지 경로 입력 팝업 포함)
│ ├── tokens.css 디자인 토큰
│ └── base.css 기본 슬라이드 스타일
│
├── data/ 로컬 데이터 (gitignored)
│ ├── block_index.faiss FAISS 벡터 인덱스
│ └── block_metadata.json 인덱스 메타데이터
│
├── 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/
핵심 원칙
- 모든 판단은 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)
└ 독립적으로 동작
Design Agent (이 프로젝트) — localhost:8001
├ 슬라이드 생성 전용
├ Kei API로 실장(1단계) + 편집자(3단계) + 블록 추천(2단계 A-2) 호출
├ FAISS 블록 검색 (bge-m3, Kei와 동일 모델)
└ 독립적으로 동작 (Kei 없이도 Sonnet fallback)
두 프로젝트는 완전히 독립. 코드 공유 없음. API 연동만.
Description
Languages
Python
86.6%
HTML
12.7%
CSS
0.7%