1.定义
- 定义一个工具
- 把工具注册进去
MCP ,全称是 Model Context Protocol (模型上下⽂协议),是⼀种标准化协议,使 AI 模型能 够以结构化的⽅式与外部⼯具和资源进⾏交互。该协议⽀持多种传输机制,以在不同环境中提供灵活 性。可以把它想象成⼀个让 AI 模型更顺畅⼯作的桥梁和助⼿。
2.快速入门(获取当前时间)
2.1导包
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
2.2配置yml文件
server:
port: 8007
spring:
application:
name: ai-siliconflow-mcp-glm
ai:
openai:
base-url: https://api.siliconflow.cn
api-key: sk-rlpneielwjrtbzwghvmtnkrfzsqoorkclubnimumojlptvqz
chat:
options:
model: "zai-org/GLM-4.6"
temperature: 0.7
2.3写config类
java
@Configuration
public class ChatClientConfig {
@Resource
private OpenAiChatModel openAiChatModel;
@Bean("openAiChatClient")
public ChatClient openAiChatClient(WeatherJiaService weatherJiaService, AmapSrvice amapSrvice){
return ChatClient.builder(openAiChatModel)
.build();
}
}
2.4写工具类service
java
package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service;
import org.springframework.stereotype.Service;
@Service
public interface NowDateToolService {
String getNowDate();
}
2.5写工具类的具体实现类
java
@Slf4j
@Service
public class NowDateToolServiceImpl implements NowDateToolService {
//@Tool(description = "获取到系统的当前时间")
public String getNowDate() {
log.info("开始调用获取当前时间的工具......");
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
return simpleDateFormat.format(new Date());
}
}
2.6在config类中注册tool工具
java
@Configuration
public class ChatClientConfig {
@Resource
private OpenAiChatModel openAiChatModel;
@Resource
private NowDateToolService nowDateToolService;
@Bean("openAiChatClient")
public ChatClient openAiChatClient(WeatherJiaService weatherJiaService, AmapSrvice amapSrvice){
return ChatClient.builder(openAiChatModel)
.defaultTools(nowDateToolService)
.build();
}
}
2.7写ccontroller类
java
@GetMapping(value = "/stream",produces = "text/html;charset=utf-8")
public Flux<String> stream(@RequestParam("question") String question){
return openAiChatClient.prompt()
.user(question)
.stream().content();
}
2.8配置启动类
并在浏览器中问关于时间的问题,会给出系统时间

3.采取行动
在前⾯的示例中,我们使⽤了⼀个⼯具来确定当前⽇期和时间。在这个例⼦中,我们将定义第⼆个⼯ 具,⽤于在特定时间设置警报。⽬标是从现在开始设置10分钟的警报,因此我们需要为模型提供这两 种⼯具来完成这项任务。 我们将把新⼯具添加到与以前相同的 DateTimeToolsService 类中。新⼯具将采⽤⼀个参数,即 ISO-8601 格式的时间。然后,该⼯具将向控制台打印⼀条消息,指示已在给定时间内设置报警。与 之前⼀样,该⼯具被定义为⼀个⽤ @tool 注释的⽅法,我们还使⽤它来提供详细的描述,以帮助模 型理解何时以及如何使⽤该⼯具。
java
@Tool(description = "设置一个时间报警器")
public void setAlarm(@ToolParam(description = "当前系统时间") String time){
log.info("时间是:{}",time);
LocalDateTime localDateTime=LocalDateTime.parse(time);
log.info("转换后的时间:{}",localDateTime);
}
4.方法即工具
4.1声明式
您可以通过使⽤ @tool 对⽅法进⾏注释,将其转化为⼯具。
如上述入门案例所示。
@Tool 注释允许您提供有关⼯具的关键信息:
- name :工具具的名称。如果没有提供,将使⽤⽅法名称。 AI 模型在调⽤⼯具时使⽤此名称来 标识⼯具。因此,不允许在同⼀类中有两个同名⼯具。对于特定的聊天请求,该名称在模型可 ⽤的所有⼯具中必须是唯⼀的。(可以不写)
- description :⼯具的描述,模型可以使⽤它来了解何时以及如何调⽤⼯具。如果没有提 供,⽅法名称将⽤作⼯具描述。然⽽,强烈建议提供详细的描述,因为这对于模型理解⼯具的 ⽬的以及如何使⽤它⾄关重要。如果不能提供⼀个好的描述,可能会导致模型在应该使⽤的时 候不使⽤⼯具,或者使⽤不当。(要写)
- returnDirect :⼯具结果是应该直接返回给客户端还是传递回模型。
- resultConverter : ToolCallResultConverter 实现,⽤于将⼯具调⽤的结果转换为 String 对象,以发送回 AI 模型。
SpringAI 将⾃动为 @Tool 注释⽅法的输⼊参数⽣成 JSON 模式。模型使⽤模式来了解如何调⽤⼯ 具和准备⼯具请求。 @ToolParam 注释可⽤于提供有关输⼊参数的其他信息,例如描述参数是必需的 还是可选的。默认情况下,所有输⼊参数都被认为是必需的。
4.2编程式
4.2.1在controller中
java
@GetMapping(value = "/stream2",produces = "text/html;charset=utf-8")
public Flux<String> stream2(@RequestParam("question") String question){
//获取到了指定的方法
Method method = ReflectionUtils.findMethod(NowDateToolService.class,
"getNowDate");
//开始创建工具
ToolDefinition toolDefinition = ToolDefinitions.builder(method)
.description("获取⽤户时区中的当前时间")
.name("getCurrentDateTime2")
.build();
//开始注册工具
MethodToolCallback methodToolCallback = MethodToolCallback.builder()
.toolDefinition(toolDefinition)
.toolMethod(method)
.toolObject(nowDateToolService) // 必须是DateTimeToolsService类的实例
.build();
return openAiChatClient.prompt()
.toolCallbacks(methodToolCallback)
.user(question)
.stream().content();
}
删掉serviceImp中的@Tool注解即可实现。
5.函数即工具
SpringAI 提供了从函数中指定⼯具的内置⽀持,可以使⽤ FunctionToolCallback 实现进⾏编 程,也可以在运⾏时动态解析 @Bean 。
5.1函数工具的回调
案例--查看城市天气(假的)
5.1.1bean包下写Weather类
定义两个实体类
java
package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.bean;
public class Weather {
//城市
public record WeatherRequest(String city){
}
//气温
public record WeatherResponse(double weather){}
}
5.1.2service包
java
package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.bean.Weather;
import org.springframework.stereotype.Service;
import java.util.function.Function;
@Service
public interface WeatherJiaService extends Function<Weather.WeatherRequest,Weather.WeatherResponse> {
}
5.1.3impl类
java
package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service.impl;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.bean.Weather;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service.WeatherJiaService;
import org.springframework.stereotype.Service;
@Service
public class WeatherJiaServiceImpl implements WeatherJiaService {
@Override
//因为WeatherRequest是内部类,在Weather类中,所以要使用Weather.WeatherRequest
public Weather.WeatherResponse apply(Weather.WeatherRequest weatherRequest) {
if ("西安".equals(weatherRequest.city())){
return new Weather.WeatherResponse(30.1);
}else if ("宝鸡".equals(weatherRequest.city())){
return new Weather.WeatherResponse(25.4);
}else if ("漠河".equals(weatherRequest.city())){
return new Weather.WeatherResponse(-27.9);
}
return new Weather.WeatherResponse(15);
}
}
5.1.4controller类(局部写法)
java
@GetMapping(value = "stream3",produces = "text/html;charset=utf-8")
public Flux<String> stream3(@RequestParam("question") String question){
FunctionToolCallback<Weather.WeatherRequest,Weather.WeatherResponse> toolCallback=
FunctionToolCallback.builder("weatherJiaServiceImpl",weatherJiaService)
.description("获取到指定位置的天气").inputType(Weather.WeatherRequest.class).build();
return openAiChatClient.prompt()
.toolCallbacks(toolCallback)
.user(question)
.stream().content();
}
5.1.5config类(全局写法)
java
@Bean("openAiChatClient")
public ChatClient openAiChatClient(WeatherJiaService weatherJiaService, AmapSrvice amapSrvice){
FunctionToolCallback<Weather.WeatherRequest,Weather.WeatherResponse> toolCallback
=FunctionToolCallback.builder("weatherJiaServiceImpl",weatherJiaService)
.description("获取到执行位置的天气").inputType(Weather.WeatherRequest.class).build();
return ChatClient.builder(openAiChatModel)
.defaultTools(weatherJiaService)
/*.defaultToolNames("currentWeather","currentDressing")*/
.defaultTools(toolCallback)
.build();
}
5.2@Bean 动态规范
5.2.1新建ToolConfig类
java
package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.config;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.bean.Weather;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service.DressingService;
import com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service.WeatherJiaService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.function.Function;
@Configuration
public class ToolConfig {
@Resource
private WeatherJiaService weatherJiaService;
@Resource
private DressingService dressingService;
@Bean("currentWeather")
public Function<Weather.WeatherRequest,Weather.WeatherResponse> weather(){
return weatherJiaService;
}
@Bean("currentDressing")
public Function<Weather.WeatherResponse,String> dressing(){
return dressingService;
}
}
5.2.2写ChatClientController
java
@Bean("openAiChatClient")
public ChatClient openAiChatClient(WeatherJiaService weatherJiaService, AmapSrvice amapSrvice){
/*FunctionToolCallback<Weather.WeatherRequest,Weather.WeatherResponse> toolCallback
=FunctionToolCallback.builder("weatherJiaServiceImpl",weatherJiaService)
.description("获取到执行位置的天气").inputType(Weather.WeatherRequest.class).build();*/
return ChatClient.builder(openAiChatModel)
/*.defaultTools(currentWeather)*/
.defaultToolNames("currentWeather")
/* .defaultTools(amapSrvice)*/
.build();
}