Repository navigation
RAG Pipeline
dev
a9974a0(2026-10-12) 기준
리포트가 근거로 쓰는 약관·판례·분쟁조정사례·장해분류표가 어떻게 수집되고, 청크가 되고, 검색되는지를 정리한다. 수집(Notion → S3)은 이 레포의 corpus_worker가, 구조 복원·청킹·임베딩은 오프라인 파이프라인(Policy-Chunker, 별도 저장소)이, 검색은 이 레포의 공용 모듈 src/rag가 맡는다.

약관 PDF가 검색 가능한 청크가 되기까지. 앞 다섯 칸은 오프라인, 마지막 칸(검색)은 리포트·챗봇이 함수로 호출한다

팀이 Notion에 쌓는 약관 PDF를 PG에 미러링하고, 우선순위대로 S3에 sha256 키로 올린다
| 결정 | 이유 |
|---|---|
| 큐 이벤트가 아니라 폴링 데몬 | Notion은 변경을 푸시하지 않고, 우선순위는 시간이 지나며 계속 바뀐다 |
| sync(증분 하루 1회 + 6일 주기 전수)와 drain을 한 프로세스의 독립 루프로 | 삭제 반영은 비싼 전수에서만. 편집 중 첨부가 잠깐 비는 문서를 지우지 않도록 "페이지 존재(observed)"와 "첨부까지 존재(seen)"를 구분 |
FOR UPDATE SKIP LOCKED claim + 지수 백오프 + 좀비 회수 |
여러 drain 태스크가 같은 문서를 잡지 않게, 레이트리밋 같은 일시 오류에서 시도 예산을 태우지 않게 |
내용주소 키 + HeadObject dedup, DB 문서 키는 비유니크 |
여러 약관이 같은 첨부(공통 특약·별표)를 공유한다 |
| PDF 전용 | → 코퍼스 스테이징 기록 |
| 우선순위 = 카테고리 지배항 + 출처(P0~P3) + 수요도 + 긴급도(지수 반감기) | 선형 감쇠는 오래된 문서에서 음수가 나와 점수가 꼬였다 |

약관 청킹 흐름. 텍스트로 바꾸면 사라지는 제목/본문 구분을 조판 정보로 되살리는 것이 핵심이다
- 구조 복원: 폰트 크기·굵기·배치·문단 간격을 점수화해 특약·조문 경계를 찾고, 목차·본문 패턴으로 잘못된 후보를 보정한다. 폰트 크기 하나만 보면 목차나 상품 설명까지 특약 제목으로 잡는다. 보험사마다 조판이 달라, 문서의 목차를 기준으로 복원 결과를 자동 평가한다.
- 청킹: 특약 → 조 → 항 → 호 계층. 기본은 조 하나가 청크 하나이고, 면책 조항처럼 한 조에 조건이 길게 나열되면 항·호로 더 나눈다. 긴 조문 때문에 특정 조건의 검색 신호가 묻히는 것을 막는다.
- 조항 분류: 복원된 조항을 9종(보장·면책·정의·특별조항·의무·청구·해지·별표·일반)으로 분류한다. 정규식 규칙이 먼저 판정하고, 실패한 조항만 LLM으로, 그것도 실패하면 키워드 규칙으로 복원한다.

조항 분류 모델 비교(주요 보험사 4곳 × 약관 3개, 60건). 전체 정확도는 qwen3.6:35b-a3b가 0.933으로 가장 높아 채택했다. 모델끼리 의견이 갈린 40건에서는 qwen3.6과 gemma4가 같았다(0.775)
| 판례·분쟁조정사례 | 임베딩·적재 |
|---|---|
![]() |
![]() |
| 사건 단위로 5블록 분할하고 사건번호·법원·선고일·쟁점을 태깅한다. 리포트 워커가 판례를 사건 단위로 dedup하고 표시 정보를 복원할 때 이 태그를 쓴다 | 1024차원 임베딩(qwen3:embedding, 폴백 BGE-M3)을 halfvec(1024)로 저장한다 |

보험사별 구조 복원 점수(목차 항목 대비 검출). 조판이 특이한 보험사에서 0.607 → 0.866으로 가장 크게 올랐다. 평가 배치 전체 평균은 0.528 → 0.651, 약관 100개에서 청크 111,791개, 최대 1,250페이지 약관까지 처리했다(팀 중간보고서)

