Claude API 호출 시 시스템 프롬프트에 현재 날짜를 포함하여 2024년이 아닌 실제 날짜 기준으로 콘텐츠가 생성되도록 수정. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
3.1 KiB
Python
106 lines
3.1 KiB
Python
"""마케터 단계 — 전환율 강화 + 브랜드커넥트 링크 삽입."""
|
|
|
|
import json
|
|
import logging
|
|
from datetime import date
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
import anthropic
|
|
|
|
from .config import ANTHROPIC_API_KEY, CLAUDE_MODEL
|
|
from .db import get_template
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_client: Optional[anthropic.Anthropic] = None
|
|
|
|
|
|
def _get_client() -> anthropic.Anthropic:
|
|
global _client
|
|
if _client is None:
|
|
_client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
|
|
return _client
|
|
|
|
|
|
def _call_claude(prompt: str, max_tokens: int = 8192) -> str:
|
|
client = _get_client()
|
|
today = date.today().isoformat()
|
|
resp = client.messages.create(
|
|
model=CLAUDE_MODEL,
|
|
max_tokens=max_tokens,
|
|
system=f"현재 날짜는 {today}입니다. 모든 콘텐츠는 이 날짜 기준으로 작성하세요.",
|
|
messages=[{"role": "user", "content": prompt}],
|
|
)
|
|
return resp.content[0].text
|
|
|
|
|
|
def enhance_for_conversion(
|
|
post_body: str,
|
|
post_title: str,
|
|
brand_links: List[Dict[str, Any]],
|
|
keyword: str,
|
|
) -> Dict[str, str]:
|
|
"""초안에 제휴 링크를 자연스럽게 삽입하고 전환율을 강화.
|
|
|
|
Args:
|
|
post_body: 작가 초안 HTML 본문
|
|
post_title: 작가 초안 제목
|
|
brand_links: 브랜드커넥트 링크 리스트
|
|
keyword: 타겟 키워드
|
|
|
|
Returns:
|
|
{"title": str, "body": str, "excerpt": str}
|
|
|
|
Raises:
|
|
ValueError: 브랜드 링크가 없을 때
|
|
"""
|
|
if not brand_links:
|
|
raise ValueError("브랜드커넥트 링크가 필요합니다")
|
|
|
|
template = get_template("marketer_enhance")
|
|
if not template:
|
|
raise RuntimeError("marketer_enhance 템플릿이 없습니다")
|
|
|
|
brand_links_text = ""
|
|
for i, link in enumerate(brand_links, 1):
|
|
brand_links_text += (
|
|
f"{i}. 상품명: {link.get('product_name', '')}\n"
|
|
f" 설명: {link.get('description', '')}\n"
|
|
f" URL: {link.get('url', '')}\n"
|
|
f" 배치 힌트: {link.get('placement_hint', '자연스럽게')}\n\n"
|
|
)
|
|
|
|
prompt = template.format(
|
|
draft_body=post_body[:6000],
|
|
keyword=keyword,
|
|
brand_links_info=brand_links_text,
|
|
)
|
|
|
|
prompt += (
|
|
"\n\n---\n"
|
|
"응답은 반드시 아래 JSON 형식으로 해주세요 (JSON만 출력):\n"
|
|
'{"title": "개선된 제목", "body": "개선된 HTML 본문", "excerpt": "2줄 요약"}'
|
|
)
|
|
|
|
raw = _call_claude(prompt)
|
|
|
|
try:
|
|
text = raw.strip()
|
|
if text.startswith("```"):
|
|
lines = text.split("\n")
|
|
lines = [l for l in lines if not l.strip().startswith("```")]
|
|
text = "\n".join(lines)
|
|
result = json.loads(text)
|
|
return {
|
|
"title": result.get("title", post_title),
|
|
"body": result.get("body", post_body),
|
|
"excerpt": result.get("excerpt", ""),
|
|
}
|
|
except (json.JSONDecodeError, KeyError):
|
|
logger.warning("Marketer JSON parse failed, using raw text")
|
|
return {
|
|
"title": post_title,
|
|
"body": raw,
|
|
"excerpt": raw[:200],
|
|
}
|