/** * 测试Vision自动提取产品特征 * 目标:验证Vision能否从素材图中自动提取"黄金产品描述" */ require('dotenv').config(); const axios = require('axios'); const fs = require('fs'); const path = require('path'); const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ'; const API_BASE = 'https://api.wuyinkeji.com/api'; // R2客户端 const r2Client = new S3Client({ region: 'auto', endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY, }, forcePathStyle: true, }); async function uploadToR2(filePath) { const fileName = `vision-test-${Date.now()}-${path.basename(filePath)}`; const fileBuffer = fs.readFileSync(filePath); await r2Client.send(new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME || 'ai-flow', Key: fileName, Body: fileBuffer, ContentType: filePath.endsWith('.png') ? 'image/png' : 'image/jpeg', })); const publicUrl = process.env.R2_PUBLIC_DOMAIN ? `${process.env.R2_PUBLIC_DOMAIN}/${fileName}` : `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com/${process.env.R2_BUCKET_NAME}/${fileName}`; return publicUrl; } // Vision提取产品特征的Prompt const VISION_EXTRACT_PROMPT = `Analyze this pet recovery cone product image in EXTREME detail. You MUST extract and output the following information as structured JSON: { "color": { "primary": "", "name": "", "secondary": "" }, "shape": { "type": "", "petal_count": , "opening": "", "description": "" }, "material": { "type": "", "finish": "", "texture": "" }, "edge_binding": { "color": "", "material": "" }, "closure": { "type": "", "color": "", "position": "" }, "logo": { "text": "", "style": "", "position": "" }, "unique_features": [ "" ], "overall_description": "<2-3 sentence summary for image generation prompt>" } Be EXTREMELY precise about colors and structural details. This will be used to generate consistent product images.`; async function testVisionExtract(imagePath) { console.log('📤 上传图片到R2...'); const imageUrl = await uploadToR2(imagePath); console.log(' URL:', imageUrl); console.log('\n🔍 调用Vision API分析图片...'); try { const response = await axios.post(`${API_BASE}/chat/index`, { key: API_KEY, model: 'gemini-3-pro', content: VISION_EXTRACT_PROMPT, image_url: imageUrl }, { timeout: 60000 }); const content = response.data.data?.choices?.[0]?.message?.content || response.data.data?.content || response.data.choices?.[0]?.message?.content; if (!content) { console.error('Vision响应为空'); console.log('完整响应:', JSON.stringify(response.data, null, 2)); return null; } console.log('\n📝 Vision原始输出:\n'); console.log(content); // 尝试提取JSON const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { try { const parsed = JSON.parse(jsonMatch[0]); console.log('\n✅ 解析后的JSON:\n'); console.log(JSON.stringify(parsed, null, 2)); return parsed; } catch (e) { console.log('\n⚠️ JSON解析失败,但有输出内容'); return { raw: content }; } } return { raw: content }; } catch (error) { console.error('Vision API错误:', error.message); if (error.response) { console.error('响应数据:', error.response.data); } return null; } } async function main() { const MATERIAL_DIR = path.join(__dirname, '素材/素材/已有的素材'); const flatImagePath = path.join(MATERIAL_DIR, 'IMG_5683.png'); console.log('='.repeat(60)); console.log('🧪 Vision产品特征提取测试'); console.log('='.repeat(60)); console.log('\n测试图片:', flatImagePath); const result = await testVisionExtract(flatImagePath); if (result) { // 保存结果 const outputPath = path.join(__dirname, 'vision-extract-result.json'); fs.writeFileSync(outputPath, JSON.stringify(result, null, 2)); console.log('\n💾 结果已保存到:', outputPath); // 如果成功解析,生成Golden Description if (result.overall_description || result.shape) { console.log('\n' + '='.repeat(60)); console.log('🌟 生成的Golden Product Description:'); console.log('='.repeat(60)); const golden = generateGoldenDescription(result); console.log(golden); fs.writeFileSync( path.join(__dirname, 'golden-description.txt'), golden ); } } } function generateGoldenDescription(visionResult) { if (visionResult.raw) { return `[需要手动整理Vision输出]\n\n${visionResult.raw}`; } const r = visionResult; return ` EXACT PRODUCT APPEARANCE (AUTO-EXTRACTED BY VISION): - Shape: ${r.shape?.petal_count || '?'}-PETAL ${r.shape?.type?.toUpperCase() || 'FLOWER'} shape, ${r.shape?.opening || 'C-shaped'} opening - Color: ${r.color?.name?.toUpperCase() || 'UNKNOWN'} (${r.color?.primary || '#???'}) - Material: ${r.material?.finish || 'Glossy'} ${r.material?.type || 'PU'} fabric with ${r.material?.texture || 'smooth'} texture - Edge binding: ${r.edge_binding?.color || 'TEAL'} ${r.edge_binding?.material || 'ribbed elastic'} around inner neck hole - Closure: ${r.closure?.color || 'White'} ${r.closure?.type || 'velcro'} on ${r.closure?.position || 'one petal end'} - Logo: "${r.logo?.text || 'TOUCHDOG'}" ${r.logo?.style || 'embroidered'} on ${r.logo?.position || 'one petal'} UNIQUE FEATURES: ${(r.unique_features || []).map(f => `- ${f}`).join('\n') || '- Visible stitching between petals\n- Soft but structured'} SUMMARY FOR PROMPTS: ${r.overall_description || 'Pet recovery cone with flower-petal design, soft waterproof material.'} CRITICAL PROHIBITIONS: - ❌ NO printed patterns or colorful fabric designs - ❌ NO hard plastic transparent cones - ❌ NO fully circular/closed shapes (must have C-opening) - ❌ NO random brand logos or text `.trim(); } main().catch(console.error);