하이브리드 검색. "상해후유장해"·"외모추상"처럼 정확히 일치해야 하는 용어와 표현이 달라도 뜻이 같은 질의가 공존해 두 검색을 함께 쓴다
| 결정 | 이유 | 상태 |
|---|---|---|
| 전용 벡터 DB(Pinecone·Qdrant) 대신 PostgreSQL + pgvector | 보험사·상품·세대·시행일·문서 종류가 복합 필터로 걸린다. 인제스트 중 버전이 섞이지 않게 트랜잭션이 필요하다. 필터·트랜잭션·벡터·키워드를 한 DB에서 | 채택 |
| HNSW (IVFFlat 아님) | 속도보다 정밀도. 잘못된 조항 하나가 리포트 전체를 틀리게 한다. 문서가 계속 추가돼도 재구성 부담이 적다 | 채택 |
halfvec(1024) (float32 vector 아님) |
인덱스 RAM이 절반. 1024차원에서 정확도 손실은 무시할 수준으로 판단 | 채택 |
| 키워드 검색: pg_search(BM25) + trigram | 설계 단계 선택. BM25 랭킹 | 대체됨 → 현재 코드는 Kiwi 형태소 토큰 + tsvector(GIN) + trigram 오타 보정. 전환 사유는 기록 없음 |
| Elasticsearch | 검색 기능은 가장 강력하지만 별도 서버와 동기화 파이프라인이 필요 | 폐기 |
오타 보정: search_terms(Kiwi 추출 용어 + trigram) |
사용자 입력을 바로 쓰지 않고 가장 가까운 용어로 보정한 뒤 검색. 갱신은 인제스트 중이 아니라 배치로(동시 인제스트 충돌 방지) | 채택 |
통합: RRF (k=60) |
두 순위를 점수 스케일 없이 합칠 수 있다 | 채택 |
| 리랭커 (bge-reranker-v2-m3) | 하이브리드로 좁힌 후보만 다시 평가해 처리량을 관리하면서 정확도를 올린다 | 오프라인 평가 파이프라인 채택. 이 레포의 src/rag에는 아직 없음 |

검색 방식별 재현율(현재 청크셋). RRF만으로는 임베딩 단독과 R@1이 같았지만, 리랭커를 붙이자 R@1 0.553 → 0.723, R@5 0.809 → 0.894로 올랐다

사용자 약관이 코퍼스에 없을 때의 경로. 증권에서 보험사·상품명·가입 시점을 뽑아 공시실에서 가입 당시 판본을 찾는다. 판매 중지된 과거 약관도 대상이다
- 보험사마다 공시실 구조가 달라 보험사별 어댑터를 둔다(5개사).
- 받은 약관은 PDF 형식·페이지 수·조문 존재·SHA-256을 검증한 뒤에만 청킹으로 넘긴다.
- 리포트 워커의 "약관 미보유 → 약관 파싱" 분기는 아직 빈 자리다(리포트 워커 구성).
팀 설계 문서 "청크 파이프라인 최적화" 기준. 그 시점 규모는 약관 20개·청크 21,791개, 문서당 처리 시간 p50 251.9초·p95 378.9초(n=11)였다.
| 항목 | 유형 | 상태 |
|---|---|---|
| 런타임 관측이 없어 사이클 실패를 알 방법이 없음. 감지까지 최대 7일(사이클 주기) | 탐지 | 사이클 결과 DB 기록 + 알림 + 헬스체크 제안 |
| 청크가 0개인 문서가 매 주기 무한 재처리 | 개선 | 미착수 |
| 임베딩 배치를 for 문으로 순차 호출해 Ollama 대기가 그대로 누적 | 개선 | 미착수 |
| SLO 후보: 인덱스 신선도(마지막 성공 적재 경과 시간), 멱등 스킵률, 처리량 | 탐지 | 미착수 |
- 팀 설계 문서 "임베딩 전략", "corpus_worker 파이프라인 설계 문서", "보험 약관 청킹 파이프라인 설계 문서", 팀 중간보고서
-
src/rag/,src/corpus_worker/,migrations/corpus/001_policy_chunks.sql,docs/architecture/rag.md
구성과 설계 근거
OCR 인식 · VLM 보완
개인정보 보호 · 마스킹
리포트 정확도
배포 · 데이터 파이프라인
레퍼런스

