crystelf-plugin/lib/ai/contextBuilder.js
2025-12-14 00:58:38 +08:00

256 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();