add brand consistency generation
parent
b6a0134ba7
commit
35e5e98524
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
from pydantic import BaseModel
|
||||
from common.utils import get_env
|
||||
from integrations.llm.schemas.report import ReportInput, ReportOutput, YouTubeDiagnosisInput, YouTubeDiagnosisOutput
|
||||
from integrations.llm.schemas.report import ReportInput, ReportOutput, YouTubeDiagnosisInput, YouTubeDiagnosisOutput, BrandConsistencyInput, BrandConsistencyOutput
|
||||
from integrations.llm.schemas.plan import PlanInput, PlanOutput
|
||||
from integrations.llm.schemas.market import (
|
||||
MarketCompetitorsInput, MarketCompetitorsOutput,
|
||||
|
|
@ -87,3 +87,10 @@ youtube_diagnosis_prompt = Prompt(
|
|||
input_class=YouTubeDiagnosisInput,
|
||||
output_class=YouTubeDiagnosisOutput,
|
||||
)
|
||||
|
||||
brand_consistency_prompt = Prompt(
|
||||
file_name="brand_consistency_prompt.txt",
|
||||
prompt_model="REPORT_MODEL",
|
||||
input_class=BrandConsistencyInput,
|
||||
output_class=BrandConsistencyOutput,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -355,3 +355,18 @@ class YouTubeDiagnosisInput(BaseModel):
|
|||
|
||||
class YouTubeDiagnosisOutput(BaseModel):
|
||||
diagnosis: list[DiagnosisItem]
|
||||
|
||||
|
||||
# --- BrandConsistency ---
|
||||
|
||||
class BrandConsistencyInput(BaseModel):
|
||||
clinic_name: str | None = None
|
||||
mainpage: str | None = None
|
||||
instagram: str | None = None
|
||||
facebook: str | None = None
|
||||
youtube: str | None = None
|
||||
gangnam_unni: str | None = None
|
||||
|
||||
|
||||
class BrandConsistencyOutput(BaseModel):
|
||||
brand_inconsistencies: list[BrandInconsistency]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
다음은 성형외과/피부과 {clinic_name} 의 채널별 브랜드 데이터입니다.
|
||||
|
||||
공식 홈페이지: {mainpage}
|
||||
인스타그램: {instagram}
|
||||
페이스북: {facebook}
|
||||
유튜브: {youtube}
|
||||
강남언니: {gangnam_unni}
|
||||
|
||||
위 채널들 간의 브랜드 불일치 항목을 분석해줘.
|
||||
비교 대상 필드 예시: 병원명(한글/영문), 전화번호, 주소, 로고, 슬로건, 소개 문구 등.
|
||||
|
||||
각 항목은 다음 JSON 형식의 배열로 출력해줘:
|
||||
- field: 불일치 필드명
|
||||
- values: 채널별 실제 값 목록 (channel, value, is_correct)
|
||||
- impact: 불일치가 브랜드에 미치는 영향
|
||||
- recommendation: 개선 권고사항
|
||||
|
||||
출처 번호([1], [2] 등)는 포함하지 마.
|
||||
|
|
@ -8,10 +8,9 @@ from common.db.run import select_run, update_run_report, update_run_plan
|
|||
from common.db.source import select_run_raw_data, select_run_mainpage_url
|
||||
from common.db.market import select_market
|
||||
from integrations.llm.llm_service import LLMService
|
||||
from integrations.llm.prompt import report_prompt, plan_prompt, youtube_diagnosis_prompt
|
||||
from integrations.llm.schemas.report import ReportOutput, ClinicSnapshot, YouTubeAudit
|
||||
from integrations.llm.prompt import report_prompt, plan_prompt, youtube_diagnosis_prompt, brand_consistency_prompt
|
||||
from integrations.llm.schemas.report import ReportOutput, ClinicSnapshot, YouTubeAudit, BrandConsistencyOutput
|
||||
from integrations.llm.schemas.plan import PlanOutput
|
||||
from models.status import AnalysisStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -233,15 +232,23 @@ async def _build_overrides(analysis_run_id: str) -> dict:
|
|||
if instagram.get("username"): ig_patch["profile_link"] = f"https://www.instagram.com/{instagram['username']}/"
|
||||
|
||||
# ── facebook ──────────────────────────────────────────────────────────────
|
||||
fb_patch: dict = {}
|
||||
if facebook.get("pageUrl"): fb_patch["url"] = facebook["pageUrl"]
|
||||
if facebook.get("pageUrl"): fb_patch["link"] = facebook["pageUrl"]
|
||||
if facebook.get("pageName"): fb_patch["page_name"] = facebook["pageName"]
|
||||
if facebook.get("followers"): fb_patch["followers"] = facebook["followers"]
|
||||
if facebook.get("intro"): fb_patch["bio"] = facebook["intro"]
|
||||
if facebook.get("categories"): fb_patch["category"] = ", ".join(facebook["categories"])
|
||||
if facebook.get("website"): fb_patch["linked_domain"] = facebook["website"]
|
||||
fb_pages: dict = {}
|
||||
if facebook.get("pageUrl"): fb_pages["url"] = facebook["pageUrl"]
|
||||
if facebook.get("pageUrl"): fb_pages["link"] = facebook["pageUrl"]
|
||||
if facebook.get("pageName"): fb_pages["page_name"] = facebook["pageName"]
|
||||
if facebook.get("followers"): fb_pages["followers"] = facebook["followers"]
|
||||
if facebook.get("intro"): fb_pages["bio"] = facebook["intro"]
|
||||
if facebook.get("categories"): fb_pages["category"] = ", ".join(facebook["categories"])
|
||||
if facebook.get("website"): fb_pages["linked_domain"] = facebook["website"]
|
||||
|
||||
brand = await generate_brand_consistency(analysis_run_id)
|
||||
brand_patch = brand.model_dump()["brand_inconsistencies"]
|
||||
|
||||
fb_patch: dict = {}
|
||||
if fb_pages:
|
||||
fb_patch["pages"] = [fb_pages]
|
||||
if brand_patch:
|
||||
fb_patch["brand_inconsistencies"] = brand_patch
|
||||
|
||||
overrides: dict = {}
|
||||
if snapshot:
|
||||
|
|
@ -249,7 +256,7 @@ async def _build_overrides(analysis_run_id: str) -> dict:
|
|||
if ig_patch:
|
||||
overrides["instagram_audit"] = {"accounts": [ig_patch]}
|
||||
if fb_patch:
|
||||
overrides["facebook_audit"] = {"pages": [fb_patch]}
|
||||
overrides["facebook_audit"] = fb_patch
|
||||
if yt_patch:
|
||||
overrides["youtube_audit"] = yt_patch
|
||||
return overrides
|
||||
|
|
@ -268,8 +275,10 @@ def _deep_merge(base: dict, overrides: dict) -> dict:
|
|||
return base
|
||||
|
||||
def _patch_report(result: ReportOutput, overrides: dict) -> ReportOutput:
|
||||
merged = _deep_merge(result.model_dump(), overrides)
|
||||
return ReportOutput(**merged)
|
||||
base = result.model_dump()
|
||||
for key in overrides:
|
||||
base.pop(key, None)
|
||||
return ReportOutput(**_deep_merge(base, overrides))
|
||||
|
||||
|
||||
_MOCK_DOMAINS = {"viewclinic.com"}
|
||||
|
|
@ -294,6 +303,24 @@ def _load_mock_plan() -> PlanOutput:
|
|||
return PlanOutput(**json.load(f))
|
||||
|
||||
|
||||
async def generate_brand_consistency(analysis_run_id: str) -> BrandConsistencyOutput:
|
||||
raw = await select_run_raw_data(analysis_run_id)
|
||||
|
||||
def _json(v) -> str | None:
|
||||
return json.dumps(v, ensure_ascii=False) if v else None
|
||||
|
||||
mainpage = raw.get("mainpage") or {}
|
||||
input_data = {
|
||||
"clinic_name": mainpage.get("clinicName"),
|
||||
"mainpage": _json(mainpage),
|
||||
"instagram": _json(raw.get("instagram")),
|
||||
"facebook": _json(raw.get("facebook")),
|
||||
"youtube": _json(raw.get("youtube")),
|
||||
"gangnam_unni": _json(raw.get("gangnam_unni")),
|
||||
}
|
||||
return await LLMService(provider="perplexity").generate(brand_consistency_prompt, input_data)
|
||||
|
||||
|
||||
async def run_report_task(analysis_run_id: str) -> None:
|
||||
logger.info("[report] start run=%s", analysis_run_id)
|
||||
if await _is_mock(analysis_run_id):
|
||||
|
|
@ -302,6 +329,7 @@ async def run_report_task(analysis_run_id: str) -> None:
|
|||
result.youtube_audit.linked_urls = []
|
||||
else:
|
||||
result = await generate_report(analysis_run_id)
|
||||
|
||||
result = _patch_report(result, await _build_overrides(analysis_run_id))
|
||||
await update_run_report(analysis_run_id, result.model_dump())
|
||||
logger.info("[report] done run=%s", analysis_run_id)
|
||||
|
|
|
|||
Loading…
Reference in New Issue