5.6 KiB
5.6 KiB
Architecture Decision Records
철학
프로젝트의 핵심 가치관:
- 정확한 수식 변환
- 로컬 작동
- 메모리 최적 사용
- AI Agent가 탐색하기 쉬운 deterministic Markdown bundle
- 원문 구조와 참조 관계 보존
ADR-001: Marker-first document parsing
결정: Marker를 기본 PDF parser로 사용한다.
이유:
- Marker는 layout, OCR, reading order, table, figure, caption, heading을 포함한 문서 구조 추적에 적합하다.
- 프로젝트 목표는 단순 텍스트 추출이 아니라 원문 논리 구조를 Markdown으로 재구성하는 것이다.
트레이드오프:
- Marker 의존성 및 model weight 관리가 필요하다.
- 배포 가능성이 생기면 GPL 및 model license 검토가 필요하다.
ADR-002: Nougat as formula-only parser
결정: Nougat은 전체 PDF parser가 아니라 수식 및 수학적 표현 parser로만 사용한다.
이유:
- Nougat은 학술 문서의 수식/LaTeX 변환에 강점이 있다.
- 전체 문서 구조는 Marker가 담당해야 reading order, 표, 그림, caption 경로가 일관된다.
트레이드오프:
- Marker block과 Nougat 결과를 연결하는 handoff/fallback 계층이 필요하다.
- Nougat 실패 시 Marker 원문 문자열을 fallback으로 사용해야 한다.
ADR-003: PyMuPDF page pre-analysis and chunk planning
결정: PyMuPDF를 페이지 수, 텍스트 레이어 품질, OCR 필요 여부, chunk 계획, 저수준 PDF 작업에 사용한다.
이유:
- 무거운 parser 실행 전에 빠른 page-level 분석이 필요하다.
- 혼합 PDF는 페이지별 OCR 개입 여부를 판단해야 한다.
- 긴 PDF는 20페이지 목표 chunk로 나누되 논리 block 경계를 고려해야 한다.
트레이드오프:
- PyMuPDF 분석 결과와 Marker layout 결과를 조정하는 adapter가 필요하다.
ADR-004: Single Python 3.11 environment
결정: repo-local 단일 Python 3.11 venv를 사용한다.
이유:
- 개발과 실행 경로를 단순화한다.
- Marker와 Nougat은 명시적 dependency pin을 두면 하나의 환경에서 함께 동작한다.
검증된 주요 pin:
torch==2.7.1+cu126torchvision==0.22.1+cu126marker-pdf==1.10.2nougat-ocr==0.1.17transformers==4.57.6albumentations==1.3.1pypdfium2==4.30.0opencv-python-headless==4.11.0.86Pillow==10.4.0fsspec==2026.2.0
트레이드오프:
- Nougat의 느슨한 dependency bounds 때문에 requirements pin을 엄격히 유지해야 한다.
- 최신 PyTorch를 무조건 사용할 수 없다. GTX 1070 Ti
sm_61지원 때문에torch==2.7.1+cu126을 사용한다.
ADR-005: Markdown bundle output without document sidecars by default
결정: 기본 출력은 chunk Markdown 파일과 asset directory로 제한한다.
이유:
- AI Agent가 읽고 탐색하기 쉬운 산출물을 우선한다.
- 별도 sidecar 산출물은 사용자가 명시적으로 요청하기 전까지 범위를 넓히지 않는다.
트레이드오프:
- 변환 diagnostics를 문서 출력과 분리해야 한다.
- runtime log/state/cache는 허용하되 문서 output contract와 구분해야 한다.
ADR-006: Focused quality assertions over full snapshots
결정: 전체 Markdown snapshot 비교보다 focused assertions를 우선한다.
이유:
- PDF 변환 결과는 줄바꿈, spacing, parser version에 민감하다.
- 품질 핵심은 heading, 수식, 표, 이미지, caption, 링크, chunk integrity, 예외 여부다.
트레이드오프:
- 테스트 설계가 더 세분화된다.
- sample metadata mapping이 필요하다.
ADR-007: Runtime fallback policy
결정:
- explicit
--runtime cuda또는--device cuda는 CUDA 실패 시 fail-fast. --runtime auto는 경고 후 CPU fallback 허용.- GPU OOM은 가능한 경우 batch/page 단위를 줄여 재시도.
이유:
- 사용자가 CUDA를 명시한 경우 조용한 CPU 전환은 예측 불가능한 지연을 만든다.
- auto mode는 유연한 실행을 제공해야 한다.
트레이드오프:
- runtime state와 오류 reporting이 필요하다.
ADR-008: Future PyQt UI as thin client
결정: PyQt UI는 변환 엔진을 직접 구현하지 않고 CLI/라이브러리 API를 호출하는 thin client로 둔다.
이유:
- 1차 목표는 CLI/library 엔진 안정화다.
- UI와 core engine의 책임을 분리해야 테스트와 유지보수가 쉽다.
트레이드오프:
- UI 설계 전에 core API contract를 안정화해야 한다.
ADR-009: File-based planner/generator/evaluator Harness
결정: 장기 작업은 planner -> generator -> evaluator 역할 분리와 파일 기반 handoff를 사용하는 Harness workflow로 관리한다.
이유:
- PDF 변환 엔진은 parser, OCR, 수식, 표, 그림, runtime, 테스트가 얽힌 장기 작업이므로 단일 대화에서 일관성을 유지하기 어렵다.
- 작은 self-contained phase step은 새 agent가 fresh context로 작업을 이어받기 쉽게 한다.
- 구현 agent와 평가 agent를 분리하면 자기 평가 편향을 줄이고, hard threshold 기반 검증을 강제할 수 있다.
PLAN.md,PROGRESS.md,phases/파일을 통한 handoff는 대화 밖에서도 현재 상태를 재구성할 수 있게 한다.
트레이드오프:
- 각 step마다 Sprint Contract와 검증 기준을 작성하는 비용이 생긴다.
- 너무 많은 agent, hook, command를 추가하면 Harness 자체가 유지보수 대상이 될 수 있으므로
docs/HARNESS.md의 단순화 규칙을 따른다. - Hook은 보조 장치일 뿐이며, evaluator 검토와 acceptance criteria를 대체하지 않는다.