feat: 角色重跑参考当前立绘+提示词,保持人物一致 (row42)

原「重跑」立绘是纯文生图,每次重抽成另一个随机人。改为:角色已有当前立绘时,
重跑传该立绘作参考图 → 后端走 image_edit(参考立绘+提示词),保持同一人物相貌/发型/身份一致。

- generate_base_asset 新增 reference_asset_id;person 有参考图则 image_edit + build_person_portrait_prompt_refs
- run_base_asset_task image_edit size 按 kind 区分(person 立绘竖 1024x1536,商品三视图横)
- 全链路透传 reference_asset_id:pipeline.regenPortrait(传 viewPortraitAsset)→ genBaseAsset →
  onGenerateBaseAsset → api.generateBaseAsset → generate-base-asset 端点
场景重跑不受影响(仍文生图);需部署生效(后端)。

构建: tsc=0/build=0; py_compile OK; projects+assets 测试无新增回归(基线 4 fail 2 err)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-30 13:20:47 +08:00
co-authored by Claude Opus 4.8
parent 535e557728
commit 4d2b9343b3
5 changed files with 44 additions and 15 deletions
+32 -6
View File
@@ -1187,6 +1187,19 @@ def build_person_frontal_prompt(description: str = "") -> str:
return render_prompt("person_portrait", default, 描述=desc)
def build_person_portrait_prompt_refs(description: str = "") -> str:
"""角色立绘「重跑」refs 版:参考图=该角色当前立绘。保持同一人物的相貌/发型/身份不变,
只据提示词微调并重绘为正面全身、纯色背景的电商真人模特立绘 —— 避免重跑重抽成另一个人。"""
desc = (description or "").strip()
base = (
"参考图是该角色当前的立绘。保持参考图中人物的相貌、五官、发型、肤色与身份特征完全一致(同一个人),"
"重绘为电商真人模特氛围正面全身照,自然妆容,柔和影棚光,真实质感,单人,纯色背景。"
)
if desc:
base += f"在保持人物一致的前提下,按以下要求调整:{desc}"
return base
# --------------------------------------------------------------------------- #
# 出图重试(中转站偶发抖动 → 一次失败就「生成不出来」的根因兜底)
# --------------------------------------------------------------------------- #
@@ -1219,7 +1232,7 @@ def _run_image_with_retry(make, *, attempts: int = _IMAGE_GEN_ATTEMPTS):
time.sleep(2 * (i + 1))
def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "", group_id: str | None = None) -> AITask:
def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "", group_id: str | None = None, reference_asset_id: str | None = None) -> AITask:
"""提交基础资产生成(**异步**):Web 请求只建 RESERVED 任务 + 预留额度(秒级),
慢出图(文生图 / 商品 image_edit)交给 Celery worker(run_base_asset_task)跑。
@@ -1234,9 +1247,19 @@ def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "
# 商品三视图:有真实商品主图 → 走 image_edit 以主图为参考,锁定包装(品牌字/配色/外形/Logo)一致;
# 无主图或当前模型不支持 image_edit → 回落纯文生图(仅凭商品名脑补,不保证还原真实包装)。
product_ref_url = _product_cover_url(project.product) if kind == BaseAssetGroup.Kind.PRODUCT else ""
use_edit = bool(product_ref_url) and hasattr(provider, "image_edit")
if use_edit:
# 角色「重跑」:若该角色已有当前立绘(reference_asset_id),以它为参考图走 image_edit,
# 保持同一人物相貌一致(仅据立绘+提示词微调),不再重抽成另一个随机人。
person_ref_url = ""
if kind == BaseAssetGroup.Kind.PERSON and reference_asset_id:
ref_asset = Asset.objects.filter(id=reference_asset_id, team=project.team, is_deleted=False).first()
if ref_asset is not None:
person_ref_url = _asset_preview_url(ref_asset)
ref_url = product_ref_url or person_ref_url
use_edit = bool(ref_url) and hasattr(provider, "image_edit")
if use_edit and product_ref_url:
gen_prompt = build_product_triview_prompt_refs(project.product, prompt)
elif use_edit and person_ref_url:
gen_prompt = build_person_portrait_prompt_refs(prompt) # 角色重跑:参考当前立绘 + 提示词,保持人物一致
elif kind == BaseAssetGroup.Kind.PERSON:
gen_prompt = build_person_frontal_prompt(prompt) # 人物立绘:包成「电商真人模特/正面全身/纯色背景」统一模板
else:
@@ -1253,7 +1276,7 @@ def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "
payload = {
"model": model_config.name, "endpoint": model_config.endpoint, "prompt": gen_prompt,
"kind": kind, "label": label or "", "group_id": str(group_id) if group_id else "",
"use_edit": use_edit, "reference_image": product_ref_url,
"use_edit": use_edit, "reference_image": ref_url,
}
task = create_ai_task(
project=project,
@@ -1291,8 +1314,11 @@ def run_base_asset_task(*, task_id: str) -> None:
try:
def _make():
if use_edit and ref_url:
# 商品三视图:比例可在 admin 改(默认横)
size = prompt_ratio_size("product_triview", "1536x1024")
# 商品三视图默认横;角色立绘重跑(image_edit 参考当前立绘)默认竖,与文生图立绘一致。
if kind == BaseAssetGroup.Kind.PERSON:
size = prompt_ratio_size("person_portrait", "1024x1536")
else:
size = prompt_ratio_size("product_triview", "1536x1024")
resp = provider.image_edit(model=model_config.name, prompt=prompt, images=[ref_url], size=size)
else:
# 场景默认横、人物立绘默认竖;两者比例都可在 admin 改