Skip to content

Architecture

dnjsgkfka edited this page Aug 18, 2026 · 5 revisions

전체 파이프라인

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

Loading
단계 실제 코드 역할
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

EvidenceChunker 내부 흐름

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
Loading

EvidenceChunker.build_corpus() 실행 시 위 파이프라인 결과(EU)에 Docling HybridChunker가 만든 표가 아닌 일반 본문 청크를 합해 하나의 검색 코퍼스로 반환한다.

모듈별 세부 설명

모듈 역할
parser/base.py Docling에 종속되지 않는 내부 문서 모델(ParsedDoc, TextBlock, TableBlock) 정의. 좌표계를 BOTTOMLEFT → TOPLEFT로 정규화, 텍스트 참조를 정수 인덱스로 치환
parser/docling.py DoclingDocumentParsedDoc 변환. 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.pyparser.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()).

Evidence Unit의 세 가지 텍스트 뷰

자세한 이유는 API Reference 참고.

  • text: LLM 컨텍스트용 전체 텍스트, table_html 포함
  • retrieval_text: 임베딩/검색 전용, table_html 제외 (토큰 예산 절약, 검색 노이즈 감소)
  • retrieval_units: 행 단위 다중 벡터(small-to-big) 패턴용 검색 단위 목록

max-pool dedupe와 chunk_id 스코프

EvidenceUnit.chunk_ideu_id이고, 행 분할된 조각은 f"{eu_id}-s{n}" 형태로 서로 다른 chunk_id를 가진다. max-pool dedupe(dedupe_by_chunk_id())은 이 chunk_id 단위로만 그룹핑하므로, 같은 원본 표에서 나온 분할 조각들 사이의 중복은 걸러내지 못한다. 자세한 내용은 Limitations & Roadmap에 정리되어 있다.

Clone this wiki locally