package com.xly.tool; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.xly.config.OperableChatMemoryProvider; import com.xly.constant.ErrorCode; import com.xly.constant.ProcedureConstant; import com.xly.constant.RuleCode; import com.xly.constant.UrlErpConstant; import com.xly.entity.*; import com.xly.exception.dto.BusinessException; import com.xly.mapper.ParamRuleMapper; import com.xly.mapper.ToolMetaMapper; import com.xly.service.DynamicExeDbService; import com.xly.service.UserSceneSessionService; import com.xly.util.*; import dev.langchain4j.agent.tool.*; import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.data.message.ChatMessageType; import dev.langchain4j.data.message.ToolExecutionResultMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.service.tool.ToolExecutor; import dev.langchain4j.service.tool.ToolProvider; import dev.langchain4j.service.tool.ToolProviderRequest; import dev.langchain4j.service.tool.ToolProviderResult; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.javassist.expr.NewArray; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import java.util.stream.IntStream; @Slf4j @Service @RequiredArgsConstructor public class DynamicToolProvider implements ToolProvider { // private final ToolMetaMapper toolMetaMapper; private final ObjectMapper objectMapper; private final ToolMetaMapper toolMetaMapper; private final ParamRuleMapper paramRuleMapper; private final DynamicExeDbService dynamicExeDbService; private final OperableChatMemoryProvider operableChatMemoryProvider; // 内存缓存:toolName -> ToolSpecificationHolder private final Map toolCache = new ConcurrentHashMap<>(); public final Map sSceneIdMap = new ConcurrentHashMap<>(); private final Map> sceneToolCacheMap = new ConcurrentHashMap<>(); private final List paramRuleDataAll = new ArrayList<>(); @Value("${erp.baseurl}") private String baseUrl; /*** * @Author 钱豹 * @Date 14:05 2026/2/10 * @Param [] * @return void * @Description 移除所有动态方法缓存 **/ public void cleanAllToolProvider() { toolCache.clear(); sceneToolCacheMap.clear(); paramRuleDataAll.clear(); } /** * 初始化时加载所有启用的工具到缓存 */ @jakarta.annotation.PostConstruct public void init() { // List metas = toolMetaMapper.findAll(); for (ToolMeta meta : metas) { try { //补全业务类型查询类显示的字段 doSetToolAIshowfieldShow(meta); ToolSpecification spec = buildToolSpecification(meta); ToolExecutor executor = createToolExecutor(meta); toolCache.put(meta.getSMethodNo(), new ToolSpecificationHolder(spec, executor)); log.info("已加载动态工具:{}", meta.getSMethodNo()); String sceneId = meta.getSSceneId(); List dataList = new ArrayList<>(); if(ObjectUtil.isNotEmpty(sceneToolCacheMap.get(sceneId))) { dataList = sceneToolCacheMap.get(sceneId); } dataList.add(new ToolSpecificationHolder(spec, executor)); sceneToolCacheMap.put(sceneId, dataList); } catch (Exception e) { e.printStackTrace(); log.error("构建工具失败,sMethodNo={}", meta.getSMethodNo(), e); } } } //补全业务类型查询类显示的字段 //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"窗体查询","5":"按钮执行","6":"其它"} private void doSetToolAIshowfieldShow(ToolMeta meta){ String sToolId = meta.getSId(); List paramRuleData = getParamRuleDataAll(); List paramRuleListAll = paramRuleData.stream().filter(one-> sToolId.equals(one.getSParentId())).collect(Collectors.toUnmodifiableList()); List paramRuleList = paramRuleListAll.stream().filter(one-> one.getBTipModel()).collect(Collectors.toUnmodifiableList()); List paramRuleListCheck = paramRuleListAll.stream().filter(one->one.getBEmpty()).collect(Collectors.toUnmodifiableList()); paramRuleList = ObjectUtil.isNotEmpty(paramRuleList)?paramRuleList:new ArrayList<>(); if((meta.getIBizType()==4 || meta.getIBizType()==5) && ObjectUtil.isNotEmpty(meta.getSAIshowfield()) && ObjectUtil.isNotEmpty(meta.getSSrcFormId()) ){ String[] sAIshowfield = meta.getSAIshowfield().split(","); List mutableList = Arrays.asList(sAIshowfield); List sAIshowfieldArry = new ArrayList<>(mutableList); sAIshowfieldArry.add("sSlaveId"); String sSrcFormId = meta.getSSrcFormId(); //获取对应的窗体配置 StringBuffer sSql =new StringBuffer().append("SELECT 10000 AS iOrderShow,A.sChinese AS label,A.sName,A.sControlName,B.sId AS sFormcustomId,A.sName AS sId FROM gdsconfigformslave AS A ") .append("JOIN gdsconfigformmaster AS B ON A.sParentId = B.sId ") .append("WHERE B.sParentId = #{sSrcFormId} AND A.sName <> '' AND INSTR(A.sControlName,'Btn')=0 AND (A.bVisible = 1 OR A.sName =#{sName}) "); Map searMap = new HashMap<>(); searMap.put("sSrcFormId",sSrcFormId); searMap.put("sName","sSlaveId"); List> sAIshowfieldShowAll = dynamicExeDbService.findSql(searMap,sSql.toString()); if(ObjectUtil.isNotEmpty(sAIshowfieldShowAll)){ sAIshowfieldShowAll = sAIshowfieldShowAll.stream().filter(m-> sAIshowfieldArry.contains(m.get("sName").toString())).collect(Collectors.toUnmodifiableList()); sAIshowfieldShowAll.forEach(one->{ one.put("iOrderShow",sAIshowfieldArry.indexOf(one.get("sName").toString())+1); }); List> sAIshowfieldShowData = new ArrayList<>(); sAIshowfieldShowData.addAll(sAIshowfieldShowAll); sAIshowfieldShowData.sort(Comparator.comparing(h -> Integer.valueOf(h.get("iOrderShow").toString()))); meta.setSAIshowfieldShow(sAIshowfieldShowData); } List paramRuleListNew = new ArrayList<>(paramRuleList); for(int i=0;i sAIshowfieldShow = sAIshowfieldShowAll.get(i); List one = paramRuleList.stream().filter(m->m.getSParamValue().equals(sAIshowfieldShow.get("sName"))).collect(Collectors.toUnmodifiableList()); if(ObjectUtil.isEmpty(one)){ ParamRule pr = new ParamRule(); pr.setIOrder(i+1); pr.setSParamValue(sAIshowfieldShowAll.get(i).get("sName").toString()); pr.setSParam(sAIshowfieldShowAll.get(i).get("label").toString()); pr.setBEmpty(false); pr.setSType("array"); if("sSlaveId".equals(sAIshowfieldShowAll.get(i).get("sName"))){ pr.setSParam("sSlaveId"); } pr.setBTipModel(true); paramRuleListNew.add(pr); } } String sendUrl = baseUrl + UrlErpConstant.getBusinessDataByFormcustomId; sendUrl = StrUtil.format(sendUrl,sAIshowfieldShowAll.get(0).get("sFormcustomId"),sSrcFormId); meta.setSendUrl(sendUrl); paramRuleList = paramRuleListNew; paramRuleListCheck = paramRuleListNew; paramRuleListAll = paramRuleListNew; } List paramRuleListNew = new ArrayList<>(paramRuleList); meta.setParamRuleList(paramRuleListNew); meta.setParamRuleListCheck(paramRuleListCheck); meta.setParamRuleListAll(paramRuleListAll); } @Override public ToolProviderResult provideTools(ToolProviderRequest request) { // List specs = new ArrayList<>(); String sUserId = request.chatMemoryId().toString(); Map executors = new HashMap<>(); // sceneToolCacheMap.get(sSceneIdMap.get(sUserId)); //获取Session UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(sUserId); //过滤对应的权限方法 List datalist = new ArrayList<>(); List toolMetaAll = session.getAuthTool(); if(ObjectUtil.isNotEmpty(toolMetaAll)){ toolMetaAll = toolMetaAll.stream().filter(to-> to.getSSceneId().equals(sSceneIdMap.get(sUserId))).collect(Collectors.toUnmodifiableList()); if(ObjectUtil.isNotEmpty(toolMetaAll)){ toolMetaAll.forEach(to->{ datalist.add(toolCache.get(to.getSMethodNo())); }); } } datalist.forEach(holder->{ // specs.add(holder.getToolSpecification()); executors.put(holder.getToolSpecification(),holder.getToolExecutor()); // executors.put(holder.getToolSpecification(), holder.getToolExecutor()); }); return ToolProviderResult.builder().addAll(executors).build(); } /*** * @Author 钱豹 * @Date 15:07 2026/1/30 * @Param [meta] * @return dev.langchain4j.agent.tool.ToolSpecification * @Description 参数注入 方法引导语注入(初始化调用) **/ private ToolSpecification buildToolSpecification(ToolMeta meta) { ToolSpecification.Builder builder = ToolSpecification.builder() .name(meta.getSMethodNo()); // .description(meta.getStoolDesc()); StringBuffer stoolDesc = new StringBuffer(); StringBuffer sbt = new StringBuffer(); StringBuffer xt = new StringBuffer(); StringBuffer sl = new StringBuffer(); if(ObjectUtil.isNotEmpty(meta.getStoolDesc())){ stoolDesc.append("MethodNo:").append(meta.getSMethodNo()).append(",当用户").append(meta.getSMethodName()); // if (meta.getIBizType()==4){ // stoolDesc.append(",").append("并选择数据后执行["+meta.getSControlName()+"]操作"); // } stoolDesc.append("时,必须调用本工具").append(meta.getSMethodNo()).append(",").append(meta.getStoolDesc()); if (meta.getIBizType()==4){ stoolDesc.append(",").append("并选择数据后执行 "+meta.getSControlName()+" 操作"); // .append("1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"1,3行确认""); } } try { List paramRuleData = meta.getParamRuleList(); // 1.必填参数:客户名称(字符串),产品名称(字符串),数量(数字); // 2.选填参数:产品描述(字符串),生产要求(字符串); // **强制输出标准JSON对象**: // 示例:{\"客户名称\":\"小羚羊软件开发有限公司\",\"产品名称\":\"企业宣传册\",\"数量\":1000,\"产品描述\":\"黑色注意色差\",\"生产要求\":\"上光,覆膜\"} Map slMap = new HashMap<>(); for (ParamRule paramRule : paramRuleData) { // String paramName = ObjectUtil.isEmpty(paramRule.getSParamValue())?null:paramRule.getSParamValue(); String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam())?null:paramRule.getSParam(); String paramType = paramRule.getSType(); Boolean bEmpty = paramRule.getBEmpty(); String sExampleValue = paramRule.getSExampleValue(); //示例值,只有枚举放 if(ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())){ //英文 // slMap.put(paramName,sExampleValue); //中文 slMap.put(paramDesc,sExampleValue); } if (paramDesc == null || paramDesc.trim().isEmpty()) { continue; } // 构建参数属性 //{"string":"字符","integer":"数字","double":"浮点","boolean":"布尔型","array":"数组","enum":"枚举"} List properties = new ArrayList<>(); // 添加类型属性 // String ,仅允许【{}】多选一,严格匹配) String sRuleCost = getConstMeg(paramRule.getSParamConfig(),paramRule); // 2. 付款方式:字符串类型,互斥枚举值[90天、60天、现结],默认值[现结] // 5. 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值 switch (paramType.toLowerCase()) { case "string": if(bEmpty){ sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、"); }else{ xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、"); } properties.add(JsonSchemaProperty.STRING); break; case "integer": case "int": if(bEmpty){ sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、"); }else{ xt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、"); } properties.add(JsonSchemaProperty.INTEGER); break; case "number": case "double": case "float": properties.add(JsonSchemaProperty.NUMBER); if(bEmpty){ sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、"); }else{ xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、"); } break; case "boolean": case "bool": if(bEmpty){ sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、"); }else{ xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、"); } properties.add(JsonSchemaProperty.BOOLEAN); break; case "array": String sRuleArray = getArrrayBySql(paramRule); // 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值 if (ObjectUtil.isNotEmpty(sRuleArray)) { sRuleArray = StrUtil.replace(sRuleArray,",","/"); properties.add(JsonSchemaProperty.enums(sRuleArray.split("/"))); } if(bEmpty){ //动态SQL 或者写死默认值的 动态SQL只存在一条数据 直接给默认值 sbt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]"); if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)){ String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)?sRuleArray:paramRule.getSDefaultValue(); sbt.append(",默认值[").append(sDefaultVal).append("]"); }else{ sbt.append(",无默认值"); } sbt.append(")、"); }else{ xt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]"); if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); }else{ xt.append(",无默认值"); } xt.append(")、"); } properties.add(JsonSchemaProperty.ARRAY); // 默认字符串数组 properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); break; case "enum": // 处理枚举值 if (ObjectUtil.isNotEmpty(sRuleCost)) { properties.add(JsonSchemaProperty.enums(sRuleCost.split("/"))); }else{ sRuleCost = getArrrayBySql(paramRule); } // eg: 付款方式(字符串,互斥枚举值[90天、60天、现结],默认值[现结]) if(bEmpty){ sbt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]"); if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ sbt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); }else{ sbt.append(",无默认值"); } sbt.append(")、"); }else{ xt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]"); if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); }else{ xt.append(",无默认值"); } xt.append(")、"); } properties.add(JsonSchemaProperty.ARRAY); // 默认字符串数组 properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); break; default: properties.add(JsonSchemaProperty.STRING); break; } // 添加描述 if (!paramDesc.isEmpty()) { properties.add(JsonSchemaProperty.description(paramDesc)); } // 检查是否必填 boolean required = bEmpty; // 添加参数 if (required) { builder.addParameter(paramDesc, properties); } else { builder.addOptionalParameter(paramDesc, properties); } } if(ObjectUtil.isNotEmpty(sbt)){ stoolDesc .append(System.lineSeparator()) .append("1.必填参数:") .append(sbt); } if(ObjectUtil.isNotEmpty(xt)){ stoolDesc .append(System.lineSeparator()) .append("2.选填参数:") .append(xt); } if(ObjectUtil.isNotEmpty(slMap)){ stoolDesc .append(System.lineSeparator()) .append(sl) .append("**强制输出标准JSON对象**:").append(System.lineSeparator()) .append("示例:").append(JSONUtil.toJsonStr(slMap)); } log.info("方法描述========================{}",stoolDesc); } catch (Exception e) { e.printStackTrace(); log.error("Failed to parse parameter rules: {}", meta.getSMethodName(), e); // 参数解析失败时,创建无参数的工具规格 } builder.description(stoolDesc.toString()); return builder.build(); } /*** * @Author 钱豹 * @Date 23:42 2026/2/3 * @Param [sConstConfig, sRule] * @return java.lang.String * @Description 常量类型枚举 **/ private String getConstMeg(String sConstConfig,ParamRule paramRule){ if(!RuleCode.CONST.getCode().equals(paramRule.getSRule())){ return StrUtil.EMPTY; } if(StrUtil.isNotEmpty(sConstConfig)){ Map constMap = JSONUtil.parseObj(sConstConfig); StringBuffer sb = new StringBuffer(); constMap.forEach((k,v)->{ sb.append(v).append("/"); }); sb.delete(sb.length()-1,sb.length()); paramRule.setSRuleTs(sb.toString()); return sb.toString(); } return StrUtil.EMPTY; } //数组类型枚举 private String getArrrayBySql(ParamRule paramRule){ Boolean bCheckArray = !(RuleCode.SQL.getCode().equals(paramRule.getSRule())); // {"string":"字符","integer":"数字","double":"浮点","boolean":"布尔型","array":"数组","enum":"枚举"} Boolean bCheckArray2= !("array".equals(paramRule.getSType()) || "enum".equals(paramRule.getSType())); Boolean bCheckArray3= ObjectUtil.isEmpty(paramRule.getSParamConfig()); if(bCheckArray || bCheckArray2 || bCheckArray3){ return StrUtil.EMPTY; } List> data = dynamicExeDbService.findSql(new HashMap<>(),paramRule.getSParamConfig()); StringBuffer sb = new StringBuffer(); if(ObjectUtil.isEmpty(data)){ return StrUtil.EMPTY; } if(data.get(0).containsKey("sGroupName")){ Map>> groupData = data.stream() .collect(Collectors.groupingBy(map -> map.get("sGroupName"))); // 打印结果 Integer sUpKeySize =groupData.size(); groupData.forEach((key, value) -> { // System.out.println("key: " + key); value.forEach(one->{ sb.append(one.get(paramRule.getSParamValue())).append("/"); }); if(sUpKeySize>1 && ObjectUtil.isNotEmpty(sb)){ sb.delete(sb.length()-1,sb.length()); sb.append(","); } }); }else{ data.forEach(one->{ if(ObjectUtil.isNotEmpty(one.get(paramRule.getSParamValue()))){ sb.append(one.get(paramRule.getSParamValue())).append("/"); } }); } if(ObjectUtil.isNotEmpty(sb)){ sb.delete(sb.length()-1,sb.length()); paramRule.setSRuleTs(sb.toString()); } return sb.toString(); } /*** * @Author 钱豹 * @Date 15:08 2026/1/30 * @Param [] * @return java.util.List * @Description 获取所有参数 **/ private List getParamRuleDataAll(){ if(paramRuleDataAll==null || paramRuleDataAll.size()==0){ paramRuleDataAll.addAll(paramRuleMapper.findAll()); } return paramRuleDataAll; } /*** * @Author 钱豹 * @Date 15:09 2026/1/30 * @Param [meta] * @return dev.langchain4j.service.tool.ToolExecutor * @Description 创建 ToolExecutor,内部包含参数自动补全与校验逻辑(创建执行器) **/ private ToolExecutor createToolExecutor(ToolMeta meta) { return (toolExecutionRequest, memoryId) -> { UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(memoryId.toString()); session.setCurrentTool(meta); // 标记一下找到了相应方法 session.setSFunPrompts(null); // 检查条件 - 如果条件满足,直接返回成功结果,不再执行后续逻辑 if (ObjectUtil.isNotEmpty(meta.getSInputTabelName()) && ObjectUtil.isNotEmpty(meta.getSStructureMemo())) { // 直接返回成功结果,阻止后续执行 return createEarlySuccessResult(toolExecutionRequest, "执行成功,终止后续执行"); } // 1. 解析模型传入的参数 Map args; try { args = objectMapper.readValue(toolExecutionRequest.arguments(), new TypeReference<>() {}); } catch (Exception e) { String errorMsg = "参数 JSON 解析失败,请检查参数格式是否正确。" + "错误详情:" + e.getMessage(); log.warn("参数解析失败,tool={}, args={}", meta.getSMethodNo(), toolExecutionRequest.arguments(), e); return String.valueOf(errorResult(toolExecutionRequest, errorMsg)); } // Map argsOld = DeepCopyUtils.deepCopy(args); List paramRuleData = meta.getParamRuleListAll(); // 2. 【自动补全】应用参数的默认值 args = applyDefaultValues(args, paramRuleData); // 2.1 【补全动态参数】动态参数补全 try{ args = applyValues(args, meta.getParamRuleListCheck()); }catch (Exception e){ log.error("返回信息",e); String sTsMsg = e.getMessage(); session.setSFunPrompts(sTsMsg); //存在多个数据返回大模型,需要继续盘问选择出唯一结果 return String.valueOf(askUserResult(toolExecutionRequest, sTsMsg)); } // 3. 【自动校验】检查必填项 List missing = checkRequiredParams(args, paramRuleData); if (!missing.isEmpty()) { // 4.1 参数缺失,生成“提问”消息,直接返给客户 String askMsg = buildAskUserMessage(meta, missing,args); session.setSFunPrompts(askMsg); return String.valueOf(askUserResult(toolExecutionRequest, askMsg)); } // 6. 【最终确认信息】所有检测通过后,需要和客户确认交互 List chatMessage = operableChatMemoryProvider.getCurrentChatMessages(memoryId.toString()); ChatMessage userMessage = getLasterUserMssage(chatMessage); String input = StrUtil.replace(userMessage.text(),"用户输入:",StrUtil.EMPTY); // {"0":"查询","1":"执行"} 查询不需要确认 Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认"); //判断是否生成数据 List> sRowData = new ArrayList<>(); String sHandleType = "merge"; if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){ Map sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData()); sRowData = (List>) sRowDataMap.get("sRowData"); sHandleType = sRowDataMap.get("sHandleType").toString(); } if((isConfirmed || 0== meta.getIActionType()) && 5!= meta.getIBizType()){ // 确认后必填项校验 List missingAfter = checkConfirmAfterParam(args, paramRuleData); if (!missingAfter.isEmpty()) { // 4.1 参数缺失,生成“提问”消息,直接返给客户 String askMsg = buildAskUserMessage(meta, missingAfter,args); session.setSFunPrompts(askMsg); return String.valueOf(askUserResult(toolExecutionRequest, askMsg)); } // 7. 【业务校验】执行业务层面的逻辑校验 + 所有校验通过,执行核心业务逻辑 return executeTool(toolExecutionRequest, meta, args, paramRuleData, memoryId.toString(), session); } String askconfirmMsg =StrUtil.EMPTY; if(0== meta.getIActionType() && 4!= meta.getIBizType() && 5!= meta.getIBizType()){ askconfirmMsg = buildConfirmUserMessage(meta, args); }else if(4== meta.getIBizType() || meta.getIBizType()==5){ askconfirmMsg = doGetFromData( meta,args,session); // session.setSFunPrompts(askconfirmMsg); // operableChatMemoryProvider.get(memoryId).add(UserMessage.from("SYSTEM: 等待用户确认或选择部分数据操作")); return executeWithConfirmation(toolExecutionRequest,askconfirmMsg,operableChatMemoryProvider.get(memoryId), session, meta).text(); }else{ askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta); } // 返回需要确认的结果 return executeWithConfirmation(toolExecutionRequest,askconfirmMsg,operableChatMemoryProvider.get(memoryId), session, meta).text(); }; } /*** * @Author 钱豹 * @Date 15:16 2026/2/9 * @Param [argMap] * @return java.lang.String * @Description MAP转提示 **/ private String getDefMessage(Map argMap,String sName,ToolMeta meta){ List showList = meta.getParamRuleListAll(); List showListData = new ArrayList<>(); showList = showList.stream().filter(one->one.getBTipModel()).collect(Collectors.toUnmodifiableList()); showListData.addAll(showList); showListData.sort(Comparator.comparing(h -> h.getIOrder())); StringBuilder markdown = new StringBuilder().append("\n"); markdown.append("\n---\n"); showListData.forEach(one->{ if(argMap.containsKey(one.getSParam())){ String valueStr = argMap.get(one.getSParam()) != null ? argMap.get(one.getSParam()).toString() : ""; markdown.append("- ") .append(one.getSParam()) .append(": `") .append(valueStr) .append("`\n"); } }); markdown.append("\n---\n"); appendConfirm(markdown,sName); return markdown.toString(); } /*** * @Author 钱豹 * @Date 14:56 2026/2/9 * @Param [markdown] * @return void * @Description 全部确认 **/ private void appendConfirmAll(StringBuilder markdown,String sName){ sName = ObjectUtil.isEmpty(sName)?StrUtil.EMPTY:"["+sName+"]"; markdown.append("请确认是否执行").append(sName).append("操作?1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"1,3行确认"\n"); //全部确认 ,部分确认,取消 markdown.append("回复:  ").append("**全部确认**").append(" ") .append("**合并确认**").append(" ") .append("**取消**"); } /*** * @Author 钱豹 * @Date 14:56 2026/2/9 * @Param [markdown] * @return void * @Description 单条确认 **/ private void appendConfirm(StringBuilder markdown,String sName){ sName = ObjectUtil.isEmpty(sName)?StrUtil.EMPTY:"["+sName+"]"; markdown.append("请确认是否执行").append(sName).append("操作?请直接回复"确认"或"取消"\n"); //全部确认 ,部分确认,取消 markdown.append("回复:  ").append("**确认**").append(" ") .append("**取消**"); } private ChatMessage getLasterUserMssage(List chatMessage){ if(chatMessage!=null){ for(int i=chatMessage.size()-1;i>0;i--){ ChatMessage data = chatMessage.get(i); ChatMessageType sType = data.type(); if(ChatMessageType.USER.equals(sType)){ return chatMessage.get(i); } } } return null; } /*** * @Author 钱豹 * @Date 13:35 2026/1/31 * @Param [args, paramDefs] * @return java.util.Map * @Description Map 值转换 **/ private Map transformationArgs(Map args, List paramDefs) { Map result = new HashMap<>(args); paramDefs.forEach(pd->{ String name = pd.getSParam(); String sValue = pd.getSParamValue(); //中文 Boolean bCheck = result.containsKey(name) && ObjectUtil.isNotEmpty(result.get(name)); //英文字段 Boolean bCheck2 = result.containsKey(sValue) && ObjectUtil.isNotEmpty(result.get(sValue)); if (!bCheck2 && bCheck ) { result.put(sValue,args.get(name)); } //常量value -> key 转换 用于后面入库 if(RuleCode.CONST.getCode().equals(pd.getSRule()) && ObjectUtil.isNotEmpty(result.get(sValue))){ String sData = result.get(sValue).toString(); Map configData = JSONUtil.parseObj(pd.getSParamConfig()); configData.forEach((k,v)->{ if(sData.equals(v)){ result.put(sValue,k); } }); } }); return result; } /** * 动态参数补全SQL */ private Map applyValues(Map args, List paramDefsCheck) { Map result = new HashMap<>(args); result = transformationArgs( result, paramDefsCheck); //根据iOrder 排序 List paramDefs = new ArrayList<>(paramDefsCheck); paramDefs.sort(Comparator.comparing(ParamRule::getIOrder)); for (ParamRule pd : paramDefs) { String name = pd.getSParam(); String sValue = pd.getSParamValue(); String sRule = pd.getSRule(); String sType = pd.getSType(); Boolean bCheck = result.containsKey(name); bCheck = bCheck && ObjectUtil.isNotEmpty(result.get(name)); bCheck = bCheck && RuleCode.SQL.getCode().equals(sRule); bCheck = bCheck && ObjectUtil.isNotEmpty(pd.getSParamConfig()); // bCheck = bCheck && !"array".equals(sType); Boolean bCheck2 = result.containsKey(sValue); bCheck2 = bCheck2 && ObjectUtil.isNotEmpty(result.get(sValue)); bCheck2 = bCheck2 && RuleCode.SQL.getCode().equals(sRule); bCheck2 = bCheck2 && ObjectUtil.isNotEmpty(pd.getSParamConfig()); // bCheck2 = bCheck2 && !"array".equals(sType); //存在动态SQL 并且是枚举的需要 if ((bCheck || bCheck2) &&("enum".equals(sType) || "string".equals(sType))){ String sSql = pd.getSParamConfig(); String sKey = bCheck?name:sValue; String nameValue = result.get(sKey).toString(); List> dataList = dynamicExeDbService.findSql(result,sSql); //传入的参数无效返回继续盘问消息 if(ObjectUtil.isEmpty(dataList)){ throw new BusinessException(ErrorCode.PARAM_REQUIRED,String.format("%s 您描述的%s 不存在,请重新告诉我",name,nameValue)); } //如果SQL没有条件 多个数据集中进行匹配 如果只匹配一个也算成功 if(ObjectUtil.isNotEmpty(args.get(name)) || ObjectUtil.isNotEmpty(args.get(sValue))){ if(("enum".equals(sType) ||"string".equals(sType)) && (args.get(name) instanceof List || args.get(sValue) instanceof List)){ //枚举返回了数组 纠正成字符串 if(args.get(name) instanceof List){ args.put(name,((List) args.get(name)).get(0)); // args.put(sValue,((List) args.get(name)).get(0)); } if(args.get(sValue) instanceof List){ args.put(sValue,((List) args.get(sValue)).get(0)); // args.put(name,((List) args.get(sValue)).get(0)); } } List> dataListNew = dataList.stream().filter(one-> one.get(sValue).equals(args.get(name)) || one.get(sValue).equals(args.get(sValue))).collect(Collectors.toUnmodifiableList()); //如果枚举类型枚举值中又不存在AI 返回的数据 if("enum".equals(sType) && ObjectUtil.isEmpty(dataListNew)){ args.remove(name); args.remove(sValue); result.remove(name); result.remove(sValue); continue; } if(ObjectUtil.isNotEmpty(dataListNew)){ dataList = new ArrayList<>(); dataList.add(dataListNew.get(0)); } } if(ObjectUtil.isNotEmpty(dataList) && dataList.size()==1){ String sCopyTo = pd.getSCopyTo(); if(ObjectUtil.isEmpty(sCopyTo)){ result.put(sValue, dataList.get(0).get(sValue)); result.put(name, dataList.get(0).get(sValue)); }else{ //赋值到 String[] sCopyToA = sCopyTo.split(","); for(String sCopyToOne:sCopyToA){ String[] sCopyToOneA = sCopyToOne.split(":"); result.put(sCopyToOneA[0], dataList.get(0).get(sCopyToOneA[1])); } } } StringBuffer sData=new StringBuffer(); //存在多个形成提示语 if(dataList.size()>1){ List> finalDataList = dataList; IntStream.range(0, dataList.size()) .forEach(iRowNum ->{ sData.append((iRowNum + 1)).append(".").append(finalDataList.get(iRowNum).get(pd.getSParamValue())) .append("\n\n"); }); String sParamMissMemo = StrUtil.EMPTY; if(ObjectUtil.isEmpty(pd.getSParamMissMemo())){ sParamMissMemo = pd.getSParam()+"存在多个,请选择:
"+sData; }else{ sParamMissMemo = StrUtil.format(pd.getSParamMissMemo(),sData.toString()); } throw new BusinessException(ErrorCode.PARAM_REQUIRED,sParamMissMemo); } } } return result; } /** * 应用参数的默认值 */ private Map applyDefaultValues(Map args, List paramDefs) { Map result = new HashMap<>(args); for (ParamRule pd : paramDefs) { String name = pd.getSParam(); if ((!result.containsKey(name) || ObjectUtil.isEmpty(result.get(name))) && ObjectUtil.isNotEmpty(pd.getSDefaultValue()) && !"enum".equals(pd.getSType()) && !"array".equals(pd.getSType()) ) { Object defaultValue = pd.getSDefaultValue(); result.put(name, defaultValue); } } return result; } /** * 检查必填参数 */ private List checkRequiredParams(Map args, List paramDefs) { Map returnMap = transformationArgs( args, paramDefs); return paramDefs.stream() .filter(pd -> Boolean.TRUE.equals(pd.getBEmpty()) && pd.getBTipModel()) .filter(pd -> (!returnMap.containsKey(pd.getSParam()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParam())))) && (!returnMap.containsKey(pd.getSParamValue()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParamValue())))) ) .map(ParamRule::getSParam) .toList(); } /** * 确认后必填参数 */ private List checkConfirmAfterParam(Map args, List paramDefs) { Map returnMap = transformationArgs( args, paramDefs); return paramDefs.stream() .filter(pd -> Boolean.TRUE.equals(pd.getBConfirmAfter()) && pd.getBTipModel()) .filter(pd -> (!returnMap.containsKey(pd.getSParam()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParam())))) && (!returnMap.containsKey(pd.getSParamValue()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParamValue())))) ) .map(ParamRule::getSParam) .toList(); } /** * 模拟执行工具 */ public String executeTool(ToolExecutionRequest toolExecutionRequest,ToolMeta meta, Map args, List paramRuleData,String userId,UserSceneSession session ) { log.info("执行工具:{},参数:{}", meta.getSMethodNo(), args); // 2.2 将中文key转换成英文key args = transformationArgs( args, paramRuleData); String sReturn ="执行成功"; try{ sReturn = executeToolAfter(meta, args,toolExecutionRequest,paramRuleData,session); }catch (Exception e) { } if(meta.getIActionType()==1){ session.setBCleanMemory(true); } return sReturn; } /**** * @Author 钱豹 * @Date 10:26 2026/2/1 * @Param * @return * @Description 返回结果后 执行业务类 **/ private String executeToolAfter(ToolMeta meta, Map args,ToolExecutionRequest toolExecutionRequest,List paramDefs,UserSceneSession session) { // {"1":"存储过程","2":"SQL查询","3":"第三方API","4":"窗体查询","5":"按钮执行","6":"其它"} String sBizContent = meta.getSBizContent(); Integer iBizType = meta.getIBizType(); args.put("sUserId", session.getUserId()); args.put("sLoginId", session.getUserName()); args.put("sMakePerson", session.getUserName()); args.put("sBrId", session.getSBrandsId()); args.put("sBrandsId", session.getSBrandsId()); args.put("sSuId", session.getSSubsidiaryId()); args.put("sSubsidiaryId", session.getSSubsidiaryId()); if (iBizType == 1 || iBizType == 4) { Map data = new HashMap<>(args); data.put("sData", JSONObject.toJSONString(data)); Map searMap = this.dynamicExeDbService.getDoProMap(sBizContent, data); Map sReturn = this.dynamicExeDbService.getCallPro(searMap, sBizContent); Integer sCode = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SCODE)) ? Integer.valueOf(sReturn.get(ProcedureConstant.SCODE).toString()) : 0; String sMsgText = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SRETURN)) ? sReturn.get(ProcedureConstant.SRETURN).toString() : "操作成功"; if (sCode < 0) { String msg = ObjectUtil.isEmpty(sMsgText) ? "调用过程sCode:" + Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()) : sMsgText; return String.valueOf(askUserResult(toolExecutionRequest, msg)); } session.setSFunPrompts(sMsgText); return sMsgText; } else if (iBizType == 2 && ObjectUtil.isNotEmpty(sBizContent)) { //SQL查询 if (sBizContent.toLowerCase().startsWith("update")) { this.dynamicExeDbService.updateSql(args, sBizContent); } else if (sBizContent.toLowerCase().startsWith("delete")) { this.dynamicExeDbService.delSql(args, sBizContent); } else if (sBizContent.toLowerCase().startsWith("insert")) { this.dynamicExeDbService.addSql(args, sBizContent); } else { List> retData = this.dynamicExeDbService.findSql(args, sBizContent); if (ObjectUtil.isNotEmpty(retData)) { StringBuffer sb = new StringBuffer(); retData.forEach(one -> { one.forEach((k, v) -> { sb.append(v).append(" "); }); sb.append("
"); }); if (ObjectUtil.isNotEmpty(retData)) { sb.append("请根据这些信息安排今天的工作吧!如果有具体任务需要进一步处理,请告诉我"); } session.setSFunPrompts(sb.toString()); if ("queryTodayTask".equals(meta.getSMethodNo())) { session.setBCleanMemory(true); } return String.valueOf(successResult(toolExecutionRequest, sb.toString())); } else { session.setSFunPrompts("未找到对应的数据"); return "未找到对应的数据"; } } } else if (iBizType == 3) { return HttpsRequestUtil.me().doRequestHttp(sBizContent, JSONUtil.toJsonStr(args), new HashMap<>(), "POST", "JSON"); } return String.valueOf(successResult(toolExecutionRequest, "操作成功")); } /*** * @Author 钱豹 * @Date 23:38 2026/2/5 * @Param [] * @return void * @Description 窗体获取数据方法 未清或者明细 **/ private String doGetFromData(ToolMeta meta, Map args,UserSceneSession session){ String sUrl = meta.getSendUrl(); Map sBody = new HashMap<>(); sBody.put("pageNum",1); sBody.put("pageSize",10000); log.info("doGetFromData========================"); List> list = new ArrayList<>(); if(ObjectUtil.isNotEmpty(args)){ List paramDefs = meta.getParamRuleList(); args.forEach((k,v)->{ if(ObjectUtil.isNotEmpty(v)){ List pdList = paramDefs.stream().filter(m-> m.getSParam().equals(k) || m.getSParamValue().equals(k)).collect(Collectors.toUnmodifiableList()); List data = new ArrayList<>(); if(v instanceof List){ data.addAll((List) v); }else{ data.add(v); } data = data.stream().filter(m-> !(m.toString().contains("全部") || m.toString().contains("所有"))).collect(Collectors.toUnmodifiableList()); if(ObjectUtil.isNotEmpty(data)){ StringBuffer bFilterValue = new StringBuffer(); for(int i=0;i serOne = new HashMap<>(4); serOne.put("bFilterCondition","like"); serOne.put("bFilterValue",bFilterValue.toString()); serOne.put("bFilterName",pdList.get(0).getSParamValue()); list.add(serOne); } } } }); } log.info("开始请请求========================{}", sUrl); sBody.put("bFilter",list); Map headers = new HashMap<>(); headers.put("Authorization",session.getAuthorization()); String result; try{ // 1. 获取实例 result = HttpsRequestUtil.me().doRequestHttp(sUrl,JSONObject.toJSONString(sBody),headers,"POST","JSON"); log.info("请求URL========================{}", sUrl); log.info("请求URLresult========================{}", result); log.info("JSON==========================={}", JSONObject.toJSONString(sBody)); log.info("headers=============================={}", JSONObject.toJSONString(headers)); log.info("请求URL,JSON,headers=={},{},{}",sUrl,JSONObject.toJSONString(sBody),JSONObject.toJSONString(headers)); ErpResult erpResult = JsonUtils.toObject(result,ErpResult.class); result = buildResultMessageWithTable( meta, erpResult, session); }catch (Exception e){ result ="执行异常:"+e.getMessage(); } return result; } /** * 构建 窗体获取数据方法 未清或者明细 */ public String buildResultMessageWithTable(ToolMeta meta,ErpResult erpResult,UserSceneSession session){ Map> currentRowData = new HashMap<>(); ErpDataset dataset = erpResult.getDataset(); //返回错误信息 if(erpResult.getCode()<0 && ObjectUtil.isNotEmpty(erpResult.getMsg())){ return erpResult.getMsg(); } if(dataset==null){ return ErrorCode.DATA_NOT_FOUND.getMessage(); } List> sAIshowfieldShow = meta.getSAIshowfieldShow(); List> rows = new ArrayList<>(); if(ObjectUtil.isNotEmpty(dataset.getRows().get(0))){ rows = dataset.getRows().get(0).getDataSet(); } List> recordData = findFieldNameByChinese(sAIshowfieldShow, rows); int recordCount = dataset != null ? dataset.getTotalCount() : 0; StringBuilder markdown = new StringBuilder(); //状态 String sStatus = erpResult.getCode()<0?ErrorCode.ERRORMSG.getMessage():ErrorCode.SUCCESSMSG.getMessage(); markdown.append(sStatus).append("\n"); if(erpResult.getCode()<0){ String sMsg = ObjectUtil.isEmpty(erpResult.getMsg())?ErrorCode.WFHYY.getMessage():erpResult.getMsg(); markdown.append("**原因**: ").append(sMsg); return markdown.toString(); }else{ markdown.append(" 共 ").append(recordCount).append(" 条记录"); if(rows.size() headers = new LinkedHashSet<>(); for (Map record : sAIshowfieldShow) { String chineseName = (String) record.get("label"); if (chineseName != null && !"sSlaveId".equals(record.get("sName"))) { headers.add(chineseName); } } headers.forEach(header -> markdown.append(header).append(" | ")); markdown.append("\n|").append("---|".repeat(headers.size() + 1)).append("\n"); // 填充表格数据 List> machineData = new LinkedList<>(); for (int i = 0; i < recordData.size(); i++) { // 保存隐藏列的值(如"唯一"字段) String uniqueValue = recordData.get(i).get("sSlaveId") != null ? recordData.get(i).get("sSlaveId").toString() : ""; markdown.append("| ").append(i + 1).append(" | "); Map rMap = new HashMap<>(); for (String header : headers) { // 这里需要根据你的数据结构来获取对应的值 Object value = recordData.get(i)!= null ? recordData.get(i).get(header) : null; markdown.append(value != null ? value : "—").append(" | "); rMap.put(header,value); } rMap.put("sSlaveId",uniqueValue); rMap.put("唯一",uniqueValue); // 在行末添加隐藏数据的特殊标记(AI可以解析) markdown.append(" "); markdown.append("\n"); machineData.add(rMap); currentRowData.put(i + 1,recordData.get(i)); } markdown.append(">"); // // 4. 机器可读的结构化数据(只出现一次!) // markdown.append("\n"); // markdown.append(JSONUtil.toJsonStr(machineData)); // markdown.append("\n\n\n"); if(meta.getIBizType()==4){ markdown.append("\n---\n"); appendConfirmAll(markdown,meta.getSControlName()); } session.setCurrentRowData(currentRowData); return markdown.toString(); } // 辅助方法:根据中文名查找字段名(通过映射关系转换) private List> findFieldNameByChinese(List> sAIshowfieldShow,List> rows){ //获取映射关系 Map keyMappings = new HashMap<>(); List selectedKeys = new ArrayList<>(); sAIshowfieldShow.forEach(one->{ keyMappings.put(one.get("sName").toString(),one.get("label").toString()); if("sSlaveId".equals(one.get("sName"))){ keyMappings.put(one.get("sName").toString(),one.get("sName").toString()); } selectedKeys.add(one.get("sName").toString()); }); List> sRowData = getFilteredDataStream(rows,rows.size(),selectedKeys,keyMappings); return sRowData; } /*** * @Author 钱豹 * @Date 2:04 2026/2/6 * @Param [rows, limit, selectedKeys] * @return java.util.List> * @Description 返回指定数量并筛选指定key **/ public List> getFilteredDataStream(List> rows, int limit, List selectedKeys, Map keyMappings) { if (rows == null || rows.isEmpty()) { return Collections.emptyList(); } return rows.stream() .limit(limit) // 限制数量 .map(original -> { // 创建新的Map,只包含指定key Map filtered = new HashMap<>(); selectedKeys.forEach(key -> { if (original.containsKey(key)) { //指定映射的key 放中文 filtered.put(keyMappings.get(key), original.get(key)); } }); return filtered.isEmpty() ? null : filtered; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** * 构建确认操作消息 */ private String buildConfirmUserMessage(ToolMeta meta, Map args) { StringBuilder markdown = new StringBuilder(); markdown.append("参数提取如下:\n\n"); List paramRuleData = meta.getParamRuleList(); paramRuleData.forEach(one->{ if(ObjectUtil.isNotEmpty(args.get(one.getSParam()))){ markdown.append("- **").append(one.getSParam()).append("**: ").append(args.get(one.getSParam())).append("\n"); } }); markdown.append("\n---\n"); appendConfirm(markdown,meta.getSControlName()); return markdown.toString(); } /** * 构建提问消息 */ private String buildAskUserMessage(ToolMeta meta, List missing,Map arg) { StringBuilder sb = new StringBuilder(); sb.append("缺少参数请补全:\n"); List paramRuleData = meta.getParamRuleList(); for (String name : missing) { paramRuleData.stream() .filter(pd -> pd.getSParam().equals(name)) .findFirst() .ifPresentOrElse( pd -> { String sTs = ObjectUtil.isEmpty(pd.getSRuleTs())?StrUtil.EMPTY:pd.getSRuleTs(); sb.append("- **").append(name).append("**: "); if(ObjectUtil.isNotEmpty(pd.getSParamMissMemo()) && ObjectUtil.isNotEmpty(arg.get(pd.getSParam())) && ObjectUtil.isNotEmpty(sTs) ){ if(!("string".equals(pd.getSType()) && RuleCode.SQL.equals(pd.getSRule()) && ObjectUtil.isNotEmpty(pd.getSParamConfig()) )) { sb.append(StrUtil.format(pd.getSParamMissMemo(),sTs)); } }else if(ObjectUtil.isNotEmpty(sTs)){ sb.append(sTs); } sb.append("\n"); }, () -> sb.append("- ").append(name).append("\n") ); } return sb.toString(); } // 创建提前成功的结果 private String createEarlySuccessResult(ToolExecutionRequest request, String message) { // 设置一个标志,告诉执行器不要继续执行 return JSONUtil.toJsonStr(Map.of( "status", "success", "message", message, // 关键标志 "executionCompleted", true, "data", successResult(request, message) )); } // 创建终止执行的结果 private String createTerminationResult(String message) { return JSONUtil.toJsonStr(Map.of( "status", "terminated", "message", message, "shouldContinue", false )); } /*** * @Author 钱豹 * @Date 10:15 2026/1/31 * @Param [request, errorMsg] * @return dev.langchain4j.data.message.ToolExecutionResultMessage * @Description 错误返回 **/ private ToolExecutionResultMessage errorResult(ToolExecutionRequest request, String errorMsg) { return ToolExecutionResultMessage.from(request, errorMsg); } /*** * @Author 钱豹 * @Date 10:15 2026/1/31 * @Param [request, text] * @return dev.langchain4j.data.message.ToolExecutionResultMessage * @Description 构建正确返回 **/ private ToolExecutionResultMessage successResult(ToolExecutionRequest request, String text) { return ToolExecutionResultMessage.from(request, text); } /** * 询问用户工具执行结果 * @param request 工具执行请求 * @param text 回复文本内容 * @return 工具执行结果消息 * @author 钱豹 */ private ToolExecutionResultMessage askUserResult(ToolExecutionRequest request, String text) { // 直接返回标准结果 return ToolExecutionResultMessage.from(request, text); } /** * 执行方法后需要用户确认的扩展版本 */ private ToolExecutionResultMessage executeWithConfirmation(ToolExecutionRequest request, String initialResult,ChatMemory chatMemory, UserSceneSession session,ToolMeta meta) { // 第一步:执行原始操作,返回初步结果 Map step1Result = new HashMap<>(); step1Result.put("initialResult", initialResult); step1Result.put("status", "PENDING_CONFIRMATION"); step1Result.put("confirmationRequired", true); step1Result.put("confirmationMessage", initialResult); // // 将确认状态保存到对话记忆 chatMemory.add(UserMessage.from("SYSTEM: 等待用户确认操作")); String userMessage = formatConfirmationResult(step1Result); session.setCurrentTool(meta); session.setSFunPrompts(userMessage); // 6. 返回确认请求 return ToolExecutionResultMessage.from(request,userMessage); } private String formatConfirmationResult(Map result) { return String.format( """ **结果** : %s """, result.get("initialResult"), result.get("confirmationMessage") ); } /*** * @Author 钱豹 * @Date 0:54 2026/2/4 * @Param [userResponse] * @return boolean * @Description 检查是确认 **/ private boolean isConfirmed(String userResponse) { return userResponse.matches("(?i)(确认|全部确认|部分确认|是|yes|confirm|true|是的|可以|没问题|确定|好的|生成|)"); } }