"""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}]({png_rel}{png})
**{sid}** ({score:.3f})
{title}" if sid == r["correct_sid"]: cell = f"
🎯 정답
{inner}
" else: cell = inner row.append(cell) lines.append("| " + " | ".join(row) + " |") lines.append("") # 세부 결과 lines.append("## 테스트 결과 세부") lines.append("") for r in reports: lines.append(f"### {r['display']} — 정답 Frame **{r['correct_sid']}**") lines.append("") lines.append(f"**MDX 구조 (v3 emit)**") lines.append("```yaml") for k, vv in r["mdx_schema"].items(): lines.append(f"{k}: {vv}") lines.append("```") lines.append("") lines.append(f"**MDX 키워드 ({len(r['mdx_kws'])}개)**: {', '.join(sorted(r['mdx_kws']))}") lines.append("") for rank, (fid, score, bd) in enumerate(r["top3"], 1): sid = frame_to_short.get(fid, "?") fig_schema = ontology.get(fid, {}) info = idx_data.get(sid, {}) png = info.get("png", "") fig_content = frames[fid].get("content", "") common = ", ".join(bd["inter"]) if bd["inter"] else "(없음)" align_str = ", ".join(bd["aligns"]) if bd["aligns"] else "-" is_correct = sid == r["correct_sid"] if is_correct: lines.append(f"**🎯 {rank}위 Frame {sid} (정답)**") else: lines.append(f"**{rank}위 Frame {sid}**") lines.append("") lines.append(f"![{sid}]({png_rel}{png})") lines.append("") lines.append(f"- **매칭 내용**: {fig_content}") lines.append(f"- **매칭 키워드**: {common}") lines.append(f"- **Figma schema**: family=`{fig_schema.get('family')}`, " f"columns={fig_schema.get('columns')}, " f"role=`{fig_schema.get('semantic_role')}`, " f"conf=`{fig_schema.get('confidence')}`") lines.append(f"- **구조 속성 정렬**: {align_str}") lines.append(f"- **축별 점수**: 키워드 {bd['kw']:.3f} / 내용 {bd['content']:.3f} / 구조 {bd['struct']:.2f}") lines.append(f"- **최종**: **{score:.3f}**") lines.append("") out = Path(__file__).parent / "MATRIX_PHASE21b.md" out.write_text("\n".join(lines), encoding="utf-8") print(f"완료: {out}") # 별도 결과 저장 (phase22용) import pickle serializable = { "phase": "21b", "desc": "synonym + 키워드 + 내용 + 구조(ontology v3)", "weights": (W_KW, W_CONTENT, W_STRUCT), "hits": hits, "reports": [{ "uid": r["uid"], "display": r["display"], "correct_sid": r["correct_sid"], "mdx_kws_count": len(r["mdx_kws"]), "top3": [(fid, score, frame_to_short.get(fid, "?"), bd) for fid, score, bd in r["top3"]], } for r in reports] } with open(Path(__file__).parent / "_phase21b_results.pkl", "wb") as f: pickle.dump(serializable, f) if __name__ == "__main__": main()