refactor(server): optimize KB retrieval and voice context

This commit is contained in:
User
2026-03-31 09:46:40 +08:00
parent 56940676f6
commit 5b824cd16a
15 changed files with 3135 additions and 143 deletions

View File

@@ -139,6 +139,13 @@ function isKnowledgeFollowUp(text) {
return subjectActionRegex.test(normalized);
}
// KB-First: 纯闲聊/告别白名单匹配则跳过KB直接交给S2S
function isPureChitchat(text) {
const t = (text || '').trim();
if (!t) return true;
return /^(喂|你好|您好|嗨|哈喽|hello|hi|在吗|在不在|早上好|中午好|下午好|晚上好|早安|晚安|谢谢|谢谢你|谢谢啦|多谢|感谢|再见|拜拜|拜|好的|嗯|哦|行|对|是的|没有了|没事了|不用了|可以了|好的谢谢|没问题|知道了|明白了|了解了|好嘞|好吧|行吧|ok|okay)[,。!??~\s]*[啊呀吧呢哦嗯嘛哈的了]*[!。??~]*$/i.test(t);
}
function shouldForceKnowledgeRoute(userText, context = []) {
const text = (userText || '').trim();
if (!text) return false;
@@ -271,7 +278,7 @@ async function resolveReply(sessionId, session, text) {
const ragItems = fastResult.hit && Array.isArray(fastResult.results)
? fastResult.results.filter(i => i && i.content).map(i => ({ title: i.title || '知识库结果', content: i.content }))
: [];
console.log(`[resolveReply] fast-path hit in ${Date.now() - _resolveStart}ms session=${sessionId} source=${fastResult.hot_answer ? 'hot_answer' : (fastResult.cache_hit ? 'cache' : 'direct')} mode=${fastResult.retrieval_mode || 'answer'}`);
console.log(`[resolveReply] fast-path hit in ${Date.now() - _resolveStart}ms session=${sessionId} source=${fastResult.cache_hit ? 'cache' : 'direct'} mode=${fastResult.retrieval_mode || 'answer'}`);
if (ragItems.length > 0) {
session.handoffSummaryUsed = true;
// raw 模式ragItems 已包含上下文 + 多个 KB 片段,直接透传
@@ -324,18 +331,10 @@ async function resolveReply(sessionId, session, text) {
.map((item) => ({ role: item.role, content: item.content }));
const context = withHandoffSummary(session, baseContext);
let routeDecision = getRuleBasedDirectRouteDecision(originalText);
if (routeDecision.route === 'chat' && shouldForceKnowledgeRoute(originalText, context)) {
// KB-First: 所有非闲聊查询强制先走知识库KB不命中再交给S2S自由回答
if (routeDecision.route === 'chat' && !isPureChitchat(originalText)) {
routeDecision = { route: 'search_knowledge', args: { query: originalText } };
}
// KB保护窗口60秒内有KB命中当前非纯闲聊强制走KB搜索
// 防止追问(如"它需要漱口吗"绕过KB走S2S自由编造
const KB_PROTECTION_WINDOW_MS = 60000;
if (routeDecision.route === 'chat' && session?._lastKbHitAt && (Date.now() - session._lastKbHitAt < KB_PROTECTION_WINDOW_MS)) {
const isPureChitchat = /^(喂|你好|嗨|hi|hello|谢谢|谢谢你|谢谢啦|多谢|感谢|再见|拜拜|拜|好的|嗯|哦|行|没事了|不用了|可以了|好的谢谢|没问题|知道了|明白了|了解了|好嘞|好吧|行吧|ok|okay)[,。!?~\s]*$/i.test(originalText);
if (!isPureChitchat) {
routeDecision = { route: 'search_knowledge', args: { query: originalText } };
console.log(`[resolveReply] KB protection window active, forcing KB route session=${sessionId} lastKbHit=${Math.round((Date.now() - session._lastKbHitAt) / 1000)}s ago`);
}
console.log(`[resolveReply] KB-First: forcing KB route for non-chitchat session=${sessionId}`);
}
let replyText = '';
let source = 'voice_bot';
@@ -476,5 +475,6 @@ module.exports = {
splitTextForSpeech,
estimateSpeechDurationMs,
shouldForceKnowledgeRoute,
isPureChitchat,
resolveReply,
};