/** * KB场景深度扩展测试 * 覆盖:更多产品×维度交叉、更多确定性改写、更多追问变体、更多多轮切换、更多热答案、更多边界 * * 运行方式: node --test tests/test_kb_scenarios_extended.js */ const { describe, it } = require('node:test'); const assert = require('node:assert/strict'); const { shouldForceKnowledgeRoute } = require('../services/realtimeDialogRouting'); const { hasKnowledgeRouteKeyword } = require('../services/knowledgeKeywords'); const ToolExecutor = require('../services/toolExecutor'); function assertKbRoute(text, ctx, msg) { assert.equal(shouldForceKnowledgeRoute(text, ctx), true, msg || `"${text}" should route to KB`); } function assertNotKbRoute(text, ctx, msg) { assert.equal(shouldForceKnowledgeRoute(text, ctx), false, msg || `"${text}" should NOT route to KB`); } function buildCtx(pairs) { return pairs.map(([role, content]) => ({ role, content })); } // ================================================================ // 1. 产品×维度全交叉 —— 每个产品 × 每个问题维度 // ================================================================ describe('产品×维度全交叉 —— 大白', () => { const dims = ['怎么吃', '功效是什么', '成分有哪些', '多少钱', '适合谁', '副作用', '哪里买', '保质期']; for (const d of dims) { it(`"大白${d}" → 应走KB`, () => { assertKbRoute(`大白${d}`); }); } }); describe('产品×维度全交叉 —— 小白', () => { const dims = ['怎么吃', '功效是什么', '成分有哪些', '多少钱', '适合谁', '副作用', '哪里买', '怎么服用']; for (const d of dims) { it(`"小白${d}" → 应走KB`, () => { assertKbRoute(`小白${d}`); }); } }); describe('产品×维度全交叉 —— CC套装', () => { const dims = ['怎么用', '功效是什么', '成分有哪些', '多少钱', '适合谁', '副作用', '区别', '包含什么']; for (const d of dims) { it(`"CC套装${d}" → 应走KB`, () => { assertKbRoute(`CC套装${d}`); }); } }); describe('产品×维度全交叉 —— Q10', () => { const dims = ['怎么吃', '功效是什么', '成分', '多少钱', '适合谁', '副作用', '怎么买', '适合什么人']; for (const d of dims) { it(`"Q10${d}" → 应走KB`, () => { assertKbRoute(`Q10${d}`); }); } }); describe('产品×维度全交叉 —— IB5', () => { const dims = ['怎么用', '功效是什么', '成分', '多少钱', '适合谁', '副作用', '什么时候用', '适合什么人']; for (const d of dims) { it(`"IB5${d}" → 应走KB`, () => { assertKbRoute(`IB5${d}`); }); } }); describe('产品×维度全交叉 —— D-Drink', () => { const dims = ['怎么用', '功效是什么', '成分', '多少钱', '适合谁', '副作用', '排毒原理', '喝法']; for (const d of dims) { it(`"D-Drink${d}" → 应走KB`, () => { assertKbRoute(`D-Drink${d}`); }); } }); describe('产品×维度全交叉 —— Hair+', () => { const dims = ['怎么用', '功效是什么', '成分', '多少钱', '适合谁', '副作用']; for (const d of dims) { it(`"Hair+${d}" → 应走KB`, () => { assertKbRoute(`Hair+${d}`); }); } }); describe('产品×维度全交叉 —— 儿童倍适', () => { const dims = ['怎么吃', '功效是什么', '成分', '多少钱', '适合几岁', '副作用', '适合什么人', '口味']; for (const d of dims) { it(`"儿童倍适${d}" → 应走KB`, () => { assertKbRoute(`儿童倍适${d}`); }); } }); describe('产品×维度全交叉 —— 关节套装', () => { const dims = ['怎么用', '功效是什么', '成分', '多少钱', '适合谁', '副作用']; for (const d of dims) { it(`"关节套装${d}" → 应走KB`, () => { assertKbRoute(`关节套装${d}`); }); } }); // ================================================================ // 2. 确定性改写深度覆盖 —— 更多产品规则 // ================================================================ describe('确定性改写深度 —— 更多产品规则', () => { describe('一成系统及子话题', () => { const cases = [ ['一成系统是什么', '一成系统'], ['一成系统怎么用', '一成系统'], ['一成系统三大平台', '一成系统'], ['一成系统行动圈', '一成系统'], ['身未动梦已成', '一成系统'], ['一部手机做天下', '一成系统'], ['如何发展PM事业', '一成系统'], ]; for (const [q, expect] of cases) { it(`"${q}" → 改写含"${expect}"`, () => { const r = ToolExecutor.buildDeterministicKnowledgeQuery(q, []); assert.ok(r && r.includes(expect), `"${q}" → got "${r}"`); }); } }); describe('PM公司相关', () => { const cases = [ ['德国PM公司介绍', 'PM'], ['PM公司背景', 'PM'], ['PM是不是传销', 'PM'], ['PM公司合法吗', 'PM'], ]; for (const [q, expect] of cases) { it(`"${q}" → 改写含"${expect}"`, () => { const r = ToolExecutor.buildDeterministicKnowledgeQuery(q, []); assert.ok(r && r.includes(expect), `"${q}" → got "${r}"`); }); } }); describe('NTC/火炉原理/阿育吠陀', () => { const cases = [ ['NTC营养保送系统是什么', 'NTC'], ['NTC核心优势', 'NTC'], ['火炉原理', '火炉原理'], ['阿育吠陀是什么', '阿育吠陀'], ]; for (const [q, expect] of cases) { it(`"${q}" → 改写含"${expect}"`, () => { const r = ToolExecutor.buildDeterministicKnowledgeQuery(q, []); assert.ok(r && r.includes(expect), `"${q}" → got "${r}"`); }); } }); describe('上下文追问改写 —— 各产品', () => { const ctxCases = [ [{ role: 'assistant', content: '小红Activize是...' }, '成分是什么', 'Activize'], [{ role: 'assistant', content: '小白Restorate帮助修复...' }, '适合谁', 'Restorate'], [{ role: 'assistant', content: 'CC套装含有CC-Cell葡萄籽精华胶囊和乳霜' }, '怎么吃', 'CC'], [{ role: 'assistant', content: 'Hair+发宝防脱发口服发宝外用发健' }, '功效', 'Hair'], [{ role: 'assistant', content: '儿童倍适PowerCocktail Junior适合小朋友' }, '怎么用', '儿童倍适'], [{ role: 'assistant', content: 'D-Drink小绿排毒饮是14天排毒方案' }, '功效是什么', 'D-Drink'], [{ role: 'assistant', content: 'Apple Antioxy Zellschutz细胞抗氧素是独立小袋包装' }, '他的规格是什么', 'Apple Antioxy'], [{ role: 'assistant', content: '小白Restorate建议睡前空腹服用' }, '它一天几次', 'Restorate'], ]; for (const [ctxMsg, query, expect] of ctxCases) { it(`上下文"${ctxMsg.content.slice(0, 10)}..."追问"${query}" → 含"${expect}"`, () => { const r = ToolExecutor.buildDeterministicKnowledgeQuery(query, [ctxMsg]); assert.ok(r && r.includes(expect), `Got "${r}"`); }); } }); describe('无匹配场景', () => { const noMatch = ['你好', '天气怎么样', '讲个故事', '几点了', '我要听音乐']; for (const q of noMatch) { it(`"${q}" → 无改写`, () => { const r = ToolExecutor.buildDeterministicKnowledgeQuery(q, []); assert.equal(r, '', `"${q}" should not rewrite, got "${r}"`); }); } }); }); // ================================================================ // 3. 追问变体全覆盖 —— 各种追问句式 × 不同产品上下文 // ================================================================ describe('追问变体全覆盖 —— 不同追问句式', () => { const followUpPatterns = [ '怎么吃', '怎么用', '功效是什么', '成分是什么', '多少钱', '适合谁', '哪里买', '什么意思', '有什么好处', '怎么服用', '详细说说', '介绍一下', '继续说', '展开说说', '配方', '原理是什么', '适合什么人', '怎么买', '具体内容', ]; const ctxProducts = [ buildCtx([['user', '大白产品'], ['assistant', '大白Basics...']]), buildCtx([['user', '小红功效'], ['assistant', '小红Activize...']]), buildCtx([['user', '一成系统'], ['assistant', '一成系统是...']]), ]; for (let pi = 0; pi < ctxProducts.length; pi++) { for (const fup of followUpPatterns) { it(`ctx${pi + 1} + "${fup}" → 应走KB`, () => { assertKbRoute(fup, ctxProducts[pi]); }); } } }); // ================================================================ // 4. 多KB切换 —— 更多组合 // ================================================================ describe('多KB切换 —— 更多组合模式', () => { it('产品→FAQ→科学: 小红→副作用→NTC原理', () => { assertKbRoute('小红有什么副作用'); assertKbRoute('PM产品有副作用吗'); assertKbRoute('NTC营养保送系统原理'); }); it('事业→公司→产品→FAQ: 招商→PM背景→大白→见效时间', () => { assertKbRoute('招商代理政策'); assertKbRoute('德国PM公司背景'); assertKbRoute('大白怎么吃'); assertKbRoute('多久见效'); }); it('科学→产品→产品→产品: NTC→小红→小白→CC', () => { assertKbRoute('NTC是什么'); assertKbRoute('小红功效'); assertKbRoute('小白成分'); assertKbRoute('CC套装怎么用'); }); it('FAQ→FAQ→FAQ: 传销→副作用→见效→全套搭配', () => { assertKbRoute('PM是传销吗'); assertKbRoute('PM产品有副作用吗'); assertKbRoute('多久能见效'); assertKbRoute('为什么要全套搭配'); }); it('系统→事业→认证: 一成系统→做PM→邓白氏', () => { assertKbRoute('一成系统介绍'); assertKbRoute('怎么做PM事业'); assertKbRoute('邓白氏AAA+认证'); }); it('产品→追问→切换→追问: 大白→怎么吃→小红→功效', () => { assertKbRoute('大白是什么'); const ctx1 = buildCtx([['user', '大白'], ['assistant', '大白Basics...']]); assertKbRoute('怎么吃', ctx1); assertKbRoute('小红产品介绍'); const ctx2 = buildCtx([['user', '小红'], ['assistant', '小红Activize...']]); assertKbRoute('功效是什么', ctx2); }); }); // ================================================================ // 5. 质疑+追问混合 —— 更多组合场景 // ================================================================ describe('质疑+追问混合 —— 更多组合', () => { it('质疑后继续追问详情', () => { assertKbRoute('大白功效'); const ctx = buildCtx([['user', '大白功效'], ['assistant', '大白Basics帮助...']]); assertKbRoute('你搞错了吧', ctx); assertKbRoute('那正确的功效到底是什么', ctx); }); it('追问后发现错误再质疑', () => { assertKbRoute('CC套装包含什么'); const ctx = buildCtx([['user', 'CC套装'], ['assistant', 'CC套装含有...']]); assertKbRoute('具体成分是什么', ctx); assertKbRoute('说的有问题,我记得不是这个成分', ctx); }); it('质疑→纠正→再追问另一个维度', () => { const ctx = buildCtx([['user', '小红怎么吃'], ['assistant', '小红冲服...']]); assertKbRoute('不对,不是冲着喝的', ctx); assertKbRoute('小红到底是什么剂型', ctx); assertKbRoute('小红适合什么人群', ctx); }); it('连续切换产品并质疑', () => { assertKbRoute('大白功效'); assertKbRoute('你说的不对'); assertKbRoute('小红功效'); assertKbRoute('也不对吧'); assertKbRoute('小白功效'); assertKbRoute('说的不准确'); }); it('先闲聊再切到KB质疑', () => { assertNotKbRoute('今天心情不错'); assertKbRoute('对了基础三合一怎么吃'); const ctx = buildCtx([['user', '基础三合一'], ['assistant', '大白小红小白...']]); assertKbRoute('不是这样吧', ctx); }); }); // ================================================================ // 6. 热答案可达性 —— 更多热门问题变体 // ================================================================ describe('热答案可达性 —— 问题变体', () => { const hotVariants = [ // 基础三合一吃法的各种问法 '基础三合一怎么吃', '基础三合一的吃法', '基础套装怎么服用', '大白小红小白怎么吃', // 传销/合法性的各种问法 'PM是不是传销', 'PM合法吗', 'PM是传销吗', 'PM正规吗', // NTC 'NTC是什么', 'NTC核心优势是什么', 'NTC营养保送系统', // 见效时间 '多久见效', '多久能见效', '吃多久有效果', // 好转反应 '好转反应', '好转反应是什么', '吃了不舒服正常吗', // 公司 '德国PM公司介绍', 'PM公司背景', 'PM公司怎么样', // 价格 '多少钱', '产品价格', '产品贵不贵', ]; for (const q of hotVariants) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); // ================================================================ // 7. 口语化查询 —— 更多自然说法 // ================================================================ describe('口语化查询 —— 真实用户的自然说法', () => { const colloquial = [ '你们公司是做什么的', '你们那个产品怎么吃', '咱们这个东西多少钱', '那个什么三合一是什么', '帮我介绍一下你们产品', '我想了解PM产品', '说说你们公司', '讲讲那个什么系统', '查查那个产品', '帮我查一下基础三合一', '帮我问一下价格', '你们产品正规吗', '你们那个东西靠谱吗', '说说那个什么功效', '我想知道怎么加入', '你们卖的是什么东西', '健康产品有哪些', '帮我看看成分', '你们的东西有什么用', '咱吃这个有好处吗', ]; for (const q of colloquial) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); // ================================================================ // 8. 负面边界 —— 更多不应走KB的场景 // ================================================================ describe('负面边界 —— 更多不应走KB的场景', () => { const negatives = [ '', ' ', '?', '!!!', '。。。', '好', '嗯', '哦', '行', '啊', '对', '是的', '好的好的', '知道了知道了', '哈哈哈哈', '嘻嘻', '666', '999', '111', '早', '晚安', 'ok', 'OK', 'hello', 'hi', 'bye', 'good', '今天天气真好', '我要睡觉了', '你是AI吗', '你能做什么', ]; for (const text of negatives) { it(`"${text}" → 不应走KB`, () => { assertNotKbRoute(text); }); } }); // ================================================================ // 9. 关键词嵌入长句 —— 确保关键词在句中也能命中 // ================================================================ describe('关键词嵌入长句 —— 子串匹配验证', () => { const embedded = [ ['我想了解一下基础三合一的功效', '基础三合一嵌入'], ['请问德国PM公司在哪个城市', '公司嵌入'], ['你能帮我查查一成系统怎么用吗', '一成系统嵌入'], ['我朋友推荐我吃小红你能介绍下吗', '小红嵌入'], ['听说有个叫NTC的营养保送系统', 'NTC嵌入'], ['好转反应的话应该怎么处理', '好转反应嵌入'], ['孕妇是不是不能吃PM的产品', '孕妇嵌入'], ['为什么说要全套搭配使用呢', '全套搭配嵌入'], ['听人家说邓白氏AAA+评级很厉害', '邓白氏嵌入'], ['想问一下儿童倍适几岁能吃', '儿童倍适嵌入'], ['我对火炉原理很感兴趣', '火炉原理嵌入'], ['请介绍一下CC套装的功效', 'CC套装嵌入'], ['Q10辅酵素是做什么用的', 'Q10嵌入'], ['Hair+发宝真的能防脱吗', 'Hair+嵌入'], ['想知道D-Drink排毒的原理', 'D-Drink嵌入'], ]; for (const [text, label] of embedded) { it(`${label}: "${text}" → 应走KB`, () => { assertKbRoute(text); }); } }); // ================================================================ // 10. 同义问法覆盖 —— 相同意图不同表达 // ================================================================ describe('同义问法 —— 相同意图不同表达', () => { describe('询问功效的N种方式', () => { const efficacyCases = [ '大白有什么功效', '大白的作用是什么', '大白有什么用', '大白好在哪', '吃大白有什么好处', ]; for (const q of efficacyCases) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); describe('询问用法的N种方式', () => { const usageCases = [ '小红怎么吃', '小红怎么服用', '小红怎么用', '小红的吃法', '小红用法', ]; for (const q of usageCases) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); describe('询问价格的N种方式', () => { const priceCases = [ '基础三合一多少钱', '基础三合一价格', '基础三合一贵不贵', '产品多少钱', '产品价格表', ]; for (const q of priceCases) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); describe('询问合法性的N种方式', () => { const legalCases = [ 'PM是不是传销', 'PM正规吗', 'PM合法吗', 'PM是传销吗', 'PM是直销还是传销', 'PM靠谱吗', ]; for (const q of legalCases) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); describe('询问公司的N种方式', () => { const companyCases = [ 'PM公司介绍', '德国PM公司怎么样', 'PM公司成立多久了', 'PM公司实力如何', ]; for (const q of companyCases) { it(`"${q}" → 应走KB`, () => { assertKbRoute(q); }); } }); }); // ================================================================ // 11. 特殊人群×产品交叉 // ================================================================ describe('特殊人群×产品 —— 能不能吃的场景', () => { const groups = ['孕妇', '小孩', '老人', '糖尿病人', '高血压']; const products = ['大白', '小红', '基础三合一', 'PM产品']; for (const g of groups) { for (const p of products) { it(`"${g}能吃${p}吗" → 应走KB`, () => { assertKbRoute(`${g}能吃${p}吗`); }); } } }); console.log('\n=== KB场景扩展测试加载完成 ===\n');