Files
_Geulbeot/04. design_agent/PLAN.md
kyeongmin 688ddbbb17 04. design_agent 추가 — 콘텐츠 시각 구조화 슬라이드 생성기
5단계 AI 파이프라인:
1. Kei 실장(Opus via Kei API) — 꼭지 추출 + 정보 구조 파악
2. 디자인 팀장 — FAISS 블록 검색 + Opus 추천 + Sonnet 블록 매핑
3. Kei 편집자(Kei API) — 도메인 전문 텍스트 정리
4. 디자인 실무자(Sonnet + Jinja2) — CSS 변수 조정 + HTML 조립
5. 디자인 팀장(Sonnet) — 균형 재검토 (최대 2회 루프)

블록 라이브러리 46개 (6 카테고리) + _legacy 13개
FAISS 블록 검색 (bge-m3, 1024차원)
SVG N개 동적 배치 (cos/sin 좌표 계산)
Pillow 이미지 크기 측정 + base64 인라인
컨테이너 예산 기반 블록 배치 (zone별 높이 px)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:47:13 +09:00

18 KiB

Design Agent — 실행 계획

Phase 1: 기반 구축

DA-1: 프로젝트 셋업

  • 파일: pyproject.toml, .env, .gitignore
  • 내용: Python 환경, 의존성 정의, 환경 변수
  • 의존성: 없음
  • 완료 기준: pip install -e . 성공

DA-2: FastAPI 서버 기본 구조

  • 파일: src/main.py, src/config.py
  • 내용: FastAPI 앱, CORS, health endpoint, 설정 관리
  • 의존성: DA-1
  • 완료 기준: uvicorn src.main:app --reload 정상 시작, /api/health 200 반환

DA-3: 디자인 토큰 + 기본 CSS

  • 파일: static/tokens.css, static/base.css
  • 내용: CLAUDE.md에 정의된 디자인 토큰을 CSS 변수로 구현, Pretendard 폰트 설정, 16:9 슬라이드 컨테이너
  • 의존성: 없음
  • 완료 기준: 빈 슬라이드가 16:9 비율로 렌더링, Pretendard 폰트 적용 확인

Phase 2: 블록 템플릿 제작

DA-4: 블록 템플릿 — 비교 (comparison)

  • 파일: templates/blocks/comparison.html
  • 내용: 2단 병렬 레이아웃, Jinja2 슬롯 ({{left_title}}, {{left_content}}, {{right_title}}, {{right_content}})
  • 의존성: DA-3
  • 완료 기준: 더미 데이터로 렌더링 시 2단 비교 표시, 디자인 토큰 적용

DA-5: 블록 템플릿 — 카드 그리드 (card-grid)

  • 파일: templates/blocks/card-grid.html
  • 내용: 2~4열 카드 배열, Jinja2 슬롯 ({{cards[n].icon}}, {{cards[n].title}}, {{cards[n].description}})
  • 의존성: DA-3
  • 완료 기준: 3개 카드 렌더링, 카드 수에 따라 자동 배열

DA-6: 블록 템플릿 — 관계도 (relationship)

  • 파일: templates/blocks/relationship.html
  • 내용: 벤 다이어그램 (CSS 원형), Jinja2 슬롯 ({{center}}, {{items[n]}})
  • 의존성: DA-3
  • 완료 기준: 3원 벤 다이어그램 렌더링, 라벨 표시

DA-7: 블록 템플릿 — 프로세스 (process)

  • 파일: templates/blocks/process.html
  • 내용: 가로 단계 흐름, Jinja2 슬롯 ({{steps[n].number}}, {{steps[n].title}}, {{steps[n].description}})
  • 의존성: DA-3
  • 완료 기준: 4단계 프로세스 렌더링, 연결선 표시

DA-8: 블록 템플릿 — 강조 인용 (quote-block)

  • 파일: templates/blocks/quote-block.html
  • 내용: 배경색 + 좌측 라인 + 인용 텍스트, Jinja2 슬롯 ({{quote_text}}, {{source}})
  • 의존성: DA-3
  • 완료 기준: 인용 블록 렌더링, 강조 스타일 적용

DA-9: 블록 템플릿 — 결론 바 (conclusion-bar)

  • 파일: templates/blocks/conclusion-bar.html
  • 내용: 하단 전체 폭 강조 영역, Jinja2 슬롯 ({{conclusion_text}})
  • 의존성: DA-3
  • 완료 기준: 결론 바 렌더링, 강조 색상 적용

