koishi-plugin-virtual-pet
Version:
虚拟宠物插件,具有记忆功能,可以监听群聊并智能回复
237 lines (222 loc) • 8.94 kB
JavaScript
;
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