PyPDFLoader, Unstructured, 커스텀 정규식 — 인도 정부 PDF로 다 해봤습니다. 병렬 처리(Fan-Out)로 유일하게 성공한 방법 공유합니다.
PyPDFLoader, Unstructured, custom regex — I tried everything on Indian Government PDFs. Here's the only thing that actually worked with Parallel Firing ( Fan-Out ).
핵심 요약
복잡한 인도 정부 PDF의 RAG 성능을 높이기 위해 병렬 쿼리 처리와 LlamaParse, 계층적 청킹을 도입한 아키텍처 공유.
- 병렬 Fan-out — 3개의 쿼리 재구성으로 검색 정확도 향상
- LlamaParse 활용 — 시각적 레이아웃 이해로 복잡한 표 추출
- 계층적 청킹 — 부모-자식 관계를 통한 문맥 유지
- 8노드 파이프라인 — 분류, 의도 파악, 환각 방지 가드레일 적용
6개월 전만 해도 나도 너희가 하는 똑같은 고민을 했었지.
"병합된 셀은 어떻게 처리하지?", "왜 표 추출이 자꾸 깨지는 거야?", "왜 RAG가 금융 수치에서 환각을 일으키지?"
내가 다루던 PDF들은 진짜 지옥이었어. RBI KYC 마스터 지침, 재무 법안, EPF 법, 인도 헌법 같은 것들이었거든. 5단계로 병합된 셀 표에, 한 셀 안에 힌디어랑 영어가 섞여 있고, 취소선 긋고, 개정안끼리 서로 참조까지 걸려 있었으니까.
그때 글이 너무 길어져서 미처 설명하지 못한 핵심 두 가지가 있어.
- 병렬 팬아웃(Parallel Fan-out) 방식
사용자 질문 하나를 던진다고 검색 요청도 하나만 나가는 게 아니야. 동시에 3개를 쏴버리지. 질문을 다르게 재구성해서 Pinecone에 병렬로 때려 넣는 거야. 3개 결과에서 나온 최상위 청크들을 합친 다음에 리랭킹(reranking)을 돌려. 이것만으로도 "질문은 제대로 했는데 답을 못 찾네" 하는 문제는 거의 다 해결됐어.
- 검색과 생성 사이의 Cohere Reranker
3개의 병렬 검색 결과가 합쳐지면, LLM한테 넘기기 전에 Cohere의 신경망 리랭커를 한 번 거치게 해. 벡터 유사도 점수랑 실제 의미론적 관련성 점수는 완전히 다른 거거든. 특히 빽빽한 법률 문서에서는 더더욱 그렇지. 코사인 유사도가 놓치는 걸 리랭커가 다 잡아내.
내가 시도했다가 망한 것들:
PyPDFLoader → 표가 그냥 엉망진창 텍스트 덩어리로 변함. 열 정렬은 아예 박살 남.
Unstructured → 좀 낫긴 한데, 정부 문서의 병합 셀 앞에서는 여전히 무력함.
Custom Regex → 3개 포맷에 맞춰 패턴을 짰는데, 20개 넘게 필요하다는 걸 깨달음. 유지보수 불가능.
Direct LLM on full PDF → 컨텍스트 윈도우 환각 작렬 + 눈물 나는 비용.
진짜 효과 본 거 — 딱 3가지:
- PyMuPDF로 사전 필터링
재무 법안이 200페이지가 넘는데, 실제로 개정안이 있는 건 80페이지 정도거든. 비싼 파서에 넣기 전에 미리 걸러내야 해. 이걸로 임베딩 비용 바로 60% 아꼈어.
- VLM 모드를 쓴 LlamaParse
이게 진짜 치트키야. 텍스트를 추출하는 게 아니라, 페이지마다 스크린샷을 찍어서 레이아웃을 시각적으로 이해해. 병합 셀, 중첩 헤더, 각주까지 다 잡아내지. 깔끔한 마크다운으로 뽑아주니까 휴리스틱이나 꼼수 쓸 필요가 없어.
- 2단계 청킹 + 부모-자식(Parent-Child) 아키텍처
먼저 MarkdownHeaderTextSplitter로 섹션 계층 구조를 살리고, 그다음 RecursiveCharacterTextSplitter로 최적의 청크 사이즈를 만들어.
이렇게 하면 검색에 필수적인 부모-자식 관계가 형성돼. 검색은 자식 청크 단위로 해서 정확도를 높이고, 자식 청크가 매칭되면 시스템이 그 부모 청크 전체를 LLM에 던져주는 방식이야. 덕분에 LLM이 문맥을 제대로 파악해서 답을 내놓지.
모든 자식 청크는 자기가 어떤 부모 섹션에 속해 있는지, 출처 문서가 뭔지, 계층 구조상 어디에 있는지 다 알고 있어. 그래서 개정안 참조 같은 거 할 때도 헛소리를 안 하는 거야. LLM은 파편화된 조각이 아니라 섹션 전체를 보니까.

