詹学伟
詹学伟
Published on 2025-11-27 / 2 Visits
0
0

saa-ToolCalling

一、说明

本章节主要介绍spring ai alibaba的Tool Calling及实践,Tool Calling即工具调用,在之前langchain4j中也叫Function Calling,也就是大模型调用外部工具。比如之前做的数据库专家智能体、医疗专家智能体,都用到了调用外部工具。

另外,本章节代码使用统一的父工程,见saa第一篇文章。

二、代码

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.saa</groupId>
		<artifactId>saa-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<groupId>com.saa</groupId>
	<artifactId>saa-toolCalling</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>saa-toolCalling</name>
	<description>saa-toolCalling</description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>17</maven.compiler.source>
		<maven.compiler.target>17</maven.compiler.target>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.34</version>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.8.16</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>

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


	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>17</source>
					<target>17</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${spring-boot.version}</version>
			</plugin>
		</plugins>
	</build>

</project>

properties配置文件

server.port=8091

server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

spring.application.name=saa-toolCalling

spring.ai.dashscope.api-key=sk-09c7b571687b46d5a2e25a03fbdd2fd1

配置类

package com.saa.toolcalling.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;

/**
 * @author zhanxuewei
 */
@Configuration
public class SsaLLMConfig {


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

    private static final String MODEL_DEEPSEEK = "deepseek-v3";
    private static final String MODEL_QWEN = "qwen-max";

    @Bean(name = "deepseek")
    public ChatModel deepseek() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(MODEL_DEEPSEEK).build())
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(MODEL_QWEN).build())
                .build();
    }

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

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen) {
        return ChatClient.builder(qwen)
                .defaultOptions(ChatOptions.builder().model(MODEL_QWEN).build())
                .build();
    }

}

控制器

不使用工具调用:

package com.saa.toolcalling.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * @author zhanxuewei
 */
@RestController
@RequestMapping("/noToolCalling")
public class NoToolCallingController {

    @Resource(name = "qwen")
    private ChatModel chatModel;

    @GetMapping("/chat")
    public Flux<String> chat(@RequestParam(name = "msg", defaultValue = "你是谁现在几点") String msg) {
        return chatModel.stream(msg);
    }
}

使用工具调用:

package com.saa.toolcalling.controller;

import com.saa.toolcalling.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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * 测试工具调用
 */
@RestController
@RequestMapping("/toolCalling")
public class ToolCallingController {

    @Resource(name = "qwen")
    private ChatModel chatModel;

    @Resource(name = "qwenChatClient")
    private ChatClient chatClient;

    @GetMapping("/chat")
    public String chat(@RequestParam(name = "msg", defaultValue = "你是谁现在几点") String msg) {
        ToolCallback[] tools = ToolCallbacks.from(new DateTimeTools());
        ChatOptions options = ToolCallingChatOptions.builder()
                .toolCallbacks(tools)
                .build();
        Prompt prompt = new Prompt(msg, options);
        return chatModel.call(prompt).getResult().getOutput().getText();
    }

    @GetMapping("/chat2")
    public Flux<String> chat2(@RequestParam(name = "msg", defaultValue = "你是谁现在几点") String msg) {
        return chatClient.prompt(msg)
                .tools(new DateTimeTools())
                .stream()
                .content();
    }
}

三、测试

不使用工具调用:

使用工具待用1:

使用工具调用2:


Comment