feat(kb-routing): expand 5-way keyword routing coverage
This commit is contained in:
@@ -3,18 +3,14 @@
|
|||||||
* 记忆最近的产品/主题关键词,用于追问理解
|
* 记忆最近的产品/主题关键词,用于追问理解
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const { TRACKER_KEYWORD_GROUPS, buildKeywordRegex } = require('./knowledgeKeywords');
|
||||||
|
|
||||||
class ContextKeywordTracker {
|
class ContextKeywordTracker {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.sessionKeywords = new Map();
|
this.sessionKeywords = new Map();
|
||||||
this.TTL = 30 * 60 * 1000;
|
this.TTL = 30 * 60 * 1000;
|
||||||
this.MAX_KEYWORDS = 8;
|
this.MAX_KEYWORDS = 8;
|
||||||
this.keywordPatterns = [
|
this.keywordPatterns = TRACKER_KEYWORD_GROUPS.map((group) => buildKeywordRegex(group, 'gi'));
|
||||||
/(一成系统|Ai众享|AI众享|数字化工作室|盛咖学愿|数字化运营|数字化经营|数字化营销|数字化创业|数字化事业)/gi,
|
|
||||||
/(PM-FitLine|PM细胞营养素|细胞营养素|德国PM|PM公司)/gi,
|
|
||||||
/(小红产品|大白产品|小白产品|Activize Oxyplus|Activize|Basics|Restorate|儿童倍适|Basic Power|CitrusCare|NutriSunny|Omega)/gi,
|
|
||||||
/(肽美|艾特维|德丽|德维|宝丽|美固健|葡萄籽|白藜芦醇|益生菌|胶原蛋白肽|Q10)/gi,
|
|
||||||
/(NTC营养保送系统|火炉原理|阿育吠陀|招商|加盟|代理|事业机会|招商加盟|合作加盟|事业合作)/gi,
|
|
||||||
];
|
|
||||||
this.cleanupTimer = setInterval(() => this.cleanup(), this.TTL);
|
this.cleanupTimer = setInterval(() => this.cleanup(), this.TTL);
|
||||||
if (typeof this.cleanupTimer.unref === 'function') {
|
if (typeof this.cleanupTimer.unref === 'function') {
|
||||||
this.cleanupTimer.unref();
|
this.cleanupTimer.unref();
|
||||||
|
|||||||
948
test2/server/services/knowledgeKeywords.js
Normal file
948
test2/server/services/knowledgeKeywords.js
Normal file
@@ -0,0 +1,948 @@
|
|||||||
|
const COMPANY_ENTITY_KEYWORDS = [
|
||||||
|
'德国PM',
|
||||||
|
'德国PM公司',
|
||||||
|
'PM公司',
|
||||||
|
'PM国际',
|
||||||
|
'PM-International',
|
||||||
|
'PM-International AG',
|
||||||
|
'Rolf Sorg',
|
||||||
|
'邓白氏',
|
||||||
|
'AAA+',
|
||||||
|
'DSN',
|
||||||
|
'BFH',
|
||||||
|
'ELAB',
|
||||||
|
'科隆名单',
|
||||||
|
'GMP',
|
||||||
|
'Halal',
|
||||||
|
'宣明会',
|
||||||
|
'世界宣明会',
|
||||||
|
'斯派尔',
|
||||||
|
'Speyer',
|
||||||
|
'卢森堡',
|
||||||
|
'培安(烟台)日用品有限责任公司',
|
||||||
|
'培安烟台',
|
||||||
|
'烟台',
|
||||||
|
];
|
||||||
|
|
||||||
|
const SYSTEM_ENTITY_KEYWORDS = [
|
||||||
|
'一成系统',
|
||||||
|
'一成AI',
|
||||||
|
'一成Ai',
|
||||||
|
'Ai众享',
|
||||||
|
'AI众享',
|
||||||
|
'数字化工作室',
|
||||||
|
'盛咖学愿',
|
||||||
|
'三大平台',
|
||||||
|
'四大AI生态',
|
||||||
|
'四大Ai生态',
|
||||||
|
'四大生态',
|
||||||
|
'盟主社区',
|
||||||
|
'AI智能生产力',
|
||||||
|
'AI生产力',
|
||||||
|
'智能生产力',
|
||||||
|
'行动圈',
|
||||||
|
'批发式晋级',
|
||||||
|
'身未动,梦已成',
|
||||||
|
'身未动梦已成',
|
||||||
|
'零成本高效率',
|
||||||
|
'零成本高效率运行',
|
||||||
|
'赋能团队',
|
||||||
|
'团队赋能',
|
||||||
|
'团队发展',
|
||||||
|
'文化解析',
|
||||||
|
'故事分享',
|
||||||
|
'自我介绍',
|
||||||
|
'邀约话术',
|
||||||
|
'线上拓客',
|
||||||
|
'线上成交',
|
||||||
|
'陌生客户',
|
||||||
|
'陌生人沟通',
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRODUCT_ENTITY_KEYWORDS = [
|
||||||
|
'PM产品',
|
||||||
|
'PM-FitLine',
|
||||||
|
'FitLine',
|
||||||
|
'PM细胞营养素',
|
||||||
|
'细胞营养素',
|
||||||
|
'基础套装',
|
||||||
|
'基础三合一',
|
||||||
|
'三合一',
|
||||||
|
'基础二合一',
|
||||||
|
'二合一',
|
||||||
|
'小红产品',
|
||||||
|
'小红',
|
||||||
|
'艾特维',
|
||||||
|
'Activize Oxyplus',
|
||||||
|
'Activize Serum',
|
||||||
|
'Activize',
|
||||||
|
'小红精华液',
|
||||||
|
'大白产品',
|
||||||
|
'大白',
|
||||||
|
'倍适',
|
||||||
|
'Basics',
|
||||||
|
'Basic Power',
|
||||||
|
'PowerCocktail',
|
||||||
|
'小白产品',
|
||||||
|
'小白',
|
||||||
|
'维适多',
|
||||||
|
'Restorate',
|
||||||
|
'儿童倍适',
|
||||||
|
'PowerCocktail Junior',
|
||||||
|
'NTC营养保送系统',
|
||||||
|
'NTC',
|
||||||
|
'Nutrient Transport Concept',
|
||||||
|
'火炉原理',
|
||||||
|
'暖炉原理',
|
||||||
|
'阿育吠陀',
|
||||||
|
'Ayurveda',
|
||||||
|
'Med Dental+',
|
||||||
|
'草本护理牙膏',
|
||||||
|
'Men Face',
|
||||||
|
'全效男士护肤抗衰乳霜',
|
||||||
|
'CC-Cell',
|
||||||
|
'CC-Cell胶囊',
|
||||||
|
'CC-Cell乳霜',
|
||||||
|
'CC套装',
|
||||||
|
'CC胶囊',
|
||||||
|
'D-Drink',
|
||||||
|
'小绿排毒饮',
|
||||||
|
'14天排毒D饮料Plus',
|
||||||
|
'ProShape Amino',
|
||||||
|
'ProShape® Amino',
|
||||||
|
'氨基酸',
|
||||||
|
'支链氨基酸',
|
||||||
|
'BCAA',
|
||||||
|
'MEN+',
|
||||||
|
'Men+',
|
||||||
|
'倍力健 MEN+',
|
||||||
|
'倍力健',
|
||||||
|
'小黑',
|
||||||
|
'Herbal Tea',
|
||||||
|
'草本茶',
|
||||||
|
'Hair+',
|
||||||
|
'med Hair+',
|
||||||
|
'口服发宝',
|
||||||
|
'外用发健',
|
||||||
|
'发宝',
|
||||||
|
'Fitness-Drink',
|
||||||
|
'运动饮料健康饮品',
|
||||||
|
'运动饮料',
|
||||||
|
'健康饮品',
|
||||||
|
'TopShape',
|
||||||
|
'孅萃TopShape纤萃减肥',
|
||||||
|
'纤萃减肥',
|
||||||
|
'乐活50+',
|
||||||
|
'Generation 50+',
|
||||||
|
'Apple Antioxy',
|
||||||
|
'苹果细胞抗氧素',
|
||||||
|
'Antioxy',
|
||||||
|
'Zellschutz',
|
||||||
|
'细胞抗氧素',
|
||||||
|
'胶原蛋白肽',
|
||||||
|
'胶原蛋白',
|
||||||
|
'Women+',
|
||||||
|
'乐活奶昔',
|
||||||
|
'乐活',
|
||||||
|
'乳清蛋白',
|
||||||
|
'蛋白粉',
|
||||||
|
'乳酪煲',
|
||||||
|
'乳酪饮品',
|
||||||
|
'乳酪',
|
||||||
|
'IB5',
|
||||||
|
'口腔免疫喷雾',
|
||||||
|
'Q10',
|
||||||
|
'辅酵素',
|
||||||
|
'Q10辅酵素氧修护',
|
||||||
|
'关节套装',
|
||||||
|
'关节舒缓',
|
||||||
|
'男士乳霜',
|
||||||
|
'男士护肤',
|
||||||
|
'去角质',
|
||||||
|
'面膜',
|
||||||
|
'叶黄素',
|
||||||
|
'葡萄籽',
|
||||||
|
'白藜芦醇',
|
||||||
|
'益生菌',
|
||||||
|
'肽美',
|
||||||
|
'德丽',
|
||||||
|
'德维',
|
||||||
|
'宝丽',
|
||||||
|
'美固健',
|
||||||
|
'CitrusCare',
|
||||||
|
'NutriSunny',
|
||||||
|
'Omega',
|
||||||
|
'Young Care',
|
||||||
|
];
|
||||||
|
|
||||||
|
const BUSINESS_ENTITY_KEYWORDS = [
|
||||||
|
'PM事业',
|
||||||
|
'做PM',
|
||||||
|
'加入PM',
|
||||||
|
'招商合作',
|
||||||
|
'招商与代理',
|
||||||
|
'招商稿',
|
||||||
|
'招商',
|
||||||
|
'招募',
|
||||||
|
'代理',
|
||||||
|
'代理商',
|
||||||
|
'代理政策',
|
||||||
|
'加盟',
|
||||||
|
'招商加盟',
|
||||||
|
'合作加盟',
|
||||||
|
'事业合作',
|
||||||
|
'事业机会',
|
||||||
|
'创业',
|
||||||
|
'培训新人起步三关',
|
||||||
|
'起步三关',
|
||||||
|
'培训打造精品会议具体如下',
|
||||||
|
'精品会议',
|
||||||
|
'会议组织',
|
||||||
|
'培训成长上总裁',
|
||||||
|
'成长上总裁',
|
||||||
|
];
|
||||||
|
|
||||||
|
const ROUTE_TOPIC_KEYWORDS = [
|
||||||
|
// 公司相关
|
||||||
|
'公司介绍',
|
||||||
|
'公司背景',
|
||||||
|
'公司实力',
|
||||||
|
'公司地址',
|
||||||
|
'公司电话',
|
||||||
|
'联系方式',
|
||||||
|
'总部',
|
||||||
|
'分公司',
|
||||||
|
'公司成立',
|
||||||
|
'公司历史',
|
||||||
|
'公司规模',
|
||||||
|
'全球布局',
|
||||||
|
'信用评级',
|
||||||
|
'行业排名',
|
||||||
|
'获奖',
|
||||||
|
'慈善',
|
||||||
|
'慈善事业',
|
||||||
|
'社会责任',
|
||||||
|
'不上市',
|
||||||
|
'汽车奖励',
|
||||||
|
'退休金',
|
||||||
|
'旅行',
|
||||||
|
'福利',
|
||||||
|
'企业性质',
|
||||||
|
'发展历程',
|
||||||
|
// 产品相关
|
||||||
|
'产品介绍',
|
||||||
|
'产品说明',
|
||||||
|
'产品推荐',
|
||||||
|
'产品有哪些',
|
||||||
|
'产品列表',
|
||||||
|
'产品图片',
|
||||||
|
'产品外观',
|
||||||
|
// 语音口语化触发词
|
||||||
|
'你们公司',
|
||||||
|
'你们的公司',
|
||||||
|
'你们产品',
|
||||||
|
'你们的产品',
|
||||||
|
'你们那个',
|
||||||
|
'咱们公司',
|
||||||
|
'咱们产品',
|
||||||
|
'咱们的',
|
||||||
|
'你们这个',
|
||||||
|
'你们卖的',
|
||||||
|
'你们的东西',
|
||||||
|
'这个东西',
|
||||||
|
'这东西',
|
||||||
|
'那玩意',
|
||||||
|
// 口语化动词
|
||||||
|
'说说',
|
||||||
|
'讲讲',
|
||||||
|
'介绍介绍',
|
||||||
|
'查查',
|
||||||
|
'帮我查',
|
||||||
|
'帮我问',
|
||||||
|
'帮我看看',
|
||||||
|
'有啥用',
|
||||||
|
'咱吃',
|
||||||
|
'咱用',
|
||||||
|
'咱回事',
|
||||||
|
'咱样',
|
||||||
|
'啥意思',
|
||||||
|
'有啥',
|
||||||
|
'咱办',
|
||||||
|
// 营养品/保健品通用词
|
||||||
|
'营养素',
|
||||||
|
'营养品',
|
||||||
|
'保健品',
|
||||||
|
'保健食品',
|
||||||
|
'营养补充',
|
||||||
|
'营养剂',
|
||||||
|
'膏营养',
|
||||||
|
// 直销事业
|
||||||
|
'直销',
|
||||||
|
'直销公司',
|
||||||
|
'直销事业',
|
||||||
|
// 健康/症状/功效相关(触发KB总入口)
|
||||||
|
'排毒',
|
||||||
|
'排毒产品',
|
||||||
|
'减肥',
|
||||||
|
'减肥产品',
|
||||||
|
'瘦身',
|
||||||
|
'护肤',
|
||||||
|
'护肤品',
|
||||||
|
'护发',
|
||||||
|
'脱发',
|
||||||
|
'掉发',
|
||||||
|
'头发',
|
||||||
|
'牙膏',
|
||||||
|
'喷雾',
|
||||||
|
'关节痛',
|
||||||
|
'关节',
|
||||||
|
'眼睛',
|
||||||
|
'视力',
|
||||||
|
'叶黄素',
|
||||||
|
'抗氧化',
|
||||||
|
'抗衰',
|
||||||
|
'抗衰老',
|
||||||
|
'胶原蛋白',
|
||||||
|
'运动饮料',
|
||||||
|
'免疫力',
|
||||||
|
'能量',
|
||||||
|
'抗疲劳',
|
||||||
|
'疲劳',
|
||||||
|
'睡眠',
|
||||||
|
'失眠',
|
||||||
|
'消化',
|
||||||
|
'肠胃',
|
||||||
|
'便秘',
|
||||||
|
'皮肤',
|
||||||
|
'美容',
|
||||||
|
'美白',
|
||||||
|
'祠斑',
|
||||||
|
'祠痘',
|
||||||
|
'痘痘',
|
||||||
|
'补钹',
|
||||||
|
'补铁',
|
||||||
|
'补血',
|
||||||
|
'骨密度',
|
||||||
|
'补钙',
|
||||||
|
// 特定人群(触发FAQ或产品库)
|
||||||
|
'孕妇',
|
||||||
|
'哺乳期',
|
||||||
|
'怀孕',
|
||||||
|
'孕期',
|
||||||
|
'儿童',
|
||||||
|
'小孩',
|
||||||
|
'孩子',
|
||||||
|
'老人',
|
||||||
|
'老年人',
|
||||||
|
'过敏',
|
||||||
|
'过敏体质',
|
||||||
|
// 事业/招商
|
||||||
|
'事业',
|
||||||
|
'做这个事业',
|
||||||
|
'怎么做这个',
|
||||||
|
'怎么赚钱',
|
||||||
|
'能赚钱吗',
|
||||||
|
'收入',
|
||||||
|
'奖金',
|
||||||
|
'奖金制度',
|
||||||
|
'纪念品',
|
||||||
|
'服务商',
|
||||||
|
'合作伙伴',
|
||||||
|
'为什么选择德国PM',
|
||||||
|
// 合法性/传销相关
|
||||||
|
'正规性',
|
||||||
|
'合法性',
|
||||||
|
'传销',
|
||||||
|
'骗局',
|
||||||
|
'骗子',
|
||||||
|
'是不是传销',
|
||||||
|
'直销还是传销',
|
||||||
|
'合不合法',
|
||||||
|
'正不正规',
|
||||||
|
'层级分销',
|
||||||
|
'非法集资',
|
||||||
|
'拉人头',
|
||||||
|
'发展下线',
|
||||||
|
'报单',
|
||||||
|
'人头费',
|
||||||
|
'安全吗',
|
||||||
|
'合规吗',
|
||||||
|
'有许可证吗',
|
||||||
|
// 好转反应/副作用
|
||||||
|
'好转反应',
|
||||||
|
'整应反应',
|
||||||
|
'排毒反应',
|
||||||
|
'副作用',
|
||||||
|
'不良反应',
|
||||||
|
'皮肤发痒',
|
||||||
|
'皮肤微痒',
|
||||||
|
// 促销活动
|
||||||
|
'促销活动',
|
||||||
|
'活动分数',
|
||||||
|
'5+1活动分数',
|
||||||
|
'5+1',
|
||||||
|
// 产品使用方法
|
||||||
|
'怎么吃',
|
||||||
|
'怎么用',
|
||||||
|
'怎么服用',
|
||||||
|
'服用方法',
|
||||||
|
'吃法',
|
||||||
|
'用法',
|
||||||
|
'用量',
|
||||||
|
'搭配',
|
||||||
|
'空腹吃',
|
||||||
|
'饭前吃',
|
||||||
|
'饭后吃',
|
||||||
|
'温水冲',
|
||||||
|
'冷水冲',
|
||||||
|
'一天吃几次',
|
||||||
|
'一次吃多少',
|
||||||
|
// 产品属性
|
||||||
|
'功效',
|
||||||
|
'作用',
|
||||||
|
'成分',
|
||||||
|
'原料',
|
||||||
|
'配方',
|
||||||
|
'多少钱',
|
||||||
|
'价格',
|
||||||
|
'适合谁',
|
||||||
|
'适用人群',
|
||||||
|
'区别',
|
||||||
|
'哪个好',
|
||||||
|
'多久见效',
|
||||||
|
'哪里买',
|
||||||
|
'怎么买',
|
||||||
|
'保质期',
|
||||||
|
'储存',
|
||||||
|
// 效果/评价相关
|
||||||
|
'效果怎么样',
|
||||||
|
'有效果吗',
|
||||||
|
'有没有用',
|
||||||
|
'好不好',
|
||||||
|
'靠谱吗',
|
||||||
|
'值得买吗',
|
||||||
|
'值得做吗',
|
||||||
|
'真的有用吗',
|
||||||
|
'真的假的',
|
||||||
|
'有科学依据吗',
|
||||||
|
// 加入/参与
|
||||||
|
'怎么加入',
|
||||||
|
'如何加入',
|
||||||
|
'怎么报名',
|
||||||
|
'怎么参与',
|
||||||
|
'怎么做',
|
||||||
|
'如何开始',
|
||||||
|
// 科普/培训/认证
|
||||||
|
'科普',
|
||||||
|
'误区',
|
||||||
|
'认证',
|
||||||
|
'检测',
|
||||||
|
'检测报告',
|
||||||
|
'安全认证',
|
||||||
|
'培训',
|
||||||
|
'新人',
|
||||||
|
'起步',
|
||||||
|
'成长',
|
||||||
|
// 疾病/健康状况(触发FAQ)
|
||||||
|
'高血压',
|
||||||
|
'糖尿病',
|
||||||
|
'胆固醇',
|
||||||
|
'心脏病',
|
||||||
|
'肾病',
|
||||||
|
'肝病',
|
||||||
|
'痛风',
|
||||||
|
'贫血',
|
||||||
|
'肥胖',
|
||||||
|
// 能不能吃/用类问题
|
||||||
|
'能吃吗',
|
||||||
|
'可以吃吗',
|
||||||
|
'能喝吗',
|
||||||
|
'可以喝吗',
|
||||||
|
'能用吗',
|
||||||
|
'可以用吗',
|
||||||
|
'一起吃',
|
||||||
|
'同时吃',
|
||||||
|
'混着吃',
|
||||||
|
'搭配吃',
|
||||||
|
'吃药',
|
||||||
|
'药物',
|
||||||
|
];
|
||||||
|
|
||||||
|
const CANONICAL_KNOWLEDGE_TERMS = [
|
||||||
|
'一成系统',
|
||||||
|
'德国PM',
|
||||||
|
'PM-FitLine',
|
||||||
|
'FitLine',
|
||||||
|
'PM细胞营养素',
|
||||||
|
'NTC营养保送系统',
|
||||||
|
'Activize Oxyplus',
|
||||||
|
'Basics',
|
||||||
|
'Restorate',
|
||||||
|
'儿童倍适',
|
||||||
|
'火炉原理',
|
||||||
|
'阿育吠陀',
|
||||||
|
];
|
||||||
|
|
||||||
|
function escapeRegExp(text) {
|
||||||
|
return String(text || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueKeywords(keywords) {
|
||||||
|
const seen = new Set();
|
||||||
|
const result = [];
|
||||||
|
for (const keyword of Array.isArray(keywords) ? keywords : []) {
|
||||||
|
const normalized = String(keyword || '').trim();
|
||||||
|
if (!normalized) continue;
|
||||||
|
const lower = normalized.toLowerCase();
|
||||||
|
if (seen.has(lower)) continue;
|
||||||
|
seen.add(lower);
|
||||||
|
result.push(normalized);
|
||||||
|
}
|
||||||
|
return result.sort((a, b) => b.length - a.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKeywordRegex(keywords, flags = 'i') {
|
||||||
|
const terms = uniqueKeywords(keywords).map(escapeRegExp);
|
||||||
|
return new RegExp(`(?:${terms.join('|')})`, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dedupeMatches(matches) {
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
for (const item of Array.isArray(matches) ? matches : []) {
|
||||||
|
const normalized = String(item || '').trim();
|
||||||
|
if (!normalized) continue;
|
||||||
|
const lower = normalized.toLowerCase();
|
||||||
|
if (seen.has(lower)) continue;
|
||||||
|
seen.add(lower);
|
||||||
|
result.push(normalized);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KNOWLEDGE_ENTITY_KEYWORDS = uniqueKeywords([
|
||||||
|
...COMPANY_ENTITY_KEYWORDS,
|
||||||
|
...SYSTEM_ENTITY_KEYWORDS,
|
||||||
|
...PRODUCT_ENTITY_KEYWORDS,
|
||||||
|
...BUSINESS_ENTITY_KEYWORDS,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const COMPANY_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
...COMPANY_ENTITY_KEYWORDS,
|
||||||
|
'公司介绍',
|
||||||
|
'公司背景',
|
||||||
|
'公司实力',
|
||||||
|
'公司地址',
|
||||||
|
'公司电话',
|
||||||
|
'联系方式',
|
||||||
|
'总部',
|
||||||
|
'分公司',
|
||||||
|
'公司成立',
|
||||||
|
'公司历史',
|
||||||
|
'公司规模',
|
||||||
|
'全球布局',
|
||||||
|
'全球业务',
|
||||||
|
'信用评级',
|
||||||
|
'行业排名',
|
||||||
|
'获奖',
|
||||||
|
'获奖情况',
|
||||||
|
'慈善',
|
||||||
|
'慈善事业',
|
||||||
|
'社会责任',
|
||||||
|
'不上市',
|
||||||
|
'为什么不上市',
|
||||||
|
'汽车奖励',
|
||||||
|
'退休金',
|
||||||
|
'旅行奖励',
|
||||||
|
'旅行',
|
||||||
|
'福利',
|
||||||
|
'企业性质',
|
||||||
|
'发展历程',
|
||||||
|
'发展史',
|
||||||
|
'创始人',
|
||||||
|
'创办人',
|
||||||
|
'老板',
|
||||||
|
'总裁',
|
||||||
|
'年营业额',
|
||||||
|
'营收',
|
||||||
|
'市场份额',
|
||||||
|
'品牌实力',
|
||||||
|
'品牌介绍',
|
||||||
|
'各国地址',
|
||||||
|
'各国电话',
|
||||||
|
'哪个国家',
|
||||||
|
'哪些国家',
|
||||||
|
'多少个国家',
|
||||||
|
'业务覆盖',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const SYSTEM_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
...SYSTEM_ENTITY_KEYWORDS,
|
||||||
|
'故事分享',
|
||||||
|
'自我介绍',
|
||||||
|
'邀约话术',
|
||||||
|
'AI赋能',
|
||||||
|
'AI落地',
|
||||||
|
'转观念',
|
||||||
|
'如何开展业务',
|
||||||
|
'开展业务',
|
||||||
|
'邀约客户',
|
||||||
|
'邀约方法',
|
||||||
|
'邀约技巧',
|
||||||
|
'邀约模板',
|
||||||
|
'线上拓客',
|
||||||
|
'线上成交',
|
||||||
|
'陌生客户',
|
||||||
|
'陌生人沟通',
|
||||||
|
'团队发展',
|
||||||
|
'团队赋能',
|
||||||
|
'团队建设',
|
||||||
|
'团队管理',
|
||||||
|
'批发式晋级',
|
||||||
|
'系统文化',
|
||||||
|
'系统介绍',
|
||||||
|
'系统是什么',
|
||||||
|
'一成是什么',
|
||||||
|
'数字化',
|
||||||
|
'智能工具',
|
||||||
|
'AI工具',
|
||||||
|
'AI众享是什么',
|
||||||
|
'盟主社区',
|
||||||
|
'行动圈',
|
||||||
|
'零成本',
|
||||||
|
'高效率',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const PRODUCT_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
...PRODUCT_ENTITY_KEYWORDS,
|
||||||
|
'产品介绍',
|
||||||
|
'产品说明',
|
||||||
|
'产品推荐',
|
||||||
|
'产品有哪些',
|
||||||
|
'产品列表',
|
||||||
|
'产品图片',
|
||||||
|
'产品外观',
|
||||||
|
'搭配',
|
||||||
|
'功效',
|
||||||
|
'作用',
|
||||||
|
'成分',
|
||||||
|
'原料',
|
||||||
|
'配方',
|
||||||
|
'怎么吃',
|
||||||
|
'怎么服用',
|
||||||
|
'服用方法',
|
||||||
|
'吃法',
|
||||||
|
'用法',
|
||||||
|
'用量',
|
||||||
|
'适合谁',
|
||||||
|
'适用人群',
|
||||||
|
'区别',
|
||||||
|
'哪个好',
|
||||||
|
'多少钱',
|
||||||
|
'价格',
|
||||||
|
'多久见效',
|
||||||
|
'哪里买',
|
||||||
|
'怎么买',
|
||||||
|
'图片',
|
||||||
|
'外观',
|
||||||
|
'规格',
|
||||||
|
'套装',
|
||||||
|
'暖炉原理',
|
||||||
|
'火炉原理',
|
||||||
|
'NTC',
|
||||||
|
'活动分数',
|
||||||
|
'专利',
|
||||||
|
'细胞营养',
|
||||||
|
// 产品功效词
|
||||||
|
'免疫力',
|
||||||
|
'能量',
|
||||||
|
'抗疲劳',
|
||||||
|
'睡眠',
|
||||||
|
'消化',
|
||||||
|
'排毒',
|
||||||
|
'护肤',
|
||||||
|
'护发',
|
||||||
|
'脱发',
|
||||||
|
'抗氧化',
|
||||||
|
'抗衰老',
|
||||||
|
'美容',
|
||||||
|
'美白',
|
||||||
|
'祠斑',
|
||||||
|
'祠痘',
|
||||||
|
'瘦身',
|
||||||
|
'减肥',
|
||||||
|
'关节',
|
||||||
|
'骨密度',
|
||||||
|
'胶原蛋白',
|
||||||
|
// 产品使用方式
|
||||||
|
'空腹吃',
|
||||||
|
'饭前吃',
|
||||||
|
'饭后吃',
|
||||||
|
'温水冲',
|
||||||
|
'冷水冲',
|
||||||
|
'一天吃几次',
|
||||||
|
'一次吃多少',
|
||||||
|
'保质期',
|
||||||
|
'储存',
|
||||||
|
'冷藏',
|
||||||
|
// 产品类别
|
||||||
|
'营养素',
|
||||||
|
'营养品',
|
||||||
|
'保健品',
|
||||||
|
'保健食品',
|
||||||
|
'营养补充',
|
||||||
|
// 产品对比
|
||||||
|
'哪个产品',
|
||||||
|
'推荐产品',
|
||||||
|
'入门产品',
|
||||||
|
'必买产品',
|
||||||
|
'明星产品',
|
||||||
|
'最畅销',
|
||||||
|
'热卖',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const FAQ_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
// 疑问词
|
||||||
|
'怎么办',
|
||||||
|
'为什么',
|
||||||
|
'能不能',
|
||||||
|
'有没有',
|
||||||
|
'怎么回事',
|
||||||
|
'咱回事',
|
||||||
|
'正常吗',
|
||||||
|
'安全吗',
|
||||||
|
'有害吗',
|
||||||
|
'有毒吗',
|
||||||
|
// 好转反应/副作用
|
||||||
|
'好转反应',
|
||||||
|
'整应反应',
|
||||||
|
'排毒反应',
|
||||||
|
'副作用',
|
||||||
|
'不良反应',
|
||||||
|
'不舒服',
|
||||||
|
'身体反应',
|
||||||
|
'身体不适',
|
||||||
|
// 症状词
|
||||||
|
'皮肤发痒',
|
||||||
|
'皮肤微痒',
|
||||||
|
'发痒',
|
||||||
|
'头晕',
|
||||||
|
'便秘',
|
||||||
|
'腹泻',
|
||||||
|
'拉肚子',
|
||||||
|
'恶心',
|
||||||
|
'呕吐',
|
||||||
|
'长痘',
|
||||||
|
'出疹子',
|
||||||
|
'红肿',
|
||||||
|
'口干',
|
||||||
|
'上火',
|
||||||
|
'胃痛',
|
||||||
|
'胃不舒服',
|
||||||
|
'抽筋',
|
||||||
|
'浮肿',
|
||||||
|
'心悸',
|
||||||
|
'气短',
|
||||||
|
'失眠',
|
||||||
|
'多梦',
|
||||||
|
'发热',
|
||||||
|
'出汗',
|
||||||
|
'拉肃子',
|
||||||
|
// 疾病/健康状况
|
||||||
|
'高血压',
|
||||||
|
'糖尿病',
|
||||||
|
'胆固醇',
|
||||||
|
'心脏病',
|
||||||
|
'肾病',
|
||||||
|
'肝病',
|
||||||
|
'痛风',
|
||||||
|
'贫血',
|
||||||
|
'肥胖',
|
||||||
|
'甲亢',
|
||||||
|
'甲减',
|
||||||
|
'胃炎',
|
||||||
|
'肠炎',
|
||||||
|
'哮喘',
|
||||||
|
'湿疹',
|
||||||
|
'过敏',
|
||||||
|
'结石',
|
||||||
|
'癌症',
|
||||||
|
'肿瘤',
|
||||||
|
'水肿',
|
||||||
|
'尿酸',
|
||||||
|
// 特定人群问题
|
||||||
|
'孕妇',
|
||||||
|
'哺乳期',
|
||||||
|
'怀孕',
|
||||||
|
'孕期',
|
||||||
|
'儿童',
|
||||||
|
'小孩',
|
||||||
|
'孩子',
|
||||||
|
'老人',
|
||||||
|
'老年人',
|
||||||
|
'手术后',
|
||||||
|
'化疗',
|
||||||
|
'放疗',
|
||||||
|
'术后',
|
||||||
|
'月子',
|
||||||
|
'吃药',
|
||||||
|
'药物',
|
||||||
|
'与药物',
|
||||||
|
'跟药一起',
|
||||||
|
'问答',
|
||||||
|
'问题',
|
||||||
|
// 能不能吃/用类问题
|
||||||
|
'能吃吗',
|
||||||
|
'可以吃吗',
|
||||||
|
'能喝吗',
|
||||||
|
'可以喝吗',
|
||||||
|
'能用吗',
|
||||||
|
'可以用吗',
|
||||||
|
'一起吃',
|
||||||
|
'同时吃',
|
||||||
|
'混着吃',
|
||||||
|
'搭配吃',
|
||||||
|
'能混合吗',
|
||||||
|
// 效果评价
|
||||||
|
'有效果吗',
|
||||||
|
'有没有用',
|
||||||
|
'好不好',
|
||||||
|
'靠谱吗',
|
||||||
|
'真的有用吗',
|
||||||
|
'真的假的',
|
||||||
|
'有科学依据吗',
|
||||||
|
'多久见效',
|
||||||
|
'多久能看到效果',
|
||||||
|
'吃多久',
|
||||||
|
'要吃多久',
|
||||||
|
// 价格/性价比
|
||||||
|
'贵不贵',
|
||||||
|
'性价比',
|
||||||
|
'划算吗',
|
||||||
|
'值得买吗',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const SCIENCE_TRAINING_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
...BUSINESS_ENTITY_KEYWORDS,
|
||||||
|
// 科普知识
|
||||||
|
'科普',
|
||||||
|
'误区',
|
||||||
|
'营养误区',
|
||||||
|
'健康误区',
|
||||||
|
'保健品误区',
|
||||||
|
'认证',
|
||||||
|
'检测',
|
||||||
|
'检测报告',
|
||||||
|
'安全认证',
|
||||||
|
'GMP认证',
|
||||||
|
'Halal认证',
|
||||||
|
'科学依据',
|
||||||
|
'临床试验',
|
||||||
|
'研究报告',
|
||||||
|
// 招商相关
|
||||||
|
'招商',
|
||||||
|
'招商稿',
|
||||||
|
'代理',
|
||||||
|
'加盟',
|
||||||
|
'加入',
|
||||||
|
'怎么加入',
|
||||||
|
'如何加入',
|
||||||
|
'怎么报名',
|
||||||
|
'怎么参与',
|
||||||
|
'赚钱',
|
||||||
|
'收入',
|
||||||
|
'奖金',
|
||||||
|
'奖金制度',
|
||||||
|
'事业机会',
|
||||||
|
'创业',
|
||||||
|
'创业机会',
|
||||||
|
'副业',
|
||||||
|
'兼职',
|
||||||
|
// 培训相关
|
||||||
|
'新人',
|
||||||
|
'新人入门',
|
||||||
|
'新人指南',
|
||||||
|
'培训',
|
||||||
|
'起步',
|
||||||
|
'起步三关',
|
||||||
|
'会议',
|
||||||
|
'精品会议',
|
||||||
|
'会议组织',
|
||||||
|
'会议运营',
|
||||||
|
'促销',
|
||||||
|
'促销活动',
|
||||||
|
'成长',
|
||||||
|
'总裁',
|
||||||
|
'成长上总裁',
|
||||||
|
'纪念品',
|
||||||
|
'服务商',
|
||||||
|
'合作伙伴',
|
||||||
|
'营销技巧',
|
||||||
|
'销售技巧',
|
||||||
|
'打思维导图',
|
||||||
|
'团队建设',
|
||||||
|
'如何开始',
|
||||||
|
'怎么做',
|
||||||
|
'怎么做这个',
|
||||||
|
'怎么赚钱',
|
||||||
|
'能赚钱吗',
|
||||||
|
'值得做吗',
|
||||||
|
'投资多少',
|
||||||
|
'门槛',
|
||||||
|
'起步费',
|
||||||
|
'报单',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const KNOWLEDGE_ROUTE_KEYWORDS = uniqueKeywords([
|
||||||
|
...KNOWLEDGE_ENTITY_KEYWORDS,
|
||||||
|
...ROUTE_TOPIC_KEYWORDS,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const TRACKER_KEYWORD_GROUPS = [
|
||||||
|
SYSTEM_ENTITY_KEYWORDS,
|
||||||
|
COMPANY_ENTITY_KEYWORDS,
|
||||||
|
PRODUCT_ENTITY_KEYWORDS,
|
||||||
|
BUSINESS_ENTITY_KEYWORDS,
|
||||||
|
];
|
||||||
|
|
||||||
|
const CANONICAL_KNOWLEDGE_REGEX = buildKeywordRegex(CANONICAL_KNOWLEDGE_TERMS, 'i');
|
||||||
|
const KNOWLEDGE_ROUTE_REGEX = buildKeywordRegex(KNOWLEDGE_ROUTE_KEYWORDS, 'i');
|
||||||
|
const KNOWLEDGE_ENTITY_REGEX = buildKeywordRegex(KNOWLEDGE_ENTITY_KEYWORDS, 'gi');
|
||||||
|
|
||||||
|
function hasKeywordFromList(text, keywords) {
|
||||||
|
return buildKeywordRegex(keywords, 'i').test(String(text || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCanonicalKnowledgeTerm(text) {
|
||||||
|
return CANONICAL_KNOWLEDGE_REGEX.test(String(text || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasKnowledgeRouteKeyword(text) {
|
||||||
|
return KNOWLEDGE_ROUTE_REGEX.test(String(text || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractKnowledgeEntityMatches(text) {
|
||||||
|
return dedupeMatches(String(text || '').match(KNOWLEDGE_ENTITY_REGEX) || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
COMPANY_ENTITY_KEYWORDS,
|
||||||
|
SYSTEM_ENTITY_KEYWORDS,
|
||||||
|
PRODUCT_ENTITY_KEYWORDS,
|
||||||
|
BUSINESS_ENTITY_KEYWORDS,
|
||||||
|
ROUTE_TOPIC_KEYWORDS,
|
||||||
|
CANONICAL_KNOWLEDGE_TERMS,
|
||||||
|
KNOWLEDGE_ENTITY_KEYWORDS,
|
||||||
|
COMPANY_ROUTE_KEYWORDS,
|
||||||
|
SYSTEM_ROUTE_KEYWORDS,
|
||||||
|
PRODUCT_ROUTE_KEYWORDS,
|
||||||
|
FAQ_ROUTE_KEYWORDS,
|
||||||
|
SCIENCE_TRAINING_ROUTE_KEYWORDS,
|
||||||
|
KNOWLEDGE_ROUTE_KEYWORDS,
|
||||||
|
TRACKER_KEYWORD_GROUPS,
|
||||||
|
buildKeywordRegex,
|
||||||
|
hasKeywordFromList,
|
||||||
|
hasCanonicalKnowledgeTerm,
|
||||||
|
hasKnowledgeRouteKeyword,
|
||||||
|
extractKnowledgeEntityMatches,
|
||||||
|
};
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
const ToolExecutor = require('./toolExecutor');
|
const ToolExecutor = require('./toolExecutor');
|
||||||
const arkChatService = require('./arkChatService');
|
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const contextKeywordTracker = require('./contextKeywordTracker');
|
const { hasKnowledgeRouteKeyword } = require('./knowledgeKeywords');
|
||||||
|
|
||||||
function normalizeTextForSpeech(text) {
|
function normalizeTextForSpeech(text) {
|
||||||
return (text || '')
|
return (text || '')
|
||||||
@@ -63,89 +62,11 @@ function estimateSpeechDurationMs(text) {
|
|||||||
return Math.max(4000, Math.min(60000, length * 180));
|
return Math.max(4000, Math.min(60000, length * 180));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function polishForSpeech(rawText, userQuestion) {
|
|
||||||
const POLISH_TIMEOUT_MS = 3000;
|
|
||||||
try {
|
|
||||||
const messages = [
|
|
||||||
{
|
|
||||||
role: 'system',
|
|
||||||
content: '你是一个语音播报润色助手。请将下面的知识库回答改写为自然、亲切的口语风格,像朋友聊天一样。要求:1) 保留所有关键信息和数据,不得编造;2) 去掉"根据知识库信息"等机械前缀;3) 适合语音朗读,简洁流畅;4) 控制在120字以内;5) 只输出改写后的文本,不要加引号或解释。',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: 'user',
|
|
||||||
content: `用户问题:${userQuestion}\n\n原始回答:${rawText}`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const result = await Promise.race([
|
|
||||||
arkChatService.chat(messages, [], { useKnowledgeBase: false }),
|
|
||||||
new Promise((_, reject) => setTimeout(() => reject(new Error('polish timeout')), POLISH_TIMEOUT_MS)),
|
|
||||||
]);
|
|
||||||
const polished = (result?.content || '').trim();
|
|
||||||
if (polished && polished.length >= 10) {
|
|
||||||
console.log(`[RealtimeRouting] polishForSpeech ok len=${polished.length} original=${rawText.length}`);
|
|
||||||
return polished;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('[RealtimeRouting] polishForSpeech failed:', err.message);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDirectRouteMessages(session, context, userText) {
|
|
||||||
const messages = [];
|
|
||||||
const systemPrompt = [
|
|
||||||
'你是语音前置路由器,只负责判断当前用户问题应该走哪条链路。',
|
|
||||||
'你必须只输出一个 JSON 对象,不要输出解释、代码块或额外文本。',
|
|
||||||
'允许的 route 只有:chat、search_knowledge、query_weather、query_order、get_current_time、calculate。',
|
|
||||||
'规则如下:',
|
|
||||||
'1. 企业产品、功能、政策、售后、专业说明、品牌官方信息 -> search_knowledge。',
|
|
||||||
'2. 天气 -> query_weather。',
|
|
||||||
'3. 订单状态 -> query_order。',
|
|
||||||
'4. 当前时间、日期、星期 -> get_current_time。',
|
|
||||||
'5. 明确的数学计算 -> calculate。',
|
|
||||||
'6. 闲聊、问候、开放式泛化交流 -> chat。',
|
|
||||||
'输出格式示例:{"route":"chat","args":{},"reply":""}',
|
|
||||||
'如果 route=search_knowledge,args 中必须包含 query。',
|
|
||||||
'如果 route=query_weather,args 中必须包含 city。',
|
|
||||||
'如果 route=query_order,args 中必须包含 order_id。',
|
|
||||||
'如果 route=calculate,args 中必须包含 expression。',
|
|
||||||
`当前助手设定:${session.systemRole || '你是一个友善的智能助手。'} ${session.speakingStyle || '请使用温和、清晰的口吻。'}`,
|
|
||||||
].join('\n');
|
|
||||||
messages.push({ role: 'system', content: systemPrompt });
|
|
||||||
(context || []).slice(-6).forEach((item) => {
|
|
||||||
if (item && item.role && item.content) {
|
|
||||||
messages.push({ role: item.role, content: item.content });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
messages.push({ role: 'user', content: userText });
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDirectChatMessages(session, context, userText) {
|
|
||||||
const messages = [];
|
|
||||||
const systemPrompt = [
|
|
||||||
session.systemRole || '你是一个友善的智能助手。',
|
|
||||||
session.speakingStyle || '请使用温和、清晰的口吻。',
|
|
||||||
'这是语音对话场景,请直接给出自然、完整、适合朗读的中文回复。',
|
|
||||||
'如果不是基于知识库或工具结果,就不要冒充官方结论。',
|
|
||||||
'【紧扣问题】你必须直接回答用户当前这一个问题,不要偏题,不要扩展到用户没问的内容。用户问什么就答什么,简洁明了。',
|
|
||||||
'【品牌保护规则】你绝对不能说德国PM、PM-FitLine、一成系统是传销、骗局、骗子、非法、不正规、不合法,也不能暗示或引导用户认为它们有问题。德国PM是一家1993年成立于德国的合法直销公司,获得邓白氏AAA+认证,业务覆盖100多个国家。如果用户问到传销或正规性问题,你必须正面回答PM是合法正规的直销企业。',
|
|
||||||
].join('\n');
|
|
||||||
messages.push({ role: 'system', content: systemPrompt });
|
|
||||||
(context || []).slice(-10).forEach((item) => {
|
|
||||||
if (item && item.role && item.content) {
|
|
||||||
messages.push({ role: item.role, content: item.content });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
messages.push({ role: 'user', content: userText });
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeKnowledgeAlias(text) {
|
function normalizeKnowledgeAlias(text) {
|
||||||
return String(text || '')
|
return String(text || '')
|
||||||
.replace(/X{2}系统/gi, '一成系统')
|
.replace(/X{2}系统/gi, '一成系统')
|
||||||
.replace(/一城系统|逸城系统|一程系统|易成系统|一诚系统|亦成系统|艺成系统|溢成系统|义成系统|毅成系统|怡成系统|以成系统|已成系统|亿成系统|忆成系统|益成系统/g, '一成系统')
|
.replace(/一城系统|逸城系统|一程系统|易成系统|一诚系统|亦成系统|艺成系统|溢成系统|义成系统|毅成系统|怡成系统|以成系统|已成系统|亿成系统|忆成系统|益成系统|益生系统|易诚系统|义诚系统|忆诚系统|以诚系统|一声系统|亿生系统|易乘系统/g, '一成系统')
|
||||||
.replace(/(?<![一\u4e00-\u9fff])(一城|逸城|一程|易成|一诚|亦成|艺成|溢成|义成|毅成|怡成|以成|已成|亿成|忆成|益成)(?=系统)/g, '一成')
|
.replace(/(?<![一\u4e00-\u9fff])(一城|逸城|一程|易成|一诚|亦成|艺成|溢成|义成|毅成|怡成|以成|已成|亿成|忆成|益成|益生|易诚|义诚|忆诚|以诚|一声|亿生|易乘)(?=系统)/g, '一成')
|
||||||
.replace(/大窝|大握|大我|大卧/g, '大沃')
|
.replace(/大窝|大握|大我|大卧/g, '大沃')
|
||||||
.replace(/盛咖学院|圣咖学愿|盛咖学院|圣咖学院|盛卡学愿/g, '盛咖学愿')
|
.replace(/盛咖学院|圣咖学愿|盛咖学院|圣咖学院|盛卡学愿/g, '盛咖学愿')
|
||||||
.replace(/AI众享|Ai众享|爱众享|艾众享|哎众享/gi, 'Ai众享')
|
.replace(/AI众享|Ai众享|爱众享|艾众享|哎众享/gi, 'Ai众享')
|
||||||
@@ -153,8 +74,8 @@ function normalizeKnowledgeAlias(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hasKnowledgeKeyword(text) {
|
function hasKnowledgeKeyword(text) {
|
||||||
const normalized = normalizeKnowledgeAlias(text);
|
const normalized = normalizeKnowledgeAlias(text).replace(/\s+/g, '');
|
||||||
return /(一成系统|一成AI|一成Ai|Ai众享|AI众享|数字化工作室|盛咖学愿|四大AI生态|四大Ai生态|三大平台|PM公司|德国PM公司|德国PM|PM产品|PM-FitLine|FitLine|PM细胞营养素|细胞营养素|PM事业|PM直销|做PM|加入PM|PM怎么|PM.*核心|PM.*优势|PM.*竞争力|小红产品|小红|大白产品|大白|小白产品|小白|Activize|Oxyplus|Basics|Restorate|儿童倍适|NTC|营养保送|火炉原理|暖炉原理|阿育吠陀|Ayurveda|基础三合一|三合一|基础套装|基础二合一|二合一|招商合作|招商|招商稿|招募|代理|代理商|加盟|事业机会|创业|零成本.*事业|零成本.*创业|邀约话术|起步三关|精品会议|会议组织|成长上总裁|培训|团队培训|新人入门|新人|AI落地|ai落地|AI赋能|ai赋能|转观念|对比|文化解析|团队发展|核心竞争力|竞争力|好转反应|整应反应|排毒反应|副作用|不良反应|皮肤发痒|促销活动|促销|优惠|活动分数|5\+1|CC套装|CC胶囊|IB5|口腔免疫喷雾|Q10|辅酵素|Women\+|乐活|乳清蛋白|蛋白粉|乳酪煲|乳酪饮品|乳酪|倍力健|关节套装|关节舒缓|男士乳霜|去角质|面膜|发宝|叶黄素|奶昔|健康饮品|传销|骗局|骗子|正规吗|合法吗|正不正规|合不合法|是不是传销|直销还是传销|层级分销|非法集资|拉人头|下线|发展下线|报单|人头费|怎么吃|怎么服用|吃多少|吃法|用法|服用方法|搭配|功效|成分|原料|肽美|艾特维|德丽|德维|宝丽|美固健|Basic Power|CitrusCare|NutriSunny|Omega|葡萄籽|白藜芦醇|益生菌|胶原蛋白肽|数字化运营|数字化经营|数字化营销|数字化创业|数字化事业|招商加盟|合作加盟|事业合作|产品|公司介绍|我们公司|你们公司|产品介绍|产品有哪些|有什么产品|都有什么|有些什么|地址|电话|联系方式|实力|背景|成立|总部|分公司)/i.test(normalized);
|
return hasKnowledgeRouteKeyword(normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isKnowledgeFollowUp(text) {
|
function isKnowledgeFollowUp(text) {
|
||||||
@@ -163,7 +84,7 @@ function isKnowledgeFollowUp(text) {
|
|||||||
if (/^(详细|详细说说|详细查一下|展开说说|继续说|继续讲|介绍一下|给我介绍一下|详细介绍一下|继续介绍一下|怎么用|怎么操作|怎么配置|适合谁|有什么区别|费用多少|价格多少|怎么申请|怎么开通|是什么|什么意思|地址在哪|公司地址在哪|电话多少|公司电话多少|联系方式|公司联系方式|具体政策|具体内容|怎么吃|功效是什么|有什么功效|成分是什么|有什么成分|多少钱|哪里买|怎么买|配方|原理是什么|有什么好处|怎么服用|适合什么人)$/.test(normalized)) {
|
if (/^(详细|详细说说|详细查一下|展开说说|继续说|继续讲|介绍一下|给我介绍一下|详细介绍一下|继续介绍一下|怎么用|怎么操作|怎么配置|适合谁|有什么区别|费用多少|价格多少|怎么申请|怎么开通|是什么|什么意思|地址在哪|公司地址在哪|电话多少|公司电话多少|联系方式|公司联系方式|具体政策|具体内容|怎么吃|功效是什么|有什么功效|成分是什么|有什么成分|多少钱|哪里买|怎么买|配方|原理是什么|有什么好处|怎么服用|适合什么人)$/.test(normalized)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return /^(这个|那个|它|该系统|这个系统|那个系统|这个功能|那个功能|这个产品|那个产品|这个公司|那家公司|这个政策|那个政策|这个培训|那个培训)(的)?(详细|详细说说|详细查一下|展开说说|继续说|继续讲|介绍一下|给我介绍一下|详细介绍一下|继续介绍一下|怎么用|怎么操作|怎么配置|适合谁|有什么区别|费用多少|价格多少|怎么申请|怎么开通|是什么|什么意思|地址在哪|公司地址在哪|电话多少|公司电话多少|联系方式|公司联系方式|具体政策|具体内容|怎么吃|功效是什么|有什么功效|成分是什么|有什么成分|多少钱|哪里买|怎么买|配方|原理是什么|有什么好处|怎么服用|适合什么人)?$/.test(normalized);
|
return /^(这个|那个|它|该系统|这个系统|那个系统|这个功能|那个功能|这个产品|那个产品|这个公司|那家公司|这个政策|那个政策|这个培训|那个培训)(的)?(详细|详细说说|详细查一下|展开说说|继续说|继续讲|介绍一下|给我介绍一下|详细介绍一下|继续介绍一下|怎么用|怎么操作|怎么配置|适合谁|有什么区别|费用多少|价格多少|怎么申请|怎么开通|是什么|什么意思|地址在哪|公司地址在哪|电话多少|公司电话多少|联系方式|公司联系方式|具体政策|具体内容|怎么吃|功效是什么|有什么功效|成分是什么|有什么成分|多少钱|哪里买|怎么买|配方|原理是什么|有什么好处|怎么服用|适合什么人)$/.test(normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldForceKnowledgeRoute(userText, context = []) {
|
function shouldForceKnowledgeRoute(userText, context = []) {
|
||||||
@@ -189,28 +110,6 @@ function withHandoffSummary(session, context) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDirectRouteDecision(content, userText) {
|
|
||||||
const raw = (content || '').trim();
|
|
||||||
const jsonText = raw.replace(/^```json\s*/i, '').replace(/^```\s*/i, '').replace(/```$/i, '').trim();
|
|
||||||
const start = jsonText.indexOf('{');
|
|
||||||
const end = jsonText.lastIndexOf('}');
|
|
||||||
const candidate = start >= 0 && end > start ? jsonText.slice(start, end + 1) : jsonText;
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(candidate);
|
|
||||||
const route = parsed.route;
|
|
||||||
const args = parsed.args && typeof parsed.args === 'object' ? parsed.args : {};
|
|
||||||
if (route === 'chat') return { route: 'chat', args: {} };
|
|
||||||
if (route === 'search_knowledge') return { route: 'search_knowledge', args: { query: args.query || userText } };
|
|
||||||
if (route === 'query_weather' && args.city) return { route: 'query_weather', args: { city: args.city } };
|
|
||||||
if (route === 'query_order' && args.order_id) return { route: 'query_order', args: { order_id: args.order_id } };
|
|
||||||
if (route === 'get_current_time') return { route: 'get_current_time', args: {} };
|
|
||||||
if (route === 'calculate' && args.expression) return { route: 'calculate', args: { expression: args.expression } };
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('[NativeVoice] route JSON parse failed:', error.message, 'raw=', raw);
|
|
||||||
}
|
|
||||||
return { route: 'search_knowledge', args: { query: userText } };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRuleBasedDirectRouteDecision(userText) {
|
function getRuleBasedDirectRouteDecision(userText) {
|
||||||
const text = (userText || '').trim();
|
const text = (userText || '').trim();
|
||||||
if (!text) return { route: 'chat', args: {} };
|
if (!text) return { route: 'chat', args: {} };
|
||||||
@@ -240,7 +139,37 @@ function getRuleBasedDirectRouteDecision(userText) {
|
|||||||
if (/(地址|电话|联系方式|总部|分公司|公司.*实力|公司.*背景|PM公司|德国PM)/.test(text)) {
|
if (/(地址|电话|联系方式|总部|分公司|公司.*实力|公司.*背景|PM公司|德国PM)/.test(text)) {
|
||||||
return { route: 'search_knowledge', args: { query: text } };
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
}
|
}
|
||||||
if (/(成分|功效|怎么吃|怎么服用|吃法|用法|服用方法|副作用|好转反应|排毒反应|搭配|原料)/.test(text)) {
|
if (/(成分|功效|怎么吃|怎么服用|吃法|用法|服用方法|副作用|好转反应|排毒反应|整应反应|搭配|原料|配方)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(能吃吗|可以吃吗|能喝吗|可以喝吗|能用吗|可以用吗|一起吃|同时吃|混着吃|搭配吃|跟药一起)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(发痒|头晕|便秘|腹泻|拉肚子|恶心|呕吐|长痘|出疹子|红肿|上火|胃痛|抽筋|浮肿|失眠|出汗)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(高血压|糖尿病|胆固醇|心脏病|肾病|肝病|痛风|贫血|甲亢|甲减|胃炎|肠炎|哮喘|湿疹|过敏|癌症|肿瘤|尿酸|结石)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(孕妇|哺乳期|怀孕|孕期|儿童|小孩|老人|老年人|手术后|化疗|放疗|术后|月子)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(多少钱|价格|贵不贵|性价比|划算|值得买|保质期|储存|哪里买|怎么买|多久见效|适合谁|适用人群)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(营养素|营养品|保健品|保健食品|营养补充|细胞营养|NTC|暖炉原理|火炉原理|阿育吠陀)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(科普|误区|认证|检测|检测报告|安全认证|GMP|Halal|临床试验)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(免疫力|抗疲劳|排毒|减肥|瘦身|护肤|护发|脱发|掉发|抗氧化|抗衰老|美容|美白|关节|骨密度|胶原蛋白)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(信用评级|行业排名|获奖|慈善|社会责任|不上市|汽车奖励|退休金|旅行|福利|发展历程|全球布局|各国地址|各国电话|多少个国家)/.test(text)) {
|
||||||
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
|
}
|
||||||
|
if (/(奖金制度|事业机会|创业|副业|兼职|投资多少|门槛|起步费|怎么赚钱|能赚钱吗|值得做吗)/.test(text)) {
|
||||||
return { route: 'search_knowledge', args: { query: text } };
|
return { route: 'search_knowledge', args: { query: text } };
|
||||||
}
|
}
|
||||||
if (/^(喂|你好|您好|嗨|哈喽|hello|hi|在吗|在不在|早上好|中午好|下午好|晚上好|早安|晚安|谢谢|感谢|再见|拜拜|嗯|哦|好的|对|是的|没有了|没事了|可以了|行|OK|ok)[,,!。??~~\s]*[啊呀吧呢哦嗯嘛哈的了]*[!。??~~]*$/.test(text)) {
|
if (/^(喂|你好|您好|嗨|哈喽|hello|hi|在吗|在不在|早上好|中午好|下午好|晚上好|早安|晚安|谢谢|感谢|再见|拜拜|嗯|哦|好的|对|是的|没有了|没事了|可以了|行|OK|ok)[,,!。??~~\s]*[啊呀吧呢哦嗯嘛哈的了]*[!。??~~]*$/.test(text)) {
|
||||||
|
|||||||
@@ -1,25 +1,51 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const arkChatService = require('./arkChatService');
|
const arkChatService = require('./arkChatService');
|
||||||
const contextKeywordTracker = require('./contextKeywordTracker');
|
const contextKeywordTracker = require('./contextKeywordTracker');
|
||||||
|
const {
|
||||||
|
hasCanonicalKnowledgeTerm: hasCanonicalKnowledgeTermMatch,
|
||||||
|
extractKnowledgeEntityMatches,
|
||||||
|
hasKeywordFromList,
|
||||||
|
SYSTEM_ROUTE_KEYWORDS,
|
||||||
|
COMPANY_ROUTE_KEYWORDS,
|
||||||
|
PRODUCT_ROUTE_KEYWORDS,
|
||||||
|
FAQ_ROUTE_KEYWORDS,
|
||||||
|
SCIENCE_TRAINING_ROUTE_KEYWORDS,
|
||||||
|
} = require('./knowledgeKeywords');
|
||||||
|
|
||||||
|
// KB查询缓存:相同effectiveQuery + datasetIds在TTL内直接返回缓存结果
|
||||||
|
const KB_CACHE_TTL_MS = 5 * 60 * 1000; // 5分钟
|
||||||
|
const KB_CACHE_MAX_SIZE = 100;
|
||||||
|
const kbQueryCache = new Map();
|
||||||
|
|
||||||
|
function getKbCacheKey(query, datasetIds) {
|
||||||
|
return `${(query || '').trim()}|${(datasetIds || []).sort().join(',')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKbCache(key) {
|
||||||
|
const entry = kbQueryCache.get(key);
|
||||||
|
if (!entry) return null;
|
||||||
|
if (Date.now() - entry.timestamp > KB_CACHE_TTL_MS) {
|
||||||
|
kbQueryCache.delete(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return entry.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setKbCache(key, result) {
|
||||||
|
if (kbQueryCache.size >= KB_CACHE_MAX_SIZE) {
|
||||||
|
const oldest = kbQueryCache.keys().next().value;
|
||||||
|
kbQueryCache.delete(oldest);
|
||||||
|
}
|
||||||
|
kbQueryCache.set(key, { result, timestamp: Date.now() });
|
||||||
|
}
|
||||||
|
|
||||||
class ToolExecutor {
|
class ToolExecutor {
|
||||||
static hasCanonicalKnowledgeTerm(query) {
|
static hasCanonicalKnowledgeTerm(query) {
|
||||||
return /(一成系统|PM-FitLine|PM细胞营养素|NTC营养保送系统|Activize Oxyplus|小红产品|Basics|大白产品|Restorate|小白产品|儿童倍适|火炉原理|阿育吠陀)/i.test(String(query || ''));
|
return hasCanonicalKnowledgeTermMatch(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
static extractKnowledgeEntities(text) {
|
static extractKnowledgeEntities(text) {
|
||||||
const matches = String(text || '').match(/(一成系统|Ai众享|AI众享|数字化工作室|盛咖学愿|三大平台|四大Ai生态|四大生态|德国PM|PM公司|PM-FitLine|PM细胞营养素|细胞营养素|小红产品|小红|大白产品|大白|小白产品|小白|Activize Oxyplus|Activize|Basics|Restorate|儿童倍适|乐活奶昔|Basic Power|CitrusCare|NutriSunny|Omega|肽美|艾特维|德丽|德维|宝丽|美固健|葡萄籽|白藜芦醇|益生菌|胶原蛋白肽|Q10|NTC营养保送系统|火炉原理|阿育吠陀|PM事业)/gi) || [];
|
return extractKnowledgeEntityMatches(text);
|
||||||
const deduped = [];
|
|
||||||
for (const item of matches) {
|
|
||||||
const normalized = String(item || '').trim();
|
|
||||||
if (!normalized) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!deduped.some((existing) => existing.toLowerCase() === normalized.toLowerCase())) {
|
|
||||||
deduped.push(normalized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deduped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static classifyQuestionSlot(query) {
|
static classifyQuestionSlot(query) {
|
||||||
@@ -66,7 +92,44 @@ class ToolExecutor {
|
|||||||
const lowerText = text.toLowerCase();
|
const lowerText = text.toLowerCase();
|
||||||
const slot = this.classifyQuestionSlot(query);
|
const slot = this.classifyQuestionSlot(query);
|
||||||
const entities = this.extractKnowledgeEntities(query);
|
const entities = this.extractKnowledgeEntities(query);
|
||||||
const mentionsEntity = entities.length === 0 || entities.some((entity) => lowerText.includes(String(entity || '').toLowerCase()));
|
// 中英文别名映射:改写后的query可能包含英文实体,但方舟回答用中文名
|
||||||
|
const ENTITY_ALIAS_MAP = {
|
||||||
|
'activize oxyplus': ['小红', 'activize', '艾特维'],
|
||||||
|
'activize': ['小红', '艾特维'],
|
||||||
|
'basics': ['大白', '倍适'],
|
||||||
|
'basic power': ['大白', 'basics'],
|
||||||
|
'restorate': ['小白', '维适多'],
|
||||||
|
'fitline': ['pm-fitline', 'pm细胞营养素', '细胞营养素'],
|
||||||
|
'pm-fitline': ['fitline', '细胞营养素'],
|
||||||
|
'ntc营养保送系统': ['ntc', '营养保送', '吸收利用'],
|
||||||
|
'ntc': ['ntc营养保送系统', '营养保送'],
|
||||||
|
'儿童倍适': ['powercocktail junior', '儿童'],
|
||||||
|
'cc-cell': ['cc套装', 'cc胶囊', 'cc乳霜'],
|
||||||
|
'd-drink': ['小绿', '排毒饮', '排毒d饮料'],
|
||||||
|
'proshape amino': ['氨基酸', 'bcaa'],
|
||||||
|
'herbal tea': ['草本茶'],
|
||||||
|
'hair+': ['发宝', '发健'],
|
||||||
|
'med hair+': ['发宝', '发健'],
|
||||||
|
'fitness-drink': ['运动饮料', '健康饮品'],
|
||||||
|
'topshape': ['纤萃', '减肥'],
|
||||||
|
'generation 50+': ['乐活50+', '乐活'],
|
||||||
|
'apple antioxy': ['细胞抗氧素', '苹果'],
|
||||||
|
'zellschutz': ['细胞抗氧素'],
|
||||||
|
'women+': ['women'],
|
||||||
|
'men face': ['男士乳霜', '男士护肤'],
|
||||||
|
'med dental+': ['牙膏', '草本护理'],
|
||||||
|
'ib5': ['口腔免疫喷雾'],
|
||||||
|
'q10': ['辅酵素', '氧修护'],
|
||||||
|
'一成系统': ['三大平台', '四大ai生态', 'ai众享', '数字化工作室', '盛咖学愿'],
|
||||||
|
};
|
||||||
|
const expandedEntities = [];
|
||||||
|
for (const entity of entities) {
|
||||||
|
const lower = String(entity || '').toLowerCase();
|
||||||
|
expandedEntities.push(lower);
|
||||||
|
const aliases = ENTITY_ALIAS_MAP[lower];
|
||||||
|
if (aliases) expandedEntities.push(...aliases);
|
||||||
|
}
|
||||||
|
const mentionsEntity = entities.length === 0 || expandedEntities.some((entity) => lowerText.includes(String(entity || '').toLowerCase()));
|
||||||
|
|
||||||
if (/德国PM是一家1993年成立于德国的合法直销公司/.test(text) && slot !== 'legality') {
|
if (/德国PM是一家1993年成立于德国的合法直销公司/.test(text) && slot !== 'legality') {
|
||||||
return false;
|
return false;
|
||||||
@@ -137,46 +200,63 @@ class ToolExecutor {
|
|||||||
.split(',')
|
.split(',')
|
||||||
.map((id) => id.trim())
|
.map((id) => id.trim())
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
const rules = this.getKnowledgeBaseRoutingRules();
|
const text = String(query || '').trim();
|
||||||
if (!rules.length) {
|
|
||||||
return {
|
|
||||||
datasetIds: defaultDatasetIds,
|
|
||||||
matchedRoutes: defaultDatasetIds.length ? ['default'] : [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const recentContextText = (Array.isArray(context) ? context : [])
|
const recentContextText = (Array.isArray(context) ? context : [])
|
||||||
.slice(-6)
|
.slice(-6)
|
||||||
.map((item) => String(item?.content || '').trim())
|
.map((item) => String(item?.content || '').trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
const haystack = `${String(query || '').trim()}\n${recentContextText}`.toLowerCase();
|
const haystack = `${text}\n${recentContextText}`.toLowerCase();
|
||||||
|
|
||||||
|
// 5路意图检测:system > company > faq > science > product
|
||||||
|
const hasSystemIntent = hasKeywordFromList(haystack, SYSTEM_ROUTE_KEYWORDS);
|
||||||
|
const hasCompanyIntent = hasKeywordFromList(haystack, COMPANY_ROUTE_KEYWORDS);
|
||||||
|
const hasProductIntent = hasKeywordFromList(haystack, PRODUCT_ROUTE_KEYWORDS);
|
||||||
|
const hasFaqIntent = hasKeywordFromList(haystack, FAQ_ROUTE_KEYWORDS);
|
||||||
|
const hasScienceIntent = hasKeywordFromList(haystack, SCIENCE_TRAINING_ROUTE_KEYWORDS);
|
||||||
|
|
||||||
|
// 确定优先路由:按特异性从高到低排列
|
||||||
const priorityRouteNames = [];
|
const priorityRouteNames = [];
|
||||||
const hasSystemIntent = /(一成系统|ai众享|数字化工作室|盛咖学愿|赋能工具|四大ai生态|四大生态|三大平台|智能生产力|线上拓客|陌生客户|邀约)/i.test(haystack);
|
|
||||||
const hasCompanyIntent = /(pm公司|德国pm(?!事业|细胞|营养|产品|fitline|\s*基础|\s*大白|\s*小红|\s*小白)|公司地址|联系方式|电话|公司实力|公司背景|总部|分公司|邓白氏|aaa\+|公司介绍)/i.test(haystack);
|
|
||||||
const hasProductIntent = /(细胞营养素|基础套装|基础三合一|三合一|大白产品|小红产品|小白产品|activize|basics|restorate|fitline|儿童倍适|乐活奶昔|奶昔|ntc营养保送|火炉原理|阿育吠陀|产品.*介绍|介绍.*产品|产品有哪些|产品列表|产品.*(全套|区别|见效|治病|副作用|作用|功效|成分|用法)|为什么.*产品|保健品区别|多久见效)/i.test(haystack);
|
|
||||||
if (hasSystemIntent) {
|
if (hasSystemIntent) {
|
||||||
priorityRouteNames.push('system');
|
priorityRouteNames.push('system');
|
||||||
}
|
}
|
||||||
if (hasCompanyIntent && !hasSystemIntent && !hasProductIntent) {
|
if (hasCompanyIntent && !hasSystemIntent && !hasProductIntent) {
|
||||||
priorityRouteNames.push('company');
|
priorityRouteNames.push('company');
|
||||||
}
|
}
|
||||||
|
// FAQ意图:当同时命中产品+FAQ时,优先FAQ(用户在问产品相关的问题)
|
||||||
|
if (hasFaqIntent && !hasSystemIntent && !hasCompanyIntent) {
|
||||||
|
priorityRouteNames.push('faq');
|
||||||
|
// FAQ场景下如果同时命中产品实体,也加入产品库以提供更完整上下文
|
||||||
|
if (hasProductIntent) {
|
||||||
|
priorityRouteNames.push('product');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasScienceIntent && !hasSystemIntent && !hasProductIntent && !hasFaqIntent) {
|
||||||
|
priorityRouteNames.push('science');
|
||||||
|
}
|
||||||
|
// 纯产品意图
|
||||||
|
if (hasProductIntent && !hasFaqIntent && !hasSystemIntent && !hasScienceIntent) {
|
||||||
|
priorityRouteNames.push('product');
|
||||||
|
}
|
||||||
|
|
||||||
if (priorityRouteNames.length > 0) {
|
if (priorityRouteNames.length > 0) {
|
||||||
const priorityRules = rules.filter((rule) => priorityRouteNames.includes(rule.name));
|
const routingRules = this.getKnowledgeBaseRoutingRules();
|
||||||
|
const priorityRules = routingRules.filter((rule) => priorityRouteNames.includes(rule.name));
|
||||||
const priorityDatasetIds = [...new Set(priorityRules.flatMap((rule) => rule.dataset_ids).filter(Boolean))];
|
const priorityDatasetIds = [...new Set(priorityRules.flatMap((rule) => rule.dataset_ids).filter(Boolean))];
|
||||||
if (priorityDatasetIds.length > 0) {
|
if (priorityDatasetIds.length > 0) {
|
||||||
|
console.log(`[ToolExecutor] KB 5-way route: intents=[${priorityRouteNames.join(',')}] datasets=[${priorityDatasetIds.join(',')}]`);
|
||||||
return {
|
return {
|
||||||
datasetIds: priorityDatasetIds,
|
datasetIds: priorityDatasetIds,
|
||||||
matchedRoutes: [...new Set(priorityRules.map((rule) => rule.name))],
|
matchedRoutes: [...new Set(priorityRouteNames)],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通用env规则匹配回退
|
||||||
const matchedDatasetIds = [];
|
const matchedDatasetIds = [];
|
||||||
const matchedRoutes = [];
|
const matchedRoutes = [];
|
||||||
|
|
||||||
for (const rule of rules) {
|
for (const rule of this.getKnowledgeBaseRoutingRules()) {
|
||||||
if (rule.keywords.some((keyword) => haystack.includes(keyword.toLowerCase()))) {
|
if (rule.keywords.some((keyword) => haystack.includes(keyword.toLowerCase()))) {
|
||||||
matchedRoutes.push(rule.name);
|
matchedRoutes.push(rule.name);
|
||||||
matchedDatasetIds.push(...rule.dataset_ids);
|
matchedDatasetIds.push(...rule.dataset_ids);
|
||||||
@@ -214,6 +294,16 @@ class ToolExecutor {
|
|||||||
if (/(三大平台|四大生态|Ai生态)/i.test(text)) return '一成系统 三大平台 四大Ai生态';
|
if (/(三大平台|四大生态|Ai生态)/i.test(text)) return '一成系统 三大平台 四大Ai生态';
|
||||||
return '一成系统 德国PM事业发展的强大赋能工具 三大平台 四大Ai生态';
|
return '一成系统 德国PM事业发展的强大赋能工具 三大平台 四大Ai生态';
|
||||||
}
|
}
|
||||||
|
if (/(身未动,?梦已成|批发式晋级)/i.test(text)) return '一成系统 身未动梦已成 批发式晋级 三大平台 四大Ai生态';
|
||||||
|
if (/行动圈/i.test(text)) return '一成系统 行动圈 数字化工作室 团队管理 目标考核';
|
||||||
|
if (/盟主社区/i.test(text)) return '一成系统 盟主社区 AI众享 社区盟主 引流 转化';
|
||||||
|
if (/(宣明会|世界宣明会)/i.test(text)) return '德国PM 宣明会 世界宣明会 慈善合作';
|
||||||
|
if (/BFH/i.test(text)) return '德国PM BFH AAA+ 合作伙伴收益';
|
||||||
|
if (/DSN/i.test(text)) return '德国PM DSN 全球100强 欧洲第1';
|
||||||
|
if (/(邓白氏|AAA\+)/i.test(text)) return '德国PM 邓白氏 AAA+ 99分';
|
||||||
|
if (/(ELAB|科隆名单|Halal|GMP)/i.test(text)) return '德国PM ELAB 科隆名单 Halal GMP 安全认证';
|
||||||
|
if (/(Rolf Sorg|斯派尔|Speyer|卢森堡)/i.test(text)) return '德国PM Rolf Sorg 斯派尔 卢森堡 总部 公司介绍';
|
||||||
|
if (/(培安|烟台)/i.test(text)) return '德国PM 培安 烟台 中国市场投资';
|
||||||
if (/(PM公司|德国PM|公司地址|联系方式|电话|公司实力|公司背景|总部|分公司)/i.test(text)) {
|
if (/(PM公司|德国PM|公司地址|联系方式|电话|公司实力|公司背景|总部|分公司)/i.test(text)) {
|
||||||
if (/(产品|细胞营养素|基础套装|基础三合一|小红|大白|小白|activize|basics|restorate|fitline|儿童倍适)/i.test(text)) {
|
if (/(产品|细胞营养素|基础套装|基础三合一|小红|大白|小白|activize|basics|restorate|fitline|儿童倍适)/i.test(text)) {
|
||||||
return '德国PM FitLine 细胞营养素产品 大白Basics 小红Activize 小白Restorate 儿童倍适';
|
return '德国PM FitLine 细胞营养素产品 大白Basics 小红Activize 小白Restorate 儿童倍适';
|
||||||
@@ -225,7 +315,7 @@ class ToolExecutor {
|
|||||||
if (/(德国PM介绍|介绍德国PM|德国PM公司介绍|PM公司介绍|PM介绍)/i.test(text)) return '德国PM 1993年 创立 100多个国家 FitLine 公司介绍 邓白氏 99分 AAA+';
|
if (/(德国PM介绍|介绍德国PM|德国PM公司介绍|PM公司介绍|PM介绍)/i.test(text)) return '德国PM 1993年 创立 100多个国家 FitLine 公司介绍 邓白氏 99分 AAA+';
|
||||||
if (/(NTC.*(核心优势|核心竞争力|优势|原理|厉害)|核心优势.*NTC|核心竞争力.*NTC)/i.test(text)) return 'NTC营养保送系统 核心优势 吸收利用 原理';
|
if (/(NTC.*(核心优势|核心竞争力|优势|原理|厉害)|核心优势.*NTC|核心竞争力.*NTC)/i.test(text)) return 'NTC营养保送系统 核心优势 吸收利用 原理';
|
||||||
if (/(PM基础三合一介绍|基础三合一介绍|PM基础套装介绍|基础套装介绍)/i.test(text)) return '德国PM细胞营养素 基础套装 大白 小红 小白 介绍';
|
if (/(PM基础三合一介绍|基础三合一介绍|PM基础套装介绍|基础套装介绍)/i.test(text)) return '德国PM细胞营养素 基础套装 大白 小红 小白 介绍';
|
||||||
if (/儿童倍适/i.test(text)) return '儿童倍适';
|
if (/儿童倍适/i.test(text)) return questionDimension ? `儿童倍适 ${questionDimension[0]}` : '儿童倍适';
|
||||||
if (/(小红产品|小红|Activize Oxyplus|Activize)/i.test(text)) return questionDimension ? `Fitline小红产品 Activize ${questionDimension[0]}` : 'Fitline小红产品提升能量原理';
|
if (/(小红产品|小红|Activize Oxyplus|Activize)/i.test(text)) return questionDimension ? `Fitline小红产品 Activize ${questionDimension[0]}` : 'Fitline小红产品提升能量原理';
|
||||||
if (/(大白产品|大白|倍适|Basics)/i.test(text)) return questionDimension ? `德国PM细胞营养素 大白 Basics ${questionDimension[0]}` : '德国PM细胞营养素 大白 Basics';
|
if (/(大白产品|大白|倍适|Basics)/i.test(text)) return questionDimension ? `德国PM细胞营养素 大白 Basics ${questionDimension[0]}` : '德国PM细胞营养素 大白 Basics';
|
||||||
if (/(小白产品|小白|维适多|Restorate)/i.test(text)) return questionDimension ? `德国PM细胞营养素 小白 Restorate ${questionDimension[0]}` : '德国PM细胞营养素 小白';
|
if (/(小白产品|小白|维适多|Restorate)/i.test(text)) return questionDimension ? `德国PM细胞营养素 小白 Restorate ${questionDimension[0]}` : '德国PM细胞营养素 小白';
|
||||||
@@ -236,7 +326,7 @@ class ToolExecutor {
|
|||||||
if (/(我们公司.*产品|公司.*产品|产品.*推荐|推荐.*产品|产品有哪些|产品介绍|产品列表)/i.test(text)) return '德国PM FitLine 细胞营养素产品 大白Basics 小红Activize 小白Restorate 儿童倍适';
|
if (/(我们公司.*产品|公司.*产品|产品.*推荐|推荐.*产品|产品有哪些|产品介绍|产品列表)/i.test(text)) return '德国PM FitLine 细胞营养素产品 大白Basics 小红Activize 小白Restorate 儿童倍适';
|
||||||
if (/(治病吗|能治病吗|产品治病|治疗疾病|替代药|是不是药)/i.test(text)) return 'PM产品 不是药 不能替代药物 保健食品 营养补充';
|
if (/(治病吗|能治病吗|产品治病|治疗疾病|替代药|是不是药)/i.test(text)) return 'PM产品 不是药 不能替代药物 保健食品 营养补充';
|
||||||
if (/(多久见效|多久有效|多久能见效|多长时间见效|几天见效|什么时候见效)/i.test(text)) return 'PM产品 多久见效 吸收利用 周期 个体差异';
|
if (/(多久见效|多久有效|多久能见效|多长时间见效|几天见效|什么时候见效)/i.test(text)) return 'PM产品 多久见效 吸收利用 周期 个体差异';
|
||||||
if (/(为什么.*(全套|搭配|三合一)|为什么要.*(全套|搭配|三合一)|产品需要全套)/i.test(text)) return '德国PM细胞营养素 全套搭配 NTC营养保送系统 协同作用';
|
if (/(为什么.*(全套|搭配|三合一)|为什么要.*(全套|搭配|三合一)|为何.*(全套|搭配|三合一)|产品需要全套)/i.test(text)) return '德国PM细胞营养素 全套搭配 NTC营养保送系统 协同作用';
|
||||||
if (/(与其它保健品区别|与其他保健品区别|和其它保健品区别|和其他保健品区别|保健品区别)/i.test(text)) return 'PM产品 与其他保健品区别 NTC营养保送系统 吸收利用';
|
if (/(与其它保健品区别|与其他保健品区别|和其它保健品区别|和其他保健品区别|保健品区别)/i.test(text)) return 'PM产品 与其他保健品区别 NTC营养保送系统 吸收利用';
|
||||||
if (/(新人起步三关|起步三关)/i.test(text)) return '培训新人起步三关';
|
if (/(新人起步三关|起步三关)/i.test(text)) return '培训新人起步三关';
|
||||||
if (/(精品会议|会议组织)/i.test(text)) return '培训打造精品会议具体如下';
|
if (/(精品会议|会议组织)/i.test(text)) return '培训打造精品会议具体如下';
|
||||||
@@ -254,35 +344,66 @@ class ToolExecutor {
|
|||||||
if (/(好转反应|整应反应|排毒反应|副作用|不良反应|皮肤发痒)/i.test(text)) return 'PM产品整应反应好转反应解析';
|
if (/(好转反应|整应反应|排毒反应|副作用|不良反应|皮肤发痒)/i.test(text)) return 'PM产品整应反应好转反应解析';
|
||||||
if (/(促销活动|促销|优惠|打折|活动分数|5\+1)/i.test(text)) return '促销活动 5+1活动分数';
|
if (/(促销活动|促销|优惠|打折|活动分数|5\+1)/i.test(text)) return '促销活动 5+1活动分数';
|
||||||
if (/暖炉原理/i.test(text)) return '火炉原理';
|
if (/暖炉原理/i.test(text)) return '火炉原理';
|
||||||
if (/(CC套装|CC胶囊)/i.test(text)) return 'CC套装 CC胶囊';
|
if (/(CC套装|CC胶囊)/i.test(text)) return questionDimension ? `CC套装 CC胶囊 ${questionDimension[0]}` : 'CC套装 CC胶囊';
|
||||||
if (/(IB5|口腔免疫喷雾)/i.test(text)) return 'IB5口腔免疫喷雾';
|
if (/(IB5|口腔免疫喷雾)/i.test(text)) return questionDimension ? `IB5 口腔免疫喷雾 ${questionDimension[0]}` : 'IB5 口腔免疫喷雾';
|
||||||
if (/(Q10|辅酵素|氧修护)/i.test(text)) return 'Q10辅酵素氧修护';
|
if (/(Q10|辅酵素|氧修护)/i.test(text)) return questionDimension ? `Q10 辅酵素 氧修护 ${questionDimension[0]}` : 'Q10 辅酵素 氧修护';
|
||||||
if (/Women\+/i.test(text)) return 'Women+';
|
if (/(Med Dental\+|Dental\+|草本护理牙膏)/i.test(text)) return questionDimension ? `Med Dental+ 草本护理牙膏 ${questionDimension[0]}` : 'Med Dental+ 草本护理牙膏';
|
||||||
if (/乐活奶昔|乐活/i.test(text)) return '乐活奶昔';
|
if (/(Men Face|全效男士护肤抗衰乳霜)/i.test(text)) return questionDimension ? `Men Face 全效男士护肤抗衰乳霜 ${questionDimension[0]}` : 'Men Face 全效男士护肤抗衰乳霜';
|
||||||
if (/(乳清蛋白|蛋白粉)/i.test(text)) return '乳清蛋白粉';
|
if (/(CC-Cell|CC Cell|CC乳霜)/i.test(text)) return questionDimension ? `CC-Cell 胶囊 乳霜 ${questionDimension[0]}` : 'CC-Cell 胶囊 乳霜';
|
||||||
if (/(乳酪煲|乳酪饮品|乳酪)/i.test(text)) return '乳酪煲 乳酪饮品';
|
if (/(D-Drink|小绿排毒饮|14天排毒D饮料Plus)/i.test(text)) return questionDimension ? `D-Drink 小绿排毒饮 14天排毒D饮料Plus ${questionDimension[0]}` : 'D-Drink 小绿排毒饮 14天排毒D饮料Plus';
|
||||||
if (/(基础二合一|二合一)/i.test(text)) return '基础二合一';
|
if (/(ProShape|ProShape® Amino|氨基酸|支链氨基酸|BCAA)/i.test(text)) return questionDimension ? `ProShape Amino 氨基酸 BCAA ${questionDimension[0]}` : 'ProShape Amino 氨基酸 BCAA';
|
||||||
if (/倍力健/i.test(text)) return '倍力健';
|
if (/(Herbal Tea|草本茶)/i.test(text)) return questionDimension ? `Herbal Tea 草本茶 ${questionDimension[0]}` : 'Herbal Tea 草本茶';
|
||||||
if (/(关节套装|关节舒缓)/i.test(text)) return '关节套装 关节舒缓膏';
|
if (/(Hair\+|med Hair\+|口服发宝|外用发健)/i.test(text)) return questionDimension ? `Hair+ med Hair+ 口服发宝 外用发健 ${questionDimension[0]}` : 'Hair+ med Hair+ 口服发宝 外用发健';
|
||||||
if (/(男士乳霜|男士护肤)/i.test(text)) return '全效男士乳霜';
|
if (/(Fitness-Drink|运动饮料健康饮品|运动饮料)/i.test(text)) return questionDimension ? `Fitness-Drink 运动饮料健康饮品 ${questionDimension[0]}` : 'Fitness-Drink 运动饮料健康饮品';
|
||||||
if (/(去角质|面膜)/i.test(text)) return '去角质面膜';
|
if (/(TopShape|孅萃TopShape纤萃减肥|纤萃减肥)/i.test(text)) return questionDimension ? `TopShape 孅萃TopShape纤萃减肥 ${questionDimension[0]}` : 'TopShape 孅萃TopShape纤萃减肥';
|
||||||
if (/发宝/i.test(text)) return '发宝';
|
if (/(Generation 50\+|乐活50\+)/i.test(text)) return questionDimension ? `乐活50+ Generation 50+ ${questionDimension[0]}` : '乐活50+ Generation 50+';
|
||||||
if (/叶黄素/i.test(text)) return '叶黄素';
|
if (/(Apple Antioxy|苹果细胞抗氧素|Antioxy|Zellschutz|细胞抗氧素)/i.test(text)) return questionDimension ? `Apple Antioxy Zellschutz 细胞抗氧素 ${questionDimension[0]}` : 'Apple Antioxy Zellschutz 细胞抗氧素';
|
||||||
if (/(奶昔)/i.test(text)) return '奶昔';
|
if (/Women\+/i.test(text)) return questionDimension ? `Women+ ${questionDimension[0]}` : 'Women+';
|
||||||
if (/(健康饮品)/i.test(text)) return '健康饮品';
|
if (/乐活奶昔|乐活/i.test(text)) return questionDimension ? `乐活奶昔 ${questionDimension[0]}` : '乐活奶昔';
|
||||||
|
if (/(乳清蛋白|蛋白粉)/i.test(text)) return questionDimension ? `乳清蛋白粉 ${questionDimension[0]}` : '乳清蛋白粉';
|
||||||
|
if (/(乳酪煲|乳酪饮品|乳酪)/i.test(text)) return questionDimension ? `乳酪煲 乳酪饮品 ${questionDimension[0]}` : '乳酪煲 乳酪饮品';
|
||||||
|
if (/(基础二合一|二合一)/i.test(text)) return questionDimension ? `基础二合一 ${questionDimension[0]}` : '基础二合一';
|
||||||
|
if (/倍力健/i.test(text)) return questionDimension ? `倍力健 ${questionDimension[0]}` : '倍力健';
|
||||||
|
if (/(关节套装|关节舒缓)/i.test(text)) return questionDimension ? `关节套装 关节舒缓膏 ${questionDimension[0]}` : '关节套装 关节舒缓膏';
|
||||||
|
if (/(男士乳霜|男士护肤)/i.test(text)) return questionDimension ? `全效男士乳霜 ${questionDimension[0]}` : '全效男士乳霜';
|
||||||
|
if (/(去角质|面膜)/i.test(text)) return questionDimension ? `去角质面膜 ${questionDimension[0]}` : '去角质面膜';
|
||||||
|
if (/发宝/i.test(text)) return questionDimension ? `发宝 ${questionDimension[0]}` : '发宝';
|
||||||
|
if (/叶黄素/i.test(text)) return questionDimension ? `叶黄素 ${questionDimension[0]}` : '叶黄素';
|
||||||
|
if (/(奶昔)/i.test(text)) return questionDimension ? `奶昔 ${questionDimension[0]}` : '奶昔';
|
||||||
|
if (/(健康饮品)/i.test(text)) return questionDimension ? `健康饮品 ${questionDimension[0]}` : '健康饮品';
|
||||||
|
|
||||||
// 第二层:当前文本是追问/代词,才通过上下文推断主题
|
// 第二层:当前文本是追问/代词,才通过上下文推断主题
|
||||||
const isFollowUp = /^(这个|那个|它|该|详细|继续|怎么|为什么|适合谁|什么意思|怎么用|怎么吃|功效|成分|好处|原理)/.test(text);
|
const isFollowUp = /^(这个|那个|它|该|详细|继续|怎么|为什么|适合谁|什么意思|怎么用|怎么吃|功效|成分|好处|原理)/.test(text);
|
||||||
if (isFollowUp) {
|
if (isFollowUp) {
|
||||||
if (/(基础三合一|三合一基础套|基础套装|大白小红小白)/i.test(recentContextText)) return '德国PM细胞营养素 基础套装 大白 小红 小白';
|
if (/(基础三合一|三合一基础套|基础套装|大白小红小白)/i.test(recentContextText)) return questionDimension ? `德国PM细胞营养素 基础套装 大白 小红 小白 ${questionDimension[0]}` : '德国PM细胞营养素 基础套装 大白 小红 小白';
|
||||||
|
if (/(身未动,?梦已成|批发式晋级)/i.test(recentContextText)) return '一成系统 身未动梦已成 批发式晋级 三大平台 四大Ai生态';
|
||||||
|
if (/行动圈/i.test(recentContextText)) return '一成系统 行动圈 数字化工作室 团队管理 目标考核';
|
||||||
|
if (/盟主社区/i.test(recentContextText)) return '一成系统 盟主社区 AI众享 社区盟主 引流 转化';
|
||||||
if (/(一成系统|Ai众享|数字化工作室|盛咖学愿)/i.test(recentContextText)) return '一成系统 德国PM事业发展的强大赋能工具 三大平台 四大Ai生态';
|
if (/(一成系统|Ai众享|数字化工作室|盛咖学愿)/i.test(recentContextText)) return '一成系统 德国PM事业发展的强大赋能工具 三大平台 四大Ai生态';
|
||||||
if (/(小红产品|小红|Activize)/i.test(recentContextText)) return 'Fitline小红产品提升能量原理';
|
if (/DSN/i.test(recentContextText)) return '德国PM DSN 全球100强 欧洲第1';
|
||||||
if (/(大白产品|大白|Basics)/i.test(recentContextText)) return '德国PM细胞营养素 大白 Basics';
|
if (/(ELAB|科隆名单|Halal|GMP)/i.test(recentContextText)) return '德国PM ELAB 科隆名单 Halal GMP 安全认证';
|
||||||
if (/(小白产品|小白|Restorate)/i.test(recentContextText)) return '德国PM细胞营养素 小白';
|
if (/(邓白氏|AAA\+)/i.test(recentContextText)) return '德国PM 邓白氏 AAA+ 99分';
|
||||||
if (/儿童倍适/i.test(recentContextText)) return '儿童倍适';
|
if (/(宣明会|世界宣明会)/i.test(recentContextText)) return '德国PM 宣明会 世界宣明会 慈善合作';
|
||||||
|
if (/(Rolf Sorg|斯派尔|Speyer|卢森堡)/i.test(recentContextText)) return '德国PM Rolf Sorg 斯派尔 卢森堡 总部 公司介绍';
|
||||||
|
if (/(培安|烟台)/i.test(recentContextText)) return '德国PM 培安 烟台 中国市场投资';
|
||||||
|
if (/(小红产品|小红|Activize)/i.test(recentContextText)) return questionDimension ? `Fitline小红产品 Activize ${questionDimension[0]}` : 'Fitline小红产品提升能量原理';
|
||||||
|
if (/(大白产品|大白|Basics)/i.test(recentContextText)) return questionDimension ? `德国PM细胞营养素 大白 Basics ${questionDimension[0]}` : '德国PM细胞营养素 大白 Basics';
|
||||||
|
if (/(小白产品|小白|Restorate)/i.test(recentContextText)) return questionDimension ? `德国PM细胞营养素 小白 Restorate ${questionDimension[0]}` : '德国PM细胞营养素 小白';
|
||||||
|
if (/儿童倍适/i.test(recentContextText)) return questionDimension ? `儿童倍适 ${questionDimension[0]}` : '儿童倍适';
|
||||||
if (/火炉原理/i.test(recentContextText)) return '火炉原理';
|
if (/火炉原理/i.test(recentContextText)) return '火炉原理';
|
||||||
if (/(阿育吠陀|Ayurveda)/i.test(recentContextText)) return '阿育吠陀医学原理';
|
if (/(阿育吠陀|Ayurveda)/i.test(recentContextText)) return '阿育吠陀医学原理';
|
||||||
if (/(NTC营养保送系统)/i.test(recentContextText)) return 'NTC营养保送系统';
|
if (/(NTC营养保送系统)/i.test(recentContextText)) return 'NTC营养保送系统';
|
||||||
|
if (/(Med Dental\+|草本护理牙膏)/i.test(recentContextText)) return questionDimension ? `Med Dental+ 草本护理牙膏 ${questionDimension[0]}` : 'Med Dental+ 草本护理牙膏';
|
||||||
|
if (/(Men Face|全效男士护肤抗衰乳霜)/i.test(recentContextText)) return questionDimension ? `Men Face 全效男士护肤抗衰乳霜 ${questionDimension[0]}` : 'Men Face 全效男士护肤抗衰乳霜';
|
||||||
|
if (/(CC-Cell|CC胶囊|CC乳霜)/i.test(recentContextText)) return questionDimension ? `CC-Cell 胶囊 乳霜 ${questionDimension[0]}` : 'CC-Cell 胶囊 乳霜';
|
||||||
|
if (/(D-Drink|小绿排毒饮|14天排毒D饮料Plus)/i.test(recentContextText)) return questionDimension ? `D-Drink 小绿排毒饮 14天排毒D饮料Plus ${questionDimension[0]}` : 'D-Drink 小绿排毒饮 14天排毒D饮料Plus';
|
||||||
|
if (/(ProShape|氨基酸|BCAA)/i.test(recentContextText)) return questionDimension ? `ProShape Amino 氨基酸 BCAA ${questionDimension[0]}` : 'ProShape Amino 氨基酸 BCAA';
|
||||||
|
if (/(Herbal Tea|草本茶)/i.test(recentContextText)) return questionDimension ? `Herbal Tea 草本茶 ${questionDimension[0]}` : 'Herbal Tea 草本茶';
|
||||||
|
if (/(Hair\+|med Hair\+|口服发宝|外用发健)/i.test(recentContextText)) return questionDimension ? `Hair+ med Hair+ 口服发宝 外用发健 ${questionDimension[0]}` : 'Hair+ med Hair+ 口服发宝 外用发健';
|
||||||
|
if (/(Fitness-Drink|运动饮料健康饮品|运动饮料)/i.test(recentContextText)) return questionDimension ? `Fitness-Drink 运动饮料健康饮品 ${questionDimension[0]}` : 'Fitness-Drink 运动饮料健康饮品';
|
||||||
|
if (/(TopShape|孅萃TopShape纤萃减肥|纤萃减肥)/i.test(recentContextText)) return questionDimension ? `TopShape 孅萃TopShape纤萃减肥 ${questionDimension[0]}` : 'TopShape 孅萃TopShape纤萃减肥';
|
||||||
|
if (/(Generation 50\+|乐活50\+)/i.test(recentContextText)) return questionDimension ? `乐活50+ Generation 50+ ${questionDimension[0]}` : '乐活50+ Generation 50+';
|
||||||
|
if (/(Apple Antioxy|苹果细胞抗氧素|Antioxy|Zellschutz|细胞抗氧素)/i.test(recentContextText)) return questionDimension ? `Apple Antioxy Zellschutz 细胞抗氧素 ${questionDimension[0]}` : 'Apple Antioxy Zellschutz 细胞抗氧素';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -329,24 +450,34 @@ class ToolExecutor {
|
|||||||
return {
|
return {
|
||||||
hit: false,
|
hit: false,
|
||||||
reason: 'empty',
|
reason: 'empty',
|
||||||
reply: `知识库中暂未找到与“${query}”直接相关的信息,请换个更具体的问法再试。`,
|
reply: `知识库中暂未找到与"${query}"直接相关的信息,请换个更具体的问法再试。`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const strictNoHitPattern = /未检索到|没有检索到|没有相关内容|暂无相关内容|未找到相关内容|未找到相关信息|没有找到相关信息|知识库中没有相关内容|知识库中没有关于|知识库中没有找到|没有找到具体|没有.*具体信息|没有.*相关说明|暂未找到与.*直接相关的信息|无法基于知识库.*回答|知识库未明确提到|知识库未提到/;
|
const strictNoHitPattern = /未检索到|没有检索到|没有相关内容|暂无相关内容|未找到相关内容|未找到相关信息|没有找到相关信息|知识库中没有相关内容|知识库中没有关于|知识库中没有找到|没有找到具体|没有.*具体信息|没有.*相关说明|暂未找到与.*直接相关的信息|无法基于知识库.*回答|知识库未明确提到|知识库未提到|很抱歉.*没有.*资料|超出.*知识范围|目前没有.*方面的|无法提供.*相关信息|暂时无法回答|不在.*知识范围|没有.*相关记录/;
|
||||||
if (strictNoHitPattern.test(text)) {
|
if (strictNoHitPattern.test(text)) {
|
||||||
return {
|
return {
|
||||||
hit: false,
|
hit: false,
|
||||||
reason: 'no_hit',
|
reason: 'no_hit',
|
||||||
reply: `知识库中暂未找到与“${query}”直接相关的信息,请换个更具体的问法再试。`,
|
reply: `知识库中暂未找到与"${query}"直接相关的信息,请换个更具体的问法再试。`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.answerMatchesQuestionSlot(query, text)) {
|
if (!this.answerMatchesQuestionSlot(query, text)) {
|
||||||
|
// 长度兜底:回答内容足够长(>=60字)且不含无结果模式时,倾向判定为hit
|
||||||
|
// 这避免了方舟LLM用同义词表达导致slot正则不匹配的误杀
|
||||||
|
if (text.length >= 60 && !strictNoHitPattern.test(text)) {
|
||||||
|
console.log(`[ToolExecutor] slot_mismatch overridden by length fallback: query="${query}" len=${text.length}`);
|
||||||
|
return {
|
||||||
|
hit: true,
|
||||||
|
reason: 'length_fallback',
|
||||||
|
reply: text,
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
hit: false,
|
hit: false,
|
||||||
reason: 'slot_mismatch',
|
reason: 'slot_mismatch',
|
||||||
reply: `知识库中暂未找到与“${query}”直接相关的信息,请换个更具体的问法再试。`,
|
reply: `知识库中暂未找到与"${query}"直接相关的信息,请换个更具体的问法再试。`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,10 +602,30 @@ class ToolExecutor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
// 缓存检查:相同effectiveQuery + datasetIds命中缓存时直接返回,避免重复API调用
|
||||||
|
const cacheKey = getKbCacheKey(effectiveQuery, kbTarget.datasetIds);
|
||||||
|
const cached = getKbCache(cacheKey);
|
||||||
|
if (cached) {
|
||||||
|
const latencyMs = Date.now() - startTime;
|
||||||
|
console.log(`[ToolExecutor] Ark KB cache hit in ${latencyMs}ms key="${cacheKey.slice(0, 60)}"`);
|
||||||
|
return {
|
||||||
|
...cached,
|
||||||
|
original_query: query,
|
||||||
|
rewritten_query: effectiveQuery,
|
||||||
|
selected_dataset_ids: kbTarget.datasetIds,
|
||||||
|
selected_kb_routes: kbTarget.matchedRoutes,
|
||||||
|
latency_ms: latencyMs,
|
||||||
|
cache_hit: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
console.log('[ToolExecutor] Trying Ark Knowledge Search...');
|
console.log('[ToolExecutor] Trying Ark Knowledge Search...');
|
||||||
const result = await this.searchArkKnowledge(effectiveQuery, [], responseMode, kbTarget.datasetIds, query);
|
const result = await this.searchArkKnowledge(effectiveQuery, [], responseMode, kbTarget.datasetIds, query);
|
||||||
const latencyMs = Date.now() - startTime;
|
const latencyMs = Date.now() - startTime;
|
||||||
console.log(`[ToolExecutor] Ark KB search succeeded in ${latencyMs}ms`);
|
console.log(`[ToolExecutor] Ark KB search succeeded in ${latencyMs}ms`);
|
||||||
|
// 仅缓存命中的结果,避免缓存错误或无结果
|
||||||
|
if (result.hit) {
|
||||||
|
setKbCache(cacheKey, result);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
original_query: query,
|
original_query: query,
|
||||||
|
|||||||
Reference in New Issue
Block a user