"""Phase 21b — ontology 기반 구조축 (tie-breaker 가중치)
공식: 점수 = 0.5 × 키워드 + 0.3 × 내용 + 0.1 × 구조(v3 ontology)
(구조는 tie-breaker 수준)
Phase 21(원본, old structure) vs Phase 21b(new ontology) 비교 의도.
"""
import sys
import json
import math
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from phase_common import (
TARGET_UNITS, load_synonyms, load_32_frames, compute_df_idf_tier,
extract_mdx_keywords, keyword_score, content_scores_batch,
load_target_units, load_frame_index, normalize_with_synonyms,
)
from structure_v3 import load_ontology, detect_mdx_structure_v3, structural_match_v3
ROOT = Path(r"d:\ad-hoc\kei\design_agent")
PREVIEW_DIR = ROOT / "data" / "figma_previews"
def main():
synonyms = load_synonyms()
frames = load_32_frames()
df, idf, tier, N = compute_df_idf_tier(frames)
vocab = set()
for v in frames.values(): vocab.update(v["keywords"])
units_full, units_title = load_target_units()
idx_data, frame_to_short = load_frame_index()
ontology = load_ontology()
W_KW, W_CONTENT, W_STRUCT = 0.5, 0.3, 0.1
reports = []
hits = 0
for uid, display, correct_sid, *_ in TARGET_UNITS:
text_orig = units_full[uid]
text = normalize_with_synonyms(text_orig, synonyms) # synonym 정규화 공통 적용
title = units_title[uid]
mdx_kws = extract_mdx_keywords(text, vocab, synonyms=None) # 이미 정규화됨
mdx_schema = detect_mdx_structure_v3(text, title) # 정규화된 text 사용
c_scores = content_scores_batch(title, frames)
ranked = []
for fid, v in frames.items():
fig_kws = set(v["keywords"])
k_s, inter = keyword_score(mdx_kws, fig_kws, idf, tier)
c_s = c_scores.get(fid, 0.0)
s_s, aligns = structural_match_v3(mdx_schema, ontology.get(fid, {}))
final = W_KW * k_s + W_CONTENT * c_s + W_STRUCT * s_s
ranked.append((fid, final, {
"kw": k_s, "content": c_s, "struct": s_s,
"aligns": aligns, "inter": sorted(inter),
}))
ranked.sort(key=lambda x: -x[1])
top3 = ranked[:3]
top_sid = frame_to_short.get(top3[0][0], "?")
if top_sid == correct_sid:
hits += 1
reports.append({
"uid": uid, "display": display, "correct_sid": correct_sid,
"mdx_kws": mdx_kws, "mdx_title": title, "mdx_schema": mdx_schema,
"top3": top3,
})
print(f"{uid}(정답={correct_sid}): "
f"Top1={top_sid} ({top3[0][1]:.3f}) "
f"[{'✓' if top_sid == correct_sid else '✗'}] "
f"struct={top3[0][2]['struct']:.2f}")
print(f"\n=== Phase 21b 결과: {hits}/4 ===")
# MD 리포트
png_rel = "../../data/figma_previews/"
lines = []
lines.append(f"# Phase 21b — Ontology 기반 구조축 (tie-breaker)")
lines.append("")
lines.append(f"**공식**: `점수 = {W_KW} × 키워드 + {W_CONTENT} × 내용 + {W_STRUCT} × 구조(ontology)`")
lines.append(f"**결과: {hits}/4**")
lines.append("")
lines.append("**Phase 21 원본 대비 차이:**")
lines.append("- 구조 매칭 로직: `structural_match_v2` (layout 문자열) → `structural_match_v3` (속성 교집합)")
lines.append("- 구조 가중치: 0.2 → 0.1 (tie-breaker 수준)")
lines.append("- 구조 ontology: layout 문자열 → `structure_ontology.yaml` 속성 schema")
lines.append("- MDX 구조 감지: ### 서브섹션/표헤더/블릿 기반 schema emit")
lines.append("- confidence 체크: low면 구조 점수 0")
lines.append("")
# 통합 매트릭스
lines.append("## 테스트 결과")
lines.append("")
lines.append("| 콘텐츠 | 1순위 | 2순위 | 3순위 |")
lines.append("|--------|-------|-------|-------|")
for r in reports:
row = [f"**{r['display']}**
정답 Frame **{r['correct_sid']}**"]
for rank_idx in range(3):
fid, score, bd = r["top3"][rank_idx]
sid = frame_to_short.get(fid, "?")
info = idx_data.get(sid, {})
png = info.get("png", "")
title = info.get("title_text", "").strip().replace("\n", " ")[:15] + "…"
inner = f"
**{sid}** ({score:.3f})
{title}"
if sid == r["correct_sid"]:
cell = f"