1、pom
-
-
-
-
-
-
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.2.0</spring-boot.version>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
<jedis.version>5.2.0</jedis.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 阿里云通义千问(DashScope)starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!--对话记忆 chat-memory-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-memory</artifactId>
</dependency><!-- Spring Security 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 统一管理Spring AI依赖版本 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Spring AI 里程碑/快照仓库(必须配置,否则依赖无法下载) -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
2、ymlserver:
port: 18086
spring:
ai:
dashscope:
api-key: sk-8718a83408d7443xxxxxxxxx
3、configimport org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user1")
.password("pass1").roles("USER").build();
UserDetails admin = User.withUsername("admin")
.password("pass2").roles("ADMIN").build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
// 放行 /tool 接口,让AI对话可访问
.requestMatchers("/tool").permitAll()
// 其他请求需认证
.anyRequest().authenticated()
).with(new FormLoginConfigurer<>(),Customizer.withDefaults());
return http.build();
}
// 仅测试用。生产请使用更安全的加密方式
@Bean
public static org.springframework.security.crypto.password.PasswordEncoder
passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
4、serviceimport org.springframework.stereotype.Service;
//票务服务
@Service
public class TicketService {
//退票
public void cancel(String ticketNumber, String name) {
// 实际业务逻辑:调用数据库/API执行退票
System.out.println("退票成果");
}
}import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.List;
@Service
public class ToolService {
@Autowired
private TicketService ticketService;
//
// record cancelParam(String dsadsa,String fdasfda){}
//
// @Tool(description = "退票")
// public String cancel(cancelParam param) {
// ticketService.cancel(param.dsadsa(), param.fdasfda());
// return "退票成功!";
// }
// record cancelParam(String mmmmmmmmmmqweqweqw,String oooooooooodsfsdds){}
//
// // @Tool告诉大模型提供了什么工具
// @Tool(description = "根据订单号和乘客姓名办理机票退票业务,退票成功后会返还票款")
// public String cancel(
// //@ToolParam告诉大模型参数描述
// cancelParam param) {
// //先查询--->先校验
// ticketService.cancel(param.mmmmmmmmmmqweqweqw(), param.oooooooooodsfsdds());
// return "退票成功!";
// }
//record cancelParam(String ticketNumber,String name){}
// @Tool告诉大模型提供了什么工具
// @Tool(description = "退票")
// public String cancel(
// //@ToolParam告诉大模型参数描述
// cancelParam param) {
// //先查询--->先校验
// ticketService.cancel(param.ticketNumber(), param.name());
// return "退票成功!";
// }
// @Tool(description = "退票")
// public String cancel(
// //@TooLParam告诉大模型参数的描述 加严参数描述与校验
// @ToolParam(description = "预定号,可以是纯数字") String ticketNumber,
// @ToolParam(description = "真实人名(必填,必须为人的真实姓名,严禁用其他信息代替如缺失请传null)") String name) {
// // 后端代码加强校验和兜底保护
// //先查询--->先校验
// ticketService.cancel(ticketNumber, name);
// return "退票成功!";
// }
record cancelParam(String ticketNumber,String name){}
// @Tool告诉大模型提供了什么工具
@Tool(description = "退票")
@PreAuthorize("hasRole('ADMIN')")
public String cancel(
//@TooLParam告诉大模型参数的描述 加严参数描述与校验
@ToolParam(description = "预定号,可以是纯数字") String ticketNumber,
@ToolParam(description = "真实人名(必填,必须为人的真实姓名,严禁用其他信息代替如缺失请传null)") String name) {
// 后端代码加强校验和兜底保护
// 获取当前登录用户信息
String username = SecurityContextHolder.getContext()
.getAuthentication()
.getName();
//先查询--->先校验
ticketService.cancel(ticketNumber, name);
return username + "退票成功!";
}
@Tool(description = "获取指定位置的天气,根据位置自动推算经纬度")
public String getAirQuality(
@ToolParam(description = "纬度") double latitude,
@ToolParam(description = "经度") double longitude) {
return "天晴";
}
/**
* 模拟从数据库中动态根据当前用户角色读取tooLs
* @param toolService
* @return
*/
public List<ToolCallback> getToolCallList(ToolService toolService) {
//1.获取tools处理的方法
Method method = ReflectionUtils.findMethod(ToolService.class,
"cancel", String.class, String.class);
//2.构建Tool定义信息 动态配置的方式@Tool @Toop
ToolDefinition build = ToolDefinition.builder()
.name("cancel")
.description("退票")
//对应@ToolParam
.inputSchema("""
{
"type": "object",
"properties": {
"ticketNumber": {
"type": "string",
"description": "预定号,可以是纯数字"
},
"name": {
"type": "string",
"description": "真实人名"
}
},
"required": ["ticketNumber", "name"]
}
""")
.build();
//一个Toolcallback对应一个tool
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(build)
.toolMethod(method)
.toolObject(toolService) // 不能自己new,自己new不能解析依赖注
.build();
return List.of(toolCallback);
}
}
5、controllerimport com.sb.dashscope18086.service.ToolService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.ResponseBody;
/**
* @author Administrator
*/
@RequestMapping("/openai")
@ResponseBody
@Controller
public class ChatclientToolController {
@Autowired
private ChatClient.Builder chatClientBuilder;
//
@Autowired
private ToolService toolService;
// ChatClient chatClient;
//
// public ChatclientToolController(ChatClient.Builder chatClientBuilder, ToolService toolService) {
// chatClient= chatClientBuilder
// .defaultTools(toolService)
// .build();
// }
@GetMapping("/simple/tool5")
public String tool5(
@RequestParam(value = "message",
defaultValue = "讲个笑话") String message
) {
// // 构建ChatClient并调用
ChatClient chatClient = chatClientBuilder
//
//系统Prompt设定限制
.build();
String content = chatClient.prompt()
.user(message)
//.tools(toolService)//用 .tools() 替代 defaultTools()
// .tools(toolService) 和 .toolCallbacks(...) 同时调用了,导致同一个 cancel 工具被注册了两次.
//,触发了 Multiple tools with the same name 错误
.toolCallbacks(toolService.getToolCallList(toolService))
.call()
.content();
System.out.println(content);
return content;
}
@GetMapping("/simple/tool6")
public String tool6(
@RequestParam(value = "message",
defaultValue = "讲个笑话") String message
) {
// // 构建ChatClient并调用
ChatClient chatClient = chatClientBuilder
//
//系统Prompt设定限制
.defaultSystem("""
# 角色
你是智能航空客服助手
## 要求
"严禁随意补全或猜测工具调用参数。 参数如缺失或语义不准,请不要补充或随意传递,请直接放弃本次工具调用。"
""")
//.defaultTools(toolService)
.build();
String content = chatClient.prompt()
.user(message)
// .tools(toolService) 和 .toolCallbacks(...) 同时调用了,导致同一个 cancel 工具被注册了两次.
//,触发了 Multiple tools with the same name 错误
//.tools(toolService)//用 .tools() 替代 defaultTools()
.toolCallbacks(toolService.getToolCallList(toolService))
.call()
.content();
System.out.println(content);
return content;
}
}import com.sb.dashscope18086.service.ToolService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Controller;
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.ResponseBody;
/**
* @author Administrator
*/
@RequestMapping("/openai")
@ResponseBody
@Controller
public class ChatclientToolControllertest {
// @Autowired
// private ChatClient.Builder chatClientBuilder;
// //
// @Autowired
// private ToolService toolService;
ChatClient chatClient;
public ChatclientToolControllertest(ChatClient.Builder chatClientBuilder, ToolService toolService) {
chatClient= chatClientBuilder
.defaultSystem("""
# 角色
你是智能航空客服助手
## 要求
"严禁随意补全或猜测工具调用参数。 参数如缺失或语义不准,请不要补充或随意传递,请直接放弃本次工具调用。"
""")
//.defaultTools(toolService)
//动态tooL设置defaultToolCallbacks
// .tools(toolService) 和 .toolCallbacks(...) 同时调用了,导致同一个 cancel 工具被注册了两次.
//,触发了 Multiple tools with the same name 错误
.defaultToolCallbacks(toolService.getToolCallList(toolService))
.build();
}
@GetMapping("/simple/tool7")
public String tool7(
@RequestParam(value = "message",
defaultValue = "讲个笑话") String message
) {
// // 构建ChatClient并调用
// ChatClient chatClient = chatClientBuilder
// //
// //系统Prompt设定限制
// .build();
String content = chatClient.prompt()
.user(message)
//.tools(toolService)//用 .tools() 替代 defaultTools()
.call()
.content();
System.out.println(content);
return content;
}
}
6、
6.1 先登录
6.2
http://localhost:18086/openai/simple/tool5?message=退票.赖宁.101991
6.3
http://localhost:18086/openai/simple/tool6?message=退票.柴荣.B88801

6.4
http://localhost:18086/openai/simple/tool7?message=退票.周太祖.10101
-
-
-
-
-
浙公网安备 33010602011771号