fix: apply prompt packs to action detail cards

This commit is contained in:
Your Name 2026-06-02 21:55:22 +08:00
parent e126355b26
commit a17d2da133

View File

@ -236,11 +236,16 @@ export async function normalizeReplacementResultImage(params: {
const shouldCropTallComposite =
Boolean(targetAspectRatio && generatedAspectRatio) &&
Number(generatedAspectRatio) < Number(targetAspectRatio) * 0.78;
const shouldFillSceneCanvas =
params.scenePlan?.promptPackPolicy !== "forbid" &&
(params.scenePlan?.generationRoute === "prompt_pack_scene" ||
params.scenePlan?.generationRoute === "people_relation_locked");
const shouldPreserveFullGeneratedCanvas =
params.scenePlan?.whiteBackgroundLikely === true ||
params.scenePlan?.textPolicy === "lock" ||
params.scenePlan?.generationRoute === "copy_locked_regenerate" ||
params.scenePlan?.generationRoute === "layout_preserve_replace";
!shouldFillSceneCanvas &&
(params.scenePlan?.whiteBackgroundLikely === true ||
params.scenePlan?.textPolicy === "lock" ||
params.scenePlan?.generationRoute === "copy_locked_regenerate" ||
params.scenePlan?.generationRoute === "layout_preserve_replace");
const resizeFit = shouldCropTallComposite
? "cover"
: shouldPreserveFullGeneratedCanvas
@ -349,7 +354,7 @@ const DETAIL_WHITE_LAYOUT_HINT_PATTERN =
const DETAIL_STRONG_COPY_HINT_PATTERN =
/白底|浅色底|产品说明|参数|规格|尺寸|卖点|标签|说明图|信息图|海报|版式|排版|文字|标题|编号|标注|材质标注|工艺标注|代名词|典雅|高贵/i;
const DETAIL_PEOPLE_ACTION_HINT_PATTERN =
/自拍|合照|拍照|拿手机|手机自拍|聚会|好友|朋友|闺蜜|夫妻|情侣|家庭|亲子|陪伴|举杯|互动|模特|人物|人像|多人|人群|男士|女士|男人|女人|男生|女生|小孩|儿童|宝宝|顾客|吃饭的人|用餐的人|正在吃饭|坐在|站在|走动|行走|手持|试坐|倚靠/i;
/自拍|合照|拍照|拿手机|手机自拍|聚会|好友|朋友|闺蜜|夫妻|情侣|家庭|亲子|陪伴|举杯|互动|模特|人物|人像|多人|人群|男士|女士|男人|女人|男生|女生|小孩|儿童|宝宝|顾客|吃饭的人|用餐的人|正在吃饭|坐在|坐着|站在|走动|行走|手持|试坐|倚靠|承重|受力|测试|稳定|稳固|使用产品|产品使用|体验/i;
export type ReplacementScenePlanningRuntime =
| {
@ -615,15 +620,15 @@ function inferDetailGenerationRoute(params: {
if (params.isProductOnly) {
return "layout_preserve_replace";
}
if (params.containsMultiplePeople || params.containsPeople) {
return "people_relation_locked";
}
if (params.whiteBackgroundLikely) {
return "layout_preserve_replace";
}
if (params.copyImageCoupled || params.hasImportantText) {
return "copy_locked_regenerate";
}
if (params.containsMultiplePeople || params.containsPeople) {
return "people_relation_locked";
}
return "prompt_pack_scene";
}
@ -732,7 +737,7 @@ function applyDetailReplacementScenePlanGuards(
next = {
...next,
generationRoute: "people_relation_locked",
promptPackPolicy: "forbid",
promptPackPolicy: "allow",
textPolicy: next.textPolicy === "free" ? "preserve" : next.textPolicy,
peoplePolicy: "preserve_relation",
useModelImage: params.hasModelImage,
@ -743,16 +748,16 @@ function applyDetailReplacementScenePlanGuards(
"当前参考页存在人物/自拍/互动需求;未提供用户模特图时仍需保留参考页人物数量、动作和关系,但不要复刻特定身份。",
routeReason: appendUniqueInstruction(
next.routeReason,
"命中人物/自拍/互动语义,禁止按普通场景词包重建。",
"命中人物/自拍/互动语义,锁定人物关系与文案层级,但允许前置词包重建背景/产品场景。",
220,
),
generationPrompt: appendUniqueInstruction(
next.generationPrompt,
"如果参考图文字或画面包含自拍、合照、聚会、用餐、互动、拿手机等动作语义,人物数量、相对位置、动作方向和出镜比例是硬约束;结果必须出现对应人物动作,不允许生成空场景。",
"如果参考图文字或画面包含自拍、合照、聚会、用餐、互动、拿手机、承重测试、使用产品等动作语义,人物数量、相对位置、动作方向和出镜比例是硬约束;同时必须按前置工作流词包替换旧背景、空间氛围、灯光、软装和产品场景,不允许只复刻参考图原背景。",
),
avoidSummary: appendUniqueInstruction(
next.avoidSummary,
"不要把有人物/自拍动作的参考页生成成无人物空间,不要把多人压成单人,不要忽略参考页文字描述的人物动作。",
"不要把有人物/自拍动作的参考页生成成无人物空间,不要把多人压成单人,不要忽略参考页文字描述的人物动作,也不要只原样保留参考图旧场景。",
260,
),
};
@ -1559,7 +1564,7 @@ export async function analyzeReplacementExecutionPlan(params: {
? `像素初筛:nearWhite=${referenceVisualProfile.nearWhitePixelRatio.toFixed(2)}, lightLowSaturation=${referenceVisualProfile.lightLowSaturationPixelRatio.toFixed(2)}, dark=${referenceVisualProfile.darkPixelRatio.toFixed(2)}, likelyWhiteLayout=${referenceVisualProfile.likelyWhiteLayout ? "true" : "false"}。${referenceVisualProfile.reason}`
: "",
referenceVisualProfile?.likelyWhiteLayout
? "像素初筛已经命中白底/浅色说明页:除非你在画面中看到明确的大面积真实生活场景,否则 generationRoute 必须是 layout_preserve_replace 或 copy_locked_regenerate,promptPackPolicy 必须 forbid。"
? "像素初筛已经命中白底/浅色说明页:如果画面是纯产品参数/说明/卖点排版且没有人物动作或真实使用展示,generationRoute 必须是 layout_preserve_replace 或 copy_locked_regenerate,promptPackPolicy 必须 forbid;如果画面包含人物、自拍、承重测试、使用产品、真实展示动作或明确生活场景,可以选择 people_relation_locked,并把 promptPackPolicy 设为 allow 或 soft。"
: "",
"产品图和模特图会在后续生图阶段单独提供给生图模型使用,你这里不要尝试理解它们的具体外观,也不要要求必须使用所有输入图。",
params.modelImageUrl && !isProductOnlyDetailReplacement
@ -1591,7 +1596,7 @@ export async function analyzeReplacementExecutionPlan(params: {
? "3. 判断 generationRoute:prompt_pack_scene=通用场景图且可吃词包;layout_preserve_replace=锁定版式/白底/产品说明/材质说明,只做有限替换;copy_locked_regenerate=文案与画面强绑定,必须锁文案;people_relation_locked=多人/模特/互动关系图,必须锁人物关系。详情替换不允许 direct_passthrough。"
: "3. 提炼 sceneSummary、cameraSummary、placementSummary、preserveSummary、styleSummary、avoidSummary。",
isDetailReplacement
? "4. 判断 promptPackPolicy:只有通用空间场景且没有强文案、白底、多人关系、自拍/动作文字、词图强绑定限制时才 allow;白底图、产品说明图、强文案图、多人关系图、自拍/人物动作图、图文匹配强的详情页必须 forbid。"
? "4. 判断 promptPackPolicy:纯白底参数/说明/卖点页、强图文绑定且没有真实展示动作的详情页必须 forbid;通用空间场景必须 allow;人物/自拍/聚会/承重测试/使用产品等动作展示页可以 people_relation_locked + allow/soft,含义是保留人物动作与文字层级,但允许前置词包重建背景、灯光、软装和产品场景。"
: "",
isDetailReplacement
? "5. 判断 textPolicy:画面中存在营销标题、卖点、编号、参数、标签且与图匹配时必须 lock 或 preserve,避免后续生图改字、混色或重排。"
@ -1605,17 +1610,17 @@ export async function analyzeReplacementExecutionPlan(params: {
isProductOnlyDetailReplacement
? "4. 输出 generationPrompt:这是一段给后续生图模型的本屏生成指令,要求只替换产品主体,保留其余背景、文案、排版、装饰和光影完全不变。"
: isDetailReplacement
? "8. 输出 generationPrompt:这是一段给后续生图模型的本屏生成指令。若 promptPackPolicy=forbid,必须明确“不使用前置词包,参考图语义优先”;若 textPolicy=lock,必须明确文字内容、颜色、层级和位置不可改;若 peoplePolicy=preserve_relation,必须明确人物数量、关系、动作和身份约束。"
? "8. 输出 generationPrompt:这是一段给后续生图模型的本屏生成指令。若 promptPackPolicy=forbid,必须明确“不使用前置词包,参考图语义优先”;若 promptPackPolicy=allow/soft 且 peoplePolicy=preserve_relation,必须明确“保留人物数量/动作/关系与文字层级,同时使用前置词包重建背景/产品场景”;若 textPolicy=lock,必须明确文字内容、颜色、层级和位置不可改。"
: "4. 输出 generationPrompt:这是一段给后续生图模型的本屏生成指令,描述这一屏需要的场景、机位、主体位置、周边环境重建方向以及必须保留的内容。",
isProductOnlyDetailReplacement
? "5. sceneSummary、styleSummary 和 generationPrompt 必须强调‘只替换产品,不动其他内容’,不要引导重建场景。"
: isDetailReplacement
? "9. 如果 generationRoute 不是 prompt_pack_scene,sceneSummary、styleSummary 和 generationPrompt 必须优先服务参考图原有语义、版式、文案和人物关系,不要强行套前置词包。"
? "9. 如果 generationRoute 是 layout_preserve_replace 或 copy_locked_regenerate,sceneSummary、styleSummary 和 generationPrompt 必须优先服务参考图原有语义、版式和文案;如果 generationRoute 是 people_relation_locked 且 promptPackPolicy=allow/soft,则必须同时服务人物动作保真和前置词包场景改造。"
: "5. sceneSummary、styleSummary 和 generationPrompt 必须优先描述“按前置工作流重建后的目标场景”,而不是只复述参考图当前已有场景。",
isProductOnlyDetailReplacement
? "6. avoidSummary 里要明确避免改背景、改文字、改按钮、改图标、改版式、改留白、改装饰、改光影氛围。"
: isDetailReplacement
? "10. 如果前置工作流风格与当前参考图原场景冲突,只有 promptPackPolicy=allow 时才以前置词包为主;其他情况必须以参考图语义和版式为主。"
? "10. 如果前置工作流风格与当前参考图原场景冲突,promptPackPolicy=allow 时以前置词包为主;promptPackPolicy=soft 时以参考图人物/文案关系为骨架、以前置词包改造背景与产品场景;promptPackPolicy=forbid 时以参考图语义和版式为主。"
: "6. 如果前置工作流风格与当前参考图原场景冲突,必须保留版式骨架,但放弃原场景物件本身,以前置工作流指定的空间风格、软装、灯光和氛围为准。",
isProductOnlyDetailReplacement
? "7. 不要根据用户提示词或工作流风格改造场景,只服务于产品主体替换。"
@ -1752,11 +1757,13 @@ function shouldApplyPromptPackToDetailReplacement(params: {
if (
scenePlan.generationRoute === "direct_passthrough" ||
scenePlan.generationRoute === "layout_preserve_replace" ||
scenePlan.generationRoute === "copy_locked_regenerate" ||
scenePlan.generationRoute === "people_relation_locked"
scenePlan.generationRoute === "copy_locked_regenerate"
) {
return scenePlan.promptPackPolicy === "allow";
}
if (scenePlan.generationRoute === "people_relation_locked") {
return true;
}
return true;
}
@ -1828,7 +1835,9 @@ export function buildAgentReplacementPrompt(params: {
? `路由理由:${params.scenePlan.routeReason}`
: "",
params.scenePlan.whiteBackgroundLikely
? "白底/说明图判断:是,禁止强行场景化。"
? shouldApplyPromptPack
? "白底/说明图判断:是,但本屏包含人物动作/真实展示语义,允许在保留版式和文字层级的前提下按词包改造背景/产品场景。"
: "白底/说明图判断:是,禁止强行场景化。"
: "",
params.scenePlan.copyImageCoupled
? "图文绑定判断:是,文案与画面语义必须保持匹配。"
@ -1936,7 +1945,7 @@ export function buildAgentReplacementPrompt(params: {
: params.scenePlan?.peoplePolicy === "preserve_relation"
? "本屏包含人物关系:必须保留人物数量、相对位置、互动关系、动作方向和出镜比例;如果参考图文字写了自拍/合照/聚会/用餐/拿手机等动作,必须让人物执行该动作,不要把多人图改成单人图、无人图或无关模特图。"
: "如果参考图没有明确人物展示需求,不要凭空新增人物主体。",
params.scenePlan?.whiteBackgroundLikely
params.scenePlan?.whiteBackgroundLikely && !shouldApplyPromptPack
? "本屏疑似白底/说明图:禁止场景化、生活化或套空间词包,只允许保留白底/说明图语义并做必要的局部替换。"
: "",
params.referenceGeometry