- 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文档、系统提示词目录
594 lines
23 KiB
JavaScript
594 lines
23 KiB
JavaScript
/**
|
||
* 基于知识库实际内容的功能性测试
|
||
* 覆盖:单KB查询、多KB查询(话题切换)、追问+质疑混合、确定性改写、热答案匹配
|
||
*
|
||
* 运行方式: node --test tests/test_kb_scenarios.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('单KB查询 —— 产品类', () => {
|
||
const productQueries = [
|
||
['基础三合一怎么吃', '基础三合一'],
|
||
['大白产品有什么功效', '大白Basics'],
|
||
['小红Activize的作用是什么', '小红Activize'],
|
||
['小白Restorate怎么服用', '小白Restorate'],
|
||
['儿童倍适适合几岁的孩子', '儿童倍适'],
|
||
['CC套装怎么用', 'CC套装'],
|
||
['Q10辅酵素有什么功效', 'Q10辅酵素'],
|
||
['IB5口腔喷雾怎么用', 'IB5口腔喷雾'],
|
||
['D-Drink小绿排毒饮怎么用', 'D-Drink'],
|
||
['Hair+发宝怎么用', 'Hair+发宝'],
|
||
['运动饮料Fitness-Drink是什么', 'Fitness-Drink'],
|
||
['TopShape纤萃减肥产品', 'TopShape'],
|
||
['Generation 50+乐活产品', 'Generation 50+'],
|
||
['Apple Antioxy细胞抗氧素功效', 'Apple Antioxy'],
|
||
['ProShape氨基酸BCAA是什么', 'ProShape'],
|
||
['Herbal Tea草本茶功效', 'Herbal Tea'],
|
||
['Med Dental+草本护理牙膏', 'Med Dental+'],
|
||
['Men Face男士护肤乳霜', 'Men Face'],
|
||
['叶黄素产品怎么吃', '叶黄素'],
|
||
['关节套装关节舒缓怎么用', '关节套装'],
|
||
['乳清蛋白粉适合谁', '乳清蛋白'],
|
||
['乐活奶昔怎么喝', '乐活奶昔'],
|
||
];
|
||
|
||
for (const [query, label] of productQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('单KB查询 —— 系统/平台类', () => {
|
||
const systemQueries = [
|
||
['一成系统是什么', '一成系统'],
|
||
['三大平台介绍一下', '三大平台'],
|
||
['四大AI生态是什么', '四大AI生态'],
|
||
['行动圈怎么用', '行动圈'],
|
||
['盟主社区是什么', '盟主社区'],
|
||
['AI众享是什么', 'AI众享'],
|
||
['数字化工作室怎么用', '数字化工作室'],
|
||
['盛咖学愿培训平台', '盛咖学愿'],
|
||
];
|
||
|
||
for (const [query, label] of systemQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('单KB查询 —— 科学原理类', () => {
|
||
const scienceQueries = [
|
||
['NTC营养保送系统是什么原理', 'NTC'],
|
||
['火炉原理是什么意思', '火炉原理'],
|
||
['阿育吠陀是什么', '阿育吠陀'],
|
||
];
|
||
|
||
for (const [query, label] of scienceQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('单KB查询 —— 公司/认证类', () => {
|
||
const companyQueries = [
|
||
['德国PM公司介绍', '德国PM'],
|
||
['PM公司地址和电话', '地址电话'],
|
||
['邓白氏认证是什么', '邓白氏'],
|
||
['DSN全球100强', 'DSN'],
|
||
['ELAB科隆名单认证', 'ELAB'],
|
||
['PM是不是传销', '合法性'],
|
||
['Rolf Sorg是谁', '创始人'],
|
||
['宣明会慈善合作', '宣明会'],
|
||
['培安烟台工厂', '培安'],
|
||
];
|
||
|
||
for (const [query, label] of companyQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('单KB查询 —— FAQ常见问题类', () => {
|
||
const faqQueries = [
|
||
['PM产品多久见效', '见效时间'],
|
||
['好转反应是什么', '好转反应'],
|
||
['为什么要全套搭配使用', '全套搭配'],
|
||
['和其他保健品有什么区别', '保健品区别'],
|
||
['孕妇能吃PM产品吗', '特殊人群'],
|
||
['产品多少钱', '价格'],
|
||
['怎么加入PM', '加入方式'],
|
||
['PM产品能治病吗', '治病声明'],
|
||
];
|
||
|
||
for (const [query, label] of faqQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('单KB查询 —— 事业发展类', () => {
|
||
const businessQueries = [
|
||
['如何发展PM事业', '事业发展'],
|
||
['线上拓客怎么做', '线上拓客'],
|
||
['招商代理政策', '招商'],
|
||
['新人起步三关是什么', '新人培训'],
|
||
['为什么选择德国PM', '选择理由'],
|
||
['陌生客户怎么沟通PM事业', '陌生沟通'],
|
||
];
|
||
|
||
for (const [query, label] of businessQueries) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
// ================================================================
|
||
// 2. 多KB查询 —— 话题切换场景
|
||
// ================================================================
|
||
describe('多KB查询 —— 话题切换(同一会话中切换不同产品/话题)', () => {
|
||
|
||
it('场景1: 大白→小红→小白 三个产品连续查询', () => {
|
||
assertKbRoute('大白产品功效是什么');
|
||
|
||
const ctx1 = buildCtx([['user', '大白产品功效是什么'], ['assistant', '德国PM大白Basics是基础营养素...']]);
|
||
assertKbRoute('那小红呢', ctx1);
|
||
|
||
const ctx2 = buildCtx([
|
||
['user', '大白产品功效是什么'], ['assistant', '德国PM大白Basics...'],
|
||
['user', '那小红呢'], ['assistant', 'FitLine小红Activize...'],
|
||
]);
|
||
assertKbRoute('小白怎么吃', ctx2);
|
||
});
|
||
|
||
it('场景2: 产品→公司→再回到产品', () => {
|
||
assertKbRoute('基础三合一介绍一下');
|
||
|
||
assertKbRoute('德国PM公司是什么时候成立的');
|
||
|
||
const ctx = buildCtx([
|
||
['user', '德国PM公司介绍'], ['assistant', '德国PM-International是1993年创立的...'],
|
||
]);
|
||
assertKbRoute('那他们的产品有哪些', ctx);
|
||
});
|
||
|
||
it('场景3: 产品→一成系统→培训', () => {
|
||
assertKbRoute('CC套装怎么用');
|
||
assertKbRoute('一成系统是什么');
|
||
assertKbRoute('新人起步三关怎么做');
|
||
});
|
||
|
||
it('场景4: 科学原理→产品→FAQ', () => {
|
||
assertKbRoute('NTC营养保送系统原理');
|
||
assertKbRoute('大白Basics功效');
|
||
assertKbRoute('多久能见效');
|
||
});
|
||
|
||
it('场景5: 合法性→公司→产品→事业', () => {
|
||
assertKbRoute('PM是不是传销');
|
||
assertKbRoute('邓白氏AAA+是什么');
|
||
const ctx5 = buildCtx([['user', '邓白氏AAA+'], ['assistant', '邓白氏是全球最权威的商业信用评估机构...']]);
|
||
assertKbRoute('那产品有哪些', ctx5);
|
||
assertKbRoute('怎么加入PM事业');
|
||
});
|
||
|
||
it('场景6: 快速切换5个不同产品', () => {
|
||
const products = [
|
||
'D-Drink排毒饮怎么用',
|
||
'Q10辅酵素功效',
|
||
'IB5口腔喷雾是什么',
|
||
'叶黄素怎么吃',
|
||
'关节套装适合谁',
|
||
];
|
||
for (const q of products) {
|
||
assertKbRoute(q);
|
||
}
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 3. 追问场景(有上下文)
|
||
// ================================================================
|
||
describe('追问场景 —— 基于上下文的知识库追问', () => {
|
||
|
||
it('聊基础三合一后追问"怎么吃"', () => {
|
||
const ctx = buildCtx([['user', '基础三合一介绍'], ['assistant', '基础三合一包含大白小红小白...']]);
|
||
assertKbRoute('怎么吃', ctx);
|
||
});
|
||
|
||
it('聊小红后追问"功效是什么"', () => {
|
||
const ctx = buildCtx([['user', '小红产品'], ['assistant', 'FitLine小红Activize Oxyplus...']]);
|
||
assertKbRoute('功效是什么', ctx);
|
||
});
|
||
|
||
it('聊一成系统后追问"怎么用"', () => {
|
||
const ctx = buildCtx([['user', '一成系统介绍'], ['assistant', '一成系统是德国PM事业...']]);
|
||
assertKbRoute('怎么用', ctx);
|
||
});
|
||
|
||
it('聊CC套装后追问"适合谁"', () => {
|
||
const ctx = buildCtx([['user', 'CC套装功效'], ['assistant', 'CC套装含有葡萄籽提取物...']]);
|
||
assertKbRoute('适合谁', ctx);
|
||
});
|
||
|
||
it('聊火炉原理后追问"什么意思"', () => {
|
||
const ctx = buildCtx([['user', '火炉原理'], ['assistant', '火炉原理是PM产品的核心理念...']]);
|
||
assertKbRoute('什么意思', ctx);
|
||
});
|
||
|
||
it('聊D-Drink后追问"多少钱"', () => {
|
||
const ctx = buildCtx([['user', 'D-Drink小绿怎么用'], ['assistant', 'D-Drink小绿是14天排毒饮料...']]);
|
||
assertKbRoute('多少钱', ctx);
|
||
});
|
||
|
||
it('聊邓白氏后追问"什么意思"', () => {
|
||
const ctx = buildCtx([['user', '邓白氏AAA+认证'], ['assistant', '邓白氏是全球最权威的...']]);
|
||
assertKbRoute('什么意思', ctx);
|
||
});
|
||
|
||
it('聊NTC后追问"有什么好处"', () => {
|
||
const ctx = buildCtx([['user', 'NTC营养保送系统'], ['assistant', 'NTC营养保送系统是PM的核心技术...']]);
|
||
assertKbRoute('有什么好处', ctx);
|
||
});
|
||
|
||
it('聊Hair+后追问"成分是什么"', () => {
|
||
const ctx = buildCtx([['user', 'Hair+发宝怎么用'], ['assistant', 'Hair+包含口服发宝和外用发健...']]);
|
||
assertKbRoute('成分是什么', ctx);
|
||
});
|
||
|
||
it('用代词追问"这个产品怎么吃"', () => {
|
||
const ctx = buildCtx([['user', '小白Restorate功效'], ['assistant', '德国PM小白Restorate的核心功效是夜间修复...']]);
|
||
assertKbRoute('这个产品怎么吃', ctx);
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 4. KB查询 + 质疑混合场景
|
||
// ================================================================
|
||
describe('KB查询+质疑混合 —— 用户查询后对回答产生质疑', () => {
|
||
|
||
it('场景1: 问基础三合一→AI说冲剂→用户纠正"不对,是胶囊"', () => {
|
||
assertKbRoute('基础三合一怎么吃');
|
||
const ctx = buildCtx([['user', '基础三合一怎么吃'], ['assistant', '基础三合一这样吃...']]);
|
||
assertKbRoute('不对,是胶囊不是冲剂', ctx);
|
||
});
|
||
|
||
it('场景2: 问小红功效→用户质疑"你搞错了吧"', () => {
|
||
assertKbRoute('小红产品功效');
|
||
const ctx = buildCtx([['user', '小红产品功效'], ['assistant', '小红Activize...']]);
|
||
assertKbRoute('你搞错了吧', ctx);
|
||
});
|
||
|
||
it('场景3: 问NTC原理→用户说"我听说不是这样的"', () => {
|
||
assertKbRoute('NTC营养保送系统原理');
|
||
const ctx = buildCtx([['user', 'NTC原理'], ['assistant', 'NTC营养保送系统...']]);
|
||
assertKbRoute('我听说不是这样的', ctx);
|
||
});
|
||
|
||
it('场景4: 问传销→用户要求"再查一下"', () => {
|
||
assertKbRoute('PM是不是传销');
|
||
const ctx = buildCtx([['user', 'PM是不是传销'], ['assistant', '德国PM不是传销...']]);
|
||
assertKbRoute('你再查查,我看网上说法不一样', ctx);
|
||
});
|
||
|
||
it('场景5: 问价格→用户质疑"太贵了,你确定吗"', () => {
|
||
assertKbRoute('产品多少钱');
|
||
const ctx = buildCtx([['user', '产品多少钱'], ['assistant', '产品价格因国家和地区有所不同...']]);
|
||
assertKbRoute('你确定吗,怎么那么贵', ctx);
|
||
});
|
||
|
||
it('场景6: 问CC套装→用户说"明明是乳霜不是胶囊"', () => {
|
||
assertKbRoute('CC套装是什么');
|
||
const ctx = buildCtx([['user', 'CC套装'], ['assistant', 'CC套装包含CC-Cell胶囊和乳霜...']]);
|
||
assertKbRoute('明明是乳霜不是胶囊', ctx);
|
||
});
|
||
|
||
it('场景7: 问好转反应→用户说"骗人的吧"', () => {
|
||
assertKbRoute('好转反应是怎么回事');
|
||
const ctx = buildCtx([['user', '好转反应'], ['assistant', '这是正常的好转反应...']]);
|
||
assertKbRoute('骗人的吧,有科学依据吗', ctx);
|
||
});
|
||
|
||
it('场景8: 问一成系统→用户说"跟我了解的不一样"', () => {
|
||
assertKbRoute('一成系统介绍');
|
||
const ctx = buildCtx([['user', '一成系统'], ['assistant', '一成系统是德国PM事业发展的智能赋能工具...']]);
|
||
assertKbRoute('跟我了解的不一样啊', ctx);
|
||
});
|
||
|
||
it('场景9: 问D-Drink→用户说"这个是泡着喝的吧"', () => {
|
||
assertKbRoute('D-Drink怎么用');
|
||
const ctx = buildCtx([['user', 'D-Drink怎么用'], ['assistant', 'D-Drink小绿是14天排毒饮料...']]);
|
||
assertKbRoute('这个是泡着喝的吧', ctx);
|
||
});
|
||
|
||
it('场景10: 问邓白氏→用户说"谁说的?有什么根据"', () => {
|
||
assertKbRoute('邓白氏评级是什么');
|
||
const ctx = buildCtx([['user', '邓白氏'], ['assistant', '邓白氏是全球最权威的商业信用评估机构...']]);
|
||
assertKbRoute('谁说的?有什么根据', ctx);
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 5. 多轮复杂对话场景(查询→追问→质疑→再查→切换话题)
|
||
// ================================================================
|
||
describe('多轮复杂对话 —— 模拟真实用户完整会话', () => {
|
||
|
||
it('5轮对话: 产品查询→追问→质疑→纠正→切换话题', () => {
|
||
// 轮1: 直接问产品
|
||
assertKbRoute('基础三合一是什么');
|
||
|
||
// 轮2: 追问怎么吃
|
||
const ctx2 = buildCtx([['user', '基础三合一是什么'], ['assistant', '基础三合一包含大白小红小白...']]);
|
||
assertKbRoute('怎么吃', ctx2);
|
||
|
||
// 轮3: 质疑回答
|
||
const ctx3 = buildCtx([
|
||
['user', '基础三合一是什么'], ['assistant', '基础三合一包含大白小红小白...'],
|
||
['user', '怎么吃'], ['assistant', '大白早上空腹1平勺...'],
|
||
]);
|
||
assertKbRoute('你说的温度不对吧', ctx3);
|
||
|
||
// 轮4: 用户纠正
|
||
assertKbRoute('应该是40度以下的水', ctx3);
|
||
|
||
// 轮5: 切换到完全不同的话题
|
||
assertKbRoute('PM是不是传销');
|
||
});
|
||
|
||
it('6轮对话: 系统→子功能→质疑→公司→产品→FAQ', () => {
|
||
assertKbRoute('一成系统介绍');
|
||
|
||
const ctx2 = buildCtx([['user', '一成系统'], ['assistant', '一成系统是德国PM事业发展的智能赋能工具...']]);
|
||
assertKbRoute('行动圈是什么', ctx2);
|
||
|
||
const ctx3 = buildCtx([['user', '一成系统'], ['assistant', '一成系统是德国PM事业发展的智能赋能工具...'], ['user', '行动圈是什么'], ['assistant', '行动圈是数字化工作室里的团队管理功能...']]);
|
||
assertKbRoute('好像不是这样吧', ctx3);
|
||
|
||
assertKbRoute('德国PM公司背景');
|
||
assertKbRoute('大白产品功效');
|
||
assertKbRoute('孕妇能吃吗');
|
||
});
|
||
|
||
it('4轮对话: 连续质疑同一个话题', () => {
|
||
assertKbRoute('火炉原理是什么');
|
||
|
||
const ctx1 = buildCtx([['user', '火炉原理'], ['assistant', '火炉原理是PM产品的核心理念比喻...']]);
|
||
assertKbRoute('不对,我记得不是这么说的', ctx1);
|
||
assertKbRoute('你再查查,应该是另一种说法', ctx1);
|
||
assertKbRoute('算了,到底是什么意思', ctx1);
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 6. 确定性改写验证
|
||
// ================================================================
|
||
describe('确定性改写 —— buildDeterministicKnowledgeQuery', () => {
|
||
|
||
describe('产品直接改写', () => {
|
||
const cases = [
|
||
['基础三合一怎么吃', '基础套装'],
|
||
['大白产品', 'Basics'],
|
||
['小红Activize功效', 'Activize'],
|
||
['小白Restorate成分', 'Restorate'],
|
||
['儿童倍适怎么吃', '儿童倍适'],
|
||
['CC套装功效', 'CC'],
|
||
['Q10辅酵素作用', 'Q10'],
|
||
['IB5口腔喷雾', 'IB5'],
|
||
['D-Drink排毒', 'D-Drink'],
|
||
];
|
||
|
||
for (const [query, expectContain] of cases) {
|
||
it(`"${query}" → 改写应含"${expectContain}"`, () => {
|
||
const result = ToolExecutor.buildDeterministicKnowledgeQuery(query, []);
|
||
assert.ok(result, `Should have deterministic rewrite for "${query}"`);
|
||
assert.ok(result.includes(expectContain),
|
||
`Rewrite should contain "${expectContain}", got "${result}"`);
|
||
});
|
||
}
|
||
});
|
||
|
||
describe('上下文追问改写', () => {
|
||
it('上下文有"大白"时追问"怎么吃" → 改写含Basics', () => {
|
||
const ctx = [{ role: 'assistant', content: '大白Basics是基础营养素...' }];
|
||
const result = ToolExecutor.buildDeterministicKnowledgeQuery('怎么吃', ctx);
|
||
assert.ok(result, 'Should rewrite');
|
||
assert.ok(result.includes('Basics'), `Should contain Basics, got "${result}"`);
|
||
});
|
||
|
||
it('上下文有"一成系统"时追问"怎么用" → 改写含一成系统', () => {
|
||
const ctx = [{ role: 'assistant', content: '一成系统是德国PM事业发展...' }];
|
||
const result = ToolExecutor.buildDeterministicKnowledgeQuery('怎么用', ctx);
|
||
assert.ok(result, 'Should rewrite');
|
||
assert.ok(result.includes('一成系统'), `Should contain 一成系统, got "${result}"`);
|
||
});
|
||
|
||
it('上下文有"火炉原理"时追问"什么意思" → 改写含火炉原理', () => {
|
||
const ctx = [{ role: 'assistant', content: '火炉原理是PM产品的核心理念...' }];
|
||
const result = ToolExecutor.buildDeterministicKnowledgeQuery('什么意思', ctx);
|
||
assert.ok(result === '火炉原理', `Should rewrite to 火炉原理, got "${result}"`);
|
||
});
|
||
});
|
||
|
||
describe('无匹配时返回空', () => {
|
||
it('"今天天气好" → 无确定性改写', () => {
|
||
const result = ToolExecutor.buildDeterministicKnowledgeQuery('今天天气好', []);
|
||
assert.equal(result, '', 'Chitchat should not have deterministic rewrite');
|
||
});
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 7. 热答案匹配测试
|
||
// ================================================================
|
||
describe('热答案匹配 —— matchHotAnswer 验证', () => {
|
||
// matchHotAnswer是模块内部函数,通过ToolExecutor.execute间接调用
|
||
// 这里直接测试确定性改写+KB路由来验证热答案可达性
|
||
|
||
const hotTopics = [
|
||
['基础三合一怎么吃', '基础三合一吃法'],
|
||
['PM是不是传销', '合法性'],
|
||
['NTC核心优势是什么', 'NTC核心优势'],
|
||
['多久见效', '见效时间'],
|
||
['为什么要全套搭配', '全套搭配原因'],
|
||
['好转反应是什么', '好转反应'],
|
||
['德国PM公司介绍', '公司介绍'],
|
||
['小红功效', '小红功效'],
|
||
['大白功效', '大白功效'],
|
||
['小白功效', '小白功效'],
|
||
['和其他保健品有什么区别', '保健品区别'],
|
||
['CC套装怎么用', 'CC套装'],
|
||
['Q10功效', 'Q10功效'],
|
||
['IB5怎么用', 'IB5'],
|
||
['邓白氏AAA+是什么', '邓白氏'],
|
||
['一成系统是什么', '一成系统'],
|
||
['火炉原理是什么', '火炉原理'],
|
||
['D-Drink排毒饮怎么用', 'D-Drink'],
|
||
['怎么加入PM', '加入方式'],
|
||
['孕妇能吃PM产品吗', '特殊人群'],
|
||
['多少钱', '价格'],
|
||
];
|
||
|
||
for (const [query, label] of hotTopics) {
|
||
it(`${label}: "${query}" → 应路由到KB(热答案可达)`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
// ================================================================
|
||
// 8. 问题维度交叉测试(同一产品不同问法)
|
||
// ================================================================
|
||
describe('问题维度交叉 —— 同一产品的不同问法', () => {
|
||
|
||
describe('基础三合一 × 多维度', () => {
|
||
const dimensions = [
|
||
'基础三合一怎么吃',
|
||
'基础三合一功效',
|
||
'基础三合一成分',
|
||
'基础三合一多少钱',
|
||
'基础三合一适合谁',
|
||
'为什么要全套搭配三合一',
|
||
];
|
||
for (const q of dimensions) {
|
||
it(`"${q}" → 应走KB`, () => { assertKbRoute(q); });
|
||
}
|
||
});
|
||
|
||
describe('小红 × 多维度', () => {
|
||
const dimensions = [
|
||
'小红怎么吃',
|
||
'小红功效是什么',
|
||
'小红成分有哪些',
|
||
'小红副作用',
|
||
'小红多少钱',
|
||
'小红适合什么人',
|
||
];
|
||
for (const q of dimensions) {
|
||
it(`"${q}" → 应走KB`, () => { assertKbRoute(q); });
|
||
}
|
||
});
|
||
|
||
describe('一成系统 × 多维度', () => {
|
||
const dimensions = [
|
||
'一成系统是什么',
|
||
'一成系统核心竞争力',
|
||
'一成系统怎么用',
|
||
'一成系统三大平台',
|
||
'一成系统AI智能生产力',
|
||
'一成系统线上拓客',
|
||
'一成系统邀约话术',
|
||
'一成系统文化',
|
||
];
|
||
for (const q of dimensions) {
|
||
it(`"${q}" → 应走KB`, () => { assertKbRoute(q); });
|
||
}
|
||
});
|
||
});
|
||
|
||
// ================================================================
|
||
// 9. 口语/ASR变体测试(语音识别的常见错误变体)
|
||
// ================================================================
|
||
describe('口语/ASR变体 —— 语音识别常见变体', () => {
|
||
const asrVariants = [
|
||
['移程系统', '一成系统ASR变体'],
|
||
['PM细胞营养素', 'PM产品'],
|
||
['暖炉原理', '火炉原理ASR变体'],
|
||
['产品有哪些', '产品列表'],
|
||
['你们公司产品', '口语化'],
|
||
['这个东西怎么吃', '口语化追问'],
|
||
['咱们公司介绍一下', '口语化公司'],
|
||
];
|
||
|
||
for (const [query, label] of asrVariants) {
|
||
it(`${label}: "${query}" → 应走KB路由`, () => {
|
||
assertKbRoute(query);
|
||
});
|
||
}
|
||
});
|
||
|
||
// ================================================================
|
||
// 10. 边界测试
|
||
// ================================================================
|
||
describe('边界测试', () => {
|
||
it('空字符串 → 不走KB', () => {
|
||
assertNotKbRoute('');
|
||
});
|
||
|
||
it('纯标点 → 不走KB', () => {
|
||
assertNotKbRoute('???');
|
||
});
|
||
|
||
it('单字"好" → 不走KB', () => {
|
||
assertNotKbRoute('好');
|
||
});
|
||
|
||
it('纯数字 → 不走KB', () => {
|
||
assertNotKbRoute('123456');
|
||
});
|
||
|
||
it('超长无意义文本 → 不走KB', () => {
|
||
assertNotKbRoute('啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊');
|
||
});
|
||
|
||
it('纯英文闲聊 → 不走KB', () => {
|
||
assertNotKbRoute('hello how are you');
|
||
});
|
||
|
||
it('含KB关键词但实际是闲聊的边界', () => {
|
||
// "不对"是质疑词,会被检测为KB follow-up
|
||
// 需要context才会真正路由到KB
|
||
// 无context时: hasKnowledgeRouteKeyword('不对') → true (因为加了质疑词)
|
||
// 这是预期行为:宁可多查一次KB,也不要漏掉用户质疑
|
||
const result = shouldForceKnowledgeRoute('不对');
|
||
assert.equal(typeof result, 'boolean', 'Should return boolean');
|
||
});
|
||
});
|
||
|
||
console.log('\n=== KB场景测试加载完成 ===\n');
|