一、前言
本文主要介绍采用spring ai对接deepseek。
以及采用spring ai alibaba做智能体的示例。
使用的是deepseek的在线的模型,采用openai的协议调用。
二、pom信息
现在使用spring ai,涉及spring boot ,spring framework等的版本对应关系,顾本文也给一下pom,免得阅读者再去找了。
本文使用的主要框架的版本
spring boot 版本3.5.8
spring-ai 版本1.1.2
spring ai alibaba版本1.1.2.0
parent-pom
xml
<?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>
<groupId>com.shenyun</groupId>
<artifactId>ly-ai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<skipTests>true</skipTests>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.deploy.skip>true</maven.deploy.skip>
<!-- <spring-framework.version>6.1.0+</spring-framework.version>-->
<!-- <spring-boot.version>3.2.0</spring-boot.version>-->
<spring-boot.version>3.5.8</spring-boot.version>
</properties>
<modules>
<module>ly-ai-web</module>
</modules>
<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>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.1.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-extensions-bom</artifactId>
<version>1.1.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.48</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version> <!-- 推荐使用最新稳定版本 -->
<!-- 或 optional=true,视模块用途而定 -->
<!-- <scope>provided</scope> -->
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xlint:-options</arg>
<!-- <arg>-Xlint:-options</arg>-->
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
子模块pom
xml
<?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.shenyun</groupId>
<artifactId>ly-ai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>ly-ai-web</artifactId>
<name>ly-ai-web</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<!-- <version>1.1.2.0</version>-->
</dependency>
<!-- DashScope-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.1.2.0</version>
</dependency>
<!-- OpenAI-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Ollama-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
</build>
</project>
三、直接上代码
api调用deepseek的接口
因为deepseek的接口是兼容openai协议的,所以使用OpenAiApi 的SDK接口
注意apikey需要换成自己的
java
package com.shenyun.agent;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import java.time.Duration;
/**
* openai sdk兼容调用deepseek
* @author shenyun
* @date 2023/9/27
*/
public class DeepSeekSimpleExample {
public static void main(String[] args) {
// 设置连接和读取超时为 5 分钟(根据模型大小调整)
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofMinutes(5));
// 1、创建 DeepSeek API
OpenAiApi deepSeekApi = OpenAiApi.builder()
.baseUrl("https://api.deepseek.com")
.apiKey("sk-")
.webClientBuilder(
//自定义连接超时配置,可以使用默认的就不设置
WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient))
)
.build();
// 2、创建 ChatModel
ChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(deepSeekApi)
.defaultOptions(OpenAiChatOptions.builder()
.model("deepseek-chat")
.build())
.build() ;
// 3、调用模型
ChatResponse response = chatModel.call(
new Prompt("How many letter 'r' are in the word 'strawberry'?"));
// 4、打印结果
// String thinking = response.getResult().getMetadata().get("thinking");
// System.out.println("thinking:\n"+ thinking);
String answer = response.getResult().getOutput().getText();
System.out.printf(answer);
}
}
使用spring ai alibaba的框架创造智能体
注意apikey需要换成自己的
java
package com.shenyun.agent;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import lombok.Data;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import java.util.function.BiFunction;
public class DeepSeekAgentExample {
public static void main(String[] args) {
// 1、初始化 DeepSeek API
OpenAiApi deepSeekApi = OpenAiApi.builder()
.baseUrl("https://api.deepseek.com")
.apiKey("sk-")
.build()
;
// 2、创建 DeepSeek ChatModel
ChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(deepSeekApi)
.defaultOptions(OpenAiChatOptions.builder()
.model("deepseek-chat")
.temperature(0.7)
.build())
.build();
// 3、创建工具
// 创建天气查询工具
ToolCallback weatherTool = FunctionToolCallback
.builder("get_weather", new WeatherTool())
.description("获取指定城市的天气信息")
.inputType(WeatherTool.Input.class)
.build();
// 创建时间查询工具
ToolCallback timeTool = FunctionToolCallback
.builder("get_current_time", new TimeTool())
.description("获取当前时间")
.inputType(Void.class)
.build();
// 创建计算器工具
ToolCallback calculatorTool = FunctionToolCallback
.builder("calculate", new CalculatorTool())
.description("执行数学计算,支持加减乘除")
.inputType(CalculatorTool.Input.class)
.build();
// 4、创建 agent
ReactAgent agent = ReactAgent.builder()
.name("deepseek-chat")
.model(chatModel)
.tools(weatherTool, timeTool, calculatorTool)
.systemPrompt("你是一个有用的助手,可以查询天气,获取时间,计算数学表达式")
.saver(new MemorySaver())
.build();
// 5、测试调用
// testAgent(agent, "[北京]今天天气怎么样?");
// testAgent(agent, "现在几点了?");
testAgent(agent, "计算 123 + 456 等于多少?");
// testAgent(agent, "帮我查一下上海天气,然后告诉我现在时间,最后计算 100 * 50");
}
private static void testAgent(ReactAgent agent, String query) {
System.out.println("\n用户提问: " + query);
try {
AssistantMessage response = agent.call(query);
System.out.println("Agent 回复: " + response.getText());
} catch (GraphRunnerException e) {
System.err.println("调用失败: " + e.getMessage());
}
System.out.println("-".repeat(50));
}
public static class WeatherTool implements BiFunction<WeatherTool.Input, ToolContext, String> {
@Data
public static class Input {
private String city;
}
@Override
public String apply(Input input, ToolContext toolMetadata) {
String city = input.city;
System.out.println("WeatherTool called with city: " + city);
if (city == null || city.trim().isEmpty()) {
return "请提供有效的城市名称";
}
// 模拟天气数据
String[] weatherConditions = {"晴天", "多云", "阴天", "小雨", "大雨", "雪"};
int temp = (int) (Math.random() * 30) + 5; // 5-35度
String condition = weatherConditions[(int) (Math.random() * weatherConditions.length)];
return String.format("%s今天的天气是%s,温度为%d°C", city, condition, temp);
}
}
// 时间查询工具
public static class TimeTool implements BiFunction<Void, ToolContext, String> {
@Override
public String apply(Void unused, ToolContext toolMetadata) {
return "当前时间: " + new java.util.Date().toString();
}
}
// 计算器工具
public static class CalculatorTool implements BiFunction<CalculatorTool.Input, ToolContext, String> {
@Data
public static class Input {
private String expression;
}
@Override
public String apply(Input input, ToolContext toolMetadata) {
String expression = input.expression;
if (expression == null || expression.trim().isEmpty()) {
return "请提供有效的数学表达式";
}
try {
// 简单的计算器实现
expression = expression.trim();
double result;
if (expression.contains("+")) {
String[] parts = expression.split("\\+");
result = Double.parseDouble(parts[0].trim()) + Double.parseDouble(parts[1].trim());
} else if (expression.contains("-")) {
String[] parts = expression.split("-");
result = Double.parseDouble(parts[0].trim()) - Double.parseDouble(parts[1].trim());
} else if (expression.contains("*")) {
String[] parts = expression.split("\\*");
result = Double.parseDouble(parts[0].trim()) * Double.parseDouble(parts[1].trim());
} else if (expression.contains("/")) {
String[] parts = expression.split("/");
result = Double.parseDouble(parts[0].trim()) / Double.parseDouble(parts[1].trim());
} else {
return "不支持的表达式格式,请使用 +, -, *, / 进行计算";
}
return String.format("计算结果: %.2f", result);
} catch (Exception e) {
return "计算错误: " + e.getMessage();
}
}
}
}
四、总结及踩坑(重点)
spring ai与spring ai alibaba的区别
Spring AI是(基础能力):
统一抽象:ChatModel、EmbeddingModel、Prompt、Tool、VectorStore 等
支持多种国际模型:OpenAI、Gemini、Claude、Mistral、Ollama 等
支持主流向量数据库:PostgreSQL(pgvector)、Redis、Milvus 等
局限:仅提供原子组件,无内置工作流编排、无智能体框架、无状态管理
Spring AI Alibaba是(增强能力):
在完全兼容 Spring AI 的基础上,新增了些功能:
Agent Framework
内置 ReActAgent、SupervisorAgent、SequentialAgent 等模式
自动上下文工程、人机交互(Human-in-the-Loop)
开箱即用构建具备推理+工具调用能力的智能体
Graph(核心创新)
基于 DAG 的低代码工作流引擎(类似 LangGraph 的 Java 实现)
预置节点:LlmNode、RAGSearchNode、SecurityCheckNode、ToolNode 等
支持条件分支、并行执行、循环、全局状态管理
国内在线模型的协议
深度集成国内模型(通义千问、月之暗面、DeepSeek)+ 兼容国际模型
关于智能体中工具方法的定义
如WeatherTool,TimeTool,CalculatorTool这三个方法的定义。
1、如果有入参,一定要定义一个DTO对象,即便入参只有一个参数,不要直接使用String
下面代码是错误的示例
java
// 天气查询工具
public static class WeatherTool implements BiFunction<String, ToolContext, String> {
@Override
public String apply(String city, ToolContext toolMetadata) {
// 模拟天气数据
if (city == null || city.trim().isEmpty()) {
return "请提供有效的城市名称";
}
// 这里可以接入真实的天气API
String[] weatherConditions = {"晴天", "多云", "阴天", "小雨", "大雨", "雪"};
int temp = (int) (Math.random() * 30) + 5; // 5-35度
String condition = weatherConditions[(int) (Math.random() * weatherConditions.length)];
return String.format("%s今天的天气是%s,温度为%d°C", city, condition, temp);
}
}
以下产生是错误日志
19:13:36.208 [main] WARN org.springframework.ai.retry.RetryUtils -- Retry error. Retry count:1
org.springframework.ai.retry.NonTransientAiException: 400 - {"error":{"message":"Invalid schema for function 'calculate': schema must be a JSON Schema of 'type: \"object\"', got 'type: \"string\"'.","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}
at org.springframework.ai.retry.RetryUtils$1.handleError(RetryUtils.java:73)
at org.springframework.ai.retry.RetryUtils$1.handleError(RetryUtils.java:57)
at org.springframework.web.client.StatusHandler.lambda$fromErrorHandler$1(StatusHandler.java:71)
at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:146)
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.applyStatusHandlers(DefaultRestClient.java:838)
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.lambda$readBody$4(DefaultRestClient.java:827)
at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:216)
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:826)
2、入参定义的DTO一定要写get,set方法。或者使用@Data这类注解。