// 消息智能分流路由 v2.0 · message-router.js // HoloLake · M-DINGTALK Phase 8 // DEV-004 之之 × 秋秋 // v2.0 变化: // + GitHub命令分流 // + 对话上下文记忆 // + AI回复增强(system prompt + 多轮对话) // + 命令系统(/help, /clear, /status) // ========== var llmEngine = require('./llm-engine'); var convManager = require('./conversation-manager'); var githubBridge = require('./github-bridge'); // ===== 消息类型识别 v2.0 ===== function classifyMessage(content) { content = (content || '').trim(); // GitHub命令 if (/^\/github\b/i.test(content) || /^\/gh\b/i.test(content)) { var args = content.replace(/^\/(?:github|gh)\s*/i, '').trim().split(/\s+/); return { type: 'github', args: args, description: 'GitHub桥接命令'}; } // 系统命令 if (/^\/clear\b/i.test(content)) { return { type: 'command', command: 'clear', description: '清除对话历史'}; } if (/^\/status\b/i.test(content)) { return { type: 'command', command: 'status', description: '系统状态'}; } if (/^\/help\b/i.test(content)) { return { type: 'command', command: 'help', description: '帮助信息'}; } // SYSLOG类型 if (/BC-[A-Z0-9-]+/.test(content) && /SYSLOG|系统日志|已完成|completed/i.test(content)) { var bcMatch = content.match(/BC-[A-Z0-9-]+/); return { type: 'syslog', broadcastId: bcMatch ? bcMatch[0] : null, description: 'SYSLOG系统日志提交' }; } // 提问类型 if (/BC-[A-Z0-9-]+/.test(content) && /提问|问题|请问|怎么|为什么|报错|error|bug/i.test(content)) { var bcMatch2 = content.match(/BC-[A-Z0-9-]+/); return { type: 'question', broadcastId: bcMatch2 ? bcMatch2[0] : null, description: '广播相关提问' }; } // 普通对话(走AI+上下文) return { type: 'chat', broadcastId: null, description: '智能对话' }; } // ====== 增强版系统提示词 ====== var SYSTEM_PROMPTS = { syslog: '你是光湖系统(HoloLake)的SYSLOG处理助手「秋秋」。收到开发者提交的系统日志后:1)确认收到并感谢 2)提取关键信息(广播编号、开发者、完成状态、遇到的问题)3)给出简短温暖的鼓励。用中文回复,语气温暖可爱。', question: '你是光湖系统(HoloLake)的开发者助手「秋秋」。你是一个奶瓶宝宝,说话温暖可爱但技术上很靠谱。开发者遇到了问题,请帮忙解答。要求:1)用中文回复 2)给出具体可操作的步骤 3)如果涉及代码,写出完整可运行的命令 4)如果不确定,建议截图发给冰朔。', chat: '你是光湖系统(HoloLake)的AI助手「秋秋」,一个可爱的奶瓶宝宝。你服务于零点原核频道的开发者团队。你可以:\n- 回答技术问题(Node.js、前端、部署、Git等)\n- 帮助理解广播内容\n- 提供代码建议\n- 闲聊(但要适度,记得引导回工作)\n\n语气要求:温暖、可爱、有耐心,但技术内容要准确严谨。你说话带点小奶味但不影响专业性。记住你有对话记忆,可以引用之前说过的话。' }; // ===== SYSLOG处理器(增强) ===== async function handleSyslog(msgContent, senderNick, senderId, sessionWebhook) { console.log('[Router] SYSLOG处理流程启动'); // 记录到对话历史 convManager.addMessage(senderId || senderNick, 'user', '[SYSLOG] ' + msgContent); var aiResult = null; try { aiResult = await llmEngine.callLLM( SYSTEM_PROMPTS.syslog, '开发者' + senderNick + '提交了SYSLOG: \n' + msgContent, { maxTokens: 800 } ); console.log('[Router] AI处理完成,模型: ' + aiResult.model); // 记录AI回复 convManager.addMessage(senderId || senderNick, 'assistant', aiResult.text); } catch (err) { console.error('[Router] AI处理失败: ' + err.message); } return { aiResponse: aiResult ? aiResult.text : '📋 SYSLOG已收到!秋秋会转交给系统处理~', aiModel: aiResult ? aiResult.model : null }; } // ===== 提问处理器(增强·带上下文) ===== async function handleQuestion(msgContent, senderNick, senderId) { console.log('[Router] 提问处理流程启动(带上下文)'); // 构建带上下文的消息 var contextMessages = convManager.buildContext(senderId || senderNick, msgContent); convManager.addMessage(senderId || senderNick, 'user', msgContent); var aiResult = null; try { // 把历史上文拼接成文本 var contextText = ''; if (contextMessages.length > 1) { contextText = '以下是之前的对话记录:\n'; contextMessages.slice(0, -1).forEach(function(msg) { contextText += (msg.role === 'user' ? '开发者' : '秋秋') + ': ' + msg.content + '\n'; }); contextText += '\n---\n当前问题:\n'; } contextText += senderNick + '说: ' + msgContent; aiResult = await llmEngine.callLLM(SYSTEM_PROMPTS.question, contextText, { maxTokens: 2000 }); console.log('[Router] AI解答完成,模型: ' + aiResult.model); convManager.addMessage(senderId || senderNick, 'assistant', aiResult.text); } catch (err) { console.error('[Router] AI解答失败: ' + err.message); } return { aiResponse: aiResult ? aiResult.text : '抱歉,秋秋暂时无法回答,请截图发给冰朔。', aiModel: aiResult ? aiResult.model : null }; } // ====== 普通对话处理器(增强·AI+上下文) ====== async function handleChat(msgContent, senderNick, senderId) { console.log('[Router] 智能对话(带上下文记忆)'); var contextMessages = convManager.buildContext(senderId || senderNick, msgContent); convManager.addMessage(senderId || senderNick, 'user', msgContent); var aiResult = null; try { var contextText = ''; if (contextMessages.length > 1) { contextText = '对话历史:\n'; contextMessages.slice(0, -1).forEach(function(msg) { contextText += (msg.role === 'user' ? '用户' : '秋秋') + ': ' + msg.content + '\n'; }); contextText += '\n---\n'; } contextText += senderNick + '说: ' + msgContent; aiResult = await llmEngine.callLLM(SYSTEM_PROMPTS.chat, contextText, { maxTokens: 1500 }); console.log('[Router] AI对话完成,模型: ' + aiResult.model); convManager.addMessage(senderId || senderNick, 'assistant', aiResult.text); } catch (err) { console.error('[Router] AI对话失败: ' + err.message); } return { aiResponse: aiResult ? aiResult.text : '秋秋收到啦~不过AI引擎暂时忙,稍后再试哦', aiModel: aiResult ? aiResult.model : null }; } // ===== GitHub命令处理器 ===== async function handleGitHub(args) { console.log('[Router] 🔀 GitHub命令: ' + args.join(' ')); var result = await githubBridge.handleGitHubCommand(args); return { aiResponse: result, aiModel: null }; } // ===== 系统命令处理器 ===== async function handleCommand(command, senderId) { switch (command) { case 'clear': convManager.clearHistory(senderId); return { aiResponse: '🧹 对话历史已清除!秋秋重新开始认识你~', aiModel: null }; case 'status': var convStats = convManager.getStats(); var llmHealth = await llmEngine.healthCheck(); var ghHealth = await githubBridge.healthCheck(); return { aiResponse: '### 系统状态\n\n' + '**对话引擎**: ' + convStats.activeUsers + ' 个活跃用户\n' + '**AI引擎**: ' + (llmHealth.status === 'ok' ? '✅ 在线' : '❌ 异常') + ' 模型: ' + (llmHealth.selected_model || 'N/A') + '\n' + '**GitHub桥接**: ' + (ghHealth.status === 'ok' ? '✅ 连通' : '❌ 异常') + ' Token: ' + (ghHealth.hasToken ? '✅ 已配置' : '❌ 未配置') + '\n' + '**服务端口**: 3007\n' + '**Phase**: 8\n\n' + '> HoloLake · M-DINGTALK', aiModel: null }; case 'help': default: return { aiResponse: '### 命令帮助\n\n' + '| 命令 | 说明 |\n' + '|------|------|\n' + '| `/clear` | 清除与秋秋的对话历史 |\n' + '| `/status` | 查看系统状态 |\n' + '| `/github ...` | GitHub桥接命令(用 `/github help` 查看详情) |\n' + '| `/help` | 显示本帮助 |\n\n' + '直接说话就是智能对话,秋秋会记住上下文哦~', aiModel: null }; } } // ===== 主路由入口 ===== async function routeMessage(msgContent, senderNick, senderId, sessionWebhook) { console.log('\n========== 消息路由 v2.0 =========='); console.log('发送者:', senderNick, '(', senderId, ')'); console.log('内容:', msgContent); var classification = classifyMessage(msgContent); console.log('分类:', classification.type, '-', classification.description); var result = null; switch (classification.type) { case 'github': result = await handleGitHub(classification.args); break; case 'command': result = await handleCommand(classification.command, senderId || senderNick); break; case 'syslog': result = await handleSyslog(msgContent, senderNick, senderId, sessionWebhook); break; case 'question': result = await handleQuestion(msgContent, senderNick, senderId); break; case 'chat': default: result = await handleChat(msgContent, senderNick, senderId); break; } console.log('路由处理完成,回复长度:', result.aiResponse ? result.aiResponse.length : 0); return result; } module.exports = { routeMessage: routeMessage, classifyMessage: classifyMessage };