-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture
flowchart LR
PDF([PDF]) --> Docling[Docling]
Docling --> Parser[Parser]
Parser --> Chunker["EvidenceChunker"]
Chunker --> Split[Split]
Split --> Export[Export]
Export --> RAG([RAG])
%% GitHub 호환용 Nord Deep 스타일 지정
style PDF fill:#EBCB8B,stroke:#D08770,stroke-width:1.5px,color:#2E3440
style Docling fill:#E5E9F0,stroke:#8FBCBB,stroke-width:1.5px,color:#3B4252
style Parser fill:#D8DEE9,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style Chunker fill:#88C0D0,stroke:#5E81AC,stroke-width:2.5px,color:#2E3440
style Split fill:#A3BE8C,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style Export fill:#B48EAD,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style RAG fill:#EBCB8B,stroke:#D08770,stroke-width:1.5px,color:#2E3440
| 단계 | 실제 코드 | 역할 |
|---|---|---|
| Docling | DocumentConverter |
PDF 파싱 (do_table_structure=True) |
| Parser | parser.docling.DoclingParser.from_doc() |
Docling API에 의존하는 유일한 지점, 내부 모델(ParsedDoc)로 변환 |
| EvidenceChunker | chunker.build_evidence_units() |
표마다 캡션 매핑 → bbox 정규화 → 인접 문단 부착 → 문장 변환 (5단계, 모듈별 세부 설명 참고) |
| Split | split.split_oversized_units() |
512토큰 초과 EU를 행 단위로 분할 |
| Export |
export.langchain / export.llamaindex
|
LangChain Document / LlamaIndex Node로 변환, max-pool dedupe |
build_evidence_units()가 표 하나마다 거치는 5단계.
flowchart TD
%%{init: { 'theme': 'base', 'themeVariables': { 'lineColor': '#4C566A', 'fontFamily': 'Inter, sans-serif' }}}%%
Parser[Parser] --> EU
subgraph EU["EvidenceChunker.build_evidence_units()"]
direction TB
S1["1. filters<br/>중복 표 & 목차 오인식 제외"]
S2["2. caption.map_table_caption()<br/>캡션 ↔ 표 1:1 매핑"]
S3["3. geometry.normalize_bbox()<br/>좌표 정규화 (BOTTOMLEFT)"]
S4["4. context.attach_context_paragraphs()<br/>인접 설명 단락 부착"]
S5["5. flatten<br/>표 → 자연어 문장 변환"]
S1 --> S2 --> S3 --> S4 --> S5
end
EU -->|"List[EvidenceUnit]"| Split["split.split_oversized_units()<br/>512토큰 초과 EU 행 단위 분할"]
Split -->|"List[EvidenceUnit]"| Export["export.langchain / llamaindex<br/>max-pool dedupe"]
Export --> RAG([RAG 파이프라인])
style Parser fill:#D8DEE9,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style EU fill:#88C0D0,stroke:#5E81AC,stroke-width:2.5px,color:#2E3440
style S1 fill:#E5E9F0,stroke:#8FBCBB,stroke-width:1px,color:#3B4252
style S2 fill:#ECEFF4,stroke:#8FBCBB,stroke-width:1px,color:#3B4252
style S3 fill:#E5E9F0,stroke:#8FBCBB,stroke-width:1px,color:#3B4252
style S4 fill:#E1E8EB,stroke:#81A1C1,stroke-width:1px,color:#3B4252
style S5 fill:#D0E1E7,stroke:#81A1C1,stroke-width:1px,color:#3B4252
style Split fill:#A3BE8C,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style Export fill:#B48EAD,stroke:#4C566A,stroke-width:1.5px,color:#2E3440
style RAG fill:#EBCB8B,stroke:#D08770,stroke-width:1.5px,color:#2E3440
EvidenceChunker.build_corpus() 실행 시 위 파이프라인 결과(EU)에 Docling HybridChunker가 만든 표가 아닌 일반 본문 청크를 합해 하나의 검색 코퍼스로 반환한다.
| 모듈 | 역할 |
|---|---|
parser/base.py |
Docling에 종속되지 않는 내부 문서 모델(ParsedDoc, TextBlock, TableBlock) 정의. 좌표계를 BOTTOMLEFT → TOPLEFT로 정규화, 텍스트 참조를 정수 인덱스로 치환 |
parser/docling.py |
DoclingDocument → ParsedDoc 변환. Docling API에 직접 의존하는 유일한 지점 |
caption.py |
캡션↔표 1:1 매핑. direct(RefItem) → bbox(200pt) → 인접 페이지 → 병합 헤더 4단계 fallback |
context.py |
표 주변 인접 설명 단락 탐지, 병합. bbox(300pt) 거리 기반 수집 + (옵션) 코사인 유사도 필터 |
flatten.py |
표 셀 데이터 → "행헤더 | 열헤더: 값" 자연어 문장 변환, 표 요약(table_abstract) 생성 |
filters.py |
중복 인식된 표, 목차/표 목록이 표로 오인식된 경우 감지 |
geometry.py |
EvidenceUnit.bbox의 0~1 정규화 BOTTOMLEFT 유지를 위한 마지막 좌표 변환 |
split.py |
512토큰 초과 EU를 표 행 단위로 분할(캡션, 문맥은 모든 조각에 복제) |
tokens.py |
tiktoken 기반 토큰 카운트 (지연 로딩) |
unit.py |
EvidenceUnit 데이터클래스. text/retrieval_text/retrieval_units 세 가지 텍스트 뷰 제공 |
chunker.py |
EvidenceChunker 메인 클래스. |
export/langchain.py, export/llamaindex.py
|
RetrievalChunk → LangChain/LlamaIndex 변환, max-pool dedupe(EvidenceRetriever) |
caption.py, context.py, filters.py, flatten.py는 parser.base.ParsedDoc만 바라보고 DoclingDocument를 직접 다루지 않는다. 즉, Docling API가 바뀌어도 parser/docling.py의 변환 로직만 손보면 되도록 격리한다. 이 경계는 tests/test_no_docling_dependency.py, tests/test_parser_injection.py로 검증된다.
파서 경계를 지난 이후 내부 계산은 전부 TOPLEFT(위쪽 = y값 작음)로 통일한다. EvidenceUnit.bbox만 공개 API 계약상 0~1 정규화된 BOTTOMLEFT를 유지한다(geometry.normalize_bbox()).
자세한 이유는 API Reference 참고.
-
text: LLM 컨텍스트용 전체 텍스트,table_html포함 -
retrieval_text: 임베딩/검색 전용,table_html제외 (토큰 예산 절약, 검색 노이즈 감소) -
retrieval_units: 행 단위 다중 벡터(small-to-big) 패턴용 검색 단위 목록
EvidenceUnit.chunk_id는 eu_id이고, 행 분할된 조각은 f"{eu_id}-s{n}" 형태로 서로 다른 chunk_id를 가진다. max-pool dedupe(dedupe_by_chunk_id())은 이 chunk_id 단위로만 그룹핑하므로, 같은 원본 표에서 나온 분할 조각들 사이의 중복은 걸러내지 못한다. 자세한 내용은 Limitations & Roadmap에 정리되어 있다.