feat: ReAct !

This commit is contained in:
Jerry 2025-12-14 10:26:26 +08:00
parent 8bc82dbd09
commit 448f26f55a
9 changed files with 582 additions and 846 deletions

View File

@ -73,28 +73,18 @@
"surprise" "surprise"
] ]
}, },
"?useReAct": "是否启用ReAct工具调用模式(需要模型支持function calling)", "?useReAct": "是否启用真正的ReAct模式(基于标准Function Calling需要模型支持)",
"useReAct": false, "useReAct": true,
"?reactConfig": "ReAct配置", "?reactConfig": "真正的ReAct配置(基于标准Function Calling)",
"reactConfig": { "reactConfig": {
"?maxIterations": "最大思考迭代次数", "?maxIterations": "最大工具调用轮数(AI自主控制)",
"maxIterations": 8, "maxIterations": 8,
"?timeout": "ReAct超时时间(毫秒)", "?timeout": "整个对话超时时间(毫秒)",
"timeout": 45000, "timeout": 45000,
"?enableThinking": "是否显示思考过程", "?enableThinking": "是否记录工具执行历史(调试用)",
"enableThinking": false, "enableThinking": false,
"?toolTimeout": "单个工具执行超时时间(毫秒)", "?memoryLimit": "自动搜索的记忆数量限制",
"toolTimeout": 10000, "memoryLimit": 5
"?minConfidence": "最小信心度阈值(0-1),低于此值会继续收集信息",
"minConfidence": 0.7,
"?contextMemoryLimit": "上下文记忆数量限制",
"contextMemoryLimit": 5,
"?enableContextHistory": "是否在迭代中包含聊天历史",
"enableContextHistory": true,
"?maxParallelTools": "单次最大并行工具调用数量",
"maxParallelTools": 5,
"?enableToolCombinations": "是否启用工具组合建议",
"enableToolCombinations": true
}, },
"?imageConfig": "图像生成配置", "?imageConfig": "图像生成配置",
"imageConfig": { "imageConfig": {

View File

@ -379,7 +379,7 @@ class AiCaller {
} }
/** /**
* ReAct模式AI调用 * 真正的ReAct模式AI调用 - 基于标准Function Calling
* @param prompt 用户输入 * @param prompt 用户输入
* @param chatHistory 聊天历史 * @param chatHistory 聊天历史
* @param memories 记忆 * @param memories 记忆
@ -401,7 +401,7 @@ class AiCaller {
imageMessages imageMessages
}; };
// 执行ReAct循环 // 执行真正的ReAct循环 - 单次AI调用AI自主控制流程
const result = await ReactEngine.execute(prompt, context); const result = await ReactEngine.execute(prompt, context);
if (result.success) { if (result.success) {
@ -409,9 +409,10 @@ class AiCaller {
success: true, success: true,
response: result.responses, response: result.responses,
rawResponse: JSON.stringify(result.responses), rawResponse: JSON.stringify(result.responses),
thinkingSteps: result.thinkingSteps, toolExecutionHistory: result.toolExecutionHistory,
iterations: result.iterations, iterations: result.iterations,
duration: result.duration duration: result.duration,
messageHistory: result.messageHistory
}; };
} else { } else {
return { return {
@ -421,7 +422,7 @@ class AiCaller {
}; };
} }
} catch (error) { } catch (error) {
logger.error(`[crystelf-ai] ReAct模式调用失败: ${error.message}`); logger.error(`[crystelf-ai] 真正的ReAct模式调用失败: ${error.message}`);
return { return {
success: false, success: false,
error: error.message error: error.message

View File

@ -1,256 +0,0 @@
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();

View File

@ -1,9 +1,6 @@
import ConfigControl from '../config/configControl.js'; import ConfigControl from '../config/configControl.js';
import toolRegistry from './tools/toolRegistry.js'; import toolRegistry from './tools/toolRegistry.js';
import AiCaller from './aiCaller.js'; import OpenaiChat from '../../modules/openai/openaiChat.js';
import ContextBuilder from './contextBuilder.js';
import MemorySystem from './memorySystem.js';
import ToolCombinations from './tools/toolCombinations.js';
/** /**
* ReAct引擎 * ReAct引擎
@ -11,9 +8,8 @@ import ToolCombinations from './tools/toolCombinations.js';
*/ */
class ReactEngine { class ReactEngine {
constructor() { constructor() {
this.maxIterations = 5; this.maxIterations = 8;
this.timeout = 30000; this.timeout = 45000;
this.toolTimeout = 10000;
this.enableThinking = false; this.enableThinking = false;
} }
@ -22,9 +18,8 @@ class ReactEngine {
const config = await ConfigControl.get('ai'); const config = await ConfigControl.get('ai');
const reactConfig = config?.reactConfig || {}; const reactConfig = config?.reactConfig || {};
this.maxIterations = reactConfig.maxIterations || 5; this.maxIterations = reactConfig.maxIterations || 8;
this.timeout = reactConfig.timeout || 30000; this.timeout = reactConfig.timeout || 45000;
this.toolTimeout = reactConfig.toolTimeout || 10000;
this.enableThinking = reactConfig.enableThinking || false; this.enableThinking = reactConfig.enableThinking || false;
logger.info('[crystelf-ai] ReAct引擎初始化完成'); logger.info('[crystelf-ai] ReAct引擎初始化完成');
@ -34,7 +29,7 @@ class ReactEngine {
} }
/** /**
* 执行ReAct循环 * 执行标准ReAct循环
* @param {string} userInput - 用户输入 * @param {string} userInput - 用户输入
* @param {Object} context - 执行上下文 * @param {Object} context - 执行上下文
* @returns {Promise<Object>} 执行结果 * @returns {Promise<Object>} 执行结果
@ -42,108 +37,50 @@ class ReactEngine {
async execute(userInput, context) { async execute(userInput, context) {
const startTime = Date.now(); const startTime = Date.now();
const responseQueue = []; const responseQueue = [];
const thinkingSteps = []; const thinkingHistory = [];
// 扩展上下文
const executionContext = {
...context,
responseQueue,
startTime,
userInput
};
try { try {
// 构建基础上下文(包含记忆搜索) logger.info(`[crystelf-ai] 开始标准ReAct循环用户输入: ${userInput.substring(0, 50)}...`);
const memories = await MemorySystem.searchMemories(context.e.user_id, userInput, 5);
const baseContext = await ContextBuilder.buildBaseContext(context.e, memories);
let shouldContinue = true; // 第一阶段:思考和信息收集
let iteration = 0; const phase1Result = await this.executePhase1_ThinkingAndCollection(userInput, context, thinkingHistory);
while (shouldContinue && iteration < this.maxIterations) { if (!phase1Result.success) {
// 检查超时 return {
if (Date.now() - startTime > this.timeout) { success: false,
logger.warn(`[crystelf-ai] ReAct超时已迭代 ${iteration}`); error: phase1Result.error,
break; responses: [{
} type: 'message',
data: '抱歉,我在分析您的问题时遇到了困难...',
// 构建当前迭代的上下文 at: -1,
const iterationContext = ContextBuilder.buildIterationContext( quote: -1,
baseContext, recall: false
iteration, }],
thinkingSteps, thinkingHistory: this.enableThinking ? thinkingHistory : []
userInput
);
// 思考阶段
const thought = await this.think(iterationContext, iteration);
// 决策阶段
const decision = await this.decide(thought, iterationContext, iteration);
// 行动阶段
const observation = await this.act(decision, executionContext);
// 记录思考步骤
const step = {
iteration: iteration + 1,
thought,
decision,
observation,
timestamp: Date.now(),
// 新增AI的推理过程和工具调用详情
reasoning: decision.reasoning || thought,
toolsUsed: observation.multipleTools ?
observation.toolResults?.map(tr => tr.toolName) || [] :
[decision.action || decision.tool_calls?.[0]?.function?.name].filter(Boolean),
executionTime: observation.duration || 0,
success: observation.success !== false
}; };
thinkingSteps.push(step);
// 检查是否应该结束循环
if (observation.shouldEnd === true) {
logger.info(`[crystelf-ai] AI决定结束循环${iteration + 1}轮完成`);
shouldContinue = false;
} else if (observation.shouldEnd === false) {
logger.info(`[crystelf-ai] AI决定继续收集信息进入第${iteration + 2}`);
shouldContinue = true;
}
iteration++;
} }
// 如果循环结束但没有响应,添加默认响应 // 第二阶段:基于收集的信息生成回复
if (responseQueue.length === 0) { const phase2Result = await this.executePhase2_ResponseGeneration(
responseQueue.push({ userInput,
type: 'message', context,
data: iteration >= this.maxIterations ? phase1Result.collectedInfo,
'我已经尽力思考了,但可能需要更多信息才能给出完美的回答...' : phase1Result.conversationMessages,
'让我来帮助您解决这个问题。', responseQueue
at: -1, );
quote: -1,
recall: false
});
}
// 重置上下文构建器
ContextBuilder.reset();
return { return {
success: true, success: true,
responses: responseQueue, responses: responseQueue,
thinkingSteps: this.enableThinking ? thinkingSteps : [], thinkingHistory: this.enableThinking ? thinkingHistory : [],
iterations: iteration, collectedInfo: phase1Result.collectedInfo,
conversationMessages: this.enableThinking ? phase1Result.conversationMessages : [],
duration: Date.now() - startTime duration: Date.now() - startTime
}; };
} catch (error) { } catch (error) {
logger.error(`[crystelf-ai] ReAct执行失败: ${error.message}`); logger.error(`[crystelf-ai] ReAct执行失败: ${error.message}`);
// 重置上下文构建器
ContextBuilder.reset();
// 返回错误响应
return { return {
success: false, success: false,
error: error.message, error: error.message,
@ -154,225 +91,484 @@ class ReactEngine {
quote: -1, quote: -1,
recall: false recall: false
}], }],
thinkingSteps: this.enableThinking ? thinkingSteps : [] thinkingHistory: this.enableThinking ? thinkingHistory : []
}; };
} }
} }
/** /**
* 思考阶段 * 第一阶段思考和信息收集
* 只包含信息收集工具不包含回复工具和人设
*/ */
async think(iterationContext, iteration) { async executePhase1_ThinkingAndCollection(userInput, context, thinkingHistory) {
const personalityPrompt = await ContextBuilder.buildPersonalityPrompt(); const startTime = Date.now();
const thinkingPrompt = `${personalityPrompt} // 构建第一阶段的消息历史
const conversationMessages = await this.buildPhase1Messages(userInput, context);
${iterationContext}
=== 思考任务 ===
请仔细分析当前情况思考
1. 用户的真实需求是什么
2. 我现在掌握了哪些信息
3. 还缺少什么关键信息
4. 下一步应该采取什么行动
请简洁明了地描述你的思考过程`;
try {
const response = await this.callAI(thinkingPrompt, {});
return response || '继续分析用户需求';
} catch (error) {
logger.error(`[crystelf-ai] 思考阶段失败: ${error.message}`);
return '继续分析用户需求';
}
}
/**
* 决策阶段
*/
async decide(thought, iterationContext, iteration) {
const toolSchemas = toolRegistry.getToolSchemas();
const personalityPrompt = await ContextBuilder.buildPersonalityPrompt();
const systemPrompt = `${personalityPrompt} // 获取信息收集工具(不包含回复工具)
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;
}
${iterationContext} logger.info(`[crystelf-ai] 第一阶段第${iteration + 1}AI思考和决策`);
=== 决策指南 === try {
基于你的思考"${thought}" // 🔥 每轮都是独立的 AI 调用
const aiResponse = await this.callAIWithTools(conversationMessages, collectionTools);
你现在需要决定下一步行动可用工具
${JSON.stringify(toolSchemas, null, 2)}
重要规则
1. 你可以同时调用多个工具来提高效率例如同时搜索记忆和获取聊天历史
2. 如果你已经有足够信息回答用户使用 find_answer 工具表示准备结束
3. 如果需要更多信息可以先调用 need_more_info 说明需求然后调用相应的信息收集工具
4. 使用具体的工具来收集信息 search_memory, get_chat_history
5. 使用 send_message 等工具来回复用户
6. 优先考虑用户的实际需求不要过度收集信息
7. 合理利用并行工具调用但避免调用冲突的工具
${ToolCombinations.getAllCombinationsDescription()}
请调用最合适的工具可以是多个记住
- 优先使用推荐的工具组合来提高效率
- 可以根据具体情况调整组合中的参数
- 避免调用冲突的工具如同时使用find_answer和need_more_info`;
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: '请根据当前思考决定下一步行动。' }
];
try {
// 调用支持工具调用的AI
const response = await this.callAIWithTools(messages, toolSchemas, {});
return response;
} catch (error) {
logger.error(`[crystelf-ai] 决策阶段失败: ${error.message}`);
return {
action: 'send_message',
parameters: {
content: '我在思考过程中遇到了问题,请稍后再试。'
}
};
}
}
/**
* 行动阶段 - 支持多工具并行调用
*/
async act(decision, context) {
try {
if (decision.tool_calls && decision.tool_calls.length > 0) {
// 处理多个工具调用
const toolResults = [];
const toolPromises = [];
logger.info(`[crystelf-ai] 准备并行执行 ${decision.tool_calls.length} 个工具`); // 将AI的回复添加到对话历史
conversationMessages.push(aiResponse);
// 并行执行所有工具调用 // 🔥 AI 可以做出不同决策
for (const toolCall of decision.tool_calls) { if (aiResponse.tool_calls && aiResponse.tool_calls.length > 0) {
const toolName = toolCall.function.name; // 决策1调用工具继续探索
const parameters = JSON.parse(toolCall.function.arguments); logger.info(`[crystelf-ai] AI决定调用 ${aiResponse.tool_calls.length} 个工具`);
logger.info(`[crystelf-ai] 调用工具: ${toolName}, 参数: ${JSON.stringify(parameters)}`); // 并行执行所有工具
const toolResults = await this.executeToolsParallel(
aiResponse.tool_calls,
conversationMessages,
context
);
const toolPromise = toolRegistry.executeTool(toolName, parameters, context) // 记录思考步骤
.then(result => ({ thinkingHistory.push({
toolName, phase: 1,
parameters, iteration: iteration + 1,
result, aiThought: aiResponse.content || '进行工具调用',
toolCallId: toolCall.id toolCalls: aiResponse.tool_calls,
})) toolResults: toolResults,
.catch(error => ({ timestamp: Date.now()
toolName,
parameters,
result: {
success: false,
message: `工具执行失败: ${error.message}`
},
toolCallId: toolCall.id,
error: error.message
}));
toolPromises.push(toolPromise);
}
// 等待所有工具执行完成
const results = await Promise.all(toolPromises);
// 整合结果
let shouldEnd = null;
let hasError = false;
const messages = [];
for (const { toolName, parameters, result, toolCallId, error } of results) {
toolResults.push({
toolName,
parameters,
result,
toolCallId,
success: !error
}); });
if (error) { // 检查AI是否通过工具调用标记找到答案
hasError = true; const answerResult = toolResults.find(result =>
messages.push(`工具 ${toolName} 执行失败: ${error}`); result.toolName === 'found_answer' || result.toolName === 'not_enough_info'
} else { );
messages.push(`工具 ${toolName} 执行成功: ${result.message || result.result || '完成'}`);
if (answerResult) {
// 检查循环控制 if (answerResult.toolName === 'found_answer') {
if (result.shouldEnd !== undefined) { // 决策2找到答案标记找到答案跳出循环到答案回复
shouldEnd = result.shouldEnd; 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 到下一轮循环
} }
return { } catch (error) {
success: !hasError, logger.error(`[crystelf-ai] 第一阶段第${iteration + 1}轮失败: ${error.message}`);
message: messages.join('\n'), break;
toolResults, }
shouldEnd, }
multipleTools: true,
toolCount: decision.tool_calls.length // 如果循环结束仍未找到答案,进行最终评估
}; if (!foundAnswer) {
logger.info(`[crystelf-ai] 达到最大迭代次数,进行最终评估`);
} else if (decision.action) {
// 处理单个直接行动 const finalEvaluation = await this.performFinalEvaluation(conversationMessages, collectionTools);
const result = await toolRegistry.executeTool(decision.action, decision.parameters, context);
return { if (finalEvaluation.foundAnswer) {
...result, foundAnswer = true;
multipleTools: false, collectedInfo = finalEvaluation.answerSummary;
toolCount: 1
};
} else { } else {
return { return {
success: false, success: false,
message: '未识别的决策格式', error: `经过${this.maxIterations}轮思考仍未找到足够信息`,
multipleTools: false, collectedInfo: '',
toolCount: 0 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) { } catch (error) {
logger.error(`[crystelf-ai] 行动阶段失败: ${error.message}`); logger.error(`[crystelf-ai] 第二阶段失败: ${error.message}`);
return {
success: false, // 添加默认回复
message: `执行行动失败: ${error.message}`, responseQueue.push({
multipleTools: false, type: 'message',
toolCount: 0 data: '我已经收集了相关信息,但在生成回复时遇到了问题...',
}; at: -1,
quote: -1,
recall: false
});
return { success: false, error: error.message };
} }
} }
/** /**
* 调用AI文本模式 * 构建第一阶段消息思考阶段不包含人设
*/ */
async callAI(prompt, context) { async buildPhase1Messages(userInput, context) {
// 创建临时的事件对象用于AI调用 const systemPrompt = `你是一个信息分析专家,需要分析用户的问题并收集相关信息。
const tempE = {
user_id: 'react_system', === 分析任务 ===
bot: { uin: 'system' }, 用户问题"${userInput}"
group_id: 'system'
请分析
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'
];
const result = await AiCaller.callTextAi(prompt, [], [], tempE); 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'
];
if (result.success) { return allTools.filter(tool =>
return result.response; responseToolNames.includes(tool.function.name)
} else { );
throw new Error(result.error || 'AI调用失败'); }
/**
* 并行执行工具
*/
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工具调用模式 * 调用AI工具调用模式
*/ */
async callAIWithTools(messages, tools, context) { async callAIWithTools(messages, tools) {
// 使用系统配置调用AI
const config = await ConfigControl.get('ai'); const config = await ConfigControl.get('ai');
const apiCaller = AiCaller.getUserOpenaiInstance('system', config); const apiCaller = new OpenaiChat();
apiCaller.init(config.apiKey, config.baseApi);
try { try {
const completion = await apiCaller.openai.chat.completions.create({ const completion = await apiCaller.openai.chat.completions.create({
@ -381,30 +577,21 @@ ${ToolCombinations.getAllCombinationsDescription()}
tools: tools, tools: tools,
tool_choice: 'auto', tool_choice: 'auto',
temperature: config.temperature || 0.7, temperature: config.temperature || 0.7,
parallel_tool_calls: true // 启用并行工具调用 parallel_tool_calls: true
}); });
const message = completion.choices[0].message; const message = completion.choices[0].message;
if (message.tool_calls && message.tool_calls.length > 0) { if (message.tool_calls && message.tool_calls.length > 0) {
logger.info(`[crystelf-ai] AI决定调用 ${message.tool_calls.length} 个工具: ${message.tool_calls.map(tc => tc.function.name).join(', ')}`); logger.info(`[crystelf-ai] AI调用工具: ${message.tool_calls.map(tc => tc.function.name).join(', ')}`);
return {
tool_calls: message.tool_calls,
finished: false,
reasoning: message.content || '执行工具调用'
};
} else { } else {
return { logger.info(`[crystelf-ai] AI文本回复: ${message.content?.substring(0, 50)}...`);
action: 'send_message',
parameters: {
content: message.content
},
finished: true,
reasoning: message.content
};
} }
return message;
} catch (error) { } catch (error) {
logger.error(`[crystelf-ai] 工具调用AI失败: ${error.message}`); logger.error(`[crystelf-ai] AI调用失败: ${error.message}`);
throw error; throw error;
} }
} }

View File

@ -62,7 +62,6 @@ class ResponseHandler {
const processedMessages = []; const processedMessages = [];
for (const response of responses) { for (const response of responses) {
// ReAct响应已经是标准格式直接处理
const processedMessage = await this.processMessage(response, userMessage, groupId, user_id); const processedMessage = await this.processMessage(response, userMessage, groupId, user_id);
if (processedMessage) { if (processedMessage) {
processedMessages.push(processedMessage); processedMessages.push(processedMessage);

View File

@ -1,98 +0,0 @@
import BaseTool from './baseTool.js';
/**
* 找到答案工具 - 结束ReAct循环
*/
class FindAnswerTool extends BaseTool {
constructor() {
super(
'find_answer',
'当你已经收集到足够信息并准备给出最终回答时使用此工具',
{
type: 'object',
properties: {
confidence: {
type: 'number',
description: '回答的信心程度(0-1)1表示非常确信',
minimum: 0,
maximum: 1
},
summary: {
type: 'string',
description: '简要总结你收集到的关键信息'
},
ready_to_respond: {
type: 'boolean',
description: '是否准备好给出最终回答',
default: true
}
},
required: ['confidence', 'summary']
}
);
}
async execute(params, context) {
const { confidence, summary, ready_to_respond = true } = params;
logger.info(`[crystelf-ai] AI决定结束循环 - 信心度: ${confidence}, 摘要: ${summary}`);
return {
success: true,
message: `已收集足够信息,准备回答`,
shouldEnd: true,
confidence,
summary,
readyToRespond: ready_to_respond
};
}
}
/**
* 需要更多信息工具 - 继续ReAct循环
*/
class NeedMoreInfoTool extends BaseTool {
constructor() {
super(
'need_more_info',
'当你需要更多信息才能给出满意回答时使用此工具',
{
type: 'object',
properties: {
missing_info: {
type: 'string',
description: '描述还缺少什么关键信息'
},
next_action_plan: {
type: 'string',
description: '下一步计划采取什么行动获取信息'
},
urgency: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: '获取这些信息的紧急程度',
default: 'medium'
}
},
required: ['missing_info', 'next_action_plan']
}
);
}
async execute(params, context) {
const { missing_info, next_action_plan, urgency = 'medium' } = params;
logger.info(`[crystelf-ai] AI需要更多信息 - 缺失: ${missing_info}, 计划: ${next_action_plan}`);
return {
success: true,
message: `需要更多信息: ${missing_info}`,
shouldEnd: false,
missingInfo: missing_info,
nextPlan: next_action_plan,
urgency
};
}
}
export { FindAnswerTool, NeedMoreInfoTool };

View File

@ -0,0 +1,87 @@
import BaseTool from './baseTool.js';
/**
* 找到答案工具 - 用于标记AI是否找到了足够的信息来回答用户
*/
class FindAnswerTool extends BaseTool {
constructor() {
super(
'found_answer',
'当你已经收集到足够信息可以回答用户问题时使用此工具',
{
type: 'object',
properties: {
answer_summary: {
type: 'string',
description: '简要总结你找到的关键信息,这将传递给下一阶段'
},
confidence: {
type: 'number',
description: '回答的信心程度(0-1)1表示非常确信',
minimum: 0,
maximum: 1,
default: 0.8
}
},
required: ['answer_summary']
}
);
}
async execute(params, context) {
const { answer_summary, confidence = 0.8 } = params;
logger.info(`[crystelf-ai] AI找到答案 - 信心度: ${confidence}, 摘要: ${answer_summary}`);
return {
success: true,
message: `已找到答案信息: ${answer_summary}`,
foundAnswer: true,
answerSummary: answer_summary,
confidence
};
}
}
/**
* 信息不足工具 - 用于标记AI认为信息不足
*/
class NotEnoughInfoTool extends BaseTool {
constructor() {
super(
'not_enough_info',
'当你认为收集的信息不足以回答用户问题时使用此工具',
{
type: 'object',
properties: {
reason: {
type: 'string',
description: '说明为什么信息不足,缺少什么关键信息'
},
tried_methods: {
type: 'array',
items: { type: 'string' },
description: '已经尝试过的搜索方法'
}
},
required: ['reason']
}
);
}
async execute(params, context) {
const { reason, tried_methods = [] } = params;
logger.info(`[crystelf-ai] AI认为信息不足 - 原因: ${reason}`);
return {
success: true,
message: `信息不足: ${reason}`,
foundAnswer: false,
reason,
triedMethods: tried_methods
};
}
}
export { FindAnswerTool, NotEnoughInfoTool };

View File

@ -1,174 +0,0 @@
/**
* 工具组合建议系统
* 为AI提供常用的工具组合模式
*/
class ToolCombinations {
constructor() {
this.combinations = new Map();
this.initializeCombinations();
}
initializeCombinations() {
// 信息收集组合
this.combinations.set('gather_user_info', {
name: '收集用户信息',
tools: ['search_memory', 'get_chat_history'],
description: '同时搜索用户记忆和获取聊天历史',
useCase: '当需要了解用户背景信息时',
example: {
search_memory: { query: '用户偏好', limit: 5 },
get_chat_history: { count: 10, include_bot: true }
}
});
// 信息收集并回复组合
this.combinations.set('search_and_respond', {
name: '搜索并回复',
tools: ['search_memory', 'send_message'],
description: '搜索相关信息后立即回复用户',
useCase: '当能够基于记忆直接回答时',
example: {
search_memory: { query: '相关关键词', limit: 3 },
send_message: { content: '基于搜索结果的回复' }
}
});
// 存储并回复组合
this.combinations.set('store_and_respond', {
name: '存储并回复',
tools: ['store_memory', 'send_message'],
description: '存储重要信息并回复用户',
useCase: '当用户提供新的重要信息时',
example: {
store_memory: { content: '重要信息', keywords: ['关键词'] },
send_message: { content: '我记住了这个信息' }
}
});
// 多渠道信息收集
this.combinations.set('comprehensive_search', {
name: '全面信息收集',
tools: ['search_memory', 'get_chat_history', 'need_more_info'],
description: '全面收集信息并说明还需要什么',
useCase: '处理复杂问题时',
example: {
search_memory: { query: '相关主题', limit: 5 },
get_chat_history: { count: 15, include_bot: true },
need_more_info: { missing_info: '具体需求', next_action_plan: '下一步计划' }
}
});
// 内容生成组合
this.combinations.set('generate_content', {
name: '生成内容',
tools: ['render_code', 'send_message'],
description: '渲染代码并发送说明',
useCase: '当需要展示代码示例时',
example: {
render_code: { code: '示例代码', language: 'javascript' },
send_message: { content: '这是相关的代码示例' }
}
});
// 完整回复组合
this.combinations.set('complete_response', {
name: '完整回复',
tools: ['find_answer', 'send_message', 'send_meme'],
description: '给出完整回答并结束对话',
useCase: '当有足够信息给出最终回答时',
example: {
find_answer: { confidence: 0.8, summary: '信息摘要' },
send_message: { content: '详细回答' },
send_meme: { emotion: 'happy' }
}
});
}
/**
* 获取推荐的工具组合
* @param {string} scenario - 场景描述
* @returns {Array} 推荐的组合
*/
getRecommendations(scenario) {
const recommendations = [];
// 基于场景关键词匹配
const scenarioLower = scenario.toLowerCase();
if (scenarioLower.includes('记忆') || scenarioLower.includes('历史')) {
recommendations.push(this.combinations.get('gather_user_info'));
}
if (scenarioLower.includes('回答') || scenarioLower.includes('回复')) {
recommendations.push(this.combinations.get('search_and_respond'));
}
if (scenarioLower.includes('存储') || scenarioLower.includes('记住')) {
recommendations.push(this.combinations.get('store_and_respond'));
}
if (scenarioLower.includes('复杂') || scenarioLower.includes('详细')) {
recommendations.push(this.combinations.get('comprehensive_search'));
}
if (scenarioLower.includes('代码') || scenarioLower.includes('示例')) {
recommendations.push(this.combinations.get('generate_content'));
}
if (scenarioLower.includes('完成') || scenarioLower.includes('结束')) {
recommendations.push(this.combinations.get('complete_response'));
}
return recommendations;
}
/**
* 获取所有组合的描述
* @returns {string} 格式化的组合描述
*/
getAllCombinationsDescription() {
let description = '=== 常用工具组合模式 ===\n\n';
for (const [key, combo] of this.combinations) {
description += `${combo.name}:\n`;
description += `- 工具: ${combo.tools.join(', ')}\n`;
description += `- 说明: ${combo.description}\n`;
description += `- 适用: ${combo.useCase}\n\n`;
}
return description;
}
/**
* 验证工具组合的兼容性
* @param {Array} toolNames - 工具名称数组
* @returns {Object} 验证结果
*/
validateCombination(toolNames) {
const conflicts = [];
const warnings = [];
// 检查冲突的工具组合
if (toolNames.includes('find_answer') && toolNames.includes('need_more_info')) {
conflicts.push('find_answer 和 need_more_info 不能同时使用');
}
if (toolNames.includes('send_message') && toolNames.length === 1) {
warnings.push('单独使用 send_message 可能缺少信息收集');
}
// 检查工具数量
if (toolNames.length > 5) {
warnings.push('同时调用过多工具可能影响性能');
}
return {
isValid: conflicts.length === 0,
conflicts,
warnings,
toolCount: toolNames.length
};
}
}
export default new ToolCombinations();

View File

@ -3,7 +3,7 @@ import { SendMessageTool, SendMemeTool, PokeTool } from './messageTool.js';
import { SearchMemoryTool, GetChatHistoryTool } from './retrievalTool.js'; import { SearchMemoryTool, GetChatHistoryTool } from './retrievalTool.js';
import { StoreMemoryTool } from './memoryTool.js'; import { StoreMemoryTool } from './memoryTool.js';
import { RenderCodeTool, RenderMarkdownTool, GenerateImageTool } from './contentTool.js'; import { RenderCodeTool, RenderMarkdownTool, GenerateImageTool } from './contentTool.js';
import { FindAnswerTool, NeedMoreInfoTool } from './controlTool.js'; import { FindAnswerTool, NotEnoughInfoTool } from './findAnswerTool.js';
/** /**
* 工具初始化器 * 工具初始化器
@ -32,9 +32,9 @@ class ToolInitializer {
toolRegistry.register(new RenderMarkdownTool()); toolRegistry.register(new RenderMarkdownTool());
toolRegistry.register(new GenerateImageTool()); toolRegistry.register(new GenerateImageTool());
// 注册循环控制工具 // 注册答案判断工具
toolRegistry.register(new FindAnswerTool()); toolRegistry.register(new FindAnswerTool());
toolRegistry.register(new NeedMoreInfoTool()); toolRegistry.register(new NotEnoughInfoTool());
const toolCount = toolRegistry.getToolList().length; const toolCount = toolRegistry.getToolList().length;
logger.info(`[crystelf-ai] 工具初始化完成,共注册 ${toolCount} 个工具`); logger.info(`[crystelf-ai] 工具初始化完成,共注册 ${toolCount} 个工具`);