2026-07-23 15:04:16 +08:00
|
|
|
|
"""文案生成器 — 模板模式 + AI 模式双模式。"""
|
|
|
|
|
|
import re
|
|
|
|
|
|
import json
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CopyGenerator:
|
|
|
|
|
|
"""文案生成器。"""
|
|
|
|
|
|
|
|
|
|
|
|
def generate_template_copy(self, template_content: str, product_rules: dict, customer_data: dict) -> dict:
|
|
|
|
|
|
"""模板模式:本地变量替换。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
template_content: 含 {{变量}} 占位符的文案模板
|
|
|
|
|
|
product_rules: 产品规则(manual_parsed_rules)
|
|
|
|
|
|
customer_data: 客户数据(confirmed_data)
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
{"headline": "...", "body": "...", "call_to_action": "..."}
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 构建变量映射
|
|
|
|
|
|
variables = {}
|
|
|
|
|
|
# 产品信息
|
|
|
|
|
|
if product_rules:
|
2026-07-29 12:19:26 +08:00
|
|
|
|
for key, value in product_rules.items():
|
|
|
|
|
|
if isinstance(value, (str, int, float)):
|
|
|
|
|
|
variables[key] = str(value)
|
2026-07-23 15:04:16 +08:00
|
|
|
|
features = product_rules.get("features", [])
|
|
|
|
|
|
for i, feat in enumerate(features[:3], 1):
|
|
|
|
|
|
variables[f"feature_{i}_title"] = feat.get("title", "")
|
|
|
|
|
|
variables[f"feature_{i}_summary"] = feat.get("summary", "")
|
|
|
|
|
|
variables["currency_options"] = ", ".join(product_rules.get("currency_options", []))
|
|
|
|
|
|
|
|
|
|
|
|
# 客户数据
|
|
|
|
|
|
if customer_data:
|
2026-07-29 12:19:26 +08:00
|
|
|
|
for key, value in customer_data.items():
|
|
|
|
|
|
if isinstance(value, (str, int, float)):
|
|
|
|
|
|
variables[key] = str(value)
|
2026-07-23 15:04:16 +08:00
|
|
|
|
|
|
|
|
|
|
# 替换占位符
|
|
|
|
|
|
result = template_content
|
|
|
|
|
|
for key, value in variables.items():
|
|
|
|
|
|
result = result.replace("{{" + key + "}}", value)
|
|
|
|
|
|
|
|
|
|
|
|
# 尝试按段落拆分为 headline/body/call_to_action
|
|
|
|
|
|
lines = [l.strip() for l in result.split("\n") if l.strip()]
|
|
|
|
|
|
return {
|
|
|
|
|
|
"headline": lines[0] if lines else "",
|
|
|
|
|
|
"body": "\n".join(lines[1:-1]) if len(lines) > 2 else lines[1] if len(lines) > 1 else "",
|
|
|
|
|
|
"call_to_action": lines[-1] if len(lines) > 1 else "",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async def generate_ai_copy(self, product_rules: dict, customer_data: dict, style: str = "专业") -> dict:
|
|
|
|
|
|
"""AI 模式:调用 LLM 生成营销文案。
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
{"headline": "...", "body": "...", "call_to_action": "..."}
|
|
|
|
|
|
"""
|
2026-07-27 13:21:34 +08:00
|
|
|
|
from insurance.ppt.llm_client import poster_llm_client
|
2026-07-23 15:04:16 +08:00
|
|
|
|
|
|
|
|
|
|
system_prompt = f"""你是一位专业的保险营销文案撰写人。
|
|
|
|
|
|
根据以下产品信息和客户数据,生成一张保险营销海报的文案。
|
|
|
|
|
|
|
|
|
|
|
|
要求:
|
|
|
|
|
|
1. 标题(headline):简短有力,8字以内
|
|
|
|
|
|
2. 正文(body):突出产品亮点与客户需求的匹配,50-100字
|
|
|
|
|
|
3. 行动号召(call_to_action):引导客户咨询,15字以内
|
|
|
|
|
|
4. 风格:{style}
|
|
|
|
|
|
5. 必须基于真实数据,不得虚构收益数字
|
|
|
|
|
|
|
|
|
|
|
|
以 JSON 格式返回(不要包含 markdown 代码块标记)。"""
|
|
|
|
|
|
|
|
|
|
|
|
user_prompt = f"""
|
|
|
|
|
|
产品信息:{json.dumps(product_rules, ensure_ascii=False)}
|
|
|
|
|
|
客户数据:{json.dumps(customer_data, ensure_ascii=False)}"""
|
|
|
|
|
|
|
2026-07-27 13:21:34 +08:00
|
|
|
|
result, _response = await poster_llm_client.structured_output(
|
2026-07-23 15:04:16 +08:00
|
|
|
|
user_prompt, system_prompt,
|
|
|
|
|
|
schema={
|
|
|
|
|
|
"type": "object",
|
|
|
|
|
|
"properties": {
|
|
|
|
|
|
"headline": {"type": "string"},
|
|
|
|
|
|
"body": {"type": "string"},
|
|
|
|
|
|
"call_to_action": {"type": "string"},
|
|
|
|
|
|
},
|
|
|
|
|
|
"required": ["headline", "body", "call_to_action"],
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
return result
|