修复连续生图保真:锁定视角背景并禁用编辑扩写
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-12 15:55:26 +08:00
parent ef8326fd33
commit 20c9f6d821
4 changed files with 117 additions and 5 deletions

View File

@ -200,7 +200,10 @@ function hasUserCustomizedImageParams(params?: PostRequestBody["imageParams"]):
const customizedImageSize =
typeof params.imageSize === "string" && params.imageSize.trim() && params.imageSize.toUpperCase() !== "2K";
const customizedImageCount = typeof params.imageCount === "number" && params.imageCount !== 1;
const customizedAdvanced = Boolean(params.size || params.seed !== undefined || params.n || params.negativePrompt);
const customizedPromptExtend = typeof params.promptExtend === "boolean";
const customizedAdvanced = Boolean(
params.size || params.seed !== undefined || params.n || params.negativePrompt || customizedPromptExtend
);
return customizedAspectRatio || customizedImageSize || customizedImageCount || customizedAdvanced;
}
@ -236,6 +239,9 @@ function mergeImageParamsWithAgentRecommendation(params: {
if (intent === "image_edit") {
merged.imageCount = 1;
if (typeof base.promptExtend !== "boolean") {
merged.promptExtend = false;
}
}
return Object.keys(merged).length > 0 ? merged : undefined;

View File

@ -111,6 +111,10 @@ function sanitizeRecommendedImageParams(
safe.promptExtend = params.promptExtend;
}
if (intent === "image_edit") {
safe.promptExtend = false;
}
return Object.keys(safe).length > 0 ? safe : undefined;
}
@ -121,7 +125,7 @@ function heuristicImageParams(
const normalized = text.toLowerCase();
const params: AgentRecommendedImageParams = {
imageSize: "2K",
promptExtend: true,
promptExtend: analysis.intent === "image_edit" ? false : true,
};
if (analysis.intent === "image_edit") {

View File

@ -316,6 +316,56 @@ function sanitizeSuggestedParams(input?: {
return Object.keys(output).length > 0 ? output : undefined;
}
function shouldKeepBackgroundAsIs(originalPrompt: string, subIntent?: string): boolean {
if (subIntent === "replace_background" || subIntent === "remove_background") {
return false;
}
return !/(换背景|改背景|替换背景|背景改成|背景换成|白底|黑底|透明背景|场景换成|背景变成)/i.test(
originalPrompt
);
}
function shouldKeepCameraAsIs(originalPrompt: string): boolean {
return !/(角度|视角|机位|镜头|构图|俯拍|仰拍|侧拍|旋转|翻转|远景|近景|特写)/i.test(
originalPrompt
);
}
function enforceImageEditStability(params: {
optimizedPrompt: string;
originalPrompt: string;
subIntent?: string;
negativePrompt?: string;
}) {
const keepBackground = shouldKeepBackgroundAsIs(params.originalPrompt, params.subIntent);
const keepCamera = shouldKeepCameraAsIs(params.originalPrompt);
const stabilityRules = [
"只修改用户明确要求的内容,其他元素保持不变",
keepCamera ? "保持原图拍摄角度、主体朝向、构图和透视关系不变" : "",
keepBackground
? "保持原图背景颜色与明暗不变;若原图是白底,继续保持纯白背景(#FFFFFF)"
: "",
"保持主体形状比例、材质纹理和光影一致,禁止出现黑底/灰底污染",
].filter(Boolean);
const mergedNegativePrompt = [
params.negativePrompt || "",
keepCamera ? "角度变化,构图漂移,主体旋转" : "",
keepBackground ? "背景变黑,背景变灰,背景误改,白底丢失" : "",
"主体变形,比例错误,材质改变,边缘脏污",
]
.filter(Boolean)
.join(",");
const promptWithStability = `${params.optimizedPrompt.trim()}\n\n保真约束:${stabilityRules.join(";")}。`;
return {
optimizedPrompt: promptWithStability,
negativePrompt: mergedNegativePrompt,
};
}
function fallbackImageGenPrompt(originalPrompt: string): OptimizeResult {
const normalized = originalPrompt.replace(/\s+/g, " ").trim();
const optimizedPrompt = [
@ -439,10 +489,22 @@ export async function optimizePromptWithLLM(params: {
const parsedResult = parseOptimizerJson(responseText);
if (parsedResult) {
console.log("[Prompt Optimizer LLM] Result parsed");
const stabilized =
intent === "image_edit"
? enforceImageEditStability({
optimizedPrompt: parsedResult.optimizedPrompt || originalPrompt,
originalPrompt,
subIntent,
negativePrompt: parsedResult.negativePrompt || "模糊,低质量,变形,失真",
})
: {
optimizedPrompt: parsedResult.optimizedPrompt || originalPrompt,
negativePrompt: parsedResult.negativePrompt || "模糊,低质量,变形,失真",
};
return {
originalPrompt,
optimizedPrompt: parsedResult.optimizedPrompt || originalPrompt,
negativePrompt: parsedResult.negativePrompt || "模糊,低质量,变形,失真",
optimizedPrompt: stabilized.optimizedPrompt,
negativePrompt: stabilized.negativePrompt,
recommendedImageParams: sanitizeSuggestedParams(parsedResult.suggestedParams),
friendlyMessage: intent === "image_edit" ? FRIENDLY_MESSAGES[subIntent || ""]?.template : undefined,
};
@ -574,6 +636,17 @@ export function optimizePrompt(params: {
friendlyMessage: friendlyMessage || `好的,我来帮你处理这张图片~`,
};
if (intent === "image_edit") {
const stabilized = enforceImageEditStability({
optimizedPrompt: result.optimizedPrompt,
originalPrompt,
subIntent,
negativePrompt: result.negativePrompt,
});
result.optimizedPrompt = stabilized.optimizedPrompt;
result.negativePrompt = stabilized.negativePrompt;
}
console.log("[Prompt Optimizer] Output:", result);
return result;
}

View File

@ -23,6 +23,35 @@ export interface AnalysisResult {
conversationalReply?: string; // 🆕 自然对话回复
}
function normalizeIntentForImageContext(
result: AnalysisResult,
text: string,
hasImage: boolean
): AnalysisResult {
if (!hasImage || result.intent !== "image_gen") {
return result;
}
const normalizedText = text.toLowerCase();
const explicitGenerate =
/重新生成|再来一张|生成新图|新建图片|创作一张|参考.*生成|按这个风格生成|以这张图为灵感生成/.test(
normalizedText
);
const editSignal =
/改|调整|优化|去掉|换成|改成|保留|继续|再.*一点|修|抠|替换|增强|清晰|重绘/.test(normalizedText);
if (explicitGenerate && !editSignal) {
return result;
}
return {
...result,
intent: "image_edit",
subIntent: result.subIntent,
confidence: Math.max(result.confidence, 0.78),
};
}
// 系统提示词 - 针对图片编辑场景优化,生成自然对话
const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用户会给你发送图片和编辑请求,你需要:
1. 理解用户想要对图片做什么
@ -319,7 +348,7 @@ ${historyText}
if (jsonMatch) {
try {
const result = JSON.parse(jsonMatch[0]) as AnalysisResult;
return result;
return normalizeIntentForImageContext(result, text, hasImage);
} catch (e) {
console.error("[Smart Analyzer] Failed to parse LLM response:", e);
}