Files
crm-ai/bridge/src/lead-intent.js
T

65 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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 };