Commit be203b191bd153f35aae8679f31f88abef5e020b

Authored by qianbao
1 parent 1599dfd4

添加未清选择 改成动态引导语

src/main/java/com/xly/agent/DynamicPartAiAgent.java 0 → 100644
  1 +package com.xly.agent;
  2 +
  3 +import dev.langchain4j.service.*;
  4 +
  5 +public interface DynamicPartAiAgent {
  6 +
  7 + /***
  8 + * @Author 钱豹
  9 + * @Date 12:52 2026/7/30
  10 + * @Param [userId, userInput, sSystemPrompt]
  11 + * @return dev.langchain4j.service.Result<java.lang.String>
  12 + * @Description 动态部件构造器
  13 + **/
  14 + @SystemMessage("{{sSystemPrompt}}")
  15 + @UserMessage("用户输入:{{userInput}}")
  16 + Result<String> getDynamicPartMethod(
  17 + @MemoryId String userId,
  18 + @V("userInput") String userInput,
  19 + @V("sSystemPrompt") String sSystemPrompt
  20 + );
  21 +}
0 \ No newline at end of file 22 \ No newline at end of file
src/main/java/com/xly/agent/SystemPromptGenerator.java
1 package com.xly.agent; 1 package com.xly.agent;
2 2
3 import cn.hutool.core.util.ObjectUtil; 3 import cn.hutool.core.util.ObjectUtil;
  4 +import cn.hutool.json.JSONUtil;
4 import com.alibaba.fastjson2.JSON; 5 import com.alibaba.fastjson2.JSON;
5 import com.alibaba.fastjson2.JSONObject; 6 import com.alibaba.fastjson2.JSONObject;
6 import com.alibaba.fastjson2.JSONWriter; 7 import com.alibaba.fastjson2.JSONWriter;
@@ -8,6 +9,7 @@ import com.xly.entity.ParamRule; @@ -8,6 +9,7 @@ import com.xly.entity.ParamRule;
8 import com.xly.entity.ToolMeta; 9 import com.xly.entity.ToolMeta;
9 import com.xly.entity.UserSceneSession; 10 import com.xly.entity.UserSceneSession;
10 11
  12 +import java.util.ArrayList;
11 import java.util.List; 13 import java.util.List;
12 import java.util.stream.Collectors; 14 import java.util.stream.Collectors;
13 15
@@ -166,5 +168,35 @@ public class SystemPromptGenerator { @@ -166,5 +168,35 @@ public class SystemPromptGenerator {
166 168
167 return sb.toString(); 169 return sb.toString();
168 } 170 }
169 - 171 + /***
  172 + * @Author 钱豹
  173 + * @Date 21:59 2026/7/30
  174 + * @Param [partList]
  175 + * @return java.lang.String
  176 + * @Description 动态部件获取提示【印刷行业|增强校验版】
  177 + **/
  178 + public static String generateChoicePartPrompt(List<String> partList) {
  179 + StringBuilder sb = new StringBuilder();
  180 + sb.append("你是印刷行业部件提取工具。你的职责是理解印刷工艺需求文本,筛选部件名称,内文就是内页。\n\n");
  181 + sb.append("【执行顺序&强制规则(严格按顺序执行)】\n");
  182 + sb.append("第一步:逐条遍历【部件列表】内所有名称,逐一核对用户文本,收集所有匹配部件,不得跳过任何列表项。\n");
  183 + sb.append("第二步:处理名称转换:识别到“内文”统一替换为“内页”,最终结果禁止出现\"内文\"\n");
  184 + sb.append("第三步:扩展提取:扫描全文,如果存在印刷书刊部件名词,附带材质、克重、P数、工艺等描述,但该名词不在【部件列表】中,追加该部件名称;无配套描述则不追加。\n\n");
  185 + sb.append("【任务】\n");
  186 + sb.append("阅读用户印刷需求输入,提取所有符合规则的部件名称,全部输出,一个都不能少。\n");
  187 + sb.append("【输出要求】\n");
  188 + sb.append("- 只输出一个 JSON 数组,如:[\"封面\",\"内页\",\"拉页\"]\n");
  189 + sb.append("- 如果用户输入中明确提到了某个部件,即使措辞略有不同,也要输出匹配的列表项\n");
  190 + sb.append("- 如果没有任何部件匹配,输出 \"none\"\n");
  191 + sb.append("- 不要输出解释、思考过程或 JSON 以外的任何文字。\n\n");
  192 + sb.append("【部件列表】\n");
  193 + sb.append(JSONUtil.toJsonStr(partList));
  194 + sb.append("\n\n【印刷业务示例】\n");
  195 + sb.append("- 用户输入:封面工艺:烫金,内文用纸80g双胶 → [\"封面\",\"内页\"]\n");
  196 + sb.append("- 用户输入:内文250g铜版纸,插页8P、157g哑粉,封面157g哑粉烫金 → [\"内页\",\"封面\",\"插页\"]\n");
  197 + sb.append("- 用户输入:护封材质特种纸,夹页4色印刷 → [\"护封\",\"夹页\"]\n");
  198 + sb.append("- 用户输入\"你好\" → \"none\"\n");
  199 + sb.append("\n**重要**:务必逐条核对部件列表,禁止遗漏列表内匹配项;不要受示例限制。\n");
  200 + return sb.toString();
  201 + }
