zhou zhou
12 小时以前 e1a6b6a5507c101d58c1ae6183361df18daef1da
rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/BuiltinMcpToolRegistryImpl.java
@@ -28,6 +28,11 @@
    private final RsfWmsTaskTools rsfWmsTaskTools;
    private final RsfWmsBaseTools rsfWmsBaseTools;
    /**
     * 校验内置 MCP 编码是否合法。
     * 当前版本只允许使用显式登记在注册表中的编码,未知编码直接拒绝,
     * 这样可以确保“页面可选项”和“运行时可挂载项”始终一致。
     */
    @Override
    public void validateBuiltinCode(String builtinCode) {
        if (!StringUtils.hasText(builtinCode)) {
@@ -38,6 +43,10 @@
        }
    }
    /**
     * 根据挂载记录创建内置工具回调。
     * 这里不会做任何动态发现,所有工具都必须经过显式注册和治理目录校验后才能暴露给模型。
     */
    @Override
    public List<ToolCallback> createToolCallbacks(AiMcpMount mount, Long userId) {
        String builtinCode = mount.getBuiltinCode();
@@ -52,6 +61,10 @@
        throw new CoolException("不支持的内置 MCP 编码: " + builtinCode);
    }
    /**
     * 返回某个内置编码下可预览的工具目录信息。
     * 该目录比运行时回调多了工具用途、查询边界和示例提问,供管理页展示。
     */
    @Override
    public List<AiMcpToolPreviewDto> listBuiltinToolCatalog(String builtinCode) {
        validateBuiltinCode(builtinCode);
@@ -62,6 +75,11 @@
    }
    private List<ToolCallback> createValidatedCallbacks(Object toolBean, String builtinCode) {
        /**
         * 把 `@Tool` Bean 转成 Spring AI ToolCallback,并强制校验:
         * 1. 工具名必须符合命名规范
         * 2. 每个工具都必须出现在治理目录里
         */
        List<ToolCallback> callbacks = Arrays.asList(ToolCallbacks.from(toolBean));
        Map<String, AiMcpToolPreviewDto> catalog = catalogByBuiltinCode(builtinCode);
        for (ToolCallback callback : callbacks) {
@@ -80,10 +98,15 @@
    }
    private List<String> supportedBuiltinCodes() {
        /** 当前版本允许挂载的全部内置 MCP 编码。 */
        return List.of(AiDefaults.MCP_BUILTIN_RSF_WMS);
    }
    private Map<String, AiMcpToolPreviewDto> catalogByBuiltinCode(String builtinCode) {
        /**
         * 构造内置工具治理目录。
         * 这里的目录是运行时校验和管理端预览的共同事实来源,不能与工具实现脱节。
         */
        if (AiDefaults.MCP_BUILTIN_RSF_WMS.equals(builtinCode)) {
            Map<String, AiMcpToolPreviewDto> catalog = new LinkedHashMap<>();
            catalog.put("rsf_query_available_inventory", buildCatalogItem(
@@ -100,19 +123,12 @@
                    "必须提供站点类型列表,类型数量最多 10 个,最多返回 50 个站点。",
                    List.of("查询入库和出库作业可用站点", "列出 AGV_PICK 类型的作业站点")
            ));
            catalog.put("rsf_query_task_list", buildCatalogItem(
                    "rsf_query_task_list",
            catalog.put("rsf_query_task", buildCatalogItem(
                    "rsf_query_task",
                    "任务查询",
                    "按任务号、状态、类型或站点条件查询任务列表。",
                    "至少提供一个过滤条件,最多返回 50 条任务记录,不支持全表扫描。",
                    List.of("查询最近 10 条状态为执行中的任务", "按任务号关键字查询任务列表")
            ));
            catalog.put("rsf_query_task_detail", buildCatalogItem(
                    "rsf_query_task_detail",
                    "任务查询",
                    "按任务 ID 或任务号查询单个任务详情。",
                    "必须提供任务 ID 或任务号之一,只返回单个任务。",
                    List.of("查询任务 12345 的详情", "根据任务号 TASK24001 查看执行明细")
                    "按任务号、状态、类型或站点条件查询任务;支持从自然语言中自动提取任务号,精确命中时返回任务明细。",
                    "过滤条件均为可选,不传过滤条件时默认返回最近任务,最多返回 50 条记录。",
                    List.of("查询最近 10 条任务", "查询任务号 TASK24001 的详情")
            ));
            catalog.put("rsf_query_warehouses", buildCatalogItem(
                    "rsf_query_warehouses",
@@ -142,6 +158,7 @@
    private AiMcpToolPreviewDto buildCatalogItem(String name, String toolGroup, String toolPurpose,
                                                 String queryBoundary, List<String> exampleQuestions) {
        /** 统一创建工具目录条目,避免不同工具组出现字段风格不一致。 */
        return AiMcpToolPreviewDto.builder()
                .name(name)
                .toolGroup(toolGroup)