Browse Source

fix: 清理工具提示词中的特殊符号

移除工具描述、工具组描述、系统工具使用提示、研判提示中的 ★、**、<b> 等标记符号;将 stat_* / ai_clean_* 这类提示中的通配星号改成明确的工具名或前缀说明。数据脱敏示例(138****1111 等)保持不变。
cc 2 days ago
parent
commit
55e33864f4

+ 3 - 3
ai-server/src/main/java/com/zsjz/ai/module/agent/insight/context/InsightContextBuilder.java

@@ -259,13 +259,13 @@ public class InsightContextBuilder {
                 .append(")\n\n");
 
         sb.append("### 数据口径(务必按此理解,禁止自创口径)\n");
-        sb.append("- 本图谱来自「对象关系分析」页面**当前画布**:节点 = 对象或实体,边 = 两者之间的资金/通信关系\n");
+        sb.append("- 本图谱来自「对象关系分析」页面当前画布:节点 = 对象或实体,边 = 两者之间的资金/通信关系\n");
         sb.append("- 画布上可能已折叠或隐藏部分节点,且本次只提供裁剪后的子集:\n");
-        sb.append("  **未出现的节点与关系不代表不存在**,不要据此断言「某人没有其他关联」\n");
+        sb.append("  未出现的节点与关系不代表不存在,不要据此断言「某人没有其他关联」\n");
         sb.append("- 节点类型 kind:person 人员 / group 团伙 / house 房屋 / car 车辆 / bank 银行卡 / phone 号码 / unknown 未知 / other 其他\n");
         sb.append("- 边的 label 是画布上显示的关系文字,形如「转账 ¥300,000」「通话 12 次」「持卡人」;\n");
         sb.append("  金额单位为元,次数为交易笔数或通话次数\n");
-        sb.append("- 本快照只有「节点 + 关系」的拓扑与文字,**没有单笔流水、没有单通详单**;\n");
+        sb.append("- 本快照只有「节点 + 关系」的拓扑与文字,没有单笔流水、没有单通详单;\n");
         sb.append("  要下明细结论必须调用 get_trans_records / get_call_records 等工具取数\n");
         sb.append("- 节点名称可能已脱敏(如 138****1111、3301**********1234),这本身是线索(身份不明)\n\n");
 

+ 2 - 2
ai-server/src/main/java/com/zsjz/ai/module/agent/insight/prompt/InsightPromptBuilder.java

@@ -124,7 +124,7 @@ public class InsightPromptBuilder {
             }
             ```
 
-            - 围栏必须**唯一**、内容必须**合法 JSON**(不要注释、不要尾逗号、不要用单引号)。
+            - 围栏必须唯一、内容必须合法 JSON(不要注释、不要尾逗号、不要用单引号)。
             - 解析失败会被降级成纯文本,用户就看不到线索卡片了 —— 这是最影响体验的一种失败。
             - `clues` 若为空数组,`summary` 里必须写明"本轮未发现异常"的原因。
             - `links.params` 的可用字段:
@@ -139,7 +139,7 @@ public class InsightPromptBuilder {
             ## 数据表结构检索
 
             你拥有 search_table_schema 工具,可按语义检索模板库中已登记的表结构(含 AI 智能清洗新建的动态表)。
-            需要自己写 SQL(execute_sql)取数时,**先检索表结构再写 SQL**,禁止凭空猜表名与字段名。
+            需要自己写 SQL(execute_sql)取数时,先检索表结构再写 SQL,禁止凭空猜表名与字段名。
 
             """;
 }

+ 1 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/mcp/AgentScopeMcpToolProvider.java

@@ -320,7 +320,7 @@ public class AgentScopeMcpToolProvider implements ToolCallbackProvider {
 
         tools.add(simple(TOOL_LIST_CASES,
                 "列出本机全部案件(ID、名称、是否已初始化数据库)。"
-                        + "★ 外部客户端接入后必须先调用本工具拿到案件 ID,再调用 open_case 打开案件,"
+                        + "外部客户端接入后必须先调用本工具拿到案件 ID,再调用 open_case 打开案件,"
                         + "否则所有通话/交易/轨迹/画像类工具都会报「缺少案件上下文」。",
                 emptySchema(),
                 (input, ctx) -> {

+ 6 - 6
ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentService.java

@@ -576,13 +576,13 @@ public class AgentService extends ServiceImpl<AgentMapper, AgentEntity> {
 
                 ## 回复输出格式契约(前端渲染依赖,必须遵守)
                 聊天前端只把以下两种围栏代码块渲染为交互组件,其余代码块按普通文本展示:
-                1. 表格类工具(execute_sql 与各业务分析工具)的数据结果会自动导出为 CSV 产物文件,与该次工具调用**成对**展示在「过程」详情里,用户可预览前 200 行、下载全量。
-                   报告正文如需呈现数据,直接写 Markdown 表格(第一行是表头,用 | 分隔列,前端会渲染为表格):把你要展示的行**完整给出**,不要省略列、不要用"……"截断;全量数据以 CSV 产物为准。
-                   ★ 业务口径优先用专用工具:它们的统计口径(未知对手/未知卡号、现金关键词、夜间/连续/敏感通话、大额/共同/相互交易、碰面时间窗口、同住推导、同行比对等)已固化,不要用 execute_sql 手写 SQL 重算同一口径;只有专用工具覆盖不到的场景才自己写 SQL。
-                   ★ 专用工具按业务域分成 6 组:person(人员画像)、call(通话明细)、trans(交易明细与资金)、track(轨迹/出行/快递)、otg(全文检索与行为时间轴)、graph(关系图谱)。当前会话默认只装备 person 组。
-                     当你要用的工具不在当前可见工具列表里时,先调用 reset_equipped_tools 把需要的组名一起写进 to_activate —— 它是**全量替换**语义,不在列表里的组会被卸载,所以要么把当前还要用的组一并列出,要么用完立刻切回。不要把无关组一起打开,会浪费上下文。
+                1. 表格类工具(execute_sql 与各业务分析工具)的数据结果会自动导出为 CSV 产物文件,与该次工具调用成对展示在「过程」详情里,用户可预览前 200 行、下载全量。
+                   报告正文如需呈现数据,直接写 Markdown 表格(第一行是表头,用 | 分隔列,前端会渲染为表格):把你要展示的行完整给出,不要省略列、不要用"……"截断;全量数据以 CSV 产物为准。
+                   业务口径优先用专用工具:它们的统计口径(未知对手/未知卡号、现金关键词、夜间/连续/敏感通话、大额/共同/相互交易、碰面时间窗口、同住推导、同行比对等)已固化,不要用 execute_sql 手写 SQL 重算同一口径;只有专用工具覆盖不到的场景才自己写 SQL。
+                   专用工具按业务域分成 6 组:person(人员画像)、call(通话明细)、trans(交易明细与资金)、track(轨迹/出行/快递)、otg(全文检索与行为时间轴)、graph(关系图谱)。当前会话默认只装备 person 组。
+                     当你要用的工具不在当前可见工具列表里时,先调用 reset_equipped_tools 把需要的组名一起写进 to_activate —— 它是全量替换语义,不在列表里的组会被卸载,所以要么把当前还要用的组一并列出,要么用完立刻切回。不要把无关组一起打开,会浪费上下文。
                 2. 展示常规图表(柱状/折线/饼/散点等)时,必须先调用 render_chart 工具校验 option(option 是结构化 JSON 对象,按该工具的 schema 传,不要传 JSON 字符串),再把工具返回的 ECharts option JSON 原样放入 ```echarts 围栏块中,前端会渲染为图表。
-                3. 展示关系图谱/网络图/关联图时,先调用 get_case_graph(graph 业务组;当前未装备时先用 reset_equipped_tools 激活)取数。**调用参数必须是** `{"query":{"type":[101,201],"personNames":["张三"]}}`:type 与 personNames 都必填;personNames 是数组(不是 personName),至少一个本方姓名;姓名用用户问题中的原名,或 list_persons 返回的 personName 原文。前端会自动把工具返回的 nodeList/edgeList 渲染为可交互关系图。**不要**输出 ```graph 围栏块,**不要**用 render_chart 手写 graph 类型 series(会被拒绝),也不要用文字罗列代替;正文只写结论。
+                3. 展示关系图谱/网络图/关联图时,先调用 get_case_graph(graph 业务组;当前未装备时先用 reset_equipped_tools 激活)取数。调用参数必须是 `{"query":{"type":[101,201],"personNames":["张三"]}}`:type 与 personNames 都必填;personNames 是数组(不是 personName),至少一个本方姓名;姓名用用户问题中的原名,或 list_persons 返回的 personName 原文。前端会自动把工具返回的 nodeList/edgeList 渲染为可交互关系图。不要输出 ```graph 围栏块,不要用 render_chart 手写 graph 类型 series(会被拒绝),也不要用文字罗列代替;正文只写结论。
                 4. 结论、摘要、分析说明正常用 markdown 书写;数据明细一律交给产物文件,不要复述。""";
         return sysPrompt == null || sysPrompt.isBlank() ? renderPrompt.strip() : sysPrompt + renderPrompt;
     }

+ 6 - 6
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/AgentToolRegistry.java

@@ -407,13 +407,13 @@ public class AgentToolRegistry {
                 "人物画像分析(24 个工具):人员枚举与分组、通话/交易画像、常联系与常交易 TOP、"
                         + "通话与交易汇总、运营商归属、银行卡汇总、月度/年度交易趋势,"
                         + "以及 9 类亲密度行为(大额交易、特殊日期交易/通话、同号绑定、持续通话/交易、同行、夜间通话)。"
-                        + "★ 任何「从人出发」的问题都先激活本组:要回答某人的情况,通常得先用 list_person_names "
+                        + "任何「从人出发」的问题都先激活本组:要回答某人的情况,通常得先用 list_person_names "
                         + "确认案件里有哪些人。本组默认已装备。",
                 filterNames(PERSON_TOOLS, toolFilter), active);
 
         if (enabled.contains(GROUP_CALL)) createGroup(toolkit, GROUP_CALL,
                 "通话明细分析(7 个工具):原始通话记录明细,以及夜间通话、连续通话、特殊日期敏感通话的汇总与明细。"
-                        + "★ 问「某人具体打了哪些电话」「夜间/凌晨通话」「连续密集通话」「特殊日期谁和谁联系过」时激活本组。"
+                        + "问「某人具体打了哪些电话」「夜间/凌晨通话」「连续密集通话」「特殊日期谁和谁联系过」时激活本组。"
                         + "只要「通话统计口径」(画像、TOP、汇总)用 person 组即可,本组是通话明细的下钻入口。",
                 filterNames(CALL_TOOLS, toolFilter), active);
 
@@ -421,14 +421,14 @@ public class AgentToolRegistry {
                 "交易明细与资金分析(16 个工具):交易原始明细、大额交易(汇总/按日/明细)、共同交易、代持卡、"
                         + "现金流、连续交易、快速资金流转、理财、定期存款、交易频次、"
                         + "特殊日期敏感交易。"
-                        + "★ 问「某人具体转了哪些账」「大额交易」「资金流向」「谁给谁转了多少钱」时激活本组。",
+                        + "问「某人具体转了哪些账」「大额交易」「资金流向」「谁给谁转了多少钱」时激活本组。",
                 filterNames(TRANS_TOOLS, toolFilter), active);
 
         if (enabled.contains(GROUP_TRACK)) createGroup(toolkit, GROUP_TRACK,
                 "轨迹、出行与快递分析(16 个工具):基站轨迹(汇总/通联明细)、境外本地昼夜活动、"
                         + "快递收寄(按人/按号码/原始单号)、人员碰面(汇总/明细)、"
                         + "同住(汇总/明细/旅馆住宿/同住服务)、同行出行(汇总/行程明细/票务原始记录)。"
-                        + "★ 问「某人去过哪」「两人碰过面吗」「同住/开房」「同行航班车次」「快递收寄」时激活本组。",
+                        + "问「某人去过哪」「两人碰过面吗」「同住/开房」「同行航班车次」「快递收寄」时激活本组。",
                 filterNames(TRACK_TOOLS, toolFilter), active);
 
         //createGroup(toolkit, GROUP_OTG,
@@ -440,7 +440,7 @@ public class AgentToolRegistry {
 
         if (enabled.contains(GROUP_GRAPH)) createGroup(toolkit, GROUP_GRAPH,
                 "关系图谱(1 个工具):案件关系网络取数(节点 + 连线)。"
-                        + "★ 用户要「关系图谱」「资金往来网络」「通话关系图」「关联图」时激活本组:"
+                        + "用户要「关系图谱」「资金往来网络」「通话关系图」「关联图」时激活本组:"
                         + "先用 get_case_graph 取数,前端会自动渲染成关系图;"
                         + "调用时 type 与 personNames 必填,personNames 是数组(如 [\"张三\"],不是 personName)。",
                 filterNames(GRAPH_TOOLS, toolFilter), active);
@@ -450,7 +450,7 @@ public class AgentToolRegistry {
                         + "「上传后解析不了表格」的文件,上传时已由大模型识别出业务分类与摘要,"
                         + "可用 list_file_profiles 按分类/关键词检索清单、stat_file_profiles_by_category 看构成、"
                         + "get_file_sample 读某份材料的正文片段(≤4000 字,是唯一能看到材料原文的入口)。"
-                        + "★ 问「案件里有哪些材料」「有没有笔录/合同/判决书/报表」「这份材料写了什么」"
+                        + "问「案件里有哪些材料」「有没有笔录/合同/判决书/报表」「这份材料写了什么」"
                         + "「有没有提到某某」时激活本组。"
                         + "注意:xls/xlsx/csv 解析出的通话/交易/人员等业务数据不在这里,用对应域的工具查。",
                 filterNames(FILE_TOOLS, toolFilter), active);

+ 8 - 8
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/CallAnalysisTool.java

@@ -106,13 +106,13 @@ public class CallAnalysisTool {
     @Tool(name = "get_call_records",
           description = "查询通话原始明细(一行一通电话),可按本方姓名/号码、对方姓名/号码、主被叫、通话类型、"
                   + "运营商、基站、通话日期范围筛选。"
-                  + "★ 问「某人的通话详单」「和某人/某号码通过几次电话、什么时候打的」「某时段的通话记录」时用本工具,"
+                  + "问「某人的通话详单」「和某人/某号码通过几次电话、什么时候打的」「某时段的通话记录」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 本工具的业务口径(未知对手/未知号码、运营商模糊匹配等)已固化。"
                   + "结果以可翻页表格返回,完整结果集会自动导出为 CSV 产物(全量),因此不加行数上限;"
                   + "但仍建议给出 personName / personPhones / otherName / otherPhones / 日期范围之一,"
                   + "以免全案话单扫描过重、也便于聚焦结论。"
                   + "若要看的是「夜间联系」「长期持续联系」「敏感日期联系」这类分析结论,"
-                  + "请改用 stat_call_night_* / stat_call_continuous_* / stat_call_sensitive_* 工具。",
+                  + "请改用 stat_call_night_summary / stat_call_night_detail / stat_call_continuous_summary / stat_call_continuous_detail / stat_call_sensitive_summary / stat_call_sensitive_detail 工具。",
           readOnly = true)
     public ToolResultBlock getCallRecords(
             @ToolParam(name = "query", description = "通话记录筛选条件,字段全部可选(但至少要能定位到人/号码/时段)")
@@ -152,7 +152,7 @@ public class CallAnalysisTool {
           description = "统计夜间通话的第一层汇总:按「本方姓名 + 对方姓名 + 对方号码」汇总夜间通话次数与涉及月份数。"
                   + "夜间时段默认 18:00—次日 06:00(nightStart=18、nightEnd=6,结束小于起始表示跨零点)。"
                   + "只统计有夜间通话的月份数 ≥ monthVal(默认 2)的对象,即「不止偶发一次」。"
-                  + "★ 问「谁经常深夜联系」「夜间通话对象有哪些」「夜间长期联系」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「谁经常深夜联系」「夜间通话对象有哪些」「夜间长期联系」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "personNames 不传则统计全案所有对象人员。"
                   + "拿到结果后若要下钻某人按小时段的明细,用 stat_call_night_detail 并传入该行的 personName。",
           readOnly = true)
@@ -181,7 +181,7 @@ public class CallAnalysisTool {
      */
     @Tool(name = "stat_call_night_detail",
           description = "统计夜间通话的第二层明细:在夜间时段内按「小时段」展开某个人与其对方的通话次数。"
-                  + "★ personName 或 personNames 至少传一个(用于定位某个人);通常先调用 stat_call_night_summary 拿到第一层结果再下钻。"
+                  + "personName 或 personNames 至少传一个(用于定位某个人);通常先调用 stat_call_night_summary 拿到第一层结果再下钻。"
                   + "otherName / otherPhone 可进一步限定对方(精确匹配,传「未知对手」「未知号码」表示对方信息缺失)。"
                   + "时段参数与第一层一致(nightStart/nightEnd 默认 18/6)。",
           readOnly = true)
@@ -223,7 +223,7 @@ public class CallAnalysisTool {
           description = "统计持续联系(连续通话)的第一层汇总:找出「跨多个月份持续通话」的本方与对方组合,"
                   + "返回持续月份数、通话总次数、连续起止月份。"
                   + "只统计连续通话月数 ≥ monthVal(默认 2)的组合。"
-                  + "★ 问「谁和谁长期保持联系」「连续几个月都有通话」「持续联系的对象」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「谁和谁长期保持联系」「连续几个月都有通话」「持续联系的对象」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "personNames 不传则统计全案所有对象人员。"
                   + "拿到结果后若要展开每对关系的起止月份,用 stat_call_continuous_detail。",
           readOnly = true)
@@ -254,7 +254,7 @@ public class CallAnalysisTool {
     @Tool(name = "stat_call_continuous_detail",
           description = "统计持续联系的第二层明细:按「本方号码 + 对方姓名 + 对方号码」展开每对关系的"
                   + "连续通话起止月份与通话次数。"
-                  + "★ 通常先调用 stat_call_continuous_summary 拿到第一层结果再下钻。"
+                  + "通常先调用 stat_call_continuous_summary 拿到第一层结果再下钻。"
                   + "personNames / personName 限定本方,otherName / otherPhone 限定对方(精确匹配)。"
                   + "monthVal 阈值建议与第一层保持一致(默认 2)。",
           readOnly = true)
@@ -285,7 +285,7 @@ public class CallAnalysisTool {
     @Tool(name = "stat_call_sensitive_summary",
           description = "统计敏感日期(案件配置的特殊日期/节假日)内的通话联系第一层:"
                   + "按「本方姓名 + 对方姓名 + 对方号码」汇总落在特殊日期内的通话次数,按次数降序。"
-                  + "★ 问「敏感日期期间和谁联系过」「特殊日期通话情况」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「敏感日期期间和谁联系过」「特殊日期通话情况」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "keyword 按对方姓名「或」对方号码精确匹配(单值,不是模糊搜索),只看某个特定对象时才传。"
                   + "personNames 不传则统计全案所有对象人员。"
                   + "拿到结果后若要下钻到具体话单,用 stat_call_sensitive_detail 并传入该行的 personName/otherName/otherPhone。",
@@ -315,7 +315,7 @@ public class CallAnalysisTool {
      */
     @Tool(name = "stat_call_sensitive_detail",
           description = "查询敏感日期(特殊日期/节假日)内的通话原始明细,返回话单并带上命中的节日名称,按通话时间倒序。"
-                  + "★ 通常先调用 stat_call_sensitive_summary 拿到第一层结果,再用 personName + otherName/otherPhone 下钻到具体话单。"
+                  + "通常先调用 stat_call_sensitive_summary 拿到第一层结果,再用 personName + otherName/otherPhone 下钻到具体话单。"
                   + "建议至少传 personName 或 otherName/otherPhone 定位,否则明细可能过多。",
           readOnly = true)
     public ToolResultBlock statCallSensitiveDetail(

+ 7 - 7
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/FileAnalysisTool.java

@@ -115,12 +115,12 @@ public class FileAnalysisTool {
     @Tool(name = "list_file_profiles",
           description = "检索案件里的「非表格材料」清单(txt / doc / docx / pdf / ppt / 压缩包 等上传后解析不了表格的文件,"
                   + "上传时已由大模型识别出业务分类与摘要,结果存在 file_ai_profile 表)。"
-                  + "★ 用户问「案件里有哪些材料」「有没有笔录 / 合同 / 判决书 / 报表」「哪份文件和某某有关」时用本工具。"
+                  + "用户问「案件里有哪些材料」「有没有笔录 / 合同 / 判决书 / 报表」「哪份文件和某某有关」时用本工具。"
                   + "可按 category(业务分类)与 keyword(文件名/摘要/关键词模糊匹配)过滤。"
                   + "返回可翻页表格,列为:fileId / fileName / fileType / mimeType / category(中文分类) / summary(一句话摘要) / "
                   + "keywords / sampleLength(样本字数) / parseStatus(识别状态) / failReason。"
-                  + "★ 本工具只给元信息,不含正文;要看某份材料到底写了什么,拿 fileId 调 get_file_sample。"
-                  + "★ 这里只有非表格文件:xls/xlsx/csv 解析出的通话/交易/人员等业务数据请用对应域的工具查。",
+                  + "本工具只给元信息,不含正文;要看某份材料到底写了什么,拿 fileId 调 get_file_sample。"
+                  + "这里只有非表格文件:xls/xlsx/csv 解析出的通话/交易/人员等业务数据请用对应域的工具查。",
           readOnly = true)
     public ToolResultBlock listFileProfiles(
             @ToolParam(name = "query", description = "筛选条件,全部可空(不传即查全部)")
@@ -145,11 +145,11 @@ public class FileAnalysisTool {
      */
     @Tool(name = "stat_file_profiles_by_category",
           description = "按业务分类统计案件里「非表格材料」的数量分布。"
-                  + "★ 想先摸清案件材料构成(各类材料各有多少份、都是什么格式)时用本工具,"
+                  + "想先摸清案件材料构成(各类材料各有多少份、都是什么格式)时用本工具,"
                   + "比把几百条 list_file_profiles 全部读一遍省得多。"
                   + "返回表格,列为:category(中文分类) / count(份数) / fileTypes(该分类下出现过的文件后缀,逗号分隔)。"
                   + "按份数从多到少排序;识别失败或已跳过的记录归入「" + UNKNOWN_CATEGORY + "」一行。"
-                  + "★ 想看某分类下的具体文件,用 list_file_profiles 带 category 再查一次。",
+                  + "想看某分类下的具体文件,用 list_file_profiles 带 category 再查一次。",
           readOnly = true)
     public ToolResultBlock statFileProfilesByCategory(
             @ToolParam(name = "query", description = "筛选条件,可空(不传即统计全部)")
@@ -172,13 +172,13 @@ public class FileAnalysisTool {
      */
     @Tool(name = "get_file_sample",
           description = "读取某份非表格材料的正文片段(上传时用 Tika 抽取的文本样本,原件不落盘、无法重新解析)。"
-                  + "★ 需要看材料里到底写了什么(笔录内容、合同条款、报表口径、举报信正文)时必须用本工具,"
+                  + "需要看材料里到底写了什么(笔录内容、合同条款、报表口径、举报信正文)时必须用本工具,"
                   + "list_file_profiles 只给摘要不给正文。fileId 取自 list_file_profiles 的 fileId 列,必传。"
                   + "返回 JSON:{fileId, fileName, category, summary, keywords, mimeType, sampleLength, "
                   + "sampleText(正文片段), truncated, failReason}。"
                   + "sampleText 最多 4000 字符(采集时就截断到这个上限),超长时 truncated=true 且只能看到开头;"
                   + "sampleText 为 null 表示这份材料没有可用文本(扫描件、加密包、纯图片等),原因见 failReason。"
-                  + "★ 一次只读一份:需要对比多份材料时分多次调用,不要一次拉多份正文。",
+                  + "一次只读一份:需要对比多份材料时分多次调用,不要一次拉多份正文。",
           readOnly = true)
     public ToolResultBlock getFileSample(
             @ToolParam(name = "query", description = "材料 ID 与返回长度")

+ 2 - 2
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/GraphAnalysisTool.java

@@ -97,10 +97,10 @@ public class GraphAnalysisTool {
      */
     @Tool(name = "get_case_graph",
           description = "查询指定人员的案件关系图谱原始数据(节点 + 连线),是画关系图谱的取数入口。"
-                  + "★ 调用参数必须是:{\"query\":{\"type\":[101,201],\"personNames\":[\"吴得福\"]}}。"
+                  + "调用参数必须是:{\"query\":{\"type\":[101,201],\"personNames\":[\"吴得福\"]}}。"
                   + "type 与 personNames 都必填;personNames 是数组(不是 personName),至少一个本方姓名;"
                   + "姓名用用户问题中的原名,或 list_persons 返回的 personName 原文。"
-                  + "★ 用户要「关系图谱」「资金往来网络」「通话关系图」「人-人/人-卡关联」时先用本工具取数,"
+                  + "用户要「关系图谱」「资金往来网络」「通话关系图」「人-人/人-卡关联」时先用本工具取数,"
                   + "前端会自动把返回的 nodeList/edgeList 渲染成可交互关系图;"
                   + "不要用 execute_sql 手写这段 SQL(它依赖临时表与人员库缓存)。"
                   + "返回 JSON 结构:{\"nodeList\":[{\"node\":节点标识,\"nodecn\":节点显示名,"

+ 3 - 3
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/GraphToolSpecs.java

@@ -55,7 +55,7 @@ public final class GraphToolSpecs {
         /** 核心人员姓名列表 */
         @ToolParam(name = "personNames",
                    description = "本方(被调查对象)姓名数组,必传,至少一个,例如 [\"吴得福\"]。"
-                           + "★ 字段名是 personNames(复数数组),不是 personName;"
+                           + "字段名是 personNames(复数数组),不是 personName;"
                            + "姓名请用用户问题中的原名或 list_persons 返回的 personName 原文。"
                            + "图谱以这些人为核心节点向外扩展一层关系",
                    required = true)
@@ -63,13 +63,13 @@ public final class GraphToolSpecs {
 
         /** 日期下限 */
         @ToolParam(name = "startDate",
-                   description = "日期下限,格式 yyyy-MM-dd(含当天);★ 必须与 endDate 同时传才生效,只传一个会被忽略",
+                   description = "日期下限,格式 yyyy-MM-dd(含当天);必须与 endDate 同时传才生效,只传一个会被忽略",
                    required = false)
         public String startDate;
 
         /** 日期上限 */
         @ToolParam(name = "endDate",
-                   description = "日期上限,格式 yyyy-MM-dd(含当天);★ 必须与 startDate 同时传才生效",
+                   description = "日期上限,格式 yyyy-MM-dd(含当天);必须与 startDate 同时传才生效",
                    required = false)
         public String endDate;
 

+ 15 - 15
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/PersonAnalysisTool.java

@@ -115,7 +115,7 @@ public class PersonAnalysisTool {
     @Tool(name = "get_person_call_profile",
           description = "查询某个对象的通话数据画像汇总(单行):名下号码数、按运营商分组的号码明细、通话总次数、"
                   + "涉及的对方号码数、通话日期范围(最早/最晚)、主叫/被叫/其他占比。"
-                  + "★ 问「某人的通话总体情况」「名下有几个号码、分别属于哪个运营商」「通话从哪天覆盖到哪天」"
+                  + "问「某人的通话总体情况」「名下有几个号码、分别属于哪个运营商」「通话从哪天覆盖到哪天」"
                   + "「主叫多还是被叫多」时用本工具,不要用 execute_sql 手写 SQL —— 号码归属与占比口径已固化。"
                   + "personName 必传(精确匹配,须与人员库中的姓名一致)。"
                   + "本工具只回汇总,不含具体话单;要看通话明细请用 get_call_records,看最常联系对象用 stat_person_call_top10。",
@@ -141,7 +141,7 @@ public class PersonAnalysisTool {
     @Tool(name = "stat_person_call_station",
           description = "统计某个对象通话话单的基站分布:按「基站大区号 + 基站小区号」分组,返回通话次数、"
                   + "基站名称、经纬度,按通话次数降序。"
-                  + "★ 问「某人常出现在哪些基站/位置」「活动轨迹的基站落点」「哪个基站通话最频繁」时用本工具,"
+                  + "问「某人常出现在哪些基站/位置」「活动轨迹的基站落点」「哪个基站通话最频繁」时用本工具,"
                   + "不要用 execute_sql 手写 SQL。personName 必传。"
                   + "若要按日期范围收窄,请改用 execute_sql 直接查话单表(本接口无日期条件)。",
           readOnly = true)
@@ -171,9 +171,9 @@ public class PersonAnalysisTool {
     @Tool(name = "stat_person_call_top10",
           description = "统计某个对象最常联系的对方号码:按对方号码分组,返回通话次数,按次数降序排列(页面只展示前 10,"
                   + "本工具返回全部并按 pageSize 分页给模型)。对方信息缺失的话单归入「未知卡号」一行。"
-                  + "★ 问「某人联系最多的是谁/哪个号码」「最常联系的人」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「某人联系最多的是谁/哪个号码」「最常联系的人」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "personName 必传;startDate / endDate 可选,用于限定通话日期范围。"
-                  + "注意:本接口按<b>号码</b>聚合,若想看「通话次数 + 主被叫 + 通话类型」的综合汇总,用 stat_call_record_summary。",
+                  + "注意:本接口按号码聚合,若想看「通话次数 + 主被叫 + 通话类型」的综合汇总,用 stat_call_record_summary。",
           readOnly = true)
     public ToolResultBlock statPersonCallTop10(
             @ToolParam(name = "query", description = "常联系对象统计条件,personName 必传")
@@ -202,7 +202,7 @@ public class PersonAnalysisTool {
      */
     @Tool(name = "stat_call_record_summary",
           description = "统计某个对象的通话记录汇总:按「本方号码 + 对方姓名 + 对方号码」分组,返回通话次数,按次数降序。"
-                  + "★ 问「某人跟某个对象通过几次电话」「某人的通话对象清单及各自次数」「和未知对手通过几次」时用本工具;"
+                  + "问「某人跟某个对象通过几次电话」「某人的通话对象清单及各自次数」「和未知对手通过几次」时用本工具;"
                   + "只要原始话单(每行一通电话、带时间与时长)请用 get_call_records,只看对方号码维度用 stat_person_call_top10。"
                   + "personName 必传;otherName 精确匹配(查对方信息缺失传「未知对手」);otherPhone 精确匹配;"
                   + "startDate / endDate 限定通话日期范围。",
@@ -242,7 +242,7 @@ public class PersonAnalysisTool {
     @Tool(name = "get_person_trans_profile",
           description = "查询某个对象的交易数据画像汇总(单行):名下卡号数、按银行分组的卡号明细、"
                   + "交易总额/总收入/总支出/交易笔数、卡内余额合计等。"
-                  + "★ 问「某人的资金总体情况」「名下有几张卡、分别属于哪家银行」「交易总额多少」「卡内余额多少」时用本工具,"
+                  + "问「某人的资金总体情况」「名下有几张卡、分别属于哪家银行」「交易总额多少」「卡内余额多少」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 收支与余额口径已固化。"
                   + "personName 必传(精确匹配)。本工具只回汇总,不含交易明细;"
                   + "要看具体交易用 execute_sql 或 stat_trans_record_summary,看交易对手用 stat_person_trans_top10。",
@@ -268,7 +268,7 @@ public class PersonAnalysisTool {
     @Tool(name = "stat_person_trans_top10",
           description = "统计某个对象最常交易的对手:按 indicator 指定的字段聚合(默认对方姓名,也可按对方卡号、IP、MAC、交易网点),"
                   + "返回交易笔数、交易总额、收入合计、支出合计,按 dimension 指定指标降序(默认交易金额,可选交易次数)。"
-                  + "★ 问「某人和谁交易最多」「交易金额最大的对手」「某人的资金流向哪些网点/IP/MAC」时用本工具,"
+                  + "问「某人和谁交易最多」「交易金额最大的对手」「某人的资金流向哪些网点/IP/MAC」时用本工具,"
                   + "不要用 execute_sql 手写 SQL。personName 必传;"
                   + "transCount / transAmount 可配比较符号(transCountSymbol / transAmountSymbol)做阈值筛选,"
                   + "如「交易金额 >= 50000」传 transAmount=50000、transAmountSymbol=ge。",
@@ -313,11 +313,11 @@ public class PersonAnalysisTool {
     @Tool(name = "stat_trans_record_summary",
           description = "统计交易记录汇总:按「本方姓名 + 本方卡号 + 对方姓名 + 对方卡号 + 银行」分组,返回交易笔数、"
                   + "交易总额、收入合计、支出合计,按交易总额降序。"
-                  + "★ 问「某人与某对手的资金往来总额」「某人名下各卡与对方的交易情况」「交易次数 >= N 的对手」"
+                  + "问「某人与某对手的资金往来总额」「某人名下各卡与对方的交易情况」「交易次数 >= N 的对手」"
                   + "「只看现金交易」时用本工具,不要用 execute_sql 手写 SQL —— 分组与收支口径已固化。"
                   + "personNames 是本方姓名(可传多个,不传则全案);otherName 精确匹配(对方信息缺失传「未知对手」);"
                   + "personCardNo / otherCardNo 精确匹配单值;source 传 1 只看现金交易。"
-                  + "注意:本接口<b>不认</b>单个 personName,限定某个人请填 personNames=[\"张三\"]。",
+                  + "注意:本接口不认单个 personName,限定某个人请填 personNames=[\"张三\"]。",
           readOnly = true)
     public ToolResultBlock statTransRecordSummary(
             @ToolParam(name = "query", description = "交易记录汇总条件,字段全部可选")
@@ -362,7 +362,7 @@ public class PersonAnalysisTool {
     @Tool(name = "get_trans_bank_card_info",
           description = "统计指定银行卡号的交易汇总:收入合计、支出合计、交易总额、交易笔数、最新余额,"
                   + "并关联账户开户信息补齐持卡人、账户状态、开户日期、销户日期、存款期限,另算净额(收支差额绝对值)。"
-                  + "★ 问「这几张卡的进出账情况」「卡里还剩多少钱、是否已销户」「某张卡的交易总额」时用本工具,"
+                  + "问「这几张卡的进出账情况」「卡里还剩多少钱、是否已销户」「某张卡的交易总额」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 余额取最新一笔、净额口径已固化。"
                   + "cardNoList 必传(至少一个卡号);bankName 建议一起传(会回填到结果的 bankName 列,便于识别)。"
                   + "本接口无分页条件,一次最多返回 10000 行。",
@@ -393,7 +393,7 @@ public class PersonAnalysisTool {
      */
     @Tool(name = "stat_trans_amount_trend_month",
           description = "按月统计交易金额趋势:按「年-月 + 收支方向」分组,返回该月交易金额合计,按月份倒序。"
-                  + "★ 问「某人每个月的资金进出变化」「交易金额的月度走势」「某个月收入/支出多少」时用本工具,"
+                  + "问「某人每个月的资金进出变化」「交易金额的月度走势」「某个月收入/支出多少」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 月份截取与收支分组口径已固化。"
                   + "personName 可选(不传则统计全案所有对象,通常应传);otherName 可限定交易对手;"
                   + "startDate / endDate 限定交易日期范围。按年看趋势请用 stat_trans_amount_trend_year。",
@@ -416,7 +416,7 @@ public class PersonAnalysisTool {
      */
     @Tool(name = "stat_trans_amount_trend_year",
           description = "按年统计交易金额趋势:按「年份 + 收支方向」分组,返回该年交易金额合计,按年份倒序。"
-                  + "★ 问「某人每一年的资金进出变化」「交易金额的年度走势」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「某人每一年的资金进出变化」「交易金额的年度走势」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "personName 可选(不传则统计全案所有对象,通常应传);otherName 可限定交易对手;"
                   + "startDate / endDate 限定交易日期范围。按月看趋势请用 stat_trans_amount_trend_month。",
           readOnly = true)
@@ -442,9 +442,9 @@ public class PersonAnalysisTool {
           description = "查询某个对象的亲密度总览:把大额转账、特殊日期特殊转账、异卡同号、频繁联系、夜间通话、"
                   + "特殊日期联系、频繁交易、同交通工具出行八种行为按关系人合并,返回每个关系人的亲密度分值、"
                   + "命中的行为标签(含权重)与行为明细计数,按分值降序,最多 100 个关系人。"
-                  + "★ 问「某人和谁关系最密切」「某人的亲密关系人有哪些、亲密度多少」「为什么某两人关系近」时"
+                  + "问「某人和谁关系最密切」「某人的亲密关系人有哪些、亲密度多少」「为什么某两人关系近」时"
                   + "优先用本工具,不要用 execute_sql 手写 SQL —— 分值由案件配置的权重与分值表计算。"
-                  + "personName 必传。需要看某一种行为的明细排行时,再用对应的 stat_intimacy_* 工具下钻。",
+                  + "personName 必传。需要看某一种行为的明细排行时,再用对应的 stat_intimacy 系列 工具下钻。",
           readOnly = true)
     public ToolResultBlock statIntimacySummary(
             @ToolParam(name = "personName", description = "本方(机主)姓名,精确匹配,必传,如「张三」") String personName) {
@@ -461,7 +461,7 @@ public class PersonAnalysisTool {
     @Tool(name = "list_persons",
           description = "列出当前案件的全部对象人员及其基本信息:姓名、证件号、出生日期、性别、年龄、地址、民族、籍贯、"
                   + "户口所在地、婚姻状态、标签,以及名下号码列表和银行卡号列表。"
-                  + "★ 问「本案有哪些人」「某人的证件号/住址/名下号码」「人员的标签」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「本案有哪些人」「某人的证件号/住址/名下号码」「人员的标签」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "无入参。只需要姓名清单(更省 token)时用 list_person_names;"
                   + "要按号码/卡号反查归属人,可配合 list_persons 的结果或 execute_sql。",
           readOnly = true)

+ 1 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/PythonAnalysisTool.java

@@ -38,7 +38,7 @@ public class PythonAnalysisTool {
                   + "用 duckdb.connect(ETL_DB_PATH, read_only=True) 只读访问(示例:"
                   + "import os, duckdb; con = duckdb.connect(os.environ['ETL_DB_PATH'], read_only=True))。"
                   + "该路径指向本次执行开始时导出的一致性快照副本(表与视图都在),"
-                  + "★ 不要试图连接案件原库文件(系统已持有其写锁,会报「另一个程序正在使用此文件」)。"
+                  + "不要试图连接案件原库文件(系统已持有其写锁,会报「另一个程序正在使用此文件」)。"
                   + "用 print() 输出分析结论文本;matplotlib 图表用 plt.savefig('chart.png') 保存"
                   + "(系统自动捕获并展示给用户)。"
                   + "适合 execute_sql 无法完成的复杂分析:统计建模、多步数据变换、专业可视化。",

+ 1 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/RagSchemaSearchTool.java

@@ -36,7 +36,7 @@ public class RagSchemaSearchTool {
           description = "按语义检索模板库中已登记的表结构(不含未登记进模板库的表),返回最相关的表:"
                   + "物理表名/中文名,以及字段的物理列名、类型、中文注释。"
                   + "当需要了解有哪些数据表、字段含义,或为编写 SQL 挑选合适的表时,必须先调用本工具,而不是凭空猜测表名。"
-                  + "AI 智能清洗新建的动态表(ai_clean_*)同样会被检索到。",
+                  + "AI 智能清洗新建的动态表(ai_clean_ 前缀)同样会被检索到。",
           readOnly = true)
     public ToolResultBlock searchTableSchema(
             @ToolParam(name = "query", description = "自然语言查询,必填,例如:包含手机号和身份证号的表") String query,

+ 1 - 1
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/SqlAnalysisTool.java

@@ -129,7 +129,7 @@ public class SqlAnalysisTool {
      * 物理表名/物理列名见 {@link #loadMetadataTableCatalog()}。
      */
     @Tool(name = "list_tables",
-          description = "列出模板库中已登记的可查表(既有模板表 + AI 智能清洗新建的动态表 ai_clean_*):"
+          description = "列出模板库中已登记的可查表(既有模板表 + AI 智能清洗新建的动态表 ai_clean_ 前缀):"
                   + "物理表名/中文名,以及字段名(物理列名)、字段类型、中文注释与是否必填。"
                   + "只有登记进模板库元数据的表才能用 execute_sql 查询。"
                   + "当 search_table_schema 语义检索未命中合适表,或需要精确确认表名和字段名时调用。",

+ 16 - 16
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/TrackAnalysisTool.java

@@ -116,7 +116,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "stat_track_cell_tower_summary",
           description = "按「人员 + 基站」汇总基站轨迹:返回每个人在每个基站下的通话次数、首末通话日期与基站经纬度。"
-                  + "★ 问「某人在哪些基站出现过」「基站轨迹」「活动区域」时用本工具,不要用 execute_sql 手写 SQL —— "
+                  + "问「某人在哪些基站出现过」「基站轨迹」「活动区域」时用本工具,不要用 execute_sql 手写 SQL —— "
                   + "本工具已内置表名切换与人员范围口径。"
                   + "不传 personNames 时统计案件内全部人员。结果是可翻页表格,先用本工具拿汇总,"
                   + "再看某个基站的详细通联记录时用 stat_track_cell_tower_detail。",
@@ -140,7 +140,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "stat_track_cell_tower_detail",
           description = "查指定基站下的通联明细:某人在某个基站(按 LAC/CID 或基站名称定位)与各号码的通话次数。"
-                  + "★ 参数取自 stat_track_cell_tower_summary 的结果列(personName / personStationName / "
+                  + "参数取自 stat_track_cell_tower_summary 的结果列(personName / personStationName / "
                   + "personRegionId / personCellId),用本工具下钻,不要用 execute_sql 手写 SQL。"
                   + "注意本工具的 personStationName 是精确匹配(汇总工具是模糊匹配)。",
           readOnly = true)
@@ -171,7 +171,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_track_en_local_summary",
           description = "按「人员 + 基站」统计昼夜活动次数:统计白天(06:00 至 morningTime)与夜间"
                   + "(nightTime 起)出现在同一基站的次数合计,可用 dayNightTotal 卡阈值挑出高频活动点。"
-                  + "★ 问「某人昼夜活动规律」「夜间活动基站」「异常作息」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「某人昼夜活动规律」「夜间活动基站」「异常作息」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "不传 personNames 时统计案件内全部人员。下钻明细用 stat_track_en_local_detail。",
           readOnly = true)
     public ToolResultBlock statTrackEnLocalSummary(
@@ -193,7 +193,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "stat_track_en_local_detail",
           description = "查昼夜活动的通话明细:返回满足昼夜时段条件的原始通话记录(一行一通)。"
-                  + "★ 参数取自 stat_track_en_local_summary 的结果列(personNames / personRegionId / "
+                  + "参数取自 stat_track_en_local_summary 的结果列(personNames / personRegionId / "
                   + "personCellId / personStationName),用本工具下钻看具体通话,不要用 execute_sql 手写 SQL。"
                   + "morningTime / nightTime 需与汇总查询保持一致,否则明细与汇总对不上。",
           readOnly = true)
@@ -222,7 +222,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_express_summary",
           description = "按「人员 + 地址」汇总快递收寄情况:返回每个人在每个地址的快递总量、收件数、寄件数,"
                   + "并给出该组合对应的记录 id 集合(ids 列)。"
-                  + "★ 问「某人收寄了哪些快递」「快递地址」「收件/寄件数量」时用本工具,不要用 execute_sql 手写 SQL —— "
+                  + "问「某人收寄了哪些快递」「快递地址」「收件/寄件数量」时用本工具,不要用 execute_sql 手写 SQL —— "
                   + "本工具已按人员电话做归属聚合。不传 personNames 时统计案件内全部人员。"
                   + "看具体快递单号明细时,把 ids 列拆成数组传给 get_express_records。",
           readOnly = true)
@@ -246,7 +246,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_express_by_phone",
           description = "按收/寄件人电话聚合快递地址:直接按号码查全量快递数据,返回每个「姓名 + 地址」的"
                   + "快递量、收件数、寄件数(ids 列为逗号分隔的记录 id)。"
-                  + "★ 已知号码、或需要跳出案件人员范围查快递时用本工具(stat_express_summary 只查案件内人员);"
+                  + "已知号码、或需要跳出案件人员范围查快递时用本工具(stat_express_summary 只查案件内人员);"
                   + "不传 personPhones 会扫描全量快递数据,慎用。",
           readOnly = true)
     public ToolResultBlock statExpressByPhone(
@@ -268,7 +268,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "get_express_records",
           description = "查询快递原始明细(一行一张快递单):单号、收寄件人、电话、地址、寄件日期等。"
-                  + "★ 问「某张快递单的详情」「按单号/姓名/地址查快递」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「某张快递单的详情」「按单号/姓名/地址查快递」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "ids 取 stat_express_summary 或 stat_express_by_phone 结果的 ids 列(逗号分隔,需拆成数组)。"
                   + "注意 ids 与其它条件是 AND 关系;一个条件都不传会扫描全量快递数据。",
           readOnly = true)
@@ -297,7 +297,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_track_meet_summary",
           description = "统计轨迹碰面:找出「在同一基站、且通话时间相差不超过 timeInterval 分钟」的人员配对,"
                   + "按配对汇总碰面总次数,并展开每个基站的碰面次数。"
-                  + "★ 问「谁和谁碰过面」「两人在哪些基站同时出现」时用本工具,不要用 execute_sql 手写 SQL —— "
+                  + "问「谁和谁碰过面」「两人在哪些基站同时出现」时用本工具,不要用 execute_sql 手写 SQL —— "
                   + "碰面的时间窗口自关联口径已固化在服务里。不传 personNames 时统计案件内全部人员。"
                   + "下钻某对人员在某基站的具体碰面记录时用 stat_track_meet_detail。",
           readOnly = true)
@@ -320,7 +320,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "stat_track_meet_detail",
           description = "查某对人员在某基站的具体碰面记录:返回碰面时间、时间差(分钟)、基站名称等。"
-                  + "★ 四个参数全部取自 stat_track_meet_summary 的结果列(lac / cid / persona / personb),"
+                  + "四个参数全部取自 stat_track_meet_summary 的结果列(lac / cid / persona / personb),"
                   + "persona 与 personb 有先后顺序,必须与汇总结果一致,不要自行调换。"
                   + "timeInterval 需与汇总查询保持一致,否则明细条数与汇总对不上。",
           readOnly = true)
@@ -359,7 +359,7 @@ public class TrackAnalysisTool {
           description = "统计同住关系:综合旅馆住宿与同行服务数据,推导「确定同住」(同酒店且住宿时段有交集)"
                   + "与「疑似同住」(同行 + 24 小时内住宿,或同酒店且入住时间在 timeGap 小时内),"
                   + "按被调查对象分组并展开每个同住人。"
-                  + "★ 问「某人和谁同住过」「同住酒店」「开房记录」时用本工具,不要用 execute_sql 手写 SQL —— "
+                  + "问「某人和谁同住过」「同住酒店」「开房记录」时用本工具,不要用 execute_sql 手写 SQL —— "
                   + "同住的推导规则(时间交集、24 小时窗口)完全固化在服务里,手写 SQL 算不出来。"
                   + "不传 personNames 时统计案件内全部人员;不传 timeGap 只返回确定同住。"
                   + "下钻某个同住人的明细时用 stat_together_live_detail。",
@@ -386,7 +386,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "stat_together_live_detail",
           description = "查同住关系明细(一行一条同住关系):被调查对象、同住人、同住酒店、同住次数、入住时间、"
-                  + "疑似同住标记。★ 参数取自 stat_together_live_summary 的结果列(owner_personName / "
+                  + "疑似同住标记。参数取自 stat_together_live_summary 的结果列(owner_personName / "
                   + "liveWithName / liveWithCertNo),用本工具下钻或做交叉筛选,不要用 execute_sql 手写 SQL。"
                   + "personName 与 liveWithName 是精确匹配;timeGap / hasTogether 需与汇总查询一致。",
           readOnly = true)
@@ -413,7 +413,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "get_hotel_stay_detail",
           description = "查旅馆住宿记录明细(一行一次入住):被调查对象与同住人在某家酒店的入住/退房时间、"
-                  + "房间信息、疑似同住标记。★ 问「某人和某人在某酒店住过几次、具体什么时间」时用本工具,"
+                  + "房间信息、疑似同住标记。问「某人和某人在某酒店住过几次、具体什么时间」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 住宿时间交集的判定已固化在服务里。"
                   + "personName 与 hotelName 必填,两者共同定位。",
           readOnly = true)
@@ -444,7 +444,7 @@ public class TrackAnalysisTool {
 
     @Tool(name = "get_together_live_info_detail",
           description = "查同住服务记录明细:基于「常住人口 + 同住服务」数据,返回某人与某同住人的同住次数、"
-                  + "入住/退房时间等。★ 与 get_hotel_stay_detail(旅馆住宿数据)是两套数据源,"
+                  + "入住/退房时间等。与 get_hotel_stay_detail(旅馆住宿数据)是两套数据源,"
                   + "问「同住服务」「住宿同住记录」且旅馆住宿查不到时用本工具。"
                   + "personName 与 liveWithName 必填,不要用 execute_sql 手写 SQL。",
           readOnly = true)
@@ -480,7 +480,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_together_travel_summary",
           description = "统计同行出行:综合同航班/同火车服务、铁路订票、民航离港、民航订票、汽车票务数据,"
                   + "找出「同一时间、同一车次/航班」出现的人员配对,返回配对与对应的记录 id 列表(idList 列)。"
-                  + "★ 问「谁和谁同行过」「同行航班/车次」时用本工具,不要用 execute_sql 手写 SQL —— "
+                  + "问「谁和谁同行过」「同行航班/车次」时用本工具,不要用 execute_sql 手写 SQL —— "
                   + "同行比对的口径(按出行时间 + 车次号匹配)已固化在服务里。"
                   + "不传 personNames 时统计案件内全部人员。下钻具体行程用 stat_together_travel_detail。",
           readOnly = true)
@@ -503,7 +503,7 @@ public class TrackAnalysisTool {
     @Tool(name = "stat_together_travel_detail",
           description = "查同行出行的行程明细:按同行汇总结果里的 idList 拉取原始出行记录,"
                   + "按出行日期分组返回(一行一个行程段)。"
-                  + "★ ids 必填,元素必须带类型前缀(形如 TOGETHER_FLIGHT_123),"
+                  + "ids 必填,元素必须带类型前缀(形如 TOGETHER_FLIGHT_123),"
                   + "前缀由 stat_together_travel_summary 的 idList 列给出,不要自己编造或只传数字。",
           readOnly = true)
     public ToolResultBlock statTogetherTravelDetail(
@@ -529,7 +529,7 @@ public class TrackAnalysisTool {
     @Tool(name = "get_travel_record_detail",
           description = "查询单条出行票务原始记录:按数据类型 + 主键返回该记录的全部字段"
                   + "(车次/航班号、出发到达站、时间、座位、证件号等)。"
-                  + "★ 问「这一条记录的完整信息」时用本工具;type 与 id 都必填,"
+                  + "问「这一条记录的完整信息」时用本工具;type 与 id 都必填,"
                   + "id 取同行明细结果里 id 列中「_」之后的纯数字部分。",
           readOnly = true)
     public ToolResultBlock getTravelRecordDetail(

+ 18 - 18
ai-server/src/main/java/com/zsjz/ai/module/agent/tools/TransAnalysisTool.java

@@ -149,7 +149,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_big_summary",
           description = "统计大额交易第一层:按「本方姓名 + 对方姓名 + 对方卡号」汇总大额交易的笔数与总金额,"
                   + "用于快速看出「谁和谁之间有大额资金往来」。"
-                  + "★ 问「大额交易有哪些对手」「和谁有大额往来」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「大额交易有哪些对手」「和谁有大额往来」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "transAmount/transCount 是阈值(默认 >= ),不传则不按金额/笔数过滤。"
                   + "personNames 不传则统计全案所有对象人员。"
                   + "要按日期看分布用 stat_trans_big_by_date,要看具体明细用 stat_trans_big_detail。",
@@ -175,7 +175,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_big_by_date",
           description = "统计大额交易第二层:按「交易日期」聚合某个对手方的大额交易笔数与金额,"
                   + "用于看「和某人的大额往来集中在哪些日子」。"
-                  + "★ 通常先调 stat_trans_big_summary 拿到第一层结果,再用 otherName 定位某个对手后调用本工具。"
+                  + "通常先调 stat_trans_big_summary 拿到第一层结果,再用 otherName 定位某个对手后调用本工具。"
                   + "阈值参数含义与第一层一致。",
           readOnly = true)
     public ToolResultBlock statTransBigByDate(
@@ -198,7 +198,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_big_detail",
           description = "统计大额交易第三层:按日期分组列出大额交易的逐笔明细(日期、金额、收付方向、交易ID)。"
-                  + "★ 用于下钻到具体某一笔大额交易;通常先走 stat_trans_big_summary → stat_trans_big_by_date。"
+                  + "用于下钻到具体某一笔大额交易;通常先走 stat_trans_big_summary → stat_trans_big_by_date。"
                   + "返回表格每行是一笔交易,date 列为所属交易日。",
           readOnly = true)
     public ToolResultBlock statTransBigDetail(
@@ -227,7 +227,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_together",
           description = "分析同一「人员分组」内的人是否在相近时间内向同一对手交易(共同交易)。"
-                  + "★ 问「这伙人有没有一起给谁打钱」「分组内共同交易」时用本工具。"
+                  + "问「这伙人有没有一起给谁打钱」「分组内共同交易」时用本工具。"
                   + "groupId 必传(人员分组ID,可用 list_person_groups 工具查询分组列表)。"
                   + "dayGap/daySymbol 控制「相近时间」的天数(如 dayGap=3 表示 3 天内);"
                   + "transAmount 控制单笔金额门槛;cardNum/cardNumSymbol 控制涉及的卡数。"
@@ -267,7 +267,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_card_hold",
           description = "分析「疑似他人代持的银行卡」:在指定人员分组内,找出非本人开户、"
                   + "但预留手机号 / 登录 MAC / 登录 IP 与主体对象一致的银行卡(异卡同号、异卡同 MAC、异卡同 IP)。"
-                  + "★ 问「有没有用别人名义开的卡」「代持卡」「异卡同号」时用本工具。"
+                  + "问「有没有用别人名义开的卡」「代持卡」「异卡同号」时用本工具。"
                   + "groupId 必传(可用 list_person_groups 查询)。"
                   + "返回表格每行是一张疑似代持卡,type 列说明是哪种一致(同号/同 MAC/同 IP),gx 列是关系说明。",
           readOnly = true)
@@ -303,7 +303,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_cash_flow",
           description = "按人员/卡号维度汇总交易笔数与金额(交易摘要统计),"
                   + "并自动套用「现金关键词」配置识别现金类交易。"
-                  + "★ 问「某人的交易汇总」「现金交易有多少」时用本工具;要看逐笔明细请用 get_trans_records。"
+                  + "问「某人的交易汇总」「现金交易有多少」时用本工具;要看逐笔明细请用 get_trans_records。"
                   + "personNames 不传则统计全案所有对象人员。",
           readOnly = true)
     public ToolResultBlock statTransCashFlow(
@@ -338,7 +338,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_continuous_summary",
           description = "统计持续交易第一层:找出「跨多个月份持续交易」的本方与对方组合,"
                   + "返回连续月份数、交易总笔数、交易总金额、连续起止月份。"
-                  + "★ 问「谁和谁长期有资金往来」「连续几个月都有交易」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「谁和谁长期有资金往来」「连续几个月都有交易」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "monthVal 是连续月份阈值(默认 1);minTransMoney/maxTransMoney 限定单笔金额区间。"
                   + "personNames 不传则统计全案所有对象人员。要展开每对关系的明细用 stat_trans_continuous_detail。",
           readOnly = true)
@@ -364,7 +364,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_continuous_detail",
           description = "统计持续交易第二层:按「本方卡号 + 对方姓名 + 对方卡号 + 连续分组」展开每段持续交易关系的"
                   + "起止月份、交易笔数与金额。"
-                  + "★ 通常先调 stat_trans_continuous_summary 拿到第一层结果,再用 otherName/otherCardNo 下钻。"
+                  + "通常先调 stat_trans_continuous_summary 拿到第一层结果,再用 otherName/otherCardNo 下钻。"
                   + "monthVal 阈值建议与第一层保持一致。",
           readOnly = true)
     public ToolResultBlock statTransContinuousDetail(
@@ -389,7 +389,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_fast_fund_flow",
           description = "分析资金快转(快进快出):同一张卡在指定时间窗口内先收到一笔钱、随后又转出,"
                   + "识别「过账卡 / 中转账户」特征。"
-                  + "★ 问「有没有快进快出」「资金过账」「当天进当天出」时用本工具。"
+                  + "问「有没有快进快出」「资金过账」「当天进当天出」时用本工具。"
                   + "hour 是时间窗口小时数(强烈建议传,如 24 表示 24 小时内进出);personName 建议传。"
                   + "返回表格分两类行:section=银行汇总 是各卡的进出总额,section=每日明细 是按日/按收付方向的笔数与金额。",
           readOnly = true)
@@ -423,7 +423,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_financial",
           description = "按「金融关键词」配置筛选金融类交易(如理财、贷款、保险等),返回命中关键词的交易明细并标注 tags。"
-                  + "★ 问「有没有买理财/贷款」「金融类交易」时用本工具。"
+                  + "问「有没有买理财/贷款」「金融类交易」时用本工具。"
                   + "financialKeyTag 用于只取某一类关键词(在「金融关键词配置」里维护);不传则用全部已配置关键词。"
                   + "若案件未配置任何金融关键词,服务端会报「请配置分析条件」。",
           readOnly = true)
@@ -453,7 +453,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_fixed_deposit",
           description = "统计定期存款第一层:按人员汇总定期存款相关的收入/支出总额与净额。"
-                  + "★ 问「有没有定期存款」「存了多少定期」时用本工具。"
+                  + "问「有没有定期存款」「存了多少定期」时用本工具。"
                   + "关键词由服务端从「定期存款关键词」配置加载,无需传。"
                   + "personNames 不传则统计全案所有对象人员。要逐笔明细用 stat_trans_fixed_deposit_detail。",
           readOnly = true)
@@ -480,7 +480,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_fixed_deposit_detail",
           description = "统计定期存款第二层:返回定期存款的逐笔交易明细与账户变动明细(含分页)。"
-                  + "★ 通常先调 stat_trans_fixed_deposit 拿到第一层结果,再下钻到明细。",
+                  + "通常先调 stat_trans_fixed_deposit 拿到第一层结果,再下钻到明细。",
           readOnly = true)
     public ToolResultBlock statTransFixedDepositDetail(
             @ToolParam(name = "query", description = "定期存款筛选条件,字段全部可选")
@@ -507,7 +507,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_frequency",
           description = "统计交易频次:按本方与对方维度汇总交易次数,可用 transCount/transCountSymbol 设门槛"
                   + "(如只看交易 ≥ 10 次的对手)。"
-                  + "★ 问「和谁交易最频繁」「交易次数多的对手」时用本工具。"
+                  + "问「和谁交易最频繁」「交易次数多的对手」时用本工具。"
                   + "personNames 不传则统计全案所有对象人员。",
           readOnly = true)
     public ToolResultBlock statTransFrequency(
@@ -542,12 +542,12 @@ public class TransAnalysisTool {
     @Tool(name = "get_trans_records",
           description = "查询交易原始明细(一行一笔),支持按本方姓名/卡号、对方姓名/卡号、收付方向、"
                   + "金额区间、交易摘要/网点/备注关键词、日期范围、是否只看现金交易筛选。"
-                  + "★ 问「某人的交易流水」「和某人之间转了多少笔、什么时候转的」时用本工具,"
+                  + "问「某人的交易流水」「和某人之间转了多少笔、什么时候转的」时用本工具,"
                   + "不要用 execute_sql 手写 SQL —— 本工具的口径(未知对手/未知卡号、现金关键词等)已固化。"
                   + "结果按交易时间倒序,以可翻页表格返回。不传任何条件会命中全案所有交易并因超过行数上限被拒绝。"
                   + "若要看的是「大额/持续/快转/敏感日期/金融关键词」这类分析结论,"
-                  + "请改用 stat_trans_big_* / stat_trans_continuous_* / stat_trans_fast_fund_flow / "
-                  + "stat_trans_sensitive_* / stat_trans_financial。",
+                  + "请改用 stat_trans_big_summary / stat_trans_big_by_date / stat_trans_big_detail / stat_trans_continuous_summary / stat_trans_continuous_detail / stat_trans_fast_fund_flow / "
+                  + "stat_trans_sensitive_summary / stat_trans_sensitive_detail / stat_trans_financial。",
           readOnly = true)
     public ToolResultBlock getTransRecords(
             @ToolParam(name = "query", description = "交易明细筛选条件,字段全部可选(但至少要能定位到人/卡/时段)")
@@ -574,7 +574,7 @@ public class TransAnalysisTool {
     @Tool(name = "stat_trans_sensitive_summary",
           description = "统计敏感日期(案件配置的特殊日期/节假日)内的交易第一层:"
                   + "按「本方姓名 + 对方姓名 + 对方卡号」汇总落在特殊日期内的交易笔数。"
-                  + "★ 问「敏感日期期间和谁有资金往来」时用本工具,不要用 execute_sql 手写 SQL。"
+                  + "问「敏感日期期间和谁有资金往来」时用本工具,不要用 execute_sql 手写 SQL。"
                   + "personNames 不传则统计全案所有对象人员。"
                   + "要下钻到具体交易用 stat_trans_sensitive_detail。",
           readOnly = true)
@@ -599,7 +599,7 @@ public class TransAnalysisTool {
      */
     @Tool(name = "stat_trans_sensitive_detail",
           description = "查询敏感日期(特殊日期/节假日)内的交易原始明细,返回话单并带上命中的节日名称。"
-                  + "★ 通常先调 stat_trans_sensitive_summary 拿到第一层结果,再用 otherName/otherCardNo 下钻。",
+                  + "通常先调 stat_trans_sensitive_summary 拿到第一层结果,再用 otherName/otherCardNo 下钻。",
           readOnly = true)
     public ToolResultBlock statTransSensitiveDetail(
             @ToolParam(name = "query", description = "敏感日期交易筛选条件,建议传 otherName/otherCardNo 定位")