inital commit
This commit is contained in:
+95
@@ -0,0 +1,95 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
## 철학
|
||||
- 공학적인 PDF 문서를 AI가 쉽게 이해할 수 있도록 수식, 이미지, 표, 글의 구조를 정확하게 변환한다.
|
||||
- AI가 쉽게 분할된 문서에 접근할 수 있도록 챕터나 문단별로 파일명, line 위치, 원본 page 정보를 연결하는 index 파일을 생성한다.
|
||||
- 로컬 실행과 재현 가능한 변환 결과를 1차 목표로 삼고, 외부 API와 UI는 이후 단계로 분리한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-001: Marker를 1차 PDF Parser로 채택
|
||||
**결정**: `Nougat` 대신 `Marker`를 1차 기본 PDF parser로 사용한다.
|
||||
|
||||
**이유**:
|
||||
- 1차 MVP 범위인 텍스트, 수식, 이미지, 표 구조화, Markdown 변환에 더 넓게 대응하기 위해서이다.
|
||||
- Marker는 PDF를 Markdown/JSON 계열 구조로 변환하는 흐름에 적합하며, 후처리 파이프라인과 연결하기 좋다.
|
||||
- Nougat은 수식 중심 과학 문서에 장점이 있지만, 현재 목표에서는 전체 문서 구조와 표/이미지 처리가 함께 중요하다.
|
||||
|
||||
**트레이드오프**:
|
||||
- Nougat 기반 수식 변환 품질을 기본값으로 활용하지 않는다.
|
||||
- Marker 결과 품질이 부족한 문서 유형에 대비해 향후 fallback adapter를 열어두어야 한다.
|
||||
- Marker와 관련 모델의 라이선스와 Windows/GPU 실행 제약은 별도 검증이 필요하다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-002: 1차 목표는 Windows Native Local Execution
|
||||
**결정**: 1차 MVP는 Windows native 환경에서 완전 로컬로 실행한다.
|
||||
|
||||
**이유**:
|
||||
- 현재 목표 사용 환경이 Windows이며, 개인 프로젝트로 로컬 문서 처리를 우선하기 때문이다.
|
||||
- 외부 API 의존을 제거하면 개인정보, 비용, 네트워크 실패 문제를 줄일 수 있다.
|
||||
- VRAM 8GB 환경을 기준으로 chunk 처리와 배치 크기 제어를 설계할 수 있다.
|
||||
|
||||
**트레이드오프**:
|
||||
- 외부 API 기반 LaTeX 복구, 이미지 설명, 품질 검토는 1차 범위에서 제외한다.
|
||||
- Windows native에서 일부 ML 의존성 설치가 까다로울 수 있다.
|
||||
- GPU OOM과 CPU fallback 정책을 반드시 고려해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-003: Obsidian 친화 Markdown을 출력 표준으로 채택
|
||||
**결정**: 변환 결과는 Obsidian에서 바로 탐색 가능한 Markdown 문서 묶음으로 출력한다.
|
||||
|
||||
**이유**:
|
||||
- 사용자가 변환 결과를 개인 지식 관리와 AI Agent 탐색에 활용하려는 목적에 맞다.
|
||||
- 내부 링크, heading 기반 이동, 이미지 embed, 수식 렌더링을 활용할 수 있다.
|
||||
- 긴 PDF를 여러 Markdown 파일과 index로 나누는 구조와 잘 맞는다.
|
||||
|
||||
**트레이드오프**:
|
||||
- 순수 GitHub Markdown만 목표로 할 때보다 링크/이미지/수식 규칙을 더 신중하게 정해야 한다.
|
||||
- Obsidian 내부 링크와 표준 Markdown 링크 사이의 호환성 정책이 필요하다.
|
||||
- 파일명 slug, heading anchor, 이미지 경로 규칙을 프로젝트 표준으로 고정해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-004: 내부 Document Model과 Source Provenance를 사용
|
||||
**결정**: Marker 출력물을 바로 최종 Markdown으로 저장하지 않고 내부 Document Model을 거쳐 렌더링한다.
|
||||
|
||||
**이유**:
|
||||
- parser engine 교체 가능성을 유지하기 위해서이다.
|
||||
- Markdown 출력, index 생성, 품질 검증, 재시도, 로그 진단을 동일한 구조 위에서 처리할 수 있다.
|
||||
- AI Agent가 변환된 문장을 원본 PDF page, block id, bbox, line 위치와 연결할 수 있어야 한다.
|
||||
|
||||
**트레이드오프**:
|
||||
- 초기 구현량이 늘어난다.
|
||||
- Document Model schema를 먼저 정의해야 한다.
|
||||
- Marker 원본 출력과 내부 모델 사이의 매핑 오류를 테스트해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-005: CLI/Library First, PyQt UI Second
|
||||
**결정**: 1차는 CLI와 라이브러리 변환 엔진을 먼저 만들고, PyQt UI는 2차 목표로 둔다.
|
||||
|
||||
**이유**:
|
||||
- 변환 품질과 테스트 자동화가 UI보다 먼저 안정화되어야 한다.
|
||||
- CLI/라이브러리 계층이 안정적이면 PyQt UI는 얇은 호출 계층으로 만들 수 있다.
|
||||
- Harness/TDD 기반 검증 흐름과 잘 맞는다.
|
||||
|
||||
**트레이드오프**:
|
||||
- 초기 사용자 경험은 UI보다 제한적이다.
|
||||
- UI 관련 상태 관리와 진행률 표시 설계는 후속 단계에서 다룬다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-006: Chunked and Resumable Conversion
|
||||
**결정**: 긴 PDF는 기본적으로 20페이지 단위 chunk로 나누고, chunk별 상태와 캐시를 보존한다.
|
||||
|
||||
**이유**:
|
||||
- AI Agent가 긴 문서를 한 번에 읽기 어렵고, 변환도 오래 걸리기 때문이다.
|
||||
- GPU VRAM 8GB 환경에서 큰 문서를 안정적으로 처리하려면 chunk 단위 작업이 필요하다.
|
||||
- 실패한 chunk만 재시도할 수 있어야 실사용성이 높다.
|
||||
|
||||
**트레이드오프**:
|
||||
- chunk 경계에서 문단, 표, 그림, 수식이 걸칠 수 있다.
|
||||
- chunk 결과를 합쳐 index를 만들 때 page range와 heading 연결을 신중히 처리해야 한다.
|
||||
- 캐시 무효화 정책이 필요하다.
|
||||
@@ -0,0 +1,114 @@
|
||||
# 아키텍처
|
||||
|
||||
## 기본 원칙
|
||||
- 1차 목표는 UI가 아니라 안정적인 CLI/라이브러리 변환 엔진이다.
|
||||
- PDF parser는 `Marker`를 기본 엔진으로 사용한다.
|
||||
- 변환 파이프라인은 Marker 출력에 직접 결합하지 않고 내부 중간 표현(Document Model)을 거친다.
|
||||
- 출력은 Obsidian 친화 Markdown을 우선하되, 표준 Markdown과의 호환성을 가능한 한 유지한다.
|
||||
- 긴 문서는 20페이지 단위 chunk로 처리하고, chunk별 상태/캐시/로그를 남겨 재시도가 가능해야 한다.
|
||||
|
||||
## 목표 디렉토리 구조
|
||||
```
|
||||
venv/ # 가상 환경
|
||||
samples/ # 품질 검증용 샘플 PDF
|
||||
src/
|
||||
├── converter/ # PDF to Markdown 변환 핵심
|
||||
│ ├── engines/ # Marker 및 향후 parser adapter
|
||||
│ ├── model/ # 내부 Document Model
|
||||
│ ├── renderers/ # Obsidian Markdown 출력
|
||||
│ ├── indexer/ # index 파일 생성
|
||||
│ └── diagnostics/ # 로그, 경고, 변환 상태
|
||||
├── ui/ # 2차 목표: PyQt UI 컴포넌트
|
||||
└── utils/ # 유틸리티 + 헬퍼
|
||||
```
|
||||
|
||||
## 주요 컴포넌트
|
||||
### Converter Core
|
||||
- PDF 입력, 사전 분석, chunk 분할, parser 실행, 중간 표현 생성, 출력 렌더링을 조율한다.
|
||||
- UI와 분리되어 CLI와 테스트에서 직접 호출 가능해야 한다.
|
||||
|
||||
### Parser Engine Adapter
|
||||
- 1차 구현은 `MarkerEngine`만 대상으로 한다.
|
||||
- 향후 `Nougat`, `Docling`, `PyMuPDF` 등 다른 엔진을 붙일 수 있도록 adapter 경계를 둔다.
|
||||
- parser adapter는 원본 parser 결과와 프로젝트 내부 Document Model 사이의 변환을 담당한다.
|
||||
|
||||
### Document Model
|
||||
내부 중간 표현은 최소한 다음 정보를 담아야 한다.
|
||||
- document id, title, source path, output slug
|
||||
- chunk id, page range, conversion status
|
||||
- block id, block type, text/content
|
||||
- heading level, paragraph, list, quote, equation, table, figure
|
||||
- source page, bbox, confidence, parser metadata
|
||||
- image/table asset path
|
||||
- warnings/errors
|
||||
|
||||
### Obsidian Renderer
|
||||
- Document Model을 Obsidian 친화 Markdown 파일로 렌더링한다.
|
||||
- 수식은 `$ ... $`, `$$ ... $$` 형식을 지킨다.
|
||||
- 이미지는 Obsidian 내부 링크 또는 프로젝트에서 정한 표준 이미지 링크 형식으로 연결한다.
|
||||
- 복잡한 표는 Markdown table에 억지로 맞추지 않고 HTML table 또는 별도 CSV 링크를 허용한다.
|
||||
|
||||
### Index Builder
|
||||
- 단순 목차가 아니라 AI Agent가 문서 내부로 진입하기 위한 탐색 index를 만든다.
|
||||
- heading, 주요 문단, 표, 그림, 수식에 대해 Markdown 파일, heading, line 위치, 원본 page 정보를 연결한다.
|
||||
- 가능한 경우 block id와 bbox를 metadata에 보존한다.
|
||||
|
||||
### Diagnostics & Recovery
|
||||
- 변환 설정, Marker 버전, GPU/CPU 사용 여부, 처리 시간, 경고, 실패 chunk를 기록한다.
|
||||
- 완료된 chunk는 재사용하고 실패한 chunk만 재시도할 수 있어야 한다.
|
||||
- 원본 Marker 결과와 내부 Document Model snapshot은 디버깅 가능한 형태로 보존한다.
|
||||
|
||||
## 프로그램 흐름
|
||||
```
|
||||
PDF 파일 입력
|
||||
-> 사전 분석: 페이지 수, 텍스트 포함 여부, 스캔 여부, 예상 chunk 수 확인
|
||||
-> 20페이지 단위 chunk 분할
|
||||
-> chunk별 Marker 변환
|
||||
-> Marker 결과를 내부 Document Model로 변환
|
||||
-> reading order, paragraph stitching, semantic mapping 보정
|
||||
-> 이미지/표/수식 asset 정리
|
||||
-> Obsidian 친화 Markdown 렌더링
|
||||
-> index 파일 생성
|
||||
-> metadata/log 저장
|
||||
```
|
||||
|
||||
## 상태 관리
|
||||
- 문서 단위 상태와 chunk 단위 상태를 분리한다.
|
||||
- chunk 상태는 `pending`, `running`, `completed`, `error`, `skipped` 같은 값으로 관리한다.
|
||||
- 변환 중 실패가 발생해도 완료된 chunk 결과는 유지한다.
|
||||
- 최종 index는 chunk 결과를 기준으로 재생성 가능해야 한다.
|
||||
|
||||
## 변환 결과 출력 구조
|
||||
```
|
||||
output/
|
||||
└── document-slug/
|
||||
├── document-slug_index.md
|
||||
├── document-slug_001.md
|
||||
├── document-slug_002.md
|
||||
├── document-slug_003.md
|
||||
├── images/
|
||||
│ ├── page-001_fig-001.png
|
||||
│ └── page-012_fig-003.png
|
||||
├── tables/
|
||||
│ ├── page-005_table-001.csv
|
||||
│ └── page-008_table-002.html
|
||||
└── metadata/
|
||||
├── document.json
|
||||
├── chunks.json
|
||||
├── parser-output/
|
||||
└── conversion.log
|
||||
```
|
||||
|
||||
## Obsidian 출력 규칙
|
||||
- 파일명은 Windows와 Obsidian에서 안전한 slug 기반 이름을 사용한다.
|
||||
- 원본 PDF 파일명은 metadata에 보존한다.
|
||||
- heading은 Obsidian 내부 링크 대상이 될 수 있도록 안정적으로 생성한다.
|
||||
- 이미지는 상대 경로 또는 Obsidian 내부 링크 중 프로젝트 표준을 정해 일관되게 사용한다.
|
||||
- index 파일은 각 chunk 파일과 주요 heading으로 이동할 수 있어야 한다.
|
||||
- 수식은 Obsidian에서 렌더링 가능한 Markdown math 문법을 사용한다.
|
||||
|
||||
## 검증 전략
|
||||
- TDD를 기본으로 한다.
|
||||
- 전체 Markdown snapshot 비교보다 부분 품질 검증을 우선한다.
|
||||
- 샘플 PDF별로 heading 추출, 수식 block 수, 이미지 파일 생성, 표 구조 보존, index 링크 생성, 예외 발생 여부를 확인한다.
|
||||
- Windows native, 한글 경로, 긴 파일명, 8GB VRAM 제한을 검증 항목에 포함한다.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# PRD: PDFtoMD
|
||||
|
||||
## 목표
|
||||
PDFtoMD는 수학, 공학, 역학 중심의 PDF 문서를 AI Agent가 쉽게 접근하고 읽을 수 있는 Obsidian 친화 Markdown 문서 묶음으로 변환하는 프로그램이다.
|
||||
|
||||
이 프로젝트의 목표는 PDF의 텍스트를 단순 추출하는 것이 아니라, 원문 문서의 논리 구조와 출처 추적성을 보존하면서 AI가 탐색 가능한 지식 자료로 재구성하는 것이다.
|
||||
|
||||
## 사용자
|
||||
- PDF 문서를 Markdown으로 변환해 AI Agent, RAG, 개인 지식 관리 도구에 활용하고 싶은 사용자
|
||||
- 수식, 표, 이미지가 많은 논문/공학 문서를 Obsidian에서 읽고 관리하고 싶은 사용자
|
||||
- 긴 PDF를 여러 Markdown 파일과 index로 나누어 부분 탐색하고 싶은 사용자
|
||||
|
||||
## 1차 MVP 범위
|
||||
- Windows native 환경에서 완전 로컬 실행
|
||||
- GPU 기본 사용, VRAM 8GB 환경을 기준으로 안정적인 chunk 처리
|
||||
- PDF parser는 `Marker`를 기본 엔진으로 사용
|
||||
- PDF 텍스트를 Markdown 문단과 heading 구조로 변환
|
||||
- PDF 내 수식을 Obsidian 렌더링이 가능한 LaTeX Markdown으로 변환
|
||||
- PDF 내 이미지를 추출하고 Markdown에서 연결
|
||||
- 이미지의 figure 번호, 캡션, 원본 페이지 정보를 가능한 한 보존
|
||||
- PDF 내 표를 구조화하고 Markdown table, HTML table, CSV 중 적절한 방식으로 출력
|
||||
- 페이지 수가 많은 문서를 20페이지 단위 chunk로 분할 변환
|
||||
- AI Agent가 쉽게 접근할 수 있도록 chunk 파일, heading, 문단, line 위치, 원본 page 정보를 연결하는 index 파일 생성
|
||||
- 변환 설정, 경고, 실패 chunk, 처리 시간 등 진단 정보를 metadata/log로 보존
|
||||
|
||||
## 2차 범위
|
||||
- PyQt 기반 Windows UI
|
||||
- 외부 API를 선택적으로 활용하는 수식 복구, 이미지 설명, 품질 검토 기능
|
||||
- Marker 외 parser engine fallback 또는 비교 실행
|
||||
- 고급 Obsidian vault 연동
|
||||
|
||||
## 핵심 기능
|
||||
1. PDF 문서를 Obsidian 친화 Markdown 문서 묶음으로 변환
|
||||
2. 수식을 `$ ... $` 또는 `$$ ... $$` 형식의 LaTeX로 보존
|
||||
3. 논문에서 자주 쓰이는 다중 컬럼 문서를 Markdown의 선형 구조로 재배치
|
||||
4. 이미지 추출 및 Markdown 연결
|
||||
5. 표 구조화 및 표 유형별 Markdown/HTML/CSV 출력
|
||||
6. 긴 PDF를 여러 chunk Markdown 파일로 분할 변환
|
||||
7. chunk/heading/문단별 접근을 돕는 index 파일 생성
|
||||
8. 원본 PDF page, page range, block id, bbox 등 출처 추적 정보 보존
|
||||
9. 중간 실패 후 실패 chunk만 재시도 가능한 변환 상태 관리
|
||||
10. `samples/` PDF 기반 품질 검증과 회귀 테스트 지원
|
||||
|
||||
## 품질 기준
|
||||
- 원문 읽기 순서가 Markdown에서 자연스럽게 유지되어야 한다.
|
||||
- heading, 본문, 리스트, 인용, 표, 그림, 수식의 의미 역할이 구분되어야 한다.
|
||||
- 수식 delimiter와 기본 LaTeX 구조가 깨지지 않아야 한다.
|
||||
- 이미지와 캡션, figure 번호, 본문 참조가 가능한 한 연결되어야 한다.
|
||||
- 표는 구조 손실을 최소화하는 형식으로 저장되어야 한다.
|
||||
- index 파일에서 원하는 섹션이나 문단의 Markdown 위치와 원본 PDF page를 찾을 수 있어야 한다.
|
||||
- Windows 경로, 한글 파일명, 긴 문서, GPU 메모리 부족 상황을 고려해야 한다.
|
||||
|
||||
## UI
|
||||
- UI는 2차 목표로 PyQt를 사용한다.
|
||||
- UI는 변환 엔진을 직접 구현하지 않고 CLI/라이브러리 계층을 호출하는 thin client로 둔다.
|
||||
- 미니멀하고 깔끔한 Windows 표준 디자인을 따른다.
|
||||
Reference in New Issue
Block a user