"""FAQ 목표 수 채우기 — 생성된 FAQ 가 모자라면 카탈로그에서 겹치지 않는 공통 질문을 고른다.""" import re from dataclasses import dataclass from typing import Iterable, Optional from common.faq_catalog import FaqCatalog # 사이트에 싣는 FAQ 목표 수. FAQ_TARGET = 20 @dataclass(frozen=True) class ExistingFaq: question: str source_fact_ids: Optional[list] = None @dataclass(frozen=True) class FillFaq: catalog_id: str question: str answer: str def _compact(text: str) -> str: return re.sub(r"\s+", "", text or "").lower() def _base_key(key: str) -> str: """객실 근거는 "A동:max_capacity" 로 적힌다 — 주제 비교에는 key 만 쓴다.""" return str(key).rsplit(":", 1)[-1] def _with_topic_particle(topic: str) -> str: """주제 뒤에 은/는 을 붙인다.""" last = topic.strip()[-1:] if "가" <= last <= "힣": return topic + ("은" if (ord(last) - ord("가")) % 28 else "는") return topic + "은(는)" def fallback_answer(catalog: FaqCatalog, index: int, topic: str, phone: Optional[str]) -> str: """문의 안내 문구.""" phone = (phone or "").strip() templates = catalog.fallback_with_contact if phone else catalog.fallback_without_contact template = templates[index % len(templates)] return template.format(topic=_with_topic_particle(topic), contact=f"전화({phone})") def pick_fill_faqs( catalog: FaqCatalog, existing: Iterable[ExistingFaq], fact_keys: Iterable[str], phone: Optional[str] = None, target: int = FAQ_TARGET, ) -> list[FillFaq]: """목표 수까지 모자란 만큼 카탈로그 질문을 고른다.""" existing = list(existing) need = target - len(existing) if need <= 0: return [] asked = [_compact(faq.question) for faq in existing] covered_keys = {_base_key(k) for faq in existing for k in (faq.source_fact_ids or [])} known_keys = {_base_key(k) for k in fact_keys} picked: list[FillFaq] = [] for item in catalog.items: if len(picked) >= need: break if known_keys.intersection(item.fact_keys): continue # 규칙 1 if covered_keys.intersection(item.fact_keys): continue # 규칙 2 — 근거 key if any(keyword in question for keyword in item.keywords for question in asked): continue # 규칙 2 — 질문 낱말 answer = fallback_answer(catalog, len(picked), item.topic, phone) picked.append(FillFaq(item.id, item.question, answer)) return picked def suggested_questions(catalog: FaqCatalog, fact_keys: Iterable[str]) -> list[str]: """fact 로 답할 수 있는 카탈로그 질문 — 프롬프트에 실어 LLM 이 이 질문들부터 쓰게 한다.""" known = {_base_key(k) for k in fact_keys} return [item.question for item in catalog.items if known.intersection(item.fact_keys)]