DA-10: 블록 템플릿 — 비교 테이블 (comparison-table)

  • 파일: templates/blocks/comparison-table.html
  • 내용: 다항목 비교 테이블, Jinja2 슬롯 ({{headers}}, {{rows}})
  • 의존성: DA-3
  • 완료 기준: 5행 3열 테이블 렌더링

DA-11: 슬라이드 조합 렌더러

  • 파일: src/renderer.py, templates/slide-base.html
  • 내용: Jinja2로 블록 조합 → HTML 생성. grid-template-areas로 블록 배치. 다중 페이지 지원.
  • 다중 페이지: .slide div 여러 개 + page-break-after: always (인쇄 시 페이지 분리)
  • 의존성: DA-4 ~ DA-10
  • 완료 기준: JSON 블록 배치 명세 → 완성 HTML 출력 (1페이지 또는 다중 페이지)

Phase 3: AI 파이프라인 연결

DA-12: 1단계 — Kei 실장 (꼭지 추출 + 분석)

  • 파일: src/kei_client.py
  • 내용: 본문에서 핵심 꼭지 추출 + 다단계 분석
    • 꼭지 추출: 본문에서 2~5개 핵심 파트 식별 (1페이지 적정: 5개)
    • 페이지 분리: 5개 초과 + 레이어 동등 → 2페이지 (의미 기반 분할 2/3, 3/4 등)
    • 5개 + 내용 많음 → 세부 내용은 자세히보기 대상
    • 레이어 수준: 각 꼭지가 도입/핵심/보조/결론 중 어디인지
    • 강조 판단: 어떤 꼭지를 시각적으로 눈에 띄게 할 것인가
    • 배치 방향: 세로로 긴 꼭지, 가로로 나열할 꼭지 판단
    • 이미지 판단: 몇 개, 어떤 꼭지 소속, 핵심/보조, 텍스트 포함 여부
    • 표 판단: 행/열 규모, 전체 표시 가능 여부
    • 상세 콘텐츠 판단: 자세히보기 대상 식별
  • 기술: Anthropic API (Sonnet)
  • 의존성: DA-2
  • 완료 기준: 꼭지 목록 + 레이어 + 강조 + 배치 + 이미지/표/상세 판단 JSON

DA-13a: 2단계 Step A — 레이아웃 프리셋 선택 (규칙 기반)

  • 파일: src/design_director.py (select_preset() 함수)
  • 내용: 실장의 role 분석을 보고 레이아웃 프리셋을 자동 선택
    • reference 꼭지 있음 → sidebar-right (65:35)
    • 모든 flow가 대등 비교 → two-column (50:50)
    • 고강조 1개 + 나머지 보조 → hero-detail
    • 나머지 → single-column
  • 기술: Python 규칙 코드 (LLM 불필요)
  • 의존성: DA-12
  • 완료 기준: 프리셋 이름 + CSS grid 반환

DA-13b: 2단계 Step B — 프리셋 내 블록 매핑 (Sonnet)

  • 파일: src/design_director.py
  • 내용: 선택된 프리셋의 zone에 꼭지를 배정 + 블록 타입 선택 + 글자 수 가이드
    • 프롬프트에 선택된 프리셋의 CSS grid가 포함됨
    • flow 꼭지 → body/main zone
    • reference 꼭지 → sidebar zone
    • detail_target → popup 연결
    • catalog에서 블록 타입 선택
    • 이미지/표 배치 판단
  • 기술: Anthropic API (Sonnet) + Pillow
  • 의존성: DA-13a
  • 완료 기준: zone별 블록 배정 + 글자 수 가이드 JSON

DA-13c: 3단계 — Kei 텍스트 편집자 (텍스트 정리)

  • 파일: src/content_editor.py
  • 내용: 팀장의 글자 수 가이드 참고하되 내용 의미 우선
    • 전체 컨텍스트와 핵심 용어 유지
    • 세련된 표현으로 편집 (의미 > 글자 수)
    • 출처 보존, 개조식, 날조 금지
    • 표 내용 편집 (핵심 행/열 선택, 요약)
    • 자세히보기 대상: 요약 버전 + 상세 버전 둘 다 작성
  • 기술: Anthropic API (Sonnet)
  • 의존성: DA-13b
  • 완료 기준: 슬롯별 텍스트 JSON. 핵심 용어 보존. 자세히보기 포함.

DA-14: 4단계 — 디자인 실무자 (디자인 조정 + HTML 조립) + 5단계 재검토

  • 파일: src/pipeline.py, src/renderer.py
  • 내용:
    • 4단계 (AI + 코드): 편집자 텍스트에 맞게 디자인 조정 (폰트/여백/박스 — 텍스트 자르지 않음)
      • Jinja2 HTML 조립, 이미지 object-fit:contain, 표 container query, <details> 접기, 인쇄 펼침 JS
    • 5단계 (AI): 팀장이 전체 균형 재검토 → 채움 비율, 블록 균형, 이미지/표 크기 점검 → 2차 조정
  • 기술: Anthropic API Sonnet (디자인 조정 + 재검토) + Jinja2/CSS (조립)
  • 의존성: DA-11, DA-12, DA-13a, DA-13b, DA-13c
  • 완료 기준: 텍스트 입력 → 균형 잡힌 슬라이드 HTML (이미지/표/자세히보기 포함, 재검토 완료)

Phase 4: UI + 출력

DA-15: 프론트엔드 — 콘텐츠 입력 + 미리보기

  • 파일: static/index.html (별도 HTML 파일), main.py (FileResponse로 서빙)
  • 내용: 텍스트 입력 영역 + iframe 미리보기 + HTML 다운로드 버튼
  • 기술: FileResponse (FastAPI 내장), fetch API + 수동 SSE 파싱
  • 의존성: DA-14
  • 완료 기준: 텍스트 붙여넣기 → 슬라이드 미리보기 표시 + HTML 다운로드
  • 주의: HTML/JS를 Python 문자열에 넣지 않는다 (이스케이프 충돌 방지)

버그 수정

BF-2: 블록 내용 비어있음 (렌더러 Jinja2 include 문제)

  • 파일: src/renderer.py, templates/slide-base.html
  • 내용: include → 블록별 개별 render() 후 HTML 삽입
  • 기술: Jinja2 get_template().render() (내장)
  • 의존성: 없음 (기존 코드 수정만)
  • 완료 기준: 콘텐츠 입력 → 슬라이드에 텍스트가 표시됨

BF-3: 한글 깨짐 (다운로드 파일)

  • 파일: static/index.html
  • 내용: download() Blob에 UTF-8 BOM 추가
  • 기술: JavaScript '\uFEFF' 1줄
  • 의존성: 없음
  • 완료 기준: 다운로드한 HTML 파일에서 한글 정상 표시

BF-4: body 블록 겹침 (같은 area에 여러 div)

  • 파일: src/renderer.py
  • 내용: 같은 area의 블록을 하나의 div로 그룹핑 (OrderedDict + flex-column)
  • 기술: Python OrderedDict (내장)
  • 의존성: BF-5 (area명 통일 선행)
  • 완료 기준: body에 4개 블록이 세로로 쌓여서 보임 (겹침 없음)
  • 상태: 코드 수정 완료, md 반영 중

BF-5: 제목 안 보임 (area명 불일치)

  • 파일: src/design_director.py (LAYOUT_PRESETS)
  • 내용: 프리셋 4개의 area명 titleheader로 통일 (slide-base.html이 header 사용)
  • 기술: 문자열 교체
  • 의존성: 없음
  • 완료 기준: 슬라이드에 제목이 표시됨
  • 상태: sidebar-right 프리셋 수정 완료, 나머지 3개 확인 필요

BF-6: sidebar 카드 3열 찢어짐

  • 파일: src/design_director.py (STEP_B_PROMPT)
  • 내용: 팀장 Step B 프롬프트에 "sidebar zone은 전체 너비의 35%로 좁다. card-grid 배치 시 카드 열 수를 공간에 맞게 판단하라" 추가
  • 기술: 프롬프트 엔지니어링
  • 의존성: 없음
  • 완료 기준: sidebar의 카드가 1열 세로로 배치됨
  • 상태: 미수정

BF-7: body 블록 텍스트 비어있음 (content_editor 매칭 오류)

  • 파일: src/content_editor.py
  • 내용: 같은 area에 여러 블록이 있을 때 첫 번째만 매칭되는 문제. break 제거 + area+topic_id로 정확 매칭. 편집자 프롬프트 출력 형식에 topic_id 추가.
  • 기술: Python 조건문 수정
  • 의존성: 없음
  • 완료 기준: body의 모든 블록에 텍스트가 채워짐
  • 상태: 미수정

BF-8: 컨테이너 예산 기반 블록 배치 (프레임 넘침 방지)

  • 파일: src/design_director.py, templates/catalog.yaml, static/base.css, templates/blocks/visuals/*.html
  • 내용:
    • 근본 원인: 팀장이 콘텐츠 중심으로 블록 선택 → 높이 고려 없이 body에 4개 블록 쌓음 → 720px 초과 → overflow:hidden으로 잘림
    • 해결: 컨테이너(zone) 크기 예산을 먼저 확인하고, 예산 안에서 블록을 선택하는 사고 순서로 전환
    • LAYOUT_PRESETS: zone별 budget_px + width_pct 추가
    • STEP_B_PROMPT: 4단계 사고 순서 (컨테이너 확인→배정→블록 선택+높이 계산→검증)
    • catalog.yaml: 블록별 height_cost 추가 (compact ~70px / medium ~150px / large ~200px / xlarge ~400px)
    • base.css: area 컨테이너에 overflow:hidden + min-height:0 안전망
    • 시각화 블록 CSS: flex-shrink + responsive SVG
  • 기술: 프롬프트 엔지니어링 + CSS 안전망
  • 의존성: 없음
  • 완료 기준: body 영역 블록이 720px 프레임 안에 모두 보임. 넘침 없음.
  • 한계: 프롬프트만으로는 Sonnet이 grid를 무시하는 문제를 방지할 수 없음 → BF-9 필요

BF-9: grid와 Sonnet의 역할 분리 (설계 수정)

  • 파일: src/design_director.py
  • 내용:
    • 설계 오류: Step B에서 Sonnet에게 grid 값을 출력하라고 요구 → Sonnet이 자기만의 grid를 생성해버림
    • 근본 원인: grid는 코드(Step A)가 결정하는 것인데 Sonnet에게 에코를 요구한 것 자체가 잘못
    • 해결:
      • Step B 프롬프트에서 grid 출력 요구 제거. Sonnet은 blocks 배열만 출력
      • create_layout_concept()에서 grid 값은 프리셋에서 직접 가져옴 (Sonnet 출력 무시)
      • Sonnet이 출력한 area명이 프리셋 zone에 없으면 가장 가까운 zone으로 코드 매핑
    • 원칙: 코드가 결정한 것은 코드가 유지한다. Sonnet은 콘텐츠 판단만.
  • 기술: Python 코드 수정 + 프롬프트 출력 형식 변경
  • 의존성: BF-8 (프리셋에 zone 정보 필요)
  • 완료 기준: Sonnet 출력에 grid 값이 없음. 최종 HTML의 grid는 항상 프리셋과 100% 일치.

BF-10: _CATALOG_MAP 캐시 갱신 문제

  • 파일: src/renderer.py
  • 내용:
    • 현상: catalog.yaml을 수정해도 서버 재시작 전까지 구 매핑 유지 → _legacy 블록 로드
    • 근본 원인: _CATALOG_MAP이 모듈 레벨 global로 한 번만 로드됨
    • 해결: reload 옵션 (--reload 시 자동 갱신) 또는 매 요청마다 파일 mtime 확인 후 reload
  • 기술: Python pathlib stat() (내장)
  • 의존성: 없음
  • 완료 기준: catalog.yaml 수정 → 서버 재시작 없이 새 매핑 적용

DA-16: 통합 테스트

  • 파일: tests/test_pipeline.py, tests/test_renderer.py
  • 내용: 전체 파이프라인 테스트 + 블록 렌더링 테스트
  • 의존성: BF-2, BF-3, BF-4, BF-5, BF-6, BF-7
  • 완료 기준: 테스트 전체 통과


Phase 5: 블록 라이브러리 확장

DA-17: Figma 에셋 추출 + 블록 템플릿 제작

  • 상태: done
  • 산출물:
    • Figma 스크린샷 16장 (docs/figma-screenshots/)
    • Figma 에셋 15개+ (docs/figma-assets/)
    • 신규 블록 템플릿 6종 (templates/blocks/ 카테고리별)
    • 디자인 분석 보고서 (docs/figma-analysis/DESIGN-ANALYSIS.md)
    • 블록 인덱스 (templates/blocks/INDEX.md)
  • 완료 기준: 신규 블록 6개 독립 렌더링 테스트 통과, Figma 톤 반영

DA-18: 블록 라이브러리 카테고리 재편

  • 상태: done
  • 내용: 플랫 구조 → 6개 카테고리 폴더 (headers, cards, tables, visuals, emphasis, media)
  • 완료 기준: INDEX.md에 전체 구조 정리, 기존 루트 파일 정리

DA-19: 변형 확장

  • 상태: done (46개 달성)
  • 산출물: 46개 블록 (6 카테고리), catalog.yaml 46개, BLOCK_SLOTS 46개 동기화
  • 완료 기준: 달성

Phase 2: 파이프라인 고도화 (상세: docs/PHASE2-PLAN.md, PHASE2-PROCESS.md)

P2-A: FAISS 블록 검색 인덱스

  • 파일: src/block_search.py (신규), scripts/build_block_index.py (신규), src/design_director.py (수정)
  • 내용: catalog 전문 → FAISS 검색으로 관련 블록 5~8개만 프롬프트에 전달
  • 기술: BAAI/bge-m3 + faiss-cpu (Kei persona와 동일 패턴)
  • 의존성: 없음
  • 완료 기준: 꼭지별 검색 → 카테고리별 최소 1개 보장 → 프롬프트 토큰 절약

P2-B: SVG N개 자동 배치

  • 파일: src/svg_calculator.py (신규), src/renderer.py (수정), templates/blocks/visuals/venn-diagram.html (수정)
  • 내용: venn-diagram 3개 고정 → N개(2~7) cos/sin 자동 계산
  • 기술: Python math (내장, 추가 의존성 없음)
  • 의존성: 없음 (2-A와 병렬 가능)
  • 완료 기준: N=2~7 각각 렌더링 테스트 통과. Phase 1 fallback 유지.

P2-C: Step A Opus+FAISS 고도화

  • 파일: src/design_director.py (수정)
  • 내용: 규칙 4줄 → Opus(Kei API)가 FAISS 후보에서 블록 선정 + 배치 결정
  • 기술: Kei API 호출 (Opus). anthropic 직접 호출 절대 금지.
  • 의존성: P2-A 완료 필수
  • 완료 기준: Opus가 콘텐츠 보고 블록 후보 선정. Kei API 실패 시 기존 방식 fallback.

P2-D: 5단계 재검토 강화

  • 파일: src/pipeline.py (수정)
  • 내용: shrink/rewrite action 구현 + 재조정 루프 (MAX=2)
  • 기술: Anthropic API (Sonnet) — 기존 패턴
  • 의존성: 없음 (병렬 가능)
  • 완료 기준: expand/shrink/rewrite 3개 action 모두 동작. 루프 2회 제한.

P2-E: 누락 기능

  • E-1 Pillow: src/design_director.py — 이미지 크기 읽기 → 블록 선택 참고
  • E-2 details-block: src/design_director.py + src/pipeline.py — detail_target → details-block 배치
  • 의존성: 없음 (병렬 가능)
  • 완료 기준: 이미지 크기 정보가 팀장에게 전달. details 접기/펼치기 동작.

의존 관계

P2-A (FAISS) ──────────────────────┐
                                    ├→ P2-C (Opus+FAISS)
P2-B (SVG N개) ── 병렬              │
P2-D (재검토) ─── 병렬              │
P2-E (누락기능) ── 병렬             │

DA-21: renderer 카테고리 경로 지원

  • 상태: todo
  • 내용: renderer.py의 블록 로드 경로를 blocks/{type}.htmlblocks/{category}/{name}.html로 변경
  • 주의: 기존 파이프라인과 호환성 유지
  • 의존성: DA-18
  • 완료 기준: 카테고리 경로로 블록 렌더링 정상 동작

DA-22: catalog.yaml 경로 업데이트

  • 상태: todo
  • 내용: template 경로를 새 폴더 구조로 반영
  • 의존성: DA-21
  • 완료 기준: catalog.yaml의 모든 template 경로가 실제 파일과 일치

의존 관계

Phase 1~2 (기반+블록):
  DA-1 → DA-2, DA-3 → DA-4~DA-10 → DA-11(렌더러)

Phase 3 (AI 파이프라인):
  DA-12(실장) → DA-13(팀장) → DA-13b(편집자) → DA-14(조립+재검토)

Phase 4 (UI):
  DA-14 → DA-15(프론트엔드) → DA-16(테스트)

Phase 5 (블록 라이브러리):
  DA-17(Figma추출) → DA-18(카테고리재편) → DA-19(변형확장) → DA-20(FAISS)
  DA-18 → DA-21(renderer경로) → DA-22(catalog경로)
  • Phase 1~2, 5: AI 없이 진행 가능
  • Phase 3: Anthropic API 필요 (5단계 파이프라인)
  • Phase 5는 Phase 3와 병렬 진행 가능

기술 스택

역할 도구 비고
서버 FastAPI + uvicorn 포트 8001
템플릿 엔진 Jinja2 카테고리별 블록 조합
렌더링 CSS Grid + 디자인 토큰 슬라이드 + 웹
한국어 폰트 Pretendard Variable word-break: keep-all
AI (5단계) Anthropic API (Sonnet) 실장→팀장→편집자→실무자→재검토
이미지 생성 Gemini API 복합 시각화 배경 (레이어 방식)
Figma Figma REST API + Framelink MCP 에셋 추출
변형 검색 FAISS (향후) 블록 40개+ 시
테스트 pytest 렌더링 + 파이프라인