HUTAMS_AUDIO/app/services/context_service.py

111 lines
4.5 KiB
Python

"""
유연한 지식(Context) 제공자 패턴 모듈.
LLM Worker가 추출한 키워드를 받아 관련 지식 DB(또는 Mock)에서
추가 컨텍스트 딕셔너리를 반환합니다. 향후 RAG 서버로 코드 수정 없이 확장할 수 있습니다.
"""
from abc import ABC, abstractmethod
import logging
from typing import List, Dict, Any
from app.core.config import settings
logger = logging.getLogger("uvicorn.error")
class ContextProvider(ABC):
"""지식 제공자 추상 클래스"""
@abstractmethod
def get_extended_context(self, keywords: List[str]) -> List[Dict[str, Any]]:
pass
class MockContextProvider(ContextProvider):
"""개발 및 데모를 위한 하드코딩된 Mock 지식 제공자"""
def __init__(self):
# 데모용 지식 딕셔너리
self._mock_db = {
"검측차": {
"title": "검측차 (Inspection Train)",
"desc": "선로, 전차선, 신호 등의 상태를 주행하며 실시간으로 점검하는 특수 목적 차량. 주로 야간 작업에 투입됨."
},
"다대포": {
"title": "다대포 해수욕장역 (Dadaepo Beach)",
"desc": "부산 도시철도 1호선의 종착역. 회차선 및 차량 유치선 존재."
},
"tcms": {
"title": "TCMS (열차종합제어장치)",
"desc": "Train Control and Monitoring System. 열차의 각종 주요 기기를 제어하고 실시간 상태를 감시, 진단하는 시스템."
},
"관제": {
"title": "관제 센터 (Control Center)",
"desc": "전체 철도 시스템의 운행 상황을 실시간 모니터링하고 통제, 지시를 내리는 종합 사령실."
}
}
def get_extended_context(self, keywords: List[str]) -> List[Dict[str, Any]]:
results = []
found_keys = set()
for kw in keywords:
kw_lower = kw.lower()
for key, val in self._mock_db.items():
if key in found_keys:
continue
# 키워드가 DB 키에 포함되거나, DB 키가 키워드에 포함된 경우 매칭
if key.lower() in kw_lower or kw_lower in key.lower():
results.append({
"keyword": key,
"title": val["title"],
"desc": val["desc"]
})
found_keys.add(key)
return results
class CSVContextProvider(ContextProvider):
"""
[Chapter 6.1]
대규모 도메인 사전(CSV) 기반의 지식 제공자. O(1) 검색 최적화 및
동음이의어(리스트 Value) 중복 처리를 허용합니다.
"""
def get_extended_context(self, keywords: List[str]) -> List[Dict[str, Any]]:
from app.core.dictionary import RAILWAY_TERMS_DICT
results = []
found_keys = set()
for kw in keywords:
kw_clean = kw.strip()
if not kw_clean: continue
entries = RAILWAY_TERMS_DICT.get(kw_clean)
if entries:
for entry in entries:
# 동음이의어 방지용 해시 (keyword+desc)
unique_hash = f"{kw_clean}_{entry['desc'][:20]}"
if unique_hash not in found_keys:
results.append({
"keyword": entry.get("category", "일반 용어"), # Frontend tag
"title": kw_clean, # Frontend subtitle
"desc": entry.get("desc", "") # Frontend text
})
found_keys.add(unique_hash)
return results
def get_context_service() -> ContextProvider:
"""설정된 CONTEXT_PROVIDER 환경 변수에 따라 적절한 구현체를 반환하는 팩토리 함수"""
provider_type = getattr(settings, "CONTEXT_PROVIDER", "mock").strip().lower()
if provider_type == "mock":
return MockContextProvider()
elif provider_type == "csv":
return CSVContextProvider()
# elif provider_type == "rag":
# return RAGContextProvider() # 향후 구축 시 연결
else:
logger.warning(f"[Context Service] 알 수 없는 프로바이더 '{provider_type}', Mock Provider로 폴백합니다.")
return MockContextProvider()
# 전역 싱글톤 인스턴스 (워커에서 Import 용)
context_service = get_context_service()