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>
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
"""Phase 12 — 키워드 수준 매칭 진단 (왜 이 Figma가 1위/2위가 됐나)
|
||||
각 MDX 유닛에 대해:
|
||||
1. MDX 측 추출 키워드 (Kiwi 형태소 + IDF 가중 상위 15개)
|
||||
2. Ensemble Top-3 후보 각각에 대해:
|
||||
- Figma 측 키워드 (공통 + 고유)
|
||||
- 공통 키워드 × IDF 기여도
|
||||
- BM25 점수 / Cross 점수 / Ensemble 점수
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import math
|
||||
import collections
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from common import load_figma_texts
|
||||
from extract_units import extract_units
|
||||
from methods import (
|
||||
_get_kiwi, _extract_content_tokens,
|
||||
method_kiwi_bm25, method_cross_encoder,
|
||||
)
|
||||
from phase10 import extract_titles_only_mdx, extract_titles_only_figma, TARGET_UNITS
|
||||
from phase11 import method_ensemble
|
||||
|
||||
ROOT = Path(r"d:\ad-hoc\kei\design_agent")
|
||||
PREVIEW_DIR = ROOT / "data" / "figma_previews"
|
||||
|
||||
|
||||
def build_idf(figma_texts, kiwi):
|
||||
"""32개 Figma 프레임 전체에서 IDF 계산"""
|
||||
doc_tokens = {}
|
||||
df = collections.Counter()
|
||||
for fid, text in figma_texts.items():
|
||||
toks = _extract_content_tokens(text, kiwi)
|
||||
doc_tokens[fid] = collections.Counter(toks)
|
||||
for w in set(toks):
|
||||
df[w] += 1
|
||||
N = len(figma_texts)
|
||||
idf = {w: math.log(N / c) for w, c in df.items() if c > 0}
|
||||
return doc_tokens, idf, N
|
||||
|
||||
|
||||
def main():
|
||||
units_full = extract_units()
|
||||
figma_full = load_figma_texts()
|
||||
figma_titles = extract_titles_only_figma()
|
||||
|
||||
mdx_titles = {}
|
||||
for uid, _, _, fname, mid, sub in TARGET_UNITS:
|
||||
mdx_titles[uid] = extract_titles_only_mdx(fname, mid, sub)
|
||||
|
||||
with open(PREVIEW_DIR / "index.json", encoding="utf-8") as f:
|
||||
idx_data = json.load(f)
|
||||
frame_to_short = {info["frame_id"]: sid for sid, info in idx_data.items()}
|
||||
|
||||
kiwi = _get_kiwi()
|
||||
doc_tokens, idf, N = build_idf(figma_full, kiwi)
|
||||
|
||||
lines = []
|
||||
lines.append("# Phase 12 — 키워드 수준 매칭 진단")
|
||||
lines.append("")
|
||||
lines.append(f"**IDF 베이스**: {N}개 Figma 프레임 전체 corpus")
|
||||
lines.append(f"**키워드 추출**: Kiwi 형태소 분석기, 내용어(명사/동사/형용사/영문/숫자)만, 2글자 이상")
|
||||
lines.append("")
|
||||
lines.append("각 유닛에 대해 Ensemble(α=0.7) Top-3 후보의 키워드 수준 매칭을 보여줍니다.")
|
||||
lines.append("")
|
||||
|
||||
for uid, display, correct_id, _, _, _ in TARGET_UNITS:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append(f"## {display}")
|
||||
lines.append(f"**정답**: Frame **{correct_id}**")
|
||||
lines.append("")
|
||||
|
||||
# MDX 측 키워드
|
||||
mdx_text = units_full[uid]
|
||||
mdx_title_text = mdx_titles[uid]
|
||||
mdx_tokens = _extract_content_tokens(mdx_text, kiwi)
|
||||
mdx_tf = collections.Counter(mdx_tokens)
|
||||
mdx_weighted = [(w, c, idf.get(w, 0), c * idf.get(w, 0))
|
||||
for w, c in mdx_tf.items() if idf.get(w, 0) > 0]
|
||||
mdx_weighted.sort(key=lambda x: -x[3])
|
||||
lines.append(f"### MDX 측 키워드 (Kiwi + IDF 상위 15개)")
|
||||
lines.append("")
|
||||
lines.append("| 키워드 | TF | IDF | TF×IDF |")
|
||||
lines.append("|-------|----|----|-------|")
|
||||
for w, tf_, widf, score in mdx_weighted[:15]:
|
||||
lines.append(f"| {w} | {tf_} | {widf:.2f} | {score:.2f} |")
|
||||
lines.append("")
|
||||
|
||||
# Ensemble Top-3
|
||||
ensemble_ranked = method_ensemble(mdx_text, mdx_title_text,
|
||||
figma_full, figma_titles, alpha=0.7)
|
||||
bm25_dict = dict(method_kiwi_bm25(mdx_text, figma_full))
|
||||
cross_dict = dict(method_cross_encoder(mdx_title_text, figma_titles))
|
||||
|
||||
lines.append(f"### Ensemble Top-3 후보 (α=0.7: 70% BM25본문×전체 + 30% Cross제목×제목)")
|
||||
lines.append("")
|
||||
|
||||
for rank_idx, (fid, ens_score) in enumerate(ensemble_ranked[:3], 1):
|
||||
short = frame_to_short.get(str(fid), "?")
|
||||
mark = " ⭐ 정답" if short == correct_id else ""
|
||||
info = idx_data.get(short, {})
|
||||
figma_title_text = info.get("title_text", "").replace("\n", " ").strip() or "(타이틀 없음)"
|
||||
figma_cleaned_title = figma_titles.get(fid, "(타이틀 섹션 비어있음)")
|
||||
|
||||
lines.append(f"#### {rank_idx}위 Frame **{short}**{mark}")
|
||||
lines.append(f"- Figma 파일 타이틀: `{figma_title_text[:50]}`")
|
||||
lines.append(f"- 추출된 타이틀: `{figma_cleaned_title[:80]}`")
|
||||
lines.append(f"- **BM25** = {bm25_dict.get(fid, 0):.1f} | "
|
||||
f"**Cross** = {cross_dict.get(fid, 0):.3f} | "
|
||||
f"**Ensemble(0.7)** = {ens_score:.3f}")
|
||||
lines.append("")
|
||||
|
||||
# 공통 키워드 분석
|
||||
figma_tf = doc_tokens.get(fid, {})
|
||||
common = []
|
||||
for w, mdx_c, widf, _ in mdx_weighted:
|
||||
if w in figma_tf:
|
||||
contribution = mdx_c * figma_tf[w] * widf
|
||||
common.append((w, mdx_c, figma_tf[w], widf, contribution))
|
||||
common.sort(key=lambda x: -x[4])
|
||||
|
||||
if common:
|
||||
lines.append("**공통 키워드 (IDF 기여도 순 상위 12개):**")
|
||||
lines.append("")
|
||||
lines.append("| 키워드 | MDX TF | Figma TF | IDF | 기여도 |")
|
||||
lines.append("|-------|--------|----------|-----|--------|")
|
||||
for w, mc, fc, widf, contrib in common[:12]:
|
||||
lines.append(f"| {w} | {mc} | {fc} | {widf:.2f} | {contrib:.2f} |")
|
||||
lines.append("")
|
||||
lines.append(f"**공통 키워드 총 {len(common)}개, 기여도 합계 = "
|
||||
f"{sum(c[4] for c in common):.2f}**")
|
||||
else:
|
||||
lines.append("**공통 키워드 없음.**")
|
||||
lines.append("")
|
||||
|
||||
# 해석 도움말
|
||||
lines.append("### 해석")
|
||||
lines.append("")
|
||||
top1 = ensemble_ranked[0]
|
||||
top1_short = frame_to_short.get(str(top1[0]), "?")
|
||||
if top1_short == correct_id:
|
||||
lines.append(f"✓ Ensemble이 정답({correct_id})을 1위로 선택.")
|
||||
else:
|
||||
lines.append(f"✗ Ensemble이 정답({correct_id})을 놓침. 1위 = {top1_short}.")
|
||||
# 정답이 Top-3에 있는지
|
||||
for i, (fid, _) in enumerate(ensemble_ranked[:3], 1):
|
||||
if frame_to_short.get(str(fid), "?") == correct_id:
|
||||
lines.append(f" (정답은 {i}위에 있음)")
|
||||
break
|
||||
lines.append("")
|
||||
|
||||
out_path = Path(__file__).parent / "MATRIX_PHASE12.md"
|
||||
out_path.write_text("\n".join(lines), encoding="utf-8")
|
||||
print(f"완료: {out_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user