Commit e5fb3dd8f70f5116579991389476bd776b3799c1
1 parent
781d3448
添加未清选择 改成动态引导语
Showing
6 changed files
with
180 additions
and
136 deletions
src/main/java/com/xly/agent/AgentSystemPrompt.java
0 → 100644
| 1 | +package com.xly.agent; | |
| 2 | + | |
| 3 | +public class AgentSystemPrompt { | |
| 4 | + | |
| 5 | + public static final String sSystemPrompt= "1. 方法匹配:先精准拆解用户查询的核心业务意图,再自动匹配唯一符合用户问题的工具方法(MethodNo),禁止自创,规则如下;\n" + | |
| 6 | + " 1.1 匹配方法时,无需考虑工具描述(@TOOL)中 1.必填参数,2.选填参数,示例,parameters内容 四个部分的内容;\n" + | |
| 7 | + " 1.2 匹配方法时,只关注工具描述(@TOOL)中 “当用户” 和 “时,必须调用本工具”两个短语之间的内容;\n" + | |
| 8 | + " 1.3 调用工具前,不需要询问用户提供缺失的参数\n" + | |
| 9 | + " 2. 参数提取:提取该工具的全部参数,与描述完全一致,严格按标注类型赋值,规则如下:\n" + | |
| 10 | + " 2.1 数字无引号,为空时禁止赋值0;\n" + | |
| 11 | + " 2.2 如果有空格需要去掉空格后再提取。"; | |
| 12 | +} | ... | ... |
src/main/java/com/xly/agent/ErpAiAgent.java
| ... | ... | @@ -10,18 +10,14 @@ import dev.langchain4j.service.V; |
| 10 | 10 | * 优化后:新增场景专属交互规则,大模型仅处理当前场景业务指令 |
| 11 | 11 | */ |
| 12 | 12 | public interface ErpAiAgent { |
| 13 | - @SystemMessage(""" | |
| 14 | - 1. 方法匹配:先精准拆解用户查询的核心业务意图,再自动匹配唯一符合用户问题的工具方法(MethodNo),禁止自创,规则如下; | |
| 15 | - 1.1 匹配方法时,无需考虑工具描述(@TOOL)中 1.必填参数,2.选填参数,示例,parameters内容 四个部分的内容; | |
| 16 | - 1.2 匹配方法时,只关注工具描述(@TOOL)中 “当用户” 和 “时,必须调用本工具”两个短语之间的内容; | |
| 17 | - 1.3 调用工具前,不需要询问用户提供缺失的参数 | |
| 18 | - 2. 参数提取:提取该工具的全部参数,与描述完全一致,严格按标注类型赋值,规则如下: | |
| 19 | - 2.1 数字无引号,为空时禁止赋值0; | |
| 20 | - 2.2 如果有空格需要去掉空格后再提取。 | |
| 21 | - """) | |
| 22 | - @UserMessage("用户输入:{{userInput}}") | |
| 23 | - String chat(@MemoryId String userId, @V("userInput") String userInput); | |
| 24 | 13 | |
| 14 | + @SystemMessage("{{sSystemPrompt}}") | |
| 15 | + @UserMessage("用户输入:{{userInput}}") | |
| 16 | + String chat( | |
| 17 | + @MemoryId String userId, | |
| 18 | + @V("userInput") String userInput, | |
| 19 | + @V("sSystemPrompt") String sSystemPrompt | |
| 20 | + ); | |
| 25 | 21 | /** |
| 26 | 22 | * 动态表结构:自然语言解释SQL执行结果 |
| 27 | 23 | * 入参:用户问题、执行的SQL、表结构、JSON格式结果 | ... | ... |
src/main/java/com/xly/entity/UserSceneSession.java
| ... | ... | @@ -75,6 +75,14 @@ public class UserSceneSession { |
| 75 | 75 | * 数据库类型 H: 缓存 D: 动态 |
| 76 | 76 | */ |
| 77 | 77 | private String dbCach; |
| 78 | + /** | |
| 79 | + * @Author 钱豹 | |
| 80 | + * @Date 22:55 2026/4/13 | |
| 81 | + * @Param | |
| 82 | + * @return | |
| 83 | + * @Description AI 模板 | |
| 84 | + **/ | |
| 85 | + private String sSystemPrompt; | |
| 78 | 86 | |
| 79 | 87 | /** |
| 80 | 88 | * 构建场景选择提示语:展示权限内场景,引导用户选择 | ... | ... |
src/main/java/com/xly/service/XlyErpService.java
| ... | ... | @@ -8,10 +8,7 @@ import cn.hutool.core.util.StrUtil; |
| 8 | 8 | import cn.hutool.json.JSONUtil; |
| 9 | 9 | import com.alibaba.fastjson2.JSON; |
| 10 | 10 | import com.alibaba.fastjson2.JSONObject; |
| 11 | -import com.xly.agent.ChatiAgent; | |
| 12 | -import com.xly.agent.DynamicTableNl2SqlAiAgent; | |
| 13 | -import com.xly.agent.ErpAiAgent; | |
| 14 | -import com.xly.agent.SceneSelectorAiAgent; | |
| 11 | +import com.xly.agent.*; | |
| 15 | 12 | import com.xly.config.OperableChatMemoryProvider; |
| 16 | 13 | import com.xly.constant.CommonConstant; |
| 17 | 14 | import com.xly.constant.ReturnTypeCode; |
| ... | ... | @@ -140,7 +137,7 @@ public class XlyErpService { |
| 140 | 137 | && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName()) |
| 141 | 138 | && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo())) |
| 142 | 139 | ){ |
| 143 | - sResponMessage = aiAgent.chat(userId, input); | |
| 140 | + sResponMessage = aiAgent.chat(userId, input , AgentSystemPrompt.sSystemPrompt); | |
| 144 | 141 | } |
| 145 | 142 | |
| 146 | 143 | if(ObjectUtil.isNotEmpty(session.getCurrentTool()) |
| ... | ... | @@ -351,7 +348,11 @@ public class XlyErpService { |
| 351 | 348 | && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName()) |
| 352 | 349 | && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo())) |
| 353 | 350 | ){ |
| 354 | - sResponMessage = aiAgent.chat(userId, input); | |
| 351 | + String sSystemPrompt = AgentSystemPrompt.sSystemPrompt; | |
| 352 | + if(ObjectUtil.isNotEmpty(session.getSSystemPrompt())){ | |
| 353 | + sSystemPrompt = session.getSSystemPrompt(); | |
| 354 | + } | |
| 355 | + sResponMessage = aiAgent.chat(userId, input,sSystemPrompt); | |
| 355 | 356 | } |
| 356 | 357 | methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSMethodName():StrUtil.EMPTY; |
| 357 | 358 | if(ObjectUtil.isNotEmpty(session.getCurrentTool()) |
| ... | ... | @@ -947,7 +948,7 @@ public class XlyErpService { |
| 947 | 948 | UserSceneSessionService.ERP_AGENT_CACHE.put(userId, aiAgent); |
| 948 | 949 | // 初始化AiService 以防止热加载太慢 找不到相应的方法 |
| 949 | 950 | try{ |
| 950 | - aiAgent.chat(userId, "initAiService"); | |
| 951 | + aiAgent.chat(userId, "initAiService",AgentSystemPrompt.sSystemPrompt); | |
| 951 | 952 | }catch (Exception e){ |
| 952 | 953 | e.printStackTrace(); |
| 953 | 954 | } | ... | ... |
src/main/java/com/xly/tool/DynamicToolProvider.java
| ... | ... | @@ -10,11 +10,9 @@ import com.fasterxml.jackson.core.type.TypeReference; |
| 10 | 10 | |
| 11 | 11 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 12 | 12 | |
| 13 | +import com.xly.agent.AgentSystemPrompt; | |
| 13 | 14 | import com.xly.config.OperableChatMemoryProvider; |
| 14 | -import com.xly.constant.ErrorCode; | |
| 15 | -import com.xly.constant.ProcedureConstant; | |
| 16 | -import com.xly.constant.RuleCode; | |
| 17 | -import com.xly.constant.UrlErpConstant; | |
| 15 | +import com.xly.constant.*; | |
| 18 | 16 | import com.xly.entity.*; |
| 19 | 17 | import com.xly.exception.dto.BusinessException; |
| 20 | 18 | import com.xly.mapper.ParamRuleMapper; |
| ... | ... | @@ -245,80 +243,67 @@ public class DynamicToolProvider implements ToolProvider { |
| 245 | 243 | return ToolProviderResult.builder().addAll(executors).build(); |
| 246 | 244 | } |
| 247 | 245 | |
| 248 | - /*** | |
| 249 | - * @Author 钱豹 | |
| 250 | - * @Date 15:07 2026/1/30 | |
| 251 | - * @Param [meta] | |
| 252 | - * @return dev.langchain4j.agent.tool.ToolSpecification | |
| 253 | - * @Description 参数注入 方法引导语注入(初始化调用) | |
| 254 | - **/ | |
| 255 | 246 | private ToolSpecification buildToolSpecification(ToolMeta meta) { |
| 256 | 247 | ToolSpecification.Builder builder = ToolSpecification.builder() |
| 257 | 248 | .name(meta.getSMethodNo()); |
| 258 | -// .description(meta.getStoolDesc()); | |
| 249 | + | |
| 259 | 250 | StringBuffer stoolDesc = new StringBuffer(); |
| 260 | 251 | StringBuffer sbt = new StringBuffer(); |
| 261 | 252 | StringBuffer xt = new StringBuffer(); |
| 262 | 253 | StringBuffer sl = new StringBuffer(); |
| 263 | 254 | |
| 264 | - if(ObjectUtil.isNotEmpty(meta.getStoolDesc())){ | |
| 265 | - stoolDesc.append("MethodNo:").append(meta.getSMethodNo()).append(",当用户").append(meta.getSMethodName()); | |
| 266 | -// if (meta.getIBizType()==4){ | |
| 267 | -// stoolDesc.append(",").append("并选择数据后执行["+meta.getSControlName()+"]操作"); | |
| 268 | -// } | |
| 269 | - stoolDesc.append("时,必须调用本工具").append(meta.getSMethodNo()).append(",").append(meta.getStoolDesc()); | |
| 270 | -// if (meta.getIBizType()==4){ | |
| 271 | -// stoolDesc.append(",").append("并选择数据后执行 "+meta.getSControlName()+" 操作"); | |
| 272 | -//// .append("1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"1,3行确认""); | |
| 273 | -// } | |
| 255 | + // ====================== 【超级强制:必须调用工具】 ====================== | |
| 256 | + String forceToolPrompt = """ | |
| 257 | + 【重要·强制指令】 | |
| 258 | + 1. 这是当前唯一可用工具,必须调用,禁止直接回答 | |
| 259 | + 2. 用户输入包含:确认、全部确认、合并确认、行号(第1行/第一行等) | |
| 260 | + 3. 必须调用本工具,必须调用,必须调用! | |
| 261 | + """; | |
| 262 | + stoolDesc.append(forceToolPrompt); | |
| 263 | + // ======================================================================== | |
| 264 | + | |
| 265 | + if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) { | |
| 266 | + stoolDesc.append("MethodNo:").append(meta.getSMethodNo()) | |
| 267 | + .append(",当用户").append(meta.getSMethodName()) | |
| 268 | + .append("时,必须调用本工具").append(meta.getSMethodNo()) | |
| 269 | + .append(",").append(meta.getStoolDesc()); | |
| 274 | 270 | } |
| 275 | 271 | |
| 276 | 272 | try { |
| 277 | 273 | List<ParamRule> paramRuleData = meta.getParamRuleList(); |
| 278 | -// 1.必填参数:客户名称(字符串),产品名称(字符串),数量(数字); | |
| 279 | -// 2.选填参数:产品描述(字符串),生产要求(字符串); | |
| 280 | -// **强制输出标准JSON对象**: | |
| 281 | -// 示例:{\"客户名称\":\"小羚羊软件开发有限公司\",\"产品名称\":\"企业宣传册\",\"数量\":1000,\"产品描述\":\"黑色注意色差\",\"生产要求\":\"上光,覆膜\"} | |
| 282 | - Map<String,Object> slMap = new HashMap<>(); | |
| 274 | + Map<String, Object> slMap = new HashMap<>(); | |
| 275 | + | |
| 283 | 276 | for (ParamRule paramRule : paramRuleData) { |
| 284 | -// String paramName = ObjectUtil.isEmpty(paramRule.getSParamValue())?null:paramRule.getSParamValue(); | |
| 285 | - String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam())?null:paramRule.getSParam(); | |
| 277 | + String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam()) ? null : paramRule.getSParam(); | |
| 286 | 278 | String paramType = paramRule.getSType(); |
| 287 | 279 | Boolean bEmpty = paramRule.getBEmpty(); |
| 288 | 280 | String sExampleValue = paramRule.getSExampleValue(); |
| 289 | - //示例值,只有枚举放 | |
| 290 | - if(ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())){ | |
| 291 | - //英文 | |
| 292 | -// slMap.put(paramName,sExampleValue); | |
| 293 | - //中文 | |
| 294 | - slMap.put(paramDesc,sExampleValue); | |
| 281 | + | |
| 282 | + if (ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())) { | |
| 283 | + slMap.put(paramDesc, sExampleValue); | |
| 295 | 284 | } |
| 296 | 285 | if (paramDesc == null || paramDesc.trim().isEmpty()) { |
| 297 | 286 | continue; |
| 298 | 287 | } |
| 299 | - // 构建参数属性 | |
| 300 | - //{"string":"字符","integer":"数字","double":"浮点","boolean":"布尔型","array":"数组","enum":"枚举"} | |
| 288 | + | |
| 301 | 289 | List<JsonSchemaProperty> properties = new ArrayList<>(); |
| 302 | - // 添加类型属性 | |
| 303 | -// String ,仅允许【{}】多选一,严格匹配) | |
| 304 | - String sRuleCost = getConstMeg(paramRule.getSParamConfig(),paramRule); | |
| 305 | -// 2. 付款方式:字符串类型,互斥枚举值[90天、60天、现结],默认值[现结] | |
| 306 | -// 5. 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值 | |
| 290 | + String sRuleCost = getConstMeg(paramRule.getSParamConfig(), paramRule); | |
| 291 | + | |
| 307 | 292 | switch (paramType.toLowerCase()) { |
| 308 | 293 | case "string": |
| 309 | - if(bEmpty){ | |
| 310 | - sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、"); | |
| 311 | - }else{ | |
| 312 | - xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、"); | |
| 294 | + if (bEmpty) { | |
| 295 | + sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); | |
| 296 | + } else { | |
| 297 | + xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); | |
| 313 | 298 | } |
| 314 | 299 | properties.add(JsonSchemaProperty.STRING); |
| 315 | 300 | break; |
| 316 | 301 | case "integer": |
| 317 | 302 | case "int": |
| 318 | - if(bEmpty){ | |
| 319 | - sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、"); | |
| 320 | - }else{ | |
| 321 | - xt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、"); | |
| 303 | + if (bEmpty) { | |
| 304 | + sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")、"); | |
| 305 | + } else { | |
| 306 | + xt.append(paramDesc).append("(数字").append(sRuleCost).append(")、"); | |
| 322 | 307 | } |
| 323 | 308 | properties.add(JsonSchemaProperty.INTEGER); |
| 324 | 309 | break; |
| ... | ... | @@ -326,91 +311,84 @@ public class DynamicToolProvider implements ToolProvider { |
| 326 | 311 | case "double": |
| 327 | 312 | case "float": |
| 328 | 313 | properties.add(JsonSchemaProperty.NUMBER); |
| 329 | - if(bEmpty){ | |
| 330 | - sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、"); | |
| 331 | - }else{ | |
| 332 | - xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、"); | |
| 314 | + if (bEmpty) { | |
| 315 | + sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、"); | |
| 316 | + } else { | |
| 317 | + xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、"); | |
| 333 | 318 | } |
| 334 | 319 | break; |
| 335 | 320 | case "boolean": |
| 336 | 321 | case "bool": |
| 337 | - if(bEmpty){ | |
| 338 | - sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、"); | |
| 339 | - }else{ | |
| 340 | - xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、"); | |
| 322 | + if (bEmpty) { | |
| 323 | + sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、"); | |
| 324 | + } else { | |
| 325 | + xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、"); | |
| 341 | 326 | } |
| 342 | 327 | properties.add(JsonSchemaProperty.BOOLEAN); |
| 343 | 328 | break; |
| 344 | 329 | case "array": |
| 345 | 330 | String sRuleArray = getArrrayBySql(paramRule); |
| 346 | - // 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值 | |
| 347 | 331 | if (ObjectUtil.isNotEmpty(sRuleArray)) { |
| 348 | - sRuleArray = StrUtil.replace(sRuleArray,",","/"); | |
| 332 | + sRuleArray = StrUtil.replace(sRuleArray, ",", "/"); | |
| 349 | 333 | properties.add(JsonSchemaProperty.enums(sRuleArray.split("/"))); |
| 350 | 334 | } |
| 351 | - if(bEmpty){ | |
| 352 | - //动态SQL 或者写死默认值的 动态SQL只存在一条数据 直接给默认值 | |
| 353 | - sbt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]"); | |
| 354 | - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)){ | |
| 355 | - String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)?sRuleArray:paramRule.getSDefaultValue(); | |
| 335 | + if (bEmpty) { | |
| 336 | + sbt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]"); | |
| 337 | + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1)) { | |
| 338 | + String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1) ? sRuleArray : paramRule.getSDefaultValue(); | |
| 356 | 339 | sbt.append(",默认值[").append(sDefaultVal).append("]"); |
| 357 | - }else{ | |
| 340 | + } else { | |
| 358 | 341 | sbt.append(",无默认值"); |
| 359 | 342 | } |
| 360 | 343 | sbt.append(")、"); |
| 361 | - }else{ | |
| 362 | - xt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]"); | |
| 363 | - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ | |
| 344 | + } else { | |
| 345 | + xt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]"); | |
| 346 | + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { | |
| 364 | 347 | xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); |
| 365 | - }else{ | |
| 348 | + } else { | |
| 366 | 349 | xt.append(",无默认值"); |
| 367 | 350 | } |
| 368 | 351 | xt.append(")、"); |
| 369 | 352 | } |
| 370 | 353 | properties.add(JsonSchemaProperty.ARRAY); |
| 371 | - // 默认字符串数组 | |
| 372 | 354 | properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); |
| 373 | 355 | break; |
| 374 | 356 | case "enum": |
| 375 | - // 处理枚举值 | |
| 376 | 357 | if (ObjectUtil.isNotEmpty(sRuleCost)) { |
| 377 | 358 | properties.add(JsonSchemaProperty.enums(sRuleCost.split("/"))); |
| 378 | - }else{ | |
| 359 | + } else { | |
| 379 | 360 | sRuleCost = getArrrayBySql(paramRule); |
| 380 | 361 | } |
| 381 | - // eg: 付款方式(字符串,互斥枚举值[90天、60天、现结],默认值[现结]) | |
| 382 | - if(bEmpty){ | |
| 383 | - sbt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]"); | |
| 384 | - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ | |
| 362 | + if (bEmpty) { | |
| 363 | + sbt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]"); | |
| 364 | + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { | |
| 385 | 365 | sbt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); |
| 386 | - }else{ | |
| 366 | + } else { | |
| 387 | 367 | sbt.append(",无默认值"); |
| 388 | 368 | } |
| 389 | 369 | sbt.append(")、"); |
| 390 | - }else{ | |
| 391 | - xt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]"); | |
| 392 | - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){ | |
| 370 | + } else { | |
| 371 | + xt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]"); | |
| 372 | + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { | |
| 393 | 373 | xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); |
| 394 | - }else{ | |
| 374 | + } else { | |
| 395 | 375 | xt.append(",无默认值"); |
| 396 | 376 | } |
| 397 | 377 | xt.append(")、"); |
| 398 | 378 | } |
| 399 | 379 | properties.add(JsonSchemaProperty.ARRAY); |
| 400 | - // 默认字符串数组 | |
| 401 | 380 | properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); |
| 402 | 381 | break; |
| 403 | 382 | default: |
| 404 | 383 | properties.add(JsonSchemaProperty.STRING); |
| 405 | 384 | break; |
| 406 | 385 | } |
| 407 | - // 添加描述 | |
| 386 | + | |
| 408 | 387 | if (!paramDesc.isEmpty()) { |
| 409 | 388 | properties.add(JsonSchemaProperty.description(paramDesc)); |
| 410 | 389 | } |
| 411 | - // 检查是否必填 | |
| 390 | + | |
| 412 | 391 | boolean required = bEmpty; |
| 413 | - // 添加参数 | |
| 414 | 392 | if (required) { |
| 415 | 393 | builder.addParameter(paramDesc, properties); |
| 416 | 394 | } else { |
| ... | ... | @@ -418,31 +396,33 @@ public class DynamicToolProvider implements ToolProvider { |
| 418 | 396 | } |
| 419 | 397 | } |
| 420 | 398 | |
| 421 | - if(ObjectUtil.isNotEmpty(sbt)){ | |
| 422 | - stoolDesc | |
| 423 | - .append(System.lineSeparator()) | |
| 424 | - .append("1.必填参数:") | |
| 425 | - .append(sbt); | |
| 399 | + if (ObjectUtil.isNotEmpty(sbt)) { | |
| 400 | + stoolDesc.append(System.lineSeparator()).append("1.必填参数:").append(sbt); | |
| 426 | 401 | } |
| 427 | - if(ObjectUtil.isNotEmpty(xt)){ | |
| 428 | - stoolDesc | |
| 429 | - .append(System.lineSeparator()) | |
| 430 | - .append("2.选填参数:") | |
| 431 | - .append(xt); | |
| 402 | + if (ObjectUtil.isNotEmpty(xt)) { | |
| 403 | + stoolDesc.append(System.lineSeparator()).append("2.选填参数:").append(xt); | |
| 432 | 404 | } |
| 433 | - if(ObjectUtil.isNotEmpty(slMap)){ | |
| 434 | - stoolDesc | |
| 435 | - .append(System.lineSeparator()) | |
| 436 | - .append(sl) | |
| 437 | - .append("**强制输出标准JSON对象**:").append(System.lineSeparator()) | |
| 438 | - .append("示例:").append(JSONUtil.toJsonStr(slMap)); | |
| 405 | + if (ObjectUtil.isNotEmpty(slMap)) { | |
| 406 | + stoolDesc.append(System.lineSeparator()).append(sl).append("**强制输出标准JSON对象**:") | |
| 407 | + .append(System.lineSeparator()).append("示例:").append(JSONUtil.toJsonStr(slMap)); | |
| 439 | 408 | } |
| 440 | - log.info("方法描述========================{}",stoolDesc); | |
| 409 | + | |
| 410 | + log.info("方法描述========================{}", stoolDesc); | |
| 441 | 411 | } catch (Exception e) { |
| 442 | 412 | e.printStackTrace(); |
| 443 | 413 | log.error("Failed to parse parameter rules: {}", meta.getSMethodName(), e); |
| 444 | - // 参数解析失败时,创建无参数的工具规格 | |
| 445 | 414 | } |
| 415 | + | |
| 416 | + // ====================== 【关键修复:强制添加两个参数】 ====================== | |
| 417 | + if (meta.getIBizType() == 4 || meta.getIBizType() == 5) { | |
| 418 | + // 强制添加 operateType(必填) | |
| 419 | + builder.addParameter("operateType", | |
| 420 | + JsonSchemaProperty.STRING, | |
| 421 | + JsonSchemaProperty.description("操作类型:全部确认/合并确认/单行确认") | |
| 422 | + ); | |
| 423 | + } | |
| 424 | + // ============================================================================ | |
| 425 | + | |
| 446 | 426 | builder.description(stoolDesc.toString()); |
| 447 | 427 | return builder.build(); |
| 448 | 428 | } |
| ... | ... | @@ -628,14 +608,14 @@ public class DynamicToolProvider implements ToolProvider { |
| 628 | 608 | String input = StrUtil.replace(userMessage.text(),"用户输入:",StrUtil.EMPTY); |
| 629 | 609 | // {"0":"查询","1":"执行"} 查询不需要确认 |
| 630 | 610 | Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认"); |
| 631 | - //判断是否生成数据 | |
| 632 | - List<Map<String,Object>> sRowData = new ArrayList<>(); | |
| 633 | - String sHandleType = "merge"; | |
| 634 | - if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){ | |
| 635 | - Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData()); | |
| 636 | - sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData"); | |
| 637 | - sHandleType = sRowDataMap.get("sHandleType").toString(); | |
| 638 | - } | |
| 611 | +// //判断是否生成数据 | |
| 612 | +// List<Map<String,Object>> sRowData = new ArrayList<>(); | |
| 613 | +// String sHandleType = "merge"; | |
| 614 | +// if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){ | |
| 615 | +// Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData()); | |
| 616 | +// sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData"); | |
| 617 | +// sHandleType = sRowDataMap.get("sHandleType").toString(); | |
| 618 | +// } | |
| 639 | 619 | //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"} |
| 640 | 620 | if((isConfirmed && (4== meta.getIBizType() ||1== meta.getIBizType())) |
| 641 | 621 | || 2== meta.getIBizType() |
| ... | ... | @@ -658,12 +638,12 @@ public class DynamicToolProvider implements ToolProvider { |
| 658 | 638 | String askconfirmMsg =StrUtil.EMPTY; |
| 659 | 639 | if(4== meta.getIBizType() || meta.getIBizType()==5){ |
| 660 | 640 | askconfirmMsg = doGetFromData( meta,args,session); |
| 661 | - return executeWithConfirmation(askconfirmMsg,operableChatMemoryProvider.get(session.getUserId()), session, meta); | |
| 641 | + return executeWithConfirmation(askconfirmMsg, session, meta); | |
| 662 | 642 | }else{ |
| 663 | 643 | askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta); |
| 664 | 644 | } |
| 665 | 645 | // 返回需要确认的结果 |
| 666 | - return executeWithConfirmation(askconfirmMsg,operableChatMemoryProvider.get(session.getUserId()), session, meta); | |
| 646 | + return executeWithConfirmation(askconfirmMsg, session, meta); | |
| 667 | 647 | } |
| 668 | 648 | |
| 669 | 649 | /*** |
| ... | ... | @@ -955,6 +935,8 @@ public class DynamicToolProvider implements ToolProvider { |
| 955 | 935 | sReturn = executeToolAfter(meta, args,paramRuleData,session); |
| 956 | 936 | }catch (BusinessException e) { |
| 957 | 937 | return e.getMessage(); |
| 938 | + }finally { | |
| 939 | + session.setSSystemPrompt(StrUtil.EMPTY); | |
| 958 | 940 | } |
| 959 | 941 | if(meta.getIActionType()==1){ |
| 960 | 942 | session.setBCleanMemory(true); |
| ... | ... | @@ -1197,7 +1179,10 @@ public class DynamicToolProvider implements ToolProvider { |
| 1197 | 1179 | rMap.put("sSlaveId",uniqueValue); |
| 1198 | 1180 | rMap.put("唯一",uniqueValue); |
| 1199 | 1181 | // 在行末添加隐藏数据的特殊标记(AI可以解析) |
| 1200 | - markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->"); | |
| 1182 | + markdown.append(" <span style=\"display:none;\">HIDDEN_DATA:") | |
| 1183 | + .append(JSONUtil.toJsonStr(rMap)) | |
| 1184 | + .append("</span>"); | |
| 1185 | +// markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->"); | |
| 1201 | 1186 | markdown.append("\n"); |
| 1202 | 1187 | machineData.add(rMap); |
| 1203 | 1188 | currentRowData.put(i + 1,recordData.get(i)); |
| ... | ... | @@ -1212,9 +1197,50 @@ public class DynamicToolProvider implements ToolProvider { |
| 1212 | 1197 | appendConfirmAll(markdown,meta.getSControlName()); |
| 1213 | 1198 | } |
| 1214 | 1199 | session.setCurrentRowData(currentRowData); |
| 1200 | + // ===================== 加入这一行 ===================== | |
| 1201 | + String sSystemPrompt = buildDynamicSystemPrompt(session); | |
| 1202 | + session.setSSystemPrompt(sSystemPrompt); | |
| 1203 | + // ====================================================== | |
| 1215 | 1204 | return markdown.toString(); |
| 1216 | 1205 | } |
| 1217 | 1206 | |
| 1207 | + public String buildDynamicSystemPrompt(UserSceneSession session) { | |
| 1208 | + | |
| 1209 | + // 1. 强制指令 + 固定方法编号 | |
| 1210 | + String promptHead = """ | |
| 1211 | + 【极强约束·必须执行】 | |
| 1212 | + 1. 禁止说话!禁止解释! | |
| 1213 | + 2. 必须调用工具,固定方法编号:MethodNo = queryUnprocessQuote | |
| 1214 | + 3. 只输出JSON,无任何其他内容 | |
| 1215 | + | |
| 1216 | + 以下是【行号 → sSlaveId】对应数据: | |
| 1217 | + """; | |
| 1218 | + | |
| 1219 | + // 2. 动态拼接行号 + 动态 sSlaveId | |
| 1220 | + Map<Integer, Map<String, Object>> rowDataMap = session.getCurrentRowData(); | |
| 1221 | + StringBuilder rowDataSb = new StringBuilder(); | |
| 1222 | + if (ObjectUtil.isNotEmpty(rowDataMap)) { | |
| 1223 | + for (Map.Entry<Integer, Map<String, Object>> entry : rowDataMap.entrySet()) { | |
| 1224 | + int rowNum = entry.getKey(); | |
| 1225 | + String sSlaveId = StrUtil.toString(entry.getValue().get("sSlaveId")); | |
| 1226 | + rowDataSb.append("第").append(rowNum).append("行 → ").append(sSlaveId).append("\n"); | |
| 1227 | + } | |
| 1228 | + } | |
| 1229 | + | |
| 1230 | + // 3. 输出格式约束 | |
| 1231 | + String promptFoot = """ | |
| 1232 | + 【输出JSON格式·严格遵守】 | |
| 1233 | + 根据用户选择的行,自动填写 sSlaveId,多行用英文逗号拼接 | |
| 1234 | + { | |
| 1235 | + "operateType": "全部确认/合并确认/单行确认", | |
| 1236 | + "sSlaveId": "填写对应的ID,多个用逗号分隔" | |
| 1237 | + } | |
| 1238 | + """; | |
| 1239 | + | |
| 1240 | + // 拼接最终返回 | |
| 1241 | + return promptHead + rowDataSb + promptFoot; | |
| 1242 | + } | |
| 1243 | + | |
| 1218 | 1244 | // 辅助方法:根据中文名查找字段名(通过映射关系转换) |
| 1219 | 1245 | private List<Map<String, Object>> findFieldNameByChinese(List<Map<String, Object>> sAIshowfieldShow,List<Map<String, Object>> rows){ |
| 1220 | 1246 | //获取映射关系 |
| ... | ... | @@ -1362,7 +1388,7 @@ public class DynamicToolProvider implements ToolProvider { |
| 1362 | 1388 | /** |
| 1363 | 1389 | * 执行方法后需要用户确认的扩展版本 |
| 1364 | 1390 | */ |
| 1365 | - private String executeWithConfirmation(String initialResult,ChatMemory chatMemory, UserSceneSession session,ToolMeta meta) { | |
| 1391 | + private String executeWithConfirmation(String initialResult, UserSceneSession session,ToolMeta meta) { | |
| 1366 | 1392 | |
| 1367 | 1393 | // 第一步:执行原始操作,返回初步结果 |
| 1368 | 1394 | Map<String, Object> step1Result = new HashMap<>(); |
| ... | ... | @@ -1377,6 +1403,7 @@ public class DynamicToolProvider implements ToolProvider { |
| 1377 | 1403 | // session.setSFunPrompts(userMessage); |
| 1378 | 1404 | // 6. 返回确认请求 |
| 1379 | 1405 | // return ToolExecutionResultMessage.from(request,userMessage); |
| 1406 | +// ToolExecutionResultMessage.from(request, text); | |
| 1380 | 1407 | return userMessage; |
| 1381 | 1408 | } |
| 1382 | 1409 | ... | ... |
src/main/resources/templates/chat.html
| ... | ... | @@ -462,11 +462,11 @@ |
| 462 | 462 | <script> |
| 463 | 463 | let sessionId =""; |
| 464 | 464 | let userid= "17522967560005776104370282597000"; |
| 465 | - let username= "钱豹"; | |
| 465 | + let username= "qianb"; | |
| 466 | 466 | let brandsid= "1111111111"; |
| 467 | 467 | let subsidiaryid= "1111111111"; |
| 468 | 468 | let usertype= "sysadmin"; |
| 469 | - let authorization="CE444885A9BCFDDE1FD793F8A0931301E9D7DE6CEDD9DE4B83ECE2219C7829A8F3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1D4CAE6F9AC893752209A98011A981375391D4466816B7D3D1AF306E28B989121C538155B7ADAEE71E899235DC1122F426"; | |
| 469 | + let authorization="CE444885A9BCFDDE1FD793F8A0931301E9D7DE6CEDD9DE4B83ECE2219C7829A8F3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1DDD971C9BA1323777865C7C7C720A0F7D9C9A98822AD3F0E3100F8DBBB5963377538155B7ADAEE71E899235DC1122F426"; | |
| 470 | 470 | let hrefLock = window.location.origin+"/xlyAi"; |
| 471 | 471 | |
| 472 | 472 | const CONFIG = { | ... | ... |