If the corpus is a static asset that you build once and then finish, this section can be short. However, in environments that handle changing data — IT company service plans, knowledge bases, API documents, etc. — corpus quality management becomes the most important part of the RAG pipeline.
| category | explanation | example |
|---|---|---|
| Absence of information (Gap) | There is no topic in the corpus | The plan for new launch features has not yet been reflected. |
| Information Staleness | Information that was correct before but is now incorrect | Last year's API v1 document remains, but currently it is v2 |
| Information Redundancy | The same content is repeated in multiple sources | Planning documents v1, v2, and v3 with the same function all exist in the corpus. |
Among these, information corruption is the most dangerous. Gap은 "검색 결과 없음"으로 드러나지만, 부패한 정보는 "검색 결과가 있지만 틀린 것"이므로 LLM이 자신있게 틀린 답을 내놓는다.
When adding a new document, compare it to the existing corpus by cosine similarity:
# 신규 청크 vs 기존 청크 유사도 행렬
sim_matrix = new_embeddings @ existing_embeddings.T # (new × existing)
# 각 신규 청크의 최대 유사도가 threshold(0.92) 이상이면 중복
for i, row in enumerate(sim_matrix):
if row.max() >= 0.92:
dup_count += 1
Judgment criteria:
duplicate (do not add)partial (added after review)new (added immediately)As the corpus grows, identical content may overlap from different sources:
python scripts/dedup.py --self-dedup data/chunks_meta.jsonl --threshold 0.95
Excluding adjacent chunks (natural similarity due to overlap), only reports similarity greater than 0.95 between different source files.
In an IT company environment, documents are constantly updated. Doing a full rebuild every time is inefficient, so the Incremental Update strategy is needed:
Strategy 1: Timestamp-based incremental synchronization
[변경 감지]
최종 동기화 시각 이후 수정된 문서만 추출
(Confluence: lastModified, Git: git log --since, 파일시스템: mtime)
│
▼
[기존 청크 제거]
해당 소스의 기존 청크를 chunks_meta.jsonl에서 삭제
│
▼
[신규 청킹]
변경된 문서만 chunk.py로 재처리
│
▼
[증분 인덱싱]
신규 청크만 임베딩 생성 → 기존 embeddings.npy에 append
BM25 인덱스는 전체 재구축 (in-memory 방식이므로 빠름)
Strategy 2: Versioning-Based Replacement
If the document has versions (plan v1 → v2 → v3):
[신규 버전 감지]
v3 문서가 들어옴
│
▼
[이전 버전 청크 삭제]
source가 "기획서_v1", "기획서_v2"인 청크를 모두 제거
│
▼
[신규 버전 청킹 + 인덱싱]
v3만 코퍼스에 남김
This will structurally prevent the “information corruption” problem. Keep previous versions in the archive, but exclude them from search.
Strategy 3: Time-To-Live (TTL) based expiration
Set a TTL for documents that change frequently (weekly reports, sprint planning, etc.):
# 청크 메타데이터에 만료 시각 추가
{
"chunk_id": "sprint_plan_0001",
"source": "2026_Q1_sprint_plan.md",
"expires_at": "2026-04-01T00:00:00", # 분기 종료 시 만료
...
}
# 검색 시 만료된 청크 필터링
results = [r for r in results if r.get("expires_at", "9999") > now()]
How to automatically detect when information in the corpus is inconsistent with reality:
Method 1: Periodic gap search (used in this project)
Define a core set of queries and periodically retrieve them to track score changes:
쿼리: "결제 API v3 엔드포인트"
- 1주 전 score: 0.85 (정상)
- 현재 score: 0.45 (급락) → 문서가 삭제되었거나 변경됨 → 알림
Method 2: User Feedback Loop
When the RAG-based bot responds “Was this helpful?” Add button:
Method 3: Cross-validation
When there are multiple sources on the same topic and the answers contradict each other, flag:
소스 A: "결제 타임아웃은 30초"
소스 B: "결제 타임아웃은 60초"
→ 모순 감지 → 어느 것이 최신인지 확인 필요
Corpus inevitably deteriorates over time. New questions arise, existing information becomes outdated, and blank areas emerge. If this is managed manually by people, it is unsustainable. The cycle of “finding gaps → filling → verifying” needs to be automated so that RAG gets better over time.
┌───────────────────────────────────────────┐
│ │
▼ │
Step 1: 갭 분석 │
(핵심 쿼리 배치 검색, score < 0.75 = 갭) │
│ │
▼ │
Step 2: 자료 수집 │
(수집 에이전트 병렬, 온라인 검증 소스만) │
│ │
▼ │
Step 3: 출처 검증 + 중복 검사 │
(출처 없는 파일 삭제, dedup.py) │
│ │
▼ │
Step 4: 인덱스 리빌드 │
(chunk.py → embed.py) │
│ │
▼ │
Step 5: 검증 (동일 쿼리 재검색) │
│ │
▼ │
Step 6: 수렴 판단 │
모든 쿼리 score ≥ 0.75? ──No──────────────────────┘
│
Yes
▼
수렴 완료
Convergence Condition:
In the Saju project, the method is to “search and collect data online,” but in the IT company, the data sources are different:
| step | Saju Project | IT company application |
|---|---|---|
| Gap Detection | Batch search of 20 key queries | Automatically collect queries with score < 0.5 from user query logs |
| Data collection | online search agent | Confluence/Notion/Git sync scheduler |
| Source Verification | Check for presence of URL | Check document owner/last modified date |
| Duplicate Inspection | Cosine similarity 0.92 | Document ID-based duplicate prevention + cosine similarity |
| Rebuild | Full re-chunking + re-embedding | Incremental updates (changes only) |
| verification | Re-search same query | A/B testing (old index vs new index) |
Specific Scenario: Service Plan RAG
[매일 02:00 크론잡]
↓
Confluence API로 최근 24h 내 수정된 페이지 목록 조회
↓
수정된 페이지: 기존 청크 삭제 → 신규 청킹 → 증분 임베딩
삭제된 페이지: 기존 청크 삭제
신규 페이지: 청킹 → 임베딩 → 추가
↓
BM25 인덱스 재구축 (in-memory라 30초 이내)
↓
검증 쿼리 셋 실행 → 이전 점수 대비 급락 항목 알림
The key to gap analysis is to design a well-designed set of expected queries: “This query should yield good results”:
// 이 프로젝트의 갭 쿼리 (20개)
[
"오행 상생상극 생극제화",
"지장간 투출 통근 격국",
"십성 식신 상관 정관 편관",
"용신 억부 조후 통관",
"궁합 천간합 오행 배합",
...
]
In an IT environment:
// 서비스 Knowledge Base 갭 쿼리 예시
[
"결제 API 타임아웃 설정값",
"iOS 앱 최소 지원 버전",
"신규 가입 프로모션 할인율",
"장바구니 상품 최대 개수",
"개인정보 보유 기간 정책",
...
]
Set expected score threshold for each query and verify it periodically. When a query below the threshold occurs, it is a signal that the documents in that area are insufficient or have changed.
사용자: "1990-05-15 14:30 남자 사주 봐줘"
│
▼
┌─────────────────────────────────┐
│ saju-analysis (오케스트레이터) │
│ Phase 1: saju.py 실행 → JSON │
│ Phase 2: 5개 서브에이전트 병렬 │
│ Phase 3: 결과 통합 → .md 출력 │
└─────────┬───────────────────────┘
│ Agent() × 5 (parallel)
┌─────┼─────┬──────┬──────┐
▼ ▼ ▼ ▼ ▼
saju- saju- saju- saju- saju-
profile structure dynamics fortune timeline
│ │ │ │ │
│ 각 에이전트가 search.py로 RAG 검색
│ (주제당 2~3개 쿼리, 총 ~30회 이내)
│ │ │ │ │
└─────┴─────┴──────┴──────┘
│
각자 원문 인용 기반 분석 작성
│
▼
오케스트레이터가 통합·중복 제거
│
▼
output/YYYY-MM-DD_이름_일주_사주.md
Compatibility analysis (gunghap-analysis) has the same structure and executes five subagents (gunghap-mind-energy, gunghap-ilji, gunghap-destiny, gunghap-yongsin, gunghap-timeline) in parallel. Both the four-week analysis and the compatibility analysis follow the same RAG search protocol.
Each subagent follows the same rules:
Search command:
.venv/bin/python scripts/search.py \
"갑목(甲木) 인월(寅月) 조후용신" \
"궁통보감(窮通寶鑑) 갑목 봄" \
"甲木 寅月 用神" \
--top-k 3 --mode hybrid --json
Standards for using results:
score ≥ 0.6 → High reliability. Be sure to cite the original textscore 0.3~0.59 → Conditional inclusion (include if it is the only result for that topic)score < 0.3 → ExcludedPrinciple of original text preservation:
The subagent does not summarize the search results and delivers the original text as is. The orchestrator is in charge of interpretation. This separation ensures the accuracy of the quotation from the nuclear power plant.