diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 15429a18e..d1cff426d 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -699,12 +699,18 @@ export async function POST(request: Request) { !smartAutoFriendlyMessage; if (needsRicherAutoReply) { - smartAutoFriendlyMessage = buildAutoModeFallbackMessage({ - text: autoTextForFallback, - hasImage, - fromHistory: smartAutoHistoryImages.length > 0, - }); - console.log("[Smart Auto Mode] 🧩 Upgraded to richer conversational reply"); + if (isImageTask) { + smartAutoFriendlyMessage = + autoGraphResult.analysis?.conversationalReply || + "我先分析你这轮图片里的主体、风格和构图,再给你执行方案。"; + } else { + smartAutoFriendlyMessage = buildAutoModeFallbackMessage({ + text: autoTextForFallback, + hasImage, + fromHistory: smartAutoHistoryImages.length > 0, + }); + } + console.log("[Smart Auto Mode] 🧩 Filled missing conversational reply"); } if (autoGraphResult.optimizedPrompt) { @@ -737,12 +743,8 @@ export async function POST(request: Request) { let bestModel = null; if (hasImage) { bestModel = allModels.find(m => m.model.supportsImageGen && !m.model.isSubModel); - // 回退时也保持结构化、可感知的自动模式反馈 - smartAutoFriendlyMessage = buildAutoModeFallbackMessage({ - text: autoTextForFallback, - hasImage: true, - fromHistory: autoFromHistoryForFallback || smartAutoHistoryImages.length > 0, - }); + smartAutoFriendlyMessage = + "我先完成这一轮图片分析,再按你的要求执行修改并返回结果。"; } else { smartAutoFriendlyMessage = buildAutoModeFallbackMessage({ text: autoTextForFallback, diff --git a/lib/ai/auto-agent-graph.ts b/lib/ai/auto-agent-graph.ts index c37234594..74c70f314 100644 --- a/lib/ai/auto-agent-graph.ts +++ b/lib/ai/auto-agent-graph.ts @@ -69,8 +69,11 @@ async function generateAgentConversation( const { getLanguageModelById } = await import("./dynamic-provider"); const allModels = await getEnabledAiModelsWithProvider(); + const preferredVisionModel = allModels.find( + (m) => m.provider.type === "alibaba" && m.model.supportsVision && m.model.isEnabled + ); const candidateModel = state.hasImage - ? allModels.find((m) => m.model.supportsVision && m.model.isEnabled) + ? preferredVisionModel || allModels.find((m) => m.model.supportsVision && m.model.isEnabled) : allModels.find((m) => m.model.isEnabled && !m.model.isSubModel); if (!candidateModel) { @@ -87,10 +90,42 @@ async function generateAgentConversation( .map((msg) => `${msg.role === "user" ? "用户" : "助手"}: ${compactText(msg.content, 120)}`) .join("\n"); + const validImageUrls = state.imageUrls + .filter((url) => /^https?:\/\//i.test(url)) + .slice(0, 4); + const imageInfo = state.hasImage ? `用户本轮可用图片数量:${state.imageUrls.length || 1}。图片来源:${state.imageFromHistory ? "历史消息复用" : "本轮上传"}。` : "用户本轮没有上传图片。"; + let visualAnalysis = ""; + if (state.hasImage && validImageUrls.length > 0) { + try { + const visionPrompt = `请分析以下参考图片,给出可用于生图/修图执行前决策的结果。 +要求: +1) 每张图输出 1 行,格式:图X:主体...;风格...;构图...;光影与色调...;材质/氛围... +2) 只写客观可见信息,不要编造品牌、尺寸、镜头参数。 +3) 全部输出控制在 6 行内。`; + + const visionInputs = validImageUrls.map((url) => ({ type: "image" as const, image: url })); + const { text: visionText } = await generateText({ + model, + messages: [ + { + role: "user" as const, + content: [{ type: "text" as const, text: visionPrompt }, ...visionInputs], + }, + ], + temperature: 0.2, + maxTokens: 360, + } as any); + + visualAnalysis = visionText.trim(); + } catch (error) { + visualAnalysis = ""; + } + } + const systemPrompt = `你是一个真实执行中的 AI 生图/修图 Agent。 请根据用户需求和本轮输入图片,给出“执行前”的分析汇报。 @@ -110,6 +145,7 @@ async function generateAgentConversation( `【用户原始需求】${state.text || "(无文本)"}`, `【意图分析】intent=${analysis.intent}; subIntent=${analysis.subIntent || "none"}; confidence=${analysis.confidence}`, `【图片信息】${imageInfo}`, + visualAnalysis ? `【视觉模型预分析】\n${visualAnalysis}` : "", state.context ? `【画布上下文】${compactText(state.context, 300)}` : "", history ? `【最近对话】\n${history}` : "", state.hasImage @@ -119,10 +155,7 @@ async function generateAgentConversation( .filter(Boolean) .join("\n\n"); - const imageInputs = state.imageUrls - .filter((url) => /^https?:\/\//i.test(url)) - .slice(0, 4) - .map((url) => ({ type: "image" as const, image: url })); + const imageInputs = validImageUrls.map((url) => ({ type: "image" as const, image: url })); const requestPayload = state.hasImage && imageInputs.length > 0 ? {