65 lines
3.7 KiB
JavaScript
65 lines
3.7 KiB
JavaScript
'use strict';
|
||
|
||
// 购买意向识别 + post-LLM 追问兜底(不依赖 LLM 自觉)。
|
||
const PRODUCT_RE = /(戒指|项链|耳环|耳钉|手链|脚链|珠宝|饰品|发饰|包|服装|连衣裙|ring|necklace|earring|bracelet|anklet|jewelry|jewellery|accessor|hair ?clip|bag|dress|clothing|pendant|charm)/i;
|
||
const QUANTITY_RE = /(\d+\s*(pcs?|pieces?|units?|sets?|pairs?|kilos?|kg|thousand|k\b|件|条|只|个|套|对)|moq|bulk|wholesale|批发|采购)/i;
|
||
|
||
const ASK = {
|
||
zh: '\n\n顺便问一下,方便告诉我贵公司名称和联系邮箱吗?这样我们好为您建立批发账户并跟进需求。',
|
||
fr: '\n\nAu fait, pourriez-vous me donner le nom de votre société et un email de contact ? Cela nous permettra de créer votre compte grossiste et de suivre votre demande.',
|
||
de: '\n\nDürfte ich noch den Namen Ihrer Firma und eine Kontakt-E-Mail haben? So können wir Ihr Großhandelskonto anlegen und Ihre Anfrage verfolgen.',
|
||
it: '\n\nA proposito, potrebbe darmi il nome della sua azienda e una email di contatto? Così possiamo creare il suo account all\'ingrosso e seguire la richiesta.',
|
||
es: '\n\nPor cierto, ¿podría darme el nombre de su empresa y un email de contacto? Así podemos crear su cuenta mayorista y dar seguimiento.',
|
||
nl: '\n\nTrouwens, mag ik de naam van uw bedrijf en een contact-e-mail hebben? Zo kunnen we uw groothandelsaccount aanmaken en uw aanvraag opvolgen.',
|
||
en: '\n\nBy the way, could you share your company name and a contact email? That way we can set up your wholesale account and follow up on your request.',
|
||
};
|
||
|
||
// 公司/联系人信号:客户主动报公司名(含法律后缀)、邮箱、电话、联系人等。
|
||
// 命中它但数量单位不规整(如 "500 stainless steel rings")时,也要触发 lead 抽取兜底,
|
||
// 否则 LLM 漏调 collect_info 时槽位会为空,商机就建不出来。
|
||
const LEAD_SIGNAL_RE = /(@|\+?\d[\d\s().-]{6,}\d|gmbh|b\.?\s?v\.?\b|ltd\b|llc\b|inc\b|sarl|srl\b|limited\b|company|corporation|we are|we're|our company|contact\s*(?:name|person)?\s*[::]|\u516c\u53f8|\u8054\u7cfb\u4eba|\u91c7\u8d2d|wholesale|bulk)/i;
|
||
|
||
function hasBuyingIntent(text) {
|
||
const value = String(text || '');
|
||
return PRODUCT_RE.test(value) && QUANTITY_RE.test(value);
|
||
}
|
||
|
||
function looksLikeLead(text) {
|
||
const value = String(text || '');
|
||
return PRODUCT_RE.test(value) && LEAD_SIGNAL_RE.test(value);
|
||
}
|
||
|
||
function missingLeadContacts(slots = {}, knownEmail = '') {
|
||
const missing = [];
|
||
if (!slots.company) missing.push('company');
|
||
if (!slots.email && !slots.phone && !slots.contactName && !knownEmail) missing.push('contact');
|
||
return missing;
|
||
}
|
||
|
||
function detectLanguage(text) {
|
||
const q = String(text || '');
|
||
if (/[\u3400-\u9fff]/.test(q)) return 'zh';
|
||
if (/\b(und|nicht|für|mit|bitte|haben|preis|menge|lieferung)\b/i.test(q)) return 'de';
|
||
if (/\b(et|pour|avec|bonjour|prix|quantité|livraison|merci)\b/i.test(q)) return 'fr';
|
||
if (/\b(e|per|con|prezzo|quantità|spedizione|grazie)\b/i.test(q)) return 'it';
|
||
if (/\b(y|para|con|precio|cantidad|envío|gracias)\b/i.test(q)) return 'es';
|
||
if (/\b(en|voor|met|prijs|aantal|levering|bedankt)\b/i.test(q)) return 'nl';
|
||
return 'en';
|
||
}
|
||
|
||
function buildLeadFollowup(lang) {
|
||
return ASK[lang] || ASK.en;
|
||
}
|
||
|
||
function alreadyAsksForLead(reply) {
|
||
return /(company|société|entreprise|firma|unternehmen|azienda|empresa|bedrijf|email|e-mail|mail|kontakt|contact|公司|邮箱|邮件)/i.test(String(reply || ''));
|
||
}
|
||
|
||
function appendLeadFollowup(reply, lang) {
|
||
const text = String(reply || '').replace(/\s+$/, '');
|
||
if (!text || alreadyAsksForLead(text)) return text;
|
||
return text + buildLeadFollowup(lang);
|
||
}
|
||
|
||
module.exports = { hasBuyingIntent, looksLikeLead, LEAD_SIGNAL_RE, missingLeadContacts, detectLanguage, buildLeadFollowup, alreadyAsksForLead, appendLeadFollowup };
|