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,200 @@
|
||||
"""전체 32 frame anchor_sets 일람 뷰 (리뷰용).
|
||||
|
||||
간결한 한 페이지 HTML. 각 frame 의 anchor_set id + terms 만 나열.
|
||||
raw 수치가 필요하면 anchor_sets_report.html 참조.
|
||||
|
||||
입력:
|
||||
anchor_sets_draft.yaml
|
||||
actual_text_nodes.yaml (frame 주제 표시용)
|
||||
normalized_text_tokens.yaml (mdx_df 참고)
|
||||
|
||||
출력:
|
||||
ANCHOR_SETS_OVERVIEW.md
|
||||
ANCHOR_SETS_OVERVIEW.html
|
||||
"""
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
import markdown
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
|
||||
DRAFT = HERE / "anchor_sets_draft.yaml"
|
||||
NODES = HERE / "actual_text_nodes.yaml"
|
||||
NORMALIZED = HERE / "normalized_text_tokens.yaml"
|
||||
OUT_MD = HERE / "ANCHOR_SETS_OVERVIEW.md"
|
||||
OUT_HTML = HERE / "ANCHOR_SETS_OVERVIEW.html"
|
||||
|
||||
TARGET_FRAMES = {13, 14, 18, 29}
|
||||
|
||||
|
||||
def load_yaml(p):
|
||||
return yaml.safe_load(p.read_text(encoding='utf-8'))
|
||||
|
||||
|
||||
def get_subject(frame_id, nodes_data):
|
||||
"""frame 의 주제 짧게 — texts.md 첫 몇 줄을 붙임."""
|
||||
info = nodes_data['frames'].get(frame_id, {})
|
||||
tn = info.get('text_nodes', [])
|
||||
if not tn:
|
||||
return '(텍스트 없음)'
|
||||
# 처음 2-3 줄
|
||||
out = []
|
||||
total_len = 0
|
||||
for t in tn[:5]:
|
||||
if total_len + len(t) > 100:
|
||||
break
|
||||
out.append(t)
|
||||
total_len += len(t)
|
||||
return ' / '.join(out) if out else tn[0][:80]
|
||||
|
||||
|
||||
def term_badge(term_obj, corpus_mdx_df):
|
||||
"""token 을 mdx_hit 표시와 함께 짧게 렌더."""
|
||||
tok = term_obj['token']
|
||||
if not term_obj.get('in_frame'):
|
||||
return f"⚠{tok}"
|
||||
mdx = term_obj.get('mdx_df', 0)
|
||||
special = term_obj.get('is_special', False)
|
||||
marks = []
|
||||
if special:
|
||||
marks.append('S')
|
||||
if mdx > 0:
|
||||
marks.append(f'M{mdx}')
|
||||
mark_str = f"<sup>{','.join(marks)}</sup>" if marks else ''
|
||||
return f"{tok}{mark_str}"
|
||||
|
||||
|
||||
def collect_review_set_ids(frame_out):
|
||||
ids = set()
|
||||
for rv in frame_out.get('review_needed') or []:
|
||||
tok = rv.get('token', '')
|
||||
if tok.endswith('(set)'):
|
||||
ids.add(tok.replace('(set)', '').strip())
|
||||
return ids
|
||||
|
||||
|
||||
def collect_review_tokens(frame_out):
|
||||
out = set()
|
||||
for rv in frame_out.get('review_needed') or []:
|
||||
tok = rv.get('token', '')
|
||||
if tok.endswith('(set)'):
|
||||
continue
|
||||
out.add(tok)
|
||||
return out
|
||||
|
||||
|
||||
def render_frame_md(fnum, fid, frame_out, subject):
|
||||
is_target = fnum in TARGET_FRAMES
|
||||
target_mark = ' 🎯 **TARGET**' if is_target else ''
|
||||
lines = [f"### Frame {fnum} / {fid}{target_mark}", ""]
|
||||
lines.append(f"**Subject**: {subject}")
|
||||
lines.append(f"**total_unique_tokens**: {frame_out.get('total_unique_tokens', '?')}")
|
||||
lines.append("")
|
||||
|
||||
rn_set_ids = collect_review_set_ids(frame_out)
|
||||
rn_tokens = collect_review_tokens(frame_out)
|
||||
|
||||
anchor_sets = frame_out.get('anchor_sets', [])
|
||||
if not anchor_sets:
|
||||
lines.append("_(anchor_set 없음)_")
|
||||
lines.append("")
|
||||
return '\n'.join(lines)
|
||||
|
||||
lines.append("**Anchor sets**:")
|
||||
lines.append("")
|
||||
for aset in anchor_sets:
|
||||
set_id = aset['id']
|
||||
set_mark = ' 🔸**review_needed**' if set_id in rn_set_ids else ''
|
||||
term_strs = []
|
||||
for t in aset.get('terms', []):
|
||||
tok = t['token']
|
||||
marks = []
|
||||
if t.get('is_special'): marks.append('S')
|
||||
if t.get('mdx_df', 0) > 0: marks.append(f"M{t['mdx_df']}")
|
||||
if tok in rn_tokens: marks.append('RN')
|
||||
if not t.get('in_frame'): marks.append('⚠')
|
||||
mark_str = f" ({','.join(marks)})" if marks else ''
|
||||
term_strs.append(f"`{tok}`{mark_str}")
|
||||
lines.append(f"- **{set_id}**{set_mark}: {', '.join(term_strs)}")
|
||||
|
||||
# notes
|
||||
notes = frame_out.get('notes')
|
||||
if notes:
|
||||
lines.append("")
|
||||
lines.append(f"**Notes**: _{notes.strip()}_")
|
||||
lines.append("")
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
draft = load_yaml(DRAFT)
|
||||
nodes = load_yaml(NODES)
|
||||
norm = load_yaml(NORMALIZED)
|
||||
corpus_mdx_df = norm['corpus']['token_mdx_df']
|
||||
|
||||
frame_ids = sorted(draft['frames'].keys())
|
||||
|
||||
md = [
|
||||
"# Anchor Sets Overview — 전체 32 frame 일람",
|
||||
"",
|
||||
"리뷰용 한 페이지 뷰. 각 anchor_set 의 id + terms 만 표시.",
|
||||
"상세 수치는 `anchor_sets_report.html` 참조.",
|
||||
"",
|
||||
"**범례**:",
|
||||
"- `S` = special token (S/W, H/W, 2D, 3D, DX, BIM, As-is, To-Be, 결과혁신, 과정혁신, 필수조건, 의사소통, 시행착오)",
|
||||
"- `M1/M2/M3` = 해당 token 의 mdx_df (MDX section 몇 개에 등장)",
|
||||
"- `RN` = review_needed term",
|
||||
"- 🔸 = set 레벨 review_needed (유지하되 재검토)",
|
||||
"- 🎯 TARGET = MDX 매칭 정답 frame (13, 14, 18, 29)",
|
||||
"",
|
||||
f"- frames: **{len(frame_ids)}**",
|
||||
f"- anchor_sets: **{sum(len(f.get('anchor_sets', [])) for f in draft['frames'].values())}**",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
]
|
||||
|
||||
for fid in frame_ids:
|
||||
frame_out = draft['frames'][fid]
|
||||
fnum = frame_out['frame_number']
|
||||
subject = get_subject(fid, nodes)
|
||||
md.append(render_frame_md(fnum, fid, frame_out, subject))
|
||||
|
||||
md_text = '\n'.join(md)
|
||||
OUT_MD.write_text(md_text, encoding='utf-8')
|
||||
|
||||
html_body = markdown.markdown(md_text, extensions=['tables'])
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Anchor Sets Overview — 전체 32 frame 일람</title>
|
||||
<style>
|
||||
body {{ font-family: -apple-system, "Segoe UI", Pretendard, sans-serif; max-width: 1100px; margin: 2em auto; padding: 0 1.5em 3em; line-height: 1.55; color: #222; }}
|
||||
h1 {{ border-bottom: 2px solid #333; padding-bottom: 0.2em; }}
|
||||
h3 {{ margin-top: 2em; color: #111; border-bottom: 1px solid #ddd; padding-bottom: 0.3em; }}
|
||||
h3:has(em) {{ background: #fffbdc; padding: 0.4em 0.6em; border-radius: 4px; }}
|
||||
ul {{ margin: 0.3em 0 0.8em 0; }}
|
||||
li {{ margin: 0.15em 0; }}
|
||||
code {{ background: #f4f4f4; padding: 1px 5px; border-radius: 3px; font-size: 0.92em; }}
|
||||
strong {{ color: #0a6; }}
|
||||
em {{ color: #888; font-style: normal; }}
|
||||
sup {{ color: #d33; font-size: 0.7em; margin-left: 1px; }}
|
||||
.subject, p > strong + em {{ color: #555; }}
|
||||
hr {{ border: 0; border-top: 1px solid #ddd; margin: 1.5em 0; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{html_body}
|
||||
</body>
|
||||
</html>"""
|
||||
OUT_HTML.write_text(html, encoding='utf-8')
|
||||
|
||||
print(f"산출:")
|
||||
print(f" md: {OUT_MD}")
|
||||
print(f" html: {OUT_HTML}")
|
||||
print(f" frames: {len(frame_ids)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user