diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts index bc872de90..8f68813c7 100644 --- a/lib/ai/smart-analyzer.ts +++ b/lib/ai/smart-analyzer.ts @@ -35,19 +35,36 @@ function inferDefaultImageEditSubIntent(text: string): AnalysisResult["subIntent return "enhance"; } -function isVideoGenerationRequest(text: string): boolean { - return /视频|动画|动起来|生成视频|做视频|视频生成|图生视频|文生视频|镜头|分镜|运镜|首帧|尾帧/.test( - text.toLowerCase(), - ); -} - function normalizeCompactText(text: string): string { return text.toLowerCase().replace(/\s+/g, "").trim(); } -function isExplicitImageGenerationRequest(text: string): boolean { +function isLikelyAdvisoryOrPlanningRequest(text: string): boolean { const normalizedText = text.toLowerCase(); - return /重新生成|再来一张|生成新图|新建图片|创作一张|参考.*生成|按这个风格生成|以这张图为灵感生成|仿照.*生成|出一张/.test( + return /怎么|如何|为什么|是否|可不可以|能不能|行不行|建议|思路|方案|流程|步骤|教程|推荐|对比|区别|差别|优缺点|评估|分析|判断|解释|讲讲|科普|适合吗|可行吗|值不值得|要不要|帮我看看|帮我分析|帮我判断/.test( + normalizedText, + ); +} + +function isExplicitVideoGenerationRequest(text: string): boolean { + const normalizedText = normalizeCompactText(text); + const hasStrongVideoMarkers = + /图生视频|文生视频|首帧|尾帧|分镜|运镜|视频生成/.test(normalizedText); + if (hasStrongVideoMarkers) return true; + + if (isLikelyAdvisoryOrPlanningRequest(text)) return false; + + return ( + /让.+动起来/.test(normalizedText) || + (/(生成|制作|做|创建|输出|渲染|合成|出|做成|变成|转成)/.test(normalizedText) && + /(视频|动画|短片|片子|动起来|会动)/.test(normalizedText)) + ); +} + +function isExplicitImageGenerationRequest(text: string): boolean { + const normalizedText = normalizeCompactText(text); + if (isLikelyAdvisoryOrPlanningRequest(text)) return false; + return /重新生成|再来一张|生成新图|新建图片|创作一张|生成一张|画一张|给我画|帮我画|出一张|做一张图|做一张海报|创建一张图片|参考.*生成|按这个风格生成|以这张图为灵感生成|仿照.*生成/.test( normalizedText, ); } @@ -83,7 +100,20 @@ function applyDirectExecutionPolicy( imageFromHistory?: boolean; } ): AnalysisResult { - if (isVideoGenerationRequest(params.text)) { + const hasUsableImage = + params.hasImage || Boolean(params.imageFromHistory) || (params.imageUrls?.length || 0) > 0; + const compactText = normalizeCompactText(params.text); + const hasAnyRequestText = compactText.length > 0; + const explicitVideoGenerationRequest = + hasAnyRequestText && isExplicitVideoGenerationRequest(params.text); + const explicitImageGenerationRequest = + hasAnyRequestText && isExplicitImageGenerationRequest(params.text); + const likelyAdvisoryRequest = + hasAnyRequestText && isLikelyAdvisoryOrPlanningRequest(params.text); + const likelyVisionRequest = hasAnyRequestText && isLikelyVisionRequest(params.text); + const likelyGeneralChatTurn = hasAnyRequestText && isLikelyGeneralChatTurn(params.text); + + if (explicitVideoGenerationRequest) { return { ...result, intent: "video_gen", @@ -99,18 +129,50 @@ function applyDirectExecutionPolicy( }; } - const hasUsableImage = - params.hasImage || Boolean(params.imageFromHistory) || (params.imageUrls?.length || 0) > 0; - if (!hasUsableImage) return result; + if (!hasUsableImage) { + if (!explicitImageGenerationRequest && !explicitVideoGenerationRequest) { + if (likelyAdvisoryRequest || likelyGeneralChatTurn || result.intent === "chat") { + return { + ...result, + intent: "chat", + subIntent: undefined, + confidence: Math.max(result.confidence, 0.76), + needsClarification: false, + clarifyingQuestions: [], + conversationalReply: + result.conversationalReply || "收到,我先按你的问题理解需求,再直接回答你。", + }; + } + } + + if (result.intent === "video_gen" && !explicitVideoGenerationRequest) { + return { + ...result, + intent: "chat", + subIntent: undefined, + confidence: Math.max(result.confidence, 0.74), + needsClarification: false, + clarifyingQuestions: [], + }; + } + + if (result.intent === "image_gen" && !explicitImageGenerationRequest) { + return { + ...result, + intent: "chat", + subIntent: undefined, + confidence: Math.max(result.confidence, 0.74), + needsClarification: false, + clarifyingQuestions: [], + }; + } + + return result; + } const baseResult = normalizeIntentForImageContext(result, params.text, hasUsableImage); - const compactText = normalizeCompactText(params.text); - const hasAnyRequestText = compactText.length > 0; const keepClarification = Boolean(result.needsClarification); const explicitEditRequest = hasAnyRequestText && isLikelyImageEditRequest(params.text); - const explicitImageGenerationRequest = hasAnyRequestText && isExplicitImageGenerationRequest(params.text); - const likelyVisionRequest = hasAnyRequestText && isLikelyVisionRequest(params.text); - const likelyGeneralChatTurn = hasAnyRequestText && isLikelyGeneralChatTurn(params.text); if (!hasAnyRequestText) { return { @@ -125,7 +187,7 @@ function applyDirectExecutionPolicy( }; } - if (!explicitEditRequest && !explicitImageGenerationRequest) { + if (!explicitEditRequest && !explicitImageGenerationRequest && !explicitVideoGenerationRequest) { if (likelyVisionRequest || baseResult.intent === "vision") { return { ...baseResult, @@ -139,7 +201,7 @@ function applyDirectExecutionPolicy( }; } - if (likelyGeneralChatTurn || baseResult.intent === "chat") { + if (likelyAdvisoryRequest || likelyGeneralChatTurn || baseResult.intent === "chat") { return { ...baseResult, intent: "chat", @@ -2041,7 +2103,7 @@ export function fallbackKeywordAnalysis( // === 图片编辑场景(需要有图片) === if (hasImage) { // 视频生成(图生视频/首尾帧等) - if (isVideoGenerationRequest(lowerText)) { + if (isExplicitVideoGenerationRequest(text)) { return { intent: "video_gen", confidence: 0.9, @@ -2186,7 +2248,7 @@ export function fallbackKeywordAnalysis( // === 无图片场景 === // 图片生成 - if (/画|绘制|生成图|创建.*图|设计.*图|画一个|生成一张|做一张/.test(lowerText)) { + if (isExplicitImageGenerationRequest(text)) { return { intent: "image_gen", confidence: 0.85, @@ -2195,7 +2257,7 @@ export function fallbackKeywordAnalysis( } // 视频生成 - if (isVideoGenerationRequest(lowerText)) { + if (isExplicitVideoGenerationRequest(text)) { return { intent: "video_gen", confidence: 0.85,