Files
bigwo/test2/server/tests/test_kb_scenarios_extended.js
User 9567eb7358 feat(server): KB prompt优化、字幕修复、S2S重连、助手配置API
- assistantProfileConfig: KB answer prompt改为分层策略(严格产品信息+灵活常识补充)
- nativeVoiceGateway: S2S upstream自动重连(最多50次)、event 351字幕debounce(800ms取最长文本)
- toolExecutor: 确定性query改写增强、KB查询传递session上下文
- contextKeywordTracker: 支持KB话题记忆优先enrichment
- contentSafeGuard: 新增品牌安全内容过滤服务
- assistantProfileService: 新增助手配置CRUD服务
- routes/assistantProfile: 新增助手配置API路由
- knowledgeKeywords: 扩展KB关键词词典
- fastAsrCorrector: ASR纠错规则更新
- tests/: KB prompt测试、保护窗口测试、Viking性能测试
- docs/: 助手配置API文档、系统提示词目录
2026-03-24 17:19:36 +08:00

492 lines
18 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.

/**
* 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');