require('dotenv').config(); const { OpenAI } = require('openai'); const db = require('./db'); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); async function getAiSuggestion(item) { try { const imageUrl = item.detail_response.image ? item.detail_response.image.imageUrl : null; const prompt = ` ROLE: Bạn là chuyên gia kiểm định listing eBay dựa trên dữ liệu + hình ảnh sản phẩm. MỤC TIÊU: Đánh giá độ tin cậy của listing dựa trên: - Độ khớp giữa HÌNH ẢNH và THÔNG TIN - Độ uy tín của seller - Độ hợp lý tổng thể (price / data consistency) INPUT: [SEARCH TARGET] - Part Number: ${item.partNumber || "N/A"} - Expected Specs: ${item.specs || "N/A"} [EBAY DATA] - Title: ${item.title} - Price: ${item.price} - Seller: ${item.detail_response?.seller?.username} - Feedback Score: ${item.detail_response?.seller?.feedbackScore} - Positive %: ${item.detail_response?.seller?.feedbackPercent} [DETAIL DATA] - Brand: ${item.detail_response?.brand || "N/A"} - Aspects: ${JSON.stringify(item.detail_response?.localizedAspects || {})} TASK (QUAN TRỌNG): Thực hiện các bước sau (ngầm, không output): 1. Nếu có ảnh: - OCR label / text chính (model, part number, specs) - So sánh với Title + Aspects 2. Check mismatch: - Sai model / sai part number / sai specs - Ảnh không liên quan (stock image / generic) 3. Đánh giá seller: - Feedback < 95% hoặc score thấp => rủi ro 4. Đánh giá tổng thể: - Consistency + seller + price OUTPUT RULE (BẮT BUỘC): - Chỉ 1 câu duy nhất kèm số điểm đánh giá từ 1-10 với độ khớp - Không markdown, không xuống dòng - Không giải thích dài - Format: Nếu tốt: "Hãy mua, dữ liệu và hình ảnh khớp, seller uy tín. (Điểm: {{điểm}})" Nếu có rủi ro: "Cẩn thận, {{lý do chính ngắn gọn}}. (Điểm: {{điểm}})" Nếu rất tệ: "Không nên mua, {{lý do rõ ràng}}. (Điểm: {{điểm}})" Ví dụ lý do: - label không khớp title - sai part number - ảnh generic / không phải sản phẩm thật - seller feedback thấp OUTPUT: `; console.log(prompt); const messages = [ { role: "system", content: "Bạn là trợ lý AI chuyên thẩm định eBay bằng hình ảnh và dữ liệu. Chỉ trả về 1 câu kết luận ngắn gọn." } ]; if (imageUrl) { messages.push({ role: "user", content: [ { type: "text", text: prompt }, { type: "image_url", image_url: { url: imageUrl, detail: "high" } } ] }); } else { messages.push({ role: "user", content: prompt }); } const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: messages, max_tokens: 150, temperature: 0.2 }); return { suggestion: response.choices[0].message.content.trim(), usage: response.usage.total_tokens }; } catch (error) { console.error(`AI Error for item ${item.id}:`, error.message); return { suggestion: "Lỗi khi gọi AI.", usage: 0 }; } } async function runAiSuggestions() { console.log("Bat dau chay AI Suggestions..."); const items = db.getMissingAiSuggestionItems(); console.log(`Tim thay ${items.length} item can check AI.`); let totalTokens = 0; const batchSize = 10; for (let i = 0; i < items.length; i += batchSize) { const batch = items.slice(i, i + batchSize); const results = await Promise.all(batch.map(async item => { console.log(`Dang check AI cho item: ${item.id}`); const { suggestion, usage } = await getAiSuggestion(item); db.updateAiSuggestion(item.id, suggestion); console.log(`Ket qua AI cho ${item.id}: ${suggestion} (Tokens: ${usage})`); return usage; })); totalTokens += results.reduce((a, b) => a + b, 0); // Minimal delay between batches to respect rate limits if (i + batchSize < items.length) { await new Promise(r => setTimeout(r, 500)); } } console.log(`Hoan thanh AI Suggestions. Total tokens: ${totalTokens}`); return totalTokens; } module.exports = { runAiSuggestions, getAiSuggestion }; // Allow standalone run if (require.main === module) { runAiSuggestions().catch(console.error); }