Files
beige/backend/app/services/ai_engine/benchmark_analyzer.py
yangqianqian c1ac2a6ab9 feat(M6): 第2环标杆8维分析引擎
8维草案(banana不变量+俊达配方+3链接):标题公式/开头钩子/正文结构/
卖点呈现/情绪语气emoji/目标人群/话题标签/视觉配图节奏。
- _benchmark_prompt.py: 8维vision prompt(不打分,提炼可复用配方)
- benchmark_analyzer.py: 引擎核心(vision读截图+手填亮点→8维JSON,失败兜底)
- benchmarks.py: POST /benchmarks/{id}/analyze 端点(写features_json,流转analyze_status)
端到端实测:8维齐全0缺失,真调LLM(conf0.9非兜底),落库done,质量高(识别收藏钩子/成分背书/诚实标未体现)
2026-06-16 14:28:17 +08:00

67 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
app/services/ai_engine/benchmark_analyzer.py — 第2环 标杆8维分析引擎
复用 products.analyze_product_image 的 vision 范式,解耦成 service。
输入:爆款截图 bytes + 手填亮点输出8维配方 JSON写入 benchmark_notes.features_json
key 链路遵基石Bplain_key 只活在调用方局部,本模块只收已构建的 clients。
"""
import json
import logging
import re
from app.services.ai_engine._benchmark_prompt import BENCHMARK_8DIM_PROMPT
logger = logging.getLogger(__name__)
# 8维字段顺序与 prompt JSON key 一致analyze_status 流转 done 时校验齐全)
DIM_KEYS = [
"title_formula", "opening_hook", "content_structure", "selling_point_style",
"emotion_tone", "target_audience", "topic_tags", "visual_rhythm",
]
def parse_benchmark_json(raw: str) -> dict:
"""从模型返回文本提取 JSON容错 markdown ```json 包裹)。"""
cleaned = re.sub(r"```json\s*", "", raw)
cleaned = re.sub(r"```\s*", "", cleaned)
m = re.search(r"\{[\s\S]*\}", cleaned)
if not m:
raise ValueError("标杆分析返回中未找到 JSON")
return json.loads(m.group())
def fallback_benchmark(highlights: str | None = None) -> dict:
"""vision 失败兜底:保证 8 维不空手,标 source=fallback 供前端提示。"""
note = highlights or "截图未体现"
return {
"title_formula": note, "opening_hook": note, "content_structure": note,
"selling_point_style": note, "emotion_tone": note, "target_audience": note,
"topic_tags": note, "visual_rhythm": note,
"confidence": 0.4, "source": "fallback",
}
async def analyze_benchmark(clients, screenshot: bytes | None, highlights: str | None) -> dict:
"""
跑 8 维分析。截图走 vision无截图但有手填亮点则把亮点拼进 prompt 兜底。
返回结构化 8 维 dict含 confidence/source。失败不抛返回 fallback。
"""
prompt = BENCHMARK_8DIM_PROMPT
if highlights:
prompt = f"{prompt}\n\n运营补充的亮点描述(作为分析参考):{highlights}"
images = [screenshot] if screenshot else []
try:
if not images:
# 无截图纯文本兜底gpt_vision_analyze 支持空图走文本)
logger.info("analyze_benchmark: 无截图,走手填亮点文本分析")
raw = await clients.gpt_vision_analyze(prompt, images)
result = parse_benchmark_json(raw)
result["source"] = "vision" if images else "text"
return result
except Exception as e:
logger.warning("analyze_benchmark failed, fallback: %s", e)
result = fallback_benchmark(highlights)
result["warning"] = f"标杆分析失败,已兜底:{e}"
return result