baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
109
backend/app/services/ai_engine/prompt_composer.py
Normal file
109
backend/app/services/ai_engine/prompt_composer.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
prompt_composer.py — 统一 prompt 组装入口(≤100行)
|
||||
扒自:banana prompts/service.py + worker/src/copy.js prompt 逻辑
|
||||
Lead 指名接口:compose_variants / compose_preference_context
|
||||
|
||||
组装逻辑委托:
|
||||
_text_prompt.py → build_prompt (文案 prompt 主体)
|
||||
preference_aggregator.py → aggregate_preference_context (飞轮上下文)
|
||||
|
||||
原则:prompt 组装从这里进,不散落在 text_variants / generate_text_variants 里。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from ._text_prompt import build_prompt, COPY_SYSTEM
|
||||
from .preference_aggregator import aggregate_preference_context
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ── 主接口 ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def compose_variants(
|
||||
product: dict,
|
||||
count: int,
|
||||
flywheel_context: str = "",
|
||||
extra_rules: str = "",
|
||||
) -> tuple[str, str]:
|
||||
"""
|
||||
一次出 count 角度文案的完整 prompt。
|
||||
|
||||
返回 (system_prompt, user_prompt)。
|
||||
飞轮片段追加到 user_prompt 末尾(不改 system,避免覆盖质量红线)。
|
||||
|
||||
参数:
|
||||
product — 产品档案 dict(name/selling_points/text_angles/custom_prompt 等)
|
||||
count — 需要几条
|
||||
flywheel_context— 由 compose_preference_context 返回的 prompt_fragment
|
||||
extra_rules — 额外规则(优化循环重生成时传 hint)
|
||||
"""
|
||||
combined_extra = "\n".join(filter(None, [flywheel_context, extra_rules]))
|
||||
user_prompt = build_prompt(product, count, extra_rules=combined_extra)
|
||||
logger.debug(
|
||||
"compose_variants: product=%s count=%d flywheel_len=%d",
|
||||
product.get("name", "?"), count, len(flywheel_context),
|
||||
)
|
||||
return COPY_SYSTEM, user_prompt
|
||||
|
||||
|
||||
def compose_preference_context(
|
||||
events: list[dict],
|
||||
product: dict,
|
||||
workspace_id: int,
|
||||
product_id: int,
|
||||
) -> dict:
|
||||
"""
|
||||
聚合偏好事件 → 可注入 prompt 的飞轮上下文。
|
||||
|
||||
返回结构(对齐 API契约 GET /tasks/{id}/preference/context):
|
||||
{
|
||||
recent_preference: str, # 人类可读摘要(前端"本次已注入"显示)
|
||||
reject_reasons: list, # 最近打回原因
|
||||
injected_count: int, # 有效信号数
|
||||
prompt_fragment: str, # 注入 compose_variants flywheel_context 的字符串
|
||||
}
|
||||
|
||||
信号不足 FLYWHEEL_COLD_START 条时用产品档案冷启动。
|
||||
按 workspace_id + product_id 双维过滤(素颜霜偏好不串精华)。
|
||||
"""
|
||||
return aggregate_preference_context(events, product, workspace_id, product_id)
|
||||
|
||||
|
||||
# ── 辅助:解析模型返回的 JSON(给 text_variants 调用,集中不散) ──────────────
|
||||
|
||||
def parse_model_output(raw: str) -> list[dict]:
|
||||
"""从 LLM 原始输出提取 JSON 数组(容错 markdown 包裹)"""
|
||||
from ._text_prompt import parse_json_array
|
||||
return parse_json_array(raw)
|
||||
|
||||
|
||||
# ── 辅助:图片 prompt 组装入口(预留,联调时填充)─────────────────────────────
|
||||
|
||||
def compose_image_prompt(
|
||||
role_name: str,
|
||||
visual_system: dict,
|
||||
product: dict,
|
||||
extra: str = "",
|
||||
) -> str:
|
||||
"""
|
||||
单张分镜 prompt 组装(供 image_gen.generate_one_image 调用)。
|
||||
TODO: 联调后从 storyboard.plan_image_set 取 base_prompt 注入。
|
||||
|
||||
role_name — 分镜角色(hook / pain_scene / closer 等)
|
||||
visual_system— build_visual_system 返回的视觉系统 dict
|
||||
extra — 追加约束(飞轮图片偏好片段,二期接入)
|
||||
"""
|
||||
name = product.get("name", "产品")
|
||||
style = visual_system.get("style", "")
|
||||
palette = visual_system.get("color_palette", "")
|
||||
base = visual_system.get("base_prompt", "")
|
||||
lines = [
|
||||
f"[{role_name}] 为产品「{name}」生成种草图。",
|
||||
base and f"视觉基调:{base}",
|
||||
style and f"摄影风格:{style}",
|
||||
palette and f"色调:{palette}",
|
||||
extra,
|
||||
]
|
||||
return "\n".join(l for l in lines if l)
|
||||
Reference in New Issue
Block a user