change prompt format
parent
0e1eae75dd
commit
7f5a75e0a5
|
|
@ -1,12 +1,13 @@
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
from pydantic import BaseModel
|
||||||
from openai import AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
from app.utils.logger import get_logger
|
from app.utils.logger import get_logger
|
||||||
from config import apikey_settings
|
from config import apikey_settings
|
||||||
from app.utils.prompts.prompts import Prompt
|
from app.utils.prompts.prompts import Prompt
|
||||||
|
|
||||||
|
|
||||||
# 로거 설정
|
# 로거 설정
|
||||||
logger = get_logger("chatgpt")
|
logger = get_logger("chatgpt")
|
||||||
|
|
||||||
|
|
@ -31,6 +32,16 @@ class ChatgptService:
|
||||||
)
|
)
|
||||||
structured_output = json.loads(response.output_text)
|
structured_output = json.loads(response.output_text)
|
||||||
return structured_output or {}
|
return structured_output or {}
|
||||||
|
|
||||||
|
async def _call_pydantic_output(self, prompt : str, output_format : BaseModel, model : str) -> BaseModel: # 입력 output_format의 경우 Pydantic BaseModel Class를 상속한 Class 자체임에 유의할 것
|
||||||
|
content = [{"type": "input_text", "text": prompt}]
|
||||||
|
response = await self.client.responses.parse(
|
||||||
|
model=model,
|
||||||
|
input=[{"role": "user", "content": content}],
|
||||||
|
text_format=output_format
|
||||||
|
)
|
||||||
|
structured_output = response.output_parsed
|
||||||
|
return structured_output.model_dump() or {}
|
||||||
|
|
||||||
async def generate_structured_output(
|
async def generate_structured_output(
|
||||||
self,
|
self,
|
||||||
|
|
@ -43,5 +54,6 @@ class ChatgptService:
|
||||||
logger.info(f"[ChatgptService] Starting GPT request with structured output with model: {prompt.prompt_model}")
|
logger.info(f"[ChatgptService] Starting GPT request with structured output with model: {prompt.prompt_model}")
|
||||||
|
|
||||||
# GPT API 호출
|
# GPT API 호출
|
||||||
response = await self._call_structured_output_with_response_gpt_api(prompt_text, prompt.prompt_output, prompt.prompt_model)
|
#response = await self._call_structured_output_with_response_gpt_api(prompt_text, prompt.prompt_output, prompt.prompt_model)
|
||||||
|
response = await self._call_pydantic_output(prompt_text, prompt.prompt_output_class, prompt.prompt_model)
|
||||||
return response
|
return response
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5-mini",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info",
|
|
||||||
"marketing_intelligence_summary",
|
|
||||||
"language",
|
|
||||||
"promotional_expression_example",
|
|
||||||
"timing_rules"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "lyric",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"lyric": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"suno_prompt":{
|
|
||||||
"type" : "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"lyric", "suno_prompt"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5-mini",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "report",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"report": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"summary": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"details": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"detail_title": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"detail_description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"detail_title",
|
|
||||||
"detail_description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"summary",
|
|
||||||
"details"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"selling_points": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"contents_advise": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"report",
|
|
||||||
"selling_points",
|
|
||||||
"tags",
|
|
||||||
"contents_advise"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
|
|
||||||
[Role & Objective]
|
|
||||||
Act as a content marketing expert with strong domain knowledge in the Korean pension / stay-accommodation industry.
|
|
||||||
Your goal is to produce a Marketing Intelligence Report that will be shown to accommodation owners BEFORE any content is generated.
|
|
||||||
The report must clearly explain what makes the property sellable, marketable, and scalable through content.
|
|
||||||
|
|
||||||
[INPUT]
|
|
||||||
- Business Name: {customer_name}
|
|
||||||
- Region: {region}
|
|
||||||
- Region Details: {detail_region_info}
|
|
||||||
|
|
||||||
[Core Analysis Requirements]
|
|
||||||
Analyze the property based on:
|
|
||||||
Location, concept, photos, online presence, and nearby environment
|
|
||||||
Target customer behavior and reservation decision factors
|
|
||||||
Include:
|
|
||||||
- Target customer segments & personas
|
|
||||||
- Unique Selling Propositions (USPs)
|
|
||||||
- Competitive landscape (direct & indirect competitors)
|
|
||||||
- Market positioning
|
|
||||||
|
|
||||||
[Key Selling Point Structuring – UI Optimized]
|
|
||||||
From the analysis above, extract the main Key Selling Points using the structure below.
|
|
||||||
Rules:
|
|
||||||
Focus only on factors that directly influence booking decisions
|
|
||||||
Each selling point must be concise and visually scannable
|
|
||||||
Language must be reusable for ads, short-form videos, and listing headlines
|
|
||||||
Avoid full sentences in descriptions; use short selling phrases
|
|
||||||
|
|
||||||
Output format:
|
|
||||||
[Category]
|
|
||||||
(Tag keyword – 5~8 words, noun-based, UI oval-style)
|
|
||||||
One-line selling phrase (not a full sentence)
|
|
||||||
Limit:
|
|
||||||
5 to 8 Key Selling Points only
|
|
||||||
|
|
||||||
[Content & Automation Readiness Check]
|
|
||||||
Ensure that:
|
|
||||||
Each tag keyword can directly map to a content theme
|
|
||||||
Each selling phrase can be used as:
|
|
||||||
- Video hook
|
|
||||||
- Image headline
|
|
||||||
- Ad copy snippet
|
|
||||||
|
|
||||||
|
|
||||||
[Tag Generation Rules]
|
|
||||||
- Tags must include **only core keywords that can be directly used for viral video song lyrics**
|
|
||||||
- Each tag should be selected with **search discovery + emotional resonance + reservation conversion** in mind
|
|
||||||
- The number of tags must be **exactly 5**
|
|
||||||
- Tags must be **nouns or short keyword phrases**; full sentences are strictly prohibited
|
|
||||||
- The following categories must be **balanced and all represented**:
|
|
||||||
1) **Location / Local context** (region name, neighborhood, travel context)
|
|
||||||
2) **Accommodation positioning** (emotional stay, private stay, boutique stay, etc.)
|
|
||||||
3) **Emotion / Experience** (healing, rest, one-day escape, memory, etc.)
|
|
||||||
4) **SNS / Viral signals** (Instagram vibes, picture-perfect day, aesthetic travel, etc.)
|
|
||||||
5) **Travel & booking intent** (travel, getaway, stay, relaxation, etc.)
|
|
||||||
|
|
||||||
- If a brand name exists, **at least one tag must include the brand name or a brand-specific expression**
|
|
||||||
- Avoid overly generic keywords (e.g., “hotel”, “travel” alone); **prioritize distinctive, differentiating phrases**
|
|
||||||
- The final output must strictly follow the JSON format below, with no additional text
|
|
||||||
|
|
||||||
"tags": ["Tag1", "Tag2", "Tag3", "Tag4", "Tag5"]
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5.2",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "report",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"report": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"selling_points": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"report",
|
|
||||||
"selling_points",
|
|
||||||
"tags"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
import os, json
|
|
||||||
from abc import ABCMeta
|
|
||||||
from config import prompt_settings
|
|
||||||
from app.utils.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger("prompt")
|
|
||||||
|
|
||||||
class Prompt():
|
|
||||||
prompt_name : str # ex) marketing_prompt
|
|
||||||
prompt_template_path : str #프롬프트 경로
|
|
||||||
prompt_template : str # fstring 포맷
|
|
||||||
prompt_input : list
|
|
||||||
prompt_output : dict
|
|
||||||
prompt_model : str
|
|
||||||
|
|
||||||
def __init__(self, prompt_name, prompt_template_path):
|
|
||||||
self.prompt_name = prompt_name
|
|
||||||
self.prompt_template_path = prompt_template_path
|
|
||||||
self.prompt_template, prompt_dict = self.read_prompt()
|
|
||||||
self.prompt_input = prompt_dict['prompt_variables']
|
|
||||||
self.prompt_output = prompt_dict['output_format']
|
|
||||||
self.prompt_model = prompt_dict.get('model', "gpt-5-mini")
|
|
||||||
|
|
||||||
def _reload_prompt(self):
|
|
||||||
self.prompt_template, prompt_dict = self.read_prompt()
|
|
||||||
self.prompt_input = prompt_dict['prompt_variables']
|
|
||||||
self.prompt_output = prompt_dict['output_format']
|
|
||||||
self.prompt_model = prompt_dict.get('model', "gpt-5-mini")
|
|
||||||
|
|
||||||
def read_prompt(self) -> tuple[str, dict]:
|
|
||||||
template_text_path = self.prompt_template_path + ".txt"
|
|
||||||
prompt_dict_path = self.prompt_template_path + ".json"
|
|
||||||
with open(template_text_path, "r") as fp:
|
|
||||||
prompt_template = fp.read()
|
|
||||||
with open(prompt_dict_path, "r") as fp:
|
|
||||||
prompt_dict = json.load(fp)
|
|
||||||
|
|
||||||
return prompt_template, prompt_dict
|
|
||||||
|
|
||||||
def build_prompt(self, input_data:dict) -> str:
|
|
||||||
self.check_input(input_data)
|
|
||||||
build_template = self.prompt_template
|
|
||||||
logger.debug(f"build_template: {build_template}")
|
|
||||||
logger.debug(f"input_data: {input_data}")
|
|
||||||
build_template = build_template.format(**input_data)
|
|
||||||
return build_template
|
|
||||||
|
|
||||||
def check_input(self, input_data:dict) -> bool:
|
|
||||||
missing_variables = input_data.keys() - set(self.prompt_input)
|
|
||||||
if missing_variables:
|
|
||||||
raise Exception(f"missing_variable for prompt {self.prompt_name} : {missing_variables}")
|
|
||||||
|
|
||||||
flooding_variables = set(self.prompt_input) - input_data.keys()
|
|
||||||
if flooding_variables:
|
|
||||||
raise Exception(f"flooding_variables for prompt {self.prompt_name} : {flooding_variables}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
marketing_prompt = Prompt(
|
|
||||||
prompt_name=prompt_settings.MARKETING_PROMPT_NAME,
|
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.MARKETING_PROMPT_NAME)
|
|
||||||
)
|
|
||||||
|
|
||||||
summarize_prompt = Prompt(
|
|
||||||
prompt_name=prompt_settings.SUMMARIZE_PROMPT_NAME,
|
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.SUMMARIZE_PROMPT_NAME)
|
|
||||||
)
|
|
||||||
|
|
||||||
lyric_prompt = Prompt(
|
|
||||||
prompt_name=prompt_settings.LYLIC_PROMPT_NAME,
|
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.LYLIC_PROMPT_NAME)
|
|
||||||
)
|
|
||||||
|
|
||||||
def reload_all_prompt():
|
|
||||||
marketing_prompt._reload_prompt()
|
|
||||||
summarize_prompt._reload_prompt()
|
|
||||||
lyric_prompt._reload_prompt()
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"prompt_variables": [
|
|
||||||
"report",
|
|
||||||
"selling_points"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "tags",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tag_keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"tag_keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
|
|
||||||
입력 :
|
|
||||||
분석 보고서
|
|
||||||
{report}
|
|
||||||
|
|
||||||
셀링 포인트
|
|
||||||
{selling_points}
|
|
||||||
|
|
||||||
위 분석 결과를 바탕으로, 주요 셀링 포인트를 다음 구조로 재정리하라.
|
|
||||||
|
|
||||||
조건:
|
|
||||||
각 셀링 포인트는 반드시 ‘카테고리 → 태그 키워드 → 한 줄 설명’ 구조를 가질 것
|
|
||||||
태그 키워드는 UI 상에서 타원(oval) 형태의 시각적 태그로 사용될 것을 가정하여
|
|
||||||
- 3 ~ 6단어 이내
|
|
||||||
- 명사 또는 명사형 키워드로 작성
|
|
||||||
- 설명은 문장이 아닌, 짧은 ‘셀링 문구’ 형태로 작성할 것
|
|
||||||
- 광고·숏폼·상세페이지 어디에도 바로 재사용 가능해야 함
|
|
||||||
- 전체 셀링 포인트 개수는 5~7개로 제한
|
|
||||||
|
|
||||||
출력 형식:
|
|
||||||
[카테고리명]
|
|
||||||
(태그 키워드)
|
|
||||||
- 한 줄 설명 문구
|
|
||||||
|
|
||||||
예시:
|
|
||||||
[공간 정체성]
|
|
||||||
(100년 적산가옥 · 시간의 결)
|
|
||||||
- 하루를 ‘숙박’이 아닌 ‘체류’로 바꾸는 공간
|
|
||||||
|
|
||||||
[입지 & 희소성]
|
|
||||||
(말랭이마을 · 로컬 히든플레이스)
|
|
||||||
- 관광지가 아닌, 군산을 아는 사람의 선택
|
|
||||||
|
|
||||||
[프라이버시]
|
|
||||||
(독채 숙소 · 프라이빗 스테이)
|
|
||||||
- 누구의 방해도 없는 완전한 휴식 구조
|
|
||||||
|
|
||||||
[비주얼 경쟁력]
|
|
||||||
(감성 인테리어 · 자연광 스폿)
|
|
||||||
- 찍는 순간 콘텐츠가 되는 공간 설계
|
|
||||||
|
|
||||||
[타깃 최적화]
|
|
||||||
(커플 · 소규모 여행)
|
|
||||||
- 둘에게 가장 이상적인 공간 밀도
|
|
||||||
|
|
||||||
[체류 경험]
|
|
||||||
(아무것도 안 해도 되는 하루)
|
|
||||||
- 일정 없이도 만족되는 하루 루틴
|
|
||||||
|
|
||||||
[브랜드 포지션]
|
|
||||||
(호텔도 펜션도 아닌 아지트)
|
|
||||||
- 다시 돌아오고 싶은 개인적 장소
|
|
||||||
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5-mini",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info",
|
|
||||||
"marketing_intelligence_summary",
|
|
||||||
"language",
|
|
||||||
"promotional_expression_example",
|
|
||||||
"timing_rules"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "lyric",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"lyric": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"suno_prompt":{
|
|
||||||
"type" : "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"lyric", "suno_prompt"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
|
|
||||||
[ROLE]
|
|
||||||
You are a content marketing expert, brand strategist, and creative songwriter
|
|
||||||
specializing in Korean pension / accommodation businesses.
|
|
||||||
You create lyrics strictly based on Brand & Marketing Intelligence analysis
|
|
||||||
and optimized for viral short-form video content.
|
|
||||||
|
|
||||||
[INPUT]
|
|
||||||
Business Name: {customer_name}
|
|
||||||
Region: {region}
|
|
||||||
Region Details: {detail_region_info}
|
|
||||||
Brand & Marketing Intelligence Report: {marketing_intelligence_summary}
|
|
||||||
Output Language: {language}
|
|
||||||
|
|
||||||
[INTERNAL ANALYSIS – DO NOT OUTPUT]
|
|
||||||
Internally analyze the following to guide all creative decisions:
|
|
||||||
- Core brand identity and positioning
|
|
||||||
- Emotional hooks derived from selling points
|
|
||||||
- Target audience lifestyle, desires, and travel motivation
|
|
||||||
- Regional atmosphere and symbolic imagery
|
|
||||||
- How the stay converts into “shareable moments”
|
|
||||||
- Which selling points must surface implicitly in lyrics
|
|
||||||
|
|
||||||
[LYRICS & MUSIC CREATION TASK]
|
|
||||||
Based on the Brand & Marketing Intelligence Report for [{customer_name} ({region})], generate:
|
|
||||||
- Original promotional lyrics
|
|
||||||
- Music attributes for AI music generation (Suno-compatible prompt)
|
|
||||||
The output must be designed for VIRAL DIGITAL CONTENT
|
|
||||||
(short-form video, reels, ads).
|
|
||||||
|
|
||||||
[LYRICS REQUIREMENTS]
|
|
||||||
Mandatory Inclusions:
|
|
||||||
- Business name
|
|
||||||
- Region name
|
|
||||||
- Promotion subject
|
|
||||||
- Promotional expressions including:
|
|
||||||
{promotional_expression_example}
|
|
||||||
|
|
||||||
Content Rules:
|
|
||||||
- Lyrics must be emotionally driven, not descriptive listings
|
|
||||||
- Selling points must be IMPLIED, not explained
|
|
||||||
- Must sound natural when sung
|
|
||||||
- Must feel like a lifestyle moment, not an advertisement
|
|
||||||
|
|
||||||
Tone & Style:
|
|
||||||
- Warm, emotional, and aspirational
|
|
||||||
- Trendy, viral-friendly phrasing
|
|
||||||
- Calm but memorable hooks
|
|
||||||
- Suitable for travel / stay-related content
|
|
||||||
|
|
||||||
[SONG & MUSIC ATTRIBUTES – FOR SUNO PROMPT]
|
|
||||||
After the lyrics, generate a concise music prompt including:
|
|
||||||
Song mood (emotional keywords)
|
|
||||||
BPM range
|
|
||||||
Recommended genres (max 2)
|
|
||||||
Key musical motifs or instruments
|
|
||||||
Overall vibe (1 short sentence)
|
|
||||||
|
|
||||||
[CRITICAL LANGUAGE REQUIREMENT – ABSOLUTE RULE]
|
|
||||||
ALL OUTPUT MUST BE 100% WRITTEN IN {language}.
|
|
||||||
no mixed languages
|
|
||||||
All names, places, and expressions must be in {language}
|
|
||||||
Any violation invalidates the entire output
|
|
||||||
|
|
||||||
[OUTPUT RULES – STRICT]
|
|
||||||
{timing_rules}
|
|
||||||
|
|
||||||
No explanations
|
|
||||||
No headings
|
|
||||||
No bullet points
|
|
||||||
No analysis
|
|
||||||
No extra text
|
|
||||||
|
|
||||||
[FAILURE FORMAT]
|
|
||||||
If generation is impossible:
|
|
||||||
ERROR: Brief reason in English
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5-mini",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "report",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"report": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"summary": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"details": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"detail_title": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"detail_description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"detail_title",
|
|
||||||
"detail_description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"summary",
|
|
||||||
"details"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"selling_points": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"contents_advise": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"report",
|
|
||||||
"selling_points",
|
|
||||||
"tags",
|
|
||||||
"contents_advise"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
|
|
||||||
[Role & Objective]
|
|
||||||
Act as a content marketing expert with strong domain knowledge in the Korean pension / stay-accommodation industry.
|
|
||||||
Your goal is to produce a Marketing Intelligence Report that will be shown to accommodation owners BEFORE any content is generated.
|
|
||||||
The report must clearly explain what makes the property sellable, marketable, and scalable through content.
|
|
||||||
|
|
||||||
[INPUT]
|
|
||||||
- Business Name: {customer_name}
|
|
||||||
- Region: {region}
|
|
||||||
- Region Details: {detail_region_info}
|
|
||||||
|
|
||||||
[Core Analysis Requirements]
|
|
||||||
Analyze the property based on:
|
|
||||||
Location, concept, and nearby environment
|
|
||||||
Target customer behavior and reservation decision factors
|
|
||||||
Include:
|
|
||||||
- Target customer segments & personas
|
|
||||||
- Unique Selling Propositions (USPs)
|
|
||||||
- Competitive landscape (direct & indirect competitors)
|
|
||||||
- Market positioning
|
|
||||||
|
|
||||||
[Key Selling Point Structuring – UI Optimized]
|
|
||||||
From the analysis above, extract the main Key Selling Points using the structure below.
|
|
||||||
Rules:
|
|
||||||
Focus only on factors that directly influence booking decisions
|
|
||||||
Each selling point must be concise and visually scannable
|
|
||||||
Language must be reusable for ads, short-form videos, and listing headlines
|
|
||||||
Avoid full sentences in descriptions; use short selling phrases
|
|
||||||
Do not provide in report
|
|
||||||
|
|
||||||
Output format:
|
|
||||||
[Category]
|
|
||||||
(Tag keyword – 5~8 words, noun-based, UI oval-style)
|
|
||||||
One-line selling phrase (not a full sentence)
|
|
||||||
Limit:
|
|
||||||
5 to 8 Key Selling Points only
|
|
||||||
Do not provide in report
|
|
||||||
|
|
||||||
[Content & Automation Readiness Check]
|
|
||||||
Ensure that:
|
|
||||||
Each tag keyword can directly map to a content theme
|
|
||||||
Each selling phrase can be used as:
|
|
||||||
- Video hook
|
|
||||||
- Image headline
|
|
||||||
- Ad copy snippet
|
|
||||||
|
|
||||||
|
|
||||||
[Tag Generation Rules]
|
|
||||||
- Tags must include **only core keywords that can be directly used for viral video song lyrics**
|
|
||||||
- Each tag should be selected with **search discovery + emotional resonance + reservation conversion** in mind
|
|
||||||
- The number of tags must be **exactly 5**
|
|
||||||
- Tags must be **nouns or short keyword phrases**; full sentences are strictly prohibited
|
|
||||||
- The following categories must be **balanced and all represented**:
|
|
||||||
1) **Location / Local context** (region name, neighborhood, travel context)
|
|
||||||
2) **Accommodation positioning** (emotional stay, private stay, boutique stay, etc.)
|
|
||||||
3) **Emotion / Experience** (healing, rest, one-day escape, memory, etc.)
|
|
||||||
4) **SNS / Viral signals** (Instagram vibes, picture-perfect day, aesthetic travel, etc.)
|
|
||||||
5) **Travel & booking intent** (travel, getaway, stay, relaxation, etc.)
|
|
||||||
|
|
||||||
- If a brand name exists, **at least one tag must include the brand name or a brand-specific expression**
|
|
||||||
- Avoid overly generic keywords (e.g., “hotel”, “travel” alone); **prioritize distinctive, differentiating phrases**
|
|
||||||
- The final output must strictly follow the JSON format below, with no additional text
|
|
||||||
|
|
||||||
"tags": ["Tag1", "Tag2", "Tag3", "Tag4", "Tag5"]
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
|
|
||||||
[Role & Objective]
|
|
||||||
Act as a content marketing expert with strong domain knowledge in the Korean pension / stay-accommodation industry.
|
|
||||||
Your goal is to produce a Marketing Intelligence Report that will be shown to accommodation owners BEFORE any content is generated.
|
|
||||||
The report must clearly explain what makes the property sellable, marketable, and scalable through content.
|
|
||||||
|
|
||||||
[INPUT]
|
|
||||||
- Business Name: {customer_name}
|
|
||||||
- Region: {region}
|
|
||||||
- Region Details: {detail_region_info}
|
|
||||||
|
|
||||||
[Core Analysis Requirements]
|
|
||||||
Analyze the property based on:
|
|
||||||
Location, concept, photos, online presence, and nearby environment
|
|
||||||
Target customer behavior and reservation decision factors
|
|
||||||
Include:
|
|
||||||
- Target customer segments & personas
|
|
||||||
- Unique Selling Propositions (USPs)
|
|
||||||
- Competitive landscape (direct & indirect competitors)
|
|
||||||
- Market positioning
|
|
||||||
|
|
||||||
[Key Selling Point Structuring – UI Optimized]
|
|
||||||
From the analysis above, extract the main Key Selling Points using the structure below.
|
|
||||||
Rules:
|
|
||||||
Focus only on factors that directly influence booking decisions
|
|
||||||
Each selling point must be concise and visually scannable
|
|
||||||
Language must be reusable for ads, short-form videos, and listing headlines
|
|
||||||
Avoid full sentences in descriptions; use short selling phrases
|
|
||||||
|
|
||||||
Output format:
|
|
||||||
[Category]
|
|
||||||
(Tag keyword – 5~8 words, noun-based, UI oval-style)
|
|
||||||
One-line selling phrase (not a full sentence)
|
|
||||||
Limit:
|
|
||||||
5 to 8 Key Selling Points only
|
|
||||||
|
|
||||||
[Content & Automation Readiness Check]
|
|
||||||
Ensure that:
|
|
||||||
Each tag keyword can directly map to a content theme
|
|
||||||
Each selling phrase can be used as:
|
|
||||||
- Video hook
|
|
||||||
- Image headline
|
|
||||||
- Ad copy snippet
|
|
||||||
|
|
||||||
|
|
||||||
[Tag Generation Rules]
|
|
||||||
- Tags must include **only core keywords that can be directly used for viral video song lyrics**
|
|
||||||
- Each tag should be selected with **search discovery + emotional resonance + reservation conversion** in mind
|
|
||||||
- The number of tags must be **exactly 5**
|
|
||||||
- Tags must be **nouns or short keyword phrases**; full sentences are strictly prohibited
|
|
||||||
- The following categories must be **balanced and all represented**:
|
|
||||||
1) **Location / Local context** (region name, neighborhood, travel context)
|
|
||||||
2) **Accommodation positioning** (emotional stay, private stay, boutique stay, etc.)
|
|
||||||
3) **Emotion / Experience** (healing, rest, one-day escape, memory, etc.)
|
|
||||||
4) **SNS / Viral signals** (Instagram vibes, picture-perfect day, aesthetic travel, etc.)
|
|
||||||
5) **Travel & booking intent** (travel, getaway, stay, relaxation, etc.)
|
|
||||||
|
|
||||||
- If a brand name exists, **at least one tag must include the brand name or a brand-specific expression**
|
|
||||||
- Avoid overly generic keywords (e.g., “hotel”, “travel” alone); **prioritize distinctive, differentiating phrases**
|
|
||||||
- The final output must strictly follow the JSON format below, with no additional text
|
|
||||||
|
|
||||||
"tags": ["Tag1", "Tag2", "Tag3", "Tag4", "Tag5"]
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
{
|
|
||||||
"model": "gpt-5.2",
|
|
||||||
"prompt_variables": [
|
|
||||||
"customer_name",
|
|
||||||
"region",
|
|
||||||
"detail_region_info"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "report",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"report": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"selling_points": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"report",
|
|
||||||
"selling_points",
|
|
||||||
"tags"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +1,57 @@
|
||||||
import os, json
|
import os, json
|
||||||
from abc import ABCMeta
|
from pydantic import BaseModel
|
||||||
from config import prompt_settings
|
from config import prompt_settings
|
||||||
from app.utils.logger import get_logger
|
from app.utils.logger import get_logger
|
||||||
|
from app.utils.prompts.schemas import *
|
||||||
|
|
||||||
logger = get_logger("prompt")
|
logger = get_logger("prompt")
|
||||||
|
|
||||||
class Prompt():
|
class Prompt():
|
||||||
prompt_name : str # ex) marketing_prompt
|
|
||||||
prompt_template_path : str #프롬프트 경로
|
prompt_template_path : str #프롬프트 경로
|
||||||
prompt_template : str # fstring 포맷
|
prompt_template : str # fstring 포맷
|
||||||
prompt_input : list
|
prompt_model : str
|
||||||
prompt_output : dict
|
|
||||||
prompt_model : str
|
prompt_input_class = BaseModel # pydantic class 자체를(instance 아님) 변수로 가짐
|
||||||
|
prompt_output_class = BaseModel
|
||||||
|
|
||||||
def __init__(self, prompt_name, prompt_template_path):
|
def __init__(self, prompt_template_path, prompt_input_class, prompt_output_class, prompt_model):
|
||||||
self.prompt_name = prompt_name
|
|
||||||
self.prompt_template_path = prompt_template_path
|
self.prompt_template_path = prompt_template_path
|
||||||
self.prompt_template, prompt_dict = self.read_prompt()
|
self.prompt_input_class = prompt_input_class
|
||||||
self.prompt_input = prompt_dict['prompt_variables']
|
self.prompt_output_class = prompt_output_class
|
||||||
self.prompt_output = prompt_dict['output_format']
|
self.prompt_template = self.read_prompt()
|
||||||
self.prompt_model = prompt_dict.get('model', "gpt-5-mini")
|
self.prompt_model = prompt_model
|
||||||
|
|
||||||
def _reload_prompt(self):
|
def _reload_prompt(self):
|
||||||
self.prompt_template, prompt_dict = self.read_prompt()
|
self.prompt_template = self.read_prompt()
|
||||||
self.prompt_input = prompt_dict['prompt_variables']
|
|
||||||
self.prompt_output = prompt_dict['output_format']
|
|
||||||
self.prompt_model = prompt_dict.get('model', "gpt-5-mini")
|
|
||||||
|
|
||||||
def read_prompt(self) -> tuple[str, dict]:
|
def read_prompt(self) -> tuple[str, dict]:
|
||||||
template_text_path = self.prompt_template_path + ".txt"
|
with open(self.prompt_template_path, "r") as fp:
|
||||||
prompt_dict_path = self.prompt_template_path + ".json"
|
|
||||||
with open(template_text_path, "r") as fp:
|
|
||||||
prompt_template = fp.read()
|
prompt_template = fp.read()
|
||||||
with open(prompt_dict_path, "r") as fp:
|
|
||||||
prompt_dict = json.load(fp)
|
|
||||||
|
|
||||||
return prompt_template, prompt_dict
|
return prompt_template
|
||||||
|
|
||||||
def build_prompt(self, input_data:dict) -> str:
|
def build_prompt(self, input_data:dict) -> str:
|
||||||
self.check_input(input_data)
|
verified_input = self.prompt_input_class(**input_data)
|
||||||
build_template = self.prompt_template
|
build_template = self.prompt_template
|
||||||
|
build_template = build_template.format(**verified_input.model_dump())
|
||||||
logger.debug(f"build_template: {build_template}")
|
logger.debug(f"build_template: {build_template}")
|
||||||
logger.debug(f"input_data: {input_data}")
|
logger.debug(f"input_data: {input_data}")
|
||||||
build_template = build_template.format(**input_data)
|
|
||||||
return build_template
|
return build_template
|
||||||
|
|
||||||
def check_input(self, input_data:dict) -> bool:
|
|
||||||
missing_variables = input_data.keys() - set(self.prompt_input)
|
|
||||||
if missing_variables:
|
|
||||||
raise Exception(f"missing_variable for prompt {self.prompt_name} : {missing_variables}")
|
|
||||||
|
|
||||||
flooding_variables = set(self.prompt_input) - input_data.keys()
|
|
||||||
if flooding_variables:
|
|
||||||
raise Exception(f"flooding_variables for prompt {self.prompt_name} : {flooding_variables}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
marketing_prompt = Prompt(
|
marketing_prompt = Prompt(
|
||||||
prompt_name=prompt_settings.MARKETING_PROMPT_NAME,
|
prompt_template_path = os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.MARKETING_PROMPT_FILE_NAME),
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.MARKETING_PROMPT_NAME)
|
prompt_input_class = MarketingPromptInput,
|
||||||
)
|
prompt_output_class = MarketingPromptOutput,
|
||||||
|
prompt_model = prompt_settings.MARKETING_PROMPT_MODEL
|
||||||
summarize_prompt = Prompt(
|
|
||||||
prompt_name=prompt_settings.SUMMARIZE_PROMPT_NAME,
|
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.SUMMARIZE_PROMPT_NAME)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
lyric_prompt = Prompt(
|
lyric_prompt = Prompt(
|
||||||
prompt_name=prompt_settings.LYLIC_PROMPT_NAME,
|
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.LYRIC_PROMPT_FILE_NAME),
|
||||||
prompt_template_path=os.path.join(prompt_settings.PROMPT_FOLDER_ROOT, prompt_settings.LYLIC_PROMPT_NAME)
|
prompt_input_class = LyricPromptInput,
|
||||||
|
prompt_output_class = LyricPromptOutput,
|
||||||
|
prompt_model = prompt_settings.LYRIC_PROMPT_MODEL
|
||||||
)
|
)
|
||||||
|
|
||||||
def reload_all_prompt():
|
def reload_all_prompt():
|
||||||
marketing_prompt._reload_prompt()
|
marketing_prompt._reload_prompt()
|
||||||
summarize_prompt._reload_prompt()
|
|
||||||
lyric_prompt._reload_prompt()
|
lyric_prompt._reload_prompt()
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .lyric import LyricPromptInput, LyricPromptOutput
|
||||||
|
from .marketing import MarketingPromptInput, MarketingPromptOutput
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
# Input 정의
|
||||||
|
class LyricPromptInput(BaseModel):
|
||||||
|
customer_name : str = Field(..., description = "마케팅 대상 사업체 이름")
|
||||||
|
region : str = Field(..., description = "마케팅 대상 지역")
|
||||||
|
detail_region_info : str = Field(..., description = "마케팅 대상 지역 상세")
|
||||||
|
marketing_intelligence_summary : str = Field(..., description = "마케팅 분석 정보 보고서")
|
||||||
|
language : str= Field(..., description = "가사 언어")
|
||||||
|
promotional_expression_example : str = Field(..., description = "판촉 가사 표현 예시")
|
||||||
|
timing_rules : str = Field(..., description = "시간 제어문")
|
||||||
|
|
||||||
|
# Output 정의
|
||||||
|
class LyricPromptOutput(BaseModel):
|
||||||
|
lyric: str = Field(..., description="생성된 가사")
|
||||||
|
suno_prompt: str = Field(..., description="Suno AI용 프롬프트")
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
# Input 정의
|
||||||
|
class MarketingPromptInput(BaseModel):
|
||||||
|
customer_name : str
|
||||||
|
region : str
|
||||||
|
detail_region_info : str
|
||||||
|
|
||||||
|
|
||||||
|
# Output 정의
|
||||||
|
class BrandIdentity(BaseModel):
|
||||||
|
location_feature_analysis: str = Field(..., description="입지 특성 분석")
|
||||||
|
concept_scalability: str = Field(..., description="컨셉 확장성")
|
||||||
|
|
||||||
|
|
||||||
|
class MarketPositioning(BaseModel):
|
||||||
|
category_definition: str = Field(..., description="마케팅 카테고리")
|
||||||
|
core_value: str = Field(..., description="마케팅 포지션 핵심 가치")
|
||||||
|
|
||||||
|
class AgeRange(BaseModel):
|
||||||
|
min_age : int = Field(..., ge=0, le=100)
|
||||||
|
max_age : int = Field(..., ge=0, le=100)
|
||||||
|
|
||||||
|
|
||||||
|
class TargetPersona(BaseModel):
|
||||||
|
persona: str = Field(..., description="타겟 페르소나 이름/설명")
|
||||||
|
age: AgeRange
|
||||||
|
favor_target: List[str] = Field(..., description="페르소나의 선호 요소")
|
||||||
|
decision_trigger: str = Field(..., description="구매 결정 트리거")
|
||||||
|
|
||||||
|
|
||||||
|
class SellingPoint(BaseModel):
|
||||||
|
category: str = Field(..., description="셀링포인트 카테고리")
|
||||||
|
description: str = Field(..., description="상세 설명")
|
||||||
|
score: int = Field(..., ge=0, le=100, description="점수 (100점 만점)")
|
||||||
|
|
||||||
|
class MarketingPromptOutput(BaseModel):
|
||||||
|
brand_identity: BrandIdentity
|
||||||
|
market_positioning: MarketPositioning
|
||||||
|
target_persona: List[TargetPersona]
|
||||||
|
selling_points: List[SellingPoint]
|
||||||
|
target_keywords: List[str] = Field(..., description="타겟 키워드 리스트")
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"prompt_variables": [
|
|
||||||
"report",
|
|
||||||
"selling_points"
|
|
||||||
],
|
|
||||||
"output_format": {
|
|
||||||
"format": {
|
|
||||||
"type": "json_schema",
|
|
||||||
"name": "tags",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tag_keywords": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"category",
|
|
||||||
"tag_keywords",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
|
|
||||||
입력 :
|
|
||||||
분석 보고서
|
|
||||||
{report}
|
|
||||||
|
|
||||||
셀링 포인트
|
|
||||||
{selling_points}
|
|
||||||
|
|
||||||
위 분석 결과를 바탕으로, 주요 셀링 포인트를 다음 구조로 재정리하라.
|
|
||||||
|
|
||||||
조건:
|
|
||||||
각 셀링 포인트는 반드시 ‘카테고리 → 태그 키워드 → 한 줄 설명’ 구조를 가질 것
|
|
||||||
태그 키워드는 UI 상에서 타원(oval) 형태의 시각적 태그로 사용될 것을 가정하여
|
|
||||||
- 3 ~ 6단어 이내
|
|
||||||
- 명사 또는 명사형 키워드로 작성
|
|
||||||
- 설명은 문장이 아닌, 짧은 ‘셀링 문구’ 형태로 작성할 것
|
|
||||||
- 광고·숏폼·상세페이지 어디에도 바로 재사용 가능해야 함
|
|
||||||
- 전체 셀링 포인트 개수는 5~7개로 제한
|
|
||||||
|
|
||||||
출력 형식:
|
|
||||||
[카테고리명]
|
|
||||||
(태그 키워드)
|
|
||||||
- 한 줄 설명 문구
|
|
||||||
|
|
||||||
예시:
|
|
||||||
[공간 정체성]
|
|
||||||
(100년 적산가옥 · 시간의 결)
|
|
||||||
- 하루를 ‘숙박’이 아닌 ‘체류’로 바꾸는 공간
|
|
||||||
|
|
||||||
[입지 & 희소성]
|
|
||||||
(말랭이마을 · 로컬 히든플레이스)
|
|
||||||
- 관광지가 아닌, 군산을 아는 사람의 선택
|
|
||||||
|
|
||||||
[프라이버시]
|
|
||||||
(독채 숙소 · 프라이빗 스테이)
|
|
||||||
- 누구의 방해도 없는 완전한 휴식 구조
|
|
||||||
|
|
||||||
[비주얼 경쟁력]
|
|
||||||
(감성 인테리어 · 자연광 스폿)
|
|
||||||
- 찍는 순간 콘텐츠가 되는 공간 설계
|
|
||||||
|
|
||||||
[타깃 최적화]
|
|
||||||
(커플 · 소규모 여행)
|
|
||||||
- 둘에게 가장 이상적인 공간 밀도
|
|
||||||
|
|
||||||
[체류 경험]
|
|
||||||
(아무것도 안 해도 되는 하루)
|
|
||||||
- 일정 없이도 만족되는 하루 루틴
|
|
||||||
|
|
||||||
[브랜드 포지션]
|
|
||||||
(호텔도 펜션도 아닌 아지트)
|
|
||||||
- 다시 돌아오고 싶은 개인적 장소
|
|
||||||
|
|
||||||
11
config.py
11
config.py
|
|
@ -146,10 +146,13 @@ class CreatomateSettings(BaseSettings):
|
||||||
model_config = _base_config
|
model_config = _base_config
|
||||||
|
|
||||||
class PromptSettings(BaseSettings):
|
class PromptSettings(BaseSettings):
|
||||||
PROMPT_FOLDER_ROOT : str = Field(default="./app/utils/prompts")
|
PROMPT_FOLDER_ROOT : str = Field(default="./app/utils/prompts/templates")
|
||||||
MARKETING_PROMPT_NAME : str = Field(default="marketing_prompt")
|
|
||||||
SUMMARIZE_PROMPT_NAME : str = Field(default="summarize_prompt")
|
MARKETING_PROMPT_FILE_NAME : str = Field(default="marketing_prompt.txt")
|
||||||
LYLIC_PROMPT_NAME : str = Field(default="lyric_prompt")
|
MARKETING_PROMPT_MODEL : str = Field(default="gpt-5.2")
|
||||||
|
|
||||||
|
LYRIC_PROMPT_FILE_NAME : str = Field(default="lyric_prompt.txt")
|
||||||
|
LYRIC_PROMPT_MODEL : str = Field(default="gpt-5-mini")
|
||||||
|
|
||||||
model_config = _base_config
|
model_config = _base_config
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue