Tool Calling

我们在使用AI的时候有着一个联网搜索的按键,当我们需要进行网上的搜索的时候就需要借助Tool Calling,所以简单而言,Tool Calling是大模型使用的工具类。

ToolCalling也被称呼为Function Calling,它允许大模型与一组API或工具进行交互,将大模型的智能与外部工具或API进行无缝连接,增强大模型的功能,注意大模型是不直接调用工具的,它是让应用程序去执行大模型的工具,大模型等待程序返回结果,然后返回给用户。

所以我们是对大模型的调用功能进行打通,主要执行某种工具让大模型进行函数的调用。

如果我们不使用ToolCalling的话,我们的实时问题也就无法正常的解决,如下图

首先我们使用ChatModel进行工具的调用,当我们直接使用一个工具类进行返回的时候,根据官方的文档我们就可以看得到默认情况下是直接进行返回,我们可以使用returnDirect参数的定义来设置是否直接返回。

我们想要让大模型进行工具的使用,所以我们首先需要进行工具类的配置如下:

package ai.utils;
​
import org.springframework.ai.tool.annotation.Tool;
​
public class DataTimeTools {
    /*
    * returnDirect=true的话,默认直接被大模型拿走
    * false的话将返回的结果进行修饰拼装再进行返回*/
    @Tool(description = "获取时间",returnDirect = false)
    public String getTime(){
        return "现在时间是:" + new java.util.Date();
    }
​
}

我们原先在帮助文档里面就可以看到,我们的大模型是否进行简单的处理就是需要returnDirect的参数控制。当我们配置类结束之后我们就可以控制台的编写,首先就是把工具放到工具类当中,然后使用ChatOptions让大模型了解到我们使用的工具类,然后将提示词进行编写入大模型,最后就可以得到结果。

package ai.controller;
​
import ai.utils.DataTimeTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
public class ToolCallingController {
    @Resource(name="qwen")
    private ChatModel chatModel;
​
    @GetMapping("/toolcall/call")
    public String chat(@RequestParam(name = "msg",defaultValue = "现在几点")String  msg) {
        // 创建工具,工具注册到工具集合中
        ToolCallback[] tools = ToolCallbacks.from(new DataTimeTools());
        // 将工具集配置进ChatOptions当中
        ToolCallingChatOptions build = ToolCallingChatOptions.builder().toolCallbacks(tools).build();
​
        Prompt prompt = new Prompt(msg, build);
​
        return chatModel.call(prompt).getResult().getOutput().getText();
​
    }
}

我们通过访问地址就可以得到经过大模型修饰过的返回结果:

如果我们使用的是ChatClient我们依旧可以正常使用和Chatmodel一样

@GetMapping("/toolcall/chatClient")
public Flux<String> stream(@RequestParam(name = "msg", defaultValue = "现在几点") String msg) {
    ToolCallback[] tools = ToolCallbacks.from(new DataTimeTools());
    // 将工具集配置进ChatOptions当中
    ToolCallingChatOptions build = ToolCallingChatOptions.builder().toolCallbacks(tools).build();
    Prompt prompt = new Prompt(msg, build);
    return qwenChatClient.prompt(prompt)
            .stream()
            .content();
}

MCP模型上下文协议

MCP是什么?

在早期Spring AI(1.0.0-M6)的版本的时候,FunctingCalling几乎要被MCP取代,因为简单来说Tool Calling的编写工具类并没有办法给多个微服务进行调用,同样如果我们想要多写几个工具包的话,我们同样也需要进行大量的代码编写,所以从多种的角度来说的话,如何进行二者的兼顾呢,为了解决这个问题,我们为了解决多个微服务之间的问题,我们就可以使用MCP协议,简单来说就是我们通过自己的服务去调用同一个大模型服务,通过遵守MCP协议这样我们就可以通过一套协议就可以使用多家的工具包,所以就出现了MCP,几乎可以取代Tool Calling。

理解MCP很简单,就是统一的Type-C协议,只要是这个接口就可以使用进行充电和传递数据,MCP就是可以这样。我们如果想使用MCP,我们有两种方式,SSE,STDIO这两种方式的区别在于STDIO支持标准输入和输出流进行通信,主要用于本地集成、命令行工具等场景,SSE支持使用 HTTP POST请求进行服务器到客户端流式处理,以实现客户端到服务器的通信。

MCP的官方网站:MCP Servers

本地MCP调用

首先我们先进行本地的MCP使用,也就是说我们自己进行撰写。

首先我们先引入依赖和修改yml文件

!--    &lt;!&ndash; Web 启动器(Spring Boot Web 核心依赖) &ndash;&gt;-->
<!--    <dependency>-->
<!--      <groupId>org.springframework.boot</groupId>-->
<!--      <artifactId>spring-boot-starter-web</artifactId>-->
<!--    </dependency>-->
    <!--注意,spring-boot-starter-web和spring-ai-starter-mcp-server-webflux是
    不能够同时使用的,使用第一个就会直接使用内置的tomcat最终导致mcpservice失败。-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>
​
spring.ai.mcp.server.type=async
spring.ai.mcp.server.name=custom-define-mcp-server
spring.ai.mcp.server.version=1.0.0
现在我们需要进行工具类的编写,这个时候就可以进行方法的使用了

package ai.Service;
​
import org.springframework.ai.tool.annotation.Tool;
​
import java.util.Map;
​
public class WeatherService {
    @Tool(description = "获取天气信息")
    public String getWeather(String city){
        Map<String,String> map = Map.of("北京","晴天"
                ,"上海","阴天"
                ,"深圳","雷阵雨");
        return map.getOrDefault(city,"未知城市");
    }
}

我们还需要一个配置类,来进行表示MCP的服务

package ai.config;
​
import ai.Service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class McpServerConfig {
    @Bean
    public ToolCallbackProvider toolCallbackProvider(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

我们启动之后就会发现启动的就是Netty服务器

我们如果想要正常使用这个服务端的话,首先我们要进行依赖的导入,我们如果想要调用需要另一个微服务进行。

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-mcp-server-mcp-client</artifactId>
</dependency>

这个时候我们的配置文件就需要有这样的改动,注意端口号要是你服务端开启的端口进行调用。

spring.ai.mcp.server.type=async
spring.ai.mcp.server.url=http://localhost:8080
spring.ai.mcp.server.timeout=5000

然后重点在于Client配置的参数引入:

@Bean
public ChatClient chatClient(ChatModel chatModel
        , ToolCallbackProvider tools) {
        return ChatClient.builder(chatModel)
                            .defaultToolCallbacks(tools.getToolCallbacks())
                            .build();        
                            }

对于控制层的代码,我们就和正常调用是一样的

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