- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규 - Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가 - templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신 - tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분) - tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가 - docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서 - scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸 - .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외 미완성 작업의 보존용 스냅샷 커밋 (2026-07-02) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
368 lines
14 KiB
Markdown
368 lines
14 KiB
Markdown
# Template-Fit v1 — MDX ↔ Figma 매칭 스펙
|
|
|
|
**목적**: MDX 꼭지를 Figma 디자인 32개 중 가장 적합한 1개에 매칭하고, 신뢰도에 따라 `use_as_is / light_edit / restructure / reject` 로 라우팅한다.
|
|
|
|
**설계 원칙**:
|
|
- **매칭 단계에는 LLM 없음**. 코드로만. LLM은 restructure 단계에서만 호출.
|
|
- "구조 DB"가 아니라 "템플릿 적합성 DB". anchor / slot / affordance / adaptation_cost.
|
|
- 계산 먼저, AI 판단은 재구성 단계에서만.
|
|
|
|
---
|
|
|
|
## 1. 파일 / 스키마 버전 관리
|
|
|
|
- 파일: `tests/matching/structure_ontology.yaml` (기존 파일 유지)
|
|
- `meta.schema_version: template-fit-v1` 선언
|
|
- 기존 `frames:` 블록 → **legacy 보존** (Phase 21b 회귀 비교용)
|
|
- 신규 `templates_v1:` 블록 → template-fit-v1 스키마
|
|
- 전환 스크립트: `tests/matching/template_fit.py` (phase 넘버 쓰지 않음)
|
|
|
|
---
|
|
|
|
## 2. Frame 스키마 (templates_v1)
|
|
|
|
```yaml
|
|
templates_v1:
|
|
<frame_id>:
|
|
short_id: "13"
|
|
template_id: three_parallel_requirements
|
|
schema_version: template-fit-v1
|
|
|
|
source:
|
|
title: 필수조건
|
|
original_layout: 3col-parallel
|
|
|
|
# Stage 1 벡터 검색용 자연어 설명. suits 맥락도 이 안에 녹여 씀.
|
|
description: |
|
|
DX 시행을 위한 3대 필수조건을 3열 병렬 구조로 설명하는 디자인.
|
|
기술, 사람, 자연이라는 3개 동등 요소를 각 카드로 나누어 설명한다.
|
|
3개 병렬 조건·3대 요소·3개 축의 균형 설명에 적합.
|
|
|
|
visual_pattern:
|
|
family: list # list / cards / table / compare / diagram 등
|
|
layout: 3-column # 표시용. scoring 미사용
|
|
axis: horizontal # horizontal / vertical
|
|
relation_type: parallel # parallel / sequence / compare / hierarchy / definition / emphasis
|
|
cardinality:
|
|
ideal: 3
|
|
min: 3
|
|
max: 3
|
|
|
|
slots:
|
|
- {id: title, type: heading, required: true, max_chars: 30}
|
|
- {id: pillar_1_label, type: label, required: true, max_chars: 10}
|
|
- {id: pillar_1_body, type: body, required: true, max_chars: 100}
|
|
# ... 반복
|
|
|
|
# OR 관계: 여러 세트 중 최고 매칭률 하나만 채택 (max)
|
|
anchor_sets:
|
|
- id: three_pillars
|
|
terms: [기술, 사람, 자연]
|
|
- id: prerequisites
|
|
terms: [필수조건, 필수요건, 요건, 조건, 3요소]
|
|
|
|
# 검수용 + 점수용 분리
|
|
fit_notes:
|
|
suits: # 검수용 bullet (description과 중복 허용). 점수 미사용
|
|
- 3개 병렬 조건
|
|
- 3대 요소
|
|
not_suits: # 점수용. hit 당 -0.20 감점, 상한 0.30
|
|
- 시간 순서
|
|
- 원인-결과 흐름
|
|
- 2개 비교
|
|
- 4개 이상 항목
|
|
|
|
adaptation_allowed:
|
|
split: true # MDX 한 꼭지를 슬롯 여러 개로 분할 허용?
|
|
merge: false # 여러 꼭지를 슬롯 하나로 합침 허용?
|
|
infer_missing_slot: false # 빠진 슬롯을 날조? 기본 false
|
|
rewrite_label: true # 라벨 재작성?
|
|
rewrite_body: true # 본문 재작성?
|
|
|
|
# Phase 21b 회귀 비교용
|
|
legacy:
|
|
family: list
|
|
surface: bullets-3
|
|
semantic_role: prerequisites
|
|
layout_original: 3col-parallel
|
|
```
|
|
|
|
---
|
|
|
|
## 3. MDX Analysis 스키마 (코드로 생성)
|
|
|
|
```yaml
|
|
mdx_analysis:
|
|
title: "DX 시행을 위한 필수요건"
|
|
summary: "제목 + 첫 문단 + 상위 bullet 결합 (코드)"
|
|
|
|
detected_terms: [DX, 기술, 사람, 자연, 필수요건, ...] # Kiwi + synonyms
|
|
|
|
item_count:
|
|
detected: 3 # ###, 볼드 블릿, 표 행 수 중 최대
|
|
source: bullets # subsections / table_rows / bullets
|
|
|
|
relation_type:
|
|
value: parallel # 규칙: 볼드 블릿 N개 → parallel, ### X.Y 순서 → sequence 등
|
|
confidence: high
|
|
|
|
content_shape:
|
|
has_table: false
|
|
has_subsections: false
|
|
has_bullets: true
|
|
bullet_count: 3
|
|
|
|
slot_candidates:
|
|
- {label: "기술", body: "디지털기술·기반지식"}
|
|
- {label: "사람", body: "역량·창의성"}
|
|
- {label: "자연", body: "여건·투자 기반"}
|
|
```
|
|
|
|
**생성 규칙 (모두 코드)**:
|
|
- summary: `title + 첫 문단 + top-3 bullet` 결합
|
|
- relation_type:
|
|
- `###` 서브섹션 2+ 있고 "과정/결과" 키워드 → `compare`
|
|
- `###` 서브섹션 3+ 시간 순서 표시 → `sequence`
|
|
- 볼드 블릿 N개, 시간어 없음 → `parallel`
|
|
- 표 헤더 에 2개 주체 + 다수 행 → `compare`
|
|
- slot_candidates: heading, table row, bold bullet, numbered list에서 추출
|
|
|
|
LLM 호출은 이 단계에 **없다**. 정확도 부족 시 별도 논의.
|
|
|
|
---
|
|
|
|
## 4. 점수 공식 (0 ~ 1 범위)
|
|
|
|
```python
|
|
base_score = (
|
|
0.25 * anchor_match + # 0~1
|
|
0.20 * cardinality_match + # 0~1
|
|
0.20 * relation_match + # 0~1
|
|
0.15 * slot_coverage + # 0~1
|
|
0.20 * content_embedding # 0~1 (ko-sroberta cosine)
|
|
)
|
|
|
|
# 개별 캡
|
|
adaptation_penalty = min(0.30, sum(adaptation_costs))
|
|
not_suits_penalty = min(0.30, not_suits_hits * 0.20)
|
|
|
|
# 전체 캡
|
|
total_penalty = min(0.50, adaptation_penalty + not_suits_penalty)
|
|
|
|
confidence = base_score - total_penalty # 0~1
|
|
```
|
|
|
|
### 4.1 anchor_match
|
|
|
|
**기본 계산**:
|
|
```python
|
|
ratio[set] = (mdx.detected_terms ∩ set.terms) / len(set.terms)
|
|
anchor_match = max over sets of ratio
|
|
```
|
|
|
|
**Per-set 옵션 (anchor_sets[i] 에 선언)**:
|
|
| 필드 | 기본 | 용도 |
|
|
|------|------|------|
|
|
| `min_hits` | 1 | 이 수치 미만 매치면 해당 set 무시 |
|
|
| `confidence_cap` | 1.0 | raw_ratio 상한 (1.0 미만이면 cap) |
|
|
| `cap_exempt_if_corroborated_by` | null | 같은 템플릿의 다른 set 최고 ratio가 이 값 이상이면 cap 면제 |
|
|
|
|
**조건부 cap 규칙 (Short Generic Anchor 보호)**:
|
|
|
|
짧고 일반적인 anchor set (예: `[BIM, DX]`, 2 term) 은 단독 증거로는 약하다 (우연 언급 가능). 하지만 같은 템플릿의 다른 anchor set 도 같이 매칭되면 진짜 주제로 인정.
|
|
|
|
```python
|
|
# set 별 effective_ratio 계산
|
|
if set.cap < 1.0 and raw_ratio > set.cap:
|
|
others_max = max(raw_ratio[other] for other in same template's anchor_sets)
|
|
if set.exempt is not None and others_max >= set.exempt:
|
|
effective = raw_ratio # cap 면제
|
|
else:
|
|
effective = set.cap # cap 적용
|
|
else:
|
|
effective = raw_ratio
|
|
|
|
anchor_match = max(effective) across sets
|
|
```
|
|
|
|
**현재 cap 적용 대상 (v1)**:
|
|
- `bim_dx [BIM, DX]`: cap 0.70, 방증 임계 0.50
|
|
- 이유: 2-term generic pair. 둘 다 등장해도 비교 주제라고 단정 불가.
|
|
- 검증: MDX01-2 (진짜 BIM/DX 비교) 는 `comparison_dimensions` 0.71 방증으로 **면제** → 정답 0.922 유지. MDX03-2 (BIM/DX 배경 언급) 는 `comparison_dimensions` 0.29 < 0.50 → **cap 적용** → 0.896 → 0.821 (light_edit, 정답 process_product 0.919와 격차 0.098).
|
|
|
|
**토큰 매칭 규칙**:
|
|
- synonyms.yaml 정규화 → HTML/JSX 제거 → Kiwi 명사 추출 → anchor 매칭
|
|
- 토큰 exact match 우선
|
|
- anchor term 길이 ≥ 2자: anchor_vocab substring 으로 보강 (Kiwi 가 복합어를 분해하는 경우 커버)
|
|
|
|
### 4.2 cardinality_match
|
|
```python
|
|
if n == ideal: 1.0
|
|
elif min <= n <= max: 0.8
|
|
elif outside AND (split or merge) allowed: 0.5
|
|
else: 0.0
|
|
```
|
|
- infer_missing_slot=false면 없는 슬롯 날조 불가 → split만으로 가능한 케이스에 한함
|
|
|
|
### 4.3 relation_match
|
|
| MDX \ Frame | parallel | sequence | compare | hierarchy |
|
|
|-------------|----------|----------|---------|-----------|
|
|
| parallel | 1.0 | 0.2 | 0.4 | 0.3 |
|
|
| sequence | 0.2 | 1.0 | 0.3 | 0.4 |
|
|
| compare | 0.4 | 0.3 | 1.0 | 0.3 |
|
|
| hierarchy | 0.3 | 0.4 | 0.3 | 1.0 |
|
|
|
|
### 4.4 slot_coverage (rough v1)
|
|
```python
|
|
slot_coverage =
|
|
0.5 if mdx.item_count ∈ [min, max] # 항목 수 적합
|
|
+ 0.3 if every item has label-like anchor # 라벨 후보 있음
|
|
+ 0.2 if every item has body-like content # 본문 후보 있음
|
|
```
|
|
- 세부 max_chars · 폰트 적합은 v1 에서 측정 안 함
|
|
|
|
### 4.5 content_embedding
|
|
```python
|
|
content_embedding = cosine(embed(mdx.summary), embed(frame.description))
|
|
```
|
|
- 모델: `jhgan/ko-sroberta-multitask` (huggingface)
|
|
- 32 frame description 임베딩은 **캐시** (변경 시 재계산)
|
|
- Stage 1 후보 필터에서 이미 계산한 값 재사용
|
|
|
|
### 4.6 adaptation_cost
|
|
| 조작 | 감점 |
|
|
|-----|-----|
|
|
| rewrite_label 1회 | 0.05 |
|
|
| rewrite_body 1회 | 0.05 |
|
|
| split (1→N) | 0.10 |
|
|
| merge (N→1) | 0.15 |
|
|
| infer_missing_slot | 0.25 |
|
|
| not_suits hit | 0.20 (별도 not_suits_penalty로 집계) |
|
|
| forbidden op 시도 | reject (즉시 0점) |
|
|
|
|
개별 캡: `min(0.30, 누적)`
|
|
전체 캡: `min(0.50, adaptation + not_suits)`
|
|
|
|
---
|
|
|
|
## 5. 라우팅
|
|
|
|
| confidence | 처리 | 재구성 AI | 설명 |
|
|
|-----------|------|---------|------|
|
|
| ≥ 0.90 | **use_as_is** | 0회 | 코드로 slot fill만. 문장 그대로. |
|
|
| 0.75 ~ 0.90 | **light_edit** | 0~1회 | max_chars 초과 시에만 AI. 구조 변경 없음 |
|
|
| 0.60 ~ 0.75 | **restructure** | 1회 | adaptation_allowed 조작 실행. AI로 슬롯 재채움 |
|
|
| < 0.60 | **reject** | 0회 | 다음 후보. top-5 모두 <0.60 이면 "적합 디자인 없음" |
|
|
|
|
---
|
|
|
|
## 6. 전체 파이프라인
|
|
|
|
```
|
|
[사전 준비 1회]
|
|
Figma 32 frame → templates_v1 스키마 작성
|
|
frame.description 임베딩 캐시 → numpy npy
|
|
|
|
[MDX 입력]
|
|
1. MDX analysis 생성 (코드, LLM 0회)
|
|
→ title, summary, detected_terms, item_count, relation_type,
|
|
content_shape, slot_candidates
|
|
|
|
2. Stage 1 필터 (벡터)
|
|
cosine(mdx.summary, frame.description) top-5 추출
|
|
|
|
3. Stage 2 fit scoring
|
|
각 후보에 대해 base_score - total_penalty 계산
|
|
|
|
4. 라우팅
|
|
최고 confidence 기준으로 use_as_is / light_edit / restructure / reject
|
|
|
|
[결과]
|
|
- use_as_is / light_edit: 해당 frame 확정
|
|
- restructure: frame 확정 + 재구성 AI 1회 호출 (나중 단계)
|
|
- reject: 다음 후보 또는 실패
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 대표 패턴 5개 (샘플 대상)
|
|
|
|
| # | template_id | frame_id | 핵심 |
|
|
|---|-------------|---------|------|
|
|
| 1 | three_parallel_requirements | 1171281190 | 3열 병렬 조건형 (기술·사람·자연) |
|
|
| 2 | three_persona_benefits | 1171281191 | 3주체 카드형 (발주자·시공자·설계자) |
|
|
| 3 | bim_dx_comparison_table | 1171281195 | BIM vs DX 행별 비교표 |
|
|
| 4 | process_product_two_way | 1171281210 | 과정/결과 2분할 (배너 + 2col) |
|
|
| 5 | sw_reality_three_emphasis | 1171281209 | 현실/문제 3강조 (상용 S/W 현실) |
|
|
|
|
이 5개 스키마로 매칭 로직 검증 → 32개 확장.
|
|
|
|
---
|
|
|
|
## 8. 실행 순서
|
|
|
|
1. ✅ template-fit-v1 스펙 확정 (이 문서)
|
|
2. ✅ 대표 패턴 5개 샘플 작성 (`structure_ontology.yaml` → `templates_v1`)
|
|
3. ✅ 사용자 사인오프
|
|
4. ✅ `template_fit.py` 구현 (5개 샘플 대상)
|
|
5. ✅ MDX analysis 코드 구현 (`detect_mdx.py`, LLM 없음)
|
|
6. ✅ ko-sroberta content embedding (`embeddings.py`, numpy cosine)
|
|
7. ✅ 조건부 cap (short generic anchor 보호) — 4/4 정답, 오답 use_as_is 없음, 격차 ≥ 0.05
|
|
8. **32개 확장** ← 다음 단계
|
|
9. Phase 21b 대비 최종 비교
|
|
|
|
---
|
|
|
|
## 9. 운영 규칙 (검수 결과 반영)
|
|
|
|
### 9.1 description ↔ fit_notes.suits 동기화
|
|
- description(산문)과 fit_notes.suits(bullet)는 같은 정보의 두 표현.
|
|
- **수정 시 반드시 함께 갱신**. 한쪽만 고치면 embedding과 검수표가 어긋남.
|
|
- not_suits는 description에 녹이지 않음 — 점수용 별도 필드.
|
|
|
|
### 9.2 relation_type 확장 여지
|
|
- 현재 vocab: `parallel / sequence / compare / hierarchy / definition / emphasis`
|
|
- **Frame 14 persona 케이스는 일반 parallel로 유지**. 나중에 혼동이 커지면 `persona_parallel` 같은 하위 타입을 추가할 수 있으나 v1에서는 보류.
|
|
- 하위 타입 추가 기준: 동일 relation_type 안에서 매칭 충돌이 반복 발생할 때만.
|
|
|
|
### 9.3 slot 추상화 수준
|
|
- Frame 18 (bim_dx_comparison_table)의 `rows: table` 슬롯은 **v1 추상화 수준**.
|
|
- restructure 단계에서 행별 재구성이 필요해지면 `row_slots[]`로 쪼개야 함 (nested slots).
|
|
- v1에서는 rows 전체를 한 덩어리로 취급 — slot_coverage 계산 시 항목수는 MDX 항목수로 체크.
|
|
|
|
### 9.4 merge 비용
|
|
- `merge`는 `split`보다 정보 손실 위험이 큼 → adaptation_cost 설정이 이미 그것을 반영:
|
|
- split: 0.10
|
|
- merge: 0.15
|
|
- Frame 28처럼 `split/merge` 둘 다 true인 프레임도 비용 차이로 자연스럽게 merge를 덜 선호하게 됨.
|
|
- 특정 프레임이 merge에 더 취약하면 `adaptation_cost_overrides` 로 프레임별 상향 가능 (v1에서는 미구현).
|
|
|
|
### 9.5 adaptation_allowed 의미 재확인
|
|
| 조작 | 허용 = true 의 의미 |
|
|
|-----|------------------|
|
|
| split | MDX 한 꼭지를 여러 슬롯으로 쪼개도 의미 손실 없음 |
|
|
| merge | 여러 MDX 꼭지를 한 슬롯으로 합쳐도 의미 손실 적음 |
|
|
| infer_missing_slot | 빠진 슬롯을 AI가 새로 만들어도 날조가 아님 (**기본 false 유지 권장**) |
|
|
| rewrite_label | 라벨 문구 재작성 허용 |
|
|
| rewrite_body | 본문 문구 재작성 허용 (max_chars 준수) |
|
|
|
|
- infer_missing_slot은 프레임 특성상 "빈 슬롯 허용 디자인"에만 true.
|
|
- 5개 샘플 전부 infer_missing_slot: false — 날조 방지.
|
|
|
|
---
|
|
|
|
## 9. Phase 21b 회귀 비교
|
|
|
|
| 구분 | 접근 | 가중치 |
|
|
|------|-----|------|
|
|
| Phase 21b (legacy) | family/semantic_role 구조 매칭 | 0.5/0.3/0.1 |
|
|
| Template-fit (신규) | anchor + slot + cardinality + relation + embedding | 0.25/0.20/0.20/0.15/0.20 (+ penalties) |
|
|
|
|
4 TARGET_UNITS 기준 hit 수 비교 → 개선 확인.
|
|
|
|
4 TARGET:
|
|
- MDX01-2-details → Frame 18 (1171281195)
|
|
- MDX02-2.2-table → Frame 14 (1171281191)
|
|
- MDX03-1 → Frame 13 (1171281190)
|
|
- MDX03-2 → Frame 29 (1171281210)
|