같은 뜻인데 표기만 다른 입력을 하나의 표준 형태로 맞추는 전처리입니다. 두 층위가 있습니다. 텍스트 정규화는 유니코드·대소문자·전각 문자·공백을 통일해 매칭 누락을 막고, 값 정규화는 "지난달", "이천원" 같은 표현을 2026-08-01~2026-08-31, 2000 같은 필터에 쓸 수 있는 값으로 바꿉니다.
ABC123), 호환 문자(㈜, ㎏)가 섞인 문서| 규칙 | 예 | 비고 |
|---|---|---|
| 유니코드 정규형 NFC/NFKC | NFD "ㅎ+ㅏ+ㄴ…" → 완성형 "한글", ABC123 → ABC123, ㈜ → (주) | NFKC는 호환 문자까지 풀어서 더 공격적. 수식·특수기호가 의미를 가지면 NFC만 |
| 대소문자 통일 | iPhone, IPHONE → iphone | 코드·식별자에서 대소문자가 의미를 가지면 제외 |
| 공백·구두점 | 연속 공백 → 한 칸, 앞뒤 공백 제거 | "갤럭시S25"와 "갤럭시 S25"는 사전·n-gram으로 따로 처리 |
| 어간 추출 / 표제어 | running → run (stemming), better → good (lemmatization) | 한국어는 형태소 분석기가 이 역할 → 불용어 제거 |
| 유형 | 입력 | 정규화 결과 (기준일 2026-09-15) |
|---|---|---|
| 상대 기간 | 지난달 주문 내역 | order_date ∈ [2026-08-01, 2026-08-31] + "주문 내역" |
| 상대 기간 | 지난주 반품 신청 | [2026-09-07, 2026-09-13] (월~일 기준) + "반품 신청" |
| 상대 기간 | 작년 7월 이후 주문 | [2025-07-01, 2026-09-15] + "주문" |
| 모호한 기간 | 삼개월 전 결제 내역 | "최근 3개월" [2026-06-15, 2026-09-15]로 해석 (정책으로 확정 필요) |
| 한글 수사 금액 | 이천원 이하 상품 | price ≤ 2000 + "상품" |
| 한글 수사 금액 | 십오만원 이상 | price ≥ 150000 |
| 호환 문자 | 120ML ABC㈜ 제품 | 120ml abc(주) 제품 |
규칙 기반은 빠르고 결정적이지만 표현을 다 커버하지 못합니다. 실무에서는 흔한 패턴을 규칙으로 처리하고, 나머지는 기준일을 주입한 LLM 구조화 출력으로 넘기는 조합이 일반적입니다.
| 장점 | 단점 |
|---|---|
| 표기 차이로 인한 조용한 누락을 막음 (특히 NFD 한글) | NFKC·소문자화가 의미 있는 구분을 지울 수 있음 (코드, 화학식, 단위) |
| 날짜·금액을 필터로 넘겨 벡터 검색이 못 하는 정확한 범위 조건 처리 | 상대 표현의 해석이 모호함 ("3개월 전" = 그 시점? 최근 3개월?) |
| 텍스트 정규화는 싸고 결정적 | 규칙은 커버리지가 낮고, LLM은 비결정적 |
| 캐시 키·중복 제거 정확도 향상 | 인덱스 쪽 규칙을 바꾸면 재인덱싱 필요 |
⚠️ 함정 — 기준일 누락: LLM에 오늘 날짜를 주지 않으면 학습 시점 기준으로 "작년"을 계산합니다. 실제로 LLM으로 생성한 예시 데이터에서 "작년 7월"은 2024년, "작년 1월"은 2025년으로 같은 데이터셋 안에서 기준 연도가 섞이는 일이 있었습니다. 기준일·타임존은 항상 코드에서 주입하세요.
⚠️ 함정 — 요일 계산: "지난주 월요일"을 "오늘 − 7일"로 계산하면 틀립니다. 주의 시작 요일(월/일)을 정하고 달력으로 계산하세요. LLM은 요일 계산을 자주 틀리므로 날짜 산술은 코드로 합니다.
⚠️ 함정 — 인덱스와 쿼리의 비대칭: 쿼리만 NFC로 바꾸고 인덱스에 NFD 텍스트가 남아 있으면 여전히 매칭되지 않습니다. 정규화는 적재 파이프라인에도 똑같이 넣으세요.
⚠️ 함정 — 값이 문서에 없음: "지난달"을 날짜 필터로 바꿔도 문서에
date메타데이터가 없으면 결과가 0건이 됩니다. 필터를 쓰기 전에 해당 필드가 청크에 채워져 있는지 확인하세요. → 제로 리콜 트러블슈팅