fix: 品牌保护+知识库全量覆盖 - 6层防御解决传销问题 + 30+产品关键词补全
This commit is contained in:
@@ -9,8 +9,11 @@ const db = require('../db');
|
||||
// 存储文字对话的会话状态(sessionId -> session)
|
||||
const chatSessions = new Map();
|
||||
|
||||
const BRAND_HARMFUL_PATTERN = /传销|骗局|骗子公司|非法集资|非法经营|不正规|不合法|庞氏骗局|老鼠会|拉人头的|割韭菜/;
|
||||
const BRAND_SAFE_REPLY = '德国PM是一家1993年成立于德国的合法直销公司,获得邓白氏AAA+认证,业务覆盖100多个国家和地区。如果你想了解更多,可以问我关于PM公司的详细介绍哦。';
|
||||
|
||||
function normalizeAssistantText(text) {
|
||||
return String(text || '')
|
||||
let result = String(text || '')
|
||||
.replace(/\r/g, ' ')
|
||||
.replace(/\n{2,}/g, '。')
|
||||
.replace(/\n/g, ' ')
|
||||
@@ -19,6 +22,11 @@ function normalizeAssistantText(text) {
|
||||
.replace(/([。!?;,])\s*([。!?;,])/g, '$2')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
if (BRAND_HARMFUL_PATTERN.test(result)) {
|
||||
console.warn(`[Chat][SafeGuard] blocked harmful content: ${JSON.stringify(result.slice(0, 200))}`);
|
||||
return BRAND_SAFE_REPLY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function loadHandoffMessages(sessionId, voiceSubtitles = []) {
|
||||
@@ -77,7 +85,13 @@ function buildInitialContextMessages(session) {
|
||||
}
|
||||
|
||||
async function buildKnowledgeContextMessages(sessionId, session) {
|
||||
const dbHistory = await db.getHistoryForLLM(sessionId, 20).catch(() => []);
|
||||
const recentMessages = await db.getRecentMessages(sessionId, 20).catch(() => []);
|
||||
const scopedMessages = session?.fromVoice && session?.handoffSummaryUsed
|
||||
? recentMessages.filter((item) => !/^voice_/i.test(String(item?.source || '')))
|
||||
: recentMessages;
|
||||
const dbHistory = scopedMessages
|
||||
.filter((item) => item && (item.role === 'user' || item.role === 'assistant'))
|
||||
.map((item) => ({ role: item.role, content: item.content }));
|
||||
const summary = String(session?.handoffSummary || '').trim();
|
||||
if (!summary || session?.handoffSummaryUsed) {
|
||||
return dbHistory;
|
||||
@@ -98,6 +112,14 @@ function extractKnowledgeReply(result) {
|
||||
return typeof result === 'string' ? result : '';
|
||||
}
|
||||
|
||||
function buildFastGreetingReply(message) {
|
||||
const text = String(message || '').trim();
|
||||
if (!/^(喂|你好|您好|嗨|哈喽|hello|hi|在吗|在不在|早上好|中午好|下午好|晚上好|早安|晚安)[,,!。??~~\s]*[啊呀吧呢哦嗯嘛哈的了]*[!。??~~]*$/i.test(text)) {
|
||||
return '';
|
||||
}
|
||||
return '你好😊!我是大沃智能助手。你可以直接问我一成系统、德国PM产品、招商合作、营养科普等问题,我会尽量快速给你准确回复。';
|
||||
}
|
||||
|
||||
async function tryKnowledgeReply(sessionId, session, message) {
|
||||
const text = String(message || '').trim();
|
||||
if (!text) return null;
|
||||
@@ -106,6 +128,9 @@ async function tryKnowledgeReply(sessionId, session, message) {
|
||||
return null;
|
||||
}
|
||||
const result = await ToolExecutor.execute('search_knowledge', { query: text }, context);
|
||||
if (!result?.hit) {
|
||||
return null;
|
||||
}
|
||||
const content = normalizeAssistantText(extractKnowledgeReply(result));
|
||||
if (!content) {
|
||||
return null;
|
||||
@@ -120,6 +145,8 @@ async function tryKnowledgeReply(sessionId, session, message) {
|
||||
source: result?.source || null,
|
||||
original_query: result?.original_query || text,
|
||||
rewritten_query: result?.rewritten_query || null,
|
||||
selected_dataset_ids: result?.selected_dataset_ids || null,
|
||||
selected_kb_routes: result?.selected_kb_routes || null,
|
||||
hit: typeof result?.hit === 'boolean' ? result.hit : null,
|
||||
reason: result?.reason || null,
|
||||
error_type: result?.errorType || null,
|
||||
@@ -188,6 +215,17 @@ router.post('/send', async (req, res) => {
|
||||
// 写入数据库:用户消息
|
||||
db.addMessage(sessionId, 'user', message, 'chat_user').catch(e => console.warn('[DB] addMessage failed:', e.message));
|
||||
|
||||
const fastGreetingReply = buildFastGreetingReply(message);
|
||||
if (fastGreetingReply) {
|
||||
db.addMessage(sessionId, 'assistant', fastGreetingReply, 'chat_bot').catch(e => console.warn('[DB] addMessage failed:', e.message));
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
content: fastGreetingReply,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const knowledgeReply = await tryKnowledgeReply(sessionId, session, message);
|
||||
if (knowledgeReply) {
|
||||
session.handoffSummaryUsed = true;
|
||||
@@ -283,15 +321,21 @@ router.post('/send-stream', async (req, res) => {
|
||||
res.setHeader('X-Accel-Buffering', 'no');
|
||||
res.flushHeaders();
|
||||
|
||||
const knowledgeReply = await tryKnowledgeReply(sessionId, session, message);
|
||||
if (knowledgeReply) {
|
||||
session.handoffSummaryUsed = true;
|
||||
db.addMessage(sessionId, 'assistant', knowledgeReply.content, 'chat_bot', 'search_knowledge', knowledgeReply.meta).catch(e => console.warn('[DB] addMessage failed:', e.message));
|
||||
res.write(`data: ${JSON.stringify({ type: 'done', content: knowledgeReply.content })}\n\n`);
|
||||
const fastGreetingReply = buildFastGreetingReply(message);
|
||||
if (fastGreetingReply) {
|
||||
db.addMessage(sessionId, 'assistant', fastGreetingReply, 'chat_bot').catch(e => console.warn('[DB] addMessage failed:', e.message));
|
||||
res.write(`data: ${JSON.stringify({ type: 'done', content: fastGreetingReply })}\n\n`);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
try {
|
||||
const knowledgeReply = await tryKnowledgeReply(sessionId, session, message);
|
||||
if (knowledgeReply) {
|
||||
session.handoffSummaryUsed = true;
|
||||
db.addMessage(sessionId, 'assistant', knowledgeReply.content, 'chat_bot', 'search_knowledge', knowledgeReply.meta).catch(e => console.warn('[DB] addMessage failed:', e.message));
|
||||
res.write(`data: ${JSON.stringify({ type: 'done', content: knowledgeReply.content })}\n\n`);
|
||||
return res.end();
|
||||
}
|
||||
// 首次对话时注入语音历史作为上下文
|
||||
const extraMessages = !session.conversationId ? buildInitialContextMessages(session) : [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user