// 🔄 会话状态管理 - 跟踪是否在连续对话模式 let inConversationMode = false; let lastActivityTime = Date.now(); const CONVERSATION_TIMEOUT = 5 * 60 * 1000; // 5分钟无活动自动退出 export default { speaker: { userId: "104544581", password: "xin12345", passToken: "V1:J7rrshrufaw8uWrlTMO7xxdYqvWIT0JZUqwbl9YuVuCnljlYhobJm6vO/rem/hRUvTASl/w3akwM4pDSC2GNH1gIVn6P7SCGjR4HIbpKzPzP3XtYeSW+5WYU9GIBlll9cqOzhi+5fi0VI3mU3K3F/tpZXa4JoTLxbRpRt9YB7DgFLjsKYD9elKSR7jr6zSnTIzgXsAntm1b7Yq//DHm1OB4fpy1Ldn4DXhR7PvwgA2MMRCrUMbE645GAKTb6fbf6mxz7MuTuDv4u56Juz+37yeoZBf3PNvyA+EbflsgnFeaE9cdVWArLIKFuBU983IZw4WuBpjxvo1X9qXvcfYSxBg==", did: "多多2", }, openai: { // 使用带搜索能力的模型 model: "gemini-2.5-flash-search", baseURL: "https://tdhxxzcu.ap-southeast-1.clawcloudrun.com/v1", apiKey: "Csrchina@163.com", }, context: { historyMaxLength: 10, }, prompt: { system: "你是一个智能助手,请以下方式回答:不需客套,直接回答问题;句子简短,但一定要完整清晰回答用户的所有问题;不用英文缩写、表情、Markdown、代码;数字用中文读法,时间与金额说全称。" }, // 首次触发关键词 callAIKeywords: ["请", "你"], // 退出连续对话的关键词 exitKeywords: ["退下", "再见", "拜拜", "结束对话"], async onMessage(engine, msg) { const text = msg.text; const now = Date.now(); // 检查是否超时,自动退出连续对话模式 if (inConversationMode && (now - lastActivityTime > CONVERSATION_TIMEOUT)) { inConversationMode = false; console.log("⏰ 连续对话超时,已自动退出"); } // 检查是否要退出连续对话 if (this.exitKeywords.some((e) => text.includes(e))) { if (inConversationMode) { inConversationMode = false; // 打断小爱并播放退出提示 await engine.speaker.abortXiaoAI(); await engine.MiOT.doAction(7, 3, "好的,有事再叫我"); console.log("👋 退出连续对话模式"); return { handled: true }; } } // 判断是否应该由 MiGPT 处理 const isFirstTrigger = this.callAIKeywords.some((e) => text.startsWith(e)); const shouldHandle = isFirstTrigger || inConversationMode; if (shouldHandle) { // 🔥 立即打断小爱(支持随时打断) await engine.speaker.abortXiaoAI(); // 进入/保持连续对话模式 if (!inConversationMode) { inConversationMode = true; console.log("🎯 进入连续对话模式"); } lastActivityTime = now; // 调用 AI 回答 const { text: reply } = await engine.askAI(msg); console.log(`🔊 ${reply}`); // 🎯 分段播放长文本,防止 TTS 超时中断 const maxLength = 200; // 单次播放最大字数 if (reply.length > maxLength) { // 按句号、问号、感叹号分段 const sentences = reply.match(/[^。!?]+[。!?]/g) || [reply]; let currentChunk = ""; for (const sentence of sentences) { if ((currentChunk + sentence).length > maxLength) { // 播放当前段落 if (currentChunk) { await engine.MiOT.doAction(7, 3, currentChunk); // 根据文字数量计算等待时间(每字约350ms)+ 1秒缓冲 const waitTime = currentChunk.length * 350 + 1000; await new Promise(resolve => setTimeout(resolve, waitTime)); } currentChunk = sentence; } else { currentChunk += sentence; } } // 播放最后一段 if (currentChunk) { await engine.MiOT.doAction(7, 3, currentChunk); } } else { // 短文本直接播放 await engine.MiOT.doAction(7, 3, reply); } return { handled: true }; } // 不在连续对话模式,且不是触发词,让原始小爱处理 }, };