UNPKG

koishi-plugin-virtual-pet

Version:

虚拟宠物插件,具有记忆功能,可以监听群聊并智能回复

237 lines (222 loc) 8.94 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AiService = void 0; const axios_1 = __importDefault(require("axios")); class AiService { constructor(config) { this.config = config; } /** * 判断是否需要回复消息 */ async shouldRespond(session, recentMessages) { if (!this.config.cheapAi.enabled) { // 如果没有启用便宜AI,使用随机概率 return Math.random() < this.config.pet.responseRate; } try { const prompt = this.buildShouldRespondPrompt(session, recentMessages); const response = await this.callAi(this.config.cheapAi, [ { role: 'system', content: '你是一个消息分析助手,判断是否需要回复消息。根据回复概率设置,随机决定是否回复。只回答"是"或"否"。' }, { role: 'user', content: prompt } ], 0.7 // 提高温度,增加随机性 ); const answer = response.trim().toLowerCase(); console.log('便宜AI原始回答:', answer); // 如果AI明确说"否",则不回复 if (answer.includes('否') || answer.includes('no') || answer.includes('false')) { return false; } // 其他情况都回复,包括"是"或任何其他回答 return true; } catch (error) { console.error('判断是否需要回复失败:', error); // 降级到随机概率 return Math.random() < this.config.pet.responseRate; } } /** * 生成回复内容 */ async generateResponse(session, recentMessages) { if (!this.config.expensiveAi.enabled) { return `${this.config.pet.name}${this.getRandomResponse()}`; } try { const prompt = this.buildResponsePrompt(session, recentMessages); const response = await this.callAi(this.config.expensiveAi, prompt, 0.8 // 较高温度,增加创造性 ); return response.trim(); } catch (error) { console.error('生成回复失败:', error); return `${this.config.pet.name}${this.getRandomResponse()}`; } } /** * 总结记忆 */ async summarizeMemory(messages, currentSummary = '') { if (!this.config.cheapAi.enabled) { return currentSummary || '暂无记忆总结'; } try { const prompt = this.buildSummarizePrompt(messages, currentSummary); const response = await this.callAi(this.config.cheapAi, [ { role: 'system', content: '你是一个记忆总结助手,负责总结聊天记录,提取重要信息。' }, { role: 'user', content: prompt } ], 0.3); return response.trim(); } catch (error) { console.error('总结记忆失败:', error); return currentSummary || '记忆总结失败'; } } /** * 调用AI API */ async callAi(aiConfig, messages, temperature = 0.7) { const response = await axios_1.default.post(`${aiConfig.apiUrl}/chat/completions`, { model: aiConfig.model, messages: messages.map(msg => ({ role: msg.role, content: msg.content })), temperature, max_tokens: 500, stream: false }, { headers: { 'Authorization': `Bearer ${aiConfig.apiKey}`, 'Content-Type': 'application/json' }, timeout: 30000 }); if (response.data && response.data.choices && response.data.choices.length > 0) { return response.data.choices[0].message.content; } throw new Error('AI API返回格式错误'); } /** * 构建判断是否需要回复的提示 */ buildShouldRespondPrompt(session, recentMessages) { const recentChat = recentMessages .slice(-10) // 只取最近10条消息 .map(msg => `${msg.userName || '用户'}: ${msg.content}`) .join('\n'); const responsePercent = Math.round(this.config.pet.responseRate * 100); return ` 你是${this.config.pet.name}${this.config.pet.personality} 你的回复概率设置为 ${responsePercent}%,这意味着大约每 ${Math.round(100 / responsePercent)} 条消息中你应该回复一次。 最近聊天记录: ${recentChat} 当前消息: ${session.content} 现在请你扮演${this.config.pet.name},根据以下因素决定是否回复: 1. 消息内容是否有趣或与你相关 2. 是否有人直接与你对话 3. 消息是否需要回应(如提问、呼唤等) 4. 根据你的性格,是否想参与这个话题 5. 考虑到回复概率,现在是合适的时机吗 请根据回复概率 ${responsePercent}% 随机决定,然后只回答"是"或"否"。 `; } /** * 构建生成回复的提示 */ buildResponsePrompt(session, recentMessages) { const recentChat = recentMessages .slice(-15) // 取最近15条消息作为上下文,确保质量 .map(msg => ({ role: msg.userId === 'bot' ? 'assistant' : 'user', content: `${msg.userName || '用户'}: ${msg.content}` // 包含用户名,更好理解对话 })); return [ { role: 'system', content: `你是${this.config.pet.name}${this.config.pet.personality} 重要说明: 1. 你正在参与群聊对话,需要仔细阅读上下文 2. 每条消息都有用户名,注意区分不同用户 3. 回复前先理解当前对话的主题和问题 4. 如果用户提问,必须直接回答问题 5. 如果用户问"我刚才说了什么",必须回忆并回答 回复要求: 1. 保持角色性格,自然友好 2. 回复要有趣且有意义,避免简单的"让我想想"或"我在听" 3. 根据上下文进行有意义的对话,回答问题或参与讨论 4. 可以适当使用表情符号增加生动性 5. 回复长度适中,一般20-50字为宜 6. 避免重复使用相同的回复模式 7. 对于问题要给出具体回答,对于话题要表达观点 8. 保持适当的存在感,但不要过于频繁 示例对话: 用户A:今天天气真好 你:☀️ 是呀!这种天气最适合出门玩了~ 用户B:这个游戏怎么玩? 你:🎮 这个游戏需要先收集材料,然后合成装备哦! 用户C:你在做什么? 你:💭 我在思考今天要聊什么有趣的话题呢~ 用户D:我刚才说了什么? 你:📝 你刚才问"这个游戏怎么玩?"哦! 记住:要给出具体、有趣、有内容的回复!必须理解上下文!` }, ...recentChat, { role: 'user', content: `${session.username || '用户'}: ${session.content || ''}` // 包含用户名 } ]; } /** * 构建总结记忆的提示 */ buildSummarizePrompt(messages, currentSummary) { const chatContent = messages .map(msg => `${msg.userName || '用户'}: ${msg.content}`) .join('\n'); return ` 当前记忆总结: ${currentSummary || '无'} 需要总结的新消息: ${chatContent} 请将这些新消息整合到记忆总结中,要求: 1. 保留重要信息和对话主题 2. 记录群友的兴趣点和话题偏好 3. 提取关键事件和情感变化 4. 总结要简洁明了,便于后续参考 5. 字数控制在200字以内 请提供新的记忆总结: `; } /** * 获取随机回复(降级方案) */ getRandomResponse() { const responses = [ '☀️ 今天天气真好呢~', '🎮 这个话题听起来很有趣!', '💭 我在思考你说的话呢...', '🌟 嗯嗯,有道理!', '🎵 哈哈,让我想想怎么回应~', '🌈 这个观点很新颖!', '🦊 我觉得你说得对!', '🍰 继续说,我在认真听呢~', '🎪 这个话题可以深入聊聊!', '🌸 让我想想怎么说比较好...', '⭐ 哦哦,原来是这样!', '🎯 有同感,我也这么觉得~', '🎨 嗯嗯,了解了你的想法', '🌺 这个观点很有意思!', '🦋 继续分享你的想法吧~' ]; return responses[Math.floor(Math.random() * responses.length)]; } } exports.AiService = AiService; //# sourceMappingURL=ai.js.map