import ConfigControl from '../config/configControl.js'; import toolRegistry from './tools/toolRegistry.js'; import OpenaiChat from '../../modules/openai/openaiChat.js'; /** * ReAct引擎 * Reasoning + Acting 循环 */ class ReactEngine { constructor() { this.maxIterations = 8; this.timeout = 45000; this.enableThinking = false; } async init() { try { const config = await ConfigControl.get('ai'); const reactConfig = config?.reactConfig || {}; this.maxIterations = reactConfig.maxIterations || 8; this.timeout = reactConfig.timeout || 45000; this.enableThinking = reactConfig.enableThinking || false; logger.info('[crystelf-ai] ReAct引擎初始化完成'); } catch (error) { logger.error(`[crystelf-ai] ReAct引擎初始化失败: ${error.message}`); } } /** * 执行标准ReAct循环 * @param {string} userInput - 用户输入 * @param {Object} context - 执行上下文 * @returns {Promise} 执行结果 */ async execute(userInput, context) { const startTime = Date.now(); const responseQueue = []; const thinkingHistory = []; try { logger.info(`[crystelf-ai] 开始标准ReAct循环,用户输入: ${userInput.substring(0, 50)}...`); // 第一阶段:思考和信息收集 const phase1Result = await this.executePhase1_ThinkingAndCollection(userInput, context, thinkingHistory); if (!phase1Result.success) { return { success: false, error: phase1Result.error, responses: [{ type: 'message', data: '抱歉,我在分析您的问题时遇到了困难...', at: -1, quote: -1, recall: false }], thinkingHistory: this.enableThinking ? thinkingHistory : [] }; } // 第二阶段:基于收集的信息生成回复 const phase2Result = await this.executePhase2_ResponseGeneration( userInput, context, phase1Result.collectedInfo, phase1Result.conversationMessages, responseQueue ); return { success: true, responses: responseQueue, thinkingHistory: this.enableThinking ? thinkingHistory : [], collectedInfo: phase1Result.collectedInfo, conversationMessages: this.enableThinking ? phase1Result.conversationMessages : [], duration: Date.now() - startTime }; } catch (error) { logger.error(`[crystelf-ai] ReAct执行失败: ${error.message}`); return { success: false, error: error.message, responses: [{ type: 'message', data: '抱歉,我在处理您的请求时遇到了问题...', at: -1, quote: -1, recall: false }], thinkingHistory: this.enableThinking ? thinkingHistory : [] }; } } /** * 第一阶段:思考和信息收集 * 只包含信息收集工具,不包含回复工具和人设 */ async executePhase1_ThinkingAndCollection(userInput, context, thinkingHistory) { const startTime = Date.now(); // 构建第一阶段的消息历史 const conversationMessages = await this.buildPhase1Messages(userInput, context); // 获取信息收集工具(不包含回复工具) const collectionTools = this.getCollectionTools(); logger.info(`[crystelf-ai] 第一阶段开始 - 思考和信息收集`); let foundAnswer = false; let collectedInfo = ''; // 🔥 物理循环:for 循环提供执行框架 for (let iteration = 0; iteration < this.maxIterations; iteration++) { // 检查超时 if (Date.now() - startTime > this.timeout) { logger.warn(`[crystelf-ai] 第一阶段超时,已迭代 ${iteration} 次`); break; } logger.info(`[crystelf-ai] 第一阶段第${iteration + 1}轮:AI思考和决策`); try { // 🔥 每轮都是独立的 AI 调用 const aiResponse = await this.callAIWithTools(conversationMessages, collectionTools); // 将AI的回复添加到对话历史 conversationMessages.push(aiResponse); // 🔥 AI 可以做出不同决策 if (aiResponse.tool_calls && aiResponse.tool_calls.length > 0) { // 决策1:调用工具继续探索 logger.info(`[crystelf-ai] AI决定调用 ${aiResponse.tool_calls.length} 个工具`); // 并行执行所有工具 const toolResults = await this.executeToolsParallel( aiResponse.tool_calls, conversationMessages, context ); // 记录思考步骤 thinkingHistory.push({ phase: 1, iteration: iteration + 1, aiThought: aiResponse.content || '进行工具调用', toolCalls: aiResponse.tool_calls, toolResults: toolResults, timestamp: Date.now() }); // 检查AI是否通过工具调用标记找到答案 const answerResult = toolResults.find(result => result.toolName === 'found_answer' || result.toolName === 'not_enough_info' ); if (answerResult) { if (answerResult.toolName === 'found_answer') { // 决策2:找到答案,标记找到答案,跳出循环到答案回复 foundAnswer = true; collectedInfo = answerResult.result.answerSummary || ''; logger.info(`[crystelf-ai] AI找到答案,跳出循环进入第二阶段`); break; // 🔥 跳出物理循环 } else { // AI明确表示信息不足,结束收集 logger.info(`[crystelf-ai] AI认为信息不足,结束收集阶段`); return { success: false, error: `信息收集失败: ${answerResult.result.reason}`, collectedInfo: '', conversationMessages }; } } } else { // 决策3:继续思考,下一轮再决策 logger.info(`[crystelf-ai] AI进行纯思考: ${aiResponse.content?.substring(0, 100)}...`); thinkingHistory.push({ phase: 1, iteration: iteration + 1, aiThought: aiResponse.content || '继续思考', toolCalls: [], toolResults: [], timestamp: Date.now() }); // continue 到下一轮循环 } } catch (error) { logger.error(`[crystelf-ai] 第一阶段第${iteration + 1}轮失败: ${error.message}`); break; } } // 如果循环结束仍未找到答案,进行最终评估 if (!foundAnswer) { logger.info(`[crystelf-ai] 达到最大迭代次数,进行最终评估`); const finalEvaluation = await this.performFinalEvaluation(conversationMessages, collectionTools); if (finalEvaluation.foundAnswer) { foundAnswer = true; collectedInfo = finalEvaluation.answerSummary; } else { return { success: false, error: `经过${this.maxIterations}轮思考仍未找到足够信息`, collectedInfo: '', conversationMessages }; } } return { success: foundAnswer, collectedInfo, conversationMessages, iterations: this.maxIterations }; } /** * 第二阶段:基于收集的信息生成回复 * 包含人设和回复工具,基于第一阶段的思考结果 */ async executePhase2_ResponseGeneration(userInput, context, collectedInfo, phase1Messages, responseQueue) { logger.info(`[crystelf-ai] 第二阶段开始 - 基于收集信息生成回复`); // 构建第二阶段的消息历史(包含人设和第一阶段结果) const phase2Messages = await this.buildPhase2Messages(userInput, context, collectedInfo, phase1Messages); // 获取回复工具(包含所有回复相关工具) const responseTools = this.getResponseTools(); try { // AI基于收集的信息生成回复 const aiResponse = await this.callAIWithTools(phase2Messages, responseTools); if (aiResponse.tool_calls && aiResponse.tool_calls.length > 0) { // AI调用回复工具 logger.info(`[crystelf-ai] AI调用 ${aiResponse.tool_calls.length} 个回复工具`); await this.executeResponseTools(aiResponse.tool_calls, responseQueue, context); } else if (aiResponse.content) { // AI直接给出文本回复 responseQueue.push({ type: 'message', data: aiResponse.content, at: -1, quote: -1, recall: false }); } return { success: true }; } catch (error) { logger.error(`[crystelf-ai] 第二阶段失败: ${error.message}`); // 添加默认回复 responseQueue.push({ type: 'message', data: '我已经收集了相关信息,但在生成回复时遇到了问题...', at: -1, quote: -1, recall: false }); return { success: false, error: error.message }; } } /** * 构建第一阶段消息(思考阶段,不包含人设) */ async buildPhase1Messages(userInput, context) { const systemPrompt = `你是一个信息分析专家,需要分析用户的问题并收集相关信息。 === 分析任务 === 用户问题:"${userInput}" 请分析: 1. 用户想要什么信息? 2. 需要搜索哪些相关内容? 3. 应该使用什么工具来收集信息? === 可用工具 === - search_memory: 搜索用户的历史记忆和偏好 - get_chat_history: 获取最近的聊天记录 - found_answer: 当收集到足够信息时标记找到答案 - not_enough_info: 当认为信息不足时标记 === 重要规则 === 1. 可以同时调用多个工具来并行收集信息 2. 仔细分析每个工具的结果 3. 当有足够信息回答用户问题时,调用found_answer 4. 如果多次尝试仍无法找到足够信息,调用not_enough_info 5. 专注于信息收集,不要尝试直接回答用户 请开始分析用户问题并收集相关信息。`; return [ { role: 'system', content: systemPrompt }, { role: 'user', content: `请分析这个问题并收集相关信息:"${userInput}"` } ]; } /** * 构建第二阶段消息(回复阶段,包含人设和第一阶段结果) */ async buildPhase2Messages(userInput, context, collectedInfo, phase1Messages) { const { e } = context; 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 thinkingProcess = this.extractThinkingProcess(phase1Messages); const systemPrompt = `你是一个名为${botInfo.name}的智能助手,具有以下特征: 1. 性格温和友善,喜欢帮助用户解决问题 2. 知识渊博,能够回答各种问题 3. 偶尔会使用一些可爱的表情和语气 4. 会记住与用户的对话内容,提供个性化的回复 5. 能够理解中文语境和网络用语 6. 回复简洁明了,避免过于冗长 === 当前对话信息 === - 你的昵称:${botInfo.name} - 你的QQ号:${botInfo.id} - 用户昵称:${userInfo.name} - 用户QQ号:${userInfo.id} - 用户是否为主人:${userInfo.isMaster ? '是' : '否'} - 当前时间:${formatDate} ${formatTime} === 用户问题 === "${userInput}" === 已收集的信息 === ${collectedInfo} === 思考过程摘要 === ${thinkingProcess} === 回复指南 === 基于以上收集的信息,请给用户一个准确、友好的回复。你可以使用以下工具: - send_message: 发送文本消息(支持@用户和引用消息) - send_meme: 发送表情包 - render_code: 渲染代码为图片 - render_markdown: 渲染Markdown为图片 - generate_image: 生成图片 - store_memory: 存储重要信息 - poke_user: 戳一戳用户 请根据收集的信息给出最合适的回复。`; return [ { role: 'system', content: systemPrompt }, { role: 'user', content: `请基于收集的信息回复用户的问题:"${userInput}"` } ]; } /** * 获取信息收集工具(第一阶段使用) */ getCollectionTools() { const allTools = toolRegistry.getToolSchemas(); const collectionToolNames = [ 'search_memory', 'get_chat_history', 'found_answer', 'not_enough_info' ]; return allTools.filter(tool => collectionToolNames.includes(tool.function.name) ); } /** * 获取回复工具(第二阶段使用) */ getResponseTools() { const allTools = toolRegistry.getToolSchemas(); const responseToolNames = [ 'send_message', 'send_meme', 'render_code', 'render_markdown', 'generate_image', 'store_memory', 'poke_user' ]; return allTools.filter(tool => responseToolNames.includes(tool.function.name) ); } /** * 并行执行工具 */ async executeToolsParallel(toolCalls, conversationMessages, context) { const toolPromises = toolCalls.map(async (toolCall) => { const toolName = toolCall.function.name; const parameters = JSON.parse(toolCall.function.arguments); logger.info(`[crystelf-ai] 执行工具: ${toolName}(${JSON.stringify(parameters)})`); try { const result = await toolRegistry.executeTool(toolName, parameters, context); // 将工具结果添加到对话历史 conversationMessages.push({ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify({ success: result.success, message: result.message || result.result, data: result }) }); return { toolName, parameters, result, success: result.success !== false, toolCallId: toolCall.id }; } catch (error) { logger.error(`[crystelf-ai] 工具执行失败: ${toolName} - ${error.message}`); conversationMessages.push({ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify({ success: false, error: error.message }) }); return { toolName, parameters, result: { success: false, message: error.message }, success: false, toolCallId: toolCall.id, error: error.message }; } }); return await Promise.all(toolPromises); } /** * 执行回复工具 */ async executeResponseTools(toolCalls, responseQueue, context) { for (const toolCall of toolCalls) { const toolName = toolCall.function.name; const parameters = JSON.parse(toolCall.function.arguments); try { const result = await toolRegistry.executeTool(toolName, parameters, { ...context, responseQueue }); logger.info(`[crystelf-ai] 回复工具执行成功: ${toolName}`); } catch (error) { logger.error(`[crystelf-ai] 回复工具执行失败: ${toolName} - ${error.message}`); } } } /** * 最终评估(当达到最大迭代次数时) */ async performFinalEvaluation(conversationMessages, collectionTools) { logger.info(`[crystelf-ai] 执行最终评估`); // 添加最终评估提示 conversationMessages.push({ role: 'user', content: '已达到最大搜索次数,请基于目前收集的所有信息做最终判断:是否有足够信息回答用户问题?请调用found_answer或not_enough_info工具。' }); try { const aiResponse = await this.callAIWithTools(conversationMessages, collectionTools); if (aiResponse.tool_calls && aiResponse.tool_calls.length > 0) { const finalTool = aiResponse.tool_calls[0]; const toolName = finalTool.function.name; const parameters = JSON.parse(finalTool.function.arguments); if (toolName === 'found_answer') { return { foundAnswer: true, answerSummary: parameters.answer_summary || '基于收集的信息可以回答' }; } } return { foundAnswer: false }; } catch (error) { logger.error(`[crystelf-ai] 最终评估失败: ${error.message}`); return { foundAnswer: false }; } } /** * 提取思考过程摘要 */ extractThinkingProcess(phase1Messages) { const thinkingSteps = []; for (let i = 0; i < phase1Messages.length; i++) { const message = phase1Messages[i]; if (message.role === 'assistant' && message.tool_calls) { const toolNames = message.tool_calls.map(tc => tc.function.name).join(', '); thinkingSteps.push(`调用了工具: ${toolNames}`); } else if (message.role === 'tool') { try { const toolResult = JSON.parse(message.content); if (toolResult.success) { thinkingSteps.push(`获得结果: ${toolResult.message}`); } } catch (e) { // 忽略解析错误 } } } return thinkingSteps.length > 0 ? thinkingSteps.join('\n') : '进行了信息收集和分析'; } /** * 调用AI(工具调用模式) */ async callAIWithTools(messages, tools) { const config = await ConfigControl.get('ai'); const apiCaller = new OpenaiChat(); apiCaller.init(config.apiKey, config.baseApi); try { const completion = await apiCaller.openai.chat.completions.create({ model: config.modelType, messages: messages, tools: tools, tool_choice: 'auto', temperature: config.temperature || 0.7, parallel_tool_calls: true }); const message = completion.choices[0].message; if (message.tool_calls && message.tool_calls.length > 0) { logger.info(`[crystelf-ai] AI调用工具: ${message.tool_calls.map(tc => tc.function.name).join(', ')}`); } else { logger.info(`[crystelf-ai] AI文本回复: ${message.content?.substring(0, 50)}...`); } return message; } catch (error) { logger.error(`[crystelf-ai] AI调用失败: ${error.message}`); throw error; } } } export default new ReactEngine();