diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index aa1cf17e8..a55c542b8 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -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; diff --git a/lib/ai/auto-agent-graph.ts b/lib/ai/auto-agent-graph.ts index b9067c439..40b1c0c3f 100644 --- a/lib/ai/auto-agent-graph.ts +++ b/lib/ai/auto-agent-graph.ts @@ -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") { diff --git a/lib/ai/prompt-optimizer.ts b/lib/ai/prompt-optimizer.ts index 9b40f410b..0e606d3b5 100644 --- a/lib/ai/prompt-optimizer.ts +++ b/lib/ai/prompt-optimizer.ts @@ -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; } diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts index da6166e1c..359a8bdda 100644 --- a/lib/ai/smart-analyzer.ts +++ b/lib/ai/smart-analyzer.ts @@ -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); }