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文档、系统提示词目录
This commit is contained in:
User
2026-03-24 17:19:36 +08:00
parent 57a03677a9
commit 9567eb7358
34 changed files with 7076 additions and 46 deletions

View File

@@ -2,6 +2,8 @@ const express = require('express');
const router = express.Router();
const { v4: uuidv4 } = require('uuid');
const ToolExecutor = require('../services/toolExecutor');
const contextKeywordTracker = require('../services/contextKeywordTracker');
const { getRuleBasedDirectRouteDecision, shouldForceKnowledgeRoute } = require('../services/realtimeDialogRouting');
const DEFAULT_TOOLS = require('../config/tools');
const db = require('../db');
@@ -66,6 +68,9 @@ router.post('/direct/message', async (req, res) => {
if (!sessionId || !text || !source) {
return res.status(400).json({ success: false, error: 'sessionId, text and source are required' });
}
if (role === 'user') {
contextKeywordTracker.updateSession(sessionId, text);
}
await db.addMessage(sessionId, role === 'user' ? 'user' : 'assistant', text, source, toolName || null);
res.json({ success: true });
} catch (error) {
@@ -94,9 +99,19 @@ router.post('/direct/query', async (req, res) => {
const context = await db.getHistoryForLLM(sessionId, 20).catch(() => []);
const cleanQuery = (query || '').trim();
if (appendUserMessage && cleanQuery) {
contextKeywordTracker.updateSession(sessionId, cleanQuery);
await db.addMessage(sessionId, 'user', cleanQuery, 'voice_asr').catch(() => null);
}
const result = await ToolExecutor.execute('search_knowledge', { query: cleanQuery }, context);
if (!appendUserMessage && cleanQuery) {
contextKeywordTracker.updateSession(sessionId, cleanQuery);
}
const routeDecision = getRuleBasedDirectRouteDecision(cleanQuery);
const forceKb = shouldForceKnowledgeRoute(cleanQuery, context);
const shouldSearchKb = routeDecision.route === 'search_knowledge' || forceKb;
const directSession = directSessions.get(sessionId);
const result = shouldSearchKb
? await ToolExecutor.execute('search_knowledge', { query: cleanQuery, session_id: sessionId, _session: { userId: directSession?.userId || null } }, context)
: { hit: false, reason: 'route_skip', source: 'route_skip', error: '该问题不在知识库范围内,请咨询其他问题。' };
let contentText = JSON.stringify(result);
if (result && result.results && Array.isArray(result.results)) {
contentText = result.results.map((item) => item.content || JSON.stringify(item)).join('\n');