fix: apply prompt packs to white scene detail replacements

This commit is contained in:
Your Name 2026-06-03 14:06:52 +08:00
parent a154a28a4a
commit ce7c2bff35

View File

@ -216,7 +216,7 @@ export async function resolveReplacementReferenceVisualProfile(
reason: likelyWhiteLayout
? "像素初筛显示大面积白底/浅色低饱和版式区域,按白底说明图保护"
: likelyWhiteScene
? "像素初筛显示白/浅色低饱和场景,按白场景保护"
? "像素初筛显示白/浅色低饱和真实场景,按白场景基调软重造"
: "像素初筛未达到白底/白场景阈值",
};
} catch (error) {
@ -763,14 +763,19 @@ function applyDetailReplacementScenePlanGuards(
scenePlan.promptPackPolicy !== "forbid";
const hasWhiteLayoutHint = DETAIL_WHITE_LAYOUT_HINT_PATTERN.test(planText);
const hasPeopleActionHint = DETAIL_PEOPLE_ACTION_HINT_PATTERN.test(planText);
const visualProfileSuggestsWhiteLayout = params.visualProfile?.likelyWhiteLayout === true;
const visualProfileSuggestsWhiteScene =
params.visualProfile?.likelyWhiteScene === true ||
params.visualProfile?.likelyWhiteLayout === true;
params.visualProfile?.likelyWhiteScene === true &&
!visualProfileSuggestsWhiteLayout;
const scenePlanSuggestsWhiteLayout =
scenePlan.whiteBackgroundLikely &&
!visualProfileSuggestsWhiteScene &&
(scenePlan.textPolicy === "lock" || scenePlan.copyImageCoupled);
const shouldProtectWhiteLayout =
!isProductOnly &&
(scenePlan.whiteBackgroundLikely ||
params.visualProfile?.likelyWhiteLayout === true ||
hasWhiteLayoutHint);
(visualProfileSuggestsWhiteLayout ||
scenePlanSuggestsWhiteLayout ||
(hasWhiteLayoutHint && !visualProfileSuggestsWhiteScene));
const shouldProtectWhiteScene =
!isProductOnly &&
!shouldProtectWhiteLayout &&
@ -790,42 +795,65 @@ function applyDetailReplacementScenePlanGuards(
let next: ReplacementScenePlan = { ...scenePlan };
if (shouldProtectPeople && (shouldProtectWhiteLayout || shouldProtectWhiteScene)) {
if (shouldProtectPeople && shouldProtectWhiteLayout) {
next = {
...next,
generationRoute: shouldProtectWhiteLayout
? "layout_preserve_replace"
: "people_relation_locked",
generationRoute: "layout_preserve_replace",
promptPackPolicy: "forbid",
textPolicy: next.textPolicy === "free" ? "preserve" : next.textPolicy,
peoplePolicy: shouldProtectPeople ? "preserve_relation" : next.peoplePolicy,
useModelImage: params.hasModelImage && shouldProtectPeople,
modelImageReason: params.hasModelImage
? next.modelImageReason ||
"当前参考页有人物/动作,但参考图为白底/浅色场景,模特只用于保持人物身份、动作和姿态,不用于重建复杂空间。"
"当前参考页有人物/动作,但参考图为白底/浅色排版页,模特只用于保持人物身份、动作和姿态,不用于重建复杂空间。"
: next.modelImageReason ||
"当前参考页有人物/动作,但参考图为白底/浅色场景;应保留人物数量、动作和关系,不要生成复杂家居场景。",
"当前参考页有人物/动作,但参考图为白底/浅色排版页;应保留人物数量、动作和关系,不要生成复杂家居场景。",
routeReason: appendUniqueInstruction(
next.routeReason,
shouldProtectWhiteLayout
? "命中白底排版 + 人物/动作语义,锁定白底版式和人物关系,禁止套用场景词包。"
: "命中白/浅色场景 + 人物/动作语义,锁定白场景和人物关系,禁止套用复杂空间词包。",
"命中白底排版 + 人物/动作语义,锁定白底版式和人物关系,禁止套用场景词包。",
220,
),
generationPrompt: appendUniqueInstruction(
next.generationPrompt,
shouldProtectWhiteLayout
? "本屏是白底/浅色详情排版页:必须保持白底或浅色底、标题区、标签、图片框、留白、人物数量/动作/关系和产品位置;不使用前置词包,不生成厨房、客厅、黑墙、吊灯或复杂软装场景。"
: "本屏是白/浅色低饱和场景页:最终结果也必须保持白色、米白、浅灰或浅木色的干净场景基调;人物数量、动作、朝向和产品关系保真,但不要生成厨房、客厅、黑墙、吊灯、复杂柜体或强软装背景。",
"本屏是白底/浅色详情排版页:必须保持白底或浅色底、标题区、标签、图片框、留白、人物数量/动作/关系和产品位置;不使用前置词包,不生成厨房、客厅、黑墙、吊灯或复杂软装场景。",
),
avoidSummary: appendUniqueInstruction(
next.avoidSummary,
"不要把白底/白场景人物页改成复杂室内场景,不要让前置词包覆盖白色背景,不要把人物动作变成走动路人或无关模特。",
"不要把白底排版人物页改成复杂室内场景,不要让前置词包覆盖白底版式,不要把人物动作变成走动路人或无关模特。",
280,
),
whiteBackgroundLikely: true,
copyImageCoupled: next.copyImageCoupled || shouldProtectWhiteLayout,
};
} else if (shouldProtectPeople && shouldProtectWhiteScene) {
next = {
...next,
generationRoute: "people_relation_locked",
promptPackPolicy: "soft",
textPolicy: next.textPolicy === "free" ? "preserve" : next.textPolicy,
peoplePolicy: "preserve_relation",
useModelImage: params.hasModelImage,
modelImageReason: params.hasModelImage
? next.modelImageReason ||
"当前参考页为白/浅色人物场景,模特用于保持人物身份、动作和姿态,词包用于重建白场景内的产品与空间。"
: next.modelImageReason ||
"当前参考页为白/浅色人物场景;需保留人物数量、动作和关系,同时按词包重建白场景内的产品与空间。",
routeReason: appendUniqueInstruction(
next.routeReason,
"命中白/浅色真实场景 + 人物/动作语义,允许词包重造场景,但必须保持白/浅色基调和原版式。",
240,
),
generationPrompt: appendUniqueInstruction(
next.generationPrompt,
"本屏是白/浅色真实场景页:必须使用前置工作流词包重造图片框内的产品、空间、软装和灯光,使差异清晰可见;但最终仍保持白色、米白、浅灰、浅木色的干净低饱和基调,保留人物数量、动作、朝向、互动关系、文字层级和原图片框边界。",
),
avoidSummary: appendUniqueInstruction(
next.avoidSummary,
"不要只小幅改动原图,不要原样保留旧场景;也不要把白场景改成深色厨房、黑墙、复杂吊灯或强软装背景。",
300,
),
whiteBackgroundLikely: true,
};
} else if (shouldProtectPeople) {
next = {
...next,
@ -885,7 +913,7 @@ function applyDetailReplacementScenePlanGuards(
whiteBackgroundLikely: next.whiteBackgroundLikely || shouldProtectWhiteLayout,
copyImageCoupled: true,
};
} else if (shouldProtectWhiteLayout || shouldProtectWhiteScene) {
} else if (shouldProtectWhiteLayout) {
next = {
...next,
generationRoute: "layout_preserve_replace",
@ -899,9 +927,30 @@ function applyDetailReplacementScenePlanGuards(
),
generationPrompt: appendUniqueInstruction(
next.generationPrompt,
shouldProtectWhiteLayout
? "本屏是白底/浅色产品说明版式:不要套用空间、人群、灯光、机位等前置词包,只保留参考图白底排版并做必要局部替换。"
: "本屏是白/浅色低饱和场景:最终结果也必须保持白场景、浅色背景、干净留白和低饱和氛围,不要改成厨房、客厅、黑墙、吊灯或复杂软装场景。",
"本屏是白底/浅色产品说明版式:不要套用空间、人群、灯光、机位等前置词包,只保留参考图白底排版并做必要局部替换。",
),
whiteBackgroundLikely: true,
};
} else if (shouldProtectWhiteScene) {
next = {
...next,
generationRoute: "prompt_pack_scene",
promptPackPolicy: "soft",
textPolicy: next.textPolicy === "free" ? "preserve" : next.textPolicy,
useModelImage: false,
routeReason: appendUniqueInstruction(
next.routeReason,
"命中白/浅色真实场景,允许词包重造场景,但必须保持白场景基调。",
240,
),
generationPrompt: appendUniqueInstruction(
next.generationPrompt,
"本屏是白/浅色低饱和真实场景:必须使用前置工作流词包重造图片框内的背景、产品、材质、软装和灯光,使新旧差异清晰;同时保持白色、米白、浅灰、浅木色的干净低饱和场景基调,不要变成深色厨房、黑墙、复杂吊灯或强软装。",
),
avoidSummary: appendUniqueInstruction(
next.avoidSummary,
"不要只复刻原图或小幅改动,不要把白场景改成深色复杂空间,不要移动标题、标签和图片框。",
300,
),
whiteBackgroundLikely: true,
};
@ -1096,12 +1145,18 @@ export function buildHeuristicReplacementScenePlan(params: {
const isProductOnly = params.replacementStrategy === "product_only";
const productImageSelection = buildDefaultProductImageSelection(params.productImageCount);
const fallbackText = String(params.reason || "");
const shouldProtectFallbackLayout =
const shouldProtectFallbackWhiteLayout =
isDetailReplacement &&
!isProductOnly &&
(Boolean(params.visualProfile?.likelyWhiteLayout) ||
Boolean(params.visualProfile?.likelyWhiteScene) ||
DETAIL_STRONG_COPY_HINT_PATTERN.test(fallbackText));
const shouldRebuildFallbackWhiteScene =
isDetailReplacement &&
!isProductOnly &&
!shouldProtectFallbackWhiteLayout &&
Boolean(params.visualProfile?.likelyWhiteScene);
const shouldProtectFallbackLayout =
shouldProtectFallbackWhiteLayout;
const generationRoute = params.shouldReplace
? isProductOnly
? "layout_preserve_replace"
@ -1128,7 +1183,9 @@ export function buildHeuristicReplacementScenePlan(params: {
? "兜底按仅替换产品执行,不使用词包。"
: isDetailReplacement
? shouldProtectFallbackLayout
? "兜底命中白底/白场景/说明/强文案特征,按详情版式或白场景保护执行,不使用词包。"
? "兜底命中白底说明/强文案版式,按详情版式保护执行,不使用词包。"
: shouldRebuildFallbackWhiteScene
? "兜底命中白/浅色真实场景,允许词包软重造,但保持白场景基调。"
: "兜底未命中白底、强文案或真人互动硬约束,按通用场景图执行词包场景替换。"
: "兜底未检测到强约束,允许使用词包生成场景。"
: params.reason,
@ -1144,7 +1201,9 @@ export function buildHeuristicReplacementScenePlan(params: {
? isProductOnly
? "严格保留参考图的场景、文案和版式,只把产品展示区中的旧产品替换成输入产品。"
: shouldProtectFallbackLayout
? "保留参考图白底/白场景、版式骨架、构图、文字和主体位置,只做必要的产品或视觉区替换。"
? "保留参考图白底说明版式、构图、文字和主体位置,只做必要的产品或视觉区替换。"
: shouldRebuildFallbackWhiteScene
? "保留参考图白/浅色低饱和基调与版式骨架,但按工作流目标明显重建图片框内产品、空间、软装和灯光。"
: "保留参考图版式骨架、构图和主体位置,但把旧场景视觉内容按工作流目标风格重建。"
: "当前参考图不满足替换条件。",
cameraSummary: "保持参考图原有机位、构图和景别,不做整体重画。",
@ -1157,18 +1216,24 @@ export function buildHeuristicReplacementScenePlan(params: {
styleSummary: isProductOnly
? "延续参考图原有风格、背景和氛围,不引入新的场景改造要求。"
: shouldProtectFallbackLayout
? "延续参考图原有白底/白场景、版式和图文语义,不引入新的空间场景。"
? "延续参考图原有白底说明版式和图文语义,不引入新的空间场景。"
: shouldRebuildFallbackWhiteScene
? "按前置词包重建为白/浅色低饱和真实场景,保持干净浅色基调,不走深色复杂空间。"
: "除版式骨架外,墙面、地面、背景家具、软装、灯光、摆件和空间氛围都要按工作流风格重建。",
avoidSummary: isProductOnly
? "不要改动背景、文案、按钮、图标、排版和装饰,不要重建整个场景,不要把多个产品角度同时塞进一张图。"
: shouldProtectFallbackLayout
? "不要把白底/白场景/说明/强文案页场景化,不要新增厨房/客厅/黑墙/吊灯/复杂软装,不要让文字和图片失配。"
? "不要把白底说明/强文案页场景化,不要新增厨房/客厅/黑墙/吊灯/复杂软装,不要让文字和图片失配。"
: shouldRebuildFallbackWhiteScene
? "不要只小幅复刻原图,不要把白场景改成深色厨房、黑墙、复杂吊灯或强软装,不要破坏原版式。"
: "不要只换产品不换场景,不要把参考图原有背景物件和空间氛围原样保留下来,不要把所有输入产品角度图同时强行出现在一张图中。",
generationPrompt: params.shouldReplace
? isProductOnly
? `仅替换参考图中的产品主体,严格保留其余背景、文案、排版与光影关系。${params.reason}`
: isDetailReplacement && shouldProtectFallbackLayout
? `保留参考图的版式、文案、白底/白场景/留白和图文语义,不使用前置词包;只在产品、材质、板材、原材料或视觉图片区做必要替换。${params.reason}`
? `保留参考图的版式、文案、白底/留白和图文语义,不使用前置词包;只在产品、材质、板材、原材料或视觉图片区做必要替换。${params.reason}`
: isDetailReplacement && shouldRebuildFallbackWhiteScene
? `当前参考图按白/浅色真实场景详情页处理:必须使用前置工作流词包明显重建图片框内旧背景、产品、空间、软装和灯光;同时保持白色、米白、浅灰、浅木色低饱和基调,保留参考图版式骨架、文字区、机位和信息层级。${params.reason}`
: isDetailReplacement
? `当前参考图按通用空间/场景详情页处理:必须使用前置工作流词包重建旧背景、空间、软装、灯光和产品场景,只保留参考图版式骨架、文字区、机位和信息层级。${params.reason}`
: `以工作流目标风格重建当前这一屏的场景,只保留版式、机位、文字区与留白结构。${params.reason}`
@ -1665,7 +1730,7 @@ export async function analyzeReplacementExecutionPlan(params: {
? "像素初筛已经命中白底/浅色说明页:只要画面存在标题、标签、参数、尺寸、卖点、图标、白底图片区、圆角图片区或明显留白排版,generationRoute 必须是 layout_preserve_replace 或 copy_locked_regenerate,promptPackPolicy 必须 forbid;即使包含人物/动作,也只能保留人物关系与白底版式,不允许套用厨房、客厅、灯光、软装等场景词包。"
: "",
!referenceVisualProfile?.likelyWhiteLayout && referenceVisualProfile?.likelyWhiteScene
? "像素初筛已经命中白/浅色低饱和场景:如果参考图主体是白底、米白、浅灰、浅木色、干净棚拍或轻生活场景,最终结果也必须保持白/浅色场景基调;不要把它改造成深色厨房、客厅、吊灯、黑墙、复杂柜体或强软装场景。"
? "像素初筛已经命中白/浅色低饱和真实场景:这不是白底说明页。若画面存在真实空间、人物或产品照片,promptPackPolicy 应为 soft 或 allow,必须使用前置工作流词包重建图片框内产品、空间、软装和灯光,使差异清晰可见;但最终仍保持白/浅色低饱和基调,不要改造成深色厨房、黑墙、复杂吊灯或强软装。"
: "",
"产品图和模特图会在后续生图阶段单独提供给生图模型使用,你这里不要尝试理解它们的具体外观,也不要要求必须使用所有输入图。",
params.modelImageUrl && !isProductOnlyDetailReplacement
@ -1700,7 +1765,7 @@ export async function analyzeReplacementExecutionPlan(params: {
? "4. 判断 promptPackPolicy:纯白底参数/说明/卖点页、强图文绑定且没有真实展示动作的详情页必须 forbid;通用空间场景必须 allow;人物/自拍/聚会/承重测试/使用产品等动作展示页可以 people_relation_locked + allow/soft,含义是保留人物动作与文字层级,但允许前置词包重建背景、灯光、软装和产品场景。"
: "",
isDetailReplacement
? "4a. 白场景优先规则:如果参考图是白底/浅色说明页或白/浅色低饱和场景,promptPackPolicy 必须 forbid;人物动作可以保留,但不能使用前置词包把白场景改成厨房、客厅、黑墙、吊灯或复杂软装。"
? "4a. 白底/白场景分流规则:白底参数、规格、卖点、强文案、图标说明或纯版式页必须 forbid;白/浅色真实场景页不能 forbid,应使用 soft/allow 词包重造图片框内产品、空间、软装和灯光,但保持白/浅色低饱和基调,禁止变成深色厨房、黑墙、复杂吊灯或强软装。"
: "",
isDetailReplacement
? "5. 判断 textPolicy:画面中存在营销标题、卖点、编号、参数、标签且与图匹配时必须 lock 或 preserve,避免后续生图改字、混色或重排。"
@ -1949,7 +2014,7 @@ export function buildAgentReplacementPrompt(params: {
: "",
params.scenePlan.whiteBackgroundLikely
? shouldApplyPromptPack
? "白底/说明图判断:是,但本屏包含人物动作/真实展示语义,允许在保留版式和文字层级的前提下按词包改造背景/产品场景。"
? "白底/白场景判断:是,本屏按白/浅色真实场景处理;允许词包重造图片框内背景、产品、空间、软装和灯光,但必须保留白/浅色低饱和基调、原版式层和文字层级。"
: "白底/白场景判断:是,禁止强行场景化;最终结果必须保持白底、浅色底或干净低饱和场景基调。"
: "",
params.scenePlan.copyImageCoupled
@ -2064,7 +2129,10 @@ export function buildAgentReplacementPrompt(params: {
: params.scenePlan?.peoplePolicy === "preserve_relation"
? "本屏包含人物关系:必须保留人物数量、相对位置、互动关系、动作方向和出镜比例;如果参考图文字写了自拍/合照/聚会/用餐/拿手机等动作,必须让人物执行该动作,不要把多人图改成单人图、无人图或无关模特图。"
: "如果参考图没有明确人物展示需求,不要凭空新增人物主体。",
params.scenePlan?.whiteBackgroundLikely && !shouldApplyPromptPack
params.scenePlan?.whiteBackgroundLikely && shouldApplyPromptPack
? "本屏是白/浅色真实场景且词包已启用:必须让图片框内旧背景、产品、软装、灯光和空间发生清晰可见的重建;不能只微调原图或原样复刻,但也不能破坏外层白底/浅色版式和文字区。"
: "",
params.scenePlan?.whiteBackgroundLikely && !shouldApplyPromptPack
? "本屏疑似白底/白场景/浅色说明图:禁止场景化、生活化或套空间词包,只允许保留白底、浅色底、干净留白、原图片框和原说明图语义并做必要的局部替换;最终不能变成厨房、客厅、黑墙、吊灯或复杂软装场景。"
: "",
params.referenceGeometry