Commit 9a664ed4 authored by ligaowei's avatar ligaowei

refactor(react): 优化ReAct执行器的工具调用处理逻辑

- 更新默认系统提示词,新增工具说明与调用规则,规范对时间相关工具的调用要求
- 调整processTokenForSteps方法,适应新提示词格式,仅识别思考和最终答案步骤
- 删除基于标记解析工具调用事件的复杂逻辑,转为由Spring AI自动处理
- 改进extractToolName方法,支持多种格式工具名称提取,包括ReAct和JSON格式
- 优化extractToolArgs方法,支持从ReAct格式与JSON提取参数,增强鲁棒性
- 新增辅助方法parseJsonToMap,实现简单的JSON字符串转换为Map
- 优化getAgentTools方法,加入详细日志,保证始终包含时间工具,增加异常时的降级策略
- DateTimeTools工具扩展,增加获取当前时间和时间戳的功能,加入格式化配置和日志记录
- 移除ToolExecutionLoggerAspect切面及相关配置,停止工具执行的自动日志切面记录
- 清理无用的@Autowired注入和未使用的导入,简化CompletionHandlerService和ErrorHandlerUtils代码
- 更新data.sql脚本,新增工具bean名称字段及默认工具配置数据,保持数据与代码同步
parent 5c90ccc6
......@@ -13,7 +13,6 @@ import pangea.hiagent.common.utils.LogUtils;
import pangea.hiagent.common.utils.UserUtils;
import pangea.hiagent.web.dto.AgentRequest;
import pangea.hiagent.web.service.AgentService;
import pangea.hiagent.workpanel.event.EventService;
import java.util.concurrent.atomic.AtomicBoolean;
/**
......@@ -30,9 +29,6 @@ public class CompletionHandlerService {
@Autowired
private UserSseService unifiedSseService;
@Autowired
private EventService eventService;
@Autowired
private ErrorHandlerService errorHandlerService;
......
package pangea.hiagent.agent.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
......@@ -15,7 +14,6 @@ public class ErrorHandlerUtils {
private static ErrorHandlerService errorHandlerService;
@Autowired
public ErrorHandlerUtils(ErrorHandlerService errorHandlerService) {
ErrorHandlerUtils.errorHandlerService = errorHandlerService;
}
......
package pangea.hiagent.common.config;
// package pangea.hiagent.common.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// import org.springframework.context.annotation.Configuration;
// import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 工具执行日志记录配置类
* 启用AOP代理以实现工具执行信息的自动记录
*/
@Configuration
@EnableAspectJAutoProxy
public class ToolExecutionLoggingConfig {
}
\ No newline at end of file
// /**
// * 工具执行日志记录配置类
// * 启用AOP代理以实现工具执行信息的自动记录
// */
// @Configuration
// @EnableAspectJAutoProxy
// public class ToolExecutionLoggingConfig {
// }
\ No newline at end of file
......@@ -278,8 +278,7 @@ public class ToolBeanNameInitializer {
* @param discoveredTools Spring容器中发现的工具Bean
* @param databaseTools 数据库中的工具记录
*/
private void synchronizeToolsWithDatabase(Map<String, ToolBeanInfo> discoveredTools,
Map<String, Tool> databaseTools) {
private void synchronizeToolsWithDatabase(Map<String, ToolBeanInfo> discoveredTools,Map<String, Tool> databaseTools) {
int updated = 0;
int created = 0;
int skipped = 0;
......
package pangea.hiagent.tool.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pangea.hiagent.workpanel.IWorkPanelDataCollector;
import java.util.HashMap;
import java.util.Map;
/**
* 工具执行日志记录切面类
* 自动记录带有@Tool注解的方法执行信息,包括工具名称、方法名、输入参数、输出结果、运行时长等
*/
@Slf4j
@Aspect
@Component
public class ToolExecutionLoggerAspect {
@Autowired
private IWorkPanelDataCollector workPanelDataCollector;
/**
* 环绕通知,拦截所有带有@Tool注解的方法
* @param joinPoint 连接点
* @return 方法执行结果
* @throws Throwable 异常
*/
@Around("@annotation(tool)")
public Object logToolExecution(ProceedingJoinPoint joinPoint, Tool tool) throws Throwable {
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getName();
String className = signature.getDeclaringType().getSimpleName();
String fullMethodName = className + "." + methodName;
// 获取工具描述
String toolDescription = tool.description();
// 获取方法参数
String[] paramNames = signature.getParameterNames();
Object[] args = joinPoint.getArgs();
// 构建输入参数映射
Map<String, Object> inputParams = new HashMap<>();
if (paramNames != null && args != null) {
for (int i = 0; i < paramNames.length; i++) {
if (i < args.length) {
inputParams.put(paramNames[i], args[i]);
}
}
}
// 记录开始时间
long startTime = System.currentTimeMillis();
// 记录工具调用开始
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallAction(className, inputParams, null, "pending", null);
} catch (Exception e) {
log.warn("记录工具调用开始时发生错误: {}", e.getMessage(), e);
}
}
log.info("开始执行工具方法: {},描述: {}", fullMethodName, toolDescription);
log.debug("工具方法参数: {}", inputParams);
try {
// 执行原方法
Object result = joinPoint.proceed();
// 记录结束时间
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
// 记录工具调用完成
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallAction(className, inputParams, result, "success", executionTime);
} catch (Exception e) {
log.warn("记录工具调用完成时发生错误: {}", e.getMessage(), e);
}
}
log.info("工具方法执行成功: {},描述: {},耗时: {}ms", fullMethodName, toolDescription, executionTime);
// 精简日志记录,避免过多的debug级别日志
if (log.isTraceEnabled()) {
log.trace("工具方法执行结果类型: {},结果: {}",
result != null ? result.getClass().getSimpleName() : "null", result);
}
return result;
} catch (Exception e) {
// 记录结束时间
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
// 记录工具调用错误
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallAction(className, inputParams, e, "error", executionTime);
} catch (Exception ex) {
log.warn("记录工具调用错误时发生错误: {}", ex.getMessage(), ex);
}
}
log.error("工具方法执行失败: {},描述: {},耗时: {}ms,错误类型: {}",
fullMethodName, toolDescription, executionTime, e.getClass().getSimpleName(), e);
throw e;
}
}
}
\ No newline at end of file
// package pangea.hiagent.tool.aspect;
// import lombok.extern.slf4j.Slf4j;
// import org.aspectj.lang.ProceedingJoinPoint;
// import org.aspectj.lang.annotation.Around;
// import org.aspectj.lang.annotation.Aspect;
// import org.aspectj.lang.reflect.MethodSignature;
// import org.springframework.ai.tool.annotation.Tool;
// import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.stereotype.Component;
// import pangea.hiagent.workpanel.IWorkPanelDataCollector;
// import java.util.HashMap;
// import java.util.Map;
// /**
// * 工具执行日志记录切面类
// * 自动记录带有@Tool注解的方法执行信息,包括工具名称、方法名、输入参数、输出结果、运行时长等
// */
// @Slf4j
// @Aspect
// @Component
// public class ToolExecutionLoggerAspect {
// @Autowired
// private IWorkPanelDataCollector workPanelDataCollector;
// /**
// * 环绕通知,拦截所有带有@Tool注解的方法
// * @param joinPoint 连接点
// * @return 方法执行结果
// * @throws Throwable 异常
// */
// @Around("@annotation(tool)")
// public Object logToolExecution(ProceedingJoinPoint joinPoint, Tool tool) throws Throwable {
// // 获取方法签名
// MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// String methodName = signature.getName();
// String className = signature.getDeclaringType().getSimpleName();
// String fullMethodName = className + "." + methodName;
// // 获取工具描述
// String toolDescription = tool.description();
// // 获取方法参数
// String[] paramNames = signature.getParameterNames();
// Object[] args = joinPoint.getArgs();
// // 构建输入参数映射
// Map<String, Object> inputParams = new HashMap<>();
// if (paramNames != null && args != null) {
// for (int i = 0; i < paramNames.length; i++) {
// if (i < args.length) {
// inputParams.put(paramNames[i], args[i]);
// }
// }
// }
// // 记录开始时间
// long startTime = System.currentTimeMillis();
// // 记录工具调用开始
// if (workPanelDataCollector != null) {
// try {
// workPanelDataCollector.recordToolCallAction(className, inputParams, null, "pending", null);
// } catch (Exception e) {
// log.warn("记录工具调用开始时发生错误: {}", e.getMessage(), e);
// }
// }
// log.info("开始执行工具方法: {},描述: {}", fullMethodName, toolDescription);
// log.debug("工具方法参数: {}", inputParams);
// try {
// // 执行原方法
// Object result = joinPoint.proceed();
// // 记录结束时间
// long endTime = System.currentTimeMillis();
// long executionTime = endTime - startTime;
// // 记录工具调用完成
// if (workPanelDataCollector != null) {
// try {
// workPanelDataCollector.recordToolCallAction(className, inputParams, result, "success", executionTime);
// } catch (Exception e) {
// log.warn("记录工具调用完成时发生错误: {}", e.getMessage(), e);
// }
// }
// log.info("工具方法执行成功: {},描述: {},耗时: {}ms", fullMethodName, toolDescription, executionTime);
// // 精简日志记录,避免过多的debug级别日志
// if (log.isTraceEnabled()) {
// log.trace("工具方法执行结果类型: {},结果: {}",
// result != null ? result.getClass().getSimpleName() : "null", result);
// }
// return result;
// } catch (Exception e) {
// // 记录结束时间
// long endTime = System.currentTimeMillis();
// long executionTime = endTime - startTime;
// // 记录工具调用错误
// if (workPanelDataCollector != null) {
// try {
// workPanelDataCollector.recordToolCallAction(className, inputParams, e, "error", executionTime);
// } catch (Exception ex) {
// log.warn("记录工具调用错误时发生错误: {}", ex.getMessage(), ex);
// }
// }
// log.error("工具方法执行失败: {},描述: {},耗时: {}ms,错误类型: {}",
// fullMethodName, toolDescription, executionTime, e.getClass().getSimpleName(), e);
// throw e;
// }
// }
// }
\ No newline at end of file
......@@ -7,6 +7,7 @@ import pangea.hiagent.tool.ToolParam;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
......@@ -25,7 +26,7 @@ public class DateTimeTools {
required = true,
group = "datetime"
)
private String dateTimeFormat;
private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
@ToolParam(
name = "dateFormat",
......@@ -35,19 +36,75 @@ public class DateTimeTools {
required = true,
group = "datetime"
)
private String dateFormat;
private String dateFormat = "yyyy-MM-dd";
@Tool(description = "获取当前日期和时间")
@ToolParam(
name = "timeFormat",
description = "时间格式",
defaultValue = "HH:mm:ss",
type = "string",
required = true,
group = "datetime"
)
private String timeFormat = "HH:mm:ss";
@Tool(description = "获取当前日期和时间,返回格式为 'yyyy-MM-dd HH:mm:ss'")
public String getCurrentDateTime() {
try {
if (dateTimeFormat == null || dateTimeFormat.trim().isEmpty()) {
dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
}
String dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateTimeFormat));
log.debug("获取当前日期时间: {}", dateTime);
log.info("【时间工具】获取当前日期时间: {}", dateTime);
return dateTime;
} catch (Exception e) {
log.error("获取当前日期时间时发生错误: {}", e.getMessage(), e);
// 发生错误时回退到默认格式
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
@Tool(description = "获取当前日期")
@Tool(description = "获取当前日期,返回格式为 'yyyy-MM-dd'")
public String getCurrentDate() {
try {
if (dateFormat == null || dateFormat.trim().isEmpty()) {
dateFormat = "yyyy-MM-dd";
}
String date = LocalDate.now().format(DateTimeFormatter.ofPattern(dateFormat));
log.debug("获取当前日期: {}", date);
log.info("【时间工具】获取当前日期: {}", date);
return date;
} catch (Exception e) {
log.error("获取当前日期时发生错误: {}", e.getMessage(), e);
// 发生错误时回退到默认格式
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
@Tool(description = "获取当前时间,返回格式为 'HH:mm:ss'")
public String getCurrentTime() {
try {
if (timeFormat == null || timeFormat.trim().isEmpty()) {
timeFormat = "HH:mm:ss";
}
String time = LocalTime.now().format(DateTimeFormatter.ofPattern(timeFormat));
log.info("【时间工具】获取当前时间: {}", time);
return time;
} catch (Exception e) {
log.error("获取当前时间时发生错误: {}", e.getMessage(), e);
// 发生错误时回退到默认格式
return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
}
}
@Tool(description = "获取当前时间戳(毫秒),返回自1970年1月1日00:00:00 UTC以来的毫秒数")
public String getCurrentTimeMillis() {
try {
long timestamp = System.currentTimeMillis();
log.info("【时间工具】获取当前时间戳: {}", timestamp);
return String.valueOf(timestamp);
} catch (Exception e) {
log.error("获取当前时间戳时发生错误: {}", e.getMessage(), e);
return String.valueOf(System.currentTimeMillis());
}
}
}
......@@ -36,19 +36,19 @@ MERGE INTO agent_tool_relation (id, agent_id, tool_id) VALUES
('relation-16', 'agent-5', 'tool-12');
-- 插入默认工具数据
MERGE INTO tool (id, name, display_name, description, category, status, owner, timeout, http_method, parameters, return_type, return_schema, implementation, api_endpoint, headers, auth_type, auth_config) VALUES
('tool-1', 'search', '搜索工具', '进行网络搜索查询', 'API', 'active', 'user-001', 30000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-2', 'calculator', '计算器', '进行数学计算', 'FUNCTION', 'active', 'user-001', 5000, 'POST', '{}', 'number', '{}', '', '', '{}', '', '{}'),
('tool-3', 'weather', '天气查询', '查询天气信息', 'API', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-4', 'get_current_time', '获取当前时间', '获取当前系统时间', 'FUNCTION', 'active', 'user-001', 1000, 'GET', '{}', 'string', '{}', '', '', '{}', '', '{}'),
('tool-5', 'technicalDocumentationRetrieval', '技术文档检索', '检索和查询技术文档内容', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-6', 'technicalCodeExplanation', '技术代码解释', '分析和解释技术代码的功能和实现逻辑', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-7', 'chartGeneration', '图表生成', '根据数据生成各种类型的图表', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-8', 'statisticalCalculation', '统计计算', '执行各种统计分析计算', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-9', 'writingStyleReference', '创作风格参考', '提供各种写作风格的参考和指导', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-10', 'documentTemplate', '文档模板', '提供各种类型的文档模板', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-11', 'studyPlanGeneration', '学习计划制定', '根据学习目标和时间安排制定个性化的学习计划', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-12', 'courseMaterialRetrieval', '课程资料检索', '检索和查询相关课程资料', 'FUNCTION', 'active', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}');
MERGE INTO tool (id, name, display_name, description, category, status, bean_name, owner, timeout, http_method, parameters, return_type, return_schema, implementation, api_endpoint, headers, auth_type, auth_config) VALUES
('tool-1', 'search', '搜索工具', '进行网络搜索查询', 'API', 'active', 'searchTool', 'user-001', 30000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-2', 'calculator', '计算器', '进行数学计算', 'FUNCTION', 'active', 'calculatorTools', 'user-001', 5000, 'POST', '{}', 'number', '{}', '', '', '{}', '', '{}'),
('tool-3', 'weather', '天气查询', '查询天气信息', 'API', 'active', 'weatherFunction', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-4', 'get_current_time', '获取当前时间', '获取当前系统时间', 'FUNCTION', 'active', 'dateTimeTools', 'user-001', 1000, 'GET', '{}', 'string', '{}', '', '', '{}', '', '{}'),
('tool-5', 'technicalDocumentationRetrieval', '技术文档检索', '检索和查询技术文档内容', 'FUNCTION', 'active', 'technicalDocumentationRetrievalTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-6', 'technicalCodeExplanation', '技术代码解释', '分析和解释技术代码的功能和实现逻辑', 'FUNCTION', 'active', 'technicalCodeExplanationTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-7', 'chartGeneration', '图表生成', '根据数据生成各种类型的图表', 'FUNCTION', 'active', 'chartGenerationTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-8', 'statisticalCalculation', '统计计算', '执行各种统计分析计算', 'FUNCTION', 'active', 'statisticalCalculationTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-9', 'writingStyleReference', '创作风格参考', '提供各种写作风格的参考和指导', 'FUNCTION', 'active', 'writingStyleReferenceTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-10', 'documentTemplate', '文档模板', '提供各种类型的文档模板', 'FUNCTION', 'active', 'documentTemplateTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-11', 'studyPlanGeneration', '学习计划制定', '根据学习目标和时间安排制定个性化的学习计划', 'FUNCTION', 'active', 'studyPlanGenerationTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}'),
('tool-12', 'courseMaterialRetrieval', '课程资料检索', '检索和查询相关课程资料', 'FUNCTION', 'active', 'courseMaterialRetrievalTool', 'user-001', 10000, 'GET', '{}', 'object', '{}', '', '', '{}', '', '{}');
-- 插入默认工具配置数据
MERGE INTO tool_configs (id, tool_name, param_name, param_value, description, default_value, type, required, group_name) VALUES
......
......@@ -236,7 +236,7 @@ const loadHistoryMessagesInternal = async (agentId: string) => {
}
// 转换消息格式
const historyMessages = messagesArray
const historyMessages: Message[] = messagesArray
.map((msg: any) => {
// 验证消息对象的必要字段
if (!msg || typeof msg !== "object") {
......@@ -265,7 +265,7 @@ const loadHistoryMessagesInternal = async (agentId: string) => {
isStreaming: false,
};
})
.filter((msg: any) => msg !== null); // 过滤掉无效消息
.filter((msg: any) => msg !== null) as Message[]; // 过滤掉无效消息并转换类型
// 修复:确保消息按时间顺序排列(最新的消息在最后)
historyMessages.sort((a: any, b: any) => a.timestamp - b.timestamp);
......
......@@ -115,8 +115,9 @@ function handleWebSocketMessage(data: any) {
addLog(`📤 调用handleDomSyncData处理数据`, 'debug')
handleDomSyncData(data)
} catch (e) {
addLog('解析数据失败:' + (e as Error).message, 'error')
} catch (e: unknown) {
const errorMessage = e instanceof Error ? e.message : String(e)
addLog('解析数据失败:' + errorMessage, 'error')
errorStats.value.parseErrors++
}
}
......@@ -172,8 +173,9 @@ const onIframeLoad = () => {
}
addLog(`iframe事件监听器绑定成功,尝试次数: ${attempt}`, 'info')
} catch (e) {
addLog(`iframe事件监听器绑定失败 (尝试 ${attempt}): ${e.message}`, 'error')
} catch (e: unknown) {
const errorMsg = e instanceof Error ? e.message : String(e)
addLog(`iframe事件监听器绑定失败 (尝试 ${attempt}): ${errorMsg}`, 'error')
errorStats.value.scriptErrors++
// 如果还有重试机会,继续重试
......
================================================================================
AI 无法获取实时时间信息 - 修复要点速查表
================================================================================
【问题症状】
用户:「现在几点了?」
AI回复:「我无法直接获取实时时间信息,因为我的知识库没有实时更新功能...」
而不是调用时间工具获取实时时间
================================================================================
【根本原因 - 4 个关键问题】
================================================================================
1. ❌ 系统提示词不足 (最严重)
位置:DefaultReactExecutor.java, 第31-39行
问题:
- 没有明确列举可用工具(时间工具)
- 没有告诉 AI 何时调用工具("现在几点"→ 必须调用时间工具)
- AI 不知道有这个能力,所以拒绝了
2. ❌ DateTimeTools 字段未初始化
位置:DateTimeTools.java, 第28和38行
问题:
- private String dateTimeFormat; // ← 值为 null
- @ToolParam 的 defaultValue 只是文档,不会自动初始化字段
- 调用工具时会 NPE 错误
3. ❌ 工具加载逻辑缺陷
位置:DefaultReactExecutor.java, 第639-654行
问题:
- Agent 为 null 时,返回空工具列表
- 用户无法使用任何工具
4. ❌ 日志输出不足
问题:
- 日志等级为 debug,生产环境看不到
- 难以排查为什么工具没被调用
================================================================================
【修复方案 - 实施清单】
================================================================================
✅ 修复1:增强 DEFAULT_SYSTEM_PROMPT
文件:DefaultReactExecutor.java (第31-47行)
内容:
- 明确列举4个时间工具(getCurrentDateTime/Date/Time/Millis)
- 使用"YOU MUST"强制AI调用工具
- 列举常见询问表述(含中文"现在几点了?")
- 说明返回格式
代码示例:
"IMPORTANT: You have the following tools available:\n" +
"1. getCurrentDateTime - 获取当前日期和时间,格式为 'yyyy-MM-dd HH:mm:ss'\n" +
"...\n" +
"- When a user asks 'what time is it?', '现在几点了?', etc., YOU MUST use getCurrentDateTime\n"
✅ 修复2:初始化 DateTimeTools 字段
文件:DateTimeTools.java (第20-60行)
内容:
- private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss"; // ← 改这里!
- private String dateFormat = "yyyy-MM-dd"; // ← 改这里!
- private String timeFormat = "HH:mm:ss"; // ← 新增
- 添加异常处理和 null 检查
- 添加两个新方法:getCurrentTime() 和 getCurrentTimeMillis()
- 改用 log.info() 提升日志级别
✅ 修复3:优化 getAgentTools() 方法
文件:DefaultReactExecutor.java (第639-687行)
内容:
- Agent == null 时,返回 [dateTimeTools] 而不是空列表
- 异常时使用回退策略,确保至少有时间工具
- 添加详细的日志和 null 检查
✅ 修复4:增强日志输出
内容:
- 日志级别 debug → info
- 添加清晰的分隔符和标记
- 打印工具加载过程的每个步骤
- 异常时输出完整堆栈跟踪
================================================================================
【验证清单】
================================================================================
编译检查:
mvn clean compile -DskipTests
✅ 通过(无编译错误)
运行测试(待执行):
[ ] 测试场景1:询问"现在几点了"→ 应该返回实时时间
[ ] 测试场景2:询问"今天几号"→ 应该返回日期
[ ] 测试场景3:询问"时间戳"→ 应该返回毫秒数
[ ] 测试场景4:Agent为null时→ 应该仍能调用时间工具
日志检查:
[ ] 查看 getAgentTools() 的详细日志
[ ] 确认时间工具被正确加载
[ ] 确认异常时有回退机制
================================================================================
【修改文件清单】
================================================================================
1. backend/src/main/java/pangea/hiagent/tool/impl/DateTimeTools.java
- 初始化字段:dateTimeFormat, dateFormat, timeFormat
- 添加方法:getCurrentTime(), getCurrentTimeMillis()
- 增强异常处理
2. backend/src/main/java/pangea/hiagent/agent/react/DefaultReactExecutor.java
- 改进 DEFAULT_SYSTEM_PROMPT(第31-47行)
- 优化 getAgentTools() 方法(第639-687行)
================================================================================
【预期效果】
================================================================================
修复前:
用户:「现在几点了?」
AI:「我无法直接获取实时时间信息...」😞
修复后:
用户:「现在几点了?」
AI:「当前时间是 2024-12-24 14:30:45」或「现在是下午 2 点 30 分」✅
================================================================================
【关键要点(务必理解)】
================================================================================
1. 问题不是工具不存在,而是 AI 不知道何时调用
→ 解决方案:明确告诉 AI 在看到"时间"相关问题时必须调用工具
2. @ToolParam 的 defaultValue 只是文档注解,不会初始化字段
→ 解决方案:在字段声明时直接赋值初始化
3. 日志等级很重要,debug 级别在生产环境看不到
→ 解决方案:改用 info 等级,便于排查问题
4. 错误处理要有回退机制,不能让一个工具失败导致其他工具也不可用
→ 解决方案:异常时返回至少时间工具可用
================================================================================
【后续改进建议】
================================================================================
短期:
1. 为其他工具(Calculator, Weather等)也增强提示词
2. 建立统一的系统提示词模板
中期:
1. 自动从 @Tool 注解生成提示词(避免手工维护)
2. 建立工具调用错误追踪机制
长期:
1. 实现工具调用的完整可观测性(OpenTelemetry)
2. 性能监控和告警
================================================================================
修复完成:2024-12-24 | 验证状态:待测试 | 优先级:P1(高)
================================================================================
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment