"""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, records: Optional[Sequence[str]] = None, suggested_questions: Optional[Sequence[str]] = None, ) -> str: """소개문·메타·FAQ 생성 프롬프트.""" sections = [ f"'{place_name}'({_CATEGORY_LABEL.get(category, '사업장')})의 공식 홈페이지 문구를 작성한다.", "", "확인된 사업장 사실:", _fact_lines(facts) if facts else "- 없음", ] if unit_facts: sections.extend(["", "확인된 객실·메뉴 사실:", _fact_lines(unit_facts)]) if records: sections.extend([ "", "업소에 대해 확인된 기록(출처가 있는 글):", *(f"- {line}" for line in records), ]) if suggested_questions: # 업종 카탈로그 중 위 사실로 답할 수 있는 질문들(services/faq_fill.suggested_questions). sections.extend([ "", "FAQ 로 먼저 쓸 질문(위 사실로 답할 수 있는 것):", *(f"- {q}" for q in suggested_questions), ]) sections.extend([ "", "출력:", "- intro: 소개문 200~600자. 사실이 충분하면 2~3문단으로 나눈다(문단 사이 빈 줄)", "- intro_fact_keys: 소개문의 근거 key", "- meta_description: 검색 요약 50~120자", f"- faqs: 최대 {max_faqs}개, 각 항목에 근거 fact_keys 포함", "", "규칙:", "- 위 사실에 없는 숫자·시설·지역 정보를 지어내지 마라.", "- **기록 절에 있는 내용은 적극적으로 쓴다.** 그것도 출처가 확인된 사실이다 —" " 건물의 내력·공간 구성·주변과의 관계처럼 이 업소만의 이야기가 거기 있다.", "- false·불가·없음 값을 가능하다고 표현하지 않는다.", "- 홍보성·평가성 표현을 쓰지 않는다.", "- 근거 없는 FAQ는 만들지 않는다.", "- FAQ 한 문항에는 주제 하나만 묻는다(예: 체크인과 체크아웃을 한 문항에 묶지 않는다).", "- 한국어 존댓말을 사용한다.", ]) return "\n".join(sections)