diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 4d827169d..413e28b78 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -331,96 +331,113 @@ export async function POST(request: Request) { let finalModelId = selectedChatModel; - // ========== Auto Mode Logic ========== + // ========== Smart Auto Mode Logic ========== if (finalModelId === "auto") { - console.log("[Auto Mode] Detecting intent..."); - const lastUserMessage = message; - const textContent = lastUserMessage.parts - .filter((p) => p.type === 'text') - .map((p) => p.text) - .join(' '); - // @ts-ignore - const hasImage = lastUserMessage.parts.some((p) => p.type === 'file' || p.type === 'image' || p.type === 'image_url'); + console.log("[Smart Auto Mode] ๐Ÿš€ Starting intelligent analysis..."); + + try { + // 1. ๆๅ–็”จๆˆทๆถˆๆฏๅ†…ๅฎน + const lastUserMessage = message; + const textContent = lastUserMessage.parts + .filter((p): p is { type: "text"; text: string } => p.type === "text") + .map((p) => p.text) + .join(" "); - // ่Žทๅ–ๆ‰€ๆœ‰ๅฏ็”จๆจกๅž‹ - const allModels = await getEnabledAiModelsWithProvider(); - console.log("[Auto Mode] Available models:", allModels.map(m => `${m.model.displayName} (่ง†้ข‘:${m.model.displayName.includes('่ง†้ข‘')}, ๅ›พ็‰‡:${m.model.supportsImageGen}, ่ง†่ง‰:${m.model.supportsVision})`)); - - let bestModel = null; + // @ts-ignore - ๆฃ€ๆŸฅๅ„็งๅฏ่ƒฝ็š„ๅ›พ็‰‡็ฑปๅž‹ + const hasImage = lastUserMessage.parts.some( + (p: any) => p.type === "file" || p.type === "image" || p.type === "image_url" + ); - // ๆ™บ่ƒฝๆ„ๅ›พ่ฏ†ๅˆซ - ๅŸบไบŽๅ…ณ้”ฎ่ฏๅŒน้… - // ๅฎšไน‰ๅ„็ฑปๆ„ๅ›พ็š„ๅ…ณ้”ฎ่ฏๅบ“ - const videoKeywords = /(่ง†้ข‘|video|movie|film|motion|mp4|avi|mov|wmv|flv|mkv|webm|m4v|็”Ÿๆˆ่ง†้ข‘|ๅˆถไฝœ่ง†้ข‘|ๅˆ›ๅปบ่ง†้ข‘|ๅš่ง†้ข‘|ๆ‹่ง†้ข‘|ๅฝ•่ง†้ข‘|ๅฝ•ๅˆถ|่ง†้ข‘ๅ‰ช่พ‘|ๅ‰ช่พ‘่ง†้ข‘|ๅ‰ช่พ‘|็ผ–่พ‘่ง†้ข‘|sora|ๅŠจ่ตทๆฅ|ๅŠจ็”ป|animation|animate|animated|ๅŠจๆ€|ๅŠจๆ•ˆ|gif|clip|clips|็Ÿญ่ง†้ข‘|้•ฟ่ง†้ข‘|ๅฝฑ็‰‡|็”ตๅฝฑ|ๅพฎ็”ตๅฝฑ|ๅฎฃไผ ็‰‡|ๅนฟๅ‘Š็‰‡|ไบงๅ“่ง†้ข‘|ๆผ”็คบ่ง†้ข‘|ๆ•™็จ‹่ง†้ข‘|mv|music video|็‰‡ๆฎต|้•œๅคด|shot|ๅœบๆ™ฏ|scene|็‰นๆ•ˆ|vfx|effects|่ฝฌๅœบ|transition|ๅญ—ๅน•|subtitle|้…้Ÿณ|dubbing|ๆ…ขๅŠจไฝœ|ๅฟซ่ฟ›|timelapse|ๅปถๆ—ถ)/i; - - const imageEditKeywords = /(ไฟฎๅ›พ|Pๅ›พ|ๆ”นๅ›พ|่ฐƒๅ›พ|ไผ˜ๅŒ–ๅ›พ็‰‡|็พŽๅŒ–ๅ›พ็‰‡|ๅค„็†ๅ›พ็‰‡|็ผ–่พ‘ๅ›พ็‰‡|็ผ–่พ‘็…ง็‰‡|็ผ–่พ‘่ฟ™ๅผ |็ผ–่พ‘|edit photo|edit image|ไฟฎๆ”นๅ›พ็‰‡|่ฐƒๆ•ดๅ›พ็‰‡|ๅ›พ็‰‡ๅค„็†|็…ง็‰‡ๅค„็†|ๅค„็†่ฟ™ๅผ |ๅค„็†็…ง็‰‡|่ƒŒๆ™ฏ|background|ๅŽป่ƒŒๆ™ฏ|ๆข่ƒŒๆ™ฏ|ๆ”น่ƒŒๆ™ฏ|่ƒŒๆ™ฏ่‰ฒ|ๅŽป่ƒŒ|ๆŠ ๅ›พ|ๆ‰ฃๅ›พ|ๆŠ ๅ‡บ|ๆ‰ฃๅ‡บ|ๅŽป้™ค|็งป้™ค|ๅˆ ้™ค|ๆ“ฆ้™ค|ๆถˆ้™ค|ๆŠนๆމ|ๆธ…้™ค|ๆ‹ฟๆމ|ๅผ„ๆމ|ๅŽปๆމ|ๅนฒๆމ|้š่—|้ฎๆŒก|remove|delete|erase|wipe|clear|eliminate|change|modify|ไฟฎๆ”น|ๅ˜ๆธ…ๆ™ฐ|ๆธ…ๆ™ฐๅŒ–|้”ๅŒ–|sharpen|sharpening|ๆจก็ณŠ|blur|blurry|่™šๅŒ–|bokeh|ๆ•ฃๆ™ฏ|ไฟฎๅค|repair|restoration|fix|่ฟ˜ๅŽŸ|ๆขๅค|recover|ๅค„็†|process|processing|ๆข|ๆ”น|ๆ›ฟๆข|ๆ›ดๆข|่ฝฌๆข|transform|replace|swap|exchange|่ฐƒ่‰ฒ|่ฐƒๆ•ด้ขœ่‰ฒ|color grade|grading|้…่‰ฒ|ๆปค้•œ|filter|filters|effect|็‰นๆ•ˆ|effects|่ฃๅ‰ช|ๅ‰ช่ฃ|crop|cropping|cut|cutting|ๆ—‹่ฝฌ|rotate|rotation|็ฟป่ฝฌ|flip|flipping|mirror|้•œๅƒ|mirroring|็ผฉๆ”พ|resize|resizing|scale|scaling|ๆ”พๅคง|enlarge|enlargement|zoom|zoom in|็ผฉๅฐ|shrink|zoom out|ๅขžๅผบ|enhance|enhancement|improve|improvement|ๆๅ‡|ๅ‡็บง|่ดจ้‡|quality|้™ๅ™ช|ๅŽปๅ™ช|denoise|denoising|็พŽ้ขœ|beauty|็ฃจ็šฎ|smooth|smoothing|็˜ฆ่„ธ|face slim|็พŽ็™ฝ|whitening|lighten|lighting|ไบฎๅบฆ|brightness|ๅฏนๆฏ”ๅบฆ|contrast|้ฅฑๅ’Œๅบฆ|saturation|ๆ›ๅ…‰|exposure|้ซ˜ๅ…‰|highlight|้˜ดๅฝฑ|shadow|้”ๅบฆ|sharpness|ๅŠ ๆฐดๅฐ|ๅŽปๆฐดๅฐ|watermark|unwatermark|ๅˆๆˆ|composite|compositing|ๆ‹ผๆŽฅ|ๆ‹ผๅ›พ|merge|merging|ๅ ๅŠ |overlay|blend|blending|้€ๆ˜Ž|transparency|alpha|่’™็‰ˆ|mask|masking)/i; - - const imageGenKeywords = /(็”ป|็ป˜ๅˆถ|็ป˜|็ป˜็”ป|็”ปๅ‡บ|็”ปไธช|็”ปไธ€ไธช|็”ปไธ€ๅผ |็”ป้ข|ๅธฎๆˆ‘็”ป|็ป™ๆˆ‘็”ป|็”ปๅผ |็ป˜ๅ›พ|็”Ÿๆˆๅ›พ็‰‡|็”Ÿๆˆๅ›พๅƒ|็”Ÿๆˆ็…ง็‰‡|็”Ÿๆˆไธ€ไธช|็”Ÿๆˆๅผ |ๅˆ›ๅปบๅ›พ็‰‡|ๅˆ›ๅปบๅ›พๅƒ|ๅšๅ›พ|ๅˆถๅ›พ|ไฝœๅ›พ|็”ปๅ›พ|็”ป็”ป|ๆƒณ่ฆ.*ๅ›พ|้œ€่ฆ.*ๅ›พ|ๆฅๅผ |ๆฅไธช|draw|paint|sketch|็ด ๆ|้€Ÿๅ†™|render|rendering|ๆธฒๆŸ“|picture|photo|image|ๅ›พๅƒ|ๅ›พ็‰‡|็…ง็‰‡|็”Ÿๅ›พ|ๅ‡บๅ›พ|็”Ÿๆˆไธ€ๅผ |็”Ÿๆˆ|create image|generate image|make image|produce|design|designing|่ฎพ่ฎก|่ฎพ่ฎกไธ€ไธช|ๆ’็”ป|illustration|illustrate|ๆผซ็”ป|comic|ๅก้€š|cartoon|anime|ๅŠจๆผซ|ไบŒๆฌกๅ…ƒ|ๆตทๆŠฅ|poster|logo|ๆ ‡ๅฟ—|ๅ•†ๆ ‡|ๅ›พๆ ‡|icon|banner|ๆจชๅน…|่ƒŒๆ™ฏๅ›พ|่ƒŒๆ™ฏ|ๅฃ็บธ|wallpaper|ๆกŒ้ข|้…ๅ›พ|ๅฐ้ข|cover|็ผฉ็•ฅๅ›พ|thumbnail|ๅคดๅƒ|profile|avatar|artwork|่‰บๆœฏไฝœๅ“|art|artistic|็พŽๆœฏ|fine art|็ด ๆ|material|asset|ๆฆ‚ๅฟตๅ›พ|concept art|ๅนณ้ข|graphic|graphics|่ง†่ง‰|visual|ๅฏ่ง†ๅŒ–|visualization|infographic|ไฟกๆฏๅ›พ)/i; - - const visionKeywords = /(็œ‹|็œ‹็œ‹|็œ‹ไธ€ไธ‹|็œ‹ไธ‹|่ฏ†ๅˆซ|่ฎคๅ‡บ|่พจ่ฎค|recognize|identify|่ฟ™ๆ˜ฏไป€ไนˆ|ๆ˜ฏไป€ไนˆ|what is|what\'s|ๅˆ†ๆž|analyze|analysis|ๆ่ฟฐ|describe|description|่งฃ้‡Š|explain|่ฎฒ่งฃ|tell me|่ฏด่ฏด|ไป‹็ป|็†่งฃ|understand|ๆฃ€ๆต‹|detect|detection|ๆ‰พ|ๆ‰พๅ‡บ|find|ๅ‘็Žฐ|observe|่ง‚ๅฏŸ|examine|ๆฃ€ๆŸฅ|่ฏปๅ–|read|้˜…่ฏป|็ฟป่ฏ‘ๅ›พ็‰‡|ocr|ๆ–‡ๅญ—่ฏ†ๅˆซ|ๆๅ–ๆ–‡ๅญ—|ๅ›พ็‰‡.*ๅ†…ๅฎน|็…ง็‰‡.*ๅ†…ๅฎน|image.*content)/i; - - if (videoKeywords.test(textContent)) { - console.log("[Auto Mode] Intent: VIDEO - ่ง†้ข‘็”Ÿๆˆ"); - bestModel = allModels.find(m => m.model.displayName.includes("่ง†้ข‘")); - } - // 2. ๅ›พ็‰‡็ผ–่พ‘๏ผš็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ๅนถ่ฆๆฑ‚็ผ–่พ‘ - else if (hasImage && imageEditKeywords.test(textContent)) { - console.log("[Auto Mode] Intent: IMAGE_EDIT - ๅ›พ็‰‡็ผ–่พ‘"); - bestModel = allModels.find(m => m.model.displayName.includes("ๅ›พ็‰‡็ผ–่พ‘") || m.model.displayName.includes("็ผ–่พ‘")); - if (!bestModel) { - bestModel = allModels.find(m => m.model.supportsVision && !m.model.displayName.includes("่ง†้ข‘")); + // @ts-ignore - ๆๅ–ๅ›พ็‰‡URL + const imageUrls = lastUserMessage.parts + .filter((p: any) => p.type === "file" && p.url) + .map((p: any) => p.url as string); + + console.log("[Smart Auto Mode] Input:", { textContent: textContent.substring(0, 100), hasImage, imageCount: imageUrls.length }); + + // 2. ่ฐƒ็”จๆ™บ่ƒฝๅˆ†ๆžๅ™จ + const { analyzeIntent } = await import("@/lib/ai/smart-analyzer"); + const analysisResult = await analyzeIntent({ + text: textContent, + hasImage, + imageUrls, + }); + + console.log("[Smart Auto Mode] Analysis result:", { + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + confidence: analysisResult.confidence, + needsClarification: analysisResult.needsClarification, + }); + + // 3. ๅฆ‚ๆžœๆ˜ฏๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ๏ผŒไผ˜ๅŒ–ๆ็คบ่ฏ + if (analysisResult.intent === "image_edit" && analysisResult.subIntent) { + const { optimizePrompt } = await import("@/lib/ai/prompt-optimizer"); + const optimizedResult = optimizePrompt({ + originalPrompt: textContent, + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + }); + + console.log("[Smart Auto Mode] โœจ Prompt optimized:", { + original: textContent.substring(0, 50), + optimized: optimizedResult.optimizedPrompt.substring(0, 100), + }); + + // ๆ›ฟๆขๆถˆๆฏไธญ็š„ๆ–‡ๆœฌไธบไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + message.parts = message.parts.map((part) => { + if (part.type === "text") { + return { + ...part, + text: optimizedResult.optimizedPrompt, + }; + } + return part; + }); } - } - // 3. ๅ›พ็‰‡็”Ÿๆˆ๏ผš็”จๆˆท่ฆๆฑ‚็”ปๅ›พ/็”Ÿๆˆๅ›พ็‰‡๏ผˆไธ้œ€่ฆไธŠไผ ๅ›พ็‰‡๏ผ‰ - else if (imageGenKeywords.test(textContent)) { - console.log("[Auto Mode] Intent: IMAGE_GEN - ๅ›พ็‰‡็”Ÿๆˆ"); - bestModel = allModels.find(m => m.model.displayName.includes("ๅ›พ็‰‡็ผ–่พ‘") || m.model.supportsImageGen); - // ๆŽ’้™ค่ง†้ข‘ๆจกๅž‹ - if (bestModel && bestModel.model.displayName.includes("่ง†้ข‘")) { - bestModel = allModels.find(m => (m.model.displayName.includes("ๅ›พ็‰‡็ผ–่พ‘") || m.model.supportsImageGen) && !m.model.displayName.includes("่ง†้ข‘")); + + // 4. ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹ + const { routeToModel } = await import("@/lib/ai/model-router"); + const routeResult = await routeToModel({ + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + hasImage, + }); + + finalModelId = routeResult.id; + console.log(`[Smart Auto Mode] โœ… Routed to: ${routeResult.displayName} (${routeResult.reason})`); + + } catch (autoError) { + // ๆ™บ่ƒฝๅˆ†ๆžๅคฑ่ดฅๆ—ถ๏ผŒๅ›ž้€€ๅˆฐ็ฎ€ๅ•้€ป่พ‘ + console.error("[Smart Auto Mode] โŒ Error, falling back:", autoError); + + const allModels = await getEnabledAiModelsWithProvider(); + // @ts-ignore - ๆฃ€ๆŸฅๅ„็งๅฏ่ƒฝ็š„ๅ›พ็‰‡็ฑปๅž‹ + const hasImage = message.parts.some( + (p: any) => p.type === "file" || p.type === "image" || p.type === "image_url" + ); + + // ็ฎ€ๅ•ๅ›ž้€€๏ผšๆœ‰ๅ›พ้€‰ๅ›พ็‰‡ๆจกๅž‹๏ผŒๆ— ๅ›พ้€‰้ป˜่ฎคๆจกๅž‹ + let bestModel = null; + if (hasImage) { + bestModel = allModels.find(m => m.model.supportsImageGen && !m.model.isSubModel); } - } - // 4. ๅคšๆจกๆ€็†่งฃ๏ผš็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒๆƒณ่ฎฉAI็œ‹ๅ›พ็†่งฃๅ†…ๅฎน - else if (hasImage && visionKeywords.test(textContent)) { - console.log("[Auto Mode] Intent: VISION - ๅคšๆจกๆ€็†่งฃ"); - bestModel = allModels.find(m => m.model.displayName.includes("ๅคšๆจกๆ€") || m.model.supportsVision); if (!bestModel) { - bestModel = allModels.find(m => m.model.supportsVision && !m.model.displayName.includes("่ง†้ข‘")); - } - } - // 5. ๅคšๆจกๆ€็†่งฃ๏ผš็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ไฝ†ๆฒกๆœ‰ๆ˜Ž็กฎๆ„ๅ›พ๏ผˆ้ป˜่ฎค็†่งฃๅ›พ็‰‡๏ผ‰ - else if (hasImage) { - console.log("[Auto Mode] Intent: VISION - ๅคšๆจกๆ€็†่งฃ๏ผˆๅ›พ็‰‡ๅทฒไธŠไผ ๏ผ‰"); - bestModel = allModels.find(m => m.model.displayName.includes("ๅคšๆจกๆ€") || m.model.supportsVision); - if (!bestModel) { - bestModel = allModels.find(m => m.model.supportsVision); - } - } - // 6. ้ป˜่ฎค๏ผšๆ™ฎ้€šๆ–‡ๆœฌๅฏน่ฏ - else { - console.log("[Auto Mode] Intent: CHAT - ๆ™ฎ้€šๅฏน่ฏ"); - // ไผ˜ๅ…ˆไฝฟ็”จๅคšๆจกๆ€ๆจกๅž‹ๆˆ–้ป˜่ฎคๆจกๅž‹ - bestModel = allModels.find(m => m.model.displayName.includes("ๅคšๆจกๆ€")); - if (!bestModel) { - bestModel = allModels.find(m => m.model.isDefault); + bestModel = allModels.find(m => m.model.isDefault && !m.model.isSubModel); } if (!bestModel && allModels.length > 0) { - bestModel = allModels[0]; + bestModel = allModels.find(m => !m.model.isSubModel) || allModels[0]; + } + + if (bestModel) { + finalModelId = bestModel.model.id; + console.log(`[Smart Auto Mode] Fallback selected: ${bestModel.model.displayName}`); + } else { + return new ChatSDKError("bad_request:api", "No available models for auto mode").toResponse(); } } - - // ๆœ€็ปˆๅ…œๅบ• - if (!bestModel && allModels.length > 0) { - bestModel = allModels.find(m => m.model.isDefault) || allModels[0]; - } - - if (bestModel) { - console.log(`[Auto Mode] โœ… Selected: ${bestModel.model.displayName} (${bestModel.model.id})`); - finalModelId = bestModel.model.id; - } else { - return new ChatSDKError("bad_request:api", "No available models for auto mode").toResponse(); - } } - // ========== End Auto Mode Logic ========== + // ========== End Smart Auto Mode Logic ========== const session = await auth(); diff --git a/docs/smart-auto-implementation-guide.md b/docs/smart-auto-implementation-guide.md new file mode 100644 index 000000000..7c9cfa825 --- /dev/null +++ b/docs/smart-auto-implementation-guide.md @@ -0,0 +1,755 @@ +# ๆ™บ่ƒฝๅ›พ็‰‡็ผ–่พ‘ - ๅฎžๆ–ฝๅผ€ๅ‘ๆŒ‡ๅ— + +> ็‰ˆๆœฌ: v1.0 +> ๆ—ฅๆœŸ: 2025-01-31 +> ็›ฎๆ ‡: ๅœจ่Šๅคฉๅฏน่ฏๆก†ไธญๅฎž็Žฐๆ™บ่ƒฝๅ›พ็‰‡็ผ–่พ‘ๅŠŸ่ƒฝ + +--- + +## ไธ€ใ€ๆ ธๅฟƒๆต็จ‹ + +``` +็”จๆˆทๅœจๅฏน่ฏๆก†ไธญๅ‘้€ [ๅ›พ็‰‡ + ๆ–‡ๅญ—] + โ”‚ + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ POST /api/chat โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 1. ๆๅ–็”จๆˆทๆถˆๆฏ โ”‚ โ”‚ +โ”‚ โ”‚ - ๅ›พ็‰‡ URL (from message.parts where type='file') โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ–‡ๅญ—ๅ†…ๅฎน (from message.parts where type='text') โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 2. ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆž (ๅฆ‚ๆžœ modelId === 'auto') โ”‚ โ”‚ +โ”‚ โ”‚ - ่ฐƒ็”จ Qwen3 VL ๅˆ†ๆž็”จๆˆทๆ„ๅ›พ โ”‚ โ”‚ +โ”‚ โ”‚ - ่ฟ”ๅ›ž: intent, subIntent, confidence โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 3. ๆ็คบ่ฏไผ˜ๅŒ– (ไป…ๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ) โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ นๆฎๅญๆ„ๅ›พ้€‰ๆ‹ฉๆจกๆฟ โ”‚ โ”‚ +โ”‚ โ”‚ - ๅกซๅ……็”จๆˆทๅ‚ๆ•ฐ๏ผŒ็”Ÿๆˆไธ“ไธšๆ็คบ่ฏ โ”‚ โ”‚ +โ”‚ โ”‚ - ๆทปๅŠ ่ดŸ้ขๆ็คบ่ฏ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 4. ๆจกๅž‹่ทฏ็”ฑ โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ นๆฎๆ„ๅ›พ้€‰ๆ‹ฉๆœ€ไฝณๆจกๅž‹ โ”‚ โ”‚ +โ”‚ โ”‚ - ๅ›พ็‰‡็ผ–่พ‘ โ†’ alibaba-image โ”‚ โ”‚ +โ”‚ โ”‚ - ่ง†่ง‰็†่งฃ โ†’ alibaba (Qwen3 VL) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 5. ่ฐƒ็”จๅฏนๅบ”ๆจกๅž‹็”Ÿๆˆ โ”‚ โ”‚ +โ”‚ โ”‚ - ไฝฟ็”จไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ โ”‚ โ”‚ +โ”‚ โ”‚ - ไผ ๅ…ฅ็”จๆˆทๅ›พ็‰‡ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ 6. ่ฟ”ๅ›ž็ป“ๆžœๅˆฐๅฏน่ฏๆก† โ”‚ โ”‚ +โ”‚ โ”‚ - ็ผ–่พ‘ๅŽ็š„ๅ›พ็‰‡ (Markdownๆ ผๅผ) โ”‚ โ”‚ +โ”‚ โ”‚ - ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## ไบŒใ€้œ€่ฆไฟฎๆ”น/ๅˆ›ๅปบ็š„ๆ–‡ไปถ + +### 2.1 ๆ–ฐๅปบๆ–‡ไปถ + +| ๆ–‡ไปถ่ทฏๅพ„ | ไฝœ็”จ | +|---------|------| +| `lib/ai/smart-analyzer.ts` | ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ | +| `lib/ai/prompt-optimizer.ts` | ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ | +| `lib/ai/model-router.ts` | ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑๅ™จ | + +### 2.2 ไฟฎๆ”นๆ–‡ไปถ + +| ๆ–‡ไปถ่ทฏๅพ„ | ไฟฎๆ”นๅ†…ๅฎน | +|---------|---------| +| `app/(chat)/api/chat/route.ts` | Auto Mode ้€ป่พ‘ๆ›ฟๆข | + +--- + +## ไธ‰ใ€่ฏฆ็ป†ไปฃ็ ๅฎž็Žฐ + +### 3.1 ๅˆ›ๅปบ `lib/ai/smart-analyzer.ts` + +```typescript +/** + * ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ + * ไฝฟ็”จ Qwen3 VL ๅˆ†ๆž็”จๆˆท่พ“ๅ…ฅ๏ผŒ่ฏ†ๅˆซๅ›พ็‰‡็ผ–่พ‘ๆ„ๅ›พ + */ + +import { generateText } from "ai"; +import { getLanguageModelById } from "./dynamic-provider"; +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +// ๅˆ†ๆž็ป“ๆžœ็ฑปๅž‹ +export interface AnalysisResult { + intent: "image_edit" | "vision" | "image_gen" | "video_gen" | "chat"; + subIntent?: string; + confidence: number; + optimizedPrompt?: string; + negativePrompt?: string; + suggestedModel?: string; + needsClarification?: boolean; + clarifyingQuestions?: string[]; +} + +// ็ณป็ปŸๆ็คบ่ฏ - ้’ˆๅฏนๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏไผ˜ๅŒ– +const ANALYZER_SYSTEM_PROMPT = `ไฝ ๆ˜ฏไธ€ไธชไธ“ไธš็š„ๅ›พ็‰‡็ผ–่พ‘ๆ„ๅ›พๅˆ†ๆžไธ“ๅฎถใ€‚่ฏทๅˆ†ๆž็”จๆˆท็š„่ฏทๆฑ‚๏ผŒๅˆคๆ–ญๅ…ถๆ„ๅ›พ็ฑปๅž‹ใ€‚ + +## ๆ„ๅ›พๅˆ†็ฑป๏ผˆๆŒ‰ไผ˜ๅ…ˆ็บง๏ผ‰ + +### 1. image_edit - ๅ›พ็‰‡็ผ–่พ‘๏ผˆๆœ€้ซ˜ไผ˜ๅ…ˆ็บง๏ผŒ้œ€่ฆ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผ‰ +ๅญๆ„ๅ›พ๏ผš +- remove_background: ๆŠ ๅ›พใ€ๅŽป่ƒŒๆ™ฏใ€้€ๆ˜Ž่ƒŒๆ™ฏใ€ๅŽป้™ค่ƒŒๆ™ฏ +- replace_background: ๆข่ƒŒๆ™ฏใ€ๆ”น่ƒŒๆ™ฏใ€็™ฝๅบ•ใ€ๅœบๆ™ฏๆ›ฟๆขใ€่ƒŒๆ™ฏๆขๆˆ +- enhance: ๅ˜ๆธ…ๆ™ฐใ€้ซ˜ๆธ…ใ€้”ๅŒ–ใ€้™ๅ™ชใ€ๆๅ‡่ดจ้‡ใ€ไฟฎๅค +- extend: ๆ‰ฉๅฑ•ใ€ๆ‹“ๅฑ•ใ€ๅปถไผธใ€outpaintingใ€ๆ‰ฉๅ›พ +- inpaint: ๅŽป้™คXXใ€ๆถˆ้™คใ€ๆ“ฆๆމใ€ไฟฎๆ”นXX้ƒจๅˆ†ใ€ๅฑ€้ƒจไฟฎๆ”นใ€Pๆމ +- style_transfer: ้ฃŽๆ ผๅŒ–ใ€ๅก้€šๅŒ–ใ€ๅŠจๆผซ้ฃŽใ€ๆฒน็”ป้ฃŽใ€ๆ”น้ฃŽๆ ผ +- resize: ็ผฉๆ”พใ€ๆ”พๅคงใ€็ผฉๅฐใ€่ฐƒๆ•ดๅฐบๅฏธใ€่ฃๅ‰ช +- color_adjust: ่ฐƒ่‰ฒใ€่ฐƒๆ•ด้ขœ่‰ฒใ€ไบฎๅบฆใ€ๅฏนๆฏ”ๅบฆใ€้ฅฑๅ’Œๅบฆ + +### 2. vision - ๅ›พ็‰‡็†่งฃ๏ผˆ้œ€่ฆ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผ‰ +่งฆๅ‘่ฏ๏ผš็œ‹ใ€่ฏ†ๅˆซใ€ๅˆ†ๆžใ€ๆ่ฟฐใ€่ฟ™ๆ˜ฏไป€ไนˆใ€ๆœ‰ไป€ไนˆ + +### 3. image_gen - ๅ›พ็‰‡็”Ÿๆˆ๏ผˆไธ้œ€่ฆไธŠไผ ๅ›พ็‰‡๏ผ‰ +่งฆๅ‘่ฏ๏ผš็”ปใ€็ป˜ๅˆถใ€็”Ÿๆˆๅ›พ็‰‡ใ€ๅˆ›ๅปบใ€่ฎพ่ฎก + +### 4. video_gen - ่ง†้ข‘็”Ÿๆˆ +่งฆๅ‘่ฏ๏ผš่ง†้ข‘ใ€ๅŠจ็”ปใ€ๅŠจ่ตทๆฅ + +### 5. chat - ๆ™ฎ้€šๅฏน่ฏ๏ผˆ้ป˜่ฎค๏ผ‰ + +## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผJSON๏ผ‰ +{ + "intent": "image_edit", + "subIntent": "replace_background", + "confidence": 0.95, + "optimizedPrompt": "ไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ", + "negativePrompt": "้œ€่ฆ้ฟๅ…็š„ๅ†…ๅฎน", + "needsClarification": false, + "clarifyingQuestions": [] +} + +## ็‰นๆฎŠ่ง„ๅˆ™ +1. ๅฆ‚ๆžœ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ไธ”ๆœ‰็ผ–่พ‘็ฑปๅ…ณ้”ฎ่ฏ โ†’ image_edit +2. ๅฆ‚ๆžœ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ไฝ†ๆ„ๅ›พไธๆ˜Ž็กฎ โ†’ needsClarification: true๏ผŒๅนถๆไพ›ๆพ„ๆธ…้—ฎ้ข˜ +3. ๅฆ‚ๆžœ็”จๆˆท่ฆๆฑ‚็ผ–่พ‘ไฝ†ๆฒกไธŠไผ ๅ›พ็‰‡ โ†’ needsClarification: true๏ผŒๆ็คบ"่ฏทไธŠไผ ๅ›พ็‰‡"`; + +/** + * ๅˆ†ๆž็”จๆˆทๆ„ๅ›พ + */ +export async function analyzeIntent(params: { + text: string; + hasImage: boolean; + imageUrls?: string[]; +}): Promise { + const { text, hasImage, imageUrls } = params; + + // ่Žทๅ–ๅƒ้—ฎ3 VL ๆจกๅž‹ + const allModels = await getEnabledAiModelsWithProvider(); + const qwenModel = allModels.find( + (m) => m.provider.type === "alibaba" && m.model.supportsVision + ); + + // ๅฆ‚ๆžœๆฒกๆœ‰ๅฏ็”จ็š„ๅˆ†ๆžๆจกๅž‹๏ผŒไฝฟ็”จๅ›ž้€€้€ป่พ‘ + if (!qwenModel) { + console.log("[Smart Analyzer] No Qwen3 VL model found, using fallback"); + return fallbackKeywordAnalysis(text, hasImage); + } + + try { + const model = await getLanguageModelById(qwenModel.model.id); + if (!model) { + return fallbackKeywordAnalysis(text, hasImage); + } + + // ๆž„ๅปบ็”จๆˆทๆถˆๆฏ + const userMessage = hasImage + ? `็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒ่ฏทๆฑ‚ๅ†…ๅฎน๏ผš"${text}"` + : `็”จๆˆท่ฏทๆฑ‚๏ผˆๆ— ๅ›พ็‰‡๏ผ‰๏ผš"${text}"`; + + console.log("[Smart Analyzer] Analyzing with Qwen3 VL:", userMessage); + + // ่ฐƒ็”จๆจกๅž‹ๅˆ†ๆž + const { text: responseText } = await generateText({ + model, + system: ANALYZER_SYSTEM_PROMPT, + prompt: userMessage, + temperature: 0.1, // ไฝŽๆธฉๅบฆไฟ่ฏ็จณๅฎš่พ“ๅ‡บ + maxTokens: 500, + }); + + // ่งฃๆžJSONๅ“ๅบ” + const jsonMatch = responseText.match(/\{[\s\S]*\}/); + if (jsonMatch) { + const result = JSON.parse(jsonMatch[0]) as AnalysisResult; + console.log("[Smart Analyzer] Analysis result:", result); + return result; + } + } catch (error) { + console.error("[Smart Analyzer] Error:", error); + } + + // ๅ‡บ้”™ๆ—ถไฝฟ็”จๅ›ž้€€้€ป่พ‘ + return fallbackKeywordAnalysis(text, hasImage); +} + +/** + * ๅ›ž้€€ๆ–นๆกˆ๏ผšๅ…ณ้”ฎ่ฏๅŒน้… + */ +function fallbackKeywordAnalysis( + text: string, + hasImage: boolean +): AnalysisResult { + const lowerText = text.toLowerCase(); + + // ๅ›พ็‰‡็ผ–่พ‘ๅ…ณ้”ฎ่ฏๆฃ€ๆต‹ + if (hasImage) { + // ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏ + if (/ๆŠ ๅ›พ|ๅŽป่ƒŒๆ™ฏ|้€ๆ˜Ž่ƒŒๆ™ฏ|ๅŽป่ƒŒ|ๆ‰ฃๅ›พ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "remove_background", + confidence: 0.9, + }; + } + + // ๆข่ƒŒๆ™ฏ + if (/ๆข่ƒŒๆ™ฏ|็™ฝๅบ•|ๆ”น่ƒŒๆ™ฏ|่ƒŒๆ™ฏๆขๆˆ|็บฏ่‰ฒ่ƒŒๆ™ฏ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "replace_background", + confidence: 0.9, + }; + } + + // ๅ›พ็‰‡ๅขžๅผบ + if (/ๅ˜ๆธ…ๆ™ฐ|้ซ˜ๆธ…|้”ๅŒ–|้™ๅ™ช|ๆๅ‡่ดจ้‡|ไฟฎๅค/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "enhance", + confidence: 0.9, + }; + } + + // ๅ›พๅƒๆ‹“ๅฑ• + if (/ๆ‰ฉๅฑ•|ๆ‹“ๅฑ•|ๅปถไผธ|ๆ‰ฉๅ›พ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "extend", + confidence: 0.9, + }; + } + + // ๅฑ€้ƒจไฟฎๆ”น + if (/ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|Pๆމ|ไฟฎๆ”น.*้ƒจๅˆ†/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "inpaint", + confidence: 0.9, + }; + } + + // ้ฃŽๆ ผ่ฟ็งป + if (/้ฃŽๆ ผ|ๅก้€šๅŒ–|ๅŠจๆผซ้ฃŽ|ๆฒน็”ป้ฃŽ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "style_transfer", + confidence: 0.9, + }; + } + + // ่ง†่ง‰็†่งฃ + if (/็œ‹|่ฏ†ๅˆซ|ๅˆ†ๆž|ๆ่ฟฐ|่ฟ™ๆ˜ฏไป€ไนˆ|ๆœ‰ไป€ไนˆ/.test(lowerText)) { + return { intent: "vision", confidence: 0.8 }; + } + + // ๆœ‰ๅ›พไฝ†ๆ„ๅ›พไธๆ˜Ž็กฎ + return { + intent: "image_edit", + confidence: 0.5, + needsClarification: true, + clarifyingQuestions: [ + "ๆ‚จๆƒณๅฏน่ฟ™ๅผ ๅ›พ็‰‡ๅšไป€ไนˆๅค„็†๏ผŸ๏ผˆๆŠ ๅ›พ/ๆข่ƒŒๆ™ฏ/ๅ˜ๆธ…ๆ™ฐ/ๅ…ถไป–๏ผ‰", + ], + }; + } + + // ๆ— ๅ›พๆƒ…ๅ†ต + if (/็”ป|็ป˜ๅˆถ|็”Ÿๆˆๅ›พ|ๅˆ›ๅปบ.*ๅ›พ/.test(lowerText)) { + return { intent: "image_gen", confidence: 0.8 }; + } + + if (/่ง†้ข‘|ๅŠจ็”ป|ๅŠจ่ตทๆฅ/.test(lowerText)) { + return { intent: "video_gen", confidence: 0.8 }; + } + + // ้ป˜่ฎคๅฏน่ฏ + return { intent: "chat", confidence: 0.7 }; +} + +export { fallbackKeywordAnalysis }; +``` + +### 3.2 ๅˆ›ๅปบ `lib/ai/prompt-optimizer.ts` + +```typescript +/** + * ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ + * ๅฐ†็”จๆˆท็ฎ€ๅ•่พ“ๅ…ฅ่ฝฌๆขไธบไธ“ไธš็š„ๅ›พ็‰‡็ผ–่พ‘ๆ็คบ่ฏ + */ + +// ไผ˜ๅŒ–็ป“ๆžœ็ฑปๅž‹ +export interface OptimizeResult { + originalPrompt: string; + optimizedPrompt: string; + negativePrompt: string; +} + +// ๅ›พ็‰‡็ผ–่พ‘ๅญๆ„ๅ›พ็š„ๆ็คบ่ฏๆจกๆฟ +const EDIT_TEMPLATES: Record< + string, + { + template: string; + negative: string; + extractors: Record; + } +> = { + remove_background: { + template: + "ๅŽป้™คๅ›พ็‰‡่ƒŒๆ™ฏ๏ผŒไฟ็•™ไธปไฝ“ๅฎŒๆ•ด๏ผŒ่พ“ๅ‡บ้€ๆ˜Ž่ƒŒๆ™ฏPNGๆ ผๅผ๏ผŒ่พน็ผ˜ๅค„็†ๅนณๆป‘่‡ช็„ถ๏ผŒไฟ็•™็ป†่Š‚๏ผˆๅฆ‚ๅ‘ไธใ€ๆฏ›ๅ‘๏ผ‰", + negative: "ๆฎ‹็•™่ƒŒๆ™ฏ๏ผŒๆฏ›่พน๏ผŒ่พน็ผ˜ๆจก็ณŠ๏ผŒไธปไฝ“ไธๅฎŒๆ•ด๏ผŒ้”ฏ้ฝฟ", + extractors: {}, + }, + + replace_background: { + template: + "ๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ{background}๏ผŒไฟๆŒไธปไฝ“ๅฎŒๆ•ดๆธ…ๆ™ฐ๏ผŒๅ…‰ๅฝฑ่‡ช็„ถๅ่ฐƒ๏ผŒ่พน็ผ˜่ฟ‡ๆธกๅนณๆป‘๏ผŒไฟ็•™ไธปไฝ“้˜ดๅฝฑ", + negative: "ๆฎ‹็•™ๅŽŸ่ƒŒๆ™ฏ๏ผŒ่พน็ผ˜ไธ่‡ช็„ถ๏ผŒๅ…‰ๅฝฑไธๅ่ฐƒ๏ผŒ้€่ง†้”™่ฏฏ๏ผŒไธปไฝ“ๅ˜ๅฝข", + extractors: { + background: /(?:ๆขๆˆ|ๆ”นๆˆ|ๆ›ฟๆขไธบ|ๅ˜ๆˆ|ๆขไธบ)(.+?)(?:่ƒŒๆ™ฏ|ๅบ•|$)/, + }, + }, + + enhance: { + template: + "ๆๅ‡ๅ›พ็‰‡ๆ•ดไฝ“ๆธ…ๆ™ฐๅบฆๅ’Œ่ดจ้‡๏ผŒไฟๆŒ่‡ช็„ถ่‰ฒๅฝฉไธๅคฑ็œŸ๏ผŒๅŽป้™คๅ™ช็‚น๏ผŒๅขžๅผบ็ป†่Š‚๏ผŒ้€‚ๅบฆ้”ๅŒ–", + negative: "่ฟ‡ๅบฆ้”ๅŒ–๏ผŒ่‰ฒๅฝฉๅคฑ็œŸ๏ผŒๅ™ช็‚นๅขžๅŠ ๏ผŒไผชๅฝฑ๏ผŒ็”ป่ดจไธ‹้™", + extractors: {}, + }, + + extend: { + template: + "ๅ‘{direction}ๆ–นๅ‘ๆ‰ฉๅฑ•ๅ›พ็‰‡{amount}๏ผŒไฟๆŒๅŽŸๆœ‰้ฃŽๆ ผๅ’Œ่‰ฒ่ฐƒไธ€่‡ด๏ผŒๅ…‰ๅฝฑ่ฟ‡ๆธก่‡ช็„ถ๏ผŒๆ— ๆ˜Žๆ˜พๆŽฅ็ผ", + negative: "้ฃŽๆ ผไธไธ€่‡ด๏ผŒๆ˜Žๆ˜พๆŽฅ็ผ๏ผŒ้€่ง†้”™่ฏฏ๏ผŒๅ†…ๅฎน็ชๅ˜", + extractors: { + direction: /ๅ‘?(ไธŠ|ไธ‹|ๅทฆ|ๅณ|ๅ››ๅ‘จ)/, + amount: /(\d+%?)/, + }, + }, + + inpaint: { + template: + "ๅŽป้™ค/ไฟฎๆ”นๅ›พ็‰‡ไธญ็š„{target}๏ผŒ็”จๅ‘จๅ›ด็Žฏๅขƒ่‡ช็„ถๅกซๅ……๏ผŒไฟๆŒๆ•ดไฝ“้ฃŽๆ ผไธ€่‡ด๏ผŒๆ— ไฟฎๅค็—•่ฟน", + negative: "ๆ˜Žๆ˜พไฟฎๅค็—•่ฟน๏ผŒ้ฃŽๆ ผไธไธ€่‡ด๏ผŒๅ†…ๅฎน้”™่ฏฏ๏ผŒ่พน็ผ˜ไธ่‡ช็„ถ", + extractors: { + target: /(?:ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|ๅˆ ้™ค|Pๆމ)(.+?)(?:๏ผŒ|ใ€‚|$)/, + }, + }, + + style_transfer: { + template: + "ๅฐ†ๅ›พ็‰‡่ฝฌๆขไธบ{style}้ฃŽๆ ผ๏ผŒไฟๆŒไธปไฝ“ๅฏ่ฏ†ๅˆซๆ€ง๏ผŒ่‰ฒๅฝฉ้ฃŽๆ ผๅ่ฐƒ๏ผŒๆ•ดไฝ“ๆ•ˆๆžœ่‡ช็„ถ", + negative: "ไธปไฝ“ๅ˜ๅฝข๏ผŒ็‰นๅพไธขๅคฑ๏ผŒ้ฃŽๆ ผไธ็ปŸไธ€๏ผŒ่‰ฒๅฝฉๅผ‚ๅธธ", + extractors: { + style: /(?:่ฝฌๆขไธบ|ๅ˜ๆˆ|ๆ”นๆˆ)(.+?)(?:้ฃŽๆ ผ|$)/, + }, + }, + + resize: { + template: "่ฐƒๆ•ดๅ›พ็‰‡ๅฐบๅฏธไธบ{size}๏ผŒไฟๆŒ็”ป่ดจๆธ…ๆ™ฐ๏ผŒๆฏ”ไพ‹ๅ่ฐƒ", + negative: "็”ป่ดจๆŸๅคฑ๏ผŒๆฏ”ไพ‹ๅคฑ่ฐƒ๏ผŒๆจก็ณŠ", + extractors: { + size: /(\d+[xร—]\d+|\d+%)/, + }, + }, + + color_adjust: { + template: "่ฐƒๆ•ดๅ›พ็‰‡{adjustment}๏ผŒไฟๆŒ่‡ช็„ถๆ•ˆๆžœ๏ผŒ่‰ฒๅฝฉๅ่ฐƒ", + negative: "่‰ฒๅฝฉๅคฑ็œŸ๏ผŒ่ฟ‡ๅบฆ่ฐƒๆ•ด๏ผŒไธ่‡ช็„ถ", + extractors: { + adjustment: /(?:่ฐƒๆ•ด|ๅขžๅŠ |ๅ‡ๅฐ‘|ๆ้ซ˜|้™ไฝŽ)(.+?)(?:๏ผŒ|ใ€‚|$)/, + }, + }, +}; + +/** + * ไผ˜ๅŒ–ๆ็คบ่ฏ + */ +export function optimizePrompt(params: { + originalPrompt: string; + intent: string; + subIntent?: string; +}): OptimizeResult { + const { originalPrompt, intent, subIntent } = params; + + // ๅชๅฏนๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ่ฟ›่กŒไผ˜ๅŒ– + if (intent !== "image_edit" || !subIntent) { + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ", + }; + } + + const template = EDIT_TEMPLATES[subIntent]; + if (!template) { + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ", + }; + } + + // ไฝฟ็”จๆจกๆฟ็”Ÿๆˆไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + let optimizedPrompt = template.template; + + // ไปŽ็”จๆˆท่พ“ๅ…ฅไธญๆๅ–ๅ‚ๆ•ฐๅนถๅกซๅ……ๆจกๆฟ + for (const [key, regex] of Object.entries(template.extractors)) { + const match = originalPrompt.match(regex); + const value = match ? match[1].trim() : getDefaultValue(key); + optimizedPrompt = optimizedPrompt.replace(`{${key}}`, value); + } + + // ้™„ๅŠ ็”จๆˆทๅŽŸๅง‹ๆ„ๅ›พ + optimizedPrompt = `${optimizedPrompt}ใ€‚\n\n็”จๆˆทๅŽŸๅง‹่ฆๆฑ‚๏ผš${originalPrompt}`; + + console.log("[Prompt Optimizer] Optimized:", { + original: originalPrompt, + optimized: optimizedPrompt, + negative: template.negative, + }); + + return { + originalPrompt, + optimizedPrompt, + negativePrompt: template.negative, + }; +} + +/** + * ่Žทๅ–ๅ‚ๆ•ฐ้ป˜่ฎคๅ€ผ + */ +function getDefaultValue(key: string): string { + const defaults: Record = { + background: "็บฏ็™ฝ่‰ฒ", + direction: "ๅ››ๅ‘จ", + amount: "50%", + target: "ๆŒ‡ๅฎšๅŒบๅŸŸ", + style: "ๅก้€š", + size: "ๅŽŸๅฐบๅฏธ", + adjustment: "้ขœ่‰ฒ", + }; + return defaults[key] || "้ป˜่ฎค"; +} +``` + +### 3.3 ๅˆ›ๅปบ `lib/ai/model-router.ts` + +```typescript +/** + * ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑๅ™จ + * ๆ นๆฎ็”จๆˆทๆ„ๅ›พ้€‰ๆ‹ฉๆœ€ไฝณๆจกๅž‹ + */ + +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +// ่ทฏ็”ฑ็ป“ๆžœ็ฑปๅž‹ +export interface RouteResult { + id: string; + displayName: string; + providerType: string; + reason: string; +} + +// ๆ„ๅ›พๅˆฐๆจกๅž‹็š„ๆ˜ ๅฐ„้…็ฝฎ +const INTENT_MODEL_MAP: Record< + string, + { + providerTypes: string[]; + requireVision?: boolean; + requireImageGen?: boolean; + } +> = { + // ๅ›พ็‰‡็ผ–่พ‘๏ผšไผ˜ๅ…ˆ alibaba-image๏ผˆ็ต้ญ‚4.5๏ผ‰ + image_edit: { + providerTypes: ["alibaba-image", "gemini"], + requireVision: true, + requireImageGen: true, + }, + + // ่ง†่ง‰็†่งฃ๏ผšไผ˜ๅ…ˆๅƒ้—ฎ3 VL๏ผˆๆˆๆœฌไฝŽๆ•ˆๆžœๅฅฝ๏ผ‰ + vision: { + providerTypes: ["alibaba", "gemini"], + requireVision: true, + }, + + // ๅ›พ็‰‡็”Ÿๆˆ + image_gen: { + providerTypes: ["alibaba-image", "gemini", "volcano-vision"], + requireImageGen: true, + }, + + // ่ง†้ข‘็”Ÿๆˆ + video_gen: { + providerTypes: ["volcano-video", "qianwen-video"], + }, + + // ๆ™ฎ้€šๅฏน่ฏ + chat: { + providerTypes: ["alibaba", "openai"], + }, +}; + +/** + * ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹ + */ +export async function routeToModel(params: { + intent: string; + subIntent?: string; + hasImage: boolean; +}): Promise { + const { intent, subIntent, hasImage } = params; + + console.log("[Model Router] Routing for intent:", intent, subIntent); + + // ่Žทๅ–ๆ‰€ๆœ‰ๅฏ็”จๆจกๅž‹ + const allModels = await getEnabledAiModelsWithProvider(); + + // ่Žทๅ–ๆ„ๅ›พๅฏนๅบ”็š„้…็ฝฎ + const config = INTENT_MODEL_MAP[intent] || INTENT_MODEL_MAP["chat"]; + + // ๆŒ‰ไผ˜ๅ…ˆ็บงๆŸฅๆ‰พๅŒน้…็š„ๆจกๅž‹ + for (const providerType of config.providerTypes) { + const candidates = allModels.filter((m) => { + // ๅŒน้… provider ็ฑปๅž‹ + if (m.provider.type !== providerType) return false; + + // ๆฃ€ๆŸฅ่ƒฝๅŠ›่ฆๆฑ‚ + if (config.requireVision && !m.model.supportsVision) return false; + if (config.requireImageGen && !m.model.supportsImageGen) return false; + + // ๆŽ’้™คๅญๆจกๅž‹ + if (m.model.isSubModel) return false; + + // ๅฟ…้กปๅฏ็”จ + if (!m.model.isEnabled) return false; + + return true; + }); + + if (candidates.length > 0) { + // ไผ˜ๅ…ˆ้€‰ๆ‹ฉ้ป˜่ฎคๆจกๅž‹ + const defaultModel = candidates.find((c) => c.model.isDefault); + const selected = defaultModel || candidates[0]; + + const result: RouteResult = { + id: selected.model.id, + displayName: selected.model.displayName, + providerType: selected.provider.type, + reason: `${intent}${subIntent ? `/${subIntent}` : ""} โ†’ ${selected.model.displayName}`, + }; + + console.log("[Model Router] Selected:", result); + return result; + } + } + + // ๅ…œๅบ•๏ผš่ฟ”ๅ›žไปปๆ„ๅฏ็”จๆจกๅž‹ + const fallback = + allModels.find((m) => !m.model.isSubModel && m.model.isEnabled) || + allModels[0]; + + console.log("[Model Router] Using fallback model:", fallback.model.displayName); + + return { + id: fallback.model.id, + displayName: fallback.model.displayName, + providerType: fallback.provider.type, + reason: "fallback", + }; +} +``` + +### 3.4 ไฟฎๆ”น `app/(chat)/api/chat/route.ts` + +ๅœจ็Žฐๆœ‰็š„ Auto Mode ้€ป่พ‘ไฝ็ฝฎ๏ผˆ็บฆ็ฌฌ 334-422 ่กŒ๏ผ‰๏ผŒๆ›ฟๆขไธบไปฅไธ‹ไปฃ็ ๏ผš + +```typescript +// ========== Smart Auto Mode Logic ========== +if (finalModelId === "auto") { + console.log("[Smart Auto Mode] Starting intelligent analysis..."); + + // 1. ๆๅ–็”จๆˆทๆถˆๆฏๅ†…ๅฎน + const lastUserMessage = message; + const textContent = lastUserMessage.parts + .filter((p) => p.type === "text") + .map((p) => (p as { type: "text"; text: string }).text) + .join(" "); + + const hasImage = lastUserMessage.parts.some( + (p) => p.type === "file" || p.type === "image" || p.type === "image_url" + ); + + const imageUrls = lastUserMessage.parts + .filter((p): p is { type: "file"; url: string } => p.type === "file") + .map((p) => p.url); + + // 2. ่ฐƒ็”จๆ™บ่ƒฝๅˆ†ๆžๅ™จ + const { analyzeIntent } = await import("@/lib/ai/smart-analyzer"); + const analysisResult = await analyzeIntent({ + text: textContent, + hasImage, + imageUrls, + }); + + console.log("[Smart Auto Mode] Analysis result:", analysisResult); + + // 3. ๅฆ‚ๆžœ้œ€่ฆๆพ„ๆธ…๏ผŒ่ฟ”ๅ›žๆพ„ๆธ…้—ฎ้ข˜๏ผˆๅฏ้€‰ๅŠŸ่ƒฝ๏ผ‰ + if (analysisResult.needsClarification && analysisResult.clarifyingQuestions) { + console.log("[Smart Auto Mode] Needs clarification, but proceeding with best guess..."); + // ๆš‚ๆ—ถไธไธญๆ–ญๆต็จ‹๏ผŒ็ปง็ปญไฝฟ็”จๆœ€ไฝณ็Œœๆต‹ + } + + // 4. ๅฆ‚ๆžœๆ˜ฏๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ๏ผŒไผ˜ๅŒ–ๆ็คบ่ฏ + if (analysisResult.intent === "image_edit" && analysisResult.subIntent) { + const { optimizePrompt } = await import("@/lib/ai/prompt-optimizer"); + const optimizedResult = optimizePrompt({ + originalPrompt: textContent, + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + }); + + console.log("[Smart Auto Mode] Prompt optimized"); + + // ๆ›ฟๆขๆถˆๆฏไธญ็š„ๆ–‡ๆœฌไธบไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + message.parts = message.parts.map((part) => { + if (part.type === "text") { + return { + ...part, + text: optimizedResult.optimizedPrompt, + }; + } + return part; + }); + + // ๅฆ‚ๆžœๆจกๅž‹ๆ”ฏๆŒ่ดŸ้ขๆ็คบ่ฏ๏ผŒๅฏไปฅๅœจ imageParams ไธญไผ ้€’ + // requestBody.imageParams = { + // ...requestBody.imageParams, + // negativePrompt: optimizedResult.negativePrompt, + // }; + } + + // 5. ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹ + const { routeToModel } = await import("@/lib/ai/model-router"); + const routeResult = await routeToModel({ + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + hasImage, + }); + + finalModelId = routeResult.id; + console.log(`[Smart Auto Mode] โœ… Routed to: ${routeResult.displayName} (${routeResult.reason})`); +} +// ========== End Smart Auto Mode Logic ========== +``` + +--- + +## ๅ››ใ€ๆต‹่ฏ•ๅœบๆ™ฏ + +### 4.1 ๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏๆต‹่ฏ• + +| ็”จๆˆท่พ“ๅ…ฅ | ้ข„ๆœŸๆ„ๅ›พ | ้ข„ๆœŸๆจกๅž‹ | +|---------|---------|---------| +| [ๅ›พ็‰‡] + "ๅธฎๆˆ‘ๆŠŠ่ƒŒๆ™ฏๆขๆˆ็™ฝ่‰ฒ" | replace_background | alibaba-image | +| [ๅ›พ็‰‡] + "ๆŠ ๅ›พ" | remove_background | alibaba-image | +| [ๅ›พ็‰‡] + "ๅ˜ๆธ…ๆ™ฐไธ€็‚น" | enhance | alibaba-image | +| [ๅ›พ็‰‡] + "ๆŠŠๅณ่พน็š„ไบบPๆމ" | inpaint | alibaba-image | +| [ๅ›พ็‰‡] + "่ฝฌๆˆๅก้€š้ฃŽๆ ผ" | style_transfer | alibaba-image | + +### 4.2 ้ž็ผ–่พ‘ๅœบๆ™ฏๆต‹่ฏ• + +| ็”จๆˆท่พ“ๅ…ฅ | ้ข„ๆœŸๆ„ๅ›พ | ้ข„ๆœŸๆจกๅž‹ | +|---------|---------|---------| +| [ๅ›พ็‰‡] + "่ฟ™ๆ˜ฏไป€ไนˆ๏ผŸ" | vision | alibaba (Qwen3 VL) | +| "็”ปไธ€ไธชๅฏ็ˆฑ็š„็Œซ" | image_gen | alibaba-image | +| "ไฝ ๅฅฝ" | chat | alibaba (Qwen3 VL) | + +--- + +## ไบ”ใ€ๅผ€ๅ‘ๆญฅ้ชค + +### Phase 1: ๅŸบ็ก€ๅฎž็Žฐ (1-2ๅคฉ) + +1. ๅˆ›ๅปบ `lib/ai/smart-analyzer.ts` - ไฝฟ็”จๅ…ณ้”ฎ่ฏๅŒน้…ไฝœไธบไธป้€ป่พ‘ +2. ๅˆ›ๅปบ `lib/ai/prompt-optimizer.ts` - ๅฎž็ŽฐๆจกๆฟๅŒ–ๆ็คบ่ฏไผ˜ๅŒ– +3. ๅˆ›ๅปบ `lib/ai/model-router.ts` - ๅฎž็ŽฐๅŸบไบŽๆ„ๅ›พ็š„ๆจกๅž‹่ทฏ็”ฑ +4. ไฟฎๆ”น `app/(chat)/api/chat/route.ts` - ้›†ๆˆๆ–ฐๆจกๅ— + +### Phase 2: ๆ™บ่ƒฝๅ‡็บง (2-3ๅคฉ) + +1. ๅœจ `smart-analyzer.ts` ไธญ้›†ๆˆ Qwen3 VL ่ฟ›่กŒๆ„ๅ›พๅˆ†ๆž +2. ๆต‹่ฏ•ๅ„็ง่พน็•Œๆƒ…ๅ†ต +3. ไผ˜ๅŒ–ๆ็คบ่ฏๆจกๆฟ + +### Phase 3: ๅ้ฆˆไผ˜ๅŒ– (ๅฏ้€‰) + +1. ๆทปๅŠ ็”จๆˆทๅ้ฆˆๆœบๅˆถ +2. ่ฎฐๅฝ•็”จๆˆทๅๅฅฝ +3. ๆŒ็ปญไผ˜ๅŒ–ๆจกๅž‹่ทฏ็”ฑ็ญ–็•ฅ + +--- + +## ๅ…ญใ€ๆณจๆ„ไบ‹้กน + +1. **ไฟๆŒๅ‘ๅŽๅ…ผๅฎน**๏ผšๅฆ‚ๆžœๆ™บ่ƒฝๅˆ†ๆžๅคฑ่ดฅ๏ผŒ่‡ชๅŠจๅ›ž้€€ๅˆฐๅ…ณ้”ฎ่ฏๅŒน้… +2. **ๆ€ง่ƒฝ่€ƒ่™‘**๏ผšQwen3 ๅˆ†ๆžไผšๅขžๅŠ ็บฆ 1-2 ็ง’ๅปถ่ฟŸ๏ผŒ่€ƒ่™‘ๆ˜ฏๅฆ้œ€่ฆ +3. **้”™่ฏฏๅค„็†**๏ผšๆ‰€ๆœ‰ๆ–ฐๆจกๅ—้ƒฝ้œ€่ฆๅฎŒๅ–„็š„ try-catch +4. **ๆ—ฅๅฟ—่ฎฐๅฝ•**๏ผšๆทปๅŠ ่ฏฆ็ป†ๆ—ฅๅฟ—ไพฟไบŽ่ฐƒ่ฏ• + +--- + +## ไธƒใ€ๆ–‡ไปถ็ป“ๆž„ + +``` +lib/ +โ””โ”€โ”€ ai/ + โ”œโ”€โ”€ smart-analyzer.ts # ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ + โ”œโ”€โ”€ prompt-optimizer.ts # ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ + โ”œโ”€โ”€ model-router.ts # ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑๅ™จ + โ”œโ”€โ”€ dynamic-provider.ts # ็Žฐๆœ‰ๆจกๅž‹ๆไพ›ๅ™จ + โ””โ”€โ”€ providers/ # ็Žฐๆœ‰ๆจกๅž‹ๆไพ›ๅ•† + โ”œโ”€โ”€ alibaba-image.ts + โ”œโ”€โ”€ gemini-image.ts + โ””โ”€โ”€ ... + +app/ +โ””โ”€โ”€ (chat)/ + โ””โ”€โ”€ api/ + โ””โ”€โ”€ chat/ + โ””โ”€โ”€ route.ts # ไฟฎๆ”น Auto Mode ้€ป่พ‘ +``` diff --git a/docs/smart-auto-model-upgrade.md b/docs/smart-auto-model-upgrade.md new file mode 100644 index 000000000..e3cbe0869 --- /dev/null +++ b/docs/smart-auto-model-upgrade.md @@ -0,0 +1,1960 @@ +# ๆ™บ่ƒฝ่‡ชๅŠจๆจกๅž‹็ณป็ปŸๅ‡็บงๅผ€ๅ‘ๆ–‡ๆกฃ + +> ็‰ˆๆœฌ: v2.0 +> ๆ—ฅๆœŸ: 2025-01-31 +> ๅ‚่€ƒ: Lovart.ai ่ฎพ่ฎกๆ™บ่ƒฝไฝ“ๆžถๆž„ใ€MagicQuill (CVPR 2025)ใ€PromptEnhancer (่…พ่ฎฏๆททๅ…ƒ) + +--- + +## ๐ŸŽฏ ๆ ธๅฟƒๅฎšไฝ + +**ๆœฌ็ณป็ปŸไธป่ฆๅบ”็”จไบŽๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏ๏ผŒไธๆ˜ฏๆ–‡็”Ÿๅ›พใ€‚** + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ไธšๅŠกๅœบๆ™ฏไผ˜ๅ…ˆ็บง โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ โญโญโญ ๅ›พ็‰‡็ผ–่พ‘ (ๆ ธๅฟƒ) โ”‚ +โ”‚ - ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏ โ”‚ +โ”‚ - ๆข่ƒŒๆ™ฏ๏ผˆ็บฏ่‰ฒ/ๅœบๆ™ฏ๏ผ‰ โ”‚ +โ”‚ - ๅ›พ็‰‡ๅขžๅผบ/ๅ˜ๆธ…ๆ™ฐ โ”‚ +โ”‚ - ๅ›พๅƒๆ‹“ๅฑ• (Outpainting) โ”‚ +โ”‚ - ๅฑ€้ƒจไฟฎๆ”น (Inpainting) โ”‚ +โ”‚ - ้ฃŽๆ ผ่ฟ็งป โ”‚ +โ”‚ โ”‚ +โ”‚ โญโญ ๅ›พ็‰‡็†่งฃ (่พ…ๅŠฉ) โ”‚ +โ”‚ - ๅ›พ็‰‡ๅ†…ๅฎนๅˆ†ๆž โ”‚ +โ”‚ - ๅฏน่ฑก่ฏ†ๅˆซ โ”‚ +โ”‚ โ”‚ +โ”‚ โญ ๅ›พ็‰‡/่ง†้ข‘็”Ÿๆˆ (่พ…ๅŠฉ) โ”‚ +โ”‚ - ๅŸบไบŽๅ‚่€ƒๅ›พ็”Ÿๆˆ โ”‚ +โ”‚ - ๅ›พ็”Ÿ่ง†้ข‘ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## ไธ€ใ€้กน็›ฎๆฆ‚่ฟฐ + +### 1.1 ็Žฐ็Šถๅˆ†ๆž + +ๅฝ“ๅ‰่‡ชๅŠจๆจกๅž‹ๅŠŸ่ƒฝ้‡‡็”จ**ๅŸบไบŽๅ…ณ้”ฎ่ฏ็š„่ง„ๅˆ™ๅŒน้…**ๆ–นๅผ๏ผš + +```typescript +// ๅฝ“ๅ‰ๅฎž็Žฐ - app/(chat)/api/chat/route.ts +const videoKeywords = /(่ง†้ข‘|video|ๅŠจ็”ป|sora|ๅŠจ่ตทๆฅ...)/i; +const imageEditKeywords = /(ไฟฎๅ›พ|Pๅ›พ|ๅŽป่ƒŒๆ™ฏ|ๆŠ ๅ›พ...)/i; +const imageGenKeywords = /(็”ป|็ป˜ๅˆถ|็”Ÿๆˆๅ›พ็‰‡|ๆตทๆŠฅ...)/i; + +if (videoKeywords.test(textContent)) { + bestModel = allModels.find(m => m.model.displayName.includes("่ง†้ข‘")); +} +``` + +**ๅญ˜ๅœจ็š„้—ฎ้ข˜๏ผš** +- โŒ ่ง„ๅˆ™็กฌ็ผ–็ ๏ผŒๆ‰ฉๅฑ•ๆ€งๅทฎ +- โŒ ๆ— ๆณ•็†่งฃๅคๆ‚/ๆจก็ณŠ็š„็”จๆˆทๆ„ๅ›พ +- โŒ ๆ็คบ่ฏ่ดจ้‡ๅฎŒๅ…จไพ่ต–็”จๆˆท +- โŒ ๆ— ๆณ•ๅญฆไน ็”จๆˆทๅๅฅฝ +- โŒ ไธๆ”ฏๆŒๅคš่ฝฎๅฏน่ฏ็ป†ๅŒ– + +### 1.2 ๆ ธๅฟƒๅบ”็”จๅœบๆ™ฏ๏ผšๅ›พ็‰‡็ผ–่พ‘ + +**ๆˆ‘ไปฌ็š„ไธป่ฆไธšๅŠกๆ˜ฏๅ›พ็‰‡็ผ–่พ‘๏ผŒไธๆ˜ฏๆ–‡็”Ÿๅ›พใ€‚** ไธป่ฆๅœบๆ™ฏๅŒ…ๆ‹ฌ๏ผš + +| ๅœบๆ™ฏ | ๆ่ฟฐ | ๅฏนๅบ”ๆจกๅž‹่ƒฝๅŠ› | +|-----|------|-------------| +| **ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏ** | ๅŽป้™คๅ›พ็‰‡่ƒŒๆ™ฏ๏ผŒไฟ็•™ไธปไฝ“ | image_edit + vision | +| **ๆข่ƒŒๆ™ฏ** | ๆ›ฟๆขไธบๆ–ฐ่ƒŒๆ™ฏ๏ผˆ็บฏ่‰ฒ/ๅœบๆ™ฏ๏ผ‰ | image_edit | +| **ๅ›พ็‰‡ๅขžๅผบ** | ๅ˜ๆธ…ๆ™ฐใ€้™ๅ™ชใ€ๆๅ‡่ดจ้‡ | image_edit | +| **ๅ›พๅƒๆ‹“ๅฑ•** | Outpainting๏ผŒๆ‰ฉๅฑ•็”ปๅธƒ่พน็•Œ | image_edit | +| **ๅฑ€้ƒจไฟฎๆ”น** | Inpainting๏ผŒไฟฎๆ”น็‰นๅฎšๅŒบๅŸŸ | image_edit + vision | +| **ๅฐบๅฏธ่ฐƒๆ•ด** | ่ฃๅ‰ชใ€็ผฉๆ”พใ€่ฐƒๆ•ดๆฏ”ไพ‹ | resize (ๅทฅๅ…ท) | +| **่ง’ๅบฆ่ฐƒๆ•ด** | ๆ—‹่ฝฌใ€็ฟป่ฝฌใ€้€่ง†ๅ˜ๆข | angle_edit (ๅทฅๅ…ท) | +| **้ฃŽๆ ผ่ฟ็งป** | ๆ”นๅ˜ๅ›พ็‰‡้ฃŽๆ ผ๏ผˆๅก้€šๅŒ–็ญ‰๏ผ‰ | image_edit | +| **ๅ›พ็‰‡็†่งฃ** | ๅˆ†ๆžๅ›พ็‰‡ๅ†…ๅฎนใ€่ฏ†ๅˆซๅฏน่ฑก | vision | +| **็”Ÿๅ›พ่พ…ๅŠฉ** | ๅŸบไบŽๅ‚่€ƒๅ›พ็”Ÿๆˆๆ–ฐๅ›พ | image_gen | + +### 1.3 ๅ‡็บง็›ฎๆ ‡ + +ๅ‚่€ƒ Lovart.ai ็š„่ฎพ่ฎกๆ™บ่ƒฝไฝ“ๆžถๆž„๏ผŒๆž„ๅปบ**ๅŸบไบŽ LLM ็š„ๆ™บ่ƒฝๅ›พ็‰‡็ผ–่พ‘็ณป็ปŸ**๏ผš + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๆ™บ่ƒฝ่‡ชๅŠจๆจกๅž‹็ณป็ปŸ v2.0 โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ ็”จๆˆท่พ“ๅ…ฅ โ”€โ”€โ–ถ Qwen3 ๅคšๆจกๆ€ๅˆ†ๆž โ”€โ”€โ–ถ ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑ โ”€โ”€โ–ถ ็”Ÿๆˆๆจกๅž‹ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ–ผ โ”‚ +โ”‚ ๆ„ๅ›พ่ฏ†ๅˆซ + ่ฏญไน‰ๅˆ†่งฃ ๆœ€ไผ˜ๆจกๅž‹้€‰ๆ‹ฉ ๆ็คบ่ฏไผ˜ๅŒ–ๆ‰ง่กŒ โ”‚ +โ”‚ (8ไธช่ฎพ่ฎก็ปดๅบฆๅˆ†ๆž) (ๅคšๅ› ็ด ่ฏ„ๅˆ†) (ไธ“ไธš็บงๆ็คบ่ฏ) โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ”‚ +โ”‚ ็”จๆˆทๅๅฅฝๅญฆไน ็ณป็ปŸ โ—€โ”€โ”€โ”€โ”€ ๅ้ฆˆ่ฟญไปฃไผ˜ๅŒ– โ”‚ +โ”‚ (่ดๅถๆ–ฏไผ˜ๅŒ–/่ฎฐๅฟ†) โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +**ๆ ธๅฟƒๅ‡็บง๏ผš** +- โœ… ไฝฟ็”จ Qwen3 ่ฟ›่กŒๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆž +- โœ… ่‡ชๅŠจไผ˜ๅŒ–/ๆ‰ฉๅ†™็”จๆˆทๆ็คบ่ฏ +- โœ… ๅŸบไบŽๅคšๅ› ็ด ่ฏ„ๅˆ†็š„ๆจกๅž‹่ทฏ็”ฑ +- โœ… ็”จๆˆทๅๅฅฝๅญฆไน ไธŽ่ฎฐๅฟ† +- โœ… ๆ”ฏๆŒๅคš่ฝฎๅฏน่ฏ็ป†ๅŒ– + +--- + +## 1.3 ๅฝ“ๅ‰ๅทฒ้…็ฝฎ็š„ๆจกๅž‹ๆธ…ๅ• + +### ๆจกๅž‹ๆไพ›ๅ•† (AiProvider) + +| ๅ็งฐ | ็ฑปๅž‹ (providerType) | API ๅœฐๅ€ | ็”จ้€” | +|-----|---------------------|----------|------| +| ้˜ฟ้‡Œไบ‘ | `alibaba` | dashscope.aliyuncs.com | ๅฏน่ฏๆจกๅž‹ | +| ๅƒ้—ฎ็”Ÿๅ›พ | `alibaba-image` | dashscope.aliyuncs.com | ้˜ฟ้‡Œๅ›พ็‰‡็”Ÿๆˆ | +| Geminiไธญ่ฝฌ | `gemini` | grsai.dakka.com.cn | Gemini ๅ›พ็‰‡็”Ÿๆˆ | +| ๆ—ขๆขฆๆจกๅž‹ | `volcano-vision` | ark.cn-beijing.volces.com | ็ซๅฑฑๅ›พ็‰‡็”Ÿๆˆ | +| ็ซๅฑฑๅผ•ๆ“Ž่ง†้ข‘ | `volcano-video` | ark.cn-beijing.volces.com | ่ง†้ข‘็”Ÿๆˆ | +| ๅƒ้—ฎ่ง†้ข‘ | `qianwen-video` | dashscope.aliyuncs.com | ้˜ฟ้‡Œ่ง†้ข‘็”Ÿๆˆ | +| ๅƒ้—ฎๆœฌๅœฐๆจกๅž‹ | `local` | 156.225.27.52:8001 | ๆœฌๅœฐ้ƒจ็ฝฒ | +| ็Žฏ็ง‘API | `openai` | api.huandutech.com | OpenAI ๅ…ผๅฎน | + +### ๅทฒๅฏ็”จ็š„ๆจกๅž‹ (AiModel) + +| ๆ˜พ็คบๅ็งฐ | modelId | ๆไพ›ๅ•†็ฑปๅž‹ | ่ƒฝๅŠ› | ้…็ฝฎ | +|---------|---------|-----------|------|------| +| **ๅƒ้—ฎ3ใ€Œๅฏน่ฏๆจกๅž‹ใ€** | qwen3-vl-plus | alibaba | ๅฏน่ฏ | ้ป˜่ฎคๆจกๅž‹ | +| **ๆ—ขๆขฆ4.5ใ€Œ็”Ÿๅ›พๆจกๅž‹ใ€** | doubao-seedream-4-5-251128 | volcano-vision | ๅ›พ็‰‡็”Ÿๆˆ | ็ซๅฑฑๆ—ขๆขฆ | +| **VIDEOใ€Œ่ง†้ข‘ๆจกๅž‹ใ€** | volcano-video-master | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ๆ”ฏๆŒๅคšๆจกๅผ | +| **GTP1.5ใ€Œ็”Ÿๅ›พๆจกๅž‹ใ€** | gpt-image-1.5 | gemini | ่ง†่ง‰็†่งฃ | Gemini ไธญ่ฝฌ | +| **้ฆ™่•‰PROใ€Œ็”Ÿๅ›พๆจกๅž‹ใ€** | nano-banana-pro | gemini | ่ง†่ง‰+ๅ›พ็‰‡็”Ÿๆˆ | ๆœ€ๅคง2ๅผ ๅ›พ | +| **็ต้ญ‚4.5ใ€Œ็”Ÿๅ›พๆจกๅž‹ใ€** | qwen-image-edit-max-2026-01-16 | alibaba-image | ่ง†่ง‰+ๅ›พ็‰‡็”Ÿๆˆ | ๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ• | +| **SeedancePro** | doubao-seedance-1-0-pro-250528 | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ไธ“ไธš็‰ˆ,1080p | +| **SeedanceProFast** | doubao-seedance-1-0-pro-fast-251015 | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ๅฟซ้€Ÿ็‰ˆ | +| **SeedanceLiteI2V** | doubao-seedance-1-0-lite-i2v-250428 | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ๅ›พ็”Ÿ่ง†้ข‘่ฝป้‡็‰ˆ | +| **SeedanceLiteT2V** | doubao-seedance-1-0-lite-t2v-250428 | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ๆ–‡็”Ÿ่ง†้ข‘่ฝป้‡็‰ˆ | + +### ๆจกๅž‹่ƒฝๅŠ›็Ÿฉ้˜ต + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๆจกๅž‹่ƒฝๅŠ›็Ÿฉ้˜ต โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ ๆจกๅž‹ โ”‚ ๅฏน่ฏ โ”‚ ่ง†่ง‰็†่งฃ โ”‚ ๅ›พ็‰‡็”Ÿๆˆ โ”‚ ่ง†้ข‘็”Ÿๆˆ โ”‚ ๅ›พ็‰‡็ผ–่พ‘ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ ๅƒ้—ฎ3 (VL) โ”‚ โœ… โ”‚ โœ… โ”‚ โŒ โ”‚ โŒ โ”‚ โŒ โ”‚ +โ”‚ ๆ—ขๆขฆ4.5 โ”‚ โŒ โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โŒ โ”‚ +โ”‚ GTP1.5 โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โŒ โ”‚ โŒ โ”‚ +โ”‚ ้ฆ™่•‰PRO โ”‚ โŒ โ”‚ โœ… โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ +โ”‚ ็ต้ญ‚4.5 โ”‚ โŒ โ”‚ โœ… โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ +โ”‚ VIDEO โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ +โ”‚ SeedancePro โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ +โ”‚ SeedanceProFast โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ +โ”‚ SeedanceLiteI2V โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ +โ”‚ SeedanceLiteT2V โ”‚ โŒ โ”‚ โŒ โ”‚ โŒ โ”‚ โœ… โ”‚ โŒ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +ๆณจ๏ผšๅƒ้—ฎ3 ไฝฟ็”จ็š„ๆ˜ฏ qwen3-vl-plus ๆจกๅž‹๏ผŒVL = Vision-Language๏ผŒๆ˜ฏ้˜ฟ้‡Œไบ‘็š„ๅคšๆจกๆ€ๆจกๅž‹๏ผŒ + ๆ—ขๆ”ฏๆŒ็บฏๆ–‡ๆœฌๅฏน่ฏ๏ผŒไนŸๆ”ฏๆŒๅ›พ็‰‡็†่งฃๅˆ†ๆžใ€‚ +``` + +### ๆ™บ่ƒฝ่ทฏ็”ฑๆ˜ ๅฐ„็ญ–็•ฅ (ๅ›พ็‰‡็ผ–่พ‘ไผ˜ๅ…ˆ) + +ๆ นๆฎ็”จๆˆทๆ„ๅ›พ๏ผŒๆ™บ่ƒฝ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹๏ผš + +| ๆ„ๅ›พ | ไผ˜ๅ…ˆ็บง | ้ฆ–้€‰ๆจกๅž‹ | ๅค‡้€‰ๆจกๅž‹ | ่ทฏ็”ฑ้€ป่พ‘ | +|-----|-------|---------|---------|---------| +| **ๅ›พ็‰‡็ผ–่พ‘** | โญโญโญ | ็ต้ญ‚4.5 | ้ฆ™่•‰PRO | ๆ ธๅฟƒไธšๅŠก๏ผŒsupportsVision + supportsImageGen | +| **่ง†่ง‰็†่งฃ** | โญโญ | ๅƒ้—ฎ3 (VL) | GTP1.5 | ๅƒ้—ฎ3ๆˆๆœฌๆ›ดไฝŽ๏ผŒๆ•ˆๆžœไนŸๅฅฝ | +| **ๅ›พ็‰‡็”Ÿๆˆ** | โญโญ | ็ต้ญ‚4.5 | ้ฆ™่•‰PRO / ๆ—ขๆขฆ4.5 | ๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ•ไผ˜ๅ…ˆ | +| **่ง†้ข‘็”Ÿๆˆ** | โญ | VIDEO (Master) | SeedancePro | ่‡ชๅŠจ้€‰ๆ‹ฉๅญๆจกๅž‹ | +| **ๅ›พ็”Ÿ่ง†้ข‘** | โญ | SeedanceLiteI2V | SeedancePro | ๆœ‰ๅ›พ็‰‡่พ“ๅ…ฅ | +| **ๆ™ฎ้€šๅฏน่ฏ** | - | ๅƒ้—ฎ3 (VL) | - | ้ป˜่ฎคๅฏน่ฏๆจกๅž‹ | + +### ๅ›พ็‰‡็ผ–่พ‘ๅญๆ„ๅ›พ่ทฏ็”ฑ + +| ๅญๆ„ๅ›พ | ๆœ€ไฝณๆจกๅž‹ | ๅŽŸๅ›  | +|-------|---------|------| +| remove_background | ็ต้ญ‚4.5 / ้ฆ™่•‰PRO | ๆ”ฏๆŒ็ฒพๅ‡†ๆŠ ๅ›พ | +| replace_background | ็ต้ญ‚4.5 | ๆ”ฏๆŒๅœบๆ™ฏ็”Ÿๆˆ | +| enhance | ็ต้ญ‚4.5 / ๆ—ขๆขฆ4.5 | ้ซ˜่ดจ้‡ๅขžๅผบ | +| extend (outpainting) | ็ต้ญ‚4.5 | ๆ”ฏๆŒ็”ปๅธƒๆ‰ฉๅฑ• | +| inpaint | ็ต้ญ‚4.5 / ้ฆ™่•‰PRO | ็ฒพๅ‡†ๅฑ€้ƒจ็ผ–่พ‘ | +| style_transfer | ้ฆ™่•‰PRO / ็ต้ญ‚4.5 | ้ฃŽๆ ผ่ฟ็งป่ƒฝๅŠ› | +| resize/crop | ๅทฅๅ…ทๅค„็† | ไธ้œ€่ฆAIๆจกๅž‹ | +| color_adjust | ๅทฅๅ…ทๅค„็† | ไธ้œ€่ฆAIๆจกๅž‹ | + +**้‡่ฆ่ฏดๆ˜Ž๏ผš** +- **ๅ›พ็‰‡็ผ–่พ‘ๆ˜ฏๆ ธๅฟƒไธšๅŠก**๏ผŒไผ˜ๅ…ˆไฟ่ฏ็ผ–่พ‘็ฑปๆ„ๅ›พ็š„่ฏ†ๅˆซๅ‡†็กฎๆ€ง +- **็ต้ญ‚4.5 (alibaba-image)** ๆ˜ฏๅ›พ็‰‡็ผ–่พ‘็š„้ฆ–้€‰๏ผŒๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ• +- **ๅƒ้—ฎ3 (VL)** ็”จไบŽๅ›พ็‰‡็†่งฃๅˆ†ๆž๏ผŒๆˆๆœฌไฝŽๆ•ˆๆžœๅฅฝ +- **resize/crop/color_adjust** ็ญ‰็ฎ€ๅ•ๆ“ไฝœ็”ฑๅ‰็ซฏๅทฅๅ…ท็›ดๆŽฅๅค„็†๏ผŒไธ้œ€่ฆ่ฐƒ็”จ AI ๆจกๅž‹ + +--- + +## ไบŒใ€่Šๅคฉๅฏน่ฏๆก†ไธญ็š„ๅ›พ็‰‡็ผ–่พ‘ๆต็จ‹่ฎพ่ฎก + +### 2.1 ๅฝ“ๅ‰็ณป็ปŸๆ•ฐๆฎๆตๅˆ†ๆž + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๅฝ“ๅ‰็ณป็ปŸๆ•ฐๆฎๆต โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ ็”จๆˆทๅœจๅฏน่ฏๆก†ไธญ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ MultimodalInput โ”‚โ”€โ”€โ”€โ–ถโ”‚ Chat ็ป„ไปถ โ”‚โ”€โ”€โ”€โ–ถโ”‚ sendMessage() โ”‚ โ”‚ +โ”‚ โ”‚ (ๅ‰็ซฏ็ป„ไปถ) โ”‚ โ”‚ ็Šถๆ€็ฎก็† โ”‚ โ”‚ ๅ‘้€ๆถˆๆฏ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ็”จๆˆท่พ“ๅ…ฅ: โ”‚ โ”‚ +โ”‚ โ”‚ - ๅ›พ็‰‡้™„ไปถ (attachments) โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ–‡ๅญ—ๆ่ฟฐ (input) โ”‚ โ”‚ +โ”‚ โ”‚ - ้€‰ๆ‹ฉ็š„ๆจกๅž‹ (selectedModelId) โ–ผ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โ”‚ POST /api/chat โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ (route.ts) โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ–ผ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โ”‚ Auto Mode ๆฃ€ๆต‹ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ (ๅ…ณ้”ฎ่ฏๅŒน้…) โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ–ผ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โ”‚ ่ทฏ็”ฑๅˆฐๅฏนๅบ”ๆจกๅž‹ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - gemini-image โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - alibaba-image โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - volcano-vision โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ–ผ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โ”‚ ๆจกๅž‹็”Ÿๆˆ็ป“ๆžœ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ่ฟ”ๅ›ž Markdown ๅ›พ็‰‡ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Messages ็ป„ไปถๆธฒๆŸ“็ป“ๆžœ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### 2.2 ๅ‡็บงๅŽ็š„ๆ™บ่ƒฝๅ›พ็‰‡็ผ–่พ‘ๆต็จ‹ + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๆ™บ่ƒฝๅ›พ็‰‡็ผ–่พ‘ๅ‡็บงๆต็จ‹ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ ็”จๆˆทๅœจๅฏน่ฏๆก†ไธญ่พ“ๅ…ฅ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ๐Ÿ“ธ ไธŠไผ ๅ›พ็‰‡ โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ’ฌ "ๅธฎๆˆ‘ๆŠŠ่ƒŒๆ™ฏๆขๆˆ็™ฝ่‰ฒ" โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ“ค ๅ‘้€ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Phase 1: ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ (Smart Analyzer) โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ไฝฟ็”จ Qwen3 VL ๅˆ†ๆž: โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ๆฃ€ๆต‹็”จๆˆทๆ„ๅ›พ: replace_background โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ๅˆ†ๆžๅ›พ็‰‡ๅ†…ๅฎน: ไบงๅ“ๅ›พ๏ผŒ่ƒŒๆ™ฏๆ‚ไนฑ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ็ฝฎไฟกๅบฆ: 0.95 โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Phase 2: ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ (Prompt Optimizer) โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ็”จๆˆทๅŽŸๅง‹: "ๅธฎๆˆ‘ๆŠŠ่ƒŒๆ™ฏๆขๆˆ็™ฝ่‰ฒ" โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ–ผ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ไผ˜ๅŒ–ๅŽ: "ๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ็บฏ็™ฝ่‰ฒ(#FFFFFF)๏ผŒไฟ็•™ไบงๅ“ไธปไฝ“๏ผŒ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ไฟๆŒ่‡ช็„ถ้˜ดๅฝฑๆ•ˆๆžœ๏ผŒ่พน็ผ˜ๅค„็†ๅนณๆป‘่‡ช็„ถ" โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ่ดŸ้ขๆ็คบ: "ๆจก็ณŠ่พน็ผ˜๏ผŒๆฎ‹็•™่ƒŒๆ™ฏ๏ผŒ้˜ดๅฝฑไธ่‡ช็„ถ๏ผŒ่ฟ‡ๆ›" โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Phase 3: ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑๅ™จ (Model Router) โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ๆ„ๅ›พ: replace_background โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ๆŽจ่ๆจกๅž‹: alibaba-image (็ต้ญ‚4.5) โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ๅŽŸๅ› : ๆ”ฏๆŒ้ซ˜่ดจ้‡่ƒŒๆ™ฏๆ›ฟๆข๏ผŒๆ็คบ่ฏๆ‰ฉๅฑ•่ƒฝๅŠ›ๅผบ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Phase 4: ๆ‰ง่กŒๅ›พ็‰‡็ผ–่พ‘ โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ่ฐƒ็”จ alibaba-image API: โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ่พ“ๅ…ฅๅ›พ็‰‡: ็”จๆˆทไธŠไผ ็š„ๅŽŸๅ›พ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ไผ˜ๅŒ–ๅŽๆ็คบ่ฏ: "ๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ็บฏ็™ฝ่‰ฒ..." โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ - ่ดŸ้ขๆ็คบ่ฏ: "ๆจก็ณŠ่พน็ผ˜..." โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ่ฟ”ๅ›ž็ป“ๆžœๅˆฐๅฏน่ฏๆก† โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ๐Ÿค– ๅŠฉๆ‰‹ๅ›žๅค: โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ "ๅทฒๅฐ†่ƒŒๆ™ฏๆ›ฟๆขไธบ็™ฝ่‰ฒ๏ผŒไฟ็•™ไบ†ไบงๅ“็š„่‡ช็„ถ้˜ดๅฝฑๆ•ˆๆžœใ€‚" โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ [็ผ–่พ‘ๅŽ็š„ๅ›พ็‰‡] โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ ๐Ÿ’ก ๆ็คบ: ๅฆ‚้œ€่ฐƒๆ•ด๏ผŒๅฏไปฅ่ฏด"้˜ดๅฝฑๆทฑไธ€็‚น"ๆˆ–"่พน็ผ˜ๆ›ดๆธ…ๆ™ฐ" โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### 2.3 ๅ…ณ้”ฎไปฃ็ ไฝ็ฝฎ่ฏดๆ˜Ž + +#### ๅ‰็ซฏไปฃ็  + +| ๆ–‡ไปถ | ไฝœ็”จ | ้œ€่ฆไฟฎๆ”น็š„ๅ†…ๅฎน | +|-----|------|---------------| +| `components/multimodal-input.tsx` | ็”จๆˆท่พ“ๅ…ฅ็ป„ไปถ | ๆ— ้œ€ๅคงๆ”น๏ผŒๅทฒๆ”ฏๆŒๅ›พ็‰‡+ๆ–‡ๅญ—ๅ‘้€ | +| `components/chat.tsx` | ่Šๅคฉ็Šถๆ€็ฎก็† | ๆทปๅŠ ๆ„ๅ›พๅˆ†ๆž็ป“ๆžœๅฑ•็คบ๏ผˆๅฏ้€‰๏ผ‰ | +| `components/messages.tsx` | ๆถˆๆฏๆธฒๆŸ“ | ๆ— ้œ€ไฟฎๆ”น๏ผŒๅทฒๆ”ฏๆŒๅ›พ็‰‡ๆธฒๆŸ“ | + +#### ๅŽ็ซฏไปฃ็  + +| ๆ–‡ไปถ | ไฝœ็”จ | ้œ€่ฆไฟฎๆ”น็š„ๅ†…ๅฎน | +|-----|------|---------------| +| `app/(chat)/api/chat/route.ts` | ่ŠๅคฉAPIๅ…ฅๅฃ | **ๆ ธๅฟƒไฟฎๆ”น็‚น** - ้›†ๆˆๆ™บ่ƒฝๅˆ†ๆžๅ™จๅ’Œ่ทฏ็”ฑๅ™จ | +| `lib/ai/smart-analyzer.ts` | ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ | **ๆ–ฐๅปบ** - Qwen3 ๆ„ๅ›พๅˆ†ๆž | +| `lib/ai/prompt-optimizer.ts` | ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ | **ๆ–ฐๅปบ** - ๆ็คบ่ฏๅขžๅผบ | +| `lib/ai/model-router.ts` | ๆจกๅž‹่ทฏ็”ฑๅ™จ | **ๆ–ฐๅปบ** - ๆ™บ่ƒฝๆจกๅž‹้€‰ๆ‹ฉ | + +### 2.4 ่ฏฆ็ป†ๅฎž็Žฐๆ–นๆกˆ + +#### 2.4.1 ไฟฎๆ”น route.ts ็š„ Auto Mode ้€ป่พ‘ + +**ๅฝ“ๅ‰ไปฃ็ ** (route.ts ็ฌฌ 334-422 ่กŒ): +```typescript +// ========== Auto Mode Logic ========== +if (finalModelId === "auto") { + console.log("[Auto Mode] Detecting intent..."); + // ไฝฟ็”จๅ…ณ้”ฎ่ฏๅŒน้…... + const videoKeywords = /่ง†้ข‘|video.../; + const imageEditKeywords = /ไฟฎๅ›พ|Pๅ›พ.../; + // ... +} +``` + +**ๅ‡็บงๅŽไปฃ็ **: +```typescript +// ========== Smart Auto Mode Logic ========== +if (finalModelId === "auto") { + console.log("[Smart Auto Mode] Starting intelligent analysis..."); + + // 1. ่ฐƒ็”จๆ™บ่ƒฝๅˆ†ๆžๅ™จ + const { analyzeIntent } = await import("@/lib/ai/smart-analyzer"); + const analysisResult = await analyzeIntent({ + text: textContent, + hasImage, + imageUrls: extractImageUrls(lastUserMessage), + }); + + console.log("[Smart Auto Mode] Analysis result:", analysisResult); + + // 2. ๅฆ‚ๆžœๆ˜ฏๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ๏ผŒไผ˜ๅŒ–ๆ็คบ่ฏ + if (analysisResult.intent.startsWith("image_edit")) { + const { optimizePrompt } = await import("@/lib/ai/prompt-optimizer"); + const optimizedResult = await optimizePrompt({ + originalPrompt: textContent, + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + }); + + // ๆ›ฟๆข็”จๆˆทๆถˆๆฏไธญ็š„ๆ–‡ๆœฌไธบไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + message.parts = message.parts.map(part => { + if (part.type === "text") { + return { ...part, text: optimizedResult.optimizedPrompt }; + } + return part; + }); + + console.log("[Smart Auto Mode] Prompt optimized:", optimizedResult.optimizedPrompt); + } + + // 3. ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹ + const { routeToModel } = await import("@/lib/ai/model-router"); + const selectedModel = await routeToModel({ + intent: analysisResult.intent, + subIntent: analysisResult.subIntent, + hasImage, + }); + + finalModelId = selectedModel.id; + console.log(`[Smart Auto Mode] โœ… Routed to: ${selectedModel.displayName}`); +} +``` + +#### 2.4.2 ๆ–ฐๅปบ smart-analyzer.ts + +```typescript +// lib/ai/smart-analyzer.ts + +import { getLanguageModelById } from "./dynamic-provider"; +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +export interface AnalysisResult { + intent: string; // ไธปๆ„ๅ›พ: image_edit, vision, image_gen, video_gen, chat + subIntent?: string; // ๅญๆ„ๅ›พ: remove_background, replace_background, enhance ็ญ‰ + confidence: number; // ็ฝฎไฟกๅบฆ 0-1 + optimizedPrompt?: string; // ไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ๏ผˆๅฏ้€‰๏ผ‰ + negativePrompt?: string; // ่ดŸ้ขๆ็คบ่ฏ๏ผˆๅฏ้€‰๏ผ‰ + suggestedModel?: string; // ๆŽจ่ๆจกๅž‹ID +} + +const ANALYZER_SYSTEM_PROMPT = `ไฝ ๆ˜ฏๅ›พ็‰‡็ผ–่พ‘ๆ„ๅ›พๅˆ†ๆžไธ“ๅฎถใ€‚ๅˆ†ๆž็”จๆˆท่พ“ๅ…ฅ๏ผŒ่ฟ”ๅ›žJSONๆ ผๅผ็ป“ๆžœใ€‚ + +## ๅ›พ็‰‡็ผ–่พ‘ๅญๆ„ๅ›พ๏ผˆๆ ธๅฟƒ๏ผŒไผ˜ๅ…ˆ่ฏ†ๅˆซ๏ผ‰ +- remove_background: ๆŠ ๅ›พใ€ๅŽป่ƒŒๆ™ฏใ€้€ๆ˜Ž่ƒŒๆ™ฏ +- replace_background: ๆข่ƒŒๆ™ฏใ€ๆ”น่ƒŒๆ™ฏใ€็™ฝๅบ•ใ€ๅœบๆ™ฏๆ›ฟๆข +- enhance: ๅ˜ๆธ…ๆ™ฐใ€้ซ˜ๆธ…ใ€้”ๅŒ–ใ€้™ๅ™ชใ€ๆๅ‡่ดจ้‡ +- extend: ๆ‰ฉๅฑ•ใ€ๆ‹“ๅฑ•ใ€outpainting +- inpaint: ๅŽป้™คXXใ€ๆถˆ้™คใ€ๆ“ฆๆމใ€ไฟฎๆ”นๅฑ€้ƒจ +- style_transfer: ้ฃŽๆ ผๅŒ–ใ€ๅก้€šๅŒ–ใ€ๅŠจๆผซ้ฃŽ +- resize: ็ผฉๆ”พใ€่ฃๅ‰ชใ€่ฐƒๆ•ดๅฐบๅฏธ +- color_adjust: ่ฐƒ่‰ฒใ€่ฐƒๆ•ด้ขœ่‰ฒใ€ไบฎๅบฆใ€ๅฏนๆฏ”ๅบฆ + +## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผJSON๏ผ‰ +{ + "intent": "image_edit", + "subIntent": "replace_background", + "confidence": 0.95, + "optimizedPrompt": "ๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ็บฏ็™ฝ่‰ฒ(#FFFFFF)๏ผŒไฟ็•™ไบงๅ“ไธปไฝ“ๅฎŒๆ•ด๏ผŒไฟๆŒ่‡ช็„ถ้˜ดๅฝฑ๏ผŒ่พน็ผ˜ๅค„็†ๅนณๆป‘่‡ช็„ถ", + "negativePrompt": "ๆจก็ณŠ่พน็ผ˜๏ผŒๆฎ‹็•™่ƒŒๆ™ฏ๏ผŒ้˜ดๅฝฑไธ่‡ช็„ถ๏ผŒ่ฟ‡ๆ›๏ผŒๅ˜ๅฝข" +}`; + +export async function analyzeIntent(params: { + text: string; + hasImage: boolean; + imageUrls?: string[]; +}): Promise { + const { text, hasImage, imageUrls } = params; + + // ่Žทๅ–ๅƒ้—ฎ3 VL ๆจกๅž‹็”จไบŽๅˆ†ๆž + const allModels = await getEnabledAiModelsWithProvider(); + const qwenModel = allModels.find(m => + m.provider.type === "alibaba" && m.model.supportsVision + ); + + if (!qwenModel) { + // ๅ›ž้€€ๅˆฐๅ…ณ้”ฎ่ฏๅŒน้… + return fallbackKeywordAnalysis(text, hasImage); + } + + const model = await getLanguageModelById(qwenModel.model.id); + if (!model) { + return fallbackKeywordAnalysis(text, hasImage); + } + + try { + // ๆž„ๅปบๆถˆๆฏ + const messages = [ + { role: "system", content: ANALYZER_SYSTEM_PROMPT }, + { + role: "user", + content: hasImage + ? `็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒ่ฏทๆฑ‚: "${text}"` + : `็”จๆˆท่ฏทๆฑ‚๏ผˆๆ— ๅ›พ็‰‡๏ผ‰: "${text}"` + } + ]; + + // ่ฐƒ็”จๅƒ้—ฎ3ๅˆ†ๆž + const response = await model.doGenerate({ + messages, + temperature: 0.1, // ไฝŽๆธฉๅบฆไฟ่ฏ็จณๅฎš่พ“ๅ‡บ + }); + + // ่งฃๆžJSONๅ“ๅบ” + const jsonMatch = response.text.match(/\{[\s\S]*\}/); + if (jsonMatch) { + return JSON.parse(jsonMatch[0]); + } + } catch (error) { + console.error("[Smart Analyzer] Error:", error); + } + + return fallbackKeywordAnalysis(text, hasImage); +} + +// ๅ›ž้€€ๆ–นๆกˆ๏ผšๅ…ณ้”ฎ่ฏๅŒน้… +function fallbackKeywordAnalysis(text: string, hasImage: boolean): AnalysisResult { + const lowerText = text.toLowerCase(); + + // ๅ›พ็‰‡็ผ–่พ‘ๅ…ณ้”ฎ่ฏ + if (hasImage) { + if (/ๆŠ ๅ›พ|ๅŽป่ƒŒๆ™ฏ|้€ๆ˜Ž/.test(lowerText)) { + return { intent: "image_edit", subIntent: "remove_background", confidence: 0.9 }; + } + if (/ๆข่ƒŒๆ™ฏ|็™ฝๅบ•|ๆ”น่ƒŒๆ™ฏ/.test(lowerText)) { + return { intent: "image_edit", subIntent: "replace_background", confidence: 0.9 }; + } + if (/ๅ˜ๆธ…ๆ™ฐ|้ซ˜ๆธ…|้”ๅŒ–|้™ๅ™ช/.test(lowerText)) { + return { intent: "image_edit", subIntent: "enhance", confidence: 0.9 }; + } + // ... ๅ…ถไป–ๅญๆ„ๅ›พ + + // ๆœ‰ๅ›พไฝ†ๆ„ๅ›พไธๆ˜Ž็กฎ โ†’ ่ง†่ง‰็†่งฃ + if (/็œ‹|่ฏ†ๅˆซ|ๅˆ†ๆž|ๆ่ฟฐ|่ฟ™ๆ˜ฏไป€ไนˆ/.test(lowerText)) { + return { intent: "vision", confidence: 0.8 }; + } + + // ๆœ‰ๅ›พไฝ†ๆ— ๆ˜Ž็กฎๆ„ๅ›พ โ†’ ้ป˜่ฎคไธบ็ผ–่พ‘ + return { intent: "image_edit", confidence: 0.5 }; + } + + // ๆ— ๅ›พ โ†’ ็”Ÿๆˆๆˆ–ๅฏน่ฏ + if (/็”ป|็ป˜ๅˆถ|็”Ÿๆˆๅ›พ/.test(lowerText)) { + return { intent: "image_gen", confidence: 0.8 }; + } + + return { intent: "chat", confidence: 0.7 }; +} +``` + +#### 2.4.3 ๆ–ฐๅปบ prompt-optimizer.ts + +```typescript +// lib/ai/prompt-optimizer.ts + +export interface OptimizeResult { + originalPrompt: string; + optimizedPrompt: string; + negativePrompt: string; +} + +// ๅ›พ็‰‡็ผ–่พ‘ๅญๆ„ๅ›พ็š„ๆ็คบ่ฏๆจกๆฟ +const EDIT_TEMPLATES: Record = { + remove_background: { + template: "ๅŽป้™คๅ›พ็‰‡่ƒŒๆ™ฏ๏ผŒไฟ็•™ไธปไฝ“{subject}๏ผŒ่พ“ๅ‡บ้€ๆ˜Ž่ƒŒๆ™ฏ๏ผŒ่พน็ผ˜ๅค„็†ๅนณๆป‘่‡ช็„ถ๏ผŒไฟ็•™็ป†่Š‚", + negative: "ๆฎ‹็•™่ƒŒๆ™ฏ๏ผŒๆฏ›่พน๏ผŒ่พน็ผ˜ๆจก็ณŠ๏ผŒไธปไฝ“ไธๅฎŒๆ•ด" + }, + replace_background: { + template: "ๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ{background}๏ผŒไฟๆŒไธปไฝ“ๅฎŒๆ•ด๏ผŒๅ…‰ๅฝฑ่‡ช็„ถๅ่ฐƒ๏ผŒ่พน็ผ˜่ฟ‡ๆธกๅนณๆป‘", + negative: "ๆฎ‹็•™ๅŽŸ่ƒŒๆ™ฏ๏ผŒ่พน็ผ˜ไธ่‡ช็„ถ๏ผŒๅ…‰ๅฝฑไธๅ่ฐƒ๏ผŒ้€่ง†้”™่ฏฏ" + }, + enhance: { + template: "ๆๅ‡ๅ›พ็‰‡ๆ•ดไฝ“ๆธ…ๆ™ฐๅบฆๅ’Œ่ดจ้‡๏ผŒไฟๆŒ่‡ช็„ถ่‰ฒๅฝฉ๏ผŒๅŽป้™คๅ™ช็‚น๏ผŒๅขžๅผบ็ป†่Š‚", + negative: "่ฟ‡ๅบฆ้”ๅŒ–๏ผŒ่‰ฒๅฝฉๅคฑ็œŸ๏ผŒๅ™ช็‚น๏ผŒไผชๅฝฑ" + }, + extend: { + template: "ๅ‘{direction}ๆ–นๅ‘ๆ‰ฉๅฑ•ๅ›พ็‰‡{amount}๏ผŒไฟๆŒๅŽŸๆœ‰้ฃŽๆ ผๅ’Œ่‰ฒ่ฐƒไธ€่‡ด๏ผŒๅ…‰ๅฝฑ่ฟ‡ๆธก่‡ช็„ถ", + negative: "้ฃŽๆ ผไธไธ€่‡ด๏ผŒๆ˜Žๆ˜พๆŽฅ็ผ๏ผŒ้€่ง†้”™่ฏฏ" + }, + inpaint: { + template: "ๅŽป้™ค/ไฟฎๆ”นๅ›พ็‰‡ไธญ็š„{target}๏ผŒ็”จๅ‘จๅ›ด็Žฏๅขƒ่‡ช็„ถๅกซๅ……๏ผŒไฟๆŒๆ•ดไฝ“้ฃŽๆ ผไธ€่‡ด", + negative: "ๆ˜Žๆ˜พไฟฎๅค็—•่ฟน๏ผŒ้ฃŽๆ ผไธไธ€่‡ด๏ผŒๅ†…ๅฎน้”™่ฏฏ" + }, + style_transfer: { + template: "ๅฐ†ๅ›พ็‰‡่ฝฌๆขไธบ{style}้ฃŽๆ ผ๏ผŒไฟๆŒไธปไฝ“ๅฏ่ฏ†ๅˆซๆ€ง๏ผŒ่‰ฒๅฝฉ้ฃŽๆ ผๅ่ฐƒ", + negative: "ไธปไฝ“ๅ˜ๅฝข๏ผŒ็‰นๅพไธขๅคฑ๏ผŒ้ฃŽๆ ผไธ็ปŸไธ€" + } +}; + +export async function optimizePrompt(params: { + originalPrompt: string; + intent: string; + subIntent?: string; +}): Promise { + const { originalPrompt, intent, subIntent } = params; + + // ๅชๅฏนๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ่ฟ›่กŒไผ˜ๅŒ– + if (intent !== "image_edit" || !subIntent) { + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "" + }; + } + + const template = EDIT_TEMPLATES[subIntent]; + if (!template) { + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข" + }; + } + + // ไปŽ็”จๆˆท่พ“ๅ…ฅไธญๆๅ–ๅ…ณ้”ฎไฟกๆฏๅกซๅ……ๆจกๆฟ + let optimizedPrompt = template.template; + + // ๆๅ–่ƒŒๆ™ฏๆ่ฟฐ๏ผˆๆข่ƒŒๆ™ฏๅœบๆ™ฏ๏ผ‰ + if (subIntent === "replace_background") { + const bgMatch = originalPrompt.match(/(?:ๆขๆˆ|ๆ”นๆˆ|ๆ›ฟๆขไธบ|ๅ˜ๆˆ)(.+?)(?:่ƒŒๆ™ฏ|$)/); + const background = bgMatch ? bgMatch[1].trim() : "็บฏ็™ฝ่‰ฒ"; + optimizedPrompt = optimizedPrompt.replace("{background}", background); + } + + // ๆๅ–ๆ‰ฉๅฑ•ๆ–นๅ‘ๅ’Œๆ•ฐ้‡๏ผˆๆ‰ฉๅฑ•ๅœบๆ™ฏ๏ผ‰ + if (subIntent === "extend") { + const dirMatch = originalPrompt.match(/ๅ‘?(ไธŠ|ไธ‹|ๅทฆ|ๅณ)/); + const direction = dirMatch ? dirMatch[1] : "ๅ››ๅ‘จ"; + const amountMatch = originalPrompt.match(/(\d+%?)/); + const amount = amountMatch ? amountMatch[1] : "50%"; + optimizedPrompt = optimizedPrompt + .replace("{direction}", direction) + .replace("{amount}", amount); + } + + // ๆๅ–็›ฎๆ ‡ๅฏน่ฑก๏ผˆๅฑ€้ƒจไฟฎๆ”นๅœบๆ™ฏ๏ผ‰ + if (subIntent === "inpaint") { + const targetMatch = originalPrompt.match(/(?:ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|ๅˆ ้™ค)(.+?)(?:๏ผŒ|ใ€‚|$)/); + const target = targetMatch ? targetMatch[1].trim() : "ๆŒ‡ๅฎšๅŒบๅŸŸ"; + optimizedPrompt = optimizedPrompt.replace("{target}", target); + } + + // ๆๅ–้ฃŽๆ ผ๏ผˆ้ฃŽๆ ผ่ฟ็งปๅœบๆ™ฏ๏ผ‰ + if (subIntent === "style_transfer") { + const styleMatch = originalPrompt.match(/(?:่ฝฌๆขไธบ|ๅ˜ๆˆ|ๆ”นๆˆ)(.+?)(?:้ฃŽๆ ผ|$)/); + const style = styleMatch ? styleMatch[1].trim() : "ๅก้€š"; + optimizedPrompt = optimizedPrompt.replace("{style}", style); + } + + return { + originalPrompt, + optimizedPrompt: `${optimizedPrompt}ใ€‚็”จๆˆทๅŽŸๅง‹่ฆๆฑ‚๏ผš${originalPrompt}`, + negativePrompt: template.negative + }; +} +``` + +#### 2.4.4 ๆ–ฐๅปบ model-router.ts + +```typescript +// lib/ai/model-router.ts + +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +export interface RouteResult { + id: string; + displayName: string; + providerType: string; + reason: string; +} + +// ๆ„ๅ›พๅˆฐๆจกๅž‹็š„ๆ˜ ๅฐ„้…็ฝฎ +const INTENT_MODEL_MAP: Record = { + // ๅ›พ็‰‡็ผ–่พ‘๏ผšไผ˜ๅ…ˆ alibaba-image + "image_edit": { + providerTypes: ["alibaba-image", "gemini"], + requireVision: true, + requireImageGen: true + }, + // ่ง†่ง‰็†่งฃ๏ผšไผ˜ๅ…ˆๅƒ้—ฎ3 VL๏ผˆๆˆๆœฌไฝŽ๏ผ‰ + "vision": { + providerTypes: ["alibaba", "gemini"], + requireVision: true + }, + // ๅ›พ็‰‡็”Ÿๆˆ + "image_gen": { + providerTypes: ["alibaba-image", "gemini", "volcano-vision"], + requireImageGen: true + }, + // ่ง†้ข‘็”Ÿๆˆ + "video_gen": { + providerTypes: ["volcano-video", "qianwen-video"] + }, + // ๆ™ฎ้€šๅฏน่ฏ + "chat": { + providerTypes: ["alibaba", "openai"] + } +}; + +export async function routeToModel(params: { + intent: string; + subIntent?: string; + hasImage: boolean; +}): Promise { + const { intent, subIntent, hasImage } = params; + + // ่Žทๅ–ๆ‰€ๆœ‰ๅฏ็”จๆจกๅž‹ + const allModels = await getEnabledAiModelsWithProvider(); + + // ่Žทๅ–ๆ„ๅ›พๅฏนๅบ”็š„ๆจกๅž‹้…็ฝฎ + const config = INTENT_MODEL_MAP[intent] || INTENT_MODEL_MAP["chat"]; + + // ๆŒ‰ไผ˜ๅ…ˆ็บงๆŸฅๆ‰พๅŒน้…็š„ๆจกๅž‹ + for (const providerType of config.providerTypes) { + const candidates = allModels.filter(m => { + // ๅŒน้… provider ็ฑปๅž‹ + if (m.provider.type !== providerType) return false; + + // ๆฃ€ๆŸฅ่ƒฝๅŠ›่ฆๆฑ‚ + if (config.requireVision && !m.model.supportsVision) return false; + if (config.requireImageGen && !m.model.supportsImageGen) return false; + + // ๆŽ’้™คๅญๆจกๅž‹๏ผˆๅช้€‰ไธปๆจกๅž‹๏ผ‰ + if (m.model.isSubModel) return false; + + return true; + }); + + if (candidates.length > 0) { + // ไผ˜ๅ…ˆ้€‰ๆ‹ฉ้ป˜่ฎคๆจกๅž‹ + const defaultModel = candidates.find(c => c.model.isDefault); + const selected = defaultModel || candidates[0]; + + return { + id: selected.model.id, + displayName: selected.model.displayName, + providerType: selected.provider.type, + reason: `ๆ„ๅ›พ ${intent}${subIntent ? `/${subIntent}` : ''} โ†’ ${selected.model.displayName}` + }; + } + } + + // ๅ…œๅบ•๏ผš่ฟ”ๅ›žไปปๆ„ๅฏ็”จๆจกๅž‹ + const fallback = allModels.find(m => !m.model.isSubModel) || allModels[0]; + return { + id: fallback.model.id, + displayName: fallback.model.displayName, + providerType: fallback.provider.type, + reason: "fallback" + }; +} +``` + +### 2.5 ็”จๆˆทไฝ“้ชŒๆต็จ‹ + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ็”จๆˆทไบคไบ’ๆต็จ‹ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ 1๏ธโƒฃ ็”จๆˆท้€‰ๆ‹ฉๆจกๅž‹๏ผš โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โœจ ๆ™บ่ƒฝ่‡ชๅŠจ (Auto) โ”‚ โ† ้ป˜่ฎค้€‰้กน๏ผŒๆŽจ่ไฝฟ็”จ โ”‚ +โ”‚ โ”‚ ็ต้ญ‚4.5 (ๅ›พ็‰‡็ผ–่พ‘) โ”‚ โ”‚ +โ”‚ โ”‚ ้ฆ™่•‰PRO โ”‚ โ”‚ +โ”‚ โ”‚ ๅƒ้—ฎ3 VL โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ”‚ 2๏ธโƒฃ ็”จๆˆทไธŠไผ ๅ›พ็‰‡ + ่พ“ๅ…ฅๆ–‡ๅญ—๏ผš โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ [๐Ÿ“ธ ไบงๅ“ๅ›พ.jpg] โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ๅธฎๆˆ‘ๆŠŠ่ƒŒๆ™ฏๆขๆˆ็™ฝ่‰ฒ _______________ ๐Ÿ“ค โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ”‚ 3๏ธโƒฃ ็ณป็ปŸๅค„็†๏ผˆๅŽๅฐ้€ๆ˜Žๆ‰ง่กŒ๏ผ‰๏ผš โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ๐Ÿ” ๅˆ†ๆžๆ„ๅ›พ: replace_background (95%) โ”‚ โ”‚ +โ”‚ โ”‚ โœจ ไผ˜ๅŒ–ๆ็คบ่ฏ... โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿš€ ่ทฏ็”ฑๅˆฐ: ็ต้ญ‚4.5 โ”‚ โ”‚ +โ”‚ โ”‚ โณ ็”Ÿๆˆไธญ... โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ”‚ 4๏ธโƒฃ ่ฟ”ๅ›ž็ป“ๆžœ๏ผš โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ๐Ÿค– ๅŠฉๆ‰‹: โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ๅทฒๅฐ†่ƒŒๆ™ฏๆ›ฟๆขไธบ็™ฝ่‰ฒใ€‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ [็ผ–่พ‘ๅŽ็š„ๅ›พ็‰‡้ข„่งˆ] โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ“ฅ ไธ‹่ฝฝๅŽŸๅ›พ ๐Ÿ”„ ้‡ๆ–ฐ็”Ÿๆˆ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## ไธ‰ใ€ๆŠ€ๆœฏๆžถๆž„ + +### 2.1 ๆ•ดไฝ“ๆžถๆž„ๅ›พ + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๅฎขๆˆท็ซฏๅฑ‚ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ MultimodalInput โ”€โ”€โ–ถ ้€‰ๆ‹ฉ"ๆ™บ่ƒฝ่‡ชๅŠจ"ๆจกๅž‹ โ”€โ”€โ–ถ ๅ‘้€ๆถˆๆฏ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ API ็ฝ‘ๅ…ณๅฑ‚ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ /api/chat/route.ts โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ”œโ”€โ–ถ ๆฃ€ๆต‹ modelId === "auto" โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ–ถ ่ฐƒ็”จๆ™บ่ƒฝๅˆ†ๆžๆœๅŠก โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ + โ–ผ โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ๆ™บ่ƒฝๅˆ†ๆžๆœๅŠกๅฑ‚ (ๆ–ฐๅขž) โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ๆ„ๅ›พๅˆ†ๆžๆจกๅ— โ”‚ โ”‚ ๆ็คบ่ฏไผ˜ๅŒ–ๆจกๅ— โ”‚ โ”‚ ๆจกๅž‹่ทฏ็”ฑๆจกๅ— โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ„ๅ›พๅˆ†็ฑป โ”‚ โ”‚ - ็ปดๅบฆๅˆ†่งฃ โ”‚ โ”‚ - ๅคšๅ› ็ด ่ฏ„ๅˆ† โ”‚ โ”‚ +โ”‚ โ”‚ - ็ฝฎไฟกๅบฆ่ฏ„ไผฐ โ”‚ โ”‚ - ไธ“ไธš่ฏๆฑ‡ๆณจๅ…ฅ โ”‚ โ”‚ - ่ƒฝๅŠ›ๅŒน้… โ”‚ โ”‚ +โ”‚ โ”‚ - ๆพ„ๆธ…้—ฎ้ข˜็”Ÿๆˆ โ”‚ โ”‚ - ่ดจ้‡ๅ…ณ้”ฎ่ฏ โ”‚ โ”‚ - ่ดŸ่ฝฝๅ‡่กก โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Qwen3 ๅคšๆจกๆ€ๆจกๅž‹ โ”‚ โ”‚ +โ”‚ โ”‚ (qwen3-turbo) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ็”Ÿๆˆๆจกๅž‹ๆ‰ง่กŒๅฑ‚ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Gemini 2.0 โ”‚ โ”‚ ้˜ฟ้‡Œไธ‡่ฑก โ”‚ โ”‚ ็ซๅฑฑ่ง†้ข‘ โ”‚ โ”‚ Qwen-VL โ”‚ โ”‚ +โ”‚ โ”‚ Flash โ”‚ โ”‚ ๅ›พ็‰‡็”Ÿๆˆ โ”‚ โ”‚ ็”Ÿๆˆๆจกๅž‹ โ”‚ โ”‚ ๅคšๆจกๆ€ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ ็”จๆˆทๅๅฅฝๅญฆไน ๅฑ‚ (ๆ–ฐๅขž) โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ ๅ้ฆˆๆ”ถ้›† โ”‚ โ”‚ ๅๅฅฝๅˆ†ๆž โ”‚ โ”‚ ไธŠไธ‹ๆ–‡่ฎฐๅฟ† โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ - ๆ˜พๅผๅ้ฆˆ โ”‚ โ”‚ - ้ฃŽๆ ผๅๅฅฝ โ”‚ โ”‚ - ๅކๅฒๆ็คบ่ฏ โ”‚ โ”‚ +โ”‚ โ”‚ - ้šๅผๅ้ฆˆ โ”‚ โ”‚ - ่‰ฒๅฝฉๅๅฅฝ โ”‚ โ”‚ - ๅธธ็”จ้ฃŽๆ ผ โ”‚ โ”‚ +โ”‚ โ”‚ - ้‡ๆ–ฐ็”Ÿๆˆ็ปŸ่ฎก โ”‚ โ”‚ - ่ดจ้‡ๅๅฅฝ โ”‚ โ”‚ - ๅ†ณ็ญ–็‚น่ฟฝ่ธช โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### 2.2 ๆ ธๅฟƒๆจกๅ—่ฎพ่ฎก + +#### 2.2.1 ๆ„ๅ›พๅˆ†ๆžๆจกๅ— + +**่พ“ๅ…ฅ๏ผš** +- ็”จๆˆทๆ–‡ๆœฌ่พ“ๅ…ฅ +- ๆ˜ฏๅฆๆœ‰ๅ›พ็‰‡้™„ไปถ +- ๅฏน่ฏๅކๅฒ (ไธŠไธ‹ๆ–‡) + +**่พ“ๅ‡บ๏ผš** +```typescript +interface IntentAnalysis { + // ๆ„ๅ›พ็ฑปๅž‹ + intent: "image_gen" | "image_edit" | "video_gen" | "vision" | "chat"; + + // ๆ„ๅ›พ็ฝฎไฟกๅบฆ (0-1) + confidence: number; + + // ๅญๆ„ๅ›พ (ๆ›ด็ป†็ฒ’ๅบฆ) + subIntent?: string; // ๅฆ‚ "remove_background", "enhance_quality", "style_transfer" + + // ๆ˜ฏๅฆ้œ€่ฆๆพ„ๆธ… + needsClarification: boolean; + + // ๆพ„ๆธ…้—ฎ้ข˜ (ๅฆ‚ๆžœ้œ€่ฆ) + clarifyingQuestions?: string[]; +} +``` + +#### 2.2.2 ๆ็คบ่ฏไผ˜ๅŒ–ๆจกๅ— + +**่ฎพ่ฎก็ปดๅบฆๅˆ†่งฃ (ๅ‚่€ƒ Lovart ็š„ 12 ็ปดๅบฆ)๏ผš** + +| ็ปดๅบฆ | ่‹ฑๆ–‡ | ๆ่ฟฐ | ็คบไพ‹ | +|-----|------|------|------| +| ไธปไฝ“ | subject | ็”ป้ข็š„ไธป่ฆๅฏน่ฑก | "ไธ€ๅชๆฉ˜่‰ฒ็š„็Œซ" | +| ๅŠจไฝœ | action | ไธปไฝ“็š„ๅงฟๆ€ๆˆ–่กŒไธบ | "ๆ…ตๆ‡’ๅœฐ่บบ็€" | +| ้ฃŽๆ ผ | style | ่‰บๆœฏ้ฃŽๆ ผ | "ๅ†™ๅฎžๆ‘„ๅฝฑ้ฃŽๆ ผ" | +| ๅ…‰ๅฝฑ | lighting | ๅ…‰็…งๆ•ˆๆžœ | "ๆŸ”ๅ’Œ็š„่‡ช็„ถๅ…‰" | +| ๆž„ๅ›พ | composition | ็”ป้ขๆž„ๅ›พ | "ไธญๆ™ฏ็‰นๅ†™" | +| ่‰ฒๅฝฉ | color | ่‰ฒๅฝฉๆ–นๆกˆ | "ๆš–่‰ฒ่ฐƒ" | +| ๆฐ›ๅ›ด | mood | ๆƒ…็ปชๆฐ›ๅ›ด | "ๆธฉ้ฆจ่ˆ’้€‚" | +| ่ดจ้‡ | quality | ่ดจ้‡ๅ…ณ้”ฎ่ฏ | "8K, ้ซ˜็ป†่Š‚" | + +**่พ“ๅ‡บ๏ผš** +```typescript +interface PromptOptimization { + // ็ปดๅบฆๅˆ†่งฃ + dimensions: { + subject: string; + action: string; + style: string; + lighting: string; + composition: string; + color: string; + mood: string; + quality: string; + }; + + // ็ปดๅบฆๆƒ้‡ (้‡่ฆ็จ‹ๅบฆ) + weights: Record; + + // ไผ˜ๅŒ–ๅŽ็š„ๅฎŒๆ•ดๆ็คบ่ฏ + optimizedPrompt: string; + + // ่ดŸ้ขๆ็คบ่ฏ (้œ€่ฆ้ฟๅ…็š„) + negativePrompt?: string; +} +``` + +#### 2.2.3 ๆจกๅž‹่ทฏ็”ฑๆจกๅ— + +**ๅคšๅ› ็ด ่ฏ„ๅˆ†็ฎ—ๆณ•๏ผš** + +```typescript +interface ModelScore { + modelId: string; + scores: { + intentMatch: number; // ๆ„ๅ›พๅŒน้…ๅบฆ (0-50) + styleMatch: number; // ้ฃŽๆ ผๅŒน้…ๅบฆ (0-20) + qualityMatch: number; // ่ดจ้‡่ƒฝๅŠ›ๅŒน้… (0-15) + costEfficiency: number; // ๆˆๆœฌๆ•ˆ็›Š (0-10) + loadBalance: number; // ่ดŸ่ฝฝๅ‡่กก (0-5) + }; + totalScore: number; +} +``` + +--- + +## ไธ‰ใ€ๅฎž็Žฐ่ฎกๅˆ’ + +### Phase 1: ๆ ธๅฟƒๅŠŸ่ƒฝ (MVP) + +**็›ฎๆ ‡๏ผš** ๅฎž็Žฐ Qwen3 ๆ„ๅ›พๅˆ†ๆž + ๆ็คบ่ฏไผ˜ๅŒ– + +**ๆ–ฐๅขžๆ–‡ไปถ๏ผš** +``` +lib/ +โ”œโ”€โ”€ ai/ +โ”‚ โ”œโ”€โ”€ smart-analyzer.ts # ๆ™บ่ƒฝๅˆ†ๆžๆ ธๅฟƒ้€ป่พ‘ +โ”‚ โ”œโ”€โ”€ prompt-optimizer.ts # ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ +โ”‚ โ””โ”€โ”€ model-router.ts # ๆจกๅž‹่ทฏ็”ฑๅ™จ +โ”‚ +app/ +โ”œโ”€โ”€ api/ +โ”‚ โ””โ”€โ”€ smart-analyze/ +โ”‚ โ””โ”€โ”€ route.ts # ๆ™บ่ƒฝๅˆ†ๆž API +``` + +**ไฟฎๆ”นๆ–‡ไปถ๏ผš** +``` +app/(chat)/api/chat/route.ts # ้›†ๆˆๆ™บ่ƒฝๅˆ†ๆž +``` + +#### 3.1.1 ๆ™บ่ƒฝๅˆ†ๆžๅ™จ (`lib/ai/smart-analyzer.ts`) + +```typescript +import { createOpenAI } from "@ai-sdk/openai"; +import { generateText } from "ai"; + +// Qwen3 ๅฎขๆˆท็ซฏ้…็ฝฎ +const qwen = createOpenAI({ + baseURL: process.env.QWEN_API_BASE_URL, + apiKey: process.env.QWEN_API_KEY, +}); + +// ็ณป็ปŸๆ็คบ่ฏ - ้’ˆๅฏนๅฝ“ๅ‰็ณป็ปŸๆจกๅž‹้…็ฝฎไผ˜ๅŒ– +const ANALYZER_SYSTEM_PROMPT = `ไฝ ๆ˜ฏไธ€ไธชไธ“ไธš็š„ AI ่ฎพ่ฎกๅŠฉๆ‰‹๏ผŒ่ดŸ่ดฃๅˆ†ๆž็”จๆˆทๆ„ๅ›พๅนถไผ˜ๅŒ–ๆ็คบ่ฏใ€‚ + +## ไฝ ็š„ไปปๅŠก +1. ๅˆ†ๆž็”จๆˆท่พ“ๅ…ฅ็š„ๆ„ๅ›พ็ฑปๅž‹ +2. ๅฐ†ๆจก็ณŠๆ่ฟฐๅˆ†่งฃไธบ 8 ไธชไธ“ไธš่ฎพ่ฎก็ปดๅบฆ +3. ็”Ÿๆˆไผ˜ๅŒ–ๅŽ็š„้ซ˜่ดจ้‡ๆ็คบ่ฏ +4. ๆŽจ่ๆœ€้€‚ๅˆ็š„็”Ÿๆˆๆจกๅž‹ + +## ๅฝ“ๅ‰็ณป็ปŸๅฏ็”จ็š„ๆจกๅž‹ +| ๆจกๅž‹ๅ็งฐ | ็ฑปๅž‹ | ่ƒฝๅŠ› | ้€‚็”จๅœบๆ™ฏ | +|---------|------|------|---------| +| ๅƒ้—ฎ3 (VL) | alibaba | ๅฏน่ฏ+่ง†่ง‰็†่งฃ | ๆ™ฎ้€šๅฏน่ฏใ€็œ‹ๅ›พ้—ฎ็ญ”ใ€ๅ›พ็‰‡ๅˆ†ๆž | +| ็ต้ญ‚4.5 | alibaba-image | ๅ›พ็‰‡็”Ÿๆˆ+็ผ–่พ‘ | ้ซ˜่ดจ้‡ๅ›พ็‰‡็”Ÿๆˆ๏ผŒๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ• | +| ้ฆ™่•‰PRO | gemini | ๅ›พ็‰‡็”Ÿๆˆ+่ง†่ง‰ | ๅ›พ็‰‡็”Ÿๆˆใ€ๅ›พ็‰‡็ผ–่พ‘ | +| ๆ—ขๆขฆ4.5 | volcano-vision | ๅ›พ็‰‡็”Ÿๆˆ | ๅฟซ้€Ÿๅ›พ็‰‡็”Ÿๆˆ | +| GTP1.5 | gemini | ่ง†่ง‰็†่งฃ | ๅ›พ็‰‡ๅˆ†ๆžใ€่ฏ†ๅˆซ | +| VIDEO | volcano-video | ่ง†้ข‘็”Ÿๆˆ | ๆ–‡็”Ÿ่ง†้ข‘ใ€ๅ›พ็”Ÿ่ง†้ข‘ | + +ๆณจๆ„๏ผšๅƒ้—ฎ3 ไฝฟ็”จ็š„ๆ˜ฏ qwen3-vl-plus ๅคšๆจกๆ€ๆจกๅž‹๏ผŒVL = Vision-Language๏ผŒๆ—ขๆ”ฏๆŒๆ–‡ๆœฌๅฏน่ฏไนŸๆ”ฏๆŒๅ›พ็‰‡็†่งฃใ€‚ + +## ๆ„ๅ›พ็ฑปๅž‹ๅฎšไน‰๏ผˆๅ›พ็‰‡็ผ–่พ‘ไธบไธป๏ผ‰ + +### ๆ ธๅฟƒๆ„ๅ›พ๏ผšๅ›พ็‰‡็ผ–่พ‘ (image_edit) - ๆœ€้ซ˜ไผ˜ๅ…ˆ็บง +็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒๅนถๅธŒๆœ›ๅฏนๅ…ถ่ฟ›่กŒ็ผ–่พ‘ไฟฎๆ”นใ€‚ + +| ๅญๆ„ๅ›พ | ่งฆๅ‘่ฏ | ่ฏดๆ˜Ž | +|-------|-------|------| +| remove_background | ๆŠ ๅ›พใ€ๅŽป่ƒŒๆ™ฏใ€ๅŽป่ƒŒใ€ๆ‰ฃๅ›พใ€้€ๆ˜Ž่ƒŒๆ™ฏ | ๅŽป้™ค่ƒŒๆ™ฏไฟ็•™ไธปไฝ“ | +| replace_background | ๆข่ƒŒๆ™ฏใ€ๆ”น่ƒŒๆ™ฏใ€่ƒŒๆ™ฏๆขๆˆใ€ๅœบๆ™ฏๆ›ฟๆข | ๆ›ฟๆขไธบๆ–ฐ่ƒŒๆ™ฏ | +| enhance | ๅ˜ๆธ…ๆ™ฐใ€้ซ˜ๆธ…ใ€้”ๅŒ–ใ€้™ๅ™ชใ€ๆๅ‡่ดจ้‡ใ€ไฟฎๅค | ๅ›พ็‰‡ๅขžๅผบ | +| extend | ๆ‰ฉๅฑ•ใ€ๆ‹“ๅฑ•ใ€ๅปถไผธใ€outpaintingใ€ๆ‰ฉๅ›พ | ๅ›พๅƒ่พน็•Œๆ‹“ๅฑ• | +| inpaint | ๅŽป้™คXXใ€ๆถˆ้™คใ€ๆ“ฆๆމใ€ไฟฎๆ”นXX้ƒจๅˆ†ใ€ๅฑ€้ƒจไฟฎๆ”น | ๅฑ€้ƒจๅŒบๅŸŸไฟฎๆ”น | +| style_transfer | ้ฃŽๆ ผๅŒ–ใ€ๅก้€šๅŒ–ใ€ๅŠจๆผซ้ฃŽใ€ๆฒน็”ป้ฃŽใ€ๆ”น้ฃŽๆ ผ | ้ฃŽๆ ผ่ฟ็งป | +| resize | ็ผฉๆ”พใ€ๆ”พๅคงใ€็ผฉๅฐใ€่ฐƒๆ•ดๅฐบๅฏธใ€่ฃๅ‰ช | ๅฐบๅฏธ่ฐƒๆ•ด | +| color_adjust | ่ฐƒ่‰ฒใ€่ฐƒๆ•ด้ขœ่‰ฒใ€ไบฎๅบฆใ€ๅฏนๆฏ”ๅบฆใ€้ฅฑๅ’Œๅบฆ | ้ขœ่‰ฒ่ฐƒๆ•ด | + +ๆŽจ่ๆจกๅž‹๏ผšalibaba-image๏ผˆ็ต้ญ‚4.5๏ผ‰ๆˆ– gemini๏ผˆ้ฆ™่•‰PRO๏ผ‰ + +### ่พ…ๅŠฉๆ„ๅ›พ๏ผšๅ›พ็‰‡็†่งฃ (vision) +็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒๆƒณ่ฆ็†่งฃ/ๅˆ†ๆžๅ…ถๅ†…ๅฎน๏ผˆไธๆถ‰ๅŠ็ผ–่พ‘๏ผ‰ใ€‚ + +่งฆๅ‘่ฏ๏ผš็œ‹ใ€่ฏ†ๅˆซใ€ๅˆ†ๆžใ€ๆ่ฟฐใ€่ฟ™ๆ˜ฏไป€ไนˆใ€ๆœ‰ไป€ไนˆใ€tell meใ€what is this +ๆŽจ่ๆจกๅž‹๏ผšalibaba๏ผˆๅƒ้—ฎ3 VL๏ผŒๆˆๆœฌไฝŽๆ•ˆๆžœๅฅฝ๏ผ‰ + +### ่พ…ๅŠฉๆ„ๅ›พ๏ผšๅ›พ็‰‡็”Ÿๆˆ (image_gen) +็”จๆˆทๆƒณ่ฆ็”Ÿๆˆๆ–ฐๅ›พ็‰‡๏ผˆๆ— ้œ€ไธŠไผ ๅ›พ็‰‡๏ผŒๆˆ–ๅŸบไบŽๅ‚่€ƒๅ›พ็”Ÿๆˆ๏ผ‰ใ€‚ + +่งฆๅ‘่ฏ๏ผš็”ปใ€็ป˜ๅˆถใ€็”Ÿๆˆๅ›พ็‰‡ใ€ๅˆ›ๅปบใ€่ฎพ่ฎก +ๆŽจ่ๆจกๅž‹๏ผšalibaba-image๏ผˆ็ต้ญ‚4.5๏ผ‰ + +### ่พ…ๅŠฉๆ„ๅ›พ๏ผš่ง†้ข‘็”Ÿๆˆ (video_gen) +็”จๆˆทๆƒณ่ฆๅฐ†ๅ›พ็‰‡่ฝฌไธบ่ง†้ข‘ๆˆ–็”Ÿๆˆ่ง†้ข‘ใ€‚ + +่งฆๅ‘่ฏ๏ผš่ง†้ข‘ใ€ๅŠจ็”ปใ€ๅŠจ่ตทๆฅใ€่ฎฉๅฎƒๅŠจ +ๆŽจ่ๆจกๅž‹๏ผšvolcano-video + +### ้ป˜่ฎคๆ„ๅ›พ๏ผšๅฏน่ฏ (chat) +ๆ™ฎ้€šๆ–‡ๅญ—ๅฏน่ฏ๏ผŒไธๆถ‰ๅŠๅ›พๅƒๅค„็†ใ€‚ + +ๆŽจ่ๆจกๅž‹๏ผšalibaba๏ผˆๅƒ้—ฎ3 VL๏ผ‰ + +## ๆ็คบ่ฏไผ˜ๅŒ–ๅ…ฌๅผ + +### ๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏ๏ผˆๆ ธๅฟƒ๏ผ‰ +้’ˆๅฏนไธๅŒ็ผ–่พ‘ๅญๆ„ๅ›พ๏ผŒไผ˜ๅŒ–ๆ็คบ่ฏ็š„้‡็‚นไธๅŒ๏ผš + +| ๅญๆ„ๅ›พ | ๆ็คบ่ฏ้‡็‚น | ็คบไพ‹ไผ˜ๅŒ– | +|-------|----------|---------| +| remove_background | ๆ่ฟฐไธปไฝ“็‰นๅพ | "ไฟ็•™ไบงๅ“ไธปไฝ“๏ผŒๅŽป้™คๆ‰€ๆœ‰่ƒŒๆ™ฏ๏ผŒ่พ“ๅ‡บ้€ๆ˜ŽPNG" | +| replace_background | ๆ่ฟฐๆ–ฐ่ƒŒๆ™ฏ็ป†่Š‚ | "ๅฐ†่ƒŒๆ™ฏๆ›ฟๆขไธบ็™ฝ่‰ฒ็บฏ่‰ฒ่ƒŒๆ™ฏ๏ผŒไฟๆŒไบงๅ“้˜ดๅฝฑ" | +| enhance | ๆ่ฟฐๅขžๅผบ็จ‹ๅบฆๅ’Œๆ–นๅ‘ | "ๆๅ‡ๅ›พ็‰‡ๆธ…ๆ™ฐๅบฆ๏ผŒไฟๆŒ่‡ช็„ถ่‰ฒๅฝฉ๏ผŒๅŽป้™คๅ™ช็‚น" | +| extend | ๆ่ฟฐๆ‰ฉๅฑ•ๆ–นๅ‘ๅ’Œๅ†…ๅฎน | "ๅ‘ๅณๆ‰ฉๅฑ•50%๏ผŒไฟๆŒๅŽŸๆœ‰้ฃŽๆ ผๅ’Œๅ…‰ๅฝฑไธ€่‡ดๆ€ง" | +| inpaint | ็ฒพ็กฎๆ่ฟฐไฟฎๆ”นๅŒบๅŸŸ | "ๅŽป้™คๅ›พ็‰‡ๅณไธŠ่ง’็š„ๆฐดๅฐ๏ผŒ็”จๅ‘จๅ›ด็บน็†่‡ช็„ถๅกซๅ……" | +| style_transfer | ๆ่ฟฐ็›ฎๆ ‡้ฃŽๆ ผ | "ๅฐ†็…ง็‰‡่ฝฌๆขไธบๅ‰ๅœๅŠ›ๅŠจ็”ป้ฃŽๆ ผ๏ผŒไฟๆŒไบบ็‰ฉ็‰นๅพ" | + +### ๅ›พ็‰‡็”Ÿๆˆๅœบๆ™ฏ๏ผˆ่พ…ๅŠฉ๏ผ‰ +[ไธปไฝ“ Subject] + [ๅŠจไฝœ Action] + [้ฃŽๆ ผ Style] + [ๅ…‰ๅฝฑ Lighting] + [ๆž„ๅ›พ Composition] + [่‰ฒๅฝฉ Color] + [ๆฐ›ๅ›ด Mood] + [่ดจ้‡ Quality] + +## ่พ“ๅ‡บๆ ผๅผ (ไธฅๆ ผ JSON) +{ + "intent": "image_gen", + "confidence": 0.95, + "subIntent": "product_design", + "needsClarification": false, + "dimensions": { + "subject": "ไธปไฝ“ๆ่ฟฐ", + "action": "ๅŠจไฝœ/ๅงฟๆ€", + "style": "่‰บๆœฏ้ฃŽๆ ผ", + "lighting": "ๅ…‰ๅฝฑๆ•ˆๆžœ", + "composition": "ๆž„ๅ›พๆ–นๅผ", + "color": "่‰ฒๅฝฉๆ–นๆกˆ", + "mood": "ๆƒ…็ปชๆฐ›ๅ›ด", + "quality": "่ดจ้‡ๅ…ณ้”ฎ่ฏ" + }, + "optimizedPrompt": "ๅฎŒๆ•ดไผ˜ๅŒ–ๅŽ็š„ไธญๆ–‡ๆ็คบ่ฏ๏ผˆ100-200ๅญ—๏ผ‰", + "negativePrompt": "้œ€่ฆ้ฟๅ…็š„ๅ…ƒ็ด ๏ผˆๅฆ‚๏ผšๆจก็ณŠใ€ไฝŽ่ดจ้‡ใ€ๅ˜ๅฝข็ญ‰๏ผ‰", + "suggestedModel": "alibaba-image", + "clarifyingQuestions": [] +} + +## ไผ˜ๅŒ–ๅŽŸๅˆ™ +1. ไฟๆŒ็”จๆˆทๅŽŸๅง‹ๆ„ๅ›พไธๅ˜ +2. ่กฅๅ……็ผบๅคฑ็š„ไธ“ไธšๆ่ฟฐ๏ผˆๅ…‰ๅฝฑใ€ๆž„ๅ›พใ€ๆฐ›ๅ›ด็ญ‰๏ผ‰ +3. ๆทปๅŠ ้€‚ๅฝ“็š„่ดจ้‡ๅ…ณ้”ฎ่ฏ๏ผˆ้ซ˜ๆธ…ใ€็ป†่Š‚ไธฐๅฏŒใ€ไธ“ไธš็บง็ญ‰๏ผ‰ +4. ไฝฟ็”จไธญๆ–‡่พ“ๅ‡บ +5. ๆ็คบ่ฏ้•ฟๅบฆๆŽงๅˆถๅœจ 100-200 ๅญ— +6. ๅฏนไบŽ chat/vision ๆ„ๅ›พ๏ผŒoptimizedPrompt ไฟๆŒ็”จๆˆทๅŽŸๅง‹่พ“ๅ…ฅๅณๅฏ + +## ็‰นๆฎŠๅœบๆ™ฏๅค„็†๏ผˆๅ›พ็‰‡็ผ–่พ‘ไผ˜ๅ…ˆ๏ผ‰ + +1. **็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ + ็ผ–่พ‘็ฑปๅ…ณ้”ฎ่ฏ** โ†’ image_edit๏ผˆๆœ€้ซ˜ไผ˜ๅ…ˆ็บง๏ผ‰ +2. **็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ + ๆ— ๆ˜Ž็กฎๆ„ๅ›พ** โ†’ ่ฟ”ๅ›žๆพ„ๆธ…้—ฎ้ข˜๏ผŒ่ฏข้—ฎ้œ€่ฆไป€ไนˆ็ผ–่พ‘ +3. **็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ + ็†่งฃ็ฑปๅ…ณ้”ฎ่ฏ** โ†’ vision๏ผˆๅƒ้—ฎ3 VL๏ผ‰ +4. **็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ + ็”Ÿๆˆ่ง†้ข‘ๅ…ณ้”ฎ่ฏ** โ†’ video_gen๏ผˆ็ซๅฑฑ่ง†้ข‘๏ผ‰ +5. **็”จๆˆทๆ— ๅ›พ็‰‡ + ็”Ÿๆˆ็ฑปๅ…ณ้”ฎ่ฏ** โ†’ image_gen๏ผˆ็ต้ญ‚4.5๏ผ‰ +6. **็”จๆˆทๆ— ๅ›พ็‰‡ + ็ผ–่พ‘็ฑปๅ…ณ้”ฎ่ฏ** โ†’ needsClarification: true๏ผŒๆ็คบ"่ฏทไธŠไผ ๅ›พ็‰‡" + +## ๆพ„ๆธ…้—ฎ้ข˜็คบไพ‹ +ๅฝ“ๆ„ๅ›พไธๆ˜Ž็กฎๆ—ถ๏ผŒ่ฟ”ๅ›žไปฅไธ‹ๆพ„ๆธ…้—ฎ้ข˜๏ผš +- "ๆ‚จๆƒณๅฏน่ฟ™ๅผ ๅ›พ็‰‡ๅšไป€ไนˆๅค„็†๏ผŸ๏ผˆๆŠ ๅ›พ/ๆข่ƒŒๆ™ฏ/ๅ˜ๆธ…ๆ™ฐ/ๅ…ถไป–๏ผ‰" +- "่ฏทๆ่ฟฐๆ‚จๅธŒๆœ›่พพๅˆฐ็š„ๆ•ˆๆžœ" +- "้œ€่ฆๆˆ‘ๅธฎๆ‚จๅŽป้™ค่ƒŒๆ™ฏๅ—๏ผŸ่ฟ˜ๆ˜ฏๆœ‰ๅ…ถไป–็ผ–่พ‘้œ€ๆฑ‚๏ผŸ"`; + +export interface SmartAnalysisResult { + intent: "image_gen" | "image_edit" | "video_gen" | "vision" | "chat"; + confidence: number; + subIntent?: string; + needsClarification: boolean; + dimensions: { + subject: string; + action: string; + style: string; + lighting: string; + composition: string; + color: string; + mood: string; + quality: string; + }; + optimizedPrompt: string; + negativePrompt?: string; + suggestedModel: string; + clarifyingQuestions?: string[]; +} + +export async function analyzeAndOptimize( + userInput: string, + hasImage: boolean, + chatHistory?: { role: string; content: string }[] +): Promise { + const contextInfo = hasImage ? "๏ผˆ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผ‰" : ""; + + const { text } = await generateText({ + model: qwen("qwen3-turbo"), + system: ANALYZER_SYSTEM_PROMPT, + messages: [ + ...(chatHistory || []), + { + role: "user", + content: `${contextInfo}\n็”จๆˆท่พ“ๅ…ฅ: ${userInput}` + } + ], + }); + + try { + return JSON.parse(text); + } catch (error) { + // ่งฃๆžๅคฑ่ดฅๆ—ถ่ฟ”ๅ›ž้ป˜่ฎคๅ€ผ + console.error("[SmartAnalyzer] JSON parse error:", error); + return { + intent: hasImage ? "vision" : "chat", + confidence: 0.5, + needsClarification: false, + dimensions: { + subject: userInput, + action: "", + style: "", + lighting: "", + composition: "", + color: "", + mood: "", + quality: "", + }, + optimizedPrompt: userInput, + suggestedModel: "qwen-vl", + }; + } +} +``` + +#### 3.1.2 ๆจกๅž‹่ทฏ็”ฑๅ™จ (`lib/ai/model-router.ts`) + +```typescript +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +interface ModelWithProvider { + model: { + id: string; + displayName: string; + modelId: string; + supportsImageGen: boolean; + supportsVision: boolean; + isDefault: boolean; + }; + provider: { + type: string; // alibaba, gemini, volcano-vision, volcano-video, alibaba-image, etc. + }; +} + +/** + * ๅŸบไบŽๅฝ“ๅ‰็ณป็ปŸๅทฒ้…็ฝฎ็š„ๆจกๅž‹๏ผŒๆ™บ่ƒฝ่ทฏ็”ฑๆ˜ ๅฐ„ + * + * ๅฝ“ๅ‰ๆจกๅž‹ๆธ…ๅ•๏ผš + * - ๅƒ้—ฎ3 (alibaba): ๅฏน่ฏๆจกๅž‹ + * - ๆ—ขๆขฆ4.5 (volcano-vision): ๅ›พ็‰‡็”Ÿๆˆ + * - GTP1.5 (gemini): ่ง†่ง‰็†่งฃ + * - ้ฆ™่•‰PRO (gemini): ่ง†่ง‰+ๅ›พ็‰‡็”Ÿๆˆ + * - ็ต้ญ‚4.5 (alibaba-image): ่ง†่ง‰+ๅ›พ็‰‡็”Ÿๆˆ+ๆ็คบ่ฏๆ‰ฉๅฑ• + * - VIDEO/Seedance* (volcano-video): ่ง†้ข‘็”Ÿๆˆ + */ +const INTENT_PROVIDER_MAP: Record = { + // ๅ›พ็‰‡็”Ÿๆˆ๏ผšไผ˜ๅ…ˆ้€‰ๆ‹ฉ alibaba-image (็ต้ญ‚4.5๏ผŒๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ•) + "image_gen": { + providers: ["alibaba-image", "gemini", "volcano-vision"], + requireImageGen: true + }, + // ๅ›พ็‰‡็ผ–่พ‘๏ผš้œ€่ฆ่ง†่ง‰่ƒฝๅŠ› + ๅ›พ็‰‡็”Ÿๆˆ + "image_edit": { + providers: ["alibaba-image", "gemini"], + requireVision: true, + requireImageGen: true + }, + // ่ง†้ข‘็”Ÿๆˆ๏ผš็ซๅฑฑ่ง†้ข‘ๆจกๅž‹ + "video_gen": { + providers: ["volcano-video", "qianwen-video"] + }, + // ่ง†่ง‰็†่งฃ๏ผšไผ˜ๅ…ˆๅƒ้—ฎ3 VL๏ผˆๆˆๆœฌไฝŽใ€ๆ•ˆๆžœๅฅฝ๏ผ‰๏ผŒ็„ถๅŽๆ˜ฏ gemini + "vision": { + providers: ["alibaba", "gemini", "alibaba-image"], + requireVision: true + }, + // ๆ™ฎ้€šๅฏน่ฏ๏ผš้˜ฟ้‡Œไบ‘ๅƒ้—ฎ3 VL๏ผˆไนŸๆ”ฏๆŒ่ง†่ง‰๏ผ‰ + "chat": { + providers: ["alibaba", "openai", "local"] + }, +}; + +// ็‰นๆฎŠๆจกๅž‹ ID ๆ˜ ๅฐ„๏ผˆ็”จไบŽ็ฒพ็กฎๅŒน้…๏ผ‰ +const SPECIAL_MODEL_ROUTING: Record = { + // ๅ›พ็”Ÿ่ง†้ข‘ๅœบๆ™ฏ๏ผšไผ˜ๅ…ˆไฝฟ็”จ SeedanceLiteI2V + "video_gen_with_image": "doubao-seedance-1-0-lite-i2v-250428", + // ็บฏๆ–‡็”Ÿ่ง†้ข‘ๅœบๆ™ฏ๏ผšไฝฟ็”จ SeedanceLiteT2V๏ผˆๆˆๆœฌไฝŽ๏ผ‰ + "video_gen_text_only": "doubao-seedance-1-0-lite-t2v-250428", + // ้ซ˜่ดจ้‡่ง†้ข‘๏ผšไฝฟ็”จ SeedancePro + "video_gen_high_quality": "doubao-seedance-1-0-pro-250528", + // ๅฟซ้€Ÿ่ง†้ข‘๏ผšไฝฟ็”จ SeedanceProFast + "video_gen_fast": "doubao-seedance-1-0-pro-fast-251015", +}; + +export async function routeToOptimalModel( + analysis: { + intent: string; + subIntent?: string; + suggestedModel?: string; + }, + hasImage: boolean = false, + availableModels?: ModelWithProvider[] +): Promise { + const models = availableModels || await getEnabledAiModelsWithProvider(); + + // 1. ็‰นๆฎŠๅœบๆ™ฏ็ฒพ็กฎ่ทฏ็”ฑ + if (analysis.intent === "video_gen") { + if (hasImage) { + const i2vModel = models.find(m => m.model.modelId === SPECIAL_MODEL_ROUTING["video_gen_with_image"]); + if (i2vModel) { + console.log("[ModelRouter] ๐ŸŽฌ ๅ›พ็”Ÿ่ง†้ข‘ โ†’ SeedanceLiteI2V"); + return i2vModel.model.id; + } + } else { + // ๆ–‡็”Ÿ่ง†้ข‘ไฝฟ็”จ่ฝป้‡็‰ˆ๏ผˆๆˆๆœฌไฝŽ๏ผ‰ + const t2vModel = models.find(m => m.model.modelId === SPECIAL_MODEL_ROUTING["video_gen_text_only"]); + if (t2vModel) { + console.log("[ModelRouter] ๐ŸŽฌ ๆ–‡็”Ÿ่ง†้ข‘ โ†’ SeedanceLiteT2V"); + return t2vModel.model.id; + } + } + } + + // 2. ่Žทๅ–ๆ„ๅ›พๅฏนๅบ”็š„่ทฏ็”ฑ่ง„ๅˆ™ + const routingRule = INTENT_PROVIDER_MAP[analysis.intent] || INTENT_PROVIDER_MAP["chat"]; + + // 3. ๅคšๅ› ็ด ่ฏ„ๅˆ†็ฎ—ๆณ• + const scoredModels = models.map(({ model, provider }) => { + let score = 0; + + // 3.1 ๆไพ›ๅ•†็ฑปๅž‹ๅŒน้… (ๆœ€้ซ˜ 50 ๅˆ†) + const providerIndex = routingRule.providers.indexOf(provider.type); + if (providerIndex !== -1) { + score += 50 - providerIndex * 10; // ่ถŠ้ ๅ‰ๅˆ†ๆ•ฐ่ถŠ้ซ˜ + } + + // 3.2 ่ƒฝๅŠ›ๅŒน้… (ๆœ€้ซ˜ 30 ๅˆ†) + if (routingRule.requireVision && model.supportsVision) { + score += 20; + } + if (routingRule.requireImageGen && model.supportsImageGen) { + score += 20; + } + + // 3.3 ้ป˜่ฎคๆจกๅž‹ๅŠ ๅˆ† (10 ๅˆ†) + if (model.isDefault) { + score += 10; + } + + // 3.4 ็‰นๆฎŠๅŠ ๅˆ†๏ผšalibaba-image ๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ• + if (analysis.intent === "image_gen" && provider.type === "alibaba-image") { + score += 15; // ๅ› ไธบๆ”ฏๆŒ supportsPromptExtend + } + + return { model, provider, score }; + }); + + // 4. ่ฟ‡ๆปคๆމไธๆปก่ถณๅฟ…่ฆๆกไปถ็š„ๆจกๅž‹ + const filteredModels = scoredModels.filter(({ model }) => { + if (routingRule.requireVision && !model.supportsVision) return false; + if (routingRule.requireImageGen && !model.supportsImageGen) return false; + return true; + }); + + // 5. ๆŒ‰ๅˆ†ๆ•ฐๆŽ’ๅบ๏ผŒ่ฟ”ๅ›žๆœ€้ซ˜ๅˆ†ๆจกๅž‹ + const sortedModels = (filteredModels.length > 0 ? filteredModels : scoredModels) + .sort((a, b) => b.score - a.score); + + console.log("[ModelRouter] Scores:", sortedModels.slice(0, 5).map(s => + `${s.model.displayName}(${s.provider.type}): ${s.score}` + ).join(", ")); + + const selectedModel = sortedModels[0]?.model; + console.log(`[ModelRouter] โœ… Selected: ${selectedModel?.displayName} (${selectedModel?.id})`); + + return selectedModel?.id || models[0]?.model.id; +} +``` + +#### 3.1.3 ไฟฎๆ”น Chat API (`app/(chat)/api/chat/route.ts`) + +```typescript +// ๅœจ Auto Mode Logic ้ƒจๅˆ†ๆ›ฟๆขไธบ๏ผš + +import { analyzeAndOptimize } from "@/lib/ai/smart-analyzer"; +import { routeToOptimalModel } from "@/lib/ai/model-router"; + +// ========== Smart Auto Mode Logic ========== +if (finalModelId === "auto") { + console.log("[Smart Auto Mode] Starting intelligent analysis..."); + + // ่Žทๅ–็”จๆˆทๆœ€ๅŽไธ€ๆกๆถˆๆฏ + const lastUserMessage = messages.findLast((m) => m.role === "user"); + if (!lastUserMessage) { + return new ChatSDKError("bad_request:api", "No user message found").toResponse(); + } + + // ๆๅ–ๆ–‡ๆœฌๅ’Œๅ›พ็‰‡ไฟกๆฏ + const textContent = lastUserMessage.parts + .filter((p) => p.type === "text") + .map((p) => p.text) + .join(" "); + const hasImage = lastUserMessage.parts.some( + (p) => p.type === "file" || p.type === "image" || p.type === "image_url" + ); + + // 1. ่ฐƒ็”จ Qwen3 ่ฟ›่กŒๆ™บ่ƒฝๅˆ†ๆž + const analysis = await analyzeAndOptimize(textContent, hasImage); + console.log("[Smart Auto Mode] Analysis result:", analysis); + + // 2. ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑ + const allModels = await getEnabledAiModelsWithProvider(); + finalModelId = await routeToOptimalModel(analysis, allModels); + console.log(`[Smart Auto Mode] โœ… Selected model: ${finalModelId}`); + + // 3. ๅฆ‚ๆžœๆ˜ฏๅ›พ็‰‡็”Ÿๆˆๆ„ๅ›พ๏ผŒไฝฟ็”จไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + if (analysis.intent === "image_gen" && analysis.optimizedPrompt) { + console.log("[Smart Auto Mode] Using optimized prompt:", analysis.optimizedPrompt); + // ๆ›ฟๆข็”จๆˆทๆถˆๆฏไธญ็š„ๆ–‡ๆœฌไธบไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + // TODO: ๅฎž็Žฐๆ็คบ่ฏๆ›ฟๆข้€ป่พ‘ + } +} +// ========== End Smart Auto Mode Logic ========== +``` + +--- + +### Phase 2: ๆ็คบ่ฏๆ›ฟๆขไธŽๅฑ•็คบ + +**็›ฎๆ ‡๏ผš** ๅฐ†ไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏๅบ”็”จๅˆฐ็”Ÿๆˆๆต็จ‹๏ผŒๅนถๅ‘็”จๆˆทๅฑ•็คบไผ˜ๅŒ–่ฟ‡็จ‹ + +**ไฟฎๆ”น๏ผš** +1. ๅœจๆถˆๆฏๆตไธญๆ’ๅ…ฅ"ๆ็คบ่ฏไผ˜ๅŒ–"ๆ็คบ +2. ๆ˜พ็คบๅŽŸๅง‹ๆ็คบ่ฏ vs ไผ˜ๅŒ–ๅŽๆ็คบ่ฏ +3. ็”จๆˆทๅฏไปฅ้€‰ๆ‹ฉไฝฟ็”จๅŽŸๅง‹ๆˆ–ไผ˜ๅŒ–็‰ˆๆœฌ + +--- + +### Phase 3: ็”จๆˆทๅๅฅฝๅญฆไน  + +**็›ฎๆ ‡๏ผš** ๅŸบไบŽ็”จๆˆทๅ้ฆˆๅญฆไน ๅๅฅฝ๏ผŒๆๅ‡ๆปกๆ„ๅบฆ + +**ๆ–ฐๅขžๆ•ฐๆฎๅบ“่กจ๏ผš** +```sql +CREATE TABLE user_preferences ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + preference_type VARCHAR(50) NOT NULL, -- style, color, quality, etc. + preference_value TEXT NOT NULL, + weight DECIMAL(3,2) DEFAULT 1.0, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE generation_feedback ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + message_id UUID NOT NULL, + original_prompt TEXT NOT NULL, + optimized_prompt TEXT, + model_used VARCHAR(100), + feedback_type VARCHAR(20), -- positive, negative, regenerate + feedback_text TEXT, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +--- + +### Phase 4: ๅคš่ฝฎๅฏน่ฏ็ป†ๅŒ– + +**็›ฎๆ ‡๏ผš** ๆ”ฏๆŒ"ๆ›ดไบฎไธ€็‚น"ใ€"ๆขไธช้ฃŽๆ ผ"็ญ‰ๆจก็ณŠๆŒ‡ไปค + +**ๅฎž็Žฐๆ€่ทฏ๏ผš** +1. ๆฃ€ๆต‹ๆ˜ฏๅฆๆ˜ฏๅฏนไธŠไธ€ๆฌก็”Ÿๆˆ็š„ไฟฎๆ”น่ฏทๆฑ‚ +2. ่งฃๆžไฟฎๆ”นๆ„ๅ›พ๏ผˆ้ขœ่‰ฒ/ๆž„ๅ›พ/้ฃŽๆ ผ/ๅ…‰ๅฝฑ็ญ‰๏ผ‰ +3. ๅŸบไบŽไธŠไธ‹ๆ–‡็”Ÿๆˆๆ–ฐ็š„ไผ˜ๅŒ–ๆ็คบ่ฏ +4. ไฟๆŒไธปไฝ“ไธๅ˜๏ผŒๅช่ฐƒๆ•ดๆŒ‡ๅฎš็ปดๅบฆ + +--- + +### Phase 5: ไธŠไธ‹ๆ–‡่ฎฐๅฟ† + +**็›ฎๆ ‡๏ผš** ่ทจไผš่ฏไฟๆŒ้ฃŽๆ ผไธ€่‡ดๆ€ง + +**ๅฎž็Žฐๆ€่ทฏ๏ผš** +1. ๆๅ–็”จๆˆทๅธธ็”จ็š„้ฃŽๆ ผ/่‰ฒๅฝฉ/่ดจ้‡ๅๅฅฝ +2. ๅœจๆ–ฐไผš่ฏไธญ่‡ชๅŠจๅบ”็”จๅކๅฒๅๅฅฝ +3. ๆ”ฏๆŒ็”จๆˆทไธปๅŠจไฟๅญ˜"้ฃŽๆ ผ้ข„่ฎพ" + +--- + +## ๅ››ใ€API ่ฎพ่ฎก + +### 4.1 ๆ™บ่ƒฝๅˆ†ๆž API + +**Endpoint:** `POST /api/smart-analyze` + +**Request:** +```json +{ + "userInput": "ๅธฎๆˆ‘็”ปไธ€ๅชๅฏ็ˆฑ็š„็Œซๅ’ช", + "hasImage": false, + "chatHistory": [ + { "role": "user", "content": "ไฝ ๅฅฝ" }, + { "role": "assistant", "content": "ไฝ ๅฅฝ๏ผๆœ‰ไป€ไนˆๅฏไปฅๅธฎๅŠฉไฝ ็š„๏ผŸ" } + ] +} +``` + +**Response:** +```json +{ + "intent": "image_gen", + "confidence": 0.95, + "dimensions": { + "subject": "ไธ€ๅชๅฏ็ˆฑ็š„็Œซๅ’ช๏ผŒๆฏ›่Œธ่Œธ็š„ๆฉ˜่‰ฒๅฐ็Œซ", + "action": "่œท็ผฉ็€่บซไฝ“๏ผŒ็œผ็›ๅŠ็œฏ", + "style": "ๅ†™ๅฎžๆ‘„ๅฝฑ้ฃŽๆ ผ๏ผŒๆŸ”็„ฆๆ•ˆๆžœ", + "lighting": "ๆธฉๆš–็š„่‡ช็„ถๅ…‰๏ผŒไพงๅ…‰็…งๆ˜Ž", + "composition": "่ฟ‘ๆ™ฏ็‰นๅ†™๏ผŒๆต…ๆ™ฏๆทฑ", + "color": "ๆš–่‰ฒ่ฐƒ๏ผŒๆฉ˜่‰ฒๅ’Œ็ฑณ็™ฝ่‰ฒไธบไธป", + "mood": "ๆธฉ้ฆจใ€ๆ…ตๆ‡’ใ€ๆฒปๆ„ˆ", + "quality": "8K้ซ˜ๆธ…๏ผŒ็ป†่…ปๆฏ›ๅ‘่ดจๆ„Ÿ๏ผŒไธ“ไธšๅฎ ็‰ฉๆ‘„ๅฝฑ" + }, + "optimizedPrompt": "ไธ€ๅชๅฏ็ˆฑ็š„ๆฉ˜่‰ฒๅฐ็Œซๅ’ช๏ผŒๆฏ›่Œธ่Œธ็š„๏ผŒ่œท็ผฉ็€่บซไฝ“็œผ็›ๅŠ็œฏ๏ผŒๅ†™ๅฎžๆ‘„ๅฝฑ้ฃŽๆ ผ้…ๅˆๆŸ”็„ฆๆ•ˆๆžœ๏ผŒๆธฉๆš–็š„่‡ช็„ถไพงๅ…‰็…งๆ˜Ž๏ผŒ่ฟ‘ๆ™ฏ็‰นๅ†™ๆต…ๆ™ฏๆทฑๆž„ๅ›พ๏ผŒๆš–่‰ฒ่ฐƒไปฅๆฉ˜่‰ฒๅ’Œ็ฑณ็™ฝ่‰ฒไธบไธป๏ผŒ่ฅ้€ ๆธฉ้ฆจๆ…ตๆ‡’ๆฒปๆ„ˆ็š„ๆฐ›ๅ›ด๏ผŒ8K้ซ˜ๆธ…็”ป่ดจ๏ผŒ็ป†่…ปๆฏ›ๅ‘่ดจๆ„Ÿ๏ผŒไธ“ไธšๅฎ ็‰ฉๆ‘„ๅฝฑๅ“่ดจ", + "negativePrompt": "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๅคšไฝ™็š„่‚ขไฝ“", + "suggestedModel": "alibaba-image" +} +``` + +--- + +## ไบ”ใ€้…็ฝฎ้กน + +### 5.1 ็Žฏๅขƒๅ˜้‡ + +```env +# Qwen3 API ้…็ฝฎ (็”จไบŽๆ™บ่ƒฝๅˆ†ๆž) +QWEN_API_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 +QWEN_API_KEY=your-qwen-api-key + +# ๆ™บ่ƒฝๅˆ†ๆžๅŠŸ่ƒฝๅผ€ๅ…ณ +SMART_AUTO_ENABLED=true + +# ๆ็คบ่ฏไผ˜ๅŒ–ๆœ€ๅคง้•ฟๅบฆ +PROMPT_OPTIMIZATION_MAX_LENGTH=200 +``` + +### 5.2 ็ฎก็†ๅŽๅฐ้…็ฝฎ + +ๅœจ"็ฝ‘็ซ™่ฎพ็ฝฎ"ไธญๆ–ฐๅขž๏ผš + +| ้…็ฝฎ้กน | ็ฑปๅž‹ | ้ป˜่ฎคๅ€ผ | ่ฏดๆ˜Ž | +|-------|------|--------|------| +| enableSmartAuto | boolean | true | ๆ˜ฏๅฆๅฏ็”จๆ™บ่ƒฝ่‡ชๅŠจๆจกๅผ | +| showPromptOptimization | boolean | true | ๆ˜ฏๅฆๅ‘็”จๆˆทๅฑ•็คบไผ˜ๅŒ–่ฟ‡็จ‹ | +| allowUserOverride | boolean | true | ๆ˜ฏๅฆๅ…่ฎธ็”จๆˆทไฝฟ็”จๅŽŸๅง‹ๆ็คบ่ฏ | +| optimizationModel | string | qwen3-turbo | ็”จไบŽๅˆ†ๆž็š„ๆจกๅž‹ | + +--- + +## ๅ…ญใ€ๆ€ง่ƒฝไธŽๆˆๆœฌ่€ƒ่™‘ + +### 6.1 ๅปถ่ฟŸไผ˜ๅŒ– + +| ้˜ถๆฎต | ้ข„ไผฐๅปถ่ฟŸ | ไผ˜ๅŒ–็ญ–็•ฅ | +|-----|---------|---------| +| Qwen3 ๅˆ†ๆž | 500-1000ms | ไฝฟ็”จ qwen3-turbo๏ผŒๆตๅผๅ“ๅบ” | +| ๆจกๅž‹่ทฏ็”ฑ | <50ms | ๅ†…ๅญ˜็ผ“ๅญ˜ๆจกๅž‹ๅˆ—่กจ | +| ๆ็คบ่ฏไผ˜ๅŒ– | ๅทฒๅŒ…ๅซๅœจๅˆ†ๆžไธญ | - | +| ๆ€ป้ขๅค–ๅปถ่ฟŸ | ~1s | ๅฏๆŽฅๅ—่Œƒๅ›ด | + +### 6.2 ๆˆๆœฌไผฐ็ฎ— + +| ่ฐƒ็”จ็ฑปๅž‹ | ๅ•ๆฌกๆˆๆœฌ | ๆ—ฅๅ‡่ฐƒ็”จ | ๆ—ฅๆˆๆœฌ | +|---------|---------|---------|--------| +| Qwen3-turbo | ยฅ0.001/1K tokens | 1000ๆฌก | ยฅ1-2 | +| ๆ€ปๆœˆๆˆๆœฌ | - | - | ยฅ30-60 | + +--- + +## ไธƒใ€ๆต‹่ฏ•่ฎกๅˆ’ + +### 7.1 ๅ•ๅ…ƒๆต‹่ฏ• + +```typescript +// __tests__/smart-analyzer.test.ts + +describe("SmartAnalyzer", () => { + test("should detect image_gen intent", async () => { + const result = await analyzeAndOptimize("็”ปไธ€ๅช็Œซ", false); + expect(result.intent).toBe("image_gen"); + expect(result.confidence).toBeGreaterThan(0.8); + }); + + test("should detect image_edit intent with image", async () => { + const result = await analyzeAndOptimize("ๅธฎๆˆ‘ๅŽปๆމ่ƒŒๆ™ฏ", true); + expect(result.intent).toBe("image_edit"); + }); + + test("should optimize prompt with all dimensions", async () => { + const result = await analyzeAndOptimize("็”ปไธ€ๅช็Œซ", false); + expect(result.dimensions.subject).toBeTruthy(); + expect(result.optimizedPrompt.length).toBeGreaterThan(50); + }); +}); +``` + +### 7.2 ้›†ๆˆๆต‹่ฏ• + +1. ๅฎŒๆ•ดๆต็จ‹ๆต‹่ฏ•๏ผš็”จๆˆท่พ“ๅ…ฅ โ†’ ๅˆ†ๆž โ†’ ่ทฏ็”ฑ โ†’ ็”Ÿๆˆ +2. ๅ„็งๆ„ๅ›พ็ฑปๅž‹่ฆ†็›–ๆต‹่ฏ• +3. ่พน็•Œๆƒ…ๅ†ตๆต‹่ฏ•๏ผˆ็ฉบ่พ“ๅ…ฅใ€่ถ…้•ฟ่พ“ๅ…ฅใ€ไนฑ็ ็ญ‰๏ผ‰ + +--- + +## ๅ…ซใ€้‡Œ็จ‹็ข‘ + +| ้˜ถๆฎต | ๅ†…ๅฎน | ้ข„่ฎกๆ—ถ้—ด | ไบคไป˜็‰ฉ | +|-----|------|---------|--------| +| Phase 1 | ๆ ธๅฟƒๅŠŸ่ƒฝ MVP | 2-3 ๅคฉ | ๆ™บ่ƒฝๅˆ†ๆž + ๆจกๅž‹่ทฏ็”ฑ | +| Phase 2 | ๆ็คบ่ฏๅฑ•็คบ | 1 ๅคฉ | UI ไผ˜ๅŒ–ๅฑ•็คบ | +| Phase 3 | ๅๅฅฝๅญฆไน  | 2-3 ๅคฉ | ๆ•ฐๆฎๅบ“ + ๅญฆไน ็ฎ—ๆณ• | +| Phase 4 | ๅคš่ฝฎ็ป†ๅŒ– | 2 ๅคฉ | ๅฏน่ฏ็ป†ๅŒ–ๅŠŸ่ƒฝ | +| Phase 5 | ไธŠไธ‹ๆ–‡่ฎฐๅฟ† | 2 ๅคฉ | ่ทจไผš่ฏไธ€่‡ดๆ€ง | +| **ๆ€ป่ฎก** | | **9-11 ๅคฉ** | | + +--- + +## ไนใ€้ฃŽ้™ฉไธŽๅบ”ๅฏน + +| ้ฃŽ้™ฉ | ๅฝฑๅ“ | ๅบ”ๅฏนๆŽชๆ–ฝ | +|-----|------|---------| +| Qwen API ไธ็จณๅฎš | ๅŠŸ่ƒฝไธๅฏ็”จ | ้™็บงๅˆฐ่ง„ๅˆ™ๅŒน้… | +| ๅˆ†ๆž็ป“ๆžœไธๅ‡†็กฎ | ็”จๆˆทไฝ“้ชŒๅทฎ | ๅ…่ฎธ็”จๆˆท่ฆ†็›– | +| ๆ็คบ่ฏไผ˜ๅŒ–่ฟ‡ๅบฆ | ๅ็ฆป็”จๆˆทๆ„ๅ›พ | ไฟ็•™ๅŽŸๅง‹ๆ็คบ่ฏ้€‰้กน | +| ๆˆๆœฌ่ถ…้ข„็ฎ— | ่ดขๅŠกๅŽ‹ๅŠ› | ่ฎพ็ฝฎ่ฐƒ็”จ้ข‘็އ้™ๅˆถ | + +--- + +## ๅใ€ๅ‚่€ƒ่ต„ๆ–™ + +### ๆ ธๅฟƒๅ‚่€ƒ + +1. [Lovart.ai ๅฎ˜ๆ–นๆ–‡ๆกฃ](https://www.lovart.ai/features) - ไธ–็•Œ้ฆ–ไธช AI ่ฎพ่ฎกๆ™บ่ƒฝไฝ“ๅนณๅฐ +2. [LLMRouter ๅผ€ๆบๅบ“](https://github.com/ulab-uiuc/LLMRouter) - 16+ ็ง่ทฏ็”ฑ็ฎ—ๆณ•๏ผŒๆ”ฏๆŒ KNN/SVM/MLP/BERT ็ญ‰ +3. [Qwen-Agent ๅฎ˜ๆ–นๆ–‡ๆกฃ](https://qwenlm.github.io/Qwen-Agent/en/guide/) - ๅƒ้—ฎไปฃ็†ๆก†ๆžถ๏ผŒๅ†…็ฝฎ `image_gen` ๅทฅๅ…ท +4. [Maestro: Self-Improving T2I Generation](https://arxiv.org/html/2509.10704v1) - Google ่‡ช่ฟ›ๅŒ–ๅ›พๅƒ็”Ÿๆˆ็ณป็ปŸ +5. [PromptSculptor: Multi-Agent Prompt Optimization](https://arxiv.org/html/2509.12446v1) - EMNLP 2025 ๅคšไปฃ็†ๆ็คบ่ฏไผ˜ๅŒ– + +### ่ทฏ็”ฑ็ฎ—ๆณ• + +6. [Router-R1](https://ulab-uiuc.github.io/Router-R1/) - NeurIPS 2025๏ผŒๅผบๅŒ–ๅญฆไน ๅคš่ฝฎ่ทฏ็”ฑ + - ๆ”ฏๆŒ "think" + "route" ไบคๆ›ฟๅ†ณ็ญ– + - ๆˆๆœฌ-ๆ€ง่ƒฝๆƒ่กกไผ˜ๅŒ– + - ้›ถๆ ทๆœฌๆณ›ๅŒ–ๅˆฐๆ–ฐๆจกๅž‹ + +### ๆ็คบ่ฏไผ˜ๅŒ– + +7. [PromptEnhancer (่…พ่ฎฏๆททๅ…ƒ)](https://hunyuan-promptenhancer.github.io/) - Chain-of-Thought ๆ็คบ่ฏ้‡ๅ†™ + - ๅผ€ๆบไปฃ็ : [GitHub](https://github.com/Hunyuan-PromptEnhancer/PromptEnhancer) + - ๅŸบไบŽ 24 ไธชๅ…ณ้”ฎ็‚น็š„ AlignEvaluator ๅฅ–ๅŠฑๆจกๅž‹ + - ่งฃๅ†ณๅฑžๆ€ง็ป‘ๅฎšใ€ๅฆๅฎšใ€็ป„ๅˆๅ…ณ็ณป็ญ‰้—ฎ้ข˜ + +8. [Stable Diffusion 3.5 Prompt Guide](https://stability.ai/learning-hub/stable-diffusion-3-5-prompt-guide) - ๅฎ˜ๆ–นๆ็คบ่ฏๆŒ‡ๅ— + +### ๆœ€ไฝณๅฎž่ทต + +9. [ๆ็คบ่ฏๅ…ฌๅผ](https://uchaaaa.medium.com/the-most-effective-way-to-prompt-the-best-designs-on-midjourney-d6c63e9a3f06): + ``` + [Medium] + [Style/Composition] + [Subject] + [Action] + [Scene] + [Lighting] + [Color] + ``` + +10. [Qwen-Agent ๅทฅๅ…ท่ฐƒ็”จ](https://github.com/QwenLM/Qwen-Agent/blob/main/docs/tool.md) - ๅ†…็ฝฎๅทฅๅ…ทๅ’Œ่‡ชๅฎšไน‰ๅทฅๅ…ทๆณจๅ†Œ + +--- + +## ้™„ๅฝ• A: ๆ็คบ่ฏไผ˜ๅŒ–็คบไพ‹ + +### ็คบไพ‹ 1: ็ฎ€ๅ•ๆ่ฟฐ + +**็”จๆˆท่พ“ๅ…ฅ๏ผš** "็”ปไธ€ๅช็Œซ" + +**ๅˆ†ๆž็ป“ๆžœ๏ผš** +```json +{ + "intent": "image_gen", + "confidence": 0.98, + "dimensions": { + "subject": "ไธ€ๅชๅฏ็ˆฑ็š„็Œซๅ’ช", + "action": "ๅฎ‰้™ๅœฐๅ็€", + "style": "ๅ†™ๅฎžๆ‘„ๅฝฑ้ฃŽๆ ผ", + "lighting": "ๆŸ”ๅ’Œ็š„่‡ช็„ถๅ…‰", + "composition": "ไธญๆ™ฏๆž„ๅ›พ", + "color": "่‡ช็„ถ่‰ฒ่ฐƒ", + "mood": "ๆธฉ้ฆจๅนณๅ’Œ", + "quality": "้ซ˜ๆธ…๏ผŒ็ป†่Š‚ไธฐๅฏŒ" + }, + "optimizedPrompt": "ไธ€ๅชๅฏ็ˆฑ็š„็Œซๅ’ช๏ผŒๆฏ›ๅ‘ๆŸ”้กบๅ…‰ไบฎ๏ผŒๅฎ‰้™ๅœฐๅ็€ๅ‡่ง†้•œๅคด๏ผŒๅ†™ๅฎžๆ‘„ๅฝฑ้ฃŽๆ ผ๏ผŒๆŸ”ๅ’Œ็š„่‡ช็„ถๅ…‰็…งๆ˜Ž๏ผŒไธญๆ™ฏๆž„ๅ›พ็ชๅ‡บไธปไฝ“๏ผŒ่‡ช็„ถๅ’Œ่ฐ็š„่‰ฒ่ฐƒ๏ผŒ่ฅ้€ ๆธฉ้ฆจๅนณๅ’Œ็š„ๆฐ›ๅ›ด๏ผŒ้ซ˜ๆธ…็”ป่ดจ๏ผŒๆฏ›ๅ‘็ป†่Š‚ไธฐๅฏŒ๏ผŒไธ“ไธšๅฎ ็‰ฉๆ‘„ๅฝฑ" +} +``` + +### ็คบไพ‹ 2: ไบงๅ“่ฎพ่ฎก + +**็”จๆˆท่พ“ๅ…ฅ๏ผš** "่ฎพ่ฎกไธ€ไธช็ฎ€็บฆ้ฃŽๆ ผ็š„ๅฐ็ฏ" + +**ๅˆ†ๆž็ป“ๆžœ๏ผš** +```json +{ + "intent": "image_gen", + "confidence": 0.95, + "subIntent": "product_design", + "dimensions": { + "subject": "ไธ€็›็ฎ€็บฆ้ฃŽๆ ผ็š„็Žฐไปฃๅฐ็ฏ", + "action": "้™็ฝฎๅฑ•็คบ", + "style": "ๅŒ—ๆฌงๆž็ฎ€ๅทฅไธš่ฎพ่ฎก้ฃŽๆ ผ", + "lighting": "ไบงๅ“ๆ‘„ๅฝฑๆฃš็ฏๅ…‰๏ผŒๆŸ”ๅ’Œๅ‡ๅŒ€", + "composition": "45ๅบฆ่ง’ไบงๅ“ๅฑ•็คบๅ›พ", + "color": "็™ฝ่‰ฒ/ๆœจ่‰ฒ/้ป‘่‰ฒ", + "mood": "็ฎ€ๆด้ซ˜็ซฏ็Žฐไปฃ", + "quality": "ไบงๅ“็บงๆธฒๆŸ“๏ผŒๆ่ดจ็œŸๅฎž" + }, + "optimizedPrompt": "ไธ€็›็ฎ€็บฆ็Žฐไปฃ้ฃŽๆ ผ็š„ๅฐ็ฏไบงๅ“่ฎพ่ฎกๅ›พ๏ผŒๅŒ—ๆฌงๆž็ฎ€ๅทฅไธš่ฎพ่ฎก่ฏญ่จ€๏ผŒๅœ†ๆถฆๆต็•…็š„้€ ๅž‹็บฟๆก๏ผŒ็™ฝ่‰ฒ็ฏ็ฝฉๆญ้…ๅŽŸๆœจๅบ•ๅบง๏ผŒไบงๅ“ๆ‘„ๅฝฑๆฃšไธ“ไธš็ฏๅ…‰ๅธƒ็ฝฎ๏ผŒ45ๅบฆ่ง’ๅฑ•็คบไบงๅ“ๅ…จ่ฒŒ๏ผŒ็ฎ€ๆด้ซ˜็ซฏ็š„็Žฐไปฃๆฐ”่ดจ๏ผŒไบงๅ“็บง3DๆธฒๆŸ“ๅ“่ดจ๏ผŒๆ่ดจ็บน็†็œŸๅฎž็ป†่…ป๏ผŒ้€‚ๅˆ็”ตๅ•†ๅฑ•็คบ" +} +``` + +--- + +## ้™„ๅฝ• B: ไปŽๅ‚่€ƒ่ต„ๆ–™ไธญๆๅ–็š„ๅ…ณ้”ฎๆŠ€ๆœฏ + +### B.1 PromptSculptor ๅ››ไปฃ็†ๆžถๆž„ + +ๆฅๆบ: EMNLP 2025 + +PromptSculptor ๅฐ†ๆ็คบ่ฏไผ˜ๅŒ–ๅˆ†่งฃไธบ **4 ไธชไธ“ไธšๅŒ–ไปฃ็†**๏ผš + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ PromptSculptor ๆžถๆž„ โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ ็”จๆˆทๆจก็ณŠๆ็คบ โ”€โ”€โ–ถ Agent 1: ๆ„ๅ›พๆŽจๆ–ญ โ”€โ”€โ–ถ Agent 2: ๅœบๆ™ฏไธฐๅฏŒ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ”‚ +โ”‚ ๆŽจๆ–ญ้šๅซ่ฏญๅขƒ ่กฅๅ……่ƒŒๆ™ฏ็ป†่Š‚ โ”‚ +โ”‚ (Chain-of-Thought) โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ–ผ โ”‚ +โ”‚ Agent 3: ่‡ชๆˆ‘่ฏ„ไผฐ โ”€โ”€โ–ถ Agent 4: ๅ้ฆˆ่ฐƒไผ˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ”‚ +โ”‚ ไธŽๅŽŸๅง‹ๆ„ๅ›พๅฏน้ฝ ็”จๆˆทๅ้ฆˆ่ฟญไปฃ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- Chain-of-Thought ๆŽจ็†้šๅซ่ฏญๅขƒ +- ่‡ชๆˆ‘่ฏ„ไผฐ็กฎไฟไธๅ็ฆป็”จๆˆทๆ„ๅ›พ +- ๆ”ฏๆŒ็”จๆˆทๅ้ฆˆ่ฟญไปฃ + +### B.2 Maestro ่‡ช่ฟ›ๅŒ–็ณป็ปŸ + +ๆฅๆบ: Google Research + +Maestro ไฝฟ็”จ **่‡ชๆˆ‘ๆ‰น่ฏ„ + ่‡ชๆˆ‘่ฟ›ๅŒ–** ๆœบๅˆถ๏ผš + +| ็ป„ไปถ | ๅŠŸ่ƒฝ | ๅฎž็Žฐๆ–นๅผ | +|-----|------|---------| +| Critic Agents | ่ฏ†ๅˆซๅ›พๅƒๅผฑ็‚น | MLLM ไธ“ไธšๅŒ–่ฏ„ไผฐ | +| Verifier Agent | ๆ•ดๅˆ็ผ–่พ‘ไฟกๅท | ไฟๆŒ็”จๆˆทๆ„ๅ›พ | +| MLLM-as-a-Judge | ๅคดๅฏนๅคดๆฏ”่พƒ | ้€‰ๆ‹ฉๆ›ดๅฅฝ็š„็‰ˆๆœฌ | +| Prompt Evolution | ่ฟญไปฃไผ˜ๅŒ–ๆ็คบ่ฏ | ไธขๅผƒ้—ฎ้ข˜่พ“ๅ‡บ | + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- ๅคšไธช MLLM Agent ๅไฝœ่ฏ„ไผฐ +- ็”Ÿๆˆๅคšไธช็‰ˆๆœฌ่ฟ›่กŒๆฏ”่พƒ +- ไฟๆŒ็”จๆˆทๆ„ๅ›พ็š„้ชŒ่ฏๆœบๅˆถ + +### B.3 Router-R1 ๅผบๅŒ–ๅญฆไน ่ทฏ็”ฑ + +ๆฅๆบ: NeurIPS 2025 + +Router-R1 ๅˆ›ๆ–ฐ็‚น๏ผš + +1. **้กบๅบๅ†ณ็ญ–่ฟ‡็จ‹**: ่ทฏ็”ฑไธๆ˜ฏไธ€ๆฌกๆ€ง็š„๏ผŒ่€Œๆ˜ฏๅคš่ฝฎไบคไบ’ +2. **Think + Route ไบคๆ›ฟ**: + - `think`: ๅ†…้ƒจๆŽจ็†๏ผŒๅˆ†ๆžไปปๅŠก + - `route`: ้€‰ๆ‹ฉๆจกๅž‹๏ผŒ่Žทๅ–ๅ“ๅบ” +3. **ๆˆๆœฌๅฅ–ๅŠฑ**: ๅœจ RL ่ฎญ็ปƒไธญๅŠ ๅ…ฅๆˆๆœฌๆƒฉ็ฝš๏ผŒๅนณ่กกๆ€ง่ƒฝๅ’Œ่Šฑ่ดน +4. **้›ถๆ ทๆœฌๆณ›ๅŒ–**: ไป…้œ€ๆจกๅž‹ๆ่ฟฐ๏ผˆๅฎšไปทใ€ๅปถ่ฟŸใ€็คบไพ‹ๆ€ง่ƒฝ๏ผ‰ๅณๅฏ้›†ๆˆๆ–ฐๆจกๅž‹ + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- ๅฏไปฅๅœจๆˆ‘ไปฌ็š„็ณป็ปŸไธญๅŠ ๅ…ฅๆˆๆœฌๆ„Ÿ็Ÿฅ่ทฏ็”ฑ +- ๆ”ฏๆŒๅŠจๆ€ๆทปๅŠ ๆ–ฐๆจกๅž‹่€Œๆ— ้œ€้‡ๆ–ฐ่ฎญ็ปƒ + +### B.4 PromptEnhancer 24 ๅ…ณ้”ฎ็‚น + +ๆฅๆบ: ่…พ่ฎฏๆททๅ…ƒ + +PromptEnhancer ๅŸบไบŽ **24 ไธชๅธธ่ง T2I ๅคฑ่ดฅๆจกๅผ** ่ฎญ็ปƒๅฅ–ๅŠฑๆจกๅž‹๏ผš + +| ็ฑปๅˆซ | ๅ…ณ้”ฎ็‚น็คบไพ‹ | +|-----|----------| +| ๅฑžๆ€ง็ป‘ๅฎš | ้ขœ่‰ฒ/ๆ่ดจๆ˜ฏๅฆๆญฃ็กฎ้™„ๅŠ ๅˆฐๅฏน่ฑก | +| ๅฏน่ฑก่ฎกๆ•ฐ | ๆ•ฐ้‡ๆ˜ฏๅฆๅ‡†็กฎ | +| ็ฉบ้—ดๅ…ณ็ณป | ๅทฆๅณ/ไธŠไธ‹/ๅ‰ๅŽไฝ็ฝฎๆ˜ฏๅฆๆญฃ็กฎ | +| ๅฆๅฎšๅค„็† | "ๆฒกๆœ‰XX" ๆ˜ฏๅฆ่ขซๆญฃ็กฎ็†่งฃ | +| ้ฃŽๆ ผไธ€่‡ดๆ€ง | ๆ•ดไฝ“้ฃŽๆ ผๆ˜ฏๅฆ็ปŸไธ€ | +| ็ป†่Š‚ๅฎŒๆ•ดๆ€ง | ้‡่ฆๅ…ƒ็ด ๆ˜ฏๅฆ็ผบๅคฑ | + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- ๅฏไปฅ็”จ่ฟ™ไบ›ๅ…ณ้”ฎ็‚นๆฅ่ฏ„ไผฐ็”Ÿๆˆ่ดจ้‡ +- ๅ้ฆˆๅพช็Žฏไธญไฝฟ็”จ่ฟ™ไบ›ๆ ‡ๅ‡† + +### B.5 Qwen-Agent ๅ†…็ฝฎๅทฅๅ…ท + +ๆฅๆบ: Qwen-Agent ๅฎ˜ๆ–นๆ–‡ๆกฃ + +Qwen-Agent ๅทฒๅ†…็ฝฎไปฅไธ‹ๅทฅๅ…ท๏ผš + +| ๅทฅๅ…ทๅ | ๅŠŸ่ƒฝ | +|-------|------| +| `image_gen` | ๅ›พ็‰‡็”Ÿๆˆ | +| `code_interpreter` | ๆ‰ง่กŒ Python ไปฃ็  | +| `web_search` | ็ฝ‘้กตๆœ็ดข | +| `web_extractor` | ็ฝ‘้กตๅ†…ๅฎนๆๅ– | +| `image_search` | ๅ›พ็‰‡ๆœ็ดข | +| `image_zoom_in_tool` | ๅ›พ็‰‡ๅฑ€้ƒจๆ”พๅคง | + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- ๅฏไปฅ็›ดๆŽฅไฝฟ็”จ Qwen-Agent ๆก†ๆžถๅฎž็Žฐๅทฅๅ…ท่ฐƒ็”จ +- ๆ”ฏๆŒ MCP (Model Context Protocol) ้›†ๆˆๅค–้ƒจๅทฅๅ…ท + +### B.6 LLMRouter 16+ ่ทฏ็”ฑ็ฎ—ๆณ• + +ๆฅๆบ: LLMRouter ๅผ€ๆบๅบ“ + +ๅฏ้€‰็š„่ทฏ็”ฑ็ฎ—ๆณ•๏ผš + +| ็ฑปๅž‹ | ็ฎ—ๆณ• | ็‰น็‚น | +|-----|------|------| +| ๅŸบ็ก€ | KNN, SVM, MLP | ๅฟซ้€Ÿ๏ผŒ้€‚ๅˆ็ฎ€ๅ•ๅœบๆ™ฏ | +| ่ฟ›้˜ถ | Matrix Factorization, Elo Rating | ่€ƒ่™‘ๆจกๅž‹่ƒฝๅŠ›ๅทฎๅผ‚ | +| ๆทฑๅบฆ | BERT-based, CausalLM | ็†่งฃ่ฏญไน‰๏ผŒๆ›ดๅ‡†็กฎ | +| ๅคš่ฝฎ | Router-R1, KNN Multi-Round | ๆ”ฏๆŒๅคๆ‚ไปปๅŠก | +| ไธชๆ€งๅŒ– | GMT Router | ๅญฆไน ็”จๆˆทๅๅฅฝ | + +**ๅฏๅ€Ÿ้‰ด็‚น๏ผš** +- ๅˆๆœŸๅฏไปฅไฝฟ็”จ KNN/MLP ๅฟซ้€Ÿๅฎž็Žฐ +- ๅŽๆœŸๅฏไปฅๅ‡็บงๅˆฐ BERT-based ๆˆ–ไธชๆ€งๅŒ–่ทฏ็”ฑ + +--- + +## ้™„ๅฝ• C: ๅ›พ็‰‡็ผ–่พ‘ๆ็คบ่ฏๆจกๆฟๅบ“๏ผˆๆ ธๅฟƒ๏ผ‰ + +### C.1 ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏๆจกๆฟ + +``` +# ๅŸบ็ก€ๆŠ ๅ›พ +ๅŽป้™คๅ›พ็‰‡่ƒŒๆ™ฏ๏ผŒไฟ็•™ไธปไฝ“{subject}๏ผŒ +่พ“ๅ‡บ้€ๆ˜Ž่ƒŒๆ™ฏPNGๆ ผๅผ๏ผŒ +่พน็ผ˜ๅค„็†๏ผšๅนณๆป‘่‡ช็„ถ๏ผŒไฟ็•™็ป†่Š‚๏ผˆๅฆ‚ๅ‘ไธใ€ๆฏ›ๅ‘๏ผ‰ + +# ไบงๅ“ๆŠ ๅ›พ +็ฒพๅ‡†ๆŠ ๅ‡บไบงๅ“ไธปไฝ“๏ผŒ +ไฟ็•™ไบงๅ“้˜ดๅฝฑๅ’Œๅๅ…‰ๆ•ˆๆžœ๏ผŒ +่พน็ผ˜ๆธ…ๆ™ฐ้”ๅˆฉ๏ผŒๆ— ๆฏ›่พน + +# ไบบ็‰ฉๆŠ ๅ›พ +ไฟ็•™ไบบ็‰ฉๅฎŒๆ•ด่ฝฎๅป“๏ผŒ +็‰นๅˆซๆณจๆ„ๅ‘ไธๅ’Œ่กฃๆœ่พน็ผ˜็š„็ป†่Š‚ๅค„็†๏ผŒ +็šฎ่‚ค่พน็ผ˜่‡ช็„ถ่ฟ‡ๆธก +``` + +### C.2 ๆข่ƒŒๆ™ฏๆจกๆฟ + +``` +# ็บฏ่‰ฒ่ƒŒๆ™ฏ +ๅฐ†่ƒŒๆ™ฏๆ›ฟๆขไธบ{color}็บฏ่‰ฒ่ƒŒๆ™ฏ๏ผŒ +ไฟๆŒไธปไฝ“้˜ดๅฝฑ่‡ช็„ถ๏ผŒ +ๅ…‰ๅฝฑไธŽไธปไฝ“ๅ่ฐƒไธ€่‡ด + +# ๅœบๆ™ฏ่ƒŒๆ™ฏ +ๅฐ†่ƒŒๆ™ฏๆ›ฟๆขไธบ{scene}ๅœบๆ™ฏ๏ผŒ +ๅœบๆ™ฏ้ฃŽๆ ผ๏ผš{style}๏ผŒ +ๅ…‰็…งๆ–นๅ‘ไธŽไธปไฝ“ไธ€่‡ด๏ผŒ +้€่ง†ๅ…ณ็ณป่‡ช็„ถๅˆ็† + +# ๆธๅ˜่ƒŒๆ™ฏ +่ƒŒๆ™ฏ่ฎพไธบไปŽ{color1}ๅˆฐ{color2}็š„ๆธๅ˜๏ผŒ +ๆธๅ˜ๆ–นๅ‘๏ผš{direction}๏ผŒ +ไธปไฝ“ไธŽ่ƒŒๆ™ฏ่žๅˆ่‡ช็„ถ +``` + +### C.3 ๅ›พ็‰‡ๅขžๅผบๆจกๆฟ + +``` +# ้€š็”จๅขžๅผบ +ๆๅ‡ๅ›พ็‰‡ๆ•ดไฝ“ๆธ…ๆ™ฐๅบฆๅ’Œ่ดจ้‡๏ผŒ +ไฟๆŒ่‡ช็„ถ่‰ฒๅฝฉ๏ผŒไธ่ฟ‡ๅบฆ้”ๅŒ–๏ผŒ +ๅŽป้™คๅ™ช็‚น๏ผŒไผ˜ๅŒ–็ป†่Š‚ + +# ไบงๅ“ๅ›พๅขžๅผบ +ๅขžๅผบไบงๅ“็ป†่Š‚ๅ’Œๆ่ดจ่กจ็Žฐ๏ผŒ +ไผ˜ๅŒ–ๅ…‰ๅฝฑๆ•ˆๆžœ๏ผŒ +ๆๅ‡่ดจๆ„Ÿๅ’Œไธ“ไธšๅบฆ + +# ไบบๅƒๅขžๅผบ +ไผ˜ๅŒ–้ข้ƒจ็ป†่Š‚๏ผŒไฟๆŒ่‡ช็„ถ่‚ค่‰ฒ๏ผŒ +ๅขžๅผบ็œผ็ฅžๅ…‰๏ผŒ +้€‚ๅบฆ็พŽๅŒ–ไฝ†ไธๅคฑ็œŸ +``` + +### C.4 ๅ›พๅƒๆ‹“ๅฑ• (Outpainting) ๆจกๆฟ + +``` +# ๆ–นๅ‘ๆ‰ฉๅฑ• +ๅ‘{direction}ๆ–นๅ‘ๆ‰ฉๅฑ•ๅ›พ็‰‡{percentage}%๏ผŒ +ไฟๆŒๅŽŸๆœ‰้ฃŽๆ ผๅ’Œ่‰ฒ่ฐƒไธ€่‡ด๏ผŒ +ๅ…‰ๅฝฑ่ฟ‡ๆธก่‡ช็„ถ๏ผŒๆ— ๆ˜Žๆ˜พๆŽฅ็ผ + +# ๅ…จๆ–นๅ‘ๆ‰ฉๅฑ• +็ญ‰ๆฏ”ไพ‹ๆ‰ฉๅฑ•็”ปๅธƒ่‡ณ{ratio}ๅฐบๅฏธ๏ผŒ +ๆ‰ฉๅฑ•ๅŒบๅŸŸๅ†…ๅฎนไธŽๅŽŸๅ›พ้ฃŽๆ ผๅ่ฐƒ๏ผŒ +ไฟๆŒ้€่ง†ๅ’Œๆ™ฏๆทฑไธ€่‡ดๆ€ง +``` + +### C.5 ๅฑ€้ƒจไฟฎๆ”น (Inpainting) ๆจกๆฟ + +``` +# ๅŽป้™คๅฏน่ฑก +ๅŽป้™คๅ›พ็‰‡ไธญ็š„{object}๏ผŒ +็”จๅ‘จๅ›ด็Žฏๅขƒ่‡ช็„ถๅกซๅ……๏ผŒ +ไฟๆŒๆ•ดไฝ“้ฃŽๆ ผไธ€่‡ด + +# ๆ›ฟๆขๅฏน่ฑก +ๅฐ†ๅ›พ็‰‡ไธญ็š„{old_object}ๆ›ฟๆขไธบ{new_object}๏ผŒ +ๆ–ฐๅฏน่ฑก้ฃŽๆ ผไธŽ็”ป้ขๅ่ฐƒ๏ผŒ +ๅ…‰ๅฝฑๅ’Œ้€่ง†ๅ…ณ็ณปๆญฃ็กฎ + +# ไฟฎๅค็‘•็–ต +ไฟฎๅคๅ›พ็‰‡ไธญ็š„{defect}๏ผˆๅฆ‚ๆฐดๅฐ/ๆฑก็‚น/็ ดๆŸ๏ผ‰๏ผŒ +่‡ช็„ถๅกซๅ……๏ผŒ็œ‹ไธๅ‡บไฟฎๅค็—•่ฟน +``` + +### C.6 ้ฃŽๆ ผ่ฟ็งปๆจกๆฟ + +``` +# ่‰บๆœฏ้ฃŽๆ ผ +ๅฐ†็…ง็‰‡่ฝฌๆขไธบ{style}่‰บๆœฏ้ฃŽๆ ผ๏ผŒ +ไฟๆŒไธปไฝ“ๅฏ่ฏ†ๅˆซๆ€ง๏ผŒ +่‰ฒๅฝฉ้ฃŽๆ ผ๏ผš{color_style} + +# ๅŠจๆผซ/ๅก้€š้ฃŽๆ ผ +่ฝฌๆขไธบ{anime_style}ๅŠจๆผซ้ฃŽๆ ผ๏ผŒ +ไฟ็•™ไบบ็‰ฉ็‰นๅพๅ’Œ่กจๆƒ…๏ผŒ +็บฟๆกๆธ…ๆ™ฐ๏ผŒ่‰ฒๅฝฉ้ฒœๆ˜Ž +``` + +### C.7 ๅ›พ็‰‡็”Ÿๆˆๆจกๆฟ๏ผˆ่พ…ๅŠฉ๏ผ‰ + +``` +# ้€š็”จ็”Ÿๆˆ +[ไธปไฝ“] {subject}๏ผŒ[ๅŠจไฝœ] {action}๏ผŒ +[้ฃŽๆ ผ] {style}้ฃŽๆ ผ๏ผŒ +[ๅ…‰ๅฝฑ] {lighting}๏ผŒ +[ๆž„ๅ›พ] {composition}๏ผŒ +[่‰ฒๅฝฉ] {color}่‰ฒ่ฐƒ๏ผŒ +[ๆฐ›ๅ›ด] {mood}ๆฐ›ๅ›ด๏ผŒ +[่ดจ้‡] ้ซ˜ๆธ…๏ผŒ็ป†่Š‚ไธฐๅฏŒ + +# ไบงๅ“ๅ›พ็”Ÿๆˆ +ไบงๅ“๏ผš{product_name}๏ผŒ +ๅฑ•็คบ่ง’ๅบฆ๏ผš{angle}๏ผŒ +่ƒŒๆ™ฏ๏ผš{background}๏ผŒ +็ฏๅ…‰๏ผšไบงๅ“ๆ‘„ๅฝฑๆฃšไธ“ไธšๅธƒๅ…‰๏ผŒ +่ดจ้‡๏ผšไบงๅ“็บงๆธฒๆŸ“๏ผŒๆ่ดจ็œŸๅฎž +``` + +### C.8 ่ดŸ้ขๆ็คบ่ฏๆจกๆฟ + +``` +# ้€š็”จ่ดŸ้ข +ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ๏ผŒๅ™ช็‚น๏ผŒ +่ฟ‡ๆ›๏ผŒๆฌ ๆ›๏ผŒไธ่‡ช็„ถ็š„้ขœ่‰ฒ๏ผŒๆฏ”ไพ‹ๅคฑ่ฐƒ + +# ไบบ็‰ฉ่ดŸ้ข +ๅคšไฝ™่‚ขไฝ“๏ผŒๅ˜ๅฝข็š„่„ธ๏ผŒไธๅฏน็งฐ๏ผŒ +ไธ่‡ช็„ถ็š„่กจๆƒ…๏ผŒๅƒต็กฌ็š„ๅงฟๅŠฟ + +# ไบงๅ“่ดŸ้ข +ๆ่ดจๅคฑ็œŸ๏ผŒๅ…‰ๅฝฑ้”™่ฏฏ๏ผŒๆฏ”ไพ‹้”™่ฏฏ๏ผŒ +่ƒŒๆ™ฏๆ‚ไนฑ๏ผŒ็ป†่Š‚ไธขๅคฑ +``` + +--- + +*ๆ–‡ๆกฃ็ป“ๆŸ* diff --git a/lib/ai/model-router.ts b/lib/ai/model-router.ts new file mode 100644 index 000000000..4ccd8625a --- /dev/null +++ b/lib/ai/model-router.ts @@ -0,0 +1,231 @@ +/** + * ๆ™บ่ƒฝๆจกๅž‹่ทฏ็”ฑๅ™จ + * ๆ นๆฎ็”จๆˆทๆ„ๅ›พ้€‰ๆ‹ฉๆœ€ไฝณAIๆจกๅž‹ + * + * ๆ ธๅฟƒ้€ป่พ‘๏ผšๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏไผ˜ๅ…ˆไฝฟ็”จ alibaba-image (็ต้ญ‚4.5) + */ + +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +// ่ทฏ็”ฑ็ป“ๆžœ็ฑปๅž‹ +export interface RouteResult { + id: string; + displayName: string; + providerType: string; + modelId: string; + reason: string; +} + +// ๆ„ๅ›พๅˆฐๆจกๅž‹็š„ๆ˜ ๅฐ„้…็ฝฎ +// providerTypes ๆŒ‰ไผ˜ๅ…ˆ็บงๆŽ’ๅบ๏ผŒ็ฌฌไธ€ไธชๆ˜ฏ้ฆ–้€‰ +const INTENT_MODEL_MAP: Record< + string, + { + providerTypes: string[]; + requireVision?: boolean; + requireImageGen?: boolean; + description: string; + } +> = { + // ๅ›พ็‰‡็ผ–่พ‘๏ผšไผ˜ๅ…ˆ alibaba-image๏ผˆ็ต้ญ‚4.5๏ผŒๆ”ฏๆŒๆ็คบ่ฏๆ‰ฉๅฑ•๏ผ‰ + image_edit: { + providerTypes: ["alibaba-image", "gemini"], + requireVision: true, + requireImageGen: true, + description: "ๅ›พ็‰‡็ผ–่พ‘", + }, + + // ่ง†่ง‰็†่งฃ๏ผšไผ˜ๅ…ˆๅƒ้—ฎ3 VL๏ผˆๆˆๆœฌไฝŽๆ•ˆๆžœๅฅฝ๏ผ‰ + vision: { + providerTypes: ["alibaba", "gemini"], + requireVision: true, + description: "ๅ›พ็‰‡็†่งฃ", + }, + + // ๅ›พ็‰‡็”Ÿๆˆ๏ผšไผ˜ๅ…ˆ็ต้ญ‚4.5 + image_gen: { + providerTypes: ["alibaba-image", "gemini", "volcano-vision"], + requireImageGen: true, + description: "ๅ›พ็‰‡็”Ÿๆˆ", + }, + + // ่ง†้ข‘็”Ÿๆˆ + video_gen: { + providerTypes: ["volcano-video", "qianwen-video"], + description: "่ง†้ข‘็”Ÿๆˆ", + }, + + // ๆ™ฎ้€šๅฏน่ฏ๏ผšไผ˜ๅ…ˆๅƒ้—ฎ3 + chat: { + providerTypes: ["alibaba", "openai", "local"], + description: "ๅฏน่ฏ", + }, +}; + +// ๅญๆ„ๅ›พ็š„็‰นๆฎŠ่ทฏ็”ฑ้…็ฝฎ๏ผˆ่ฆ†็›–้ป˜่ฎค้…็ฝฎ๏ผ‰ +const SUB_INTENT_OVERRIDES: Record< + string, + { + providerTypes?: string[]; + reason?: string; + } +> = { + // ๆŠ ๅ›พ๏ผšalibaba-image ๆ•ˆๆžœๆœ€ๅฅฝ + remove_background: { + providerTypes: ["alibaba-image", "gemini"], + reason: "ๆŠ ๅ›พ้ฆ–้€‰็ต้ญ‚4.5", + }, + + // ๆข่ƒŒๆ™ฏ๏ผšalibaba-image ๆ”ฏๆŒๅœบๆ™ฏ็”Ÿๆˆ + replace_background: { + providerTypes: ["alibaba-image", "gemini"], + reason: "ๆข่ƒŒๆ™ฏ้ฆ–้€‰็ต้ญ‚4.5", + }, + + // ๅ›พ็‰‡ๅขžๅผบ + enhance: { + providerTypes: ["alibaba-image", "gemini", "volcano-vision"], + reason: "ๅขžๅผบ้ฆ–้€‰็ต้ญ‚4.5", + }, + + // ้ฃŽๆ ผ่ฟ็งป๏ผšgemini ๅฏ่ƒฝๆ›ดๆœ‰ๅˆ›ๆ„ + style_transfer: { + providerTypes: ["gemini", "alibaba-image"], + reason: "้ฃŽๆ ผ่ฟ็งปไผ˜้€‰Gemini", + }, +}; + +/** + * ่ทฏ็”ฑๅˆฐๆœ€ไฝณๆจกๅž‹ + */ +export async function routeToModel(params: { + intent: string; + subIntent?: string; + hasImage: boolean; +}): Promise { + const { intent, subIntent, hasImage } = params; + + console.log("[Model Router] Routing:", { intent, subIntent, hasImage }); + + // ่Žทๅ–ๆ‰€ๆœ‰ๅฏ็”จๆจกๅž‹ + const allModels = await getEnabledAiModelsWithProvider(); + console.log("[Model Router] Available models:", allModels.length); + + // ่Žทๅ–ๆ„ๅ›พๅฏนๅบ”็š„้…็ฝฎ + let config = INTENT_MODEL_MAP[intent] || INTENT_MODEL_MAP["chat"]; + + // ๆฃ€ๆŸฅๆ˜ฏๅฆๆœ‰ๅญๆ„ๅ›พ็š„็‰นๆฎŠ้…็ฝฎ + if (subIntent && SUB_INTENT_OVERRIDES[subIntent]) { + const override = SUB_INTENT_OVERRIDES[subIntent]; + if (override.providerTypes) { + config = { ...config, providerTypes: override.providerTypes }; + } + } + + // ๆŒ‰ไผ˜ๅ…ˆ็บงๆŸฅๆ‰พๅŒน้…็š„ๆจกๅž‹ + for (const providerType of config.providerTypes) { + const candidates = allModels.filter((m) => { + // ๅŒน้… provider ็ฑปๅž‹ + if (m.provider.type !== providerType) return false; + + // ๆฃ€ๆŸฅ่ƒฝๅŠ›่ฆๆฑ‚ + if (config.requireVision && !m.model.supportsVision) return false; + if (config.requireImageGen && !m.model.supportsImageGen) return false; + + // ๆŽ’้™คๅญๆจกๅž‹๏ผˆๅช้€‰ไธปๆจกๅž‹๏ผ‰ + if (m.model.isSubModel) return false; + + // ๅฟ…้กปๅฏ็”จ + if (!m.model.isEnabled) return false; + + return true; + }); + + if (candidates.length > 0) { + // ไผ˜ๅ…ˆ้€‰ๆ‹ฉ้ป˜่ฎคๆจกๅž‹ + const defaultModel = candidates.find((c) => c.model.isDefault); + const selected = defaultModel || candidates[0]; + + const result: RouteResult = { + id: selected.model.id, + displayName: selected.model.displayName, + providerType: selected.provider.type, + modelId: selected.model.modelId, + reason: subIntent + ? `${config.description}/${subIntent} โ†’ ${selected.model.displayName}` + : `${config.description} โ†’ ${selected.model.displayName}`, + }; + + console.log("[Model Router] โœ… Selected:", result); + return result; + } + } + + // ๅ…œๅบ•้€ป่พ‘๏ผš่ฟ”ๅ›žไปปๆ„ๅฏ็”จ็š„ๅ›พ็‰‡็ผ–่พ‘ๆจกๅž‹ + const fallbackCandidates = allModels.filter( + (m) => + !m.model.isSubModel && + m.model.isEnabled && + (m.model.supportsImageGen || m.model.supportsVision) + ); + + if (fallbackCandidates.length > 0) { + const fallback = fallbackCandidates[0]; + console.log("[Model Router] Using fallback (with image capability):", fallback.model.displayName); + return { + id: fallback.model.id, + displayName: fallback.model.displayName, + providerType: fallback.provider.type, + modelId: fallback.model.modelId, + reason: `fallback (${config.description})`, + }; + } + + // ๆœ€็ปˆๅ…œๅบ•๏ผš่ฟ”ๅ›žไปปๆ„ๅฏ็”จๆจกๅž‹ + const anyModel = allModels.find((m) => !m.model.isSubModel && m.model.isEnabled); + if (anyModel) { + console.log("[Model Router] Using any available model:", anyModel.model.displayName); + return { + id: anyModel.model.id, + displayName: anyModel.model.displayName, + providerType: anyModel.provider.type, + modelId: anyModel.model.modelId, + reason: "fallback (any)", + }; + } + + // ๅฆ‚ๆžœๆฒกๆœ‰ไปปไฝ•ๆจกๅž‹๏ผŒๆŠ›ๅ‡บ้”™่ฏฏ + throw new Error("No available models for routing"); +} + +/** + * ่Žทๅ–ๆ„ๅ›พ็š„ๆŽจ่ๆจกๅž‹ๅˆ—่กจ๏ผˆ็”จไบŽ UI ๅฑ•็คบ๏ผ‰ + */ +export function getRecommendedModelsForIntent(intent: string): string[] { + const config = INTENT_MODEL_MAP[intent]; + return config ? config.providerTypes : INTENT_MODEL_MAP["chat"].providerTypes; +} + +/** + * ๆฃ€ๆŸฅๆจกๅž‹ๆ˜ฏๅฆ้€‚ๅˆๆŒ‡ๅฎšๆ„ๅ›พ + */ +export function isModelSuitableForIntent( + providerType: string, + intent: string, + supportsVision: boolean, + supportsImageGen: boolean +): boolean { + const config = INTENT_MODEL_MAP[intent]; + if (!config) return true; // ๆœช็Ÿฅๆ„ๅ›พ๏ผŒๅ…่ฎธไปปไฝ•ๆจกๅž‹ + + // ๆฃ€ๆŸฅ provider ็ฑปๅž‹ๆ˜ฏๅฆๅŒน้… + if (!config.providerTypes.includes(providerType)) { + return false; + } + + // ๆฃ€ๆŸฅ่ƒฝๅŠ›่ฆๆฑ‚ + if (config.requireVision && !supportsVision) return false; + if (config.requireImageGen && !supportsImageGen) return false; + + return true; +} diff --git a/lib/ai/prompt-optimizer.ts b/lib/ai/prompt-optimizer.ts new file mode 100644 index 000000000..abe837014 --- /dev/null +++ b/lib/ai/prompt-optimizer.ts @@ -0,0 +1,231 @@ +/** + * ๆ็คบ่ฏไผ˜ๅŒ–ๅ™จ + * ๅฐ†็”จๆˆท็ฎ€ๅ•่พ“ๅ…ฅ่ฝฌๆขไธบไธ“ไธš็š„ๅ›พ็‰‡็ผ–่พ‘ๆ็คบ่ฏ + * + * ไธป่ฆๅบ”็”จๅœบๆ™ฏ๏ผšๅ›พ็‰‡็ผ–่พ‘๏ผˆๆŠ ๅ›พใ€ๆข่ƒŒๆ™ฏใ€ๅขžๅผบ็ญ‰๏ผ‰ + */ + +// ไผ˜ๅŒ–็ป“ๆžœ็ฑปๅž‹ +export interface OptimizeResult { + originalPrompt: string; + optimizedPrompt: string; + negativePrompt: string; +} + +// ๅ›พ็‰‡็ผ–่พ‘ๅญๆ„ๅ›พ็š„ๆ็คบ่ฏๆจกๆฟ +const EDIT_TEMPLATES: Record< + string, + { + template: string; + negative: string; + extractors: Record; + defaults: Record; + } +> = { + remove_background: { + template: + "็ฒพๅ‡†ๆŠ ๅ›พ๏ผšๅŽป้™คๅ›พ็‰‡่ƒŒๆ™ฏ๏ผŒไฟ็•™ไธปไฝ“ๅฎŒๆ•ดๆธ…ๆ™ฐ๏ผŒ่พ“ๅ‡บ้€ๆ˜Ž่ƒŒๆ™ฏPNGๆ ผๅผใ€‚่พน็ผ˜ๅค„็†่ฆๆฑ‚๏ผšๅนณๆป‘่‡ช็„ถๆ— ้”ฏ้ฝฟ๏ผŒไฟ็•™็ฒพ็ป†็ป†่Š‚๏ผˆๅฆ‚ๅ‘ไธใ€ๆฏ›ๅ‘ใ€่พน็ผ˜็บน็†๏ผ‰ใ€‚ไธปไฝ“ไฟๆŒๅŽŸๆœ‰ๆธ…ๆ™ฐๅบฆๅ’Œ่‰ฒๅฝฉใ€‚", + negative: "ๆฎ‹็•™่ƒŒๆ™ฏ่‰ฒๅ—๏ผŒ่พน็ผ˜ๆฏ›็ณ™๏ผŒ้”ฏ้ฝฟๆ˜Žๆ˜พ๏ผŒไธปไฝ“ไธๅฎŒๆ•ด๏ผŒ่พน็ผ˜ๆจก็ณŠ๏ผŒ็ป†่Š‚ไธขๅคฑ๏ผŒ้ขœ่‰ฒๆบขๅ‡บ", + extractors: {}, + defaults: {}, + }, + + replace_background: { + template: + "่ƒŒๆ™ฏๆ›ฟๆข๏ผšๅฐ†ๅ›พ็‰‡่ƒŒๆ™ฏๆ›ฟๆขไธบ{background}ใ€‚่ฆๆฑ‚๏ผšไฟๆŒไธปไฝ“ๅฎŒๆ•ดๆธ…ๆ™ฐไธๅ˜ๅฝข๏ผŒ่พน็ผ˜่ฟ‡ๆธก่‡ช็„ถๅนณๆป‘๏ผŒๅ…‰ๅฝฑๆ•ˆๆžœไธŽๆ–ฐ่ƒŒๆ™ฏๅ่ฐƒไธ€่‡ดใ€‚{extra}", + negative: "ๆฎ‹็•™ๅŽŸ่ƒŒๆ™ฏ๏ผŒ่พน็ผ˜ไธ่‡ช็„ถ๏ผŒ้€่ง†้”™่ฏฏ๏ผŒไธปไฝ“ๅ˜ๅฝข๏ผŒๅ…‰ๅฝฑไธๅ่ฐƒ๏ผŒๆ˜Žๆ˜พPๅ›พ็—•่ฟน๏ผŒ่พน็ผ˜ๅ…‰ๆ™•", + extractors: { + background: /(?:ๆขๆˆ|ๆ”นๆˆ|ๆ›ฟๆขไธบ|ๅ˜ๆˆ|ๆขไธบ|ๆ”นไธบ)(.+?)(?:่ƒŒๆ™ฏ|ๅบ•|$)/, + }, + defaults: { + background: "็บฏ็™ฝ่‰ฒ(#FFFFFF)", + extra: "ไฟ็•™ไธปไฝ“่‡ช็„ถ้˜ดๅฝฑๆ•ˆๆžœ", + }, + }, + + enhance: { + template: + "ๅ›พ็‰‡ๅขžๅผบ๏ผšๆๅ‡ๅ›พ็‰‡ๆ•ดไฝ“ๆธ…ๆ™ฐๅบฆๅ’Œ็”ป่ดจใ€‚่ฆๆฑ‚๏ผšไฟๆŒ่‡ช็„ถ่‰ฒๅฝฉไธๅคฑ็œŸ๏ผŒๆ™บ่ƒฝๅŽป้™คๅ™ช็‚น๏ผŒๅขžๅผบ็ป†่Š‚ๅฑ‚ๆฌก๏ผŒ้€‚ๅบฆ้”ๅŒ–่พน็ผ˜ใ€‚{extra}", + negative: "่ฟ‡ๅบฆ้”ๅŒ–๏ผŒ่‰ฒๅฝฉๅคฑ็œŸ๏ผŒๅ™ช็‚นๅขžๅŠ ๏ผŒไผชๅฝฑ๏ผŒ็”ป่ดจไธ‹้™๏ผŒไธ่‡ช็„ถ็š„ๅฏนๆฏ”ๅบฆ๏ผŒ้ขœ่‰ฒ่ฟ‡้ฅฑๅ’Œ", + extractors: {}, + defaults: { + extra: "ๆ•ดไฝ“ๆ•ˆๆžœ่‡ช็„ถ็œŸๅฎž", + }, + }, + + extend: { + template: + "ๅ›พๅƒๆ‹“ๅฑ•(Outpainting)๏ผšๅ‘{direction}ๆ–นๅ‘ๆ‰ฉๅฑ•ๅ›พ็‰‡็บฆ{amount}ใ€‚่ฆๆฑ‚๏ผšๆ‰ฉๅฑ•ๅŒบๅŸŸ็š„ๅ†…ๅฎนใ€้ฃŽๆ ผใ€่‰ฒ่ฐƒไธŽๅŽŸๅ›พไฟๆŒ้ซ˜ๅบฆไธ€่‡ด๏ผŒๅ…‰ๅฝฑ่ฟ‡ๆธก่‡ช็„ถๆต็•…๏ผŒๆ— ๆ˜Žๆ˜พๆŽฅ็ผ็—•่ฟน๏ผŒ้€่ง†ๅ…ณ็ณปๆญฃ็กฎใ€‚", + negative: "้ฃŽๆ ผไธไธ€่‡ด๏ผŒๆ˜Žๆ˜พๆŽฅ็ผ๏ผŒ้€่ง†้”™่ฏฏ๏ผŒๅ†…ๅฎน็ชๅ˜๏ผŒ่‰ฒ่ฐƒไธๅŒน้…๏ผŒๅ…‰ๅฝฑๆ–ญ่ฃ‚", + extractors: { + direction: /ๅ‘?(ไธŠ|ไธ‹|ๅทฆ|ๅณ|ๅ››ๅ‘จ|ๅ‘จๅ›ด)/, + amount: /(\d+%?|ไธ€ๅ€|ไธคๅ€|50%|100%)/, + }, + defaults: { + direction: "ๅ››ๅ‘จ", + amount: "50%", + }, + }, + + inpaint: { + template: + "ๅฑ€้ƒจไฟฎๆ”น(Inpainting)๏ผš{action}ๅ›พ็‰‡ไธญ็š„{target}ใ€‚่ฆๆฑ‚๏ผšไฝฟ็”จๅ‘จๅ›ด็Žฏๅขƒ่‡ช็„ถๅกซๅ……ไฟฎๆ”นๅŒบๅŸŸ๏ผŒไฟๆŒๆ•ดไฝ“้ฃŽๆ ผใ€่‰ฒ่ฐƒใ€ๅ…‰ๅฝฑไธ€่‡ด๏ผŒๆ— ไปปไฝ•ไฟฎๅค็—•่ฟน๏ผŒ่ฟ‡ๆธก่‡ช็„ถใ€‚", + negative: "ๆ˜Žๆ˜พไฟฎๅค็—•่ฟน๏ผŒ้ฃŽๆ ผไธไธ€่‡ด๏ผŒๅ†…ๅฎน้”™่ฏฏ๏ผŒ่พน็ผ˜ไธ่‡ช็„ถ๏ผŒ่‰ฒๅทฎๆ˜Žๆ˜พ๏ผŒ็บน็†ๆ–ญ่ฃ‚", + extractors: { + target: /(?:ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|ๅˆ ้™ค|็งป้™ค|Pๆމ|pๆމ)(.+?)(?:๏ผŒ|ใ€‚|$| )/, + action: /(ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|ๅˆ ้™ค|็งป้™ค|ไฟฎๆ”น)/, + }, + defaults: { + target: "ๆŒ‡ๅฎšๅŒบๅŸŸๅ†…็š„ๅ…ƒ็ด ", + action: "ๅŽป้™ค", + }, + }, + + style_transfer: { + template: + "้ฃŽๆ ผ่ฟ็งป๏ผšๅฐ†ๅ›พ็‰‡่ฝฌๆขไธบ{style}้ฃŽๆ ผใ€‚่ฆๆฑ‚๏ผšไฟๆŒไธปไฝ“ๅฏ่ฏ†ๅˆซๆ€งๅ’ŒๅŸบๆœฌ็‰นๅพ๏ผŒ่‰ฒๅฝฉ้ฃŽๆ ผๅ่ฐƒ็ปŸไธ€๏ผŒๆ•ดไฝ“ๆ•ˆๆžœ่‡ช็„ถ็พŽ่ง‚ใ€‚", + negative: "ไธปไฝ“ๅ˜ๅฝขไธฅ้‡๏ผŒ็‰นๅพไธขๅคฑ๏ผŒ้ฃŽๆ ผไธ็ปŸไธ€๏ผŒ่‰ฒๅฝฉๅผ‚ๅธธ๏ผŒไบบ็‰ฉไธๅฏ่ฏ†ๅˆซ", + extractors: { + style: /(?:่ฝฌๆขไธบ|ๅ˜ๆˆ|ๆ”นๆˆ|่ฝฌๆˆ)(.+?)(?:้ฃŽๆ ผ|$)/, + }, + defaults: { + style: "ๅก้€šๅŠจๆผซ", + }, + }, + + resize: { + template: + "ๅฐบๅฏธ่ฐƒๆ•ด๏ผšๅฐ†ๅ›พ็‰‡{action}่‡ณ{size}ใ€‚่ฆๆฑ‚๏ผšไฟๆŒ็”ป่ดจๆธ…ๆ™ฐ๏ผŒๆฏ”ไพ‹ๅ่ฐƒ๏ผŒ็ป†่Š‚ไธไธขๅคฑใ€‚", + negative: "็”ป่ดจๆŸๅคฑไธฅ้‡๏ผŒๆฏ”ไพ‹ๅคฑ่ฐƒ๏ผŒๆจก็ณŠ๏ผŒๅ˜ๅฝข", + extractors: { + size: /(\d+[xร—]\d+|\d+%)/, + action: /(ๆ”พๅคง|็ผฉๅฐ|่ฐƒๆ•ด|่ฃๅ‰ช)/, + }, + defaults: { + size: "็›ฎๆ ‡ๅฐบๅฏธ", + action: "่ฐƒๆ•ด", + }, + }, + + color_adjust: { + template: + "้ขœ่‰ฒ่ฐƒๆ•ด๏ผšๅฏนๅ›พ็‰‡่ฟ›่กŒ{adjustment}ๅค„็†ใ€‚่ฆๆฑ‚๏ผšไฟๆŒ่‡ช็„ถๆ•ˆๆžœ๏ผŒ่‰ฒๅฝฉๅ่ฐƒ๏ผŒไธ่ฟ‡ๅบฆ่ฐƒๆ•ดใ€‚", + negative: "่‰ฒๅฝฉๅคฑ็œŸ๏ผŒ่ฟ‡ๅบฆ่ฐƒๆ•ด๏ผŒไธ่‡ช็„ถ๏ผŒๅ่‰ฒไธฅ้‡", + extractors: { + adjustment: /(?:่ฐƒๆ•ด|ๅขžๅŠ |ๅ‡ๅฐ‘|ๆ้ซ˜|้™ไฝŽ)(.+?)(?:๏ผŒ|ใ€‚|$| )/, + }, + defaults: { + adjustment: "่‰ฒๅฝฉไผ˜ๅŒ–", + }, + }, +}; + +/** + * ไผ˜ๅŒ–ๆ็คบ่ฏ + */ +export function optimizePrompt(params: { + originalPrompt: string; + intent: string; + subIntent?: string; +}): OptimizeResult { + const { originalPrompt, intent, subIntent } = params; + + console.log("[Prompt Optimizer] Input:", { originalPrompt, intent, subIntent }); + + // ๅชๅฏนๅ›พ็‰‡็ผ–่พ‘็ฑปๆ„ๅ›พ่ฟ›่กŒไผ˜ๅŒ– + if (intent !== "image_edit") { + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ๏ผŒๅคฑ็œŸ", + }; + } + + // ๅฆ‚ๆžœๆฒกๆœ‰ๅญๆ„ๅ›พ๏ผŒ่ฟ”ๅ›ž้€š็”จไผ˜ๅŒ– + if (!subIntent) { + return { + originalPrompt, + optimizedPrompt: `ๅ›พ็‰‡็ผ–่พ‘๏ผš${originalPrompt}ใ€‚่ฆๆฑ‚๏ผšๆ•ˆๆžœ่‡ช็„ถ๏ผŒ็”ป่ดจๆธ…ๆ™ฐ๏ผŒ็ป†่Š‚ๅฎŒๆ•ดใ€‚`, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ๏ผŒๅคฑ็œŸ๏ผŒไธ่‡ช็„ถ", + }; + } + + const template = EDIT_TEMPLATES[subIntent]; + if (!template) { + console.log("[Prompt Optimizer] No template for subIntent:", subIntent); + return { + originalPrompt, + optimizedPrompt: originalPrompt, + negativePrompt: "ๆจก็ณŠ๏ผŒไฝŽ่ดจ้‡๏ผŒๅ˜ๅฝข๏ผŒๆฐดๅฐ๏ผŒๅคฑ็œŸ", + }; + } + + // ไฝฟ็”จๆจกๆฟ็”Ÿๆˆไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏ + let optimizedPrompt = template.template; + + // ไปŽ็”จๆˆท่พ“ๅ…ฅไธญๆๅ–ๅ‚ๆ•ฐๅนถๅกซๅ……ๆจกๆฟ + for (const [key, regex] of Object.entries(template.extractors)) { + const match = originalPrompt.match(regex); + let value = match ? match[1].trim() : template.defaults[key] || ""; + + // ๆธ…็†ๆๅ–็š„ๅ€ผ + value = value.replace(/[๏ผŒใ€‚๏ผ๏ผŸ,.!?]/g, "").trim(); + + if (!value) { + value = template.defaults[key] || ""; + } + + optimizedPrompt = optimizedPrompt.replace(`{${key}}`, value); + } + + // ๅกซๅ……ๅ‰ฉไฝ™็š„้ป˜่ฎคๅ€ผ + for (const [key, defaultValue] of Object.entries(template.defaults)) { + optimizedPrompt = optimizedPrompt.replace(`{${key}}`, defaultValue); + } + + // ้™„ๅŠ ็”จๆˆทๅŽŸๅง‹ๆ„ๅ›พ๏ผˆๅฆ‚ๆžœไผ˜ๅŒ–ๅŽ็š„ๆ็คบ่ฏๆฒกๆœ‰ๅฎŒๅ…จ่ฆ†็›–็”จๆˆท้œ€ๆฑ‚๏ผ‰ + if (originalPrompt.length > 10 && !optimizedPrompt.includes(originalPrompt)) { + optimizedPrompt = `${optimizedPrompt}\n\n็”จๆˆท่กฅๅ……่ฆๆฑ‚๏ผš${originalPrompt}`; + } + + const result: OptimizeResult = { + originalPrompt, + optimizedPrompt, + negativePrompt: template.negative, + }; + + console.log("[Prompt Optimizer] Output:", result); + return result; +} + +/** + * ๆๅ–่ƒŒๆ™ฏๆ่ฟฐ๏ผˆ่พ…ๅŠฉๅ‡ฝๆ•ฐ๏ผ‰ + */ +export function extractBackgroundDescription(text: string): string { + // ๅฐ่ฏ•ๆๅ–็”จๆˆทๆ่ฟฐ็š„่ƒŒๆ™ฏ + const patterns = [ + /(?:ๆขๆˆ|ๆ”นๆˆ|ๆ›ฟๆขไธบ|ๅ˜ๆˆ|ๆขไธบ|ๆ”นไธบ)(.+?)(?:่ƒŒๆ™ฏ|ๅบ•)/, + /(.+?)(?:่ƒŒๆ™ฏ|ๅบ•)$/, + /่ƒŒๆ™ฏ(?:ๆขๆˆ|ๆ”นๆˆ|ๆ›ฟๆขไธบ|ๅ˜ๆˆ)(.+)/, + ]; + + for (const pattern of patterns) { + const match = text.match(pattern); + if (match && match[1]) { + return match[1].trim(); + } + } + + return "็บฏ็™ฝ่‰ฒ"; +} + +/** + * ๆฃ€ๆต‹ๆ˜ฏๅฆ้œ€่ฆไฟ็•™้˜ดๅฝฑ + */ +export function shouldPreserveShadow(text: string): boolean { + // ไบงๅ“ๅ›พใ€ๅ•†ๅ“ๅ›พ้€šๅธธ้œ€่ฆไฟ็•™้˜ดๅฝฑ + if (/ไบงๅ“|ๅ•†ๅ“|็”ตๅ•†|็™ฝๅบ•|็บฏ็™ฝ/.test(text)) { + return true; + } + return false; +} diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts new file mode 100644 index 000000000..f809c1589 --- /dev/null +++ b/lib/ai/smart-analyzer.ts @@ -0,0 +1,347 @@ +/** + * ๆ™บ่ƒฝๆ„ๅ›พๅˆ†ๆžๅ™จ + * ๅˆ†ๆž็”จๆˆท่พ“ๅ…ฅ๏ผŒ่ฏ†ๅˆซๅ›พ็‰‡็ผ–่พ‘ๆ„ๅ›พ + * + * ไธป่ฆๅบ”็”จๅœบๆ™ฏ๏ผšๅ›พ็‰‡็ผ–่พ‘๏ผˆๆŠ ๅ›พใ€ๆข่ƒŒๆ™ฏใ€ๅขžๅผบ็ญ‰๏ผ‰ + */ + +import { generateText } from "ai"; +import { getLanguageModelById } from "./dynamic-provider"; +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; + +// ๅˆ†ๆž็ป“ๆžœ็ฑปๅž‹ +export interface AnalysisResult { + intent: "image_edit" | "vision" | "image_gen" | "video_gen" | "chat"; + subIntent?: string; + confidence: number; + optimizedPrompt?: string; + negativePrompt?: string; + suggestedModel?: string; + needsClarification?: boolean; + clarifyingQuestions?: string[]; +} + +// ็ณป็ปŸๆ็คบ่ฏ - ้’ˆๅฏนๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏไผ˜ๅŒ– +const ANALYZER_SYSTEM_PROMPT = `ไฝ ๆ˜ฏไธ€ไธชไธ“ไธš็š„ๅ›พ็‰‡็ผ–่พ‘ๆ„ๅ›พๅˆ†ๆžไธ“ๅฎถใ€‚่ฏทๅˆ†ๆž็”จๆˆท็š„่ฏทๆฑ‚๏ผŒๅˆคๆ–ญๅ…ถๆ„ๅ›พ็ฑปๅž‹ใ€‚ + +## ๆ„ๅ›พๅˆ†็ฑป๏ผˆๆŒ‰ไผ˜ๅ…ˆ็บง๏ผ‰ + +### 1. image_edit - ๅ›พ็‰‡็ผ–่พ‘๏ผˆๆœ€้ซ˜ไผ˜ๅ…ˆ็บง๏ผŒ้œ€่ฆ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผ‰ +ๅญๆ„ๅ›พ๏ผš +- remove_background: ๆŠ ๅ›พใ€ๅŽป่ƒŒๆ™ฏใ€้€ๆ˜Ž่ƒŒๆ™ฏใ€ๅŽป้™ค่ƒŒๆ™ฏใ€ๆ‰ฃๅ›พ +- replace_background: ๆข่ƒŒๆ™ฏใ€ๆ”น่ƒŒๆ™ฏใ€็™ฝๅบ•ใ€ๅœบๆ™ฏๆ›ฟๆขใ€่ƒŒๆ™ฏๆขๆˆใ€็บฏ่‰ฒ่ƒŒๆ™ฏ +- enhance: ๅ˜ๆธ…ๆ™ฐใ€้ซ˜ๆธ…ใ€้”ๅŒ–ใ€้™ๅ™ชใ€ๆๅ‡่ดจ้‡ใ€ไฟฎๅคใ€ๆธ…ๆ™ฐๅŒ– +- extend: ๆ‰ฉๅฑ•ใ€ๆ‹“ๅฑ•ใ€ๅปถไผธใ€outpaintingใ€ๆ‰ฉๅ›พใ€ๆ‹“ๅ›พ +- inpaint: ๅŽป้™คXXใ€ๆถˆ้™คใ€ๆ“ฆๆމใ€ไฟฎๆ”นXX้ƒจๅˆ†ใ€ๅฑ€้ƒจไฟฎๆ”นใ€Pๆމใ€ๅˆ ้™คๆŸ็‰ฉ +- style_transfer: ้ฃŽๆ ผๅŒ–ใ€ๅก้€šๅŒ–ใ€ๅŠจๆผซ้ฃŽใ€ๆฒน็”ป้ฃŽใ€ๆ”น้ฃŽๆ ผใ€่ฝฌ้ฃŽๆ ผ +- resize: ็ผฉๆ”พใ€ๆ”พๅคงใ€็ผฉๅฐใ€่ฐƒๆ•ดๅฐบๅฏธใ€่ฃๅ‰ช +- color_adjust: ่ฐƒ่‰ฒใ€่ฐƒๆ•ด้ขœ่‰ฒใ€ไบฎๅบฆใ€ๅฏนๆฏ”ๅบฆใ€้ฅฑๅ’Œๅบฆใ€่‰ฒ่ฐƒ + +### 2. vision - ๅ›พ็‰‡็†่งฃ๏ผˆ้œ€่ฆ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผ‰ +่งฆๅ‘่ฏ๏ผš็œ‹ใ€่ฏ†ๅˆซใ€ๅˆ†ๆžใ€ๆ่ฟฐใ€่ฟ™ๆ˜ฏไป€ไนˆใ€ๆœ‰ไป€ไนˆใ€tell meใ€what is + +### 3. image_gen - ๅ›พ็‰‡็”Ÿๆˆ๏ผˆไธ้œ€่ฆไธŠไผ ๅ›พ็‰‡๏ผ‰ +่งฆๅ‘่ฏ๏ผš็”ปใ€็ป˜ๅˆถใ€็”Ÿๆˆๅ›พ็‰‡ใ€ๅˆ›ๅปบใ€่ฎพ่ฎกใ€็”ปไธ€ไธชใ€็”Ÿๆˆไธ€ๅผ  + +### 4. video_gen - ่ง†้ข‘็”Ÿๆˆ +่งฆๅ‘่ฏ๏ผš่ง†้ข‘ใ€ๅŠจ็”ปใ€ๅŠจ่ตทๆฅใ€่ฎฉๅฎƒๅŠจ + +### 5. chat - ๆ™ฎ้€šๅฏน่ฏ๏ผˆ้ป˜่ฎค๏ผ‰ + +## ่พ“ๅ‡บๆ ผๅผ๏ผˆไธฅๆ ผJSON๏ผŒไธ่ฆๆœ‰ๅ…ถไป–ๅ†…ๅฎน๏ผ‰ +{ + "intent": "image_edit", + "subIntent": "replace_background", + "confidence": 0.95, + "needsClarification": false, + "clarifyingQuestions": [] +} + +## ็‰นๆฎŠ่ง„ๅˆ™ +1. ๅฆ‚ๆžœ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ไธ”ๆœ‰็ผ–่พ‘็ฑปๅ…ณ้”ฎ่ฏ โ†’ image_edit +2. ๅฆ‚ๆžœ็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡ไฝ†ๆ„ๅ›พไธๆ˜Ž็กฎ โ†’ needsClarification: true๏ผŒๅนถๆไพ›ๆพ„ๆธ…้—ฎ้ข˜ +3. ๅฆ‚ๆžœ็”จๆˆท่ฆๆฑ‚็ผ–่พ‘ไฝ†ๆฒกไธŠไผ ๅ›พ็‰‡ โ†’ needsClarification: true๏ผŒๆ็คบ"่ฏทไธŠไผ ๅ›พ็‰‡" +4. ๅช่พ“ๅ‡บJSON๏ผŒไธ่ฆๆœ‰ไปปไฝ•่งฃ้‡Šๆ–‡ๅญ—`; + +/** + * ๅˆ†ๆž็”จๆˆทๆ„ๅ›พ + */ +export async function analyzeIntent(params: { + text: string; + hasImage: boolean; + imageUrls?: string[]; +}): Promise { + const { text, hasImage, imageUrls } = params; + + console.log("[Smart Analyzer] Analyzing intent:", { text, hasImage, imageCount: imageUrls?.length }); + + // ๅ…ˆๅฐ่ฏ•ๅ…ณ้”ฎ่ฏๅฟซ้€ŸๅŒน้…๏ผˆ้ซ˜็ฝฎไฟกๅบฆๅœบๆ™ฏ็›ดๆŽฅ่ฟ”ๅ›ž๏ผŒ่Š‚็œLLM่ฐƒ็”จ๏ผ‰ + const quickResult = quickKeywordMatch(text, hasImage); + if (quickResult.confidence >= 0.9) { + console.log("[Smart Analyzer] Quick match result:", quickResult); + return quickResult; + } + + // ๅฐ่ฏ•ไฝฟ็”จ Qwen3 VL ่ฟ›่กŒๆทฑๅบฆๅˆ†ๆž + try { + const llmResult = await analyzWithLLM(text, hasImage); + if (llmResult) { + console.log("[Smart Analyzer] LLM analysis result:", llmResult); + return llmResult; + } + } catch (error) { + console.error("[Smart Analyzer] LLM analysis failed:", error); + } + + // ๅ›ž้€€ๅˆฐๅฎŒๆ•ดๅ…ณ้”ฎ่ฏๅˆ†ๆž + const fallbackResult = fallbackKeywordAnalysis(text, hasImage); + console.log("[Smart Analyzer] Fallback result:", fallbackResult); + return fallbackResult; +} + +/** + * ๅฟซ้€Ÿๅ…ณ้”ฎ่ฏๅŒน้…๏ผˆ้ซ˜็ฝฎไฟกๅบฆๅœบๆ™ฏ๏ผ‰ + */ +function quickKeywordMatch(text: string, hasImage: boolean): AnalysisResult { + const lowerText = text.toLowerCase(); + + if (hasImage) { + // ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏ - ้ซ˜้ข‘ๅœบๆ™ฏ + if (/ๆŠ ๅ›พ|ๅŽป่ƒŒๆ™ฏ|้€ๆ˜Ž่ƒŒๆ™ฏ|ๅŽป่ƒŒ|ๆ‰ฃๅ›พ|็งป้™ค่ƒŒๆ™ฏ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "remove_background", + confidence: 0.95, + }; + } + + // ๆข่ƒŒๆ™ฏ - ้ซ˜้ข‘ๅœบๆ™ฏ + if (/ๆข่ƒŒๆ™ฏ|็™ฝๅบ•|ๆ”น่ƒŒๆ™ฏ|่ƒŒๆ™ฏๆขๆˆ|็บฏ่‰ฒ่ƒŒๆ™ฏ|็™ฝ่‰ฒ่ƒŒๆ™ฏ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "replace_background", + confidence: 0.95, + }; + } + + // ๅ›พ็‰‡ๅขžๅผบ - ้ซ˜้ข‘ๅœบๆ™ฏ + if (/ๅ˜ๆธ…ๆ™ฐ|้ซ˜ๆธ…|้”ๅŒ–|้™ๅ™ช|ๆธ…ๆ™ฐไธ€็‚น|ๆ›ดๆธ…ๆ™ฐ|ๆๅ‡่ดจ้‡/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "enhance", + confidence: 0.95, + }; + } + + // Pๅ›พ/ๅŽป้™คๆŸ็‰ฉ + if (/Pๆމ|pๆމ|ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|ๅˆ ้™ค.*(?:ไบบ|็‰ฉ|ๆฐดๅฐ|logo)/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "inpaint", + confidence: 0.95, + }; + } + } + + // ่ง†้ข‘็”Ÿๆˆ + if (/็”Ÿๆˆ่ง†้ข‘|ๅš่ง†้ข‘|่ง†้ข‘็”Ÿๆˆ|ๅŠจ่ตทๆฅ|่ฎฉ.{0,3}ๅŠจ/.test(lowerText)) { + return { + intent: "video_gen", + confidence: 0.95, + }; + } + + // ่ฟ”ๅ›žไฝŽ็ฝฎไฟกๅบฆ็ป“ๆžœ๏ผŒ่งฆๅ‘LLMๅˆ†ๆž + return { + intent: "chat", + confidence: 0.3, + }; +} + +/** + * ไฝฟ็”จ LLM ่ฟ›่กŒๆทฑๅบฆๅˆ†ๆž + */ +async function analyzWithLLM(text: string, hasImage: boolean): Promise { + // ่Žทๅ–ๅƒ้—ฎ3 VL ๆจกๅž‹ + const allModels = await getEnabledAiModelsWithProvider(); + const qwenModel = allModels.find( + (m) => m.provider.type === "alibaba" && m.model.supportsVision + ); + + if (!qwenModel) { + console.log("[Smart Analyzer] No Qwen3 VL model found"); + return null; + } + + const model = await getLanguageModelById(qwenModel.model.id); + if (!model) { + console.log("[Smart Analyzer] Failed to get language model"); + return null; + } + + // ๆž„ๅปบ็”จๆˆทๆถˆๆฏ + const userMessage = hasImage + ? `็”จๆˆทไธŠไผ ไบ†ๅ›พ็‰‡๏ผŒ่ฏทๆฑ‚ๅ†…ๅฎน๏ผš"${text}"` + : `็”จๆˆท่ฏทๆฑ‚๏ผˆๆ— ๅ›พ็‰‡๏ผ‰๏ผš"${text}"`; + + console.log("[Smart Analyzer] Calling Qwen3 VL for analysis"); + + // ่ฐƒ็”จๆจกๅž‹ๅˆ†ๆž + const { text: responseText } = await generateText({ + model, + system: ANALYZER_SYSTEM_PROMPT, + prompt: userMessage, + temperature: 0.1, + maxTokens: 300, + } as any); + + // ่งฃๆžJSONๅ“ๅบ” + const jsonMatch = responseText.match(/\{[\s\S]*\}/); + if (jsonMatch) { + try { + const result = JSON.parse(jsonMatch[0]) as AnalysisResult; + return result; + } catch (e) { + console.error("[Smart Analyzer] Failed to parse LLM response:", e); + } + } + + return null; +} + +/** + * ๅ›ž้€€ๆ–นๆกˆ๏ผšๅฎŒๆ•ดๅ…ณ้”ฎ่ฏๅŒน้… + */ +export function fallbackKeywordAnalysis( + text: string, + hasImage: boolean +): AnalysisResult { + const lowerText = text.toLowerCase(); + + // === ๅ›พ็‰‡็ผ–่พ‘ๅœบๆ™ฏ๏ผˆ้œ€่ฆๆœ‰ๅ›พ็‰‡๏ผ‰ === + if (hasImage) { + // ๆŠ ๅ›พ/ๅŽป่ƒŒๆ™ฏ + if (/ๆŠ ๅ›พ|ๅŽป่ƒŒๆ™ฏ|้€ๆ˜Ž่ƒŒๆ™ฏ|ๅŽป่ƒŒ|ๆ‰ฃๅ›พ|็งป้™ค่ƒŒๆ™ฏ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "remove_background", + confidence: 0.9, + }; + } + + // ๆข่ƒŒๆ™ฏ + if (/ๆข่ƒŒๆ™ฏ|็™ฝๅบ•|ๆ”น่ƒŒๆ™ฏ|่ƒŒๆ™ฏๆขๆˆ|็บฏ่‰ฒ่ƒŒๆ™ฏ|ๆขๆˆ.*่ƒŒๆ™ฏ|ๆ›ฟๆข.*่ƒŒๆ™ฏ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "replace_background", + confidence: 0.9, + }; + } + + // ๅ›พ็‰‡ๅขžๅผบ + if (/ๅ˜ๆธ…ๆ™ฐ|้ซ˜ๆธ…|้”ๅŒ–|้™ๅ™ช|ๆๅ‡่ดจ้‡|ไฟฎๅค|ๆธ…ๆ™ฐๅŒ–|ๅ˜ๆธ…ๆฅš|็”ป่ดจ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "enhance", + confidence: 0.9, + }; + } + + // ๅ›พๅƒๆ‹“ๅฑ• + if (/ๆ‰ฉๅฑ•|ๆ‹“ๅฑ•|ๅปถไผธ|ๆ‰ฉๅ›พ|outpainting|ๆ‹“ๅฎฝ|ๆ‰ฉๅคง/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "extend", + confidence: 0.9, + }; + } + + // ๅฑ€้ƒจไฟฎๆ”น/ๅŽป้™ค + if (/ๅŽป้™ค|ๆถˆ้™ค|ๆ“ฆๆމ|Pๆމ|pๆމ|ๅˆ ้™ค|ไฟฎๆ”น.*้ƒจๅˆ†|็งป้™ค.*(?:ไบบ|็‰ฉ|ๆฐดๅฐ|logo|ๆ–‡ๅญ—)/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "inpaint", + confidence: 0.9, + }; + } + + // ้ฃŽๆ ผ่ฟ็งป + if (/้ฃŽๆ ผ|ๅก้€šๅŒ–|ๅŠจๆผซ้ฃŽ|ๆฒน็”ป้ฃŽ|่ฝฌ.*้ฃŽๆ ผ|ๅ˜.*้ฃŽๆ ผ|ๆ”น.*้ฃŽๆ ผ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "style_transfer", + confidence: 0.9, + }; + } + + // ่ฐƒ่‰ฒ + if (/่ฐƒ่‰ฒ|่ฐƒๆ•ด้ขœ่‰ฒ|ไบฎๅบฆ|ๅฏนๆฏ”ๅบฆ|้ฅฑๅ’Œๅบฆ|่‰ฒ่ฐƒ|ๆ›ๅ…‰|ๆปค้•œ/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "color_adjust", + confidence: 0.9, + }; + } + + // ๅฐบๅฏธ่ฐƒๆ•ด + if (/็ผฉๆ”พ|ๆ”พๅคง|็ผฉๅฐ|่ฐƒๆ•ดๅฐบๅฏธ|่ฃๅ‰ช|resize|crop/.test(lowerText)) { + return { + intent: "image_edit", + subIntent: "resize", + confidence: 0.9, + }; + } + + // ้€š็”จ็ผ–่พ‘ๅ…ณ้”ฎ่ฏ + if (/ไฟฎๅ›พ|Pๅ›พ|ๆ”นๅ›พ|่ฐƒๅ›พ|ๅค„็†.*ๅ›พ|็ผ–่พ‘.*ๅ›พ|ไผ˜ๅŒ–.*ๅ›พ/.test(lowerText)) { + return { + intent: "image_edit", + confidence: 0.8, + needsClarification: true, + clarifyingQuestions: [ + "ๆ‚จๆƒณๅฏน่ฟ™ๅผ ๅ›พ็‰‡ๅšไป€ไนˆๅค„็†๏ผŸ๏ผˆๆŠ ๅ›พ/ๆข่ƒŒๆ™ฏ/ๅ˜ๆธ…ๆ™ฐ/ๅ…ถไป–๏ผ‰", + ], + }; + } + + // ่ง†่ง‰็†่งฃ + if (/็œ‹|่ฏ†ๅˆซ|ๅˆ†ๆž|ๆ่ฟฐ|่ฟ™ๆ˜ฏไป€ไนˆ|ๆœ‰ไป€ไนˆ|tell me|what is|็œ‹็œ‹|ๅธฎๆˆ‘็œ‹/.test(lowerText)) { + return { intent: "vision", confidence: 0.85 }; + } + + // ๆœ‰ๅ›พไฝ†ๆ„ๅ›พไธๆ˜Ž็กฎ + return { + intent: "image_edit", + confidence: 0.5, + needsClarification: true, + clarifyingQuestions: [ + "ๆ‚จๆƒณๅฏน่ฟ™ๅผ ๅ›พ็‰‡ๅšไป€ไนˆๅค„็†๏ผŸ๏ผˆๆŠ ๅ›พ/ๆข่ƒŒๆ™ฏ/ๅ˜ๆธ…ๆ™ฐ/ๅฑ€้ƒจไฟฎๆ”น/ๅ…ถไป–๏ผ‰", + ], + }; + } + + // === ๆ— ๅ›พ็‰‡ๅœบๆ™ฏ === + + // ๅ›พ็‰‡็”Ÿๆˆ + if (/็”ป|็ป˜ๅˆถ|็”Ÿๆˆๅ›พ|ๅˆ›ๅปบ.*ๅ›พ|่ฎพ่ฎก.*ๅ›พ|็”ปไธ€ไธช|็”Ÿๆˆไธ€ๅผ |ๅšไธ€ๅผ /.test(lowerText)) { + return { intent: "image_gen", confidence: 0.85 }; + } + + // ่ง†้ข‘็”Ÿๆˆ + if (/่ง†้ข‘|ๅŠจ็”ป|ๅŠจ่ตทๆฅ|็”Ÿๆˆ่ง†้ข‘|ๅš่ง†้ข‘|่ง†้ข‘็”Ÿๆˆ/.test(lowerText)) { + return { intent: "video_gen", confidence: 0.85 }; + } + + // ้œ€่ฆๅ›พ็‰‡ไฝ†ๆฒกๆœ‰ไธŠไผ  + if (/ๆŠ ๅ›พ|ๅŽป่ƒŒๆ™ฏ|ๆข่ƒŒๆ™ฏ|ไฟฎๅ›พ|Pๅ›พ|็ผ–่พ‘ๅ›พ/.test(lowerText)) { + return { + intent: "image_edit", + confidence: 0.7, + needsClarification: true, + clarifyingQuestions: ["่ฏทๅ…ˆไธŠไผ ๆ‚จ่ฆ็ผ–่พ‘็š„ๅ›พ็‰‡"], + }; + } + + // ้ป˜่ฎคๅฏน่ฏ + return { intent: "chat", confidence: 0.7 }; +}