Files
YuShiSheJiShi/backend/app/services/prompt_builder.py
2ef126e445 feat: 强化多视角图片一致性 + 修复下载逻辑 + 技术文档
- 新增品类专属背面/侧面描述(BACK_VIEW_HINTS/SIDE_VIEW_HINTS)
- 强化一致性前缀策略,按视角定制相机位置描述
- 更新视角映射提示词为纯摄影术语
- 修复前端下载逻辑:改用fetch直接下载当前视角图片
- HTTPS改HTTP修复外网URL访问
- 新增多视角一致性与3D视频生成技术文档
2026-03-28 19:51:08 +08:00

207 lines
7.5 KiB
Python

"""
专业玉雕设计提示词构建器(数据库版)
从数据库 prompt_templates + prompt_mappings 读取配置,支持后台热更新
"""
import logging
from typing import Optional, Dict, List
from ..database import SessionLocal
from ..models.prompt_template import PromptTemplate, PromptMapping
logger = logging.getLogger(__name__)
# ============================================================
# 品类视角配置(保留硬编码,因为与业务流程强关联)
# ============================================================
CATEGORY_VIEWS: Dict[str, List[str]] = {
"牌子": ["效果图", "正面图", "背面图"],
"珠子": ["效果图", "正面图"],
"手把件": ["效果图", "正面图", "侧面图", "背面图"],
"雕刻件": ["效果图", "正面图", "侧面图", "背面图"],
"摆件": ["效果图", "正面图", "侧面图", "背面图"],
"手镯": ["效果图", "正面图", "侧面图"],
"耳钉": ["效果图", "正面图"],
"耳饰": ["效果图", "正面图"],
"手链": ["效果图", "正面图"],
"项链": ["效果图", "正面图"],
"戒指": ["效果图", "正面图", "侧面图"],
"表带": ["效果图", "正面图"],
"随形": ["效果图", "正面图", "侧面图", "背面图"],
}
# ============================================================
# 品类专属背面描述(不同品类的背面特征差异很大)
# ============================================================
BACK_VIEW_HINTS: Dict[str, str] = {
"牌子": (
"IMPORTANT: The reverse/back side of a jade pendant plaque is traditionally a smooth, flat, polished surface. "
"It may have a brief inscription or seal mark, but it must NOT have any carved figure, face, or decorative relief pattern. "
"The back is plain and minimalist. Do NOT mirror or duplicate the front carving on the back."
),
"手把件": (
"The back of this hand-held jade piece continues the same sculptural form as the front. "
"It is part of the same three-dimensional object, showing the natural continuation of the carving from the rear angle."
),
"雕刻件": (
"The back of this carved jade piece shows the rear of the same three-dimensional sculpture. "
"The carving continues around the object naturally, not a separate or different design."
),
"摆件": (
"The back of this jade display piece shows the rear of the same three-dimensional artwork. "
"The form and carving continue naturally around the object."
),
"随形": (
"The back of this free-form jade piece shows the natural stone surface from the rear. "
"The organic shape continues naturally, the back may show more of the raw jade texture."
),
}
# 品类专属侧面描述
SIDE_VIEW_HINTS: Dict[str, str] = {
"牌子": (
"The side/edge view of a jade pendant plaque shows its thin, flat profile. "
"The plaque is typically 5-10mm thick, showing the edge thickness and any subtle edge carving."
),
}
def _load_mappings(mapping_type: str) -> Dict[str, str]:
"""从数据库加载指定类型的映射字典"""
try:
db = SessionLocal()
try:
rows = db.query(PromptMapping).filter(
PromptMapping.mapping_type == mapping_type
).order_by(PromptMapping.sort_order).all()
return {r.cn_key: r.en_value for r in rows}
finally:
db.close()
except Exception as e:
logger.warning(f"加载映射 {mapping_type} 失败: {e}")
return {}
def _load_template(template_key: str, default: str = "") -> str:
"""从数据库加载模板"""
try:
db = SessionLocal()
try:
tpl = db.query(PromptTemplate).filter(
PromptTemplate.template_key == template_key
).first()
if tpl:
return tpl.template_value
finally:
db.close()
except Exception as e:
logger.warning(f"加载模板 {template_key} 失败: {e}")
return default
def get_views_for_category(category_name: str) -> List[str]:
"""获取品类对应的视角列表"""
return CATEGORY_VIEWS.get(category_name, ["效果图", "正面图"])
def build_prompt(
category_name: str,
view_name: str,
sub_type_name: Optional[str] = None,
color_name: Optional[str] = None,
user_prompt: Optional[str] = None,
carving_technique: Optional[str] = None,
design_style: Optional[str] = None,
motif: Optional[str] = None,
size_spec: Optional[str] = None,
surface_finish: Optional[str] = None,
usage_scene: Optional[str] = None,
) -> str:
"""
构建专业英文生图提示词(从数据库读取映射和模板)
业务逻辑:用户参数 → 中英映射 → 填入模板 → 最终prompt
"""
# 从数据库加载所有映射
category_map = _load_mappings("category")
color_map = _load_mappings("color")
view_map = _load_mappings("view")
carving_map = _load_mappings("carving")
style_map = _load_mappings("style")
motif_map = _load_mappings("motif")
finish_map = _load_mappings("finish")
scene_map = _load_mappings("scene")
sub_type_map = _load_mappings("sub_type")
# 加载模板
quality_suffix = _load_template("quality_suffix",
"professional jewelry product photography, studio lighting setup, pure white background, ultra-detailed, sharp focus, 8K resolution, photorealistic rendering, high-end commercial quality")
default_color = _load_template("default_color",
"natural Hetian nephrite jade with warm luster")
# 构建各部分
parts = []
# 1. 品类主体
subject = category_map.get(category_name, f"Chinese Hetian nephrite jade {category_name}")
parts.append(subject)
# 2. 子类型
if sub_type_name:
sub_detail = sub_type_map.get(sub_type_name, sub_type_name)
parts.append(sub_detail)
# 3. 颜色
if color_name:
color_desc = color_map.get(color_name, f"{color_name} colored nephrite jade")
parts.append(color_desc)
else:
parts.append(default_color)
# 4. 题材纹样
if motif:
motif_desc = motif_map.get(motif, f"{motif} themed design")
parts.append(f"featuring {motif_desc}")
# 5. 雕刻工艺
if carving_technique:
tech_desc = carving_map.get(carving_technique, carving_technique)
parts.append(tech_desc)
# 6. 设计风格
if design_style:
style_desc = style_map.get(design_style, design_style)
parts.append(style_desc)
# 7. 表面处理
if surface_finish:
finish_desc = finish_map.get(surface_finish, surface_finish)
parts.append(finish_desc)
# 8. 用途场景
if usage_scene:
scene_desc = scene_map.get(usage_scene, usage_scene)
parts.append(scene_desc)
# 9. 尺寸
if size_spec:
parts.append(f"size approximately {size_spec}")
# 10. 用户描述
if user_prompt:
parts.append(f"design concept: {user_prompt}")
# 11. 视角
view_desc = view_map.get(view_name, "three-quarter view")
parts.append(view_desc)
# 12. 品类专属视角描述(背面/侧面特征)
if view_name == "背面图" and category_name in BACK_VIEW_HINTS:
parts.append(BACK_VIEW_HINTS[category_name])
elif view_name == "侧面图" and category_name in SIDE_VIEW_HINTS:
parts.append(SIDE_VIEW_HINTS[category_name])
# 13. 质量后缀
parts.append(quality_suffix)
return ", ".join(parts)