fix(#2174 P0): 信任链从i2i改为t2i文生图——方舟实测验证通过
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m17s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m11s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m16s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m36s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m43s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 5m56s
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m17s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m11s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m16s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m36s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m43s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 5m56s
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
#2170/#2172的信任链用Seedream图生图(i2i,传reference_images=用户真人照), 实测产物不被Seedance信任,仍被400 portrait_intercept拦截。 方舟文档+实测确认:信任的是Seedream文生图(t2i)产物——纯prompt描述人物, 不传reference_images,产物是纯模型生成人像,同账号30天内可直接传Seedance。 改动: - VLM prompt新增portrait_prompt字段(60-100字人物外貌描述) - preheat_trust_chain签名从portrait_urls改为portrait_descriptions,t2i模式不传reference_images - video_generation移除现场跑i2i逻辑,仅使用预热好的t2i结果;预热失败走#2166自动降级t2v - 预热时机从VLM前改为VLM后(需要VLM输出的portrait_prompt),仍与文案阶段并行 - 单测适配:TestTrustChain全部改为pre_trusted_images路径验证 实测验证(充值后): - Seedream flash t2i ~15s出图,人像prompt中英文混合写实风格 - Seedance 2.5 i2v用t2i产物URL → 成功出5s视频(120s) - Seedance 2.0-mini i2v用t2i产物URL → 成功出5s视频 - 对照组直传用户真人照片 → 400 portrait_intercept(预期)
This commit is contained in:
@@ -419,45 +419,61 @@ class DoubaoClient:
|
||||
|
||||
def preheat_trust_chain(
|
||||
self,
|
||||
portrait_urls: list[str],
|
||||
portrait_descriptions: list[str],
|
||||
*,
|
||||
timeout: int | None = None,
|
||||
size: str | None = None,
|
||||
) -> list[str] | None:
|
||||
"""#2172 信任链预热:对一组人像 URL 执行 Seedream AI 化,返回 AI 化后的 URL 列表。
|
||||
"""#2174 信任链预热(t2i 版):用 VLM 分析出的人物外貌描述,纯文生图生成 Seedream 人像,
|
||||
返回信任产物 URL 列表。
|
||||
|
||||
重要:方舟信任链规则是 Seedream 文生图(t2i)产物(不传 reference_images)才被 Seedance 信任;
|
||||
i2i(带用户照片 reference)产物不被信任,仍会被肖像审核拦截。
|
||||
|
||||
- portrait_descriptions: VLM 输出的 portrait_prompt 列表(中文描述),与原始图片顺序对应
|
||||
- 全部成功返回 list[str](顺序与输入一致)
|
||||
- 任何一张失败返回 None(保留上层回退原图直传的路径)
|
||||
- 若 trust_chain_enabled=False 直接返回 None(#2173: 可配置关闭)
|
||||
- 供 worker 在视频生成前并行预热使用;video_generation 内部若收到 preheated 结果会直接使用,不再现场跑。
|
||||
- 任何一张失败返回 None(上层会走现场兜底:直接 t2v 不带参考图)
|
||||
- 若 trust_chain_enabled=False 或所有描述均为"无人像",直接返回 None
|
||||
- 供 worker 在 VLM 分析完成后后台预热使用;video_generation 内部若收到 preheated 结果会直接使用。
|
||||
"""
|
||||
if not portrait_urls or not self.is_available or not getattr(self, "trust_chain_enabled", True):
|
||||
if not portrait_descriptions or not self.is_available or not getattr(self, "trust_chain_enabled", True):
|
||||
return None
|
||||
# 过滤掉"无人像"等无效描述,收集需要生成的索引
|
||||
_idx_map: list[int] = []
|
||||
_prompts: list[str] = []
|
||||
for i, desc in enumerate(portrait_descriptions):
|
||||
if not desc or "无人像" in desc or len(desc) < 10:
|
||||
continue
|
||||
_idx_map.append(i)
|
||||
# 把VLM的中文描述包装成适合Seedream t2i的英文+中文混合prompt,明确是写实半身人像
|
||||
_prompts.append(
|
||||
f"高清写实半身人像照片,{desc},自然光线,面部清晰居中,皮肤质感自然,"
|
||||
"高清摄影细节,构图居中,人物占据画面主体,背景柔和虚化。photorealistic portrait, "
|
||||
"sharp focus on face, soft natural lighting, high detail, half body shot."
|
||||
)
|
||||
if not _prompts:
|
||||
logger.info("[trust-chain][preheat] 无需生成信任人像(所有图片均无人像),跳过")
|
||||
return None
|
||||
seedream_prompt = (
|
||||
"保持此人五官特征、发型、肤色、面部轮廓、年龄感,生成一张高清写实人像照片,"
|
||||
"人物外貌特征与参考图完全一致,皮肤自然,光线柔和,高清细节,不要过度美化。"
|
||||
)
|
||||
trusted: list[str] = []
|
||||
_t0 = time.time()
|
||||
for idx, raw_url in enumerate(portrait_urls):
|
||||
sd_prompt = seedream_prompt if len(portrait_urls) == 1 else f"{seedream_prompt}(这是参考图{idx + 1})"
|
||||
for idx, sd_prompt in enumerate(_prompts):
|
||||
# #2174 关键:t2i模式——不传reference_images,纯prompt文生图,产物才被Seedance信任
|
||||
sd_result = self.image_generation(
|
||||
prompt=sd_prompt,
|
||||
reference_images=[raw_url],
|
||||
size=size or getattr(self, "image_size", "1K"),
|
||||
timeout=timeout or getattr(self, "image_timeout", 60),
|
||||
)
|
||||
if not sd_result:
|
||||
logger.warning(
|
||||
"[trust-chain][preheat] Seedream 第 %d/%d 张失败: %s,预热整体失败",
|
||||
"[trust-chain][preheat] Seedream t2i 第 %d/%d 张失败: %s,预热整体失败",
|
||||
idx + 1,
|
||||
len(portrait_urls),
|
||||
len(_prompts),
|
||||
getattr(self, "last_image_error", None),
|
||||
)
|
||||
return None
|
||||
trusted.append(sd_result["url"])
|
||||
logger.info(
|
||||
"[trust-chain][preheat] Seedream AI 化预热完成 %d 张,总耗时 %.1fs",
|
||||
"[trust-chain][preheat] Seedream t2i 文生图预热完成 %d 张,总耗时 %.1fs",
|
||||
len(trusted),
|
||||
time.time() - _t0,
|
||||
)
|
||||
@@ -566,11 +582,13 @@ class DoubaoClient:
|
||||
# 真人照片直接传给 Seedance 会触发 50411 肖像审核拦截。
|
||||
# 解决:先通过同账号的 Seedream 5.0 Pro 图生图 AI 化(保持五官特征),
|
||||
# 得到的 AI 产物图属于"模型信任产物",再作为 reference_image 传给 Seedance 即可通过审核。
|
||||
# #2172: 支持预热结果 pre_trusted_images(worker 在文案阶段并行预热,省掉串行等待);
|
||||
# 预热结果有效则直接使用,否则现场跑一次 Seedream AI 化。
|
||||
# #2172/#2174: 信任链——使用预热好的 Seedream t2i 文生图(纯模型生成人像,是方舟信任产物,
|
||||
# 不会触发肖像审核)。预热在 VLM 分析后由 daemon 线程后台完成,结果通过 pre_trusted_images 传入。
|
||||
# - 预热结果有效 → 替换原参考图,走 omni_ref 模式
|
||||
# - 预热结果不可用 → 直接用原图(若被400肖像拦截,#2166自动降级纯t2v),避免现场跑t2i阻塞渲染
|
||||
# 信任链只作用于 doubao provider;DashScope(Wan) 保持原行为。
|
||||
trust_chain_applied = False
|
||||
if provider == "doubao" and getattr(self, "trust_chain_enabled", True):
|
||||
if provider == "doubao" and getattr(self, "trust_chain_enabled", True) and pre_trusted_images:
|
||||
raw_portrait_urls: list[str] = []
|
||||
if image_url:
|
||||
raw_portrait_urls.append(image_url)
|
||||
@@ -578,32 +596,13 @@ class DoubaoClient:
|
||||
if u not in raw_portrait_urls:
|
||||
raw_portrait_urls.append(u)
|
||||
trusted_urls: list[str] = []
|
||||
if pre_trusted_images and len(pre_trusted_images) == len(raw_portrait_urls):
|
||||
# #2172: 使用预热结果
|
||||
if len(pre_trusted_images) >= 1:
|
||||
trusted_urls = list(pre_trusted_images)
|
||||
trust_chain_applied = True
|
||||
logger.info(
|
||||
"[trust-chain] 使用预热结果 %d 张,替换为 reference_image 模式",
|
||||
len(trusted_urls),
|
||||
"[trust-chain] 使用预热t2i结果 %d 张,替换原参考图走 reference_image 模式(原n=%d)",
|
||||
len(trusted_urls), len(raw_portrait_urls),
|
||||
)
|
||||
elif raw_portrait_urls:
|
||||
# 现场跑信任链
|
||||
_tc_t0 = time.time()
|
||||
trusted_urls = self.preheat_trust_chain(raw_portrait_urls) or []
|
||||
if trusted_urls and len(trusted_urls) == len(raw_portrait_urls):
|
||||
trust_chain_applied = True
|
||||
logger.info(
|
||||
"[trust-chain] Seedream AI 化完成 %d 张,总耗时 %.1fs,替换为 reference_image 模式",
|
||||
len(trusted_urls),
|
||||
time.time() - _tc_t0,
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"[trust-chain] Seedream AI 化不完整(%d/%d),总耗时 %.1fs,回退原图直传",
|
||||
len(trusted_urls),
|
||||
len(raw_portrait_urls),
|
||||
time.time() - _tc_t0,
|
||||
)
|
||||
if trust_chain_applied and trusted_urls:
|
||||
# 替换:原 image_url 用第一张 AI 图,ref_imgs 用剩余
|
||||
if image_url and trusted_urls:
|
||||
|
||||
@@ -605,16 +605,19 @@ def call_vision(
|
||||
return raw
|
||||
|
||||
|
||||
def preheat_trust_chain(portrait_urls: list[str], *, timeout: int = 120) -> list[str] | None:
|
||||
"""#2172 信任链预热:提前把人像图跑 Seedream AI 化,结果可传给 call_video_generation(pre_trusted_images=...)。
|
||||
def preheat_trust_chain(portrait_descriptions: list[str], *, timeout: int = 120) -> list[str] | None:
|
||||
"""#2174 信任链预热(t2i版):用 VLM 分析出的人物外貌描述,跑 Seedream 文生图,
|
||||
生成的信任产物 URL 可传给 call_video_generation(pre_trusted_images=...)。
|
||||
|
||||
成功返回与输入同序的 AI 化 URL 列表;任意一张失败返回 None(调用方回退到现场跑信任链)。
|
||||
- portrait_descriptions: VLM输出的portrait_prompt列表(中文人物外貌描述)
|
||||
- 成功返回与输入同序的信任图URL列表;任意一张失败返回None(调用方回退到纯t2v)
|
||||
- 必须传VLM人物描述,不传reference_images,走纯t2i路径才是方舟信任产物
|
||||
"""
|
||||
client = get_doubao_client()
|
||||
if not client.is_available:
|
||||
return None
|
||||
try:
|
||||
return client.preheat_trust_chain(portrait_urls, timeout=timeout)
|
||||
return client.preheat_trust_chain(portrait_descriptions, timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.error("[ai_service] preheat_trust_chain 异常: %s", e, exc_info=True)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user