Files
C.E.L_Slide_test2/tests/matching/phase22.py
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

175 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Phase 22 — 정제 anchor keywords only (baseline)
공식: 점수 = 1.0 × 키워드 (IDF-weighted Jaccard)
데이터:
- keyword_base.yaml (Milestone 1.6 결과, 268 canonical + variants)
- analysis.md mirror (templates_v1.anchor_sets 기반, Milestone 2 결과)
- MDX 추출: keyword_base variants → canonical 정규화
→ direct canonical hit + Kiwi token hit union
목적: 정제 anchor keyword 만으로 4 TARGET 매칭이 어디까지 되는지 baseline.
"""
import sys
import pickle
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from phase_common import (
TARGET_UNITS, load_keyword_base, load_32_frames, compute_df_idf_tier,
extract_mdx_keywords, keyword_score,
load_target_units, load_frame_index,
)
HERE = Path(__file__).parent
PNG_REL = "../../data/figma_previews/"
def run():
kb = load_keyword_base()
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()
reports = []
hits = 0
for uid, display, correct_sid, *_ in TARGET_UNITS:
text = units_full[uid]
mdx_kws = extract_mdx_keywords(text, vocab, keyword_base=kb)
ranked = []
for fid, v in frames.items():
fig_kws = set(v["keywords"])
k_s, inter = keyword_score(mdx_kws, fig_kws, idf, tier)
ranked.append((fid, k_s, {"kw": k_s, "inter": sorted(inter)}))
ranked.sort(key=lambda x: -x[1])
top_sid = frame_to_short.get(ranked[0][0], "?")
if top_sid == correct_sid:
hits += 1
top1_score = ranked[0][1]
top2_score = ranked[1][1] if len(ranked) > 1 else 0
margin = top1_score - top2_score
reports.append({
"uid": uid, "display": display, "correct_sid": correct_sid,
"mdx_kws": sorted(mdx_kws), "top3": ranked[:3],
"margin": margin,
})
return {
"phase": 22, "desc": "키워드만 (baseline)",
"formula": "1.0 × 키워드",
"hits": hits, "reports": reports,
"frame_to_short": frame_to_short, "idx_data": idx_data, "frames": frames,
}
def write_md(r):
lines = []
lines.append(f"# Phase 22 — 정제 anchor keywords only (baseline)")
lines.append("")
lines.append(f"**공식**: `점수 = 1.0 × 키워드` (IDF-weighted Jaccard)")
lines.append(f"**결과: {r['hits']}/4 정답**")
lines.append("")
lines.append("**데이터 기반**:")
lines.append("- `keyword_base.yaml` (Milestone 1.6 결과, 268 canonical + variants)")
lines.append("- 32 Figma frames 의 anchor keywords (analysis.md mirror — templates_v1.anchor_sets 기반)")
lines.append("")
lines.append("**MDX 키워드 추출**:")
lines.append("1. keyword_base variants → canonical 정규화")
lines.append("2. direct canonical substring hit (compound 보호: 설계Data, 공사비절감 등)")
lines.append("3. Kiwi 형태소 분석 → Figma vocab 교집합")
lines.append("4. direct hit Kiwi hit")
lines.append("")
# 요약 표
lines.append("## 1. TARGET별 결과")
lines.append("")
lines.append("| MDX | 정답 | 1위 | 2위 | margin |")
lines.append("|-----|------|-----|-----|--------|")
for rep in r["reports"]:
top1 = r["frame_to_short"].get(rep["top3"][0][0], "?")
top2 = r["frame_to_short"].get(rep["top3"][1][0], "?")
mark = "✓" if top1 == rep["correct_sid"] else "✗"
lines.append(
f"| {rep['display']} | {rep['correct_sid']} | {top1} ({rep['top3'][0][1]:.3f}) {mark} | "
f"{top2} ({rep['top3'][1][1]:.3f}) | {rep['margin']:.3f} |"
)
lines.append("")
# Top-3 이미지 매트릭스
lines.append("## 2. Top-3 매트릭스")
lines.append("")
lines.append("| 콘텐츠 | 1순위 | 2순위 | 3순위 |")
lines.append("|--------|-------|-------|-------|")
for rep in r["reports"]:
row = [f"**{rep['display']}**<br>정답 Frame **{rep['correct_sid']}**"]
for rank_idx in range(3):
fid, score, _ = rep["top3"][rank_idx]
sid = r["frame_to_short"].get(fid, "?")
info = r["idx_data"].get(sid, {})
png = info.get("png", "")
title = info.get("title_text", "").strip().replace("\n", " ")[:15] + "…"
inner = f"![{sid}]({PNG_REL}{png})<br>**{sid}** ({score:.3f})<br>{title}"
if sid == rep["correct_sid"]:
cell = f"<div style='background:#fff3cd;border:3px solid #dc2626;padding:8px;border-radius:6px'>🎯 <b>정답</b><br>{inner}</div>"
else:
cell = inner
row.append(cell)
lines.append("| " + " | ".join(row) + " |")
lines.append("")
# 상세
lines.append("## 3. 유닛별 세부")
lines.append("")
for rep in r["reports"]:
lines.append(f"### {rep['display']} — 정답 Frame **{rep['correct_sid']}**")
lines.append("")
lines.append(f"**MDX 키워드 ({len(rep['mdx_kws'])}개)**: {', '.join(rep['mdx_kws'])}")
lines.append("")
for rank, (fid, score, bd) in enumerate(rep["top3"], 1):
sid = r["frame_to_short"].get(fid, "?")
info = r["idx_data"].get(sid, {})
png = info.get("png", "")
fig_content = r["frames"][fid].get("content", "")
common = ", ".join(bd["inter"]) if bd["inter"] else "(없음)"
is_correct = sid == rep["correct_sid"]
prefix = "🎯 " if is_correct else ""
lines.append(f"**{prefix}{rank}위 Frame {sid}** {'(정답)' if is_correct else ''}")
lines.append("")
lines.append(f"![{sid}]({PNG_REL}{png})")
lines.append("")
lines.append(f"- **매칭 내용**: {fig_content}")
lines.append(f"- **매칭 키워드**: {common}")
lines.append(f"- **점수**: **{score:.3f}**")
lines.append("")
# 한계
lines.append("## 4. 한계")
lines.append("")
lines.append("- **정제 anchor 만으론 recall 한계**: Milestone 1.6 에서 구조어·weak·AI 요약어 49개 제외 후 anchor pool 이 좁아짐.")
lines.append(" - 원래 analysis.md 후보 키워드 (수동 큐레이션, messy) 대비 recall 낮을 수 있음")
lines.append(" - 대신 precision ↑ + evidence-based 방어 가능")
lines.append("- **IDF Jaccard 만으론 의미 관계 포착 못함**: 같은 주제를 다른 canonical 로 표현 시 매칭 실패 가능")
lines.append("- **구조적 호환성 무시**: 3열 병렬과 2열 비교를 구분 못 함")
lines.append("- **다음 단계**: Phase 23 에서 MDX summary embedding 추가")
lines.append("")
out = HERE / "MATRIX_PHASE22.md"
out.write_text("\n".join(lines), encoding="utf-8")
print(f"완료: {out}")
def main():
result = run()
with open(HERE / "_phase22_results.pkl", "wb") as f:
pickle.dump(result, f)
write_md(result)
print(f"Phase 22: {result['hits']}/4 정답")
if __name__ == "__main__":
main()