170 } 202 }
171 \ No newline at end of file 203 \ No newline at end of file
src/main/java/com/xly/config/ModelConfig.java
@@ -4,10 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; @@ -4,10 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
4 import com.fasterxml.jackson.databind.ObjectMapper; 4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import com.fasterxml.jackson.databind.SerializationFeature; 5 import com.fasterxml.jackson.databind.SerializationFeature;
6 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
7 -import com.xly.agent.ChatiAgent;  
8 -import com.xly.agent.DynamicTableNl2SqlAiAgent;  
9 -import com.xly.agent.SceneSelectorAiAgent;  
10 -import com.xly.agent.SecMethodAiAgent; 7 +import com.xly.agent.*;
11 import dev.langchain4j.memory.chat.MessageWindowChatMemory; 8 import dev.langchain4j.memory.chat.MessageWindowChatMemory;
12 import dev.langchain4j.model.ollama.OllamaChatModel; 9 import dev.langchain4j.model.ollama.OllamaChatModel;
13 import dev.langchain4j.model.ollama.OllamaStreamingChatModel; 10 import dev.langchain4j.model.ollama.OllamaStreamingChatModel;
@@ -188,4 +185,19 @@ public class ModelConfig { @@ -188,4 +185,19 @@ public class ModelConfig {
188 .maxSequentialToolsInvocations(1) 185 .maxSequentialToolsInvocations(1)
189 .build(); 186 .build();
190 } 187 }
  188 +
  189 + /**
  190 + * 方法选择 Agent(二级路由):从数据中获取到对应的 添加的部件名称,删除的部件名称
  191 + * 分类任务,用极短记忆窗口避免被上文惯性带偏。
  192 + */
  193 + @Bean
  194 + public DynamicPartAiAgent dynamicPartAiAgent(
  195 + @Qualifier("chatLanguageModel") OllamaChatModel chatLanguageModel) {
  196 + return AiServices.builder(DynamicPartAiAgent.class)
  197 + .chatModel(chatLanguageModel)
  198 + .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(2))
  199 + .maxSequentialToolsInvocations(1)
  200 + .build();
  201 + }
  202 +
191 } 203 }
192 \ No newline at end of file 204 \ No newline at end of file
src/main/java/com/xly/entity/SceneDto.java
@@ -42,8 +42,6 @@ public class SceneDto { @@ -42,8 +42,6 @@ public class SceneDto {
42 42
43 private String sSceneContext; 43 private String sSceneContext;
44 44
45 - private String sKnowledgeBase;  
46 -  
47 private String sStatus; 45 private String sStatus;
48 46
49 47
src/main/java/com/xly/entity/ToolMeta.java
@@ -56,4 +56,7 @@ public class ToolMeta { @@ -56,4 +56,7 @@ public class ToolMeta {
56 private Boolean sQue = false; 56 private Boolean sQue = false;
57 private Boolean bQuo = false; 57 private Boolean bQuo = false;
58 58
  59 + private String sKnowledgeBase;
  60 + private String sdynamicMemo;
  61 +
59 } 62 }
src/main/java/com/xly/entity/UserSceneSession.java
@@ -2,6 +2,7 @@ package com.xly.entity; @@ -2,6 +2,7 @@ package com.xly.entity;
2 2
3 3
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 import lombok.Data; 6 import lombok.Data;
6 7
7 import java.util.List; 8 import java.util.List;
@@ -36,6 +37,9 @@ public class UserSceneSession { @@ -36,6 +37,9 @@ public class UserSceneSession {
36 private List<String> sUserQuestionList; 37 private List<String> sUserQuestionList;
37 38
38 private List<ToolMeta> authTool; 39 private List<ToolMeta> authTool;
  40 +
  41 + //需要确认的动态部件
  42 + private List<String> dynamicPartList;
39 /** 43 /**
40 * 是否已选择场景(true=已选,进入专属场景交互;false=未选,先展示选择界面) 44 * 是否已选择场景(true=已选,进入专属场景交互;false=未选,先展示选择界面)
41 */ 45 */
@@ -119,6 +123,10 @@ public class UserSceneSession { @@ -119,6 +123,10 @@ public class UserSceneSession {
119 **/ 123 **/
120 private Boolean toolExecuted=false; 124 private Boolean toolExecuted=false;
121 125
  126 + private Boolean bConfirmedDynamicPart =false;
  127 + private Boolean bConfirmedDynamicPartFist =false;
  128 + private String sDynamicPartUp= StrUtil.EMPTY;
  129 +
122 /** 130 /**
123 * 构建场景选择提示语:展示权限内场景,引导用户选择 131 * 构建场景选择提示语:展示权限内场景,引导用户选择
124 * @return 自然语言提示语 132 * @return 自然语言提示语
src/main/java/com/xly/mapper/SceneMapper.java
@@ -11,6 +11,6 @@ import java.util.List; @@ -11,6 +11,6 @@ import java.util.List;
11 public interface SceneMapper { 11 public interface SceneMapper {
12 12
13 // XML配置方式 13 // XML配置方式
14 - @Select("SELECT A.*,fun_ai_getsKnowledgeBase(A.sId) AS sKnowledgeBase FROM ai_agent AS A order by iOrder") 14 + @Select("SELECT A.* FROM ai_agent AS A order by iOrder")
15 List<SceneDto> findAll(); 15 List<SceneDto> findAll();
16 } 16 }
17 \ No newline at end of file 17 \ No newline at end of file
src/main/java/com/xly/mapper/ToolMetaMapper.java
@@ -13,6 +13,6 @@ import java.util.List; @@ -13,6 +13,6 @@ import java.util.List;
13 public interface ToolMetaMapper { 13 public interface ToolMetaMapper {
14 14
15 // XML配置方式 15 // XML配置方式
16 - @Select("SELECT A.* FROM ai_tool AS A order by iOrder") 16 + @Select("SELECT A.*,fun_ai_getsKnowledgeBase(A.sSceneId,A.sId) AS sKnowledgeBase FROM ai_tool AS A order by iOrder")
17 List<ToolMeta> findAll(); 17 List<ToolMeta> findAll();
18 } 18 }
19 \ No newline at end of file 19 \ No newline at end of file
src/main/java/com/xly/service/DynamicPartService.java 0 → 100644
  1 +package com.xly.service;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.hutool.json.JSONUtil;
  6 +import com.alibaba.fastjson2.JSONObject;
  7 +import com.xly.agent.DynamicPartAiAgent;
  8 +import com.xly.agent.SystemPromptGenerator;
  9 +import com.xly.entity.ParamRule;
  10 +import com.xly.entity.UserSceneSession;
  11 +import com.xly.tool.DynamicToolProvider;
  12 +import com.xly.util.PinyinUtil;
  13 +import com.xly.util.Validator;
  14 +import lombok.RequiredArgsConstructor;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +import org.springframework.beans.BeanUtils;
  17 +import org.springframework.stereotype.Service;
  18 +
  19 +import java.util.*;
  20 +import java.util.stream.Collectors;
  21 +
  22 +/***
  23 + * @Author 钱豹
  24 + * @Date 22:39 2026/1/30
  25 + * @Param
  26 + * @return
  27 + * @Description 动态部件提取类
  28 + **/
  29 +@Slf4j
  30 +@Service("dynamicPartService")
  31 +@RequiredArgsConstructor
  32 +public class DynamicPartService {
  33 + private final DynamicToolProvider dynamicToolProvider;
  34 +
  35 + private final DynamicPartAiAgent aiAgent;
  36 +
  37 + /***
  38 + * @Author 钱豹
  39 + * @Date 23:07 2026/7/29
  40 + * @Description AI识别获取方法(二级路由:在当前场景内选出一个 sMethodNo)
  41 + **/
  42 + public List<String> dynamicPartDTOChoiceResult(String userId, String userInput, UserSceneSession session) {
  43 + //判断输入
  44 + Boolean isConfirmed = dynamicToolProvider.isConfirmed(userInput,session);
  45 + if(ObjectUtil.isNotEmpty(session.getDynamicPartList())){
  46 + List<String> partList =session.getDynamicPartList();
  47 + List<String> partListBs = getPartBs(session);
  48 + // 移除 partList 中存在于 partListBs 的所有元素
  49 + partList.removeAll(partListBs);
  50 + // 找出 partList 中存在于 partListPz 的元素
  51 + List<String> result = new ArrayList<>(partList);
  52 + List<String> partListPz = getPartPz(session);
  53 + List<String> partListAdd = new ArrayList<>();
  54 + if(isConfirmed){
  55 + //方法添加动态参数
  56 + partListAdd.addAll(partList);
  57 + partList =null;
  58 + session.setBConfirmedDynamicPartFist(true);
  59 + }
  60 + if(!isConfirmed){
  61 + //添加再配置中的动态
  62 + result.retainAll(partListPz);
  63 + partListAdd.addAll(result);
  64 + partList =null;
  65 + session.setBConfirmedDynamicPartFist(true);
  66 + }
  67 + if(ObjectUtil.isNotEmpty(partListAdd)){
  68 + //部件添加数据
  69 + addToolMethod( partListAdd, session);
  70 + }
  71 + return partList;
  72 + }
  73 +
  74 + //如果存在动态部件
  75 + if(ObjectUtil.isEmpty(session.getCurrentTool())
  76 + || ObjectUtil.isEmpty(session.getCurrentTool().getSdynamicMemo())
  77 + || session.getBConfirmedDynamicPart()
  78 + ){
  79 + return null;
  80 + }
  81 +
  82 + List<String> partList = getPart(session);
  83 +
  84 + // 5. 生成系统提示词
  85 + String sSystemPrompt = SystemPromptGenerator.generateChoicePartPrompt(partList);
  86 +
  87 + // 3. 调大模型
  88 + String raw = aiAgent.getDynamicPartMethod(userId, userInput, sSystemPrompt).content();
  89 + log.debug("方法获取动态部件: userId={}, raw={}", userId, raw);
  90 +
  91 + // 4. 解析(含 ```json 容错)
  92 + List<String> result = parseChoice(raw);
  93 + if (result == null || ObjectUtil.isEmpty(result)) {
  94 + return null;
  95 + }
  96 + // 5. none / 阈值 / 白名单 三重校验
  97 + if ("none".equalsIgnoreCase(raw)) {
  98 + log.info("方法动态方法:无匹配部件, input={}", userInput);
  99 + return null;
  100 + }
  101 + session.setDynamicPartList(result);
  102 + return result;
  103 + }
  104 +
  105 + /***
  106 + * @Author 钱豹
  107 + * @Date 20:01 2026/7/30
  108 + * @Param [partListAdd, session]
  109 + * @return void
  110 + * @Description 动态方法 添加参数
  111 + **/
  112 + private void addToolMethod(List<String> partListAdd, UserSceneSession session) {
  113 + if (ObjectUtil.isEmpty(partListAdd)) {
  114 + return;
  115 + }
  116 + List<ParamRule> paramRuleDataAll = new ArrayList<>(session.getCurrentTool().getParamRuleListAll());
  117 + // 获取已有的动态部件名称,用于去重
  118 + Set<String> existingDynamicParts = new HashSet<>();
  119 + // 筛选标准参数(只筛选一次,提高性能)
  120 + List<ParamRule> standardParams = paramRuleDataAll.stream()
  121 + .filter(d -> ObjectUtil.isNotEmpty(d.getSGroup())
  122 + && !d.getBDynamicParam()
  123 + && "部件".equals(d.getSGroup())
  124 + && d.getBStandardParam())
  125 + .collect(Collectors.toList());
  126 +
  127 + if (ObjectUtil.isEmpty(standardParams)) {
  128 + log.warn("未找到部件标准参数,无法添加动态部件");
  129 + return;
  130 + }
  131 + for (String sVal : partListAdd) {
  132 + // 去重:如果该部件已存在,跳过
  133 + if (existingDynamicParts.contains(sVal)) {
  134 + log.debug("部件已存在,跳过: {}", sVal);
  135 + continue;
  136 + }
  137 + // 拼音转换
  138 + String firstLetters = "";
  139 + try {
  140 + firstLetters = PinyinUtil.convertToPinyin(sVal);
  141 + } catch (Exception e) {
  142 + log.warn("拼音转换失败: {}", sVal, e);
  143 + firstLetters = sVal.length() > 0 ? sVal.substring(0, 1).toUpperCase() : "";
  144 + }
  145 +
  146 + // 遍历标准参数,生成动态参数
  147 + for (ParamRule standard : standardParams) {
  148 + ParamRule copy = new ParamRule();
  149 + BeanUtils.copyProperties(standard, copy);
  150 +
  151 + // 解析参数名:取 "-" 后的部分
  152 + String paramName = standard.getSParam();
  153 + if (paramName != null && paramName.contains("-")) {
  154 + String[] parts = paramName.split("-");
  155 + paramName = parts.length > 1 ? parts[1] : parts[0];
  156 + }
  157 +
  158 + copy.setSParam(sVal + "-" + paramName);
  159 + copy.setSParamValue(firstLetters + standard.getSParamValue());
  160 + copy.setIOrder(standard.getIOrder() + 100000);
  161 + copy.setBDynamicParam(true);
  162 + copy.setSDynamicParam(sVal);
  163 + copy.setSDynamicParamName("sDynamicPart");
  164 +
  165 + paramRuleDataAll.add(copy);
  166 + }
  167 + // 记录已添加的部件
  168 + existingDynamicParts.add(sVal);
  169 + }
  170 +
  171 + // 重新筛选分类列表
  172 + List<ParamRule> paramRuleList = paramRuleDataAll.stream()
  173 + .filter(one -> Boolean.TRUE.equals(one.getBTipModel()))
  174 + .collect(Collectors.toList());
  175 +
  176 + List<ParamRule> paramRuleListCheck = paramRuleDataAll.stream()
  177 + .filter(one -> Boolean.TRUE.equals(one.getBEmpty()))
  178 + .collect(Collectors.toList());
  179 +
  180 + // 更新 session
  181 + session.getCurrentTool().setParamRuleListAll(paramRuleDataAll);
  182 + session.getCurrentTool().setParamRuleListCheck(paramRuleListCheck);
  183 + session.getCurrentTool().setParamRuleList(paramRuleList);
  184 +
  185 + // 更新 args
  186 + Map<String, Object> args = new HashMap<>();
  187 + if (ObjectUtil.isNotEmpty(session.getArgs())) {
  188 + args.putAll(session.getArgs());
  189 + }
  190 + args.put("sDynamicPart", JSONUtil.toJsonStr(partListAdd));
  191 + session.setArgs(args);
  192 + }
  193 +
  194 +
  195 +
  196 + /***
  197 + * @Author 钱豹
  198 + * @Date 18:13 2026/7/30
  199 + * @Param [session]
  200 + * @return java.util.List<java.lang.String>
  201 + * @Description 获取部件
  202 + **/
  203 + private List<String> getPart( UserSceneSession session){
  204 + // 1. 获取参数规则列表
  205 + List<ParamRule> paramRuleAll = session.getCurrentTool().getParamRuleList();
  206 + // 2. 从 ParamRule 中提取符合条件的部件名称
  207 + Set<String> partSet = new HashSet<>();
  208 + if (ObjectUtil.isNotEmpty(paramRuleAll)) {
  209 + for (ParamRule rule : paramRuleAll) {
  210 + String sGroup = rule.getSGroup();
  211 + String sParam = rule.getSParam();
  212 + if ("部件".equals(sGroup)
  213 + && !"动态部件".equals(sParam)
  214 + && !"删除动态部件".equals(sParam)) {
  215 + // 取 "-" 前的部分作为部件名称
  216 + String partName = sParam.contains("-") ? sParam.split("-")[0] : sParam;
  217 + partSet.add(partName);
  218 + }
  219 + }
  220 + }
  221 +
  222 + // 3. 从 sSdynamicMemo 中获取已保存的部件
  223 + String sSdynamicMemo = session.getCurrentTool().getSdynamicMemo();
  224 + Set<String> mergedSet = new LinkedHashSet<>();
  225 +
  226 + // 先加入 sSdynamicMemo 中的部件(保持原有顺序)
  227 + if (ObjectUtil.isNotEmpty(sSdynamicMemo)) {
  228 + String[] memoParts = sSdynamicMemo.split(",");
  229 + for (String part : memoParts) {
  230 + String trimmed = part.trim();
  231 + if (!trimmed.isEmpty()) {
  232 + mergedSet.add(trimmed);
  233 + }
  234 + }
  235 + }
  236 +
  237 + // 再加入从 ParamRule 提取的部件(自动去重)
  238 + mergedSet.addAll(partSet);
  239 +
  240 + // 4. 转为 List
  241 + List<String> partList = new ArrayList<>(mergedSet);
  242 + return partList;
  243 + }
  244 +
  245 + /***
  246 + * @Author 钱豹
  247 + * @Date 18:13 2026/7/30
  248 + * @Param [session]
  249 + * @return java.util.List<java.lang.String>
  250 + * @Description 获取部件
  251 + **/
  252 + private List<String> getPartBs( UserSceneSession session){
  253 + // 1. 获取参数规则列表
  254 + List<ParamRule> paramRuleAll = session.getCurrentTool().getParamRuleList();
  255 + // 2. 从 ParamRule 中提取符合条件的部件名称
  256 + Set<String> partSet = new HashSet<>();
  257 + if (ObjectUtil.isNotEmpty(paramRuleAll)) {
  258 + for (ParamRule rule : paramRuleAll) {
  259 + String sGroup = rule.getSGroup();
  260 + String sParam = rule.getSParam();
  261 + if ("部件".equals(sGroup)
  262 + && !"动态部件".equals(sParam)
  263 + && !"删除动态部件".equals(sParam)) {
  264 + // 取 "-" 前的部分作为部件名称
  265 + String partName = sParam.contains("-") ? sParam.split("-")[0] : sParam;
  266 + partSet.add(partName);
  267 + }
  268 + }
  269 + }
  270 + return new ArrayList<>(partSet);
  271 + }
  272 +
  273 + private List<String> getPartPz( UserSceneSession session){
  274 + String sSdynamicMemo = session.getCurrentTool().getSdynamicMemo();
  275 + Set<String> mergedSet = new LinkedHashSet<>();
  276 +
  277 + // 先加入 sSdynamicMemo 中的部件(保持原有顺序)
  278 + if (ObjectUtil.isNotEmpty(sSdynamicMemo)) {
  279 + String[] memoParts = sSdynamicMemo.split(",");
  280 + for (String part : memoParts) {
  281 + String trimmed = part.trim();
  282 + if (!trimmed.isEmpty()) {
  283 + mergedSet.add(trimmed);
  284 + }
  285 + }
  286 + }
  287 + return new ArrayList<>(mergedSet);
  288 + }
  289 +
  290 + /***
  291 + * @Author 钱豹
  292 + * @Date 23:07 2026/7/29
  293 + * @Description
  294 + **/
  295 + public String doCheckDynamicPart(String userId,String userInput,UserSceneSession session) {
  296 + List<String> partList = getPart(session);
  297 + //判断是否存返回的部件是否有不存在
  298 + List<String> dDynamicPartList = session.getDynamicPartList();
  299 + // 判断 dDynamicPartList 中不存在于 partList 的数据(即多余的、已失效的部件)
  300 + List<String> invalidParts = new ArrayList<>();
  301 + if (ObjectUtil.isNotEmpty(dDynamicPartList)) {
  302 + // 将 partList 转为 Set 提高查询效率
  303 + Set<String> partSet = new HashSet<>(partList);
  304 + for (String dynamicPart : dDynamicPartList) {
  305 + if (!partSet.contains(dynamicPart)) {
  306 + invalidParts.add(dynamicPart);
  307 + }
  308 + }
  309 + }
  310 + //存在动态部件,没有采集 需要添加参数
  311 + if (ObjectUtil.isNotEmpty(dDynamicPartList) && ObjectUtil.isEmpty(invalidParts)) {
  312 + List<String> partListBs = getPartBs(session);
  313 + // 移除 partList 中存在于 partListBs 的所有元素
  314 + dDynamicPartList.removeAll(partListBs);
  315 + List<String> partListAdd = new ArrayList<>(dDynamicPartList);
  316 + addToolMethod(partListAdd,session);
  317 + }
  318 + if(ObjectUtil.isNotEmpty(invalidParts)){
  319 + return String.join(",", invalidParts)+"部件不存在,是否是部件,如果是,输入是,否则输入否";
  320 + }
  321 + return null;
  322 + }
  323 +
  324 +
  325 + /** 解析模型输出,兼容被 ```json ... ``` 包裹或前后带杂字符的情况 */
  326 + private List<String> parseChoice(String raw) {
  327 + if (ObjectUtil.isEmpty(raw)) return null;
  328 + try {
  329 + return JSONUtil.toList(raw,String.class);
  330 + } catch (Exception e) {
  331 + log.error("方法路由结果解析失败, raw={}", raw, e);
  332 + return null;
  333 + }
  334 + }
  335 +
  336 +}
0 \ No newline at end of file 337 \ No newline at end of file
src/main/java/com/xly/service/XlyErpService.java
@@ -49,6 +49,7 @@ import java.util.stream.IntStream; @@ -49,6 +49,7 @@ import java.util.stream.IntStream;
49 public class XlyErpService { 49 public class XlyErpService {
50 //中文对话模型 50 //中文对话模型
51 private final OllamaChatModel chatModel; 51 private final OllamaChatModel chatModel;
  52 + private final DynamicPartService dynamicPartService;
52 private final ChatiAgent chatiAgent; 53 private final ChatiAgent chatiAgent;
53 private final SceneSelectorAiAgent sceneSelectorAiAgent; 54 private final SceneSelectorAiAgent sceneSelectorAiAgent;
54 private final UserSceneSessionService userSceneSessionService; 55 private final UserSceneSessionService userSceneSessionService;
@@ -340,8 +341,9 @@ public class XlyErpService { @@ -340,8 +341,9 @@ public class XlyErpService {
340 } 341 }
341 List<ToolMeta> metaAll = dynamicToolProvider.metaAll; 342 List<ToolMeta> metaAll = dynamicToolProvider.metaAll;
342 if(ObjectUtil.isNotEmpty(metaAll)){ 343 if(ObjectUtil.isNotEmpty(metaAll)){
  344 + String finalInput = input;
343 List<ToolMeta> metaOne = metaAll.stream().filter(m-> 345 List<ToolMeta> metaOne = metaAll.stream().filter(m->
344 - userInput.equals(m.getSMethodName().trim()) || ("查询"+userInput).equals(m.getSMethodName().trim()) 346 + finalInput.equals(m.getSMethodName().trim()) || ("查询"+ finalInput).equals(m.getSMethodName().trim())
345 ).collect(Collectors.toUnmodifiableList()); 347 ).collect(Collectors.toUnmodifiableList());
346 if(ObjectUtil.isNotEmpty(metaOne)){ 348 if(ObjectUtil.isNotEmpty(metaOne)){
347 session.setCurrentTool(metaOne.get(0)); 349 session.setCurrentTool(metaOne.get(0));
@@ -363,13 +365,48 @@ public class XlyErpService { @@ -363,13 +365,48 @@ public class XlyErpService {
363 ErpAiAgent aiAgent = null; 365 ErpAiAgent aiAgent = null;
364 //不存在方法需要选择方法 366 //不存在方法需要选择方法
365 if(ObjectUtil.isEmpty(session.getCurrentTool())){ 367 if(ObjectUtil.isEmpty(session.getCurrentTool())){
366 - MethodChoiceResult mr = methodChoiceResult(userId, userInput, session); 368 + MethodChoiceResult mr = methodChoiceResult(userId, input, session);
367 if(ObjectUtil.isNotEmpty(mr) && ObjectUtil.isNotEmpty(mr.getSMethodNo())){ 369 if(ObjectUtil.isNotEmpty(mr) && ObjectUtil.isNotEmpty(mr.getSMethodNo())){
368 List<ToolMeta> metaOne = metaAll.stream().filter(m-> mr.getSMethodNo().equals(m.getSMethodNo().trim()) 370 List<ToolMeta> metaOne = metaAll.stream().filter(m-> mr.getSMethodNo().equals(m.getSMethodNo().trim())
369 ).collect(Collectors.toUnmodifiableList()); 371 ).collect(Collectors.toUnmodifiableList());
370 session.setCurrentTool(metaOne.get(0)); 372 session.setCurrentTool(metaOne.get(0));
371 } 373 }
372 } 374 }
  375 +
  376 + //AI根据构件库 获取部件的名称获取到的数据放入动态部件参数 、然后方法添加动态部件
  377 + if(ObjectUtil.isNotEmpty(session)
  378 + && ObjectUtil.isNotEmpty(session.getCurrentTool())
  379 + && session.getCurrentTool().getBQuo()
  380 + && !session.getBConfirmedDynamicPart()
  381 + ){
  382 + List<String> dt = dynamicPartService.dynamicPartDTOChoiceResult(userId,input,session);
  383 + if(ObjectUtil.isNotEmpty(dt)){
  384 + String sResponMessage = dynamicPartService.doCheckDynamicPart(userId,input,session);
  385 + if(ObjectUtil.isNotEmpty(sResponMessage)){
  386 + session.setSDynamicPartUp(input);
  387 + return AiResponseDTO.builder().sSceneName(sceneName)
  388 + .sMethodName(session.getCurrentTool().getSMethodName())
  389 + .aiText(sResponMessage)
  390 + .sCopyTo(session.getSCopyTo())
  391 + .sCopyToSrcId(session.getSCopyToSrcId())
  392 + .dbType(session.getDbType())
  393 + .dbCach(session.getDbCach())
  394 + .sReturnType(sReturnType)
  395 + .build();
  396 + }
  397 + }
  398 + //返回上一次确认数据
  399 + if(ObjectUtil.isNotEmpty(session.getSDynamicPartUp())){
  400 + input = session.getSDynamicPartUp();
  401 + }
  402 + }
  403 + //确认了动态部件
  404 + session.setBConfirmedDynamicPart(true);
  405 + //如果输入的文字等于方法描述的文字,直接锁定方法 返回提示语
  406 +// String sSceneId = session.getCurrentScene().getSId();
  407 + String sResponMessage = StrUtil.EMPTY;
  408 + //用户输入添加方法(如果没有方法,动态SQL方法不需要)
  409 + Boolean isConfirmed = dynamicToolProvider.isConfirmed(input,session) && !session.getBConfirmedDynamicPartFist();
373 //还是不存在方法走老的方式兜底 410 //还是不存在方法走老的方式兜底
374 if(ObjectUtil.isEmpty(session.getCurrentTool())){ 411 if(ObjectUtil.isEmpty(session.getCurrentTool())){
375 // 4. 构建Agent,执行业务交互,如果返回为null,说明大模型没有判段出场景,必判断出后才能继续 412 // 4. 构建Agent,执行业务交互,如果返回为null,说明大模型没有判段出场景,必判断出后才能继续
@@ -379,20 +416,19 @@ public class XlyErpService { @@ -379,20 +416,19 @@ public class XlyErpService {
379 return getChatiAgent (input,session); 416 return getChatiAgent (input,session);
380 } 417 }
381 } 418 }
382 - //如果输入的文字等于方法描述的文字,直接锁定方法 返回提示语  
383 -// String sSceneId = session.getCurrentScene().getSId();  
384 - String sResponMessage = StrUtil.EMPTY;  
385 - //用户输入添加方法(如果没有方法,动态SQL方法不需要)  
386 - Boolean isConfirmed = dynamicToolProvider.isConfirmed(input,session);  
387 if(!(ObjectUtil.isNotEmpty(session.getCurrentTool()) 419 if(!(ObjectUtil.isNotEmpty(session.getCurrentTool())
388 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName()) 420 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName())
389 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo())) 421 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo()))
390 ){ 422 ){
391 String sSystemPrompt =AgentSystemPrompt.sSystemPrompt; 423 String sSystemPrompt =AgentSystemPrompt.sSystemPrompt;
392 - if(ObjectUtil.isNotEmpty(session) && ObjectUtil.isNotEmpty(session.getCurrentScene())  
393 - && "quotemanage".equals(session.getCurrentScene().getSSceneNo())){ 424 + if(ObjectUtil.isNotEmpty(session)
  425 + && ObjectUtil.isNotEmpty(session.getCurrentTool())
  426 + && ObjectUtil.isNotEmpty(session.getCurrentScene())
  427 + && "quotemanage".equals(session.getCurrentScene().getSSceneNo())
  428 + ){
394 sSystemPrompt =AgentSystemPrompt.sSystemPromptQuo; 429 sSystemPrompt =AgentSystemPrompt.sSystemPromptQuo;
395 - sSystemPrompt = StrUtil.format(sSystemPrompt,session.getCurrentScene().getSKnowledgeBase()); 430 + sSystemPrompt = StrUtil.format(sSystemPrompt,session.getCurrentTool().getSKnowledgeBase());
  431 +
396 } 432 }
397 //如果客户输入了确认/生成 切换引导语言 433 //如果客户输入了确认/生成 切换引导语言
398 if(ObjectUtil.isNotEmpty(session.getSSystemPrompt()) && isConfirmed){ 434 if(ObjectUtil.isNotEmpty(session.getSSystemPrompt()) && isConfirmed){
@@ -404,12 +440,15 @@ public class XlyErpService { @@ -404,12 +440,15 @@ public class XlyErpService {
404 440
405 //报价每次定义参数 441 //报价每次定义参数
406 if(!isConfirmed && ObjectUtil.isNotEmpty(session) && ObjectUtil.isNotEmpty(session.getCurrentTool()) && session.getCurrentTool().getBQuo()){ 442 if(!isConfirmed && ObjectUtil.isNotEmpty(session) && ObjectUtil.isNotEmpty(session.getCurrentTool()) && session.getCurrentTool().getBQuo()){
407 - sSystemPrompt = SystemPromptGenerator.generate(session.getCurrentTool().getSMethodName(),session,session.getCurrentScene().getSKnowledgeBase()); 443 + sSystemPrompt = SystemPromptGenerator.generate(session.getCurrentTool().getSMethodName(),session,session.getCurrentTool().getSKnowledgeBase());
408 aiAgent = createConfirmeAgent(session); 444 aiAgent = createConfirmeAgent(session);
409 } 445 }
410 Result<String> rs = aiAgent.chat(userId, input,sSystemPrompt); 446 Result<String> rs = aiAgent.chat(userId, input,sSystemPrompt);
411 sResponMessage = rs.content(); 447 sResponMessage = rs.content();
412 } 448 }
  449 + //确认动态部件重置
  450 + session.setBConfirmedDynamicPartFist(false);
  451 + session.setSDynamicPartUp(StrUtil.EMPTY);
413 methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSMethodName():StrUtil.EMPTY; 452 methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSMethodName():StrUtil.EMPTY;
414 if(ObjectUtil.isNotEmpty(session.getCurrentTool()) 453 if(ObjectUtil.isNotEmpty(session.getCurrentTool())
415 && !ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName()) 454 && !ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName())
@@ -988,7 +1027,9 @@ public class XlyErpService { @@ -988,7 +1027,9 @@ public class XlyErpService {
988 * @Description AI识别获取方法(二级路由:在当前场景内选出一个 sMethodNo) 1027 * @Description AI识别获取方法(二级路由:在当前场景内选出一个 sMethodNo)
989 **/ 1028 **/
990 private MethodChoiceResult methodChoiceResult(String userId, String userInput, UserSceneSession session) { 1029 private MethodChoiceResult methodChoiceResult(String userId, String userInput, UserSceneSession session) {
991 - 1030 + if(ObjectUtil.isEmpty(session.getCurrentScene())){
  1031 + return null;
  1032 + }
992 // 1. 取当前场景下的可选方法(方法树扁平集合) 1033 // 1. 取当前场景下的可选方法(方法树扁平集合)
993 List<ToolMeta> metaOne = dynamicToolProvider.metaAll.stream() 1034 List<ToolMeta> metaOne = dynamicToolProvider.metaAll.stream()
994 .filter(m -> session.getCurrentScene().getSId().equals(m.getSSceneId())) 1035 .filter(m -> session.getCurrentScene().getSId().equals(m.getSSceneId()))
@@ -1000,7 +1041,8 @@ public class XlyErpService { @@ -1000,7 +1041,8 @@ public class XlyErpService {
1000 } 1041 }
1001 1042
1002 // 2. 生成方法选择 prompt(行业知识库按需注入) 1043 // 2. 生成方法选择 prompt(行业知识库按需注入)
1003 - String sKnowledgeBase = session.getCurrentScene().getSKnowledgeBase(); // 无此字段就传 null 1044 + String sKnowledgeBase = StrUtil.EMPTY;
  1045 + //session.getCurrentScene().getSKnowledgeBase(); // 无此字段就传 null
1004 String sSystemPrompt = SystemPromptGenerator.generateChoiceMethodPrompt(metaOne, sKnowledgeBase); 1046 String sSystemPrompt = SystemPromptGenerator.generateChoiceMethodPrompt(metaOne, sKnowledgeBase);
1005 1047
1006 // 3. 调大模型 1048 // 3. 调大模型
src/main/java/com/xly/tool/DynamicToolProvider.java
@@ -211,7 +211,10 @@ public class DynamicToolProvider implements ToolProvider { @@ -211,7 +211,10 @@ public class DynamicToolProvider implements ToolProvider {
211 if(ObjectUtil.isNotEmpty(sDynamicValueOld)){ 211 if(ObjectUtil.isNotEmpty(sDynamicValueOld)){
212 List<String> data = new ArrayList<>(); 212 List<String> data = new ArrayList<>();
213 if (JSONUtil.isTypeJSONArray(sDynamicValueOld)) { 213 if (JSONUtil.isTypeJSONArray(sDynamicValueOld)) {
214 - data = JSONUtil.toList(sDynamicValueOld, String.class); 214 + try{
  215 + data = JSONUtil.toList(sDynamicValueOld, String.class);
  216 + }catch (Exception e){}
  217 +
215 } else { 218 } else {
216 data.add(sDynamicValueOld); 219 data.add(sDynamicValueOld);
217 } 220 }
@@ -220,7 +223,10 @@ public class DynamicToolProvider implements ToolProvider { @@ -220,7 +223,10 @@ public class DynamicToolProvider implements ToolProvider {
220 if(ObjectUtil.isNotEmpty(sDynamicValue)){ 223 if(ObjectUtil.isNotEmpty(sDynamicValue)){
221 List<String> data = new ArrayList<>(); 224 List<String> data = new ArrayList<>();
222 if (JSONUtil.isTypeJSONArray(sDynamicValue)) { 225 if (JSONUtil.isTypeJSONArray(sDynamicValue)) {
223 - data = JSONUtil.toList(sDynamicValue, String.class); 226 + try{
  227 + data = JSONUtil.toList(sDynamicValue,String.class);
  228 + }catch (Exception e){
  229 + }
224 } else { 230 } else {
225 data.add(sDynamicValue); 231 data.add(sDynamicValue);
226 } 232 }
@@ -354,7 +360,12 @@ public class DynamicToolProvider implements ToolProvider { @@ -354,7 +360,12 @@ public class DynamicToolProvider implements ToolProvider {
354 List<String> addValue = new ArrayList<>(); 360 List<String> addValue = new ArrayList<>();
355 361
356 if (JSONUtil.isTypeJSONArray(sValue)) { 362 if (JSONUtil.isTypeJSONArray(sValue)) {
357 - addValue = JSONUtil.toList(sValue, String.class); 363 + try{
  364 + addValue = JSONUtil.toList(sValue, String.class);
  365 + }catch (Exception e){
  366 + e.printStackTrace();;
  367 + }
  368 +
358 } else { 369 } else {
359 addValue.add(sValue); 370 addValue.add(sValue);
360 } 371 }
@@ -437,7 +448,11 @@ public class DynamicToolProvider implements ToolProvider { @@ -437,7 +448,11 @@ public class DynamicToolProvider implements ToolProvider {
437 : StrUtil.EMPTY; 448 : StrUtil.EMPTY;
438 if (ObjectUtil.isNotEmpty(sValueDel)) { 449 if (ObjectUtil.isNotEmpty(sValueDel)) {
439 if (JSONUtil.isTypeJSONArray(sValueDel)) { 450 if (JSONUtil.isTypeJSONArray(sValueDel)) {
440 - sDynamicPartDelValue = JSONUtil.toList(sValueDel, String.class); 451 + try{
  452 + sDynamicPartDelValue = JSONUtil.toList(sValueDel, String.class);
  453 + }catch (Exception e){
  454 + e.printStackTrace();;
  455 + }
441 } else { 456 } else {
442 sDynamicPartDelValue.add(sValueDel); 457 sDynamicPartDelValue.add(sValueDel);
443 } 458 }
@@ -459,7 +474,11 @@ public class DynamicToolProvider implements ToolProvider { @@ -459,7 +474,11 @@ public class DynamicToolProvider implements ToolProvider {
459 List<String> addValueOld = new ArrayList<>(); 474 List<String> addValueOld = new ArrayList<>();
460 if(ObjectUtil.isNotEmpty(sValue)){ 475 if(ObjectUtil.isNotEmpty(sValue)){
461 if (JSONUtil.isTypeJSONArray(sValue)) { 476 if (JSONUtil.isTypeJSONArray(sValue)) {
462 - addValueOld = JSONUtil.toList(sValue, String.class); 477 + try{
  478 + addValueOld = JSONUtil.toList(sValue, String.class);
  479 + }catch (Exception e){
  480 + e.printStackTrace();;
  481 + }
463 } else { 482 } else {
464 addValueOld.add(sValue); 483 addValueOld.add(sValue);
465 } 484 }
@@ -521,6 +540,7 @@ public class DynamicToolProvider implements ToolProvider { @@ -521,6 +540,7 @@ public class DynamicToolProvider implements ToolProvider {
521 log.info("动态部件已存在,跳过: {}", sVal); 540 log.info("动态部件已存在,跳过: {}", sVal);
522 } 541 }
523 } 542 }
  543 +
524 // 移除不在 addValue 中的动态部件 544 // 移除不在 addValue 中的动态部件
525 // paramRuleDataAllNew = paramRuleDataAllNew.stream() 545 // paramRuleDataAllNew = paramRuleDataAllNew.stream()
526 // .filter(p -> { 546 // .filter(p -> {