- 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>
182 lines
6.1 KiB
Python
182 lines
6.1 KiB
Python
"""MDX 01~03에서 17개 단위를 각각 추출.
|
|
중목차는 '제목만'이 아니라 소목차/블릿 내용까지 포함한 의미 단위로 처리."""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
MDX_DIR = Path(r"d:\ad-hoc\kei\design_agent\samples\mdx_batch")
|
|
|
|
|
|
def read_mdx(fname):
|
|
with open(MDX_DIR / fname, encoding="utf-8") as f:
|
|
content = f.read()
|
|
# frontmatter 제거
|
|
content = re.sub(r"^---.*?---", "", content, flags=re.DOTALL).strip()
|
|
# import 문 제거
|
|
content = re.sub(r"^import .*$", "", content, flags=re.MULTILINE)
|
|
return content
|
|
|
|
|
|
def extract_details_blocks(text):
|
|
"""<details>...</details> 블록들 반환. [(full_match, inner_text), ...]"""
|
|
pattern = re.compile(r"<details>.*?</details>", re.DOTALL)
|
|
return pattern.findall(text)
|
|
|
|
|
|
def remove_details(text):
|
|
"""<details>...</details> 블록 제거"""
|
|
return re.sub(r"<details>.*?</details>", "", text, flags=re.DOTALL)
|
|
|
|
|
|
def extract_markdown_tables(text):
|
|
"""| ... | ... | 형식 표 블록들 반환."""
|
|
lines = text.split("\n")
|
|
tables = []
|
|
current = []
|
|
in_table = False
|
|
for ln in lines:
|
|
stripped = ln.strip()
|
|
if re.match(r"^\|.*\|", stripped):
|
|
current.append(ln)
|
|
in_table = True
|
|
elif in_table:
|
|
# 표 끝
|
|
if current:
|
|
tables.append("\n".join(current))
|
|
current = []
|
|
in_table = False
|
|
if current:
|
|
tables.append("\n".join(current))
|
|
return tables
|
|
|
|
|
|
def extract_images(text):
|
|
""" 이미지. [(alt, src), ...]"""
|
|
pattern = re.compile(r"!\[([^\]]*)\]\(([^)]+)\)")
|
|
return pattern.findall(text)
|
|
|
|
|
|
def get_section(text, level, title_pattern, title_weight=3):
|
|
"""특정 레벨의 섹션 제목 + 본문 추출.
|
|
title_weight: 제목을 본문 맨 앞에 N번 반복해서 가중치 부여 (BM25/TF-IDF에서 자동으로 높은 점수).
|
|
0으로 주면 제목 제외 (구버전 동작)."""
|
|
prefix = "#" * level
|
|
lines = text.split("\n")
|
|
start = None
|
|
title_text = ""
|
|
for i, ln in enumerate(lines):
|
|
m = re.match(rf"^{prefix}\s+{title_pattern}", ln)
|
|
if m:
|
|
start = i
|
|
title_text = ln[level:].strip()
|
|
break
|
|
if start is None:
|
|
return ""
|
|
end = len(lines)
|
|
for i in range(start + 1, len(lines)):
|
|
ln = lines[i]
|
|
m = re.match(r"^(#+)\s", ln)
|
|
if m and len(m.group(1)) <= level:
|
|
end = i
|
|
break
|
|
body = "\n".join(lines[start + 1:end]).strip()
|
|
if title_weight > 0 and title_text:
|
|
title_block = "\n".join([title_text] * title_weight)
|
|
return (title_block + "\n\n" + body).strip()
|
|
return body
|
|
|
|
|
|
def extract_units():
|
|
"""17개 단위를 추출해서 {unit_id: text} dict 반환"""
|
|
mdx01 = read_mdx("01.mdx")
|
|
mdx02 = read_mdx("02.mdx")
|
|
mdx03 = read_mdx("03.mdx")
|
|
|
|
units = {}
|
|
|
|
# ═══════ MDX01 ═══════
|
|
# intro: 첫 ## 이전 텍스트, <details>는 제외
|
|
intro_block = mdx01.split("\n## ")[0]
|
|
# intro의 <details>
|
|
details01 = extract_details_blocks(intro_block)
|
|
# intro 본문 (details 제외)
|
|
units["MDX01-intro"] = remove_details(intro_block).strip()
|
|
units["MDX01-intro-details"] = details01[0] if details01 else ""
|
|
|
|
# ## 1. 용어 정의
|
|
sec01_1 = get_section(mdx01, 2, "1\\. 용어 정의")
|
|
units["MDX01-1"] = sec01_1
|
|
|
|
# ## 2. 용어간 상호관계 — 본문은 details 제외, 이미지 제외
|
|
sec01_2 = get_section(mdx01, 2, "2\\. 용어간 상호관계")
|
|
sec01_2_main = remove_details(sec01_2)
|
|
# 이미지 제거
|
|
sec01_2_main = re.sub(r"!\[[^\]]*\]\([^)]+\)", "", sec01_2_main).strip()
|
|
units["MDX01-2"] = sec01_2_main
|
|
|
|
# 이미지 단위
|
|
imgs01_2 = extract_images(sec01_2)
|
|
if imgs01_2:
|
|
alt, src = imgs01_2[0]
|
|
units["MDX01-2-image"] = f"{alt} {src}"
|
|
else:
|
|
units["MDX01-2-image"] = ""
|
|
|
|
# <details> (비교표)
|
|
details01_2 = extract_details_blocks(sec01_2)
|
|
units["MDX01-2-details"] = details01_2[0] if details01_2 else ""
|
|
|
|
# ═══════ MDX02 ═══════
|
|
# ## 1. DX의 궁극적 목표 — 이미지 제외
|
|
sec02_1 = get_section(mdx02, 2, "1\\. DX의 궁극적 목표")
|
|
sec02_1_main = re.sub(r"!\[[^\]]*\]\([^)]+\)", "", sec02_1).strip()
|
|
units["MDX02-1"] = sec02_1_main
|
|
imgs02_1 = extract_images(sec02_1)
|
|
units["MDX02-1-image"] = f"{imgs02_1[0][0]} {imgs02_1[0][1]}" if imgs02_1 else ""
|
|
|
|
# ## 2. 주체별 기대효과 (컨테이너 — 소목차+블릿 전체 포함)
|
|
sec02_2 = get_section(mdx02, 2, "2\\. DX 기반 Process 혁신")
|
|
units["MDX02-2"] = sec02_2 # 소목차까지 다 포함
|
|
|
|
# ### 2.1 업무 수행 과정의 변화
|
|
sec02_2_1 = get_section(mdx02, 3, "2\\.1 업무 수행 과정")
|
|
units["MDX02-2.1"] = sec02_2_1
|
|
|
|
# ### 2.2 DX 시행 주체별 기대효과
|
|
sec02_2_2 = get_section(mdx02, 3, "2\\.2 DX 시행 주체별")
|
|
units["MDX02-2.2"] = sec02_2_2
|
|
|
|
# 2.2 안의 표만
|
|
tables02_2_2 = extract_markdown_tables(sec02_2_2)
|
|
units["MDX02-2.2-table"] = tables02_2_2[0] if tables02_2_2 else ""
|
|
|
|
# ═══════ MDX03 ═══════
|
|
# ## 1. 필수 요건
|
|
sec03_1 = get_section(mdx03, 2, "1\\. DX 시행을 위한 필수 요건")
|
|
units["MDX03-1"] = sec03_1
|
|
|
|
# ## 2. Process/Product 혁신 (컨테이너)
|
|
sec03_2 = get_section(mdx03, 2, "2\\. Process의 혁신과 Product")
|
|
units["MDX03-2"] = sec03_2
|
|
|
|
# ### 2.1 과정(Process)의 혁신
|
|
sec03_2_1 = get_section(mdx03, 3, "2\\.1 과정\\(Process\\)의 혁신")
|
|
units["MDX03-2.1"] = sec03_2_1
|
|
|
|
# 2.1 안의 표만
|
|
tables03_2_1 = extract_markdown_tables(sec03_2_1)
|
|
units["MDX03-2.1-table"] = tables03_2_1[0] if tables03_2_1 else ""
|
|
|
|
# ### 2.2 결과(Product)의 변화
|
|
sec03_2_2 = get_section(mdx03, 3, "2\\.2 결과\\(Product\\)의 변화")
|
|
units["MDX03-2.2"] = sec03_2_2
|
|
|
|
return units
|
|
|
|
|
|
if __name__ == "__main__":
|
|
units = extract_units()
|
|
print(f"총 {len(units)}개 단위 추출\n")
|
|
for uid, text in units.items():
|
|
preview = text[:80].replace("\n", " ").strip()
|
|
print(f" {uid:25s} [{len(text):4d}자] {preview}...")
|