迁移系统加固 — 加 advisory lock 防并发,失败回滚并中止启动(原来是 catch-and-continue) 安全漏洞 — Settings/History API 权限收紧,API key 返回掩码值,海报 case 所有权校验 依赖缺失 — requirements.txt 补齐 python-pptx/openai/Pillow,Dockerfile 改为统一安装 海报鉴权下载 — 前端全部改用 authenticated blob,不再 window.open 无 token URL LLM 配置分离 — 海报文案读取 poster_llm_*(不再复用 ppt_llm_*),支持 config namespace 图片生成器 — 兼容 b64_json 和 URL 两种响应格式,追踪 generation_mode/provider/model 种子数据 — 新环境自动获得 2 个海报模板 + 2 个文案模板
92 lines
3.6 KiB
Python
92 lines
3.6 KiB
Python
"""文案生成器 — 模板模式 + 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:
|
||
variables["product_name"] = product_rules.get("product_name", "")
|
||
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:
|
||
variables["age"] = str(customer_data.get("age", ""))
|
||
variables["currency"] = customer_data.get("currency", "")
|
||
variables["sum_assured"] = str(customer_data.get("sum_assured", ""))
|
||
variables["annual_premium"] = str(customer_data.get("annual_premium", ""))
|
||
variables["coverage_period"] = customer_data.get("coverage_period", "")
|
||
|
||
# 替换占位符
|
||
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": "..."}
|
||
"""
|
||
from insurance.ppt.llm_client import poster_llm_client
|
||
|
||
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)}"""
|
||
|
||
result, _response = await poster_llm_client.structured_output(
|
||
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
|