Files
2nd/10_Wiki/Topic_Programming/Subsystems/TFIDF_이중언어_스코어링.md
T
Antigravity Agent e2c5471046 wiki: Topic_Blog 신규 문서 일괄 추가 + ASTRA 성장 자산 동기화
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:55:38 +09:00

7.1 KiB
Raw Blame History

id, title, category, status, verification_status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, created_at, updated_at, review_reason, merge_history, tags, raw_sources, applied_in, github_commit
id title category status verification_status canonical_id aliases duplicate_of source_trust_level confidence_score created_at updated_at review_reason merge_history tags raw_sources applied_in github_commit
tfidf-bilingual-scoring TF-IDF 이중언어 스코어링 AI_and_ML draft applied
TF-IDF
토크나이저
tokenizer
한국어 영어 토큰화
동의어 확장
검색 점수
stop words
섹션 청킹
A 0.92 2026-06-13 2026-06-13
tfidf
tokenizer
search
nlp
korean
astraai
AstraAI/src/retrieval/scoring.ts
AstraAI/src/retrieval/chunker.ts
AstraAI

TF-IDF 이중언어 스코어링

🎯 한 줄 통찰 (One-line insight)

임베딩 엔진 없이도 쓸 수 있는 가벼운 검색의 핵심은 "좋은 토크나이저 + TF-IDF 가중"이며, AstraAI 는 한국어/영어 혼합 토크나이저·불용어·동의어 확장·제목 가중·충돌 신호 를 더해 단순 includes() 매칭을 넘어선 점수를 낸다 [S1].

🧠 핵심 개념 (Core concepts)

  1. TF-IDF: 용어 빈도(TF, 문서 내 흔함) × 역문서빈도(IDF, 전체에서 희소함). 흔하면서 그 문서에만 자주 나오는 단어가 고득점 [S1].
  2. 이중언어 토큰화: 한글-영문 경계를 분리(성능optimization성능 optimization), 특수기호 보존(C++, C#, .net) [S1].
  3. 불용어(Stop words): 검색에 무의미한 단어(영/한 각각 집합)를 제거 [S1].
  4. 동의어 확장: 질의 토큰을 관련어로 확장(성능performance, optimization, 최적화) [S1].
  5. 제목 가중: 제목 일치는 본문보다 3배 가중(TITLE_MULTIPLIER: 3.0) [S1].
  6. 토큰 캐시: 같은 텍스트의 토큰화를 Map 으로 캐시(한도 초과 시 전체 clear) [S1].

🧩 추출된 패턴 (Extracted patterns)

  • 중앙 설정 객체: SCORING_CONFIG 에 불용어·동의어·임계값·가중치를 모아 한 곳에서 조정 [S1].
  • 경계 분리 정규식: replace(/([a-z0-9]+)([가-힣]+)/gi, '$1 $2') 로 언어 경계 분할, split(/[^a-z0-9가-힣+#.-]+/g) 로 특수기호(C++) 보존 [S1].
  • TF 계산 1회화: buildTermCounts 로 문서당 용어 빈도 맵을 한 번 만들고 질의 용어마다 재사용 — O(질의×문서) 재스캔 회피 [S1].
  • IDF smoothing: 문서 수가 적을 때도 안정적이도록 평활화 적용 [S1].
  • 충돌 신호 탐지: "반대/충돌/conflict/vs" 등 지표 단어 수로 conflictSeverity 산출 — 지식 충돌 가능 문서를 표시 [S1].
  • 순수 함수 분리: chunker/scoring 은 fs·네트워크 의존 없는 순수 함수라 단위 테스트·재현이 쉽다 [S2].

📖 세부 내용 (Details)

토크나이저 (가장 중요한 부품)

const normalized = text.toLowerCase()
    .replace(/[-]/g, '')      // zero-width 제거
    .replace(/[^\w\s가-힣_+#.-]/g, ' ');         // 의미 없는 기호 → 공백
const splitText = normalized
    .replace(/([a-z0-9]+)([가-힣]+)/gi, '$1 $2') // 영→한 경계 분리
    .replace(/([가-힣]+)([a-z0-9]+)/gi, '$1 $2');// 한→영 경계 분리
const tokens = splitText.split(/[^a-z0-9가-힣+#.-]+/g)  // C++, C#, .net 보존
    .map(t => t.trim().replace(/[.,]$/g, ''))
    .filter(t => /[가-힣]/.test(t) ? t.length >= 1 : t.length >= 2)  // 한글 1자+, 영문 2자+
    .filter(t => !STOP_EN.has(t) && !STOP_KO.has(t));

한국어는 한 글자도 의미를 가질 수 있어 1자 이상 허용, 영문은 2자 이상으로 노이즈를 줄인다 [S1].

동의어 확장

질의 [성능][성능, performance, optimization, 최적화, speed]. Set 으로 중복 제거 후 반환. brain 문서가 영어로, 질의가 한국어로 와도(또는 반대) 매칭되게 하는 양국어 다리 [S1].

섹션 청킹과의 결합

긴 문서를 통째 색인하면 5000자 다주제 문서가 흐릿한 한 단위가 되어 정밀도가 떨어진다. chunker.ts 가 헤딩(#~######) 경계로 섹션을 나누고, 짧은 섹션은 병합·긴 섹션은 문단 경계로 재분할한다. fenced code block(```) 안의 # 는 헤딩으로 보지 않는다. 헤딩 breadcrumb 을 보존해 청크가 문맥을 잃지 않게 한다 [S2].

⚖️ 모순 및 업데이트 (Contradictions & updates)

  • TF-IDF 의 한계: 어휘가 다르면(동의어 사전에 없는 환언) 못 잡는다. 그래서 임베딩 하이브리드로 보완한다 → RAG 검색 파이프라인.
  • 동의어 사전의 유지보수: 수작업 사전이라 도메인이 늘면 누락이 생긴다. 핵심 도메인 용어 위주로 관리하는 절충.
  • 형태소 분석 부재: 한국어 조사/어미를 정밀 분해하지 않는다(경량 우선). 정밀도가 더 필요하면 형태소 분석기 도입 여지.

🛠️ 적용 사례 (Applied in summary)

  • AstraAI/src/retrieval/scoring.ts — tokenize/expandQuery/TF-IDF/충돌 탐지 [S1].
  • AstraAI/src/retrieval/chunker.ts — splitIntoSections 섹션 청킹 [S2].

💻 코드 패턴 (Code patterns)

// 1) 중앙 설정 객체 — 가중치/임계값 한 곳에서 (src/retrieval/scoring.ts)
const SCORING_CONFIG = {
    STOP_WORDS_EN: new Set(['the','a','and',/* ... */]),
    STOP_WORDS_KO: new Set(['그리고','그런데',/* ... */]),
    SYNONYM_DATA: [['성능', ['performance','optimization','최적화','speed']], /* ... */],
    TITLE_MULTIPLIER: 3.0,
    GLOBAL_CACHE_LIMIT: 2000,
};

// 2) TF 계산 1회화 (src/retrieval/scoring.ts)
function buildTermCounts(tokens: string[]): Map<string, number> {
    const counts = new Map<string, number>();
    for (const t of tokens) counts.set(t, (counts.get(t) || 0) + 1);
    return counts;   // 질의 용어마다 재스캔 대신 이 맵을 조회
}

// 3) 동의어 확장 (src/retrieval/scoring.ts)
export function expandQuery(tokens: string[]): string[] {
    const expanded = new Set(tokens);
    for (const t of tokens) (synonymMap.get(t) ?? []).forEach(s => expanded.add(s));
    return Array.from(expanded);
}

// 4) 헤딩 경계 섹션 청킹 — fence 안의 # 무시 (src/retrieval/chunker.ts)
const fence = line.trimStart().startsWith('```'); if (fence) inFence = !inFence;
const m = !inFence ? line.match(HEADING_RE) : null;   // 코드블록 내 #는 헤딩 아님

검증 상태 및 신뢰도

  • 상태: draft
  • 검증 단계: applied
  • 출처 신뢰도: A
  • 신뢰 점수: 0.92
  • 중복 검사 결과: 신규 생성 (New discovery)

🔗 지식 그래프 (Knowledge Graph)

📚 출처 (Sources)

  • [S1] AstraAI/src/retrieval/scoring.ts — 토크나이저, TF-IDF, 동의어, 불용어, 충돌 탐지, 캐시
  • [S2] AstraAI/src/retrieval/chunker.ts — 섹션 청킹(순수 함수)

📝 변경 이력 (Change history)

  • 2026-06-13: AstraAI 코드 분석 기반 초안 생성.