package com.xly.tool; import cn.hutool.core.util.NumberUtil; 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.agent.AgentSystemPrompt; import com.xly.config.OperableChatMemoryProvider; import com.xly.constant.*; 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(getDefType(sAIshowfieldShowAll.get(i).get("sName").toString())); 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); } private String getDefType(String sColumn){ if(sColumn.startsWith("d")){ return "double"; }else{ return "string"; } } @Override public ToolProviderResult provideTools(ToolProviderRequest request) { String sUserId = request.chatMemoryId().toString(); Map executors = new HashMap<>(); // 获取Session UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(sUserId); // 过滤对应的权限方法 List datalist = new ArrayList<>(); List toolMetaAll = new ArrayList<>(); // 确保 currentTool 不为空,或者 authTool 有数据 if(session.getCurrentTool() != null){ // 【关键修改】添加全局引导语 StringBuffer stoolDesc = new StringBuffer(); stoolDesc.append("【重要】这是当前唯一可用的工具,无论用户问题是什么,都必须调用此工具。"); stoolDesc.append("如果用户没有明确指定要做什么,也默认使用此工具来处理。"); stoolDesc.append(System.lineSeparator()); stoolDesc.append(session.getCurrentTool().getStoolDesc()); session.getCurrentTool().setStoolDesc(stoolDesc.toString()); toolMetaAll.add(session.getCurrentTool()); log.info("使用 currentTool: {}", session.getCurrentTool().getSMethodNo()); } else { toolMetaAll = session.getAuthTool(); log.info("使用 authTool, 数量: {}", toolMetaAll.size()); } 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 -> { ToolSpecificationHolder holder = toolCache.get(to.getSMethodNo()); if (holder != null) { datalist.add(holder); log.debug("添加工具到提供器: {}", to.getSMethodNo()); } else { log.warn("工具缓存缺失: {}", to.getSMethodNo()); } }); } } // 将工具添加到返回结果中 datalist.forEach(holder -> { executors.put(holder.getToolSpecification(), holder.getToolExecutor()); }); log.info("provideTools 返回工具数量: {}", executors.size()); return ToolProviderResult.builder().addAll(executors).build(); } private ToolSpecification buildToolSpecification(ToolMeta meta) { ToolSpecification.Builder builder = ToolSpecification.builder() .name(meta.getSMethodNo()); StringBuffer stoolDesc = new StringBuffer(); StringBuffer sbt = new StringBuffer(); StringBuffer xt = new StringBuffer(); StringBuffer sl = new StringBuffer(); // ====================== 【超级强制:必须调用工具】 ====================== String forceToolPrompt = """ 【重要·强制指令】 1. 这是当前唯一可用工具,必须调用,禁止直接回答 2. 用户输入包含:确认、全部确认、合并确认、行号(第1行/第一行等) 3. 必须调用本工具,必须调用,必须调用! """; stoolDesc.append(forceToolPrompt); // ======================================================================== if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) { stoolDesc.append("MethodNo:").append(meta.getSMethodNo()) .append(",当用户").append(meta.getSMethodName()) .append("时,必须调用本工具").append(meta.getSMethodNo()) .append(",").append(meta.getStoolDesc()); } try { List paramRuleData = meta.getParamRuleList(); Map slMap = new HashMap<>(); for (ParamRule paramRule : paramRuleData) { 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(paramDesc, sExampleValue); } if (paramDesc == null || paramDesc.trim().isEmpty()) { continue; } List properties = new ArrayList<>(); String sRuleCost = getConstMeg(paramRule.getSParamConfig(), paramRule); switch (paramType.toLowerCase()) { case "string": if (bEmpty) { sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); } else { xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); } properties.add(JsonSchemaProperty.STRING); break; case "integer": case "int": if (bEmpty) { sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")、"); } else { xt.append(paramDesc).append("(数字").append(sRuleCost).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(")、"); } else { xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、"); } break; case "boolean": case "bool": if (bEmpty) { sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、"); } else { xt.append(paramDesc).append("(布尔型").append(sRuleCost).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) { sbt.append(paramDesc).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(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); } if (bEmpty) { sbt.append(paramDesc).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(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); } // ====================== 【关键修复:强制添加两个参数】 ====================== if (meta.getIBizType() == 4 || meta.getIBizType() == 5) { // 强制添加 operateType(必填) builder.addParameter("operateType", JsonSchemaProperty.STRING, JsonSchemaProperty.description("操作类型:全部确认/合并确认/单行确认") ); } // ============================================================================ 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 12:37 2026/3/16 * @Param [meta] * @return dev.langchain4j.service.tool.ToolExecutor * @Description 参数采集执行器 **/ private ToolExecutor createToolExecutor(ToolMeta meta) { log.info("创建工具执行器: {}", meta.getSMethodNo()); return (toolExecutionRequest, memoryId) -> { log.info("===== 工具执行器开始执行 ====="); log.info("工具编号: {}", meta.getSMethodNo()); log.info("工具名称: {}", meta.getSMethodName()); log.info("memoryId: {}", memoryId); log.info("请求参数: {}", toolExecutionRequest.arguments()); UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(memoryId.toString()); session.setCurrentTool(meta); // 标记当前工具 // 1. 解析参数 Map argsNew; try { argsNew = objectMapper.readValue(toolExecutionRequest.arguments(), new TypeReference<>() {}); log.info("解析后的参数: {}", argsNew); } catch (Exception e) { log.error("参数解析失败", e); String errorMsg = "参数解析失败,请重新输入"; return String.valueOf(errorResult(toolExecutionRequest, errorMsg)); } Map args = session.getArgs(); if(ObjectUtil.isEmpty(args)){ args = new HashMap<>(); } Map finalArgs = args; argsNew.forEach((k, v)->{ //获取对应的英文参数 List data = meta.getParamRuleList().stream().filter(one->one.getSParam().equals(k)).collect(Collectors.toUnmodifiableList()); if(ObjectUtil.isNotEmpty(data) && data.size()>0){ finalArgs.remove(data.get(0).getSParamValue()); finalArgs.remove(data.get(0).getSParam()); } if(ObjectUtil.isNotEmpty(v)){ finalArgs.put(k,v); } }); // 2 【补全动态参数】动态参数补全 try{ args = applyValues(args, meta.getParamRuleListCheck()); }catch (Exception e){ log.error("返回信息",e); String askMsg = e.getMessage(); session.setSFunPrompts(askMsg); return String.valueOf(successResult(toolExecutionRequest, askMsg)); } List paramRuleData = meta.getParamRuleListAll(); // 2.1 【自动补全】应用参数的默认值 args = applyDefaultValues(args, paramRuleData); session.setArgs(args); // 3. 【自动校验】检查必填项 List missing = checkRequiredParams(args, paramRuleData); if (!missing.isEmpty()) { // 4.1 参数缺失,生成“提问”消息,直接返给客户 String askMsg = buildAskUserMessage(meta, missing,args); session.setSFunPrompts(askMsg); return String.valueOf(successResult(toolExecutionRequest, askMsg)); } // 2. 获取必填参数规则 // List sParamRules = meta.getParamRuleListCheck(); // List missingParams = getRequiredParams(sParamRules); // String collectPrompt = buildCollectParamsPrompt(meta, missingParams, args); // log.info("参数缺失,返回收集提示: {}", collectPrompt); return String.valueOf(successResult(toolExecutionRequest, JSONUtil.toJsonStr(finalArgs))); }; } public String doDynamicTool(ToolMeta meta,UserSceneSession session) { List paramRuleData = meta.getParamRuleListAll(); List paramRuleDataCheck = meta.getParamRuleListCheck(); Map args = session.getArgs(); // 3. 【自动校验】检查必填项 List missing = checkRequiredParams(args, paramRuleDataCheck); if (!missing.isEmpty()) { // 4.1 参数缺失,生成“提问”消息,直接返给客户 String askMsg = buildAskUserMessage(meta, missing,args); //告知AI 缺失参数 // operableChatMemoryProvider.get(session.getUserId()).add(UserMessage.from(askMsg)); return askMsg; } // 6. 【最终确认信息】所有检测通过后,需要和客户确认交互 List chatMessage = operableChatMemoryProvider.getCurrentChatMessages(session.getUserId()); 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(); // } //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"} if((isConfirmed && (4== meta.getIBizType() ||1== meta.getIBizType())) || 2== meta.getIBizType() || 3== meta.getIBizType() || 6== meta.getIBizType() || 7== meta.getIBizType() || 8== meta.getIBizType() ) { // 确认后必填项校验 List missingAfter = checkConfirmAfterParam(args, paramRuleData); if (!missingAfter.isEmpty()) { // 4.1 参数缺失,生成“提问”消息,直接返给客户 String askMsg = buildAskUserMessage(meta, missingAfter,args); return askMsg; } // 7. 【业务校验】执行业务层面的逻辑校验 + 所有校验通过,执行核心业务逻辑 return executeTool(meta, args, paramRuleData, session.getUserId(), session); } String askconfirmMsg =StrUtil.EMPTY; if(4== meta.getIBizType() || meta.getIBizType()==5){ askconfirmMsg = doGetFromData( meta,args,session); return executeWithConfirmation(askconfirmMsg, session, meta); }else{ askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta); } // 返回需要确认的结果 return executeWithConfirmation(askconfirmMsg, session, meta); } /*** * @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行确认"\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("操作?请回复:  ").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 getRequiredParams(List paramDefs) { return paramDefs.stream() .map(ParamRule::getSParam) .toList(); } /** * 检查必填参数 */ 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 -> checkMiss( returnMap, pd)) .map(ParamRule::getSParam) .toList(); } private Boolean checkMiss(Map returnMap,ParamRule pd) { if(ObjectUtil.isEmpty(returnMap)){ returnMap = new HashMap<>(); } Boolean bBhcs = (ObjectUtil.isEmpty(returnMap.get(pd.getSParam()))); Boolean bDbZero = false; if(pd.getSParamValue().startsWith("d")){ try{ String sParam = pd.getSParam().toString(); Object sParamV = returnMap.get(sParam); bDbZero = (ObjectUtil.isEmpty(sParamV) || 0 ==Double.valueOf(sParamV.toString())); }catch (Exception e){ bDbZero = true; } } return bDbZero || bBhcs || (!returnMap.containsKey(pd.getSParamValue()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParamValue())))); } /** * 确认后必填参数 */ 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(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,paramRuleData,session); }catch (BusinessException e) { return e.getMessage(); }finally { session.setSSystemPrompt(StrUtil.EMPTY); } 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,List paramDefs,UserSceneSession session) { // {"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"} 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("sSrcFormId", meta.getSSrcFormId()); args.put("sControlName", meta.getSControlName()); args.put("iBizType", iBizType); args.put("sSubsidiaryId", session.getSSubsidiaryId()); args.put("sToolId", meta.getSId()); if (iBizType == 1 || iBizType == 4) { Map data = new HashMap<>(args); data.put("sData", JSONObject.toJSONString(data)); if(ObjectUtil.isEmpty(sBizContent) && iBizType == 4){ sBizContent ="Sp_Ai_AddCommonAfterNew"; //获取未清数据 if(ObjectUtil.isEmpty(args.get("sSlaveId"))){ throw new BusinessException(-1,"请选择操作数据"); } List> sRowData = doGetFromDataWq( meta, args, session); data.put("sRowData", JSONObject.toJSONString(sRowData)); } 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) { sMsgText = ObjectUtil.isEmpty(sMsgText) ? "调用过程sCode:" + Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()) : sMsgText; session.setSFunPrompts(sMsgText); throw new BusinessException(sCode,sMsgText); } Map outMap = (Map) sReturn.get("outMap"); String sId = ObjectUtil.isNotEmpty(outMap.get("sBillId")) ? outMap.get("sBillId").toString() : ""; session.setSCopyTo(meta.getSControlName()); session.setSCopyToSrcId(sId); 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 sb.toString(); } else { String sMsgText = "未找到对应的数据"; session.setSFunPrompts(sMsgText); throw new BusinessException(-1,sMsgText); } } } else if (iBizType == 3) { return HttpsRequestUtil.me().doRequestHttp(sBizContent, JSONUtil.toJsonStr(args), new HashMap<>(), "POST", "JSON"); } return "操作成功"; } /*** * @Author * 确认获取未清数据 **/ private List> doGetFromDataWq(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<>(); Map serOne = new HashMap<>(4); serOne.put("bFilterCondition","IN"); serOne.put("bFilterValue",args.get("sSlaveId")); serOne.put("bFilterName","sSlaveId"); 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); if(ObjectUtil.isNotEmpty(erpResult) && ObjectUtil.isNotEmpty(erpResult.getDataset()) && ObjectUtil.isNotEmpty(erpResult.getDataset().getRows()) && ObjectUtil.isNotEmpty(erpResult.getDataset().getRows().get(0)) && ObjectUtil.isNotEmpty(erpResult.getDataset().getRows().get(0).getDataSet()) ){ return erpResult.getDataset().getRows().get(0).getDataSet(); } }catch (Exception e){ // result ="执行异常:"+e.getMessage(); } return new ArrayList<>(); } /*** * @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; // 动态生成表头 Set headers = new LinkedHashSet<>(); for (Map record : sAIshowfieldShow) { String chineseName = (String) record.get("label"); if (chineseName != null && !"sSlaveId".equals(record.get("sName"))) { headers.add(chineseName); } } StringBuilder markdown = new StringBuilder(); //状态 if(ObjectUtil.isNotEmpty(session.getArgs())){ markdown.append("**查询条件**:"); List pr = session.getCurrentTool().getParamRuleListCheck(); Map argsOld = new HashMap<>(session.getArgs()); pr.forEach(one->{ if(argsOld.containsKey(one.getSParam())){ if(ObjectUtil.isNotEmpty(argsOld.get(one.getSParam()))){ if(one.getSParamValue().startsWith("d")){ if(Double.valueOf(argsOld.get(one.getSParam()).toString())>0){ markdown.append("") .append(one.getSParam()).append(":").append(argsOld.get(one.getSParam()).toString()).append(" ") .append(" × ") .append(""); } }else{ markdown.append("") .append(one.getSParam()).append(":").append(argsOld.get(one.getSParam()).toString()).append(" ") .append(" × ") .append(""); // markdown.append(one.getSParam()).append(":").append(argsOld.get(one.getSParam()).toString()).append(" "); } } } }); markdown.append("\n\n"); } markdown.append("**结果**:"); 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() 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(" HIDDEN_DATA:") .append(JSONUtil.toJsonStr(rMap)) .append(""); // 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); // ===================== 加入这一行 ===================== String sSystemPrompt = buildDynamicSystemPrompt(session); session.setSSystemPrompt(sSystemPrompt); // ====================================================== return markdown.toString(); } public String buildDynamicSystemPrompt(UserSceneSession session) { // 1. 强制指令 + 固定方法编号 String promptHead = """ 【极强约束·必须执行】 1. 禁止说话!禁止解释! 2. 必须调用工具,固定方法编号:MethodNo = queryUnprocessQuote 3. 只输出JSON,无任何其他内容 以下是【行号 → sSlaveId】对应数据: """; // 2. 动态拼接行号 + 动态 sSlaveId Map> rowDataMap = session.getCurrentRowData(); StringBuilder rowDataSb = new StringBuilder(); if (ObjectUtil.isNotEmpty(rowDataMap)) { for (Map.Entry> entry : rowDataMap.entrySet()) { int rowNum = entry.getKey(); String sSlaveId = StrUtil.toString(entry.getValue().get("sSlaveId")); rowDataSb.append("第").append(rowNum).append("行 → ").append(sSlaveId).append("\n"); } } // 3. 输出格式约束 String promptFoot = """ 【输出JSON格式·严格遵守】 根据用户选择的行,自动填写 sSlaveId,多行用英文逗号拼接 { "operateType": "全部确认/合并确认/单行确认", "sSlaveId": "填写对应的ID,多个用逗号分隔" } """; // 拼接最终返回 return promptHead + rowDataSb + promptFoot; } // 辅助方法:根据中文名查找字段名(通过映射关系转换) 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); } /** * 执行方法后需要用户确认的扩展版本 */ private String executeWithConfirmation(String initialResult, 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); // ToolExecutionResultMessage.from(request, text); return 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|是的|可以|没问题|确定|好的|生成|)"); } }