diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index babf53682..fef99be53 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1397,6 +1397,31 @@ export async function POST(request: Request) { const streamId = generateUUID(); await createStreamId({ streamId, chatId: id }); + if (shouldRunAgentConversation && smartAutoNeedsClarification) { + const hasCurrentImageInput = message.parts.some((part: any) => + part?.type === "file" || part?.type === "image" || part?.type === "image_url" + ); + const hasHistoryImageInput = smartAutoHistoryImages.length > 0; + const latestUserText = message.parts + .filter((part): part is { type: "text"; text: string } => part.type === "text") + .map((part) => part.text) + .join(" ") + .trim(); + + if (hasCurrentImageInput || hasHistoryImageInput || latestUserText.length > 0) { + smartAutoNeedsClarification = false; + smartAutoClarificationQuestion = undefined; + if ( + !smartAutoFriendlyMessage || + /[??]$/.test(smartAutoFriendlyMessage.trim()) || + /哪一张|哪个|请先确认/.test(smartAutoFriendlyMessage) + ) { + smartAutoFriendlyMessage = "收到,我将按你的当前需求直接执行。"; + } + console.log("[Smart Auto Mode] ✅ Force direct execution (skip clarification)"); + } + } + if (shouldRunAgentConversation && smartAutoNeedsClarification) { const clarificationMessage = smartAutoClarificationQuestion || diff --git a/lib/ai/prompt-optimizer.ts b/lib/ai/prompt-optimizer.ts index 0e606d3b5..3584cee86 100644 --- a/lib/ai/prompt-optimizer.ts +++ b/lib/ai/prompt-optimizer.ts @@ -199,6 +199,12 @@ const LLM_OPTIMIZER_SYSTEM_PROMPT = `你是一个专业的AI图片编辑提示 "negativePrompt": "需要避免的效果" } +## Nano Banana Pro 三段式模板(官方最佳实践) +optimizedPrompt 必须按以下结构输出: +目标:一句话说明最终要生成/修改的结果 +约束:一句话说明必须保持不变和禁止项 +细节:一句话补充构图、光线、材质、风格等关键细节 + ## 示例 用户请求:"把椅子颜色改成绿色" @@ -252,6 +258,10 @@ const IMAGE_GEN_OPTIMIZER_SYSTEM_PROMPT = `你是一个专业的图片生成提 4. negativePrompt 要包含:畸形、模糊、水印、文字错乱、额外主体、比例错误等。 5. 输出简洁可执行,不要解释过程。 6. 参数建议必须服务于“首图命中率”:人物全身/竖构图偏 9:16,横向场景偏 16:9,未明确时可用 auto。 +7. 按 Nano Banana Pro 官方三段式写法组织提示词: + - 目标:一句话写清最终画面 + - 约束:一句话写清必须保持不变与禁止项 + - 细节:一句话补充风格/材质/光线/构图 `; function parseOptimizerJson(responseText: string): { @@ -316,6 +326,33 @@ function sanitizeSuggestedParams(input?: { return Object.keys(output).length > 0 ? output : undefined; } +function ensureNanoBananaPromptStructure(prompt: string, intent: string): string { + const clean = prompt.replace(/\s+/g, " ").trim(); + if (!clean) return prompt; + if (/目标[::].+约束[::].+细节[::]/s.test(clean)) { + return clean + .replace(/\s*目标[::]\s*/g, "目标:") + .replace(/\s*约束[::]\s*/g, "\n约束:") + .replace(/\s*细节[::]\s*/g, "\n细节:"); + } + + const fragments = clean + .split(/[。!?!?]/) + .map((item) => item.trim()) + .filter(Boolean); + const goal = fragments[0] || clean; + const defaultConstraint = + intent === "image_edit" + ? "仅修改用户要求的部分,保持构图、主体比例、背景和光影关系稳定" + : "保持主体比例与构图稳定,避免多余元素、变形和文字错误"; + const constraint = fragments[1] || defaultConstraint; + const detail = + fragments.slice(2).join(",") || + "材质真实、边缘清晰、光线自然、色彩准确,输出商业可用效果"; + + return `目标:${goal}\n约束:${constraint}\n细节:${detail}`; +} + function shouldKeepBackgroundAsIs(originalPrompt: string, subIntent?: string): boolean { if (subIntent === "replace_background" || subIntent === "remove_background") { return false; @@ -368,11 +405,12 @@ function enforceImageEditStability(params: { function fallbackImageGenPrompt(originalPrompt: string): OptimizeResult { const normalized = originalPrompt.replace(/\s+/g, " ").trim(); - const optimizedPrompt = [ + const basePrompt = [ "高质量图像生成:", normalized, "要求:主体清晰,构图平衡,光影自然,材质真实,细节丰富,整体风格统一。", ].join(" "); + const optimizedPrompt = ensureNanoBananaPromptStructure(basePrompt, "image_gen"); return { originalPrompt, @@ -492,13 +530,19 @@ export async function optimizePromptWithLLM(params: { const stabilized = intent === "image_edit" ? enforceImageEditStability({ - optimizedPrompt: parsedResult.optimizedPrompt || originalPrompt, + optimizedPrompt: ensureNanoBananaPromptStructure( + parsedResult.optimizedPrompt || originalPrompt, + intent + ), originalPrompt, subIntent, negativePrompt: parsedResult.negativePrompt || "模糊,低质量,变形,失真", }) : { - optimizedPrompt: parsedResult.optimizedPrompt || originalPrompt, + optimizedPrompt: ensureNanoBananaPromptStructure( + parsedResult.optimizedPrompt || originalPrompt, + intent + ), negativePrompt: parsedResult.negativePrompt || "模糊,低质量,变形,失真", }; return { @@ -548,9 +592,10 @@ export function optimizePrompt(params: { // 如果没有子意图,返回通用优化 if (!subIntent) { + const basePrompt = `图片编辑:${originalPrompt}。要求:效果自然,画质清晰,细节完整。`; return { originalPrompt, - optimizedPrompt: `图片编辑:${originalPrompt}。要求:效果自然,画质清晰,细节完整。`, + optimizedPrompt: ensureNanoBananaPromptStructure(basePrompt, intent), negativePrompt: "模糊,低质量,变形,水印,失真,不自然", }; } @@ -605,6 +650,7 @@ export function optimizePrompt(params: { if (originalPrompt.length > 10 && !optimizedPrompt.includes(originalPrompt)) { optimizedPrompt = `${optimizedPrompt}\n\n用户补充要求:${originalPrompt}`; } + optimizedPrompt = ensureNanoBananaPromptStructure(optimizedPrompt, intent); // 生成友好的对话消息 let friendlyMessage = ""; diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts index 359a8bdda..b824ddc23 100644 --- a/lib/ai/smart-analyzer.ts +++ b/lib/ai/smart-analyzer.ts @@ -23,6 +23,50 @@ export interface AnalysisResult { conversationalReply?: string; // 🆕 自然对话回复 } +function inferDefaultImageEditSubIntent(text: string): AnalysisResult["subIntent"] { + const lowerText = text.toLowerCase(); + if (/抠图|去背景|透明背景|去背|扣图|移除背景/.test(lowerText)) return "remove_background"; + if (/换背景|改背景|背景换成|背景改成|白底|透明底/.test(lowerText)) return "replace_background"; + if (/风格|卡通|动漫|油画|像素风|写实|插画/.test(lowerText)) return "style_transfer"; + if (/颜色|调色|亮度|对比度|饱和度|改成.*色|换成.*色/.test(lowerText)) return "color_adjust"; + if (/去除|消除|擦掉|删除|移除|p掉|重绘/.test(lowerText)) return "inpaint"; + return "enhance"; +} + +function applyDirectExecutionPolicy( + result: AnalysisResult, + params: { + text: string; + hasImage: boolean; + imageUrls?: string[]; + imageFromHistory?: boolean; + } +): AnalysisResult { + const hasUsableImage = + params.hasImage || Boolean(params.imageFromHistory) || (params.imageUrls?.length || 0) > 0; + if (!hasUsableImage) return result; + + const compactText = params.text.replace(/\s+/g, "").trim(); + const hasAnyRequestText = compactText.length > 0; + const normalizedIntent = result.intent === "chat" || result.intent === "vision" ? "image_edit" : result.intent; + const normalizedSubIntent = + normalizedIntent === "image_edit" + ? result.subIntent || inferDefaultImageEditSubIntent(params.text) + : result.subIntent; + + return { + ...result, + intent: normalizedIntent, + subIntent: normalizedSubIntent, + confidence: Math.max(result.confidence, 0.72), + needsClarification: false, + clarifyingQuestions: [], + conversationalReply: + result.conversationalReply || + (hasAnyRequestText ? "收到,我会按你的描述直接执行生成。" : "收到图片,我先按保真优化策略直接执行生成。"), + }; +} + function normalizeIntentForImageContext( result: AnalysisResult, text: string, @@ -57,6 +101,7 @@ const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用 1. 理解用户想要对图片做什么 2. 用自然、友好的语气回复用户,告诉他你理解了他的需求并会帮他处理 3. 同时输出结构化的意图分析结果 +4. 默认直接执行,不要反问用户;只有在完全无图且需求为空时才允许澄清 ## 意图分类 @@ -102,7 +147,7 @@ const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用 - conversationalReply 要自然、友好、简洁 - 可以加一些语气词让对话更亲切,如"好的"、"没问题"、"~"等 - 说明你理解了什么需求,让用户知道你在做什么 -- 如果意图不明确,礼貌地询问用户想要什么效果 +- 有图片时优先直接执行,不要提问 - 如果提供了画布上下文,仅用于理解,不要在 conversationalReply 中复述坐标或技术细节 ## 示例 @@ -127,9 +172,10 @@ const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用 用户发了图片但没说要做什么: { "intent": "image_edit", - "confidence": 0.5, - "conversationalReply": "我看到你发了一张图片,你想对它做什么处理呢?比如抠图、换背景、变清晰?", - "needsClarification": true + "subIntent": "enhance", + "confidence": 0.72, + "conversationalReply": "收到图片,我先做保真优化并输出结果,你可以再继续细化要求~", + "needsClarification": false } 连续对话场景 - 用户说"换成蓝色"(系统自动使用历史图片): @@ -146,9 +192,8 @@ const ANALYZER_SYSTEM_PROMPT = `你是一个友好的AI图片编辑助手。用 "intent": "image_edit", "subIntent": "redo", "confidence": 0.8, - "conversationalReply": "没问题,你想怎么调整呢?比如换个颜色、调整风格?", - "needsClarification": true, - "clarifyingQuestions": ["你具体想调整哪方面呢?"] + "conversationalReply": "没问题,我先按上一轮目标继续优化并给你一个更稳的版本~", + "needsClarification": false } 连续对话场景 - 用户说"再清晰一点": @@ -191,7 +236,7 @@ export async function analyzeIntent(params: { const quickResult = quickKeywordMatch(text, hasImage); if (quickResult.confidence >= 0.9) { console.log("[Smart Analyzer] Quick match result:", quickResult); - return quickResult; + return applyDirectExecutionPolicy(quickResult, { text, hasImage, imageUrls, imageFromHistory }); } // 尝试使用 Qwen3 VL 进行深度分析(传入历史对话) @@ -199,7 +244,7 @@ export async function analyzeIntent(params: { const llmResult = await analyzWithLLM(text, hasImage, imageUrls, historyMessages, context); if (llmResult) { console.log("[Smart Analyzer] LLM analysis result:", llmResult); - return llmResult; + return applyDirectExecutionPolicy(llmResult, { text, hasImage, imageUrls, imageFromHistory }); } } catch (error) { console.error("[Smart Analyzer] LLM analysis failed:", error); @@ -208,7 +253,7 @@ export async function analyzeIntent(params: { // 回退到完整关键词分析 const fallbackResult = fallbackKeywordAnalysis(text, hasImage); console.log("[Smart Analyzer] Fallback result:", fallbackResult); - return fallbackResult; + return applyDirectExecutionPolicy(fallbackResult, { text, hasImage, imageUrls, imageFromHistory }); } /** @@ -461,9 +506,10 @@ export function fallbackKeywordAnalysis( if (/修图|P图|改图|调图|处理.*图|编辑.*图|优化.*图/.test(lowerText)) { return { intent: "image_edit", + subIntent: inferDefaultImageEditSubIntent(text), confidence: 0.8, - needsClarification: true, - conversationalReply: "我看到你想编辑这张图片,你具体想要做什么处理呢?比如抠图、换背景、变清晰?", + needsClarification: false, + conversationalReply: "收到,我先按你的需求直接执行图片优化。", }; } @@ -479,9 +525,10 @@ export function fallbackKeywordAnalysis( // 有图但意图不明确 return { intent: "image_edit", - confidence: 0.5, - needsClarification: true, - conversationalReply: "我看到你发了一张图片,你想对它做什么处理呢?比如抠图、换背景、变清晰?", + subIntent: inferDefaultImageEditSubIntent(text), + confidence: 0.75, + needsClarification: false, + conversationalReply: "收到图片,我先按保真优化策略直接执行。", }; }