mirror of
https://github.com/Jerryplusy/crystelf-plugin.git
synced 2026-01-29 09:17:27 +00:00
feat: ReAct !
This commit is contained in:
parent
8bc82dbd09
commit
448f26f55a
@ -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": {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
|
||||||
@ -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: [{
|
||||||
}
|
|
||||||
|
|
||||||
// 构建当前迭代的上下文
|
|
||||||
const iterationContext = ContextBuilder.buildIterationContext(
|
|
||||||
baseContext,
|
|
||||||
iteration,
|
|
||||||
thinkingSteps,
|
|
||||||
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) {
|
|
||||||
responseQueue.push({
|
|
||||||
type: 'message',
|
type: 'message',
|
||||||
data: iteration >= this.maxIterations ?
|
data: '抱歉,我在分析您的问题时遇到了困难...',
|
||||||
'我已经尽力思考了,但可能需要更多信息才能给出完美的回答...' :
|
|
||||||
'让我来帮助您解决这个问题。',
|
|
||||||
at: -1,
|
at: -1,
|
||||||
quote: -1,
|
quote: -1,
|
||||||
recall: false
|
recall: false
|
||||||
});
|
}],
|
||||||
|
thinkingHistory: this.enableThinking ? thinkingHistory : []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置上下文构建器
|
// 第二阶段:基于收集的信息生成回复
|
||||||
ContextBuilder.reset();
|
const phase2Result = await this.executePhase2_ResponseGeneration(
|
||||||
|
userInput,
|
||||||
|
context,
|
||||||
|
phase1Result.collectedInfo,
|
||||||
|
phase1Result.conversationMessages,
|
||||||
|
responseQueue
|
||||||
|
);
|
||||||
|
|
||||||
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}
|
// 获取信息收集工具(不包含回复工具)
|
||||||
|
const collectionTools = this.getCollectionTools();
|
||||||
|
|
||||||
=== 思考任务 ===
|
logger.info(`[crystelf-ai] 第一阶段开始 - 思考和信息收集`);
|
||||||
请仔细分析当前情况,思考:
|
|
||||||
1. 用户的真实需求是什么?
|
|
||||||
2. 我现在掌握了哪些信息?
|
|
||||||
3. 还缺少什么关键信息?
|
|
||||||
4. 下一步应该采取什么行动?
|
|
||||||
|
|
||||||
请简洁明了地描述你的思考过程。`;
|
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 {
|
try {
|
||||||
const response = await this.callAI(thinkingPrompt, {});
|
// 🔥 每轮都是独立的 AI 调用
|
||||||
return response || '继续分析用户需求';
|
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) {
|
} catch (error) {
|
||||||
logger.error(`[crystelf-ai] 思考阶段失败: ${error.message}`);
|
logger.error(`[crystelf-ai] 第一阶段第${iteration + 1}轮失败: ${error.message}`);
|
||||||
return '继续分析用户需求';
|
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 decide(thought, iterationContext, iteration) {
|
async buildPhase1Messages(userInput, context) {
|
||||||
const toolSchemas = toolRegistry.getToolSchemas();
|
const systemPrompt = `你是一个信息分析专家,需要分析用户的问题并收集相关信息。
|
||||||
const personalityPrompt = await ContextBuilder.buildPersonalityPrompt();
|
|
||||||
|
|
||||||
const systemPrompt = `${personalityPrompt}
|
=== 分析任务 ===
|
||||||
|
用户问题:"${userInput}"
|
||||||
|
|
||||||
${iterationContext}
|
请分析:
|
||||||
|
1. 用户想要什么信息?
|
||||||
|
2. 需要搜索哪些相关内容?
|
||||||
|
3. 应该使用什么工具来收集信息?
|
||||||
|
|
||||||
=== 决策指南 ===
|
=== 可用工具 ===
|
||||||
基于你的思考:"${thought}"
|
- search_memory: 搜索用户的历史记忆和偏好
|
||||||
|
- get_chat_history: 获取最近的聊天记录
|
||||||
|
- found_answer: 当收集到足够信息时标记找到答案
|
||||||
|
- not_enough_info: 当认为信息不足时标记
|
||||||
|
|
||||||
你现在需要决定下一步行动。可用工具:
|
=== 重要规则 ===
|
||||||
${JSON.stringify(toolSchemas, null, 2)}
|
1. 可以同时调用多个工具来并行收集信息
|
||||||
|
2. 仔细分析每个工具的结果
|
||||||
|
3. 当有足够信息回答用户问题时,调用found_answer
|
||||||
|
4. 如果多次尝试仍无法找到足够信息,调用not_enough_info
|
||||||
|
5. 专注于信息收集,不要尝试直接回答用户
|
||||||
|
|
||||||
重要规则:
|
请开始分析用户问题并收集相关信息。`;
|
||||||
1. 你可以同时调用多个工具来提高效率,例如同时搜索记忆和获取聊天历史
|
|
||||||
2. 如果你已经有足够信息回答用户,使用 find_answer 工具表示准备结束
|
|
||||||
3. 如果需要更多信息,可以先调用 need_more_info 说明需求,然后调用相应的信息收集工具
|
|
||||||
4. 使用具体的工具来收集信息(如 search_memory, get_chat_history 等)
|
|
||||||
5. 使用 send_message 等工具来回复用户
|
|
||||||
6. 优先考虑用户的实际需求,不要过度收集信息
|
|
||||||
7. 合理利用并行工具调用,但避免调用冲突的工具
|
|
||||||
|
|
||||||
${ToolCombinations.getAllCombinationsDescription()}
|
return [
|
||||||
|
|
||||||
请调用最合适的工具(可以是多个)。记住:
|
|
||||||
- 优先使用推荐的工具组合来提高效率
|
|
||||||
- 可以根据具体情况调整组合中的参数
|
|
||||||
- 避免调用冲突的工具(如同时使用find_answer和need_more_info)`;
|
|
||||||
|
|
||||||
const messages = [
|
|
||||||
{ role: 'system', content: systemPrompt },
|
{ role: 'system', content: systemPrompt },
|
||||||
{ role: 'user', content: '请根据当前思考决定下一步行动。' }
|
{ 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'
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
return allTools.filter(tool =>
|
||||||
// 调用支持工具调用的AI
|
collectionToolNames.includes(tool.function.name)
|
||||||
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) {
|
getResponseTools() {
|
||||||
try {
|
const allTools = toolRegistry.getToolSchemas();
|
||||||
if (decision.tool_calls && decision.tool_calls.length > 0) {
|
const responseToolNames = [
|
||||||
// 处理多个工具调用
|
'send_message',
|
||||||
const toolResults = [];
|
'send_meme',
|
||||||
const toolPromises = [];
|
'render_code',
|
||||||
|
'render_markdown',
|
||||||
|
'generate_image',
|
||||||
|
'store_memory',
|
||||||
|
'poke_user'
|
||||||
|
];
|
||||||
|
|
||||||
logger.info(`[crystelf-ai] 准备并行执行 ${decision.tool_calls.length} 个工具`);
|
return allTools.filter(tool =>
|
||||||
|
responseToolNames.includes(tool.function.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 并行执行所有工具调用
|
/**
|
||||||
for (const toolCall of decision.tool_calls) {
|
* 并行执行工具
|
||||||
|
*/
|
||||||
|
async executeToolsParallel(toolCalls, conversationMessages, context) {
|
||||||
|
const toolPromises = toolCalls.map(async (toolCall) => {
|
||||||
const toolName = toolCall.function.name;
|
const toolName = toolCall.function.name;
|
||||||
const parameters = JSON.parse(toolCall.function.arguments);
|
const parameters = JSON.parse(toolCall.function.arguments);
|
||||||
|
|
||||||
logger.info(`[crystelf-ai] 调用工具: ${toolName}, 参数: ${JSON.stringify(parameters)}`);
|
logger.info(`[crystelf-ai] 执行工具: ${toolName}(${JSON.stringify(parameters)})`);
|
||||||
|
|
||||||
const toolPromise = toolRegistry.executeTool(toolName, parameters, context)
|
try {
|
||||||
.then(result => ({
|
const result = await toolRegistry.executeTool(toolName, parameters, context);
|
||||||
toolName,
|
|
||||||
parameters,
|
|
||||||
result,
|
|
||||||
toolCallId: toolCall.id
|
|
||||||
}))
|
|
||||||
.catch(error => ({
|
|
||||||
toolName,
|
|
||||||
parameters,
|
|
||||||
result: {
|
|
||||||
success: false,
|
|
||||||
message: `工具执行失败: ${error.message}`
|
|
||||||
},
|
|
||||||
toolCallId: toolCall.id,
|
|
||||||
error: error.message
|
|
||||||
}));
|
|
||||||
|
|
||||||
toolPromises.push(toolPromise);
|
// 将工具结果添加到对话历史
|
||||||
}
|
conversationMessages.push({
|
||||||
|
role: 'tool',
|
||||||
// 等待所有工具执行完成
|
tool_call_id: toolCall.id,
|
||||||
const results = await Promise.all(toolPromises);
|
content: JSON.stringify({
|
||||||
|
success: result.success,
|
||||||
// 整合结果
|
message: result.message || result.result,
|
||||||
let shouldEnd = null;
|
data: result
|
||||||
let hasError = false;
|
})
|
||||||
const messages = [];
|
|
||||||
|
|
||||||
for (const { toolName, parameters, result, toolCallId, error } of results) {
|
|
||||||
toolResults.push({
|
|
||||||
toolName,
|
|
||||||
parameters,
|
|
||||||
result,
|
|
||||||
toolCallId,
|
|
||||||
success: !error
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
|
||||||
hasError = true;
|
|
||||||
messages.push(`工具 ${toolName} 执行失败: ${error}`);
|
|
||||||
} else {
|
|
||||||
messages.push(`工具 ${toolName} 执行成功: ${result.message || result.result || '完成'}`);
|
|
||||||
|
|
||||||
// 检查循环控制
|
|
||||||
if (result.shouldEnd !== undefined) {
|
|
||||||
shouldEnd = result.shouldEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: !hasError,
|
toolName,
|
||||||
message: messages.join('\n'),
|
parameters,
|
||||||
toolResults,
|
result,
|
||||||
shouldEnd,
|
success: result.success !== false,
|
||||||
multipleTools: true,
|
toolCallId: toolCall.id
|
||||||
toolCount: decision.tool_calls.length
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} else if (decision.action) {
|
|
||||||
// 处理单个直接行动
|
|
||||||
const result = await toolRegistry.executeTool(decision.action, decision.parameters, context);
|
|
||||||
return {
|
|
||||||
...result,
|
|
||||||
multipleTools: false,
|
|
||||||
toolCount: 1
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: '未识别的决策格式',
|
|
||||||
multipleTools: false,
|
|
||||||
toolCount: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[crystelf-ai] 行动阶段失败: ${error.message}`);
|
logger.error(`[crystelf-ai] 工具执行失败: ${toolName} - ${error.message}`);
|
||||||
return {
|
|
||||||
|
conversationMessages.push({
|
||||||
|
role: 'tool',
|
||||||
|
tool_call_id: toolCall.id,
|
||||||
|
content: JSON.stringify({
|
||||||
success: false,
|
success: false,
|
||||||
message: `执行行动失败: ${error.message}`,
|
error: error.message
|
||||||
multipleTools: false,
|
})
|
||||||
toolCount: 0
|
});
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用AI(文本模式)
|
* 最终评估(当达到最大迭代次数时)
|
||||||
*/
|
*/
|
||||||
async callAI(prompt, context) {
|
async performFinalEvaluation(conversationMessages, collectionTools) {
|
||||||
// 创建临时的事件对象用于AI调用
|
logger.info(`[crystelf-ai] 执行最终评估`);
|
||||||
const tempE = {
|
|
||||||
user_id: 'react_system',
|
// 添加最终评估提示
|
||||||
bot: { uin: 'system' },
|
conversationMessages.push({
|
||||||
group_id: 'system'
|
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 || '基于收集的信息可以回答'
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await AiCaller.callTextAi(prompt, [], [], tempE);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
return result.response;
|
|
||||||
} else {
|
|
||||||
throw new Error(result.error || 'AI调用失败');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 };
|
|
||||||
87
lib/ai/tools/findAnswerTool.js
Normal file
87
lib/ai/tools/findAnswerTool.js
Normal 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 };
|
||||||
@ -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();
|
|
||||||
@ -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} 个工具`);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user