AI_Menu_Search / core /query_expander.py
Juhaha
HF Spaces 데모 배포 (Streamlit + Qdrant 임베디드, 색인 빌드타임 생성)
fbd1091
Raw
History Blame Contribute Delete
9.21 kB
"""
HTS 도메인 특화 규칙 기반 쿼리 확장기 (QueryExpander)
- LLM 없음 → 10ms 이내 실행
- HTS 핵심 용어 동의어 사전으로 쿼리 확장
- BM25 검색에서 사용 (Dense 임베딩에는 사용 안 함)
사용 예:
expander = QueryExpander()
expander.expand("주문 취소")
# → "주문 취소 매수 매도 거래 체결 미체결 정정 주문변경"
"""
from typing import List
# ---------------------------------------------------------------------------
# HTS 도메인 동의어 사전
# 키: 원본 단어, 값: 함께 추가할 관련어 리스트 (원본 포함)
# ---------------------------------------------------------------------------
SYNONYM_MAP = {
# ── 주문 계열 ──────────────────────────────────────────────────────────
"취소": ["취소", "정정", "주문변경", "미체결", "주문취소", "정정취소"],
"정정": ["정정", "취소", "수정", "변경", "주문변경", "주문수정"],
"주문": ["주문", "매수", "매도", "거래", "체결", "미체결"],
"매수": ["매수", "사다", "구매", "주문", "체결"],
"매도": ["매도", "팔다", "판매", "주문", "체결"],
"사고": ["매수", "사다", "구매", "주문"],
"팔고": ["매도", "팔다", "판매", "주문"],
"예약": ["예약", "예약주문", "자동주문", "시간지정", "걸어놓다"],
"일괄": ["일괄", "묶음", "동시", "한꺼번에", "대량주문"],
"자동": ["자동", "예약", "예약주문", "자동주문", "걸어놓다"],
"걸어": ["예약", "예약주문", "자동주문", "걸어놓다"],
# ── 잔고 / 손익 계열 ───────────────────────────────────────────────────
"잔고": ["잔고", "보유", "수량", "평가금액", "자산", "보유주식"],
"손익": ["손익", "수익", "수익률", "평가손익", "실현손익"],
"수익": ["수익", "손익", "수익률", "평가손익", "이익", "수익현황"],
"얼마": ["잔고", "평가금액", "수익률", "손익", "잔고조회", "수익", "보유"],
"올랐": ["손익", "수익률", "평가손익", "수익", "잔고"],
"내렸": ["손익", "평가손익", "손실", "수익률"],
"보유": ["보유", "잔고", "내주식", "포트폴리오", "보유주식"],
"주식": ["주식", "잔고", "보유주식", "주식잔고", "국내주식잔고"],
"확인": ["확인", "조회", "잔고확인", "현황", "조회하다"],
"내주식": ["잔고", "보유주식", "국내잔고", "수익", "평가금액"],
# ── 시세 계열 ──────────────────────────────────────────────────────────
"시세": ["시세", "현재가", "가격", "주가", "호가", "실시간"],
"현재가": ["현재가", "시세", "실시간", "주가", "체결가"],
"가격": ["현재가", "시세", "주가", "호가", "체결가"],
"호가": ["호가", "체결", "호가창", "매수호가", "매도호가"],
"체결": ["체결", "호가", "체결내역", "체결가", "거래"],
# "지금"은 자연어 시간 강조 표현 — 현재가/시세로 확장 시 잔고 쿼리에서 역효과
# 예: "지금 내 주식 얼마야" → 호가로 오매칭. BM25 자체 IDF로 처리.
# "지금": ["현재가", "실시간", "시세", "주가"], # 비활성화
# ── 차트 계열 ──────────────────────────────────────────────────────────
"차트": ["차트", "일봉", "주봉", "캔들", "그래프", "시세차트"],
"일봉": ["일봉", "차트", "일별시세", "캔들차트", "주봉"],
"그래프": ["차트", "그래프", "일봉", "캔들"],
# ── 조건검색 계열 ──────────────────────────────────────────────────────
"조건": ["조건", "조건검색", "스크리닝", "필터", "검색식"],
"스크리닝": ["스크리닝", "조건검색", "종목검색", "필터", "조건"],
"검색식": ["검색식", "조건검색", "조건식", "스크리닝"],
"실시간": ["실시간", "조건검색", "현재가", "시세"],
# ── 자산 / 계좌 계열 ───────────────────────────────────────────────────
"이체": ["이체", "계좌이체", "자금이동", "송금", "계좌간이체"],
"계좌": ["계좌", "잔고", "자산", "통합조회", "계좌잔고"],
"자산": ["자산", "계좌", "잔고", "포트폴리오", "통합조회"],
"통합": ["통합", "통합조회", "계좌잔고", "자산", "전체"],
"송금": ["이체", "계좌이체", "자금이동", "송금"],
# ── 투자정보 계열 ──────────────────────────────────────────────────────
"공시": ["공시", "전자공시", "공시검색", "사업보고서", "기업공시"],
"뉴스": ["뉴스", "종목뉴스", "시장정보", "이슈", "뉴스검색"],
"정보": ["투자정보", "뉴스", "공시", "시장정보"],
# ── 설정 계열 ──────────────────────────────────────────────────────────
"알림": ["알림", "푸시", "푸시알림", "알림설정", "알림끄기"],
"푸시": ["푸시", "알림", "푸시알림", "알림설정"],
"설정": ["설정", "환경설정", "화면설정", "알림설정"],
"끄기": ["해제", "알림해제", "알림끄기", "알림설정"],
# ── 해외주식 계열 ──────────────────────────────────────────────────────
"해외": ["해외", "미국", "해외주식", "글로벌", "나스닥"],
"미국": ["미국", "해외", "나스닥", "NYSE", "미국주식", "해외주식"],
"나스닥": ["나스닥", "미국", "해외주식", "NYSE"],
# ── 선물/옵션 계열 ────────────────────────────────────────────────────
"선물": ["선물", "선물주문", "선물옵션", "파생", "futures"],
"옵션": ["옵션", "선물옵션", "파생상품", "선물"],
}
class QueryExpander:
"""
짧은 HTS 검색 쿼리를 도메인 동의어로 확장.
BM25 검색 전처리에 사용. Dense 임베딩에는 사용하지 않음.
Args:
max_extra_terms: 추가할 최대 동의어 수 (기본 8개)
너무 많으면 BM25 IDF 점수 희석
사용 예::
expander = QueryExpander()
print(expander.expand("주문 취소하고 싶어"))
# "주문 취소하고 싶어 매수 매도 거래 정정 주문변경 미체결"
print(expander.get_expansion_terms("차트 보여줘"))
# ["일봉", "주봉", "캔들", "그래프", "시세차트"]
"""
def __init__(self, max_extra_terms: int = 8):
self.synonym_map = SYNONYM_MAP
self.max_extra_terms = max_extra_terms
def expand(self, query: str) -> str:
"""
쿼리에서 등장하는 키워드를 동의어로 확장.
원본 쿼리를 앞에 유지하고, 동의어를 뒤에 추가.
공백 기반 토큰 단위로 처리 (형태소 분석 없음).
Returns:
확장된 쿼리 문자열
"""
tokens = query.strip().split()
extra_terms: List[str] = []
seen = set(tokens)
for token in tokens:
if token in self.synonym_map:
for synonym in self.synonym_map[token]:
if synonym not in seen:
extra_terms.append(synonym)
seen.add(synonym)
if len(extra_terms) >= self.max_extra_terms:
break
if len(extra_terms) >= self.max_extra_terms:
break
if extra_terms:
return query + " " + " ".join(extra_terms)
return query
def get_expansion_terms(self, query: str) -> List[str]:
"""확장 용어 목록만 반환 (디버깅/로깅용)"""
original_tokens = set(query.strip().split())
expanded = self.expand(query)
all_tokens = expanded.split()
return [t for t in all_tokens if t not in original_tokens]