diff --git a/IMPROVEMENT-PHASE-H.md b/IMPROVEMENT-PHASE-H.md new file mode 100644 index 0000000..4bc64ae --- /dev/null +++ b/IMPROVEMENT-PHASE-H.md @@ -0,0 +1,230 @@ +# Phase H: 스토리라인 설계 기반 파이프라인 전환 — 실행 상세 + +> "꼭지 추출 → 블록 매칭"에서 "스토리라인 설계 → 목적 기반 배치 → 원본 보존 편집"으로 전환. +> 코드 구조 변경 없음. 프롬프트 3개만 수정. 하류 호환 유지 (topics 구조 동일). +> 원칙: 하드코딩 금지. 원본 텍스트 최대 보존. 회귀 금지. + +--- + +## 문제 진단 + +### 문제 1: 1단계가 "꼭지 추출"만 함 — 스토리라인 설계 없음 + +현재 KEI_PROMPT: "본문에서 핵심 꼭지 2~5개를 추출해줘" +- 꼭지가 개별 덩어리로 나옴 +- 각 꼭지가 슬라이드 안에서 왜 그 위치에 있어야 하는지 맥락 없음 +- 결과: 제목 중복, 빈 블록, 맥락 없는 배치 + +있어야 하는 것: +``` +핵심 메시지: "BIM은 DX의 일부분이다" +스토리 흐름: + (1) 문제 제기 → (2) 근거/사례 → (3) 핵심 전달 → (4) 용어 정의 → (5) 결론 +``` + +### 문제 2: 3단계 편집자가 텍스트를 과도하게 재작성 + +현재 EDITOR_PROMPT: "세련된 표현으로 편집한다" +- 원본이 이미 충분히 정리된 텍스트인데 2줄로 압축 +- description 슬롯을 비워둠 +- 원본 분량 수준에서는 약간의 편집만 필요 + +### 문제 3: 2단계 팀장이 블록의 "목적"을 모름 + +현재: 꼭지 제목만 보고 블록 형태 매칭 +- "용어 혼용 문제" → callout? quote? 무작위 +- 블록의 목적(문제제기? 근거? 정의?)을 모르고 형태만 매칭 + +--- + +## H-1: KEI_PROMPT 재설계 — "꼭지 추출" → "스토리라인 설계" + +### 현재 상태 +``` +"본문에서 핵심 꼭지 2~5개를 추출해줘" +→ topics: [{id, title, summary, layer, role, ...}] +``` + +### 변경 +``` +"이 콘텐츠로 슬라이드 1장을 만든다면 어떤 스토리로 구성할지 설계해줘" +``` + +프롬프트에 추가할 지시: +- **핵심 메시지**를 먼저 파악 (이 슬라이드가 전달해야 할 한 줄) +- **스토리 흐름**을 설계 (문제→근거→핵심→정의→결론) +- 각 위치의 **목적(purpose)** 명시 (문제제기/근거사례/핵심전달/용어정의/결론강조) +- 각 위치에 **원본의 어떤 부분**이 가는지 명시 +- 원본 텍스트는 최대한 보존. 슬라이드에 맞게 약간만 편집. + +출력 형식 (기존 topics 구조 유지 + 필드 추가): +```json +{ + "title": "건설산업 DX의 올바른 이해", + "core_message": "BIM은 건설산업 DX의 기초 일부분이지 전체가 아니다", + "total_pages": 1, + "info_structure": "...", + "topics": [ + { + "id": 1, + "title": "용어 혼용 문제", + "summary": "DX와 BIM이 혼용되어 사용", + "purpose": "문제제기", + "source_hint": "용어의 혼용 섹션 전체", + "layer": "intro", + "role": "flow", + ... + } + ] +} +``` + +### 하류 호환 +- topics[] 배열 구조 동일 → 2단계/3단계 파싱 깨지지 않음 +- purpose, core_message, source_hint는 새 필드 → `.get()`으로 접근하면 없어도 안전 +- 기존 필드(layer, role, emphasis 등) 유지 + +### 수정 파일 +- `src/kei_client.py` — KEI_PROMPT + +--- + +## H-2: EDITOR_PROMPT 수정 — 원본 텍스트 최대 보존 + +### 현재 상태 +``` +"세련된 표현으로 편집한다 (원본 그대로가 아님)" +→ 과도한 요약/재작성. description 비움. +``` + +### 변경 +``` +"원본 텍스트를 최대한 보존한다. +슬라이드 공간에 맞게 약간만 축약한다. +의미를 바꾸거나 완전히 재작성하지 않는다. +각 블록의 purpose를 보고 해당 목적에 맞는 텍스트를 원본에서 가져온다. +모든 슬롯을 빠짐없이 채운다. 빈 슬롯 금지." +``` + +### 핵심 변경 포인트 +- "세련된 표현으로 편집" → "원본 보존, 약간만 축약" +- "빈 슬롯 금지" 명시 +- purpose 필드를 참고하여 "이 위치에 무엇이 들어가야 하는지" 맥락 제공 + +### 수정 파일 +- `src/content_editor.py` — EDITOR_PROMPT + +--- + +## H-3: STEP_B_PROMPT 보강 — purpose 기반 블록 선택 + 출력에 purpose 추가 + +### 현재 상태 +``` +"꼭지에 적합한 블록을 선택해줘" +→ 형태만 보고 매칭. 목적 모름. +→ 출력 JSON에 purpose 필드 없음. +``` + +### 변경 1: purpose 기반 블록 선택 가이드 추가 +STEP_B_PROMPT의 "블록 선택 규칙" 섹션 뒤에 추가: + +``` +## purpose 기반 블록 선택 가이드 (참고, 강제 아님) +- purpose: 문제제기 → callout-warning, quote-big-mark, quote-question 중 선택 +- purpose: 근거사례 → quote-left-border (출처 포함), card-text-grid (항목 나열) +- purpose: 핵심전달 → comparison-2col, compare-pill-pair, compare-2col-split +- purpose: 용어정의 → card-text-grid (정의+출처), card-numbered (순서 있으면) +- purpose: 결론강조 → conclusion-accent-bar (footer), banner-gradient +- purpose: 구조시각화 → venn-diagram, layer-diagram (단독 배치) +``` + +### 변경 2: 출력 JSON에 purpose 필드 추가 +정밀 검토에서 발견: H-4에서 편집자에게 purpose를 전달하려면, Step B 출력에 purpose가 있어야 함. + +```json +{"blocks": [{"area": "...", "type": "...", "topic_id": 1, "purpose": "문제제기", "reason": "...", ...}]} +``` + +### 하드코딩 점검 +- purpose 가이드는 AI 참고용 추천. 강제 아님. Sonnet이 무시해도 기존과 동일 동작. +- purpose 값은 1단계 Kei가 결정한 것을 2단계 Sonnet이 참조. AI 판단 체인. + +### 수정 파일 +- `src/design_director.py` — STEP_B_PROMPT (가이드 추가 + 출력 형식에 purpose 추가) + +--- + +## H-4: 3단계 편집자에게 purpose 전달 + +### 현재 상태 +content_editor.py의 `fill_content()`에서 각 블록의 슬롯 정보는 전달하지만, +**이 블록이 왜 여기 있는지 (purpose)** 는 전달하지 않음. + +### 변경 +블록별 슬롯 요청 생성 시 purpose를 포함: + +```python +req_text = ( + f"블록 {i+1} ({block_type}, 영역: {area}, topic_id: {topic_id}):\n" + f" 목적(purpose): {block.get('purpose', '미지정')}\n" # 추가 + f" 용도: {block.get('reason', '미지정')}\n" + ... +) +``` + +### 수정 파일 +- `src/content_editor.py` — `fill_content()` 내 slot_requirements 생성 부분 + +--- + +## 수정 파일 총괄 + +| 파일 | 항목 | 변경 성격 | +|------|------|----------| +| `src/kei_client.py` | H-1 | KEI_PROMPT 재설계 (스토리라인 설계) | +| `src/content_editor.py` | H-2, H-4 | EDITOR_PROMPT 수정 (원본 보존) + purpose 전달 | +| `src/design_director.py` | H-3 | STEP_B_PROMPT에 purpose 기반 블록 선택 가이드 추가 | + +코드 구조 변경 없음. 프롬프트만 수정. persona_agent 수정 0건. + +--- + +## 검증 체크리스트 + +- [ ] H-1: 1단계 출력에 core_message, purpose, source_hint 포함 +- [ ] H-1: 기존 topics 구조 유지 (하류 깨지지 않음) +- [ ] H-2: 편집자가 원본 텍스트를 과도하게 축약하지 않음 +- [ ] H-2: 모든 슬롯에 텍스트 채워짐 (빈 슬롯 0건) +- [ ] H-3: purpose에 맞는 블록 선택 (문제제기→경고계열, 정의→카드계열) +- [ ] H-4: 편집자가 각 블록의 purpose를 인지하고 적절한 텍스트 배치 +- [ ] 전체: 슬라이드에 스토리 흐름이 있음 (문제→근거→핵심→정의→결론) + +--- + +## 정밀 검토 결과 + +### 발견 사항 + +| # | 발견 | 대응 | +|---|------|------| +| 1 | H-4가 동작하려면 H-3의 JSON 출력에 purpose 필드 필요 | H-3에 출력 형식 수정 포함 (위에 반영) | +| 2 | "약간만 축약" 시 슬라이드 초과 가능 | 기존 안전망 유지 (4단계 CSS 조정 + 5단계 재검토) | +| 3 | core_message 현재 미활용 | Kei의 사고 과정 기록용. 향후 5단계에서 활용 가능 | + +### 충돌/회귀/하드코딩 총괄 + +| 항목 | 충돌 | 회귀 | 하드코딩 | API | persona 수정 | +|------|:----:|:----:|:-------:|:---:|:----------:| +| H-1 | 없음 | 없음 | 없음 (Kei 사고) | Kei API | 없음 | +| H-2 | 없음 | 없음 | 없음 | Kei API | 없음 | +| H-3 | 없음 | 없음 | 가이드일 뿐 강제 아님 | Sonnet | 없음 | +| H-4 | 없음 | 없음 | 없음 | Kei API | 없음 | + +--- + +## 수정 이력 + +| 날짜 | 내용 | +|------|------| +| 2026-03-26 | 초안. 스토리라인 설계 기반 전환. 프롬프트 3개 수정. | +| 2026-03-26 | 정밀 검토. H-3 출력 형식에 purpose 추가 필요 발견. 반영. | diff --git a/IMPROVEMENT.md b/IMPROVEMENT.md index a24a0c7..be5c831 100644 --- a/IMPROVEMENT.md +++ b/IMPROVEMENT.md @@ -301,6 +301,33 @@ CLAUDE.md 요구사항 전수검토 결과 발견된 미구현/부분구현/위 --- +## Phase H: 스토리라인 설계 기반 파이프라인 전환 (4개) + +> **실행 상세:** [IMPROVEMENT-PHASE-H.md](IMPROVEMENT-PHASE-H.md) +> 코드 구조 변경 없음. 프롬프트 3개만 수정. 원본 텍스트 최대 보존. + +### H-1: KEI_PROMPT 재설계 — "꼭지 추출" → "스토리라인 설계" +- **문제:** 꼭지만 추출하고 전체 스토리 흐름 없음 +- **해결:** "이 슬라이드의 스토리를 설계해줘" + core_message + purpose + source_hint +- **파일:** kei_client.py + +### H-2: EDITOR_PROMPT 수정 — 원본 텍스트 최대 보존 +- **문제:** "세련된 편집"으로 과도한 재작성 +- **해결:** "원본 보존, 약간만 축약, 빈 슬롯 금지" +- **파일:** content_editor.py + +### H-3: STEP_B_PROMPT 보강 — purpose 기반 블록 선택 +- **문제:** 형태만 보고 블록 매칭. 목적 모름. +- **해결:** purpose별 블록 선택 가이드 (문제제기→경고, 정의→카드 등) +- **파일:** design_director.py + +### H-4: 3단계 편집자에게 purpose 전달 +- **문제:** 편집자가 "이 블록이 왜 여기 있는지" 모름 +- **해결:** slot_requirements에 purpose 포함 +- **파일:** content_editor.py + +--- + ## Phase별 의존 관계 ``` diff --git a/src/content_editor.py b/src/content_editor.py index 6f517b3..392a1f4 100644 --- a/src/content_editor.py +++ b/src/content_editor.py @@ -26,14 +26,17 @@ EDITOR_PROMPT = """당신은 도메인 전문가이자 콘텐츠 편집자이다 원본 콘텐츠의 핵심 내용을 유지하면서 각 블록의 슬롯에 맞게 텍스트를 정리한다. ## 핵심 원칙 -- **내용의 의미와 정확성이 글자 수보다 우선한다** +- **원본 텍스트를 최대한 보존한다.** 슬라이드 공간에 맞게 약간만 축약한다. +- 의미를 바꾸거나 완전히 재작성하지 않는다. - 팀장이 제시한 글자 수 가이드는 참고. 의미를 살리려면 가이드를 초과해도 된다. - 디자인 실무자가 텍스트에 맞게 디자인을 조정할 것이므로, 텍스트를 억지로 자르지 않는다. +- **모든 슬롯을 빠짐없이 채운다. 빈 슬롯 금지.** ## 편집 규칙 - 전체 컨텍스트와 핵심 용어를 보존한다 -- 세련된 표현으로 편집한다 (원본 그대로가 아님) +- 원본 표현을 살리되, 슬라이드에 맞게 약간만 다듬는다 - 개조식(불릿, 번호)으로 작성한다. 줄글 금지. +- 각 블록의 **목적(purpose)**을 보고 해당 목적에 맞는 텍스트를 원본에서 가져온다 - **불릿 항목은 반드시 각각 별도 줄(\n)로 작성한다.** 한 줄에 여러 항목을 넣지 마라. - 올바른 예: "• 추진과제: 건설산업 디지털화\n• 실행과제: BIM 전면 도입\n• 출처: 국토교통부" - 잘못된 예: "• 추진과제: 건설산업 디지털화 • 실행과제: BIM 전면 도입 • 출처: 국토교통부" @@ -82,6 +85,7 @@ async def fill_content( topic_id = block.get("topic_id", i + 1) req_text = ( f"블록 {i+1} ({block_type}, 영역: {block.get('area', '?')}, topic_id: {topic_id}):\n" + f" 목적(purpose): {block.get('purpose', '미지정')}\n" f" 용도: {block.get('reason', '미지정')}\n" f" 크기: {block.get('size', 'medium')}\n" f" 필수 슬롯: {slots.get('required', [])}\n" diff --git a/src/design_director.py b/src/design_director.py index 5bbf961..905e809 100644 --- a/src/design_director.py +++ b/src/design_director.py @@ -259,6 +259,15 @@ header/footer는 고정이므로 건드리지 않는다. - catalog의 when/not_for와 height_cost를 반드시 읽고 선택 - 같은 블록 타입 반복 금지 — 다양한 블록 활용 +## purpose 기반 블록 선택 가이드 (참고, 강제 아님) +각 꼭지의 purpose에 맞는 블록 계열을 선택하라: +- 문제제기 → callout-warning, quote-big-mark, quote-question +- 근거사례 → quote-left-border (출처 포함), card-text-grid (항목 나열) +- 핵심전달 → comparison-2col, compare-pill-pair, compare-2col-split +- 용어정의 → card-text-grid (정의+출처), card-numbered (순서 있으면) +- 결론강조 → banner-gradient (footer) +- 구조시각화 → venn-diagram, layer-diagram (단독 배치) + ## 허용된 블록 id 목록 (이 목록에 없는 블록은 절대 선택하지 마라) {allowed_ids} @@ -274,6 +283,7 @@ grid는 이미 확정되었으므로 출력하지 마라. blocks 배열만 출 "area": "zone이름", "type": "블록타입", "topic_id": 1, + "purpose": "문제제기|근거사례|핵심전달|용어정의|결론강조|구조시각화", "reason": "이유", "size": "small|medium|large", "char_guide": {{{{"slot": 글자수}}}} diff --git a/src/kei_client.py b/src/kei_client.py index 2dd80dd..c7ebcca 100644 --- a/src/kei_client.py +++ b/src/kei_client.py @@ -18,36 +18,45 @@ from src.config import settings logger = logging.getLogger(__name__) KEI_PROMPT = ( - "다음 콘텐츠를 슬라이드로 정리하려고 해.\n\n" - "## 1단계: 정보 구조 파악 (꼭지 추출 전에 먼저 수행)\n" - "- 이 콘텐츠가 하나의 흐름으로 읽히는가, 아니면 '본문 흐름'과 '참조 정보'로 분리되는 구조인가?\n" - "- 독립적으로 참조되는 정보(용어 정의, 부록, 별도 설명)가 있는가?\n" - "- 상세히 다뤄야 하지만 본문 흐름을 끊는 내용(비교표, 상세 데이터)이 있는가?\n" - "- 정보 구조를 info_structure 필드에 기술해줘.\n\n" - "## 2단계: 꼭지 추출\n" - "- 1단계에서 파악한 정보 구조를 바탕으로 꼭지를 나눠줘\n" + "다음 콘텐츠로 슬라이드 1장을 만들려고 해. 어떤 스토리로 구성할지 설계해줘.\n\n" + "## 1단계: 핵심 메시지 파악\n" + "- 이 콘텐츠가 전달하려는 **핵심 메시지**를 한 줄로 파악해줘.\n" + "- 슬라이드를 본 사람이 기억해야 할 단 하나의 문장.\n" + "- core_message 필드에 기록.\n\n" + "## 2단계: 정보 구조 파악\n" + "- 본문 흐름(flow)과 참조 정보(reference)로 분리되는 구조인가?\n" + "- 독립적으로 참조되는 정보(용어 정의, 부록)가 있는가?\n" + "- info_structure 필드에 기술.\n\n" + "## 3단계: 슬라이드 스토리라인 설계\n" + "핵심 메시지를 전달하기 위한 **흐름**을 설계해줘. 각 위치에 **목적(purpose)**을 부여:\n" + "- 문제제기: 왜 이 주제가 중요한가? 현재 무엇이 잘못되고 있는가?\n" + "- 근거사례: 문제의 근거, 사례, 증거 (출처 포함)\n" + "- 핵심전달: 그래서 사실은 이거다. 핵심 내용 전달.\n" + "- 용어정의: 사용된 용어를 구체적으로 설명 (보조 참조, sidebar 배치)\n" + "- 결론강조: 핵심 메시지 강조. 슬라이드 하단.\n" + "- 구조시각화: 관계도, 프로세스 등 시각화가 필요한 경우\n\n" + "## 원본 텍스트 보존 원칙\n" "- 원본의 논리 흐름과 정보를 빠뜨리지 마라\n" + "- 원본 텍스트는 최대한 보존. 약간의 편집만.\n" "- 원본에 있는 내용을 임의로 제거하거나 다른 의미로 바꾸지 마라\n" - "- 슬라이드에 맞게 정리하되, 원본이 말하려는 흐름은 유지\n" - "- 각 꼭지의 레이어(도입/핵심/보조/결론), 강조 여부, 배치 방향을 판단해줘\n" - "- 참조 정보는 role: 'reference'로, 본문 흐름은 role: 'flow'로 표시\n" - "- reference 꼭지는 페이지 안에 사이드바로 배치한다. popup으로 빼지 않는다.\n" - "- 본문 흐름을 뒷받침하는 근거/사례도 페이지 안에 배치한다. popup으로 빼지 않는다.\n" - "- detail_target: true는 정말로 별도로 봐야 하는 상세 데이터(비교표, 상세 스펙)에만 사용한다.\n" - "- 근거, 사례, 용어 정의는 detail_target이 아니다.\n" - "- 이미지가 있으면: 몇 개인지, 어떤 꼭지 소속인지, 핵심(도표/차트)인지 보조(참고 사진)인지, 텍스트가 포함된 이미지인지 판단해줘\n" - "- 표가 있으면: 행/열 규모, 1페이지에 전체 표시 가능한지 판단해줘\n" - "- 이미지/표 판단 결과를 images[], tables[] 배열에 기록해줘\n" - "- 1페이지 적정 꼭지: 5개. 초과 시 2페이지 분리.\n" - "- 2페이지로 분리하는 기준: 꼭지 수가 아니라 콘텐츠 분량이 진짜 많을 때만.\n" - "- 꼭지가 5~6개라도 각 꼭지의 내용이 적으면 1페이지에 충분히 담을 수 있다.\n" - "- 억지로 2페이지로 나누지 마라. 1페이지로 담을 수 있으면 1페이지로.\n\n" + "- 각 꼭지의 source_hint에 원본의 어떤 부분이 가는지 명시\n\n" + "## 배치 규칙\n" + "- 참조 정보(용어 정의 등)는 role: 'reference'로 표시 → 사이드바 배치\n" + "- 본문 흐름은 role: 'flow' → 메인 영역 배치\n" + "- 결론은 layer: 'conclusion' → 하단 배치\n" + "- detail_target: true는 정말로 별도로 봐야 하는 상세 데이터에만 사용\n" + "- 이미지/표가 있으면 images[], tables[]에 기록\n" + "- 1페이지 적정 꼭지: 5개. 분량 적으면 1페이지로.\n\n" "## 출력 형식 (JSON만)\n" "```json\n" - '{"title": "제목", "total_pages": 1, ' - '"info_structure": "이 콘텐츠의 정보 구조 설명 (본문 흐름 vs 참조 분리 등)", ' + '{"title": "제목", ' + '"core_message": "이 슬라이드의 핵심 메시지 한 줄", ' + '"total_pages": 1, ' + '"info_structure": "정보 구조 설명", ' '"topics": [' '{"id": 1, "title": "꼭지 제목", "summary": "요약", ' + '"purpose": "문제제기|근거사례|핵심전달|용어정의|결론강조|구조시각화", ' + '"source_hint": "원본에서 이 위치에 가져올 텍스트 범위 설명", ' '"layer": "intro|core|supporting|conclusion", ' '"role": "flow|reference", ' '"emphasis": true, "direction": "vertical|horizontal|flexible", '