""" Step 4: 검색 품질 평가 (Hit@K 메트릭) 실행: python scripts/04_evaluate.py """ import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from rich.console import Console from rich.table import Table from core.search_engine import MenuSearchEngine console = Console() # (쿼리, 정답 menu_id) 평가셋 — 실제 SCR_ 형식 ID 기준 (real_menus.json 검증) EVAL_SET = [ # 국내주식 - 관심/시세 (search_count 상위) ("관심종목 보고싶어", "SCR_1000"), # 국내관심, sc=12,739,646 ("삼성전자 호가 보여줘", "SCR_1050"), # 호가, sc=9,120,154 ("종목 차트 보고싶어", "SCR_1200"), # 종목차트, sc=11,006,235 # 국내주식 - 계좌/주문 ("국내주식 잔고 확인", "SCR_1300"), # 국내잔고, sc=14,981,128 ("미체결 주문 확인", "SCR_1301"), # 미체결, sc=1,895,282 ("오늘 손익 얼마야", "SCR_1309"), # 실현손익, sc=875,319 ("예약주문 걸어놓고 싶어", "SCR_1180"), # 예약주문, sc=107,490 ("내 조건검색식 편집하고 싶어", "SCR_1871"), # 조건검색, sc=311,728 # 해외주식 ("미국 주식 주문하고 싶어", "SCR_3030"), # 해외주식주문, sc=1,615,638 ("해외주식 잔고 확인", "SCR_3032"), # 해외잔고, sc=4,127,850 ("애플 주식 현재가 호가", "SCR_3010"), # 해외호가, sc=1,662,209 # 파생상품 (CFD) ("CFD 주문하고 싶어", "SCR_2020"), # 파생 CFD 주문, sc=117 # 자산/뱅킹 ("입출금 하고싶어", "SCR_5000"), # 입출금, sc=1,210,154 ("이체내역 조회", "SCR_5006"), # 이체내역조회, sc=224,757 # search_count 낮은 메뉴 (다양성 확보) ("주식더모으기 설정", "SCR_1182"), # 주식더모으기, sc=0 ] def evaluate(engine: MenuSearchEngine, k_values=(1, 3, 5)): hits = {k: 0 for k in k_values} details = [] for query, correct_id in EVAL_SET: results = engine.search(query, top_n=max(k_values), threshold=0.0) returned_ids = [r["menu_id"] for r in results] top1 = results[0]["menu_name"] if results else "-" top1_sim = results[0]["similarity_pct"] if results else "-" row = {"query": query, "correct": correct_id, "top1": top1, "top1_sim": top1_sim} for k in k_values: row[f"hit@{k}"] = correct_id in returned_ids[:k] if row[f"hit@{k}"]: hits[k] += 1 details.append(row) # 상세 결과 테이블 table = Table(title="검색 품질 평가 상세", show_lines=True) table.add_column("쿼리", style="white", max_width=30) table.add_column("Top-1 결과", style="cyan") table.add_column("유사도", style="yellow") table.add_column("Hit@1", style="green") table.add_column("Hit@3", style="green") table.add_column("Hit@5", style="green") for row in details: table.add_row( row["query"], row["top1"], row["top1_sim"], "✅" if row["hit@1"] else "❌", "✅" if row["hit@3"] else "❌", "✅" if row["hit@5"] else "❌", ) console.print(table) # 요약 total = len(EVAL_SET) console.print("\n[bold]=== 평가 요약 ===[/bold]") for k in k_values: rate = hits[k] / total * 100 color = "green" if rate >= 70 else "yellow" if rate >= 50 else "red" console.print(f" Hit@{k}: [{color}]{hits[k]}/{total} ({rate:.1f}%)[/{color}]") if __name__ == "__main__": engine = MenuSearchEngine.get_instance() evaluate(engine)