Spring AI 1.x 系列【21】ToolCallbackProvider 动态工具集成

文章目录

  • [1. 前言](#1. 前言)
  • [2. 工具提供者](#2. 工具提供者)
    • [2.1 ToolCallbackProvider](#2.1 ToolCallbackProvider)
    • [2.2 StaticToolCallbackProvider](#2.2 StaticToolCallbackProvider)
    • [2.3 MethodToolCallbackProvider](#2.3 MethodToolCallbackProvider)
    • [2.4 AugmentedToolCallbackProvider](#2.4 AugmentedToolCallbackProvider)
    • [2.5 McpToolCallbackProvider](#2.5 McpToolCallbackProvider)
  • [3. 案例演示](#3. 案例演示)
    • [3.1 工具硬编码问题](#3.1 工具硬编码问题)
    • [3.2 定义工具来源](#3.2 定义工具来源)
    • [3.3 工具函数](#3.3 工具函数)
    • [3.4 实现 ToolCallbackProvider](#3.4 实现 ToolCallbackProvider)
    • [3.5 ChatClient 配置](#3.5 ChatClient 配置)
    • [3.6 测试](#3.6 测试)

1. 前言

在之前我们学习了创建方法型、函数型工具的多种方式,以及通过 ChatClient 进行全局默认、运行时动态配置,也了解了工具执行和解析的核心流程和源码。

以上都是在代码中直接定义工具,属于本地硬编码式的工具集成方案 ------无论是基于 @Tool 注解的业务方法封装、函数式接口实现的工具 Bean,还是手动构建的 ToolCallback 实例,所有工具的定义、逻辑、注册都与应用代码强耦合,必须随项目一起编译、部署、重启才能生效。

但在真实的生产级 AI 应用、微服务架构中,这种本地硬编码的方式存在明显局限:

  • 工具需要跨服务、跨应用共享时,无法复用统一的工具能力;
  • 业务迭代需要新增、修改、下线工具时,必须重启服务,影响线上稳定性;
  • 对接第三方服务、远程 API、外部系统能力时,本地编码实现成本高、维护繁琐;
  • 企业级场景下,需要中心化管理、权限控制、动态调度 AI 工具,硬编码无法满足。

为此,Spring AI 提供了更灵活、更贴近生产实践的「非编码式/远程式工具集成体系」 ,彻底打破本地代码的限制,支持对接远程工具、动态注册工具、中心化管理工具,这也是构建企业级 AI 智能体的核心能力。

接下来,我们将重点讲解 Spring AI 工具生态的 ToolCallbackProvider

2. 工具提供者

2.1 ToolCallbackProvider

用于批量管理和提供 ToolCallback实例的核心接口,充当工具工厂工具仓库 的角色。它的核心职责将不同来源的工具定义转换为 ToolCallback 数组,供 AI 模型调用。

接口定义

java 复制代码
package org.springframework.ai.tool;

public interface ToolCallbackProvider {
    // 返回工具回调数组,核心方法
    ToolCallback[] getToolCallbacks();
    
    // 静态工厂方法:从工具回调列表创建提供者
    static ToolCallbackProvider from(List<? extends FunctionCallback> toolCallbacks) {
        return new StaticToolCallbackProvider(toolCallbacks);
    }
    // 同上
	  static ToolCallbackProvider from(ToolCallback... toolCallbacks) {
		   return new StaticToolCallbackProvider(toolCallbacks);
	}
}

设计意图:

  • 解耦工具定义与注册 :工具可以在不同来源定义(配置文件、数据库、MCP、注解),统一通过 Provider 注册
  • 延迟加载getToolCallbacks() 方法在实际需要时才被调用
  • 可组合性 :多个 Provider 可以同时注册,工具自动合并

Spring AI 提供了5 种官方实现,覆盖不同工具来源与增强场景:

  • StaticToolCallbackProvider:静态工具回调
  • MethodToolCallbackProvider:从 @Tool 注解方法生成
  • AugmentedToolCallbackProvider: 带增强参数的包装器
  • SyncMcpToolCallbackProviderMCP 同步工具发现
  • AsyncMcpToolCallbackProviderMCP 异步工具发现

2.2 StaticToolCallbackProvider

ToolCallbackProvider 接口的最基础实现,用于:

  • 持有静态工具集:在构造时确定工具列表,运行时不可变
  • 简化工具注册:提供简单的 API 将已构建好的 ToolCallback 包装成 Provider

内部维护了一个不可变的 final 工具列表:

java 复制代码
private final ToolCallback[] toolCallbacks;

源码:

java 复制代码
  public class StaticToolCallbackProvider implements ToolCallbackProvider {

        private final ToolCallback[] toolCallbacks;

        // 构造方法1: 接受可变参数数组
        public StaticToolCallbackProvider(ToolCallback... toolCallbacks) {
                Assert.notNull(toolCallbacks, "ToolCallbacks must not be null");
                this.toolCallbacks = toolCallbacks;
        }

        // 构造方法2: 接受 List
        public StaticToolCallbackProvider(List<? extends ToolCallback> toolCallbacks) {
                Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
                this.toolCallbacks = toolCallbacks.toArray(new ToolCallback[0]);
        }

        @Override
        public ToolCallback[] getToolCallbacks() {
                return this.toolCallbacks;
        }
  }

2.3 MethodToolCallbackProvider

基于方法注解的工具提供者,核心能力是扫描并自动将带有 @Tool 注解的方法转换为 ToolCallback 实例,是 Spring AI 工具注册体系中最常用的实现之一。

核心流程

  1. 反射获取类上声明了 @Tool 的方法对象
  2. 排除 Function/Supplier/Consumer 返回类型的方法
  3. 每个方法对象创建 MethodToolCallback 实例
  4. 返回 ToolCallback[] 列表

核心属性和构造方法:

java 复制代码
  public final class MethodToolCallbackProvider implements ToolCallbackProvider {

      private static final Logger logger = LoggerFactory.getLogger(MethodToolCallbackProvider.class);

      private final List<Object> toolObjects;  // 存储 @Tool 注解方法所在的对象

      // 私有构造,通过 Builder 创建
      private MethodToolCallbackProvider(List<Object> toolObjects) {
          Assert.notNull(toolObjects, "toolObjects cannot be null");
          Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");
          assertToolAnnotatedMethodsPresent(toolObjects);  // 验证存在 @Tool 方法
          this.toolObjects = toolObjects;
          validateToolCallbacks(getToolCallbacks());  // 验证无重名工具
      }
  }

核心方法 getToolCallbacks()

java 复制代码
  @Override
  public ToolCallback[] getToolCallbacks() {
      var toolCallbacks = this.toolObjects.stream()
          // 1. 遍历每个工具对象
          .map(toolObject -> Stream
              // 2. 获取该对象声明的所有方法
              .of(ReflectionUtils.getDeclaredMethods(
                  // 处理 AOP 代理对象
                  AopUtils.isAopProxy(toolObject)
                      ? AopUtils.getTargetClass(toolObject)
                      : toolObject.getClass()))
              // 3. 过滤:带有 @Tool 注解
              .filter(this::isToolAnnotatedMethod)
              // 4. 过滤:排除 Function/Supplier/Consumer 返回类型
              .filter(toolMethod -> !isFunctionalType(toolMethod))
              // 5. 过滤:排除编译器生成的方法
              .filter(ReflectionUtils.USER_DECLARED_METHODS::matches)
              // 6. 为每个方法创建 MethodToolCallback
              .map(toolMethod -> MethodToolCallback.builder()
                  .toolDefinition(ToolDefinitions.from(toolMethod))
                  .toolMetadata(ToolMetadata.from(toolMethod))
                  .toolMethod(toolMethod)
                  .toolObject(toolObject)
                  .toolCallResultConverter(ToolUtils.getToolCallResultConverter(toolMethod))
                  .build())
              .toArray(ToolCallback[]::new))
          // 7. 扁平化为单一流
          .flatMap(Stream::of)
          .toArray(ToolCallback[]::new);

      // 8. 验证无重名工具
      validateToolCallbacks(toolCallbacks);

      return toolCallbacks;
  }

2.4 AugmentedToolCallbackProvider

Spring AI 中工具输入增强的核心实现,核心能力是在不修改原始工具方法签名的前提下,为工具追加输入 Schema 扩展字段,实现工具调用上下文信息传递、元数据记录等高级功能,是构建复杂智能体应用的关键组件之一。

常见应用场景:

  • 内部思考 / 推理:捕获模型执行工具前的分步推理逻辑
  • 记忆增强:提取关键信息并存储至长期记忆
  • 分析与追踪:收集元数据、用户意图或使用模式
  • 多智能体协作:传递智能体标识或协作指令

核心组件:

  • AugmentedToolCallbackProvider<T>:包装工具对象 / 提供者,为所有工具添加指定的记录类型参数
  • AugmentedToolCallback<T>:包装单个 ToolCallback 实例
  • AugmentedArgumentEvent<T>:包含工具定义、原始输入和增强参数,供消费者使用
  • ToolInputSchemaAugmenter:底层的模式修改工具类

核心属性和构造函数:

java 复制代码
public class AugmentedToolCallbackProvider<T extends Record> implements ToolCallbackProvider {
	// 委托提供者
	// 作为被增强的对象,持有原始的 ToolCallbackProvider
	private final ToolCallbackProvider delegate;
	
	// 后处理开关
	// 作用: 控制扩展参数在消费后是否从输入中移除
	private final boolean removeExtraArgumentsAfterProcessing;
	
	// 参数消费者
	// 作用: 处理 LLM 传入的扩展参数的回调函数
	// 触发时机: 在工具实际执行之前
	private Consumer<AugmentedArgumentEvent<T>> argumentConsumer;
	
	// 扩展参数类型
	// 定义要注入到工具中的额外参数的结构
	// - 必须是 Record 类型 - 保证不可变性和可序列化
	// 至少包含一个字段 - 空 Record 无意义
	private final Class<T> argumentType;
	

	public AugmentedToolCallbackProvider(Object toolObject, Class<T> argumentType,
			Consumer<AugmentedArgumentEvent<T>> argumentConsumer, boolean removeExtraArgumentsAfterProcessing) {
		this(MethodToolCallbackProvider.builder().toolObjects(toolObject).build(), argumentType, argumentConsumer,
				removeExtraArgumentsAfterProcessing);
	}

	public AugmentedToolCallbackProvider(ToolCallbackProvider delegate, Class<T> argumentType,
			Consumer<AugmentedArgumentEvent<T>> argumentConsumer, boolean removeExtraArgumentsAfterProcessing) {
		this.delegate = delegate;
		this.argumentType = argumentType;
		this.argumentConsumer = argumentConsumer;
		this.removeExtraArgumentsAfterProcessing = removeExtraArgumentsAfterProcessing;
	}

配置项 removeExtraArgumentsAfterProcessing 控制增强参数是否传递给原始工具:

  • true(默认):调用工具前移除增强参数,原始工具看不到扩展参数,适用于会话 ID、租户 ID 等上下文信息场景
  • false:保留输入中的增强参数,原始工具可访问,适用于原始工具也需要处理的扩展参数

核心方法将普通的 ToolCallback 包装为增强版的 AugmentedToolCallback

java 复制代码
	@Override
	public ToolCallback[] getToolCallbacks() {

		return Arrays.stream(this.delegate.getToolCallbacks())
			.map(toolCallback -> new AugmentedToolCallback<T>(toolCallback, this.argumentType, this.argumentConsumer,
					this.removeExtraArgumentsAfterProcessing))
			.toArray(ToolCallback[]::new);

	}

将增强参数定义为 Java 记录类:

java 复制代码
public record AgentThinking(
    @ToolParam(description = "调用该工具的推理过程", required = true)
    String innerThought,

    @ToolParam(description = "置信度(低/中/高)", required = false)
    String confidence
) {}

通过 AugmentedToolCallbackProvider 包装你的工具:

java 复制代码
AugmentedToolCallbackProvider<AgentThinking> provider = AugmentedToolCallbackProvider
    .<AgentThinking>builder()
    .toolObject(new MyTools())  // 带@Tool注解的工具类
    .argumentType(AgentThinking.class)
    .argumentConsumer(event -> {
        AgentThinking thinking = event.arguments();
        log.info("工具:{} | 推理:{}", event.toolDefinition().name(), thinking.innerThought());
    })
    .removeExtraArgumentsAfterProcessing(true)
    .build();

在聊天客户端中使用:

java 复制代码
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultToolCallbacks(provider)
    .build();

大语言模型会识别包含新增字段的增强后模式;消费者会接收 AgentThinking 记录对象,而原始工具仅接收其预期的参数。

2.5 McpToolCallbackProvider

MCP 相关的 ToolCallbackProviderspring-ai-mcp 模块提供的,SyncMcpToolCallbackProviderAsyncMcpToolCallbackProviderSpring AIMCP (Model Context Protocol) 工具集成的核心组件,负责将 MCP Server 提供的工具转换为 Spring AIToolCallback

示例 1 ,直接使用 Spring Bean 注册,自动配置

java 复制代码
  @Bean
  public ToolCallbackProvider myTools() {
      List<ToolCallback> tools = List.of(
          FunctionToolCallback.builder("tool1", fn1).build(),
          FunctionToolCallback.builder("tool2", fn2).build()
      );
      return ToolCallbackProvider.from(tools);
  }
java 复制代码
  ChatClient chatClient = ChatClient.builder(chatModel)
      .defaultTools(ToolCallbackProvider.from(callback1, callback2))
      .build();

3. 案例演示

3.1 工具硬编码问题

在之前的案例中,我们定义好工具之后,还需要显式的将工具对象传递给 ChatClient

否则,该工具不会被加载:


解决方案 :通过 ToolCallbackProvider 实现动态工具加载。

3.2 定义工具来源

Tool Calling 的本质是函数调用,无论如何都需要提供 ToolCallback 工具对象,但是我们可以通过动态加载工具信息,然后再实例化对象,并可以提供一个注册表,实现对工具的动态管理。这里将工具信息存放到本地文件,实际开发可以选择数据库、配置中心。

创建一个工具信息类:

java 复制代码
/**
 * 工具配置(对应 tools.json)
 */
@Data
public class AiToolInfo {
    private String name;        // 工具名称
    private String description; // 工具描述
    private String implClass;   // 工具实现类
    private String inputType;   // 输入参数类型(Function 需要,Supplier 不需要)
    private String type;        // 类型:function(默认)或 supplier
    private Integer status;     // 1=启用 0=禁用
}

resources/tools 目录下创建 tools.json 文件:

json 复制代码
[
  {
    "name": "query_weather",
    "description": "查询指定城市的天气信息,参数:city-城市名称",
    "implClass": "com.example.aichatdemo.file.WeatherTool",
    "inputType": "com.example.aichatdemo.file.WeatherInput",
    "type": "function",
    "status": 1
  },
  {
    "name": "get_time",
    "description": "获取当前日期和时间",
    "implClass": "com.example.aichatdemo.file.TimeTool",
    "type": "supplier",
    "status": 1
  }
]

3.3 工具函数

提供一个天气查询工具:

java 复制代码
public class WeatherTool implements Function<WeatherInput, String> {

    @Override
    public String apply(WeatherInput input) {
        return "城市: " + input.getCity() + ", 天气: 晴朗, 温度: 25°C";
    }
}
@Data
public class WeatherInput {
    private String city;
}

提供一个时间查询工具:

java 复制代码
public class TimeTool implements Supplier<String> {

    @Override
    public String get() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

3.4 实现 ToolCallbackProvider

自定义 ToolCallbackProvider 实现类:

java 复制代码
/**
 * 基于配置文件的工具回调提供器
 * <p>
 * 功能说明:
 * 从 tools.json 配置文件动态加载工具定义,并构建对应的 ToolCallback 对象。
 * 支持两种类型的函数工具:
 * - Function<REQ, RES>: 有输入参数的函数工具(如天气查询,需要传入城市名称)
 * - Supplier<RES>: 无输入参数的函数工具(如获取当前时间)
 * <p>
 * 工作流程:
 * 1. 启动时从 tools.json 加载工具配置列表
 * 2. 过滤启用的工具(status=1)
 * 3. 反射实例化工具实现类
 * 4. 自动推断工具类型和输入参数类型,构建 FunctionToolCallback
 * <p>
 * 配置示例(tools.json)- inputType 和 type 自动推断,无需配置:
 * <pre>
 * [
 *   {
 *     "name": "query_weather",
 *     "description": "查询指定城市的天气信息",
 *     "implClass": "com.example.aichatdemo.file.WeatherTool",
 *     "status": 1
 *   },
 *   {
 *     "name": "get_time",
 *     "description": "获取当前日期和时间",
 *     "implClass": "com.example.aichatdemo.file.TimeTool",
 *     "status": 1
 *   }
 * ]
 * </pre>
 *
 * @author TD
 * @see ToolCallbackProvider Spring AI 工具回调提供器接口
 * @see FunctionToolCallback 函数工具回调实现
 * @see AiToolInfo 工具配置信息实体
 */
@Slf4j
@Component
public class FileToolCallbackProvider implements ToolCallbackProvider {

    /**
     * 默认 Schema 类型,用于生成工具输入参数的 JSON Schema
     */
    private static final SchemaType DEFAULT_SCHEMA_TYPE = SchemaType.JSON_SCHEMA;

    /**
     * 当前使用的 Schema 类型
     */
    private final SchemaType schemaType;

    /**
     * JSON 解析器,用于解析 tools.json 配置文件
     */
    private final ObjectMapper mapper = new ObjectMapper();

    /**
     * 构造器
     *
     * @param schemaType 可选的 Schema 类型,为空时使用默认值 JSON_SCHEMA
     */
    public FileToolCallbackProvider(@Nullable SchemaType schemaType) {
        this.schemaType = schemaType != null ? schemaType : DEFAULT_SCHEMA_TYPE;
    }

    /**
     * 获取所有工具回调
     * <p>
     * 实现逻辑:
     * 1. 加载配置文件中的工具定义
     * 2. 遍历并过滤启用的工具
     * 3. 构建每个工具的 ToolCallback
     * 4. 返回 ToolCallback 数组供 ChatClient 使用
     *
     * @return ToolCallback 数组,包含所有启用的工具回调
     */
    @Override
    public ToolCallback[] getToolCallbacks() {
        List<ToolCallback> callbacks = new ArrayList<>();
        // 从配置文件加载工具定义
        List<AiToolInfo> toolInfos = loadConfig();

        log.info("配置文件加载 {} 个工具定义", toolInfos.size());

        // 遍历所有工具配置
        for (AiToolInfo info : toolInfos) {
            // 跳过未启用的工具(status != 1)
            if (info.getStatus() != 1) continue;

            try {
                // 构建工具回调对象
                ToolCallback callback = buildCallback(info);
                callbacks.add(callback);
                log.info("加载工具: {}", info.getName());
            } catch (Exception e) {
                // 单个工具加载失败不影响其他工具
                log.warn("加载工具失败: {}", info.getName(), e);
            }
        }

        return callbacks.toArray(ToolCallback[]::new);
    }

    /**
     * 根据工具配置信息构建 ToolCallback
     * <p>
     * 构建逻辑:
     * - Supplier 类型:无输入参数,使用空 schema "{}"
     * - Function 类型:有输入参数,自动生成 JSON Schema
     *
     * @param info 工具配置信息
     * @return 构建好的 ToolCallback 对象
     * @throws Exception 类加载或实例化失败时抛出异常
     */
    private ToolCallback buildCallback(AiToolInfo info) throws Exception {
        String toolName = info.getName();
        String description = info.getDescription();
        Integer status = info.getStatus();

        // 仅加载启用的工具
        if (status == 1) {
            // 通过反射实例化工具实现类
            Object instance = Class.forName(info.getImplClass())
                    .getDeclaredConstructor()
                    .newInstance();

            // 处理 Supplier 类型工具(无参数,如获取当前时间)
            if (instance instanceof Supplier<?> supplier) {
                return FunctionToolCallback.builder(toolName, supplier)
                        .description(description)
                        .inputSchema("{}")  // Supplier 无参数,使用空 schema
                        .build();
            }

            // 处理 Function 类型工具(有参数,如查询天气需要城市名称)
            if (instance instanceof Function<?, ?> function) {
                // 加载输入参数类型
                Class<?> inputTypeClass = Class.forName(info.getInputType());
                // 转换为 ResolvableType 用于生成 Schema
                ResolvableType toolInputType = ResolvableType.forClass(inputTypeClass);

                return FunctionToolCallback.builder(toolName, function)
                        .description(description)
                        .inputSchema(generateSchema(toolInputType))  // 自动生成参数的 JSON Schema
                        .inputType(ParameterizedTypeReference.forType(inputTypeClass))
                        .build();
            }

            // 不支持其他类型
            throw new RuntimeException("不支持的类型: " + instance.getClass().getName());
        }

        throw new RuntimeException("当前工具未启用");
    }

    /**
     * 根据输入类型生成 JSON Schema
     * <p>
     * JSON Schema 用于告诉大模型工具参数的结构,例如:
     * <pre>
     * {
     *   "type": "object",
     *   "properties": {
     *     "city": { "type": "string" }
     *   },
     *   "required": ["city"]
     * }
     * </pre>
     *
     * @param toolInputType 工具输入参数的类型
     * @return JSON Schema 字符串
     */
    private String generateSchema(ResolvableType toolInputType) {
        // OPEN_API_SCHEMA 格式需要特殊处理(类型值大写)
        if (this.schemaType == SchemaType.OPEN_API_SCHEMA) {
            return JsonSchemaGenerator.generateForType(
                    toolInputType.getType(),
                    JsonSchemaGenerator.SchemaOption.UPPER_CASE_TYPE_VALUES
            );
        }
        // 默认使用 JSON_SCHEMA 格式
        return JsonSchemaGenerator.generateForType(toolInputType.getType());
    }

    /**
     * 从配置文件加载工具定义列表
     * <p>
     * 加载顺序:
     * 1. 尝试从项目相对路径加载(开发环境)
     * 2. 尝试从 ClassPath 加载(打包后的运行环境)
     *
     * @return 工具配置信息列表,加载失败时返回空列表
     */
    private List<AiToolInfo> loadConfig() {
        // 可能的配置文件路径(支持不同运行环境)
        String[] possiblePaths = {
                "src/main/resources/tools/tools.json",           // 根目录运行
                "ai-chat-demo/src/main/resources/tools/tools.json" // 子模块运行
        };

        // 尝试从文件系统加载
        for (String path : possiblePaths) {
            File file = new File(path);
            if (file.exists()) {
                try {
                    return mapper.readValue(file, new TypeReference<List<AiToolInfo>>() {});
                } catch (Exception e) {
                    log.warn("解析失败: {}", e.getMessage());
                }
            }
        }

        // 尝试从 ClassPath 加载(适用于 jar 包运行环境)
        try {
            return mapper.readValue(
                    new org.springframework.core.io.ClassPathResource("tools/tools.json").getInputStream(),
                    new TypeReference<List<AiToolInfo>>() {}
            );
        } catch (Exception e) {
            log.warn("读取 tools.json 失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }
}

提示:这里没有将工具对象保存到本地内存,每次大模型请求时,都会重新从文件中查找工具信息,创建工具对象,你可以使用内存进行缓存:

java 复制代码
 private final ToolCallback[] toolCallbacks;

3.5 ChatClient 配置

构建 ChatClient 时注入 FileToolCallbackProvider

java 复制代码
@Configuration
public class ChatClientConfig {

    /**
     * 创建智谱AI ChatClient,注入YML配置的工具
     */
    @Bean("zhiPuAiChatClient")
    public ChatClient zhiPuAiChatClient(ZhiPuAiChatModel zhiPuAiChatModel, FileToolCallbackProvider fileToolCallbackProvider) {
        return ChatClient.builder(zhiPuAiChatModel)
                .defaultToolCallbacks(fileToolCallbackProvider)
                .build();
    }
}

3.6 测试

提供一个对话测试接口:

java 复制代码
@RestController
@RequestMapping("/chat")
@RequiredArgsConstructor
public class ChatController {

    private final ChatClient chatClient;

    @GetMapping
    public String chat(@RequestParam String message) {
        return chatClient.prompt().user(message).call().content();
    }
}

启动时会执行加载:

调用访问接口进行对话:

java 复制代码
http://localhost:8080/chat?message=几点了?

再次执行了工具加载并返回工具结果:


时间工具改为禁用状态:

再次请求只加载了天气工具:

无法再查看时间:

相关推荐
做个文艺程序员2 小时前
Spring AI 1.1 三件套实战:Structured Output + Tool Calling + Memory 从踩坑到生产落地
java·大数据·人工智能
杰克尼2 小时前
SpringCloud_day04
后端·spring·spring cloud
beyond阿亮2 小时前
OpenClaw接入企业微信
人工智能·ai·企业微信·openclaw
芯智工坊2 小时前
第4章 Mosquitto命令行工具快速上手
网络·人工智能·mqtt·开源
咚咚王者2 小时前
人工智能之语音领域 语音处理 第五章 语音处理实践落地与常见问题解决
人工智能
VBsemi-专注于MOSFET研发定制2 小时前
面向电动车直流快充桩的功率MOSFET选型分析——以高功率密度、高可靠电源与模块化系统为例
人工智能
夏沫の梦2 小时前
Agent Skills技术详解与实战
人工智能·a·skill
财迅通Ai2 小时前
科创芯片ETF(589100)大涨超3.5%,AI+涨价潮点燃芯片景气
人工智能·科创芯片etf
薛定猫AI2 小时前
【技术干货】Gemma 4 上手深度指南:本地多模态大模型的新基线
人工智能·架构·自动化