Upload latest code and optimized prompts (v6)
This commit is contained in:
198
lib/vision-extractor.js
Normal file
198
lib/vision-extractor.js
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Vision产品特征提取器
|
||||
* 支持超时重试 + 缓存
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ';
|
||||
const API_BASE = 'https://api.wuyinkeji.com/api';
|
||||
|
||||
// Vision提取Prompt
|
||||
const VISION_EXTRACT_PROMPT = `Analyze this pet product image in EXTREME detail.
|
||||
|
||||
Output ONLY a valid JSON object (no markdown, no explanation, no thinking):
|
||||
|
||||
{
|
||||
"color": {
|
||||
"primary": "<hex code like #C3E6E8>",
|
||||
"name": "<descriptive name like 'ice blue', 'mint green'>",
|
||||
"secondary": "<accent colors>"
|
||||
},
|
||||
"shape": {
|
||||
"type": "<flower/fan/cone/donut>",
|
||||
"petal_count": <number of segments>,
|
||||
"opening": "<C-shaped/full-circle/adjustable>",
|
||||
"description": "<detailed shape description>"
|
||||
},
|
||||
"material": {
|
||||
"type": "<PU/nylon/polyester/fabric>",
|
||||
"finish": "<glossy/matte/satin>",
|
||||
"texture": "<smooth/quilted/padded>"
|
||||
},
|
||||
"edge_binding": {
|
||||
"color": "<color of inner neck edge>",
|
||||
"material": "<ribbed elastic/fabric>"
|
||||
},
|
||||
"closure": {
|
||||
"type": "<velcro/button/snap>",
|
||||
"color": "<white/matching>",
|
||||
"position": "<location description>"
|
||||
},
|
||||
"logo": {
|
||||
"text": "<brand name>",
|
||||
"style": "<embroidered/printed/tag>",
|
||||
"position": "<location>"
|
||||
},
|
||||
"unique_features": ["<list distinctive features>"],
|
||||
"overall_description": "<2-3 sentence summary for image generation>"
|
||||
}`;
|
||||
|
||||
/**
|
||||
* 调用Vision API提取产品特征
|
||||
* @param {string} imageUrl - 图片URL
|
||||
* @param {object} options - 配置选项
|
||||
* @returns {object|null} - 提取的产品特征JSON
|
||||
*/
|
||||
async function extractProductFeatures(imageUrl, options = {}) {
|
||||
const {
|
||||
maxRetries = 3,
|
||||
timeout = 120000, // 120秒
|
||||
retryDelay = 5000,
|
||||
cacheDir = null,
|
||||
cacheKey = null
|
||||
} = options;
|
||||
|
||||
// 检查缓存
|
||||
if (cacheDir && cacheKey) {
|
||||
const cachePath = path.join(cacheDir, `vision-cache-${cacheKey}.json`);
|
||||
if (fs.existsSync(cachePath)) {
|
||||
try {
|
||||
const cached = JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
|
||||
if (cached && cached.color) {
|
||||
console.log(' 📦 使用缓存的Vision结果');
|
||||
return cached;
|
||||
}
|
||||
} catch (e) {
|
||||
// 缓存无效,继续请求
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let lastError = null;
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
console.log(` 🔍 Vision分析 (尝试 ${attempt}/${maxRetries})...`);
|
||||
|
||||
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 });
|
||||
|
||||
const content = response.data.data?.choices?.[0]?.message?.content ||
|
||||
response.data.data?.content;
|
||||
|
||||
if (!content) {
|
||||
throw new Error('Vision响应为空');
|
||||
}
|
||||
|
||||
// 提取JSON(跳过thinking部分)
|
||||
let jsonStr = content;
|
||||
|
||||
// 如果有<think>标签,跳过它
|
||||
const thinkEnd = content.indexOf('</think>');
|
||||
if (thinkEnd !== -1) {
|
||||
jsonStr = content.substring(thinkEnd + 8);
|
||||
}
|
||||
|
||||
// 提取JSON
|
||||
const jsonMatch = jsonStr.match(/\{[\s\S]*\}/);
|
||||
if (jsonMatch) {
|
||||
const parsed = JSON.parse(jsonMatch[0]);
|
||||
|
||||
// 验证必要字段
|
||||
if (parsed.color && parsed.shape) {
|
||||
console.log(' ✓ Vision提取成功');
|
||||
|
||||
// 保存缓存
|
||||
if (cacheDir && cacheKey) {
|
||||
const cachePath = path.join(cacheDir, `vision-cache-${cacheKey}.json`);
|
||||
fs.writeFileSync(cachePath, JSON.stringify(parsed, null, 2));
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('无法解析有效的JSON');
|
||||
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
console.log(` ⚠️ 尝试 ${attempt} 失败: ${error.message}`);
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
console.log(` ⏳ ${retryDelay/1000}秒后重试...`);
|
||||
await new Promise(r => setTimeout(r, retryDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.error(` ❌ Vision提取最终失败: ${lastError?.message}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Vision结果转换为Golden Description
|
||||
*/
|
||||
function buildGoldenDescription(visionResult, productType = 'pet recovery cone') {
|
||||
if (!visionResult) {
|
||||
return `
|
||||
PRODUCT: ${productType}
|
||||
- Shape: Soft flower/petal shape with C-shaped opening
|
||||
- Material: Soft waterproof fabric
|
||||
- Closure: Velcro strap
|
||||
- Comfortable design for pets
|
||||
|
||||
CRITICAL: Follow the reference image EXACTLY for product shape and color.
|
||||
`;
|
||||
}
|
||||
|
||||
const r = visionResult;
|
||||
|
||||
return `
|
||||
EXACT PRODUCT APPEARANCE (MUST MATCH REFERENCE IMAGE):
|
||||
|
||||
- Shape: ${r.shape?.petal_count || '7-8'}-PETAL ${(r.shape?.type || 'flower').toUpperCase()} shape
|
||||
- Opening: ${r.shape?.opening || 'C-shaped'} (NOT a full circle)
|
||||
- Color: ${(r.color?.name || 'ice blue').toUpperCase()} (${r.color?.primary || '#C3E6E8'})
|
||||
- Material: ${r.material?.finish || 'Soft'} ${r.material?.type || 'waterproof fabric'} with ${r.material?.texture || 'padded'} texture
|
||||
- Edge binding: ${r.edge_binding?.color || 'Matching color'} ${r.edge_binding?.material || 'ribbed elastic'} around inner neck hole
|
||||
- Closure: ${r.closure?.color || 'White'} ${r.closure?.type || 'velcro'} on ${r.closure?.position || 'one segment'}
|
||||
- Logo: "${r.logo?.text || 'TOUCHDOG'}" ${r.logo?.style || 'embroidered'} on ${r.logo?.position || 'one petal'}
|
||||
|
||||
UNIQUE FEATURES:
|
||||
${(r.unique_features || ['Scalloped petal edges', 'Radial stitching', 'Soft padded construction']).map(f => `- ${f}`).join('\n')}
|
||||
|
||||
OVERALL: ${r.overall_description || 'A soft, comfortable pet recovery cone with flower-petal design.'}
|
||||
|
||||
CRITICAL PROHIBITIONS:
|
||||
- ❌ NO printed colorful patterns (solid color only unless reference shows otherwise)
|
||||
- ❌ NO hard plastic transparent cones
|
||||
- ❌ NO fully circular/closed shapes (must match reference C-opening)
|
||||
- ❌ NO random brand logos
|
||||
- ❌ MUST match reference image product EXACTLY
|
||||
`.trim();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractProductFeatures,
|
||||
buildGoldenDescription,
|
||||
VISION_EXTRACT_PROMPT
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user