perf(scoring): optimized retrieval engine with caching and IDF smoothing v2.69.0

This commit is contained in:
g1nation
2026-05-05 10:50:20 +09:00
parent 55c8e1c9dd
commit 8d6e510b00
3 changed files with 44 additions and 6 deletions
+41 -3
View File
@@ -28,16 +28,43 @@ const STOP_WORDS_KO = new Set([
'대한', '대해', '통해', '따라', '위해', '대로', '만큼'
]);
// ─── Internal Cache for Tokenization ───
const TOKEN_CACHE = new Map<string, string[]>();
const MAX_CACHE_SIZE = 1000;
/**
* 한국어/영어 혼합 텍스트를 토큰으로 분리합니다.
* 한국어/영어 혼합 텍스트를 정규화하고 토큰으로 분리합니다.
* (Performance Optimization: 내부 캐시 적용)
*/
export function tokenize(text: string): string[] {
return text
if (!text) return [];
// 캐시 확인
if (TOKEN_CACHE.has(text)) {
return TOKEN_CACHE.get(text)!;
}
// 1. Pre-normalization: 특수문자 정제 및 표준화
const normalized = text
.toLowerCase()
.replace(/[\u200B-\u200D\uFEFF]/g, '') // Zero-width spaces 제거
.replace(/[^\w\s가-힣_.-]/g, ' ') // 허용된 문자 외에는 공백 처리
.trim();
// 2. Tokenization: 정제된 텍스트 분리
const tokens = normalized
.split(/[^a-z0-9가-힣_.-]+/g)
.map((t) => t.trim())
.filter((t) => t.length >= 2)
.filter((t) => !STOP_WORDS_EN.has(t) && !STOP_WORDS_KO.has(t));
// 캐시 저장 (메모리 관리: 임계값 초과 시 비우기)
if (TOKEN_CACHE.size >= MAX_CACHE_SIZE) {
TOKEN_CACHE.clear();
}
TOKEN_CACHE.set(text, tokens);
return tokens;
}
/**
@@ -90,13 +117,24 @@ function termFrequency(term: string, documentTokens: string[]): number {
/**
* IDF (Inverse Document Frequency): 전체 문서 대비 희소도
* (Stability Enhancement: Smoothing 적용 및 최소 문서 수 대응)
*/
function inverseDocumentFrequency(
term: string,
allDocumentTokenSets: Array<Set<string>>
): number {
const N = allDocumentTokenSets.length;
if (N === 0) return 1.0;
const containing = allDocumentTokenSets.filter((doc) => doc.has(term)).length;
return Math.log((allDocumentTokenSets.length + 1) / (containing + 1)) + 1;
// N이 매우 작을 때(예: 5개 이하) 스코어 편향 방지를 위한 최소 분모 보정
const smoothN = N < 5 ? N + 5 : N;
const smoothContaining = containing;
// Standard Smooth IDF: log((N+1) / (containing+1)) + 1
// containing이 0일 경우에도 안전하게 동작하도록 설계
return Math.log((smoothN + 1) / (smoothContaining + 1)) + 1;
}
export interface ScoredDocument {