Upload latest code and optimized prompts (v6)
This commit is contained in:
209
test-vision-extract.js
Normal file
209
test-vision-extract.js
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* 测试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": "<hex code like #C5E8ED>",
|
||||
"name": "<descriptive name like 'ice blue', 'mint green'>",
|
||||
"secondary": "<any accent colors>"
|
||||
},
|
||||
"shape": {
|
||||
"type": "<flower/fan/cone/other>",
|
||||
"petal_count": <number of segments/petals>,
|
||||
"opening": "<C-shaped/full-circle/other>",
|
||||
"description": "<detailed shape description>"
|
||||
},
|
||||
"material": {
|
||||
"type": "<PU/fabric/plastic/other>",
|
||||
"finish": "<glossy/matte/satin>",
|
||||
"texture": "<smooth/quilted/ribbed>"
|
||||
},
|
||||
"edge_binding": {
|
||||
"color": "<color of inner neck edge>",
|
||||
"material": "<ribbed elastic/fabric/other>"
|
||||
},
|
||||
"closure": {
|
||||
"type": "<velcro/button/snap/other>",
|
||||
"color": "<white/matching/other>",
|
||||
"position": "<which petal or location>"
|
||||
},
|
||||
"logo": {
|
||||
"text": "<brand name if visible>",
|
||||
"style": "<embroidered/printed/tag>",
|
||||
"position": "<location on product>"
|
||||
},
|
||||
"unique_features": [
|
||||
"<list any distinctive 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);
|
||||
|
||||
Reference in New Issue
Block a user