From f0318b7bafc221490c485a944620d9658d7d751a Mon Sep 17 00:00:00 2001 From: qianbao Date: Tue, 7 Jul 2026 12:34:22 +0800 Subject: [PATCH] 添加未清选择 改成动态引导语 --- src/main/java/com/xly/agent/SystemPromptGenerator.java | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main/java/com/xly/constant/ReturnTypeCode.java | 1 + src/main/java/com/xly/entity/ParamRule.java | 5 +++++ src/main/java/com/xly/entity/ToolMeta.java | 1 + src/main/java/com/xly/service/QuoService.java | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/xly/service/XlyErpService.java | 16 ++++++++++------ src/main/java/com/xly/tool/DynamicToolProvider.java | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 7 files changed, 332 insertions(+), 206 deletions(-) create mode 100644 src/main/java/com/xly/service/QuoService.java diff --git a/src/main/java/com/xly/agent/SystemPromptGenerator.java b/src/main/java/com/xly/agent/SystemPromptGenerator.java index 49f0b85..d18d658 100644 --- a/src/main/java/com/xly/agent/SystemPromptGenerator.java +++ b/src/main/java/com/xly/agent/SystemPromptGenerator.java @@ -1,8 +1,6 @@ package com.xly.agent; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.json.JSONConfig; -import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONWriter; @@ -10,6 +8,7 @@ import com.xly.entity.ParamRule; import com.xly.entity.UserSceneSession; import java.util.List; +import java.util.stream.Collectors; public class SystemPromptGenerator { @@ -50,4 +49,64 @@ public class SystemPromptGenerator { return sb.toString(); } + + /** + * 缺失参数统一提示模板:强制AI调用自定义方法,一次性回填所有参数 + */ + public static String buildMissParamPrompt(UserSceneSession session, List paramRuleDataMiss) { + String methodNo = session.getCurrentTool().getSMethodNo(); + // 1. 拼接缺失参数的描述(增加“参数名”和“英文名”的对应,方便模型映射) + String paramDesc = paramRuleDataMiss.stream() + .map(p -> String.format("- 参数名:%s (对应字段: %s),示例值:%s", + p.getSParam(), p.getSParamValue(), p.getSExampleValue())) + .collect(Collectors.joining("\n")); + // 但根据你的需求,其实 prompt 里并不需要强行插入一个空的 JSON 片段, + // 直接告诉模型去“合并”即可。 + return String.format(""" + 请你根据用户最新的输入,补充缺失的参数,并带着【所有完整参数】立即发起工具调用。 + + 【核心执行规则】 + 1. **参数合并(至关重要)**:你必须保留上一轮对话中已经获取的参数(如 sSlaveId, operateType 等),并将用户本次提供的新参数与旧参数合并。 + 2. **必须调用工具**:必须调用工具 `%s`。绝对禁止直接输出 JSON 文本,也绝对禁止向用户反问或索要信息。 + 3. **完整调用**:请直接生成包含所有参数的 tool_calls 请求。 + 【已获取数据】 + %s + 【当前缺失的参数说明】 + %s + """, methodNo,JSONObject.toJSONString(session.getArgs()), paramDesc); + } + + public static String buildDynamicSystemPrompt(UserSceneSession session) { + // 获取当前工具编号(用于 JSON 的 name 字段) + String methodNo = session.getCurrentTool().getSMethodNo(); + return """ + 【极强约束·必须执行】 + 1. 禁止说话!禁止解释! + 2. 必须调用工具! + 3. 只输出标准工具调用JSON! + + ### 任务背景 ### + 用户会输入自然语言指令(如“第1行确认”、“全部合并”等)。 + 你的任务是:解析出【操作类型】和【目标行号】。 + + ### 解析规则(必须严格遵守) ### + + 1. **操作类型 (operateType)**: + - 如果用户说“全部确认”、“生成多个单据”,识别为:"全部确认" + - 如果用户说“合并确认”、“生成一个单据”,识别为:"合并确认" + - 其他情况,默认为:"单行确认" + + 2. **行号提取 (rowNumbers)**: + - 单选:如“第5行”,提取为 [5] + - 多选:如“第1、3、5行”,提取为 [1, 3, 5] + - 范围:如“第2到4行”,提取为 [2, 3, 4] + - 全选:如“全部”、“所有”,提取为 ["ALL"] + + ### 输出格式规范 ### + 请直接输出以下 JSON(不要任何 Markdown 格式,不要 ```json): + {"name": "%s", "parameters": {"operateType": "解析出的类型", "rowNumbers": [行号数组或"ALL"]}} + + """.formatted(methodNo); + } + } \ No newline at end of file diff --git a/src/main/java/com/xly/constant/ReturnTypeCode.java b/src/main/java/com/xly/constant/ReturnTypeCode.java index 41663a5..ac012ee 100644 --- a/src/main/java/com/xly/constant/ReturnTypeCode.java +++ b/src/main/java/com/xly/constant/ReturnTypeCode.java @@ -14,6 +14,7 @@ public enum ReturnTypeCode { // 成功 HTML("html", "html"), + JSON("JSON", "JSON"), STREAM("stream", "stream"), MAKEDOWN("makedown", "makedown"); diff --git a/src/main/java/com/xly/entity/ParamRule.java b/src/main/java/com/xly/entity/ParamRule.java index 9e889dc..f9af7dc 100644 --- a/src/main/java/com/xly/entity/ParamRule.java +++ b/src/main/java/com/xly/entity/ParamRule.java @@ -3,6 +3,8 @@ package com.xly.entity; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; /*** * @Author 钱豹 @@ -32,6 +34,9 @@ public class ParamRule { private String sDynamicParamRuleSql; private String sDynamicParam; private String sDynamicParamName; + private String sGroup; + private Object sParamConfigValue; + private Boolean bStandardParam; private Boolean bDynamicParam = false; } diff --git a/src/main/java/com/xly/entity/ToolMeta.java b/src/main/java/com/xly/entity/ToolMeta.java index 996100e..4c5976f 100644 --- a/src/main/java/com/xly/entity/ToolMeta.java +++ b/src/main/java/com/xly/entity/ToolMeta.java @@ -53,5 +53,6 @@ public class ToolMeta { private String sVectorfiledAll; private String sVectorfiledShow; private Boolean sQue = false; + private Boolean bQuo = false; } diff --git a/src/main/java/com/xly/service/QuoService.java b/src/main/java/com/xly/service/QuoService.java new file mode 100644 index 0000000..843c877 --- /dev/null +++ b/src/main/java/com/xly/service/QuoService.java @@ -0,0 +1,173 @@ +package com.xly.service; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSONObject; +import com.xly.agent.SystemPromptGenerator; +import com.xly.constant.ErrorCode; +import com.xly.constant.ProcedureConstant; +import com.xly.entity.ParamRule; +import com.xly.entity.ToolMeta; +import com.xly.entity.UserSceneSession; +import com.xly.exception.dto.BusinessException; +import com.xly.mapper.DynamicExeDbMapper; +import com.xly.util.DeepCopyUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/*** + * @Author 钱豹 + * @Date 0:06 2026/7/7 + * @Param + * @return + * @Description 报价业务类 + **/ +@Slf4j +@Service("quoService") +@RequiredArgsConstructor +public class QuoService { + + + /*** + * @Author 钱豹 + * @Date 1:25 2026/7/7 + * @Param [argsNew, session, dynamicParamRule] + * @return java.util.Map + * @Description 报价获取参数组装 + **/ + public Map getQuoData(Map argsNew, UserSceneSession session, List dynamicParamRule){ + //获取当前方法 + Map retData = new HashMap<>(); + ToolMeta meta = session.getCurrentTool(); + //放入方法动态参数中 + meta.setDynamicParamRule(dynamicParamRule); + session.setCurrentTool(meta); + Map argsOld = session.getArgs(); + Map args = DeepCopyUtils.deepCopy(argsNew); + if(ObjectUtil.isEmpty(argsOld)){ + argsOld = new HashMap<>(); + } + argsOld.putAll(args); + session.setArgs(argsOld); + List dataAll = session.getCurrentTool().getParamRuleListAll(); + //获取产品的参数 + List dataProduct = dataAll.stream().filter(one-> "产品".equals(one.getSGroup()) && !one.getBDynamicParam() ).collect(Collectors.toUnmodifiableList()); + List dataPart = dataAll.stream().filter(one-> "部件".equals(one.getSGroup()) && !one.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + List datasDynamicPart = dataAll.stream().filter(one-> "sDynamicPart".equals(one.getSParamValue()) && !one.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + List> sAll = new ArrayList<>(); + List> productList = new ArrayList<>(); + Map finalArgsOld = argsOld; + dataProduct.forEach(p->{ + Map product = new HashMap<>(); + String sValue = ObjectUtil.isNotEmpty(finalArgsOld.get(p.getSParam()))? finalArgsOld.get(p.getSParam()).toString():(ObjectUtil.isNotEmpty(finalArgsOld.get(p.getSParam()))? finalArgsOld.get(p.getSParam()).toString():StrUtil.EMPTY); + product.put("sName",p.getSParamValue()); + product.put("showName",p.getSParam()); + product.put("bEmpty",p.getBEmpty()); + product.put("sValue", sValue); + product.put("sParamMissMemo", p.getSParamMissMemo()==null?StrUtil.EMPTY:p.getSParamMissMemo()); + product.put("iOrder", p.getIOrder()); + product.put("sParamConfigValue",p.getSParamConfigValue()==null?StrUtil.EMPTY:p.getSParamConfigValue()); + productList.add(product); + }); + List> partList = new ArrayList<>(); + dataPart.forEach(p->{ + Map part = new HashMap<>(); + String sValue = ObjectUtil.isNotEmpty(finalArgsOld.get(p.getSParam()))?finalArgsOld.get(p.getSParam()).toString():(ObjectUtil.isNotEmpty(finalArgsOld.get(p.getSParam()))? finalArgsOld.get(p.getSParam()).toString():StrUtil.EMPTY); + part.put("sPartName",p.getSParam().split("-")[0]); + part.put("sName",p.getSParamValue()); + part.put("showName",p.getSParam()); + part.put("bEmpty",p.getBEmpty()); + part.put("sValue", sValue); + part.put("iOrder", p.getIOrder()); + part.put("sParamMissMemo", p.getSParamMissMemo()==null?StrUtil.EMPTY:p.getSParamMissMemo()); + part.put("sParamConfigValue",p.getSParamConfigValue()==null?StrUtil.EMPTY:p.getSParamConfigValue()); + //获取动态参数 + if(ObjectUtil.isNotEmpty(sValue)){ + List dataParamDynamic = dataAll.stream().filter(d -> sValue.equals(d.getSDynamicParam()) && p.getSParamValue().equals(d.getSDynamicParamName())&&d.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + if(ObjectUtil.isNotEmpty(dataParamDynamic)){ + List> dynamicData = new ArrayList<>(); + dataParamDynamic.forEach(dynamic->{ + Map dynamicOne = new HashMap<>(); + String dynamicValue = ObjectUtil.isNotEmpty(finalArgsOld.get(dynamic.getSParam()))?finalArgsOld.get(dynamic.getSParam()).toString():(ObjectUtil.isNotEmpty(finalArgsOld.get(dynamic.getSParam()))? finalArgsOld.get(dynamic.getSParam()).toString():StrUtil.EMPTY); + dynamicOne.put("sName",dynamic.getSParamValue()); + dynamicOne.put("showName",dynamic.getSParam()); + dynamicOne.put("bEmpty",dynamic.getBEmpty()); + dynamicOne.put("sValue", dynamicValue); + dynamicOne.put("iOrder", dynamic.getIOrder()); + dynamicOne.put("sParamMissMemo", dynamic.getSParamMissMemo()==null?StrUtil.EMPTY:dynamic.getSParamMissMemo()); + dynamicOne.put("sParamConfigValue",dynamic.getSParamConfigValue()==null?StrUtil.EMPTY:dynamic.getSParamConfigValue()); + //获取动态参数 + dynamicData.add(dynamicOne); + }); + sAll.addAll(dynamicData); + part.put("dynamicData",dynamicData); + } + } + + partList.add(part); + }); + //动态部件 + if(ObjectUtil.isNotEmpty(datasDynamicPart)){ + String datasDynamicPartValue = ObjectUtil.isNotEmpty(finalArgsOld.get("sDynamicPart"))? finalArgsOld.get("sDynamicPart").toString(): StrUtil.EMPTY; +// :(ObjectUtil.isNotEmpty(finalArgsOld.get(p.getSParam()))? finalArgsOld.get(p.getSParam()).toString():StrUtil.EMPTY); + if(StrUtil.isNotEmpty(datasDynamicPartValue)){ + List datasDynamicPartArray = Collections.singletonList(JSONObject.parseObject(datasDynamicPartValue, String.class)); + datasDynamicPartArray.forEach(one->{ + List dataPartDynamic = dataAll.stream().filter(d -> one.equals(d.getSDynamicParam()) && "sDynamicPart".equals(d.getSDynamicParamName())&&d.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + if(ObjectUtil.isNotEmpty(dataPartDynamic)){ + dataPartDynamic.forEach(dynamic->{ + Map part = new HashMap<>(); + String sValue = ObjectUtil.isNotEmpty(finalArgsOld.get(dynamic.getSParam()))?finalArgsOld.get(dynamic.getSParam()).toString():(ObjectUtil.isNotEmpty(finalArgsOld.get(dynamic.getSParam()))? finalArgsOld.get(dynamic.getSParam()).toString():StrUtil.EMPTY); + part.put("sPartName",one); + part.put("sName",dynamic.getSParamValue()); + part.put("showName",dynamic.getSParam()); + part.put("bEmpty",dynamic.getBEmpty()); + part.put("sValue", sValue); + part.put("iOrder", dynamic.getIOrder()); + part.put("sParamConfigValue",dynamic.getSParamConfigValue()); + part.put("sParamMissMemo", dynamic.getSParamMissMemo()); + //获取动态参数 + if(ObjectUtil.isNotEmpty(sValue)){ + List dataParamDynamic = dataAll.stream().filter(d -> sValue.equals(d.getSDynamicParam()) && dynamic.getSParamValue().equals(d.getSDynamicParamName())&&d.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + if(ObjectUtil.isNotEmpty(dataParamDynamic)){ + List> dynamicData = new ArrayList<>(); + dataParamDynamic.forEach(dynamicD->{ + Map dynamicOne = new HashMap<>(); + String dynamicValue = ObjectUtil.isNotEmpty(finalArgsOld.get(dynamicD.getSParam()))?finalArgsOld.get(dynamicD.getSParam()).toString():(ObjectUtil.isNotEmpty(finalArgsOld.get(dynamicD.getSParam()))? finalArgsOld.get(dynamicD.getSParam()).toString():StrUtil.EMPTY); + dynamicOne.put("sName",dynamicD.getSParamValue()); + dynamicOne.put("showName",dynamicD.getSParam()); + dynamicOne.put("bEmpty",dynamicD.getBEmpty()); + dynamicOne.put("sValue", dynamicValue); + dynamicOne.put("iOrder", dynamicD.getIOrder()); + dynamicOne.put("sParamMissMemo", dynamicD.getSParamMissMemo()==null?StrUtil.EMPTY:dynamicD.getSParamMissMemo()); + dynamicOne.put("sParamConfigValue",dynamicD.getSParamConfigValue()==null?StrUtil.EMPTY:dynamicD.getSParamConfigValue()); + //获取动态参数 + dynamicData.add(dynamicOne); + }); + sAll.addAll(dynamicData); + part.put("dynamicData",dynamicData); + } + } + partList.add(part); + }); + } + }); + } + } + sAll.addAll(productList); + sAll.addAll(partList); + retData.put("sAll",sAll); + retData.put("product",productList); + retData.put("partList",partList); + return retData; + } + +} \ No newline at end of file diff --git a/src/main/java/com/xly/service/XlyErpService.java b/src/main/java/com/xly/service/XlyErpService.java index 7afa87c..89db558 100644 --- a/src/main/java/com/xly/service/XlyErpService.java +++ b/src/main/java/com/xly/service/XlyErpService.java @@ -311,6 +311,7 @@ public class XlyErpService { String sceneName = StrUtil.EMPTY; String methodName = StrUtil.EMPTY; UserSceneSession session=null; + String sReturnType = ReturnTypeCode.HTML.getCode(); try { // 0. 预处理用户输入:去空格、转小写(方便匹配) String input= InputPreprocessor.preprocessWithCommons(userInput); @@ -363,7 +364,9 @@ public class XlyErpService { aiAgent = createConfirmeAgent(session); } //存在动态参数需要重新定义引导语 - if(ObjectUtil.isNotEmpty(session.getCurrentTool())&&ObjectUtil.isNotEmpty(session.getCurrentTool().getDynamicParamRule())){ + if(ObjectUtil.isNotEmpty(session.getCurrentTool()) + && ObjectUtil.isNotEmpty(session.getCurrentTool().getDynamicParamRule()) + ){ sSystemPrompt = SystemPromptGenerator.generate(session.getCurrentTool().getSMethodName(),session); aiAgent = createConfirmeAgent(session); } @@ -376,9 +379,10 @@ public class XlyErpService { ){ input = session.getCurrentTool().getSMethodName()+","+input; } + sReturnType = (session!=null && ObjectUtil.isNotEmpty(session.getCurrentTool()) && session.getCurrentTool().getBQuo())?ReturnTypeCode.JSON.getCode():ReturnTypeCode.HTML.getCode(); //动态方法或返回需要提示的信息 if(ObjectUtil.isNotEmpty(session.getSFunPrompts())){ - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(session.getSFunPrompts()).sReturnType(ReturnTypeCode.HTML.getCode()).build(); + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(session.getSFunPrompts()).sReturnType(sReturnType).build(); } // 1.找到方法并且本方法带表结构描述时,需要调用 自然语言转SQL智能体 if((ObjectUtil.isNotEmpty(session.getCurrentTool()) @@ -408,7 +412,7 @@ public class XlyErpService { session.setDbType("G"); sResponMessage = getDynamicTableSql(session, input, userId, userInput,0,StrUtil.EMPTY,StrUtil.EMPTY,"0",StrUtil.EMPTY, aiAgent,sCleanSql); // } - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(sResponMessage).sReturnType(ReturnTypeCode.HTML.getCode()).dbType(session.getDbType()).dbCach(session.getDbCach()).build(); + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(sResponMessage).sReturnType(sReturnType).dbType(session.getDbType()).dbCach(session.getDbCach()).build(); } else if (ObjectUtil.isNotEmpty(session.getCurrentTool())) { //2.处理工具参数采集结束后业务逻辑处理 //调用方法,参数缺失部分提示,就直接使用方法返回的 @@ -420,16 +424,16 @@ public class XlyErpService { .sCopyToSrcId(session.getSCopyToSrcId()) .dbType(session.getDbType()) .dbCach(session.getDbCach()) - .sReturnType(ReturnTypeCode.HTML.getCode()) + .sReturnType(sReturnType) .build(); }else if(session.getCurrentScene()== null ){ - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText("当前场景:没有选择 退回当前场景 请输入 "+ CommonConstant.RESET + sResponMessage).dbType(session.getDbType()).dbCach(session.getDbCach()).sReturnType(ReturnTypeCode.HTML.getCode()).build(); + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText("当前场景:没有选择 退回当前场景 请输入 "+ CommonConstant.RESET + sResponMessage).dbType(session.getDbType()).dbCach(session.getDbCach()).sReturnType(sReturnType).build(); }else{ return getChatiAgent (input, session); } } catch (Exception e) { e.printStackTrace(); - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText("系统异常:" + e.getMessage() + ",请稍后重试!").dbType(session.getDbType()).dbCach(session.getDbCach()).sReturnType(ReturnTypeCode.HTML.getCode()).build(); + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText("系统异常:" + e.getMessage() + ",请稍后重试!").dbType(session.getDbType()).dbCach(session.getDbCach()).sReturnType(sReturnType).build(); }finally { //5.执行工具方法后,清除记忆 if(session !=null && session.getBCleanMemory()){ diff --git a/src/main/java/com/xly/tool/DynamicToolProvider.java b/src/main/java/com/xly/tool/DynamicToolProvider.java index 198a42b..6224da1 100644 --- a/src/main/java/com/xly/tool/DynamicToolProvider.java +++ b/src/main/java/com/xly/tool/DynamicToolProvider.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.xly.agent.SystemPromptGenerator; import com.xly.config.OperableChatMemoryProvider; import com.xly.constant.*; import com.xly.entity.*; @@ -20,6 +21,7 @@ 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.QuoService; import com.xly.service.UserSceneSessionService; import com.xly.util.*; import dev.langchain4j.agent.tool.*; @@ -52,6 +54,7 @@ public class DynamicToolProvider implements ToolProvider { private final ObjectMapper objectMapper; private final ToolMetaMapper toolMetaMapper; private final ParamRuleMapper paramRuleMapper; + private final QuoService quoService; private final DynamicExeDbService dynamicExeDbService; private final OperableChatMemoryProvider operableChatMemoryProvider; @@ -186,9 +189,9 @@ public class DynamicToolProvider implements ToolProvider { //需要移除的老的参数 List data = meta.getParamRuleList().stream().filter( one-> one.getSParam().equals(k) - && ObjectUtil.isNotEmpty(v) - && !"0".equals(v) - && !"\"\"".equals(v) + && ObjectUtil.isNotEmpty(v) + && !"0".equals(v) + && !"\"\"".equals(v) ).collect(Collectors.toUnmodifiableList()); if(ObjectUtil.isNotEmpty(data) && data.size()>0){ finalArgs.remove(data.get(0).getSParamValue()); @@ -205,7 +208,6 @@ public class DynamicToolProvider implements ToolProvider { log.error("返回信息",e); String askMsg = e.getMessage(); session.setSFunPrompts(askMsg); - addSystemMessage(session, askMsg); // 需要提问用户 → 抛异常停止循环 throw new RuntimeException(askMsg); } @@ -215,35 +217,36 @@ public class DynamicToolProvider implements ToolProvider { //存入已采集参数 session.setArgs(args); // 2.1.1 根据当前获取的新参数判断是否存在有值必填例如动态参数、动态部件 - List dynamicParamRule = doSetParamgetArgsAfter(args, session); - if(ObjectUtil.isNotEmpty(dynamicParamRule)){ - String askMsg = hasDynamicParamRule(argsNew, session,dynamicParamRule); - session.getCurrentTool().setDynamicParamRule(dynamicParamRule); - //此处修改动态参数 - session.setSFunPrompts(askMsg); - addSystemMessage(session, askMsg); - // 需要提问用户 → 抛异常停止循环 - return String.valueOf(successResult(toolExecutionRequest, askMsg)); + if(meta.getBQuo()){ + List dynamicParamRule = doSetParamgetArgsAfter(args, session); + if(ObjectUtil.isNotEmpty(dynamicParamRule)){ + Map data = quoService.getQuoData( argsNew, session, dynamicParamRule); + data.remove("sAll"); + data.put("bTs",true); + String askMsg = JSONObject.toJSONString(data); + session.setSFunPrompts(askMsg); + String sSystemPrompt = SystemPromptGenerator.buildMissParamPrompt(session,dynamicParamRule); + session.setSSystemPrompt(sSystemPrompt); + // 需要提问用户 → 抛异常停止循环 + return String.valueOf(successResult(toolExecutionRequest, askMsg)); + } } + //客户选择行号转sSLaveId if(ObjectUtil.isNotEmpty(args) &&( (args.containsKey("rowNumbers") && ObjectUtil.isNotEmpty(args.get("rowNumbers"))) - || (args.containsKey("operateType") && "全部确认".equals(args.get("operateType"))) - )&& ObjectUtil.isNotEmpty(session.getCurrentRowData()) + || (args.containsKey("operateType") && "全部确认".equals(args.get("operateType"))) + )&& ObjectUtil.isNotEmpty(session.getCurrentRowData()) ){ doSetSlaveIdToArgs( args, session); } //存入已采集参数 session.setArgs(args); - // 3. 【自动校验】检查必填项 - List missing = checkRequiredParams(args, paramRuleData); - if (!missing.isEmpty()) { - // 4.1 参数缺失,生成“提问”消息,直接返给客户 - String askMsg = buildAskUserMessage(meta, missing,args); - session.setSFunPrompts(askMsg); - addSystemMessage(session, askMsg); - return String.valueOf(successResult(toolExecutionRequest, askMsg)); + //获取是否缺失参数 + String askMsg = getMissParamMsg( meta, session, args); + if(ObjectUtil.isNotEmpty(askMsg)){ + return askMsg; } // ====================== 返回时带终止指令 ====================== String resp = JSONUtil.toJsonStr(args) ; @@ -271,7 +274,7 @@ public class DynamicToolProvider implements ToolProvider { List paramRuleData = tool.getParamRuleListAll(); paramRuleData = paramRuleData.stream().filter(one-> ( ObjectUtil.isNotEmpty(args.get(one.getSParam())) ||ObjectUtil.isNotEmpty(args.get(one.getSParamValue()))) - && ObjectUtil.isNotEmpty(one.getSDynamicParamRuleSql()) + && ObjectUtil.isNotEmpty(one.getSDynamicParamRuleSql()) ).collect(Collectors.toUnmodifiableList()); List dynamicParamRule = new ArrayList<>(); //为空的时候需要重新插入 不为空 表示之前已经放过了 @@ -283,9 +286,9 @@ public class DynamicToolProvider implements ToolProvider { searMap.putAll(args); //新参数如果值变了移除掉之前插入的 List paramRuleDataAllNew = paramRuleDataAll.get().stream().filter(d-> - !one.getSParam().equals(d.getSDynamicParam()) - ||(one.getSParam().equals(d.getSDynamicParam()) && one.getSParamValue().equals(d.getSParamValue())) - ).collect(Collectors.toUnmodifiableList()); + !one.getSParam().equals(d.getSDynamicParam()) + ||(one.getSParam().equals(d.getSDynamicParam()) && one.getSParamValue().equals(d.getSParamValue())) + ).collect(Collectors.toUnmodifiableList()); List paramRuleList = tool.getParamRuleList().stream().filter(d-> !one.getSParam().equals(d.getSDynamicParam()) ||(one.getSParam().equals(d.getSDynamicParam()) && one.getSParamValue().equals(d.getSParamValue())) @@ -334,7 +337,6 @@ public class DynamicToolProvider implements ToolProvider { } }else{ //表示过程走自定义参数 - } if(ObjectUtil.isNotEmpty(dynamicParamRuleOne)){ //转换成BEAN @@ -407,34 +409,6 @@ public class DynamicToolProvider implements ToolProvider { .stream() .collect(Collectors.toList()); } - /*** - * @Author 钱豹 - * @Date 23:35 2026/6/28 - * @Param [argsNew, session] - * @return java.util.List - * @Description 存在动态参数返回提示 - **/ - private String hasDynamicParamRule(Map argsNew,UserSceneSession session,List dynamicParamRule){ - //获取当前方法 - ToolMeta meta = session.getCurrentTool(); - meta.setDynamicParamRule(dynamicParamRule); - session.setCurrentTool(meta); - Map argsOld = session.getArgs(); - Map args = DeepCopyUtils.deepCopy(argsNew); - if(ObjectUtil.isEmpty(argsOld)){ - argsOld = new HashMap<>(); - } - argsOld.putAll(args); - //根据获取的参数获取数据 - List dynamicParamRuleTs = dynamicParamRule.stream().map(ParamRule::getSParam).toList(); - session.setArgs(argsOld); - String askMsg = buildAskUserMessage(meta, dynamicParamRuleTs,args); - session.setSFunPrompts(askMsg); - String sSystemPrompt = buildMissParamPrompt(session,dynamicParamRule); - session.setSSystemPrompt(sSystemPrompt); - addSystemMessage(session, askMsg); - return askMsg; - } private void doSetSlaveIdToArgs(Map args,UserSceneSession session){ @@ -545,14 +519,6 @@ public class DynamicToolProvider implements ToolProvider { 2. 只要方法返回结果,必须停止调用 3. 当用户输入包含【数据确认】、确认数据、确认、第*条数据确认时,必须调用本工具 """; -// String forceToolPrompt = """ -// 【工具调用规则】 -// 1. 每个自定义方法最多只能调用 1 次 -// 2. 只要方法返回结果,必须停止调用 -// 3. 禁止重复调用同一个方法 -// 4. 禁止无意义循环调用 -// 5. 当用户输入包含【数据确认】、确认数据、确认、第*条数据确认时,必须调用本工具 -// """; stoolDesc.append(forceToolPrompt); if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) { stoolDesc.append("MethodNo:").append(meta.getSMethodNo()) @@ -661,6 +627,8 @@ public class DynamicToolProvider implements ToolProvider { }); sb.delete(sb.length()-1,sb.length()); paramRule.setSRuleTs(sb.toString()); + //存入动态配置 + paramRule.setSParamConfigValue(constMap); return sb.toString(); } return StrUtil.EMPTY; @@ -678,6 +646,7 @@ public class DynamicToolProvider implements ToolProvider { if(ObjectUtil.isEmpty(data)){ return StrUtil.EMPTY; } + if(data.get(0).containsKey("sGroupName")){ Map>> groupData = data.stream() .collect(Collectors.groupingBy(map -> map.get("sGroupName"))); @@ -691,12 +660,16 @@ public class DynamicToolProvider implements ToolProvider { sb.append(","); } }); + //放入动态配置值 + paramRule.setSParamConfigValue(groupData); }else{ data.forEach(one->{ if(ObjectUtil.isNotEmpty(one.get(paramRule.getSParamValue()))){ sb.append(one.get(paramRule.getSParamValue())).append("/"); } }); + //放入动态配置值 + paramRule.setSParamConfigValue(data); } if(ObjectUtil.isNotEmpty(sb)){ sb.delete(sb.length()-1,sb.length()); @@ -712,22 +685,47 @@ public class DynamicToolProvider implements ToolProvider { return paramRuleDataAll; } + //校验是否缺失参数 + private String getMissParamMsg(ToolMeta meta,UserSceneSession session, Map args){ + if(meta.getBQuo()){ + List dataParamDynamic = session.getCurrentTool().getParamRuleListAll().stream().filter(d -> + d.getBDynamicParam()).collect(Collectors.toUnmodifiableList()); + Map data = quoService.getQuoData(args, session, dataParamDynamic); + List> sAll = (List>) data.get("sAll"); + List> sEmpty = sAll.stream().filter(one-> BooleanUtil.toBoolean(one.get("bEmpty").toString()) + && (ObjectUtil.isEmpty(one.get("sValue")) || "0".equals(one.get("sValue")))).collect(Collectors.toUnmodifiableList()); + if(ObjectUtil.isNotEmpty(sEmpty)){ + data.remove("sAll"); + data.put("bTs",true); + String askMsg = JSONObject.toJSONString(data); + session.setSFunPrompts(askMsg); + String sSystemPrompt = SystemPromptGenerator.buildMissParamPrompt(session,dataParamDynamic); + session.setSSystemPrompt(sSystemPrompt); + // 需要提问用户 → 抛异常停止循环 + return askMsg; + } + }else{ + List missing = checkRequiredParams(args, meta.getParamRuleListCheck()); + if (!missing.isEmpty()) { + String askMsg = buildAskUserMessage(meta, missing,args); + return askMsg; + } + } + return null; + } public String doDynamicTool(ToolMeta meta,UserSceneSession session) { List paramRuleData = meta.getParamRuleListAll(); - List paramRuleDataCheck = meta.getParamRuleListCheck(); Map argsOld = session.getArgs(); if(ObjectUtil.isEmpty(argsOld)){ argsOld = new HashMap<>(); } Map args = session.getArgs(); - List missing = checkRequiredParams(args, paramRuleDataCheck); - if (!missing.isEmpty()) { - String askMsg = buildAskUserMessage(meta, missing,args); - addSystemMessage(session, askMsg); + //获取是否缺失参数 + String askMsg = getMissParamMsg( meta, session, args); + if(ObjectUtil.isNotEmpty(askMsg)){ return askMsg; } - List chatMessage = operableChatMemoryProvider.getCurrentChatMessages(session.getUserId()); ChatMessage userMessage = getLastUserMessage(chatMessage); String input = ""; @@ -750,12 +748,11 @@ public class DynamicToolProvider implements ToolProvider { //合并已选择数据 argsOld.putAll(args); session.setArgs(argsOld); - String askMsg = buildAskUserMessage(meta, missingAfter,args); + askMsg = buildAskUserMessage(meta, missingAfter,args); session.setSFunPrompts(askMsg); List paramRuleDataMiss = getMissParamRuleAfter(args, paramRuleData); - String sSystemPrompt = buildMissParamPrompt(session,paramRuleDataMiss); + String sSystemPrompt = SystemPromptGenerator.buildMissParamPrompt(session,paramRuleDataMiss); session.setSSystemPrompt(sSystemPrompt); - addSystemMessage(session, askMsg); return askMsg; } return executeTool(meta, args, paramRuleData, session.getUserId(), session); @@ -764,7 +761,7 @@ public class DynamicToolProvider implements ToolProvider { String askconfirmMsg =StrUtil.EMPTY; if(4== meta.getIBizType() || meta.getIBizType()==5){ askconfirmMsg = doGetFromData( meta,args,session); - String sSystemPrompt = buildDynamicSystemPrompt(session); + String sSystemPrompt = SystemPromptGenerator.buildDynamicSystemPrompt(session); session.setSSystemPrompt(sSystemPrompt); return executeWithConfirmation(askconfirmMsg, session, meta); }else{ @@ -961,9 +958,9 @@ public class DynamicToolProvider implements ToolProvider { Map returnMap = transformationArgs( args, paramDefs); List paramDefsCheck = paramDefs.stream() .filter(pd -> !"sDynamicPart".equals(pd.getSParamValue())).collect(Collectors.toUnmodifiableList()); - paramDefsCheck = paramDefsCheck.stream() + paramDefsCheck = paramDefsCheck.stream() .filter(pd -> Boolean.TRUE.equals(pd.getBEmpty()) && pd.getBTipModel()).collect(Collectors.toUnmodifiableList()); - paramDefsCheck = paramDefsCheck.stream() + paramDefsCheck = paramDefsCheck.stream() .filter(pd -> checkMiss( returnMap, pd)).collect(Collectors.toUnmodifiableList()); return paramDefsCheck.stream().map(ParamRule::getSParam) .toList(); @@ -1024,10 +1021,10 @@ public class DynamicToolProvider implements ToolProvider { private List getMissParamRuleAfter(Map args, List paramDefs) { Map returnMap = transformationArgs( args, paramDefs); return paramDefs.stream() - .filter(pd -> ObjectUtil.isNotEmpty(pd.getBConfirmAfter()) && BooleanUtil.toBoolean(pd.getBConfirmAfter().toString())) - .filter(pd -> (!returnMap.containsKey(pd.getSParam()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParam())))) + .filter(pd -> ObjectUtil.isNotEmpty(pd.getBConfirmAfter()) && BooleanUtil.toBoolean(pd.getBConfirmAfter().toString())) + .filter(pd -> (!returnMap.containsKey(pd.getSParam()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParam())))) && (!returnMap.containsKey(pd.getSParamValue()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParamValue())))) - ) + ) .toList(); } @@ -1050,14 +1047,7 @@ public class DynamicToolProvider implements ToolProvider { return sReturn; } - //参数进行处理 - private void doArgs(ToolMeta meta, Map args,List paramDefs) { - List paramDefsD = paramDefs.stream().filter(one-> ObjectUtil.isNotEmpty(one.getSDynamicParamRuleSql())).collect(Collectors.toUnmodifiableList()); - - - - } - + //执行工具后 private String executeToolAfter(ToolMeta meta, Map args,List paramDefs,UserSceneSession session) { String sBizContent = meta.getSBizContent(); Integer iBizType = meta.getIBizType(); @@ -1084,7 +1074,6 @@ public class DynamicToolProvider implements ToolProvider { sessA.remove("sSlaveId"); sessA.remove("operateType"); session.setArgs(sessA); - addSystemMessage(session, sMsg); throw new BusinessException(-1,sMsg); } List> sRowData = doGetFromDataWq( meta, args, session); @@ -1095,8 +1084,6 @@ public class DynamicToolProvider implements ToolProvider { sessA.remove("sSlaveId"); sessA.remove("operateType"); session.setArgs(sessA); - //插入AI记忆 - addSystemMessage(session, sMsg); throw new BusinessException(-1,sMsg); } Map dataOne = DeepCopyUtils.deepCopy(args); @@ -1150,7 +1137,6 @@ public class DynamicToolProvider implements ToolProvider { } else { String sMsgText = "未找到对应的数据"; session.setSFunPrompts(sMsgText); - addSystemMessage(session, sMsgText); throw new BusinessException(-1,sMsgText); } } @@ -1161,12 +1147,6 @@ public class DynamicToolProvider implements ToolProvider { return "操作成功"; } - private void addSystemMessage(UserSceneSession session,String sMsg){ -// if(ObjectUtil.isNotEmpty(operableChatMemoryProvider) && ObjectUtil.isNotEmpty(operableChatMemoryProvider.get(session.getUserId()))){ -// operableChatMemoryProvider.get(session.getUserId()).add(SystemMessage.from("上一次工具调用失败。原因:" + sMsg + "。请根据用户意图修正参数,并再次调用工具。")); -// } - } - private List> doGetFromDataWq(ToolMeta meta, Map args,UserSceneSession session){ String sUrl = meta.getSendUrl(); Map sBody = new HashMap<>(); @@ -1367,103 +1347,6 @@ public class DynamicToolProvider implements ToolProvider { return markdown.toString(); } - - /** - * 缺失参数统一提示模板:强制AI调用自定义方法,一次性回填所有参数 - */ - public String buildMissParamPrompt(UserSceneSession session, List paramRuleDataMiss) { - String methodNo = session.getCurrentTool().getSMethodNo(); - // 1. 拼接缺失参数的描述(增加“参数名”和“英文名”的对应,方便模型映射) - String paramDesc = paramRuleDataMiss.stream() - .map(p -> String.format("- 参数名:%s (对应字段: %s),示例值:%s", - p.getSParam(), p.getSParamValue(), p.getSExampleValue())) - .collect(Collectors.joining("\n")); - // 但根据你的需求,其实 prompt 里并不需要强行插入一个空的 JSON 片段, - // 直接告诉模型去“合并”即可。 - return String.format(""" - 请你根据用户最新的输入,补充缺失的参数,并带着【所有完整参数】立即发起工具调用。 - - 【核心执行规则】 - 1. **参数合并(至关重要)**:你必须保留上一轮对话中已经获取的参数(如 sSlaveId, operateType 等),并将用户本次提供的新参数与旧参数合并。 - 2. **必须调用工具**:必须调用工具 `%s`。绝对禁止直接输出 JSON 文本,也绝对禁止向用户反问或索要信息。 - 3. **完整调用**:请直接生成包含所有参数的 tool_calls 请求。 - 【已获取数据】 - %s - 【当前缺失的参数说明】 - %s - """, methodNo,JSONObject.toJSONString(session.getArgs()), paramDesc); - } - - public String buildDynamicSystemPrompt(UserSceneSession session) { - // 获取当前工具编号(用于 JSON 的 name 字段) - String methodNo = session.getCurrentTool().getSMethodNo(); - return """ - 【极强约束·必须执行】 - 1. 禁止说话!禁止解释! - 2. 必须调用工具! - 3. 只输出标准工具调用JSON! - - ### 任务背景 ### - 用户会输入自然语言指令(如“第1行确认”、“全部合并”等)。 - 你的任务是:解析出【操作类型】和【目标行号】。 - - ### 解析规则(必须严格遵守) ### - - 1. **操作类型 (operateType)**: - - 如果用户说“全部确认”、“生成多个单据”,识别为:"全部确认" - - 如果用户说“合并确认”、“生成一个单据”,识别为:"合并确认" - - 其他情况,默认为:"单行确认" - - 2. **行号提取 (rowNumbers)**: - - 单选:如“第5行”,提取为 [5] - - 多选:如“第1、3、5行”,提取为 [1, 3, 5] - - 范围:如“第2到4行”,提取为 [2, 3, 4] - - 全选:如“全部”、“所有”,提取为 ["ALL"] - - ### 输出格式规范 ### - 请直接输出以下 JSON(不要任何 Markdown 格式,不要 ```json): - {"name": "%s", "parameters": {"operateType": "解析出的类型", "rowNumbers": [行号数组或"ALL"]}} - - """.formatted(methodNo); - } -// public String buildDynamicSystemPrompt(UserSceneSession session) { -// // 获取当前工具编号 -// String methodNo = session.getCurrentTool().getSMethodNo(); -// // 格式化表格数据(保持缩进,方便模型阅读) -// String rowJson = JSONUtil.toJsonPrettyStr(session.getCurrentRowData()); -// -// return """ -// 【极强约束·必须执行】 -// 1. 禁止说话!禁止解释! -// 2. 必须调用工具! -// 3. 只输出标准工具调用JSON! -// -// ### 任务背景 ### -// 用户会输入类似“第5行确认”或“确认第3条”的指令。 -// 你的任务是:解析行号 -> 在下方表格中找到对应行 -> 提取 sSlaveId -> 调用工具。 -// -// ### 数据源(表格 key 是行号) ### -// %s -// -// ### 防错黄金法则(必须严格遵守) ### -// 1. **查找阶段**:不要凭空编造 ID。必须先在“思考步骤”中明确写出: -// “用户输入是[xx],提取行号[N] -> 查找表格 Key=[N] -> 对应的 sSlaveId 是 [这里填入表格里的完整数字]” -// 2. **复制阶段**:sSlaveId 是一长串数字,请务必**直接复制**表格中的原始值,不要加省略号(...),不要改变数字。 -// 3. **输出阶段**:只有确认复制无误后,才在 JSON 的 sSlaveId 字段中填入该值。 -// -// ### 输出格式规范 ### -// {"name": "%s", "parameters": {"operateType": "单行确认", "sSlaveId": "在这里填入你从表格中复制的ID"}} -// -// ### 执行流程(一步一步来) ### -// 1. **解析**:阅读用户输入,确定他想操作的是第几行(N)。 -// 2. **思考**:在回复的第一行,写出你的查找过程(例如:用户要求第5行...)。 -// 3. **JSON**:换行后,输出最终的工具调用 JSON。 -// -// 【警告】 -// 如果用户要求的行号在表格中不存在,请返回错误信息,不要调用工具。 -// """.formatted(rowJson, methodNo); -// } - private List> findFieldNameByChinese(List> sAIshowfieldShow,List> rows){ Map keyMappings = new HashMap<>(); List selectedKeys = new ArrayList<>(); @@ -1596,9 +1479,9 @@ public class DynamicToolProvider implements ToolProvider { return check || userResponse.contains("生成") || userResponse.contains("确认") || (ObjectUtil.isNotEmpty(session.getArgs()) - && session.getArgs().containsKey("operateType") - && session.getArgs().containsKey("sSlaveId") - && ObjectUtil.isNotEmpty(session.getArgs().get("sSlaveId")) - ); + && session.getArgs().containsKey("operateType") + && session.getArgs().containsKey("sSlaveId") + && ObjectUtil.isNotEmpty(session.getArgs().get("sSlaveId")) + ); } } \ No newline at end of file -- libgit2 0.22.2