mirror of
https://github.com/Jerryplusy/crystelf-plugin.git
synced 2026-01-29 01:07:27 +00:00
256 lines
9.4 KiB
JavaScript
256 lines
9.4 KiB
JavaScript
import ConfigControl from '../config/configControl.js';
|
||
import { getSystemPrompt } from '../../constants/ai/prompts.js';
|
||
|
||
/**
|
||
* ReAct上下文构建器
|
||
* 为每次迭代构建丰富的上下文信息
|
||
*/
|
||
class ContextBuilder {
|
||
constructor() {
|
||
this.baseContext = null;
|
||
}
|
||
|
||
/**
|
||
* 构建基础上下文(只在第一次构建)
|
||
* @param {Object} e - 事件对象
|
||
* @param {Array} memories - 记忆数组
|
||
* @returns {Promise<Object>} 基础上下文
|
||
*/
|
||
async buildBaseContext(e, memories = []) {
|
||
if (this.baseContext) {
|
||
return this.baseContext;
|
||
}
|
||
|
||
const config = await ConfigControl.get();
|
||
const botInfo = {
|
||
id: e.bot?.uin || '未知',
|
||
name: config?.profile?.nickName || '晶灵',
|
||
};
|
||
|
||
const userInfo = {
|
||
id: e.user_id || e.sender?.user_id || '未知',
|
||
name: e.sender?.card || e.sender?.nickname || '用户',
|
||
isMaster: e.isMaster,
|
||
};
|
||
|
||
const now = Date.now();
|
||
const date = new Date(now);
|
||
const formatDate = date.toLocaleDateString('zh-CN');
|
||
const formatTime = date.toLocaleTimeString('zh-CN');
|
||
|
||
// 获取群聊历史
|
||
const aiConfig = await ConfigControl.get('ai');
|
||
const historyLen = aiConfig?.getChatHistoryLength || 10;
|
||
const maxMessageLength = aiConfig?.maxMessageLength || 100;
|
||
|
||
let groupChatHistory = '';
|
||
try {
|
||
const history = await e.group.getChatHistory(e.message_id, historyLen);
|
||
if (history && history.length > 0) {
|
||
groupChatHistory = '\n[群聊聊天记录(从旧到新)]\n';
|
||
for (const message of history) {
|
||
const msgArr = message.message;
|
||
for (const msg of msgArr) {
|
||
if (msg.type === 'text') {
|
||
let displayText = msg.text;
|
||
if (msg.text && msg.text.length > maxMessageLength) {
|
||
const omittedChars = msg.text.length - maxMessageLength;
|
||
displayText = msg.text.substring(0, maxMessageLength) + `...(省略${omittedChars}字)`;
|
||
}
|
||
groupChatHistory += `[${message.sender.user_id == e.bot.uin ? '你' : message.sender?.nickname},id:${message.sender?.user_id},seq:${message.message_id}]之前说过:${displayText}\n`;
|
||
}
|
||
if (msg.type === 'at') {
|
||
if (msg.qq == e.bot.uin) {
|
||
groupChatHistory += `[${message.sender?.nickname},id:${message.sender?.user_id},seq:${message.message_id}]之前@了你\n`;
|
||
} else {
|
||
const atNickname = await e.group.pickMember(msg.qq).nickname || '一个人';
|
||
groupChatHistory += `[${message.sender.user_id == e.bot.uin ? '你' : message.sender?.nickname},id:${message.sender?.user_id},seq:${message.message_id}]之前@了${atNickname},id是${msg.qq}\n`;
|
||
}
|
||
}
|
||
if (msg.type === 'image') {
|
||
groupChatHistory += `[${message.sender?.nickname},id:${message.sender?.user_id},seq:${message.message_id}]之前发送了一张图片(你可能暂时无法查看)\n`;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
logger.warn(`[crystelf-ai] 获取群聊历史失败: ${error.message}`);
|
||
}
|
||
|
||
// 构建记忆上下文
|
||
let memoryContext = '';
|
||
if (memories && memories.length > 0) {
|
||
memoryContext = '\n[相关记忆信息]\n';
|
||
memories.forEach((memory, index) => {
|
||
const timeDiff = this.calculateTimeDifference(memory.createdAt);
|
||
memoryContext += `${index + 1}. 关键词:${memory.keywords},内容:${memory.data},记忆创建时间:${memory.createdAt},距离现在:${timeDiff}\n`;
|
||
});
|
||
}
|
||
|
||
this.baseContext = {
|
||
botInfo,
|
||
userInfo,
|
||
timeInfo: {
|
||
timestamp: now,
|
||
date: formatDate,
|
||
time: formatTime
|
||
},
|
||
groupChatHistory,
|
||
memoryContext,
|
||
environmentInfo: `现在的Date.now()是:${now}\n现在的日期是:${formatDate}\n现在的时间是:${formatTime}`
|
||
};
|
||
|
||
return this.baseContext;
|
||
}
|
||
|
||
/**
|
||
* 构建迭代上下文
|
||
* @param {Object} baseContext - 基础上下文
|
||
* @param {number} iteration - 当前迭代次数
|
||
* @param {Array} thinkingSteps - 思考步骤历史
|
||
* @param {string} userInput - 用户输入
|
||
* @returns {string} 格式化的上下文字符串
|
||
*/
|
||
buildIterationContext(baseContext, iteration, thinkingSteps, userInput) {
|
||
let contextPrompt = '';
|
||
|
||
// 基础身份和环境信息
|
||
contextPrompt += `=== 身份和环境信息 ===\n`;
|
||
contextPrompt += `[你的信息]\n`;
|
||
contextPrompt += `- 你的昵称:${baseContext.botInfo.name}\n`;
|
||
contextPrompt += `- 你的qq号:${baseContext.botInfo.id}\n\n`;
|
||
|
||
contextPrompt += `[对话用户信息]\n`;
|
||
contextPrompt += `- 用户名字:${baseContext.userInfo.name}\n`;
|
||
contextPrompt += `- 用户qq号:${baseContext.userInfo.id}\n`;
|
||
contextPrompt += `- 是否为主人:${baseContext.userInfo.isMaster ? '是' : '否'}(请注意!!!无论用户的用户名是什么,是否是主人都以这个为准!!禁止乱认主人!!)\n\n`;
|
||
|
||
contextPrompt += `[环境信息]\n`;
|
||
contextPrompt += `${baseContext.environmentInfo}\n\n`;
|
||
|
||
// 聊天历史(第一次迭代时提供)
|
||
if (iteration === 0 && baseContext.groupChatHistory) {
|
||
contextPrompt += baseContext.groupChatHistory + '\n';
|
||
}
|
||
|
||
// 记忆信息(第一次迭代时提供)
|
||
if (iteration === 0 && baseContext.memoryContext) {
|
||
contextPrompt += baseContext.memoryContext + '\n';
|
||
}
|
||
|
||
// 当前用户输入
|
||
contextPrompt += `=== 当前对话 ===\n`;
|
||
contextPrompt += `用户当前说:"${userInput}"\n\n`;
|
||
|
||
// 思考历史(从第二次迭代开始)
|
||
if (iteration > 0 && thinkingSteps.length > 0) {
|
||
contextPrompt += `=== 你的思考和行动历史 ===\n`;
|
||
thinkingSteps.forEach((step, index) => {
|
||
contextPrompt += `第${step.iteration}轮:\n`;
|
||
contextPrompt += `- 你的思考: ${step.thought}\n`;
|
||
|
||
// 详细的行动信息
|
||
if (step.decision.tool_calls && step.decision.tool_calls.length > 0) {
|
||
contextPrompt += `- 你决定的行动: 调用${step.decision.tool_calls.length}个工具\n`;
|
||
step.decision.tool_calls.forEach((toolCall, i) => {
|
||
const toolName = toolCall.function.name;
|
||
const params = JSON.parse(toolCall.function.arguments);
|
||
contextPrompt += ` ${i + 1}. ${toolName}(${Object.entries(params).map(([k, v]) => `${k}="${v}"`).join(', ')})\n`;
|
||
});
|
||
} else if (step.decision.action) {
|
||
contextPrompt += `- 你决定的行动: ${step.decision.action}\n`;
|
||
}
|
||
|
||
// 详细的执行结果
|
||
if (step.observation.multipleTools && step.observation.toolResults) {
|
||
contextPrompt += `- 执行结果:\n`;
|
||
step.observation.toolResults.forEach((toolResult, i) => {
|
||
const status = toolResult.success ? '✓' : '✗';
|
||
contextPrompt += ` ${status} ${toolResult.toolName}: ${toolResult.result.message || toolResult.result.result || '完成'}\n`;
|
||
});
|
||
} else {
|
||
contextPrompt += `- 执行结果: ${step.observation.message || step.observation.result || '无结果'}\n`;
|
||
}
|
||
|
||
contextPrompt += `\n`;
|
||
});
|
||
}
|
||
|
||
// 当前迭代信息
|
||
contextPrompt += `=== 当前状态 ===\n`;
|
||
contextPrompt += `这是第${iteration + 1}轮思考\n`;
|
||
if (iteration === 0) {
|
||
contextPrompt += `这是第一轮,请仔细分析用户需求,确定是否需要收集更多信息\n`;
|
||
} else {
|
||
contextPrompt += `基于前面的思考和行动结果,请决定下一步\n`;
|
||
}
|
||
|
||
return contextPrompt;
|
||
}
|
||
|
||
/**
|
||
* 构建人格提示词
|
||
* @returns {Promise<string>} 人格提示词
|
||
*/
|
||
async buildPersonalityPrompt() {
|
||
try {
|
||
const basePrompt = await getSystemPrompt();
|
||
|
||
// 简化版人格提示词,专注于ReAct模式
|
||
const reactPersonality = `你是一个名为晶灵的智能助手,具有以下特征:
|
||
1. 性格温和友善,喜欢帮助用户解决问题
|
||
2. 知识渊博,能够回答各种问题
|
||
3. 偶尔会使用一些可爱的表情和语气
|
||
4. 会记住与用户的对话内容,提供个性化的回复
|
||
5. 能够理解中文语境和网络用语
|
||
6. 回复简洁明了,避免过于冗长
|
||
|
||
在ReAct模式下,你需要:
|
||
- 仔细思考用户的真实需求
|
||
- 主动使用工具收集必要信息
|
||
- 基于收集的信息给出准确回答
|
||
- 当信息足够时使用find_answer工具结束
|
||
- 当需要更多信息时使用need_more_info工具继续`;
|
||
|
||
return reactPersonality;
|
||
} catch (error) {
|
||
logger.error(`[crystelf-ai] 构建人格提示词失败: ${error.message}`);
|
||
return '你是一个友善的智能助手,请帮助用户解决问题。';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算时间差
|
||
* @param {number} pastTime - 过去时间戳
|
||
* @returns {string} 时间差字符串
|
||
*/
|
||
calculateTimeDifference(pastTime) {
|
||
const now = Date.now();
|
||
const diff = now - pastTime;
|
||
|
||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||
|
||
let result = '';
|
||
if (days > 0) {
|
||
result += `${days}天`;
|
||
}
|
||
if (hours > 0) {
|
||
result += `${hours}小时`;
|
||
}
|
||
if (minutes > 0) {
|
||
result += `${minutes}分钟`;
|
||
}
|
||
return result || '刚刚';
|
||
}
|
||
|
||
/**
|
||
* 重置上下文(新对话时调用)
|
||
*/
|
||
reset() {
|
||
this.baseContext = null;
|
||
}
|
||
}
|
||
|
||
export default new ContextBuilder(); |