2026-03-18 17:43:13 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 零 LLM 极速 ASR 修正器
|
2026-03-31 09:46:40 +08:00
|
|
|
|
* 纯字典映射 + 正则 + 拼音模糊匹配,< 5ms
|
2026-03-18 17:43:13 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-03-31 09:46:40 +08:00
|
|
|
|
const { pinyinMatchProducts } = require('./pinyinProductMatcher');
|
|
|
|
|
|
|
2026-03-18 17:43:13 +08:00
|
|
|
|
const PHRASE_MAP = {
|
|
|
|
|
|
'一城系统': '一成系统',
|
|
|
|
|
|
'逸城系统': '一成系统',
|
|
|
|
|
|
'一程系统': '一成系统',
|
|
|
|
|
|
'易成系统': '一成系统',
|
|
|
|
|
|
'一诚系统': '一成系统',
|
|
|
|
|
|
'亦成系统': '一成系统',
|
|
|
|
|
|
'艺成系统': '一成系统',
|
|
|
|
|
|
'溢成系统': '一成系统',
|
|
|
|
|
|
'义成系统': '一成系统',
|
|
|
|
|
|
'毅成系统': '一成系统',
|
|
|
|
|
|
'怡成系统': '一成系统',
|
|
|
|
|
|
'以成系统': '一成系统',
|
|
|
|
|
|
'已成系统': '一成系统',
|
|
|
|
|
|
'亿成系统': '一成系统',
|
|
|
|
|
|
'忆成系统': '一成系统',
|
|
|
|
|
|
'益成系统': '一成系统',
|
|
|
|
|
|
'一乘系统': '一成系统',
|
|
|
|
|
|
'一承系统': '一成系统',
|
|
|
|
|
|
'一丞系统': '一成系统',
|
|
|
|
|
|
'一呈系统': '一成系统',
|
|
|
|
|
|
'一澄系统': '一成系统',
|
|
|
|
|
|
'一橙系统': '一成系统',
|
|
|
|
|
|
'一层系统': '一成系统',
|
|
|
|
|
|
'一趁系统': '一成系统',
|
|
|
|
|
|
'一陈系统': '一成系统',
|
|
|
|
|
|
'依成系统': '一成系统',
|
|
|
|
|
|
'伊成系统': '一成系统',
|
2026-03-24 17:19:36 +08:00
|
|
|
|
'益生系统': '一成系统',
|
|
|
|
|
|
'易诚系统': '一成系统',
|
|
|
|
|
|
'易乘系统': '一成系统',
|
|
|
|
|
|
'一声系统': '一成系统',
|
|
|
|
|
|
'亿生系统': '一成系统',
|
|
|
|
|
|
'义诚系统': '一成系统',
|
|
|
|
|
|
'忆诚系统': '一成系统',
|
|
|
|
|
|
'以诚系统': '一成系统',
|
2026-03-18 17:43:13 +08:00
|
|
|
|
'盛咖学院': '盛咖学愿',
|
|
|
|
|
|
'圣咖学愿': '盛咖学愿',
|
|
|
|
|
|
'盛卡学愿': '盛咖学愿',
|
|
|
|
|
|
'营养配送系统': 'NTC营养保送系统',
|
|
|
|
|
|
'营养输送系统': 'NTC营养保送系统',
|
|
|
|
|
|
'营养传送系统': 'NTC营养保送系统',
|
|
|
|
|
|
'营养传输系统': 'NTC营养保送系统',
|
|
|
|
|
|
'暖炉原理': '火炉原理',
|
|
|
|
|
|
'整应反应': '好转反应',
|
|
|
|
|
|
'整健反应': '好转反应',
|
|
|
|
|
|
'排毒反应': '好转反应',
|
|
|
|
|
|
'5加1': '5+1',
|
|
|
|
|
|
'五加一': '5+1',
|
|
|
|
|
|
'起步三观': '起步三关',
|
|
|
|
|
|
'起步三官': '起步三关',
|
|
|
|
|
|
'doublepm': '德国PM',
|
|
|
|
|
|
'double pm': '德国PM',
|
|
|
|
|
|
'DoublePM': '德国PM',
|
|
|
|
|
|
'Double PM': '德国PM',
|
|
|
|
|
|
'DOUBLEPM': '德国PM',
|
|
|
|
|
|
'DOUBLE PM': '德国PM',
|
|
|
|
|
|
'基础三合一': 'PM细胞营养素 基础套装',
|
|
|
|
|
|
'三合一基础套': 'PM细胞营养素 基础套装',
|
|
|
|
|
|
'大白小红小白': 'PM细胞营养素 基础套装',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const WORD_MAP = {
|
|
|
|
|
|
'一城': '一成', '逸城': '一成', '一程': '一成', '易成': '一成',
|
|
|
|
|
|
'一诚': '一成', '亦成': '一成', '艺成': '一成', '溢成': '一成',
|
|
|
|
|
|
'义成': '一成', '毅成': '一成', '怡成': '一成', '以成': '一成',
|
|
|
|
|
|
'已成': '一成', '亿成': '一成', '忆成': '一成', '益成': '一成',
|
|
|
|
|
|
'一乘': '一成', '一承': '一成', '一丞': '一成', '一呈': '一成',
|
|
|
|
|
|
'一澄': '一成', '一橙': '一成', '一层': '一成', '一陈': '一成',
|
|
|
|
|
|
'依成': '一成', '伊成': '一成',
|
2026-03-24 17:19:36 +08:00
|
|
|
|
'益生': '一成', '易诚': '一成', '义诚': '一成', '忆诚': '一成', '以诚': '一成',
|
|
|
|
|
|
'一声': '一成', '亿生': '一成', '易乘': '一成',
|
2026-03-18 17:43:13 +08:00
|
|
|
|
'大窝': '大沃', '大握': '大沃', '大我': '大沃', '大卧': '大沃',
|
|
|
|
|
|
'爱众享': 'Ai众享', '艾众享': 'Ai众享', '哎众享': 'Ai众享',
|
|
|
|
|
|
'小洪': '小红', '小宏': '小红', '小鸿': '小红',
|
|
|
|
|
|
'大百': '大白', '大柏': '大白',
|
|
|
|
|
|
'小百': '小白', '小柏': '小白', '维适多': '小白',
|
|
|
|
|
|
'营养配送': '营养保送', '营养输送': '营养保送',
|
|
|
|
|
|
'阿玉吠陀': '阿育吠陀', '阿育费陀': '阿育吠陀',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const PRODUCT_ALIAS_MAP = {
|
|
|
|
|
|
'小红': '小红产品 Activize Oxyplus',
|
|
|
|
|
|
'Activize': '小红产品 Activize Oxyplus',
|
|
|
|
|
|
'Activize Oxyplus': '小红产品 Activize Oxyplus',
|
|
|
|
|
|
'大白': '大白产品 Basics',
|
|
|
|
|
|
'Basics': '大白产品 Basics',
|
|
|
|
|
|
'小白': '小白产品 Restorate',
|
|
|
|
|
|
'Restorate': '小白产品 Restorate',
|
|
|
|
|
|
'FitLine': 'PM-FitLine',
|
|
|
|
|
|
'PM FitLine': 'PM-FitLine',
|
|
|
|
|
|
'PM细胞营养': 'PM细胞营养素',
|
|
|
|
|
|
'PM营养素': 'PM细胞营养素',
|
|
|
|
|
|
'德国PM营养素': 'PM细胞营养素',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-31 09:46:40 +08:00
|
|
|
|
// ============ 谐音纠错:产品名同音字变体正则 ============
|
|
|
|
|
|
// ASR 常把产品名个别字识别为同音/近音字,用正则匹配常见变体并纠正
|
|
|
|
|
|
// 4字产品优先匹配,避免3字子串误匹配
|
|
|
|
|
|
const PHONETIC_CORRECTIONS = [
|
|
|
|
|
|
// --- 5字产品 ---
|
|
|
|
|
|
[/[细希西系息][胞苞包宝][抗康][氧养仰样][素速]/, '细胞抗氧素'],
|
|
|
|
|
|
// --- 4字产品 ---
|
|
|
|
|
|
[/[胶交教焦角][原圆远元源][蛋旦但淡][白百柏拍]/, '胶原蛋白'],
|
|
|
|
|
|
[/[白百柏][藜梨黎离莉丽力利理礼里][芦炉路鹿鲁卢露陆][醇纯唇]/, '白藜芦醇'],
|
|
|
|
|
|
[/[好号浩耗][转赚砖专][反返犯翻范][应映影英]/, '好转反应'],
|
|
|
|
|
|
[/[阿啊][育玉域遇雨宇御][吠废费肺飞非][陀驼拖脱托]/, '阿育吠陀'],
|
|
|
|
|
|
[/[骨谷古鼓][骼格隔革各阁][健剑键建][康慷抗]/, '骨骼健康'],
|
|
|
|
|
|
// --- 3字产品 ---
|
|
|
|
|
|
[/[活火获霍货][力利立厉励历丽][健剑键建见件]/, '活力健'],
|
|
|
|
|
|
[/[倍被背贝备辈杯北][力利立厉励历丽][健剑键建见件]/, '倍力健'],
|
|
|
|
|
|
[/[氨安暗按胺][基机鸡积极几计][酸算]/, '氨基酸'],
|
|
|
|
|
|
[/[益意易亿以][生声胜升省圣][菌军均君]/, '益生菌'],
|
|
|
|
|
|
[/[辅付副附府腐][酵教叫觉较角][素速诉]/, '辅酵素'],
|
|
|
|
|
|
[/[葡铺浦蒲][萄逃淘桃陶][籽子紫]/, '葡萄籽'],
|
|
|
|
|
|
[/[排牌拍派][毒独度读督][饮引印隐]/, '排毒饮'],
|
|
|
|
|
|
[/[乳如入][酪烙络落][煲包保宝]/, '乳酪煲'],
|
|
|
|
|
|
[/[草操曹][本苯奔][茶查差]/, '草本茶'],
|
|
|
|
|
|
[/[异意易][黄皇荒慌][酮铜同桐]/, '异黄酮'],
|
|
|
|
|
|
[/[骨谷古鼓][骼格隔革各阁][健剑键建见]/, '骨骼健'],
|
|
|
|
|
|
[/[舒书叔输][采彩菜蔡][健剑键建见]/, '舒采健'],
|
|
|
|
|
|
[/[衡横恒亨][醇纯唇春][饮引印隐]/, '衡醇饮'],
|
|
|
|
|
|
[/[纤先鲜仙][萃翠脆粹催]/, '纤萃'],
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
function phoneticCorrectProducts(text) {
|
|
|
|
|
|
let result = text;
|
|
|
|
|
|
for (const [regex, product] of PHONETIC_CORRECTIONS) {
|
|
|
|
|
|
if (regex.test(result)) {
|
|
|
|
|
|
result = result.replace(regex, product);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 17:43:13 +08:00
|
|
|
|
function escapeRegExp(text) {
|
|
|
|
|
|
return String(text || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function replaceOrderedMappings(text, mapping) {
|
|
|
|
|
|
let result = String(text || '');
|
|
|
|
|
|
const orderedEntries = Object.entries(mapping).sort((a, b) => b[0].length - a[0].length);
|
|
|
|
|
|
for (const [from, to] of orderedEntries) {
|
|
|
|
|
|
if (result.includes(from)) {
|
|
|
|
|
|
result = result.split(from).join(to);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function shouldExpandProductAlias(text, alias) {
|
|
|
|
|
|
if (text === alias) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
const escapedAlias = escapeRegExp(alias);
|
|
|
|
|
|
return new RegExp(`${escapedAlias}(?=\\s|的|是|有|和|跟|及|怎么|为什么|适合谁|什么意思|怎么吃|怎么用|功效|成分|多少钱|哪里买|价格|副作用|区别|哪个好|是什么|呢|吗|呀|啊|哦|吧|啦|了|$)`, 'i').test(text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function correctAsrText(text) {
|
|
|
|
|
|
if (!text || typeof text !== 'string') {
|
|
|
|
|
|
return text || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let result = text.trim();
|
|
|
|
|
|
|
|
|
|
|
|
result = replaceOrderedMappings(result, PHRASE_MAP);
|
|
|
|
|
|
result = replaceOrderedMappings(result, WORD_MAP);
|
|
|
|
|
|
|
2026-03-31 09:46:40 +08:00
|
|
|
|
// 谐音纠错:同音字变体 → 正确产品名(在字典替换之后、别名扩展之前)
|
|
|
|
|
|
result = phoneticCorrectProducts(result);
|
|
|
|
|
|
|
|
|
|
|
|
// 拼音模糊匹配:系统化方案,自动覆盖所有产品名的同音字变体
|
|
|
|
|
|
result = pinyinMatchProducts(result);
|
|
|
|
|
|
|
2026-03-18 17:43:13 +08:00
|
|
|
|
// 激进策略:所有"X+系统"格式(非常见系统词)一律转为"一成系统"
|
2026-03-24 17:19:36 +08:00
|
|
|
|
result = result.replace(/[一二三四五六七八九十壹贰叁肆伍陆柒捌玖拾百千万亿兆零两几单双半多少全数整这那某每各以已亦艺毅怡逸溢义忆益伊依乙翼奕弈邑佚颐译蚁屹役疫裔翊熠旖漪倚绮峄羿轶壹弋驿奕懿肄翌苡圯佾诒铱仡易]{1,2}(?:成|城|程|诚|乘|承|丞|呈|澄|橙|层|陈|趁|撑|称|秤|盛|剩|胜|生|声)系统/g, '一成系统');
|
2026-03-18 17:43:13 +08:00
|
|
|
|
|
|
|
|
|
|
for (const [from, to] of Object.entries(PRODUCT_ALIAS_MAP).sort((a, b) => b[0].length - a[0].length)) {
|
|
|
|
|
|
if (shouldExpandProductAlias(result, from)) {
|
|
|
|
|
|
result = result.split(from).join(to);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
correctAsrText,
|
2026-03-31 09:46:40 +08:00
|
|
|
|
phoneticCorrectProducts,
|
2026-03-18 17:43:13 +08:00
|
|
|
|
PHRASE_MAP,
|
|
|
|
|
|
WORD_MAP,
|
|
|
|
|
|
PRODUCT_ALIAS_MAP,
|
2026-03-31 09:46:40 +08:00
|
|
|
|
PHONETIC_CORRECTIONS,
|
2026-03-18 17:43:13 +08:00
|
|
|
|
};
|