Spring AI Alibaba Tools 完全指南:从基础到进阶

Spring AI Alibaba Tools 完全指南:从基础到进阶

本文基于 Spring AI Alibaba 1.1.2.0 官方文档,提供一份完整、可直接运行的 Tools 示例项目,涵盖所有常见创建方式、注册方法、高级特性,并修正了常见的编译错误(如内部 public record 问题)。


一、Tools 核心概念

Tools(工具) 是 Agent 调用来执行操作的组件,它们通过定义良好的输入和输出让模型与外部世界交互。主要应用场景:

场景 说明 示例
信息检索 从外部源获取实时数据 查询天气、搜索数据库、获取当前时间
执行操作 在系统中执行具体任务 发送邮件、创建记录、设置闹钟

工作流程

复制代码
用户提问 → Model 决定调用 Tool → 应用程序执行 Tool → 结果返回 → 发送回 Model → 生成最终回复

在 AI 应用中,大语言模型(LLM)本身无法访问实时信息或执行实际操作。Tools(工具) 正是为了解决这一限制而设计的。

1.1 Tools 的核心价值

Tools 是 Agent 调用来执行操作的组件,它们通过定义良好的输入和输出让模型与外部世界交互,从而扩展模型的能力。Tools 主要应用于两大场景:

场景 说明 示例
信息检索 从外部源获取实时数据 查询天气、搜索数据库、获取当前时间
执行操作 在系统中执行具体任务 发送邮件、创建记录、设置闹钟

1.2 Tool Calling 的工作流程

Tool calling 的核心流程如下:

text

复制代码
1. 用户提问 → 2. Model 决定调用 Tool → 3. 应用程序执行 Tool
         ↓
4. Tool 结果返回 → 5. 结果发送回 Model → 6. Model 生成最终回复

关键安全原则:Model 永远无法直接访问作为 Tools 提供的任何 API,Tool 的实际执行由客户端应用程序完成,这是一个关键的安全考量。


二、创建 Tool 的方式

Spring AI Alibaba 提供了三种创建 Tool 的方式:

2.1 声明式:使用 @Tool 注解(推荐)

这是最常用、最简洁的方式,通过在方法上添加 @Tool 注解将方法转换为 Tool。

java

复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class DateTimeTools {
    
    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(
            LocaleContextHolder.getTimeZone().toZoneId()
        ).toString();
    }
    
    @Tool(description = "Set a user alarm for the given time")
    void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }
}
@Tool 注解属性说明
属性 说明
name Tool 名称,默认使用方法名。必须在所有 Tool 中唯一
description Tool 描述,强烈建议提供,帮助模型理解何时调用
returnDirect 是否将结果直接返回给客户端而非模型,默认 false
resultConverter 自定义结果转换器
@ToolParam 注解属性说明
属性 说明
description 参数描述,帮助模型理解参数格式和用途
required 是否必需,默认 true

2.2 编程式:MethodToolCallback

通过编程方式构建 MethodToolCallback,适用于需要精细控制的场景。

java

复制代码
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.ai.tool.ToolDefinitions;
import org.springframework.util.ReflectionUtils;

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");

ToolCallback toolCallback = MethodToolCallback.builder()
        .toolDefinition(ToolDefinitions.builder(method)
                .description("Get the current date and time in the user's timezone")
                .build())
        .toolMethod(method)
        .toolObject(new DateTimeTools())
        .build();

对于静态方法,可以省略 toolObject()

java

复制代码
class DateTimeTools {
    static String getCurrentDateTime() { /* ... */ }
}

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");

ToolCallback toolCallback = MethodToolCallback.builder()
        .toolDefinition(ToolDefinitions.builder(method)
                .description("Get the current date and time")
                .build())
        .toolMethod(method)
        .build();

2.3 函数式:FunctionToolCallback

通过 FunctionToolCallbackFunctionSupplierConsumerBiFunction 转换为 Tool。

java

复制代码
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;

public record WeatherRequest(String location, Unit unit) {}
public enum Unit { C, F }
public record WeatherResponse(double temp, Unit unit) {}

public class WeatherService implements Function<WeatherRequest, WeatherResponse> {
    @Override
    public WeatherResponse apply(WeatherRequest request) {
        return new WeatherResponse(25.0, Unit.C);
    }
}

ToolCallback toolCallback = FunctionToolCallback
        .builder("currentWeather", new WeatherService())
        .description("Get the weather in location")
        .inputType(WeatherRequest.class)
        .build();

2.4 动态规范:@Bean 方式

将 Tool 定义为 Spring Bean,由框架在运行时动态解析:

java

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import java.util.function.Function;

@Configuration(proxyBeanMethods = false)
class WeatherTools {
    
    public static final String CURRENT_WEATHER_TOOL = "currentWeather";
    
    @Bean(CURRENT_WEATHER_TOOL)
    @Description("Get the weather in location")
    Function<WeatherRequest, WeatherResponse> currentWeather() {
        return new WeatherService();
    }
}

2.5 方法 Tool 的限制

以下类型不支持作为 Tool 方法的参数或返回类型:

  • Optional
  • 异步类型(CompletableFutureFuture
  • 响应式类型(FlowMonoFlux
  • 函数类型(FunctionSupplierConsumer

函数类型可通过 FunctionToolCallback 方式支持。


三、Tool 的注册与使用

3.1 在 ChatClient 中使用

通过 tools() 方法传递 Tool 实例:

java

复制代码
ChatModel chatModel = ...;
String response = ChatClient.create(chatModel)
        .prompt("What day is tomorrow?")
        .tools(new DateTimeTools())
        .call()
        .content();

3.2 在 Agent 中使用

ReactAgent 中注册 Tool,需要将 @Tool 注解的对象转换为 ToolCallback 数组:

java

复制代码
@Bean
public ReactAgent assistantAgent(ChatModel chatModel, AgentTools agentTools) {
    ToolCallback[] toolCallbacks = MethodToolCallbackProvider.builder()
            .toolObjects(agentTools)
            .build()
            .getToolCallbacks();

    return ReactAgent.builder()
            .name("assistant_agent")
            .model(chatModel)
            .tools(toolCallbacks)  // 传入 ToolCallback 数组
            .systemPrompt("...")
            .build();
}

3.3 添加默认 Tools 到 ChatClient.Builder

通过 defaultToolCallbacks() 添加所有请求共享的默认 Tool:

java

复制代码
ChatModel chatModel = ...;
ToolCallback toolCallback = ...;
ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultToolCallbacks(toolCallback)
        .build();

3.4 动态 Tool 名称解析

使用 toolNames() 按名称动态解析 Tool:

java

复制代码
ChatClient.create(chatModel)
        .prompt("What's the weather like in Copenhagen?")
        .toolNames("currentWeather")
        .call()
        .content();

四、高级特性

4.1 JSON Schema 与参数描述

Spring AI 会自动为 @Tool 注解方法的输入参数生成 JSON Schema。可以通过多种注解自定义生成的 Schema:

注解 说明
@ToolParam(description = "...") Spring AI 原生注解
@JsonClassDescription Jackson 注解
@JsonPropertyDescription Jackson 注解
@Schema(description = "...") Swagger 注解

必需/可选参数:默认所有参数都是必需的,可通过以下注解设为可选:

java

复制代码
@Tool(description = "Update customer information")
void updateCustomerInfo(Long id, 
                        String name, 
                        @ToolParam(required = false) String email) {
    // ...
}

4.2 自定义结果转换

默认情况下,Tool 结果使用 Jackson 序列化为 JSON 发送给模型。可以通过自定义 ToolCallResultConverter 改变序列化行为:

java

复制代码
class CustomerTools {
    @Tool(description = "Retrieve customer information", 
          resultConverter = CustomToolCallResultConverter.class)
    Customer getCustomerInfo(Long id) {
        return customerRepository.findById(id);
    }
}

4.3 返回直接(Return Direct)

默认情况下,Tool 结果会发送回 Model 继续处理。将 returnDirect 设为 true 可以让结果直接返回给调用者,适用于:

  • RAG 场景中直接返回检索结果
  • 应该结束 Agent 推理循环的 Tool

java

复制代码
@Tool(description = "Retrieve customer information", returnDirect = true)
Customer getCustomerInfo(Long id) {
    return customerRepository.findById(id);
}

4.4 自定义 ToolCallingManager

通过提供自定义 ToolCallingManager Bean 来控制 Tool 执行行为:

java

复制代码
@Bean
ToolCallingManager toolCallingManager() {
    return ToolCallingManager.builder().build();
}

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

五、环境搭建

5.1 pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
    </parent>

    <groupId>com.example.ai</groupId>
    <artifactId>spring-ai-alibaba-tools-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
        <jackson.version>2.16.2</jackson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Agent Framework 核心 -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-agent-framework</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- DashScope 模型接入 -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- 强制锁定 Jackson 版本,避免 NoSuchMethodError -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
</project>

5.2 application.yml

yaml 复制代码
server:
  port: 885

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}   # 从环境变量读取
      chat:
        options:
          model: deepseek-v4-flash     # 或 qwen-max

logging:
  level:
    com.alibaba.cloud.ai: debug

六、完整可运行代码

6.1 项目结构

复制代码
src/main/java/com/example/ai/
├── SpringAiDemoApplication.java      # 启动类
├── config/
│   └── AgentConfig.java               # Agent 配置类
├── tools/
│   ├── DateTimeTools.java             # 日期时间工具(声明式 @Tool)
│   ├── WeatherTools.java              # 天气工具(函数式 FunctionToolCallback)
│   └── CustomerTools.java             # 客户工具(returnDirect + 可选参数)
├── service/
│   └── AgentService.java              # Agent 服务层
└── controller/
    └── AgentController.java           # REST 控制器

6.2 启动类

java 复制代码
package com.example.ai;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringAiDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAiDemoApplication.class, args);
    }
}

6.3 工具类定义

DateTimeTools.java(声明式 @Tool
java 复制代码
package com.example.ai.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class DateTimeTools {

    @Tool(description = "获取当前日期和时间(用户时区)")
    public String getCurrentDateTime() {
        return LocalDateTime.now()
                .atZone(LocaleContextHolder.getTimeZone().toZoneId())
                .toString();
    }

    @Tool(description = "设置闹钟(ISO-8601 格式时间)")
    public String setAlarm(@ToolParam(description = "时间,格式:yyyy-MM-ddTHH:mm:ss,例如 2026-06-18T15:30:00") 
                           String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        return "闹钟已设置为 " + alarmTime;
    }
}
WeatherTools.java(函数式 FunctionToolCallback

修正说明 :为避免 public record 导致的编译错误,将 record 定义为 非 public(package-private),与工具类放在同一文件中。

java 复制代码
package com.example.ai.tools;

import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

// 非 public record(包内可见)
record WeatherRequest(String location) {}

record WeatherResponse(String location, String weather, double temperature, String unit) {}

@Component
public class WeatherTools {

    private static final Map<String, WeatherResponse> WEATHER_DATA = new HashMap<>();
    static {
        WEATHER_DATA.put("北京", new WeatherResponse("北京", "晴", 25.0, "°C"));
        WEATHER_DATA.put("上海", new WeatherResponse("上海", "多云", 28.0, "°C"));
        WEATHER_DATA.put("杭州", new WeatherResponse("杭州", "小雨", 22.0, "°C"));
        WEATHER_DATA.put("深圳", new WeatherResponse("深圳", "晴", 30.0, "°C"));
    }

    public ToolCallback getWeatherTool() {
        return FunctionToolCallback
                .builder("getWeather", (Function<WeatherRequest, WeatherResponse>) request -> {
                    String location = request.location();
                    return WEATHER_DATA.getOrDefault(location,
                            new WeatherResponse(location, "未知", 0.0, "°C"));
                })
                .description("获取指定城市的当前天气")
                .inputType(WeatherRequest.class)
                .build();
    }
}
CustomerTools.java(高级特性:returnDirect + 可选参数)
java 复制代码
package com.example.ai.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class CustomerTools {

    private final Map<Long, String> customerDatabase = new HashMap<>();

    public CustomerTools() {
        customerDatabase.put(1L, "张三 - VIP 客户");
        customerDatabase.put(2L, "李四 - 普通客户");
    }

    @Tool(description = "根据 ID 查询客户信息", returnDirect = true)
    public String getCustomerInfo(@ToolParam(description = "客户 ID") Long id) {
        return customerDatabase.getOrDefault(id, "未找到该客户");
    }

    @Tool(description = "更新客户信息(姓名必填,邮箱可选)")
    public String updateCustomerInfo(@ToolParam(description = "客户 ID") Long id,
                                     @ToolParam(description = "新姓名") String name,
                                     @ToolParam(required = false, description = "新邮箱地址") 
                                     String email) {
        customerDatabase.put(id, name + (email != null ? " (" + email + ")" : ""));
        return "客户 " + id + " 更新成功";
    }
}

6.4 Agent 配置类

java 复制代码
package com.example.ai.config;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.agent.hook.modelcalllimit.ModelCallLimitHook;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.example.ai.tools.CustomerTools;
import com.example.ai.tools.DateTimeTools;
import com.example.ai.tools.WeatherTools;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class AgentConfig {

    @Value("${spring.ai.dashscope.api-key}")
    private String apiKey;

    @Bean
    public ChatModel chatModel() {
        DashScopeApi dashScopeApi = DashScopeApi.builder()
                .apiKey(apiKey)
                .build();

        return DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi)
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel("deepseek-v4-flash")
                        .withTemperature(0.7)
                        .withMaxToken(2000)
                        .build())
                .build();
    }

    @Bean
    public ReactAgent assistantAgent(ChatModel chatModel,
                                     DateTimeTools dateTimeTools,
                                     CustomerTools customerTools,
                                     WeatherTools weatherTools) {
        // 1. 从 @Tool 注解的对象转换 ToolCallback
        ToolCallback[] methodCallbacks = MethodToolCallbackProvider.builder()
                .toolObjects(dateTimeTools, customerTools)
                .build()
                .getToolCallbacks();

        // 2. 获取函数式 Tool
        ToolCallback weatherCallback = weatherTools.getWeatherTool();

        // 3. 合并所有 Tool
        List<ToolCallback> allCallbacks = new ArrayList<>();
        allCallbacks.addAll(List.of(methodCallbacks));
        allCallbacks.add(weatherCallback);

        return ReactAgent.builder()
                .name("assistant_agent")
                .model(chatModel)
                .tools(allCallbacks.toArray(new ToolCallback[0]))  // 关键:传入 ToolCallback 数组
                .systemPrompt("""
                        你是一个智能助手,可以帮助用户完成各种任务。
                        你有以下能力:
                        1. 获取当前日期和时间 (getCurrentDateTime)
                        2. 设置闹钟 (setAlarm)
                        3. 查询城市天气 (getWeather)
                        4. 查询客户信息 (getCustomerInfo) - 结果将直接返回
                        5. 更新客户信息 (updateCustomerInfo)
                        
                        请根据用户的问题,选择合适的工具来解决问题。
                        如果用户的问题不需要工具,直接回答即可。
                        """)
                .hooks(ModelCallLimitHook.builder().runLimit(5).build())
                .saver(new MemorySaver())
                .build();
    }
}

6.5 Service 层

java 复制代码
package com.example.ai.service;

import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;

@Service
public class AgentService {

    private final ReactAgent agent;

    public AgentService(ReactAgent agent) {
        this.agent = agent;
    }

    public String chat(String userMessage) throws GraphRunnerException {
        AssistantMessage response = agent.call(userMessage);
        return response.getText();
    }

    public String chatWithMemory(String userMessage, String sessionId) throws GraphRunnerException {
        RunnableConfig config = RunnableConfig.builder()
                .threadId(sessionId)
                .build();
        AssistantMessage response = agent.call(userMessage, config);
        return response.getText();
    }
}

6.6 Controller 层

java 复制代码
package com.example.ai.controller;

import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.example.ai.service.AgentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/api/agent")
public class AgentController {

    private static final Logger log = LoggerFactory.getLogger(AgentController.class);
    private final AgentService agentService;

    public AgentController(AgentService agentService) {
        this.agentService = agentService;
    }

    @PostMapping("/chat")
    public Map<String, Object> chat(@RequestBody Map<String, String> request) {
        String message = request.get("message");
        try {
            String response = agentService.chat(message);
            return Map.of("success", true, "response", response);
        } catch (GraphRunnerException e) {
            log.error("Agent 执行失败", e);
            return Map.of("success", false, "error", "Agent 执行失败: " + e.getMessage());
        }
    }

    @PostMapping("/chat/session")
    public Map<String, Object> chatWithSession(@RequestBody Map<String, String> request) {
        String message = request.get("message");
        String sessionId = request.getOrDefault("sessionId", UUID.randomUUID().toString());
        try {
            String response = agentService.chatWithMemory(message, sessionId);
            return Map.of("success", true, "sessionId", sessionId, "response", response);
        } catch (GraphRunnerException e) {
            log.error("Agent 执行失败", e);
            return Map.of("success", false, "error", "Agent 执行失败: " + e.getMessage());
        }
    }
}

七、测试与验证

7.1 启动应用

确保环境变量 DASHSCOPE_API_KEY 已设置,然后运行 SpringAiDemoApplication

7.2 测试命令

bash 复制代码
# 1. 获取当前时间
curl -X POST http://localhost:885/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "现在几点了?"}'

# 2. 查询天气
curl -X POST http://localhost:885/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "北京今天天气怎么样?"}'

# 3. 设置闹钟
curl -X POST http://localhost:885/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "请帮我设置一个10分钟后的闹钟"}'

# 4. 查询客户信息(returnDirect 示例)
curl -X POST http://localhost:885/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "查询客户ID为1的信息"}'

# 5. 更新客户信息
curl -X POST http://localhost:885/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "更新客户ID为2的名字为王五"}'

# 6. 多轮对话(带记忆)
curl -X POST http://localhost:885/api/agent/chat/session \
  -H "Content-Type: application/json" \
  -d '{"message": "我叫张三", "sessionId": "test-001"}'

curl -X POST http://localhost:885/api/agent/chat/session \
  -H "Content-Type: application/json" \
  -d '{"message": "我叫什么名字?", "sessionId": "test-001"}'

7.3 预期输出示例

对于 "北京今天天气怎么样?":

json 复制代码
{
  "success": true,
  "response": "北京今天的天气是晴,温度25.0°C。"
}

测试客户查询:

测试更新客户:

八、常见问题与解决方案

8.1 Cannot resolve method 'tools(AgentTools)'

原因ReactAgent.builder().tools() 方法接受 ToolCallback... 类型。

解决 :使用 MethodToolCallbackProvider 转换:

java 复制代码
ToolCallback[] toolCallbacks = MethodToolCallbackProvider.builder()
        .toolObjects(agentTools)
        .build()
        .getToolCallbacks();

ReactAgent.builder()
        .tools(toolCallbacks)
        .build();

8.2 public record 导致编译错误

原因 :在单个 .java 文件中,只能有一个 public 顶层类/record。

解决 :将 record 改为 非 public(package-private)或移到独立文件。

java 复制代码
// ✅ 正确:非 public record
record WeatherRequest(String location) {}
record WeatherResponse(String location, String weather, double temperature, String unit) {}

8.3 Model 不调用 Tool

解决方案

  • 检查 @Tool 注解的 description 是否清晰
  • 在 System Prompt 中明确列出所有 Tool 及其用途
  • 确保 Tool 方法参数类型与模型推理匹配

8.4 参数解析错误

解决方案

  • 使用 @ToolParam(description = "...") 提供清晰描述
  • 使用 @Schema@JsonProperty 标注数据结构
  • 避免使用不支持的类型(OptionalCompletableFuture 等)

8.5 returnDirect 与多 Tool 同时调用

规则 :如果需要同时请求多个 Tool,所有 Tool 的 returnDirect 属性必须一致(全部 true 或全部 false)。

8.6 Unhandled exception: GraphRunnerException

解决方案 :在 Service 方法上声明 throws GraphRunnerException,由 Controller 统一捕获处理。

java 复制代码
public String chat(String userMessage) throws GraphRunnerException {
    AssistantMessage response = agent.call(userMessage);
    return response.getText();
}

九、总结

本文提供了 Spring AI Alibaba Tools 的完整可运行示例,涵盖了:

知识点 实现方式
声明式 Tool @Tool + @ToolParam 注解
函数式 Tool FunctionToolCallback + Function
Tool 注册 MethodToolCallbackProviderToolCallback[]
返回直接 returnDirect = true
可选参数 @ToolParam(required = false)
多 Tool 组合 合并多个 ToolCallback 数组
记忆对话 MemorySaver + threadId
异常处理 GraphRunnerException 统一捕获

你可以基于此示例快速构建自己的 Tools 集合,扩展 Agent 的能力边界。


📎 参考资源:Spring AI Alibaba GitHub | 官方文档 | Tools 教程原文