Files
C.E.L_Slide_test2/tests/matching/TEMPLATE_FIT_V1.md
T
KyeongminandClaude Opus 4.8 b836e79ee1 wip: phase_z2 evidence 파이프라인 + matching 실험(phase2~26) + 프론트 trace 패널 진행분 스냅샷
- 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>
2026-07-02 17:03:42 +09:00

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)