ToolCalling工具调用

55 阅读2分钟

ToolCalling

实时信息获取不到,LLM大模型搞不定实时信息。 所有现在要使用Tool Calling

spring官网: docs.spring.io/spring-ai/r…

spring ai alibaba官网: java2ai.com/docs/framew…

ToolCalling是什么

Tool Calling: LLM的外部utils工具类

  • ToolCalling允许大模型与一组API或工具进行交互,将LLM的智能与外部工具或API无缝衔接,从而增强大模型功能。
  • LLM大模型本身不能实际调用工具,只指示应该调用哪个函数以及如何调用

ToolCalling能做什么?

  • 实时信息访问(比如天气、股票信息等)
  • 执行某种工具类/辅助类操作

ToolCalling 工作流程

image.png

  1. 当我们想要使 tool 可用于 model 时,我们在聊天请求中包含其定义。每个 tool 定义包括名称、描述和输入参数的 schema。
  2. 当 model 决定调用 tool 时,它发送带有 tool 名称和根据定义 schema 建模的输入参数的响应。
  3. 应用程序负责使用 tool 名称来识别并使用提供的输入参数执行 tool。
  4. Tool call 的结果由应用程序处理。
  5. 应用程序将 tool call 结果发送回 model。
  6. Model 使用 tool call 结果作为附加上下文生成最终响应。

开发步骤

创建module

image.png

POM

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.miao</groupId>
        <artifactId>SpringAIAlibaba-test01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>SAA-10ToolCalling</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

        <!--  模型服务灵积  调用alibaba生态的协议 对标openai协议   -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
    </dependencies>
</project>

写yml

server:
  port: 8082
  servlet:
    encoding:
      enabled: true
      force: true
      charset: UTF-8

spring:
  application:
    name: SAA-10
  ai:
    dashscope:
      api-key: ${qwen-api-key}
      chat:
        options:
          model: qwen3-vl-flash

主启动类

package com.miao;

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

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

业务类

config配置
package com.miao.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 org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

    private static final String QWEN_MODEL = "qwen3-max";
    private static final String DEEPSEEK_MODEL = "deepseek-v3.1";

    @Value("${spring.ai.dashscope.url")
    private String qwenUrl;

    @Bean(name = "deepseek")
    public ChatModel deepSeek() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(DEEPSEEK_MODEL)
                        .build())
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                .build();
    }

    @Bean(name = "deepseekChatClient")
    public ChatClient deepSeekChatClient(@Qualifier("deepseek") ChatModel deepseekModel) {
        return ChatClient.builder(deepseekModel)
                .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
                .build();
    }

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwenModel) {
        return ChatClient.builder(qwenModel)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .build();
    }
}
tools工具
package com.miao.utils;

import org.springframework.ai.tool.annotation.Tool;

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

public class DateTimeTools {


    /**
     * returnDirect = false 表示返回结果不直接返回给用户,而是作为后续处理的输入
     * returnDirect = true 表示返回结果直接返回给用户
     *
     * 如果没有msg入参,直接抛出异常,所以在此处加了一共没有什么用的参数
     */
    @Tool(name = "get_cur_time", description = "获取当前时间", returnDirect = false)
    public String getCurrentDateTime(String msg) {
        return LocalDateTime.now().toString();
    }
}
controller
package com.miao.controller;

import com.miao.utils.DateTimeTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
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;
import reactor.core.publisher.Flux;

@RestController
public class ToolCallingController {

    @Resource
    private ChatModel deepseek;

    @Resource
    private ChatClient deepseekChatClient;


    @GetMapping(value = "/toolCalling")
    public Object toolCalling(@RequestParam(name = "msg", defaultValue = "现在多少年 几点几分了") String msg) {
        // 1 工具类注册到工具集里面
        ToolCallback[] tools = ToolCallbacks.from(new DateTimeTools());

        // 2将工具类配置进入ChatOptions对象
        ChatOptions toolOptions = ToolCallingChatOptions
                .builder()
                .toolCallbacks(tools)
                .build();

        // 3 构建提示词
        Prompt prompt = new Prompt(msg, toolOptions);

        return deepseek.call(prompt).getResult().getOutput().getText();
    }


    @GetMapping(value = "/chatClientToolCalling")
    public Flux<String> chatClientToolCalling(@RequestParam(name = "msg", defaultValue = "现在多少年 几点几分了") String msg) {
        return deepseekChatClient.prompt(msg)
                .tools(new DateTimeTools())
                .stream()
                .content();
    }


}