"""Prompt contract for grounded homepage copy.""" from typing import Optional, Protocol, Sequence from common.enums import PlaceCategory class FactLike(Protocol): key: str label: str value: str unit: Optional[str] RESPONSE_SCHEMA = { "type": "object", "properties": { "intro": {"type": "string"}, "intro_fact_keys": {"type": "array", "items": {"type": "string"}}, "meta_description": {"type": "string"}, "faqs": { "type": "array", "items": { "type": "object", "properties": { "question": {"type": "string"}, "answer": {"type": "string"}, "fact_keys": {"type": "array", "items": {"type": "string"}}, }, "required": ["question", "answer", "fact_keys"], }, }, }, "required": ["intro", "intro_fact_keys", "meta_description", "faqs"], } _CATEGORY_LABEL = { PlaceCategory.LODGING: "숙박업소", PlaceCategory.CAFE: "카페", PlaceCategory.RESTAURANT: "음식점", PlaceCategory.CLINIC: "관광·체험 시설", } def _fact_lines(facts: Sequence[FactLike]) -> str: return "\n".join( f"- {fact.key} ({fact.label}) = {fact.value}" + (f" {fact.unit}" if fact.unit else "") for fact in facts ) def build_prompt( place_name: str, category: PlaceCategory, facts: Sequence[FactLike], max_faqs: int, unit_facts: Optional[Sequence[FactLike]] = None, ) -> str: sections = [ f"'{place_name}'({_CATEGORY_LABEL.get(category, '사업장')})의 공식 홈페이지 문구를 작성한다.", "", "확인된 사업장 사실:", _fact_lines(facts) if facts else "- 없음", ] if unit_facts: sections.extend(["", "확인된 객실·메뉴 사실:", _fact_lines(unit_facts)]) sections.extend([ "", "출력:", "- intro: 소개문 100~250자", "- intro_fact_keys: 소개문의 근거 key", "- meta_description: 검색 요약 50~120자", f"- faqs: 최대 {max_faqs}개, 각 항목에 근거 fact_keys 포함", "", "규칙:", "- 위 사실에 없는 숫자·시설·지역 정보를 지어내지 마라.", "- false·불가·없음 값을 가능하다고 표현하지 않는다.", "- 홍보성·평가성 표현을 쓰지 않는다.", "- 근거 없는 FAQ는 만들지 않는다.", "- 한국어 존댓말을 사용한다.", ]) return "\n".join(sections)