신규 파일: - classifier_service.py: exaone binary classifier (sufficient/insufficient) parallel with evidence, circuit breaker, timeout 5s - refusal_gate.py: multi-signal fusion (score + classifier) AND 조건, conservative fallback 3-tier (classifier 부재 시) - grounding_check.py: strong/weak flag 분리 strong: fabricated_number + intent_misalignment(important keywords) weak: uncited_claim + low_overlap + intent_misalignment(generic) re-gate: 2+ strong → refuse, 1 strong → partial - sentence_splitter.py: regex 기반 (Phase 3.5b KSS 업그레이드) - classifier.txt: exaone Y+ prompt (calibration examples 포함) - search_synthesis_partial.txt: partial answer 전용 프롬프트 - 102_ask_events.sql: /ask 관측 테이블 (completeness 3-분리 지표) - queries.yaml: Phase 3.5 smoke test 평가셋 10개 수정 파일: - search.py /ask: classifier parallel + refusal gate + grounding re-gate + defense_layers 로깅 + AskResponse completeness/aspects/confirmed_items - config.yaml: classifier model 섹션 (exaone3.5:7.8b GPU Ollama) - config.py: classifier optional 파싱 - AskAnswer.svelte: 4분기 렌더 (full/partial/insufficient/loading) - ask.ts: Completeness + ConfirmedItem 타입 P1 실측: exaone ternary 불안정 → binary gate 축소. partial은 grounding이 담당. 토론 9라운드 확정. plan: quiet-meandering-nova.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
34 lines
974 B
Python
34 lines
974 B
Python
"""문장 분할 (Phase 3.5a — regex 기반).
|
|
|
|
Phase 3.5b 에서 KSS 라이브러리 기반으로 업그레이드 예정.
|
|
"""
|
|
|
|
import re
|
|
|
|
MIN_SENTENCE_CHARS = 15
|
|
|
|
|
|
def split_sentences(text: str) -> list[str]:
|
|
"""한국어/영어 혼합 텍스트를 문장 단위로 분할.
|
|
|
|
규칙:
|
|
- 마침표/느낌표/물음표 + 공백/줄바꿈
|
|
- 한국어 종결 어미 (다. 함. 음. 됨.) 패턴
|
|
- MIN_SENTENCE_CHARS 미만은 이전 문장에 병합
|
|
"""
|
|
# 1차 분할: punctuation + whitespace
|
|
raw = re.split(r'(?<=[.!?。])\s+|(?<=[다됨음함]\.)\s+|\n{2,}', text)
|
|
|
|
# 2차: 너무 짧은 것 병합
|
|
merged: list[str] = []
|
|
for part in raw:
|
|
part = part.strip()
|
|
if not part:
|
|
continue
|
|
if merged and len(part) < MIN_SENTENCE_CHARS:
|
|
merged[-1] = merged[-1] + " " + part
|
|
else:
|
|
merged.append(part)
|
|
|
|
return merged if merged else [text.strip()] if text.strip() else []
|