SpringAIAlibaba学习使用 ---核心API、RAG、Tool Calling
本文为学习记录文档,学习参考于三更草堂springAI视频

SpringAIAlibaba概述
它本质上是一个基于 Spring AI 抽象层的"增强插件集",其核心价值在于提供了统一、便捷的接口,让你 可以灵活地选择和切换底层模型。【用于调用大模型的api,将原始的调用方法进行封装,便于用户填参数调用 】 官方文档地址:java2ai.com/docs/overvi...
本次采用调用智谱AI为例
1、快速入门
创建父工程,便于后续开发、调用
版本说明:
- jdk:17(最低要用17)
- springAi:1.0.0
- SpringBoot:3.4.0
- Spring Ai Alibaba: 1.0.0.4
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>
<groupId>com.xiaoyu</groupId>
<artifactId>springai-examples</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.version>1.0-SNAPSHOT</project.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
<!-- Spring Boot -->
<spring-boot.version>3.4.0</spring-boot.version>
<!-- maven plugin -->
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
</properties>
<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>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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>17</release>
<compilerArgs>
<compilerArg>-parameters</compilerArg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<inherited>true</inherited>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>ossrh</flattenMode>
<pomElements>
<distributionManagement>remove</distributionManagement>
<dependencyManagement>remove</dependencyManagement>
<repositories>remove</repositories>
<scm>keep</scm>
<url>keep</url>
<organization>resolve</organization>
</pomElements>
</configuration>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
创建子工程
添加依赖:
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
由于我们要调用的是智谱AI,所以需要引入spring-ai-starter-model-zhipuai。如果用的是别的AI模型引入 对于的启动器即可
- deepseek - spring-ai-starter-model-deepseek
- 千问 - spring-ai-alibaba-starter-dashscope
- OpenAI - spring-ai-starter-model-openai
配置文件
yaml
server:
port: 8080
servlet:
encoding:
charset: utf-8
enabled: true # 启用流
force: true
spring:
ai:
zhipuai:
api-key: ******************* # 配置 API Key
base-url: "https://open.bigmodel.cn/api/paas" # 配置 模型地址
chat:
options:
model: glm-4.5
http:
client:
read-timeout: 1000000
启动类
java
package com.xiaoyu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ZhipuAIApplication {
public static void main(String[] args) {
SpringApplication.run(ZhipuAIApplication.class, args);
}
}
代码
java
@RestController
@RequestMapping("/zhipuai")
public class ZhipuChatController {
private final ChatModel chatModel;
// 通过构造器注入 ChatModel
public ZhipuChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
// 调用ChatModel 的call方法传入问题完成模型调用
return chatModel.call(query);
}
2、 核心Api
2.1 Message
智谱api地址:docs.bigmodel.cn/cn/api/intr... 选自其中的一段调用实列:
bash
curl -X POST "https://open.bigmodel.cn/api/paas/v4/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "glm-5",
"messages": [
{
"role": "system",
"content": "你是一个有用的AI助手。"
},
{
"role": "user",
"content": "你好,请介绍一下自己。"
}
],
"temperature": 1.0,
"stream": true
}'
messages是一个对象数组,其中的每个对象都有role和content两个属性。 智谱文档中对Message的介绍:

SpringAI中的设计的Message的继承体系:

从这个继承关系中我们可以看到AbstractMessage相对于把我们接口中看到的role字段抽象成 messageType属性。content字段抽象成textContent属性。
MessageType定义的枚举源码如下:
java
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
TOOL("tool");
}
和目前role可以使用的4种值对应 因此简单实现为:
java
@GetMapping("/message")
public String message(@RequestParam("query") String query) {
SystemMessage systemMessage = new SystemMessage("你是一个AI小助手");
UserMessage userMessage = new UserMessage(query);
return chatModel.call(systemMessage, userMessage);
}
}
2.2 Prompt
Prompt 提示词 是引导 AI 模型生成特定输出的输入格式,Prompt 的设计和措辞会显著影响模型的响 应。

在SpringAI中,是把模型参数和消息列表的组合抽象为Prompt。 所以Prompt中主要是有两个属性【messages和chatOptions】

执行chatModel.call方法时填入prompt参数,直接设置强求参数

ChatOptions
不同厂商的模型支持的模型参数不同,当然也有些参数上普遍都支持的。所以SpringAI在 设计的时候把共有的模型参数都设计到了ChatOptions中。
每个模型厂商可以创建自己的ChatOptions实现定义厂商支持的模型参数。
所以我们看下当前项目中ChatOptions的实现有哪些。就发现了ZhiPuAiChatOptions。

ZhiPuAiChatOptions对象的创建
java
ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
zhiPuAiChatOptions.setModel("glm-4.5");
zhiPuAiChatOptions.setTemperature(0.0);
zhiPuAiChatOptions.setMaxTokens(15536);
// 流式赋值
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
.model("glm-4.5")
.maxTokens(15536) // 最大tokens数
.temperature(0.0) // 0到1之间 值越高(如0.8)使输出更加随机,值越低(如0.2)使其更集中,更有确定性
.build();
改造调用:
java
@GetMapping("/chatOptions")
public ChatResponse chatOptions(@RequestParam("query") String query) {
SystemMessage systemMessage = new SystemMessage("你是一个AI助手");
UserMessage userMessage = new UserMessage(query);
ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
zhiPuAiChatOptions.setTemperature(0.0);
zhiPuAiChatOptions.setMaxTokens(15536);
// zhiPuAiChatOptions.builder().model("glm-4.5").temperature(0.0).maxTokens(15536);
return chatModel.call(new Prompt(List.of(systemMessage, userMessage), zhiPuAiChatOptions));
}
2.3 ChatModel
如图,ChatModel API 让应用开发者可以非常方便的与 AI 模型进行文本交互,它抽象了应用与 模型交互的过程,包括使用 Prompt作为输入,使用ChatResponse作为输出等。

代码编写
1、从容器注入
java
@RestController
@RequestMapping("/zhipu")
public class ZhiPuChatController {
private final ChatModel chatModel;
public ZhiPuChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
}
2、模型调用
java
/* 同步响应 */
@GetMapping("/simple")
public String simple(@RequestParam("query") String query) {
return chatModel.call(query);
}
/* 流式响应 */
@GetMapping("/stream/chat")
public Flux<String> stream(@RequestParam("query") String query) {
Flux<String> chatResponse = chatModel.stream(query);
return chatResponse;
}
3、乱码问题解决:
yaml
server:
servlet:
encoding:
charset: utf-8
enabled: true # 启用流
force: true
2.4 ChatClient
通过chatmodel调用模型,方法步骤有些繁琐,springAI提供了ChatClient方便我们的调用
大致功能
- 定制和组装模型的输入(Prompt)
- 格式化解析模型的输出(Structured Output)
- 调整模型交互参数(ChatOptions)
- 聊天记忆(Chat Memory)
- 工具/函数调用(Function Calling)
- RAG
- 等等..........
代码编写 1、创建ChatClient ①容器注入
java
@RestController
@RequestMapping("chatclient")
public class ZhiPuChatClientController {
private final ChatClient chatClient;
public ZhiPuChatClientController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
}
②创建ChatClient.Builder来构造ChatClient
java
@RestController
@RequestMapping("/chatclient")
public class ZhipuChatClientController {
private final ChatClient chatClient;
public ZhipuChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.build();
}
}
2、调用模型
java
/* 调用流式赋值 */
@GetMapping("/simple0")
public String simple0(@RequestParam("query") String query) {
ZhiPuAiChatOptions build = ZhiPuAiChatOptions.builder()
.temperature(0.0)
.maxTokens(15536)
.build();
return chatClient.prompt().system("你是一个AI助手").user(query)
.options(build).call().content();
}
/* 普通赋值 */
@GetMapping("/simple")
public String simple(@RequestParam("query") String query) {
SystemMessage systemMessage = new SystemMessage("你是一个AI助手");
UserMessage userMessage = new UserMessage(query);
ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
zhiPuAiChatOptions.setTemperature(0.0);
zhiPuAiChatOptions.setMaxTokens(15536);
return chatClient
.prompt(new Prompt(List.of(systemMessage,userMessage), zhiPuAiChatOptions))
.call()
.content();
}
3、响应处理
java
@GetMapping("/simple3")
public ChatResponse simple3(@RequestParam("query") String query) {
UserMessage userMessage = new UserMessage(query);
ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
zhiPuAiChatOptions.setTemperature(0.0);
zhiPuAiChatOptions.setMaxTokens(15536);
ChatResponse chatResponse = chatClient.prompt()
.system("你是一个AI助手")
.user(query)
.options(zhiPuAiChatOptions)
.call()
.chatResponse();
return chatResponse;
}
4、响应数据转化为实体 ①定义实体
java
@Data
public class Book {
private String name;
private String author;
}
②使用chatClient调用完call方法后调用entity方法传入要转化类的字节码对象
java
@GetMapping("/entity")
public Book entity() {
Book book = chatClient.prompt()
.system("你是一个AI助手")
.user("请帮我生成一本书,要求书名和作者为中文")
.call().entity(Book.class);
return book;
}
5、响应数据流式返回
java
@GetMapping("/stream")
public Flux<String> stream() {
Flux<String> flux = chatClient.prompt()
.system("你是一个AI助手")
.user("请帮我生成一本书,要求书名和作者为中文")
.stream()
.content();
return flux;
}
2.4 Advisors
Spring AI Advisors 是 Spring AI 框架中的核心拦截器组件,专门用于处理和增强 AI 应用程序中的请求 与响应流。其设计于过滤器拦截器非常类似。 优势:
- 将常见的生成式 AI 模式(如对话记忆、敏感词过滤、RAG 检索)打包成可重用单元,简化开发流程
- 创建可跨不同模型和用例工作的可重用转换组件,提升代码灵活性
1、Advisor增强流程:

2、继承体系: 
- 同步调用增强可以使用CallAdvisor
- 流式调用(响应式调用)增强可以 使用StreamAdvisor
3、代码编写
- 1、添加Advisor进行增强
- 2、实现CallAdvisor接口重写其中的方法
- 3、adviseCall方法中的chatClientRequest 是封装了AI请求的对象,我们可以在Advisor方法中对其进行增强
- 4、adviseCall方法中的callAdvisorChain可以让我们对当前的AI请求进行放行,并且返回 ChatClientResponse。我们可以增强ChatClientResponse后再返回。
第一个advisor
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
@Slf4j
public class SGCallAdvisor1 implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
log.info("advisor1请求");
ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
log.info("advisor1响应");
return chatClientResponse;
}
@Override
public String getName() {
return "SGCallAdvisor1";
}
@Override
public int getOrder() {
return 0;
}
}
第二个advisor
java
package com.sangeng.advisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
@Slf4j
public class SGCallAdvisor2 implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
log.info("advisor2请求");
ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
log.info("advisor2响应");
return chatClientResponse;
}
@Override
public String getName() {
return "SGCallAdvisor2";
}
@Override
public int getOrder() {
return 0;
}
}
ChatClient添加Advisor
java
@RestController
@RequestMapping("/advisor")
public class AdvisorController {
private final ChatClient chatClient;
public AdvisorController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.build();
}
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
.maxTokens(15536)
.temperature(0.0)
.model("glm-4.5")
.build();
return chatClient.prompt()
.system("你是一个有用的AI助手。")
.user(query)
.advisors(new SGCallAdvisor1(),new SGCallAdvisor2())
.options(chatOptions)
.call().content();
}
}
输出: 
调整顺序 Advisor链中的顺序只需要修改getOrder的返回值,getOrder的返回值越小 在Advisor链中的顺序越靠前,也就是越早处理请求。
案例,自定义Advisor实现理解语义的对话,即:能够在进行多轮对话时,模型能知道我们之前聊过什么内容。
定义Advisor
java
package com.sangeng.advisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
演示增加上下文功能,实际中不使用该方法
* */
@Slf4j
public class SimpleMessageChatMemoryAdvisor implements BaseAdvisor {
private static Map<String, List<Message>> chatMemory = new HashMap<>();
// 请求增强
@Override
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
// 1、通过对话id查询之前的对话
String conversationId = chatClientRequest.context().get("conversationId").toString(); // 通过上下文获取
List<Message> messages = chatMemory.get(conversationId);
if (messages == null) {
messages = new ArrayList<>();
}
// 2、把这次的对话添加到对话记录中
List<Message> requestMessage = chatClientRequest.prompt().getInstructions();
messages.addAll(requestMessage);
chatMemory.put(conversationId, messages); // 放入会话id 和 记录
// 3、添加后的List<Message>放入请求中
Prompt oldPrompt = chatClientRequest.prompt();
Prompt newPrompt = oldPrompt.mutate().messages(messages).build();
ChatClientRequest request = chatClientRequest.mutate().prompt(newPrompt).build();
return request;
}
// 响应处理
@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
// 1、通过会话id查询之前的对话记录
String conversationId = chatClientResponse.context().get("conversationId").toString(); // 通过上下文获取
List<Message> historyMessages = chatMemory.get(conversationId);
if (historyMessages == null) {
historyMessages = new ArrayList<>();
}
// 2、获取response中的ai消息 添加到对话记录中
ChatResponse chatResponse = chatClientResponse.chatResponse();
if (ObjectUtils.isEmpty(chatResponse)) {
return chatClientResponse;
}
AssistantMessage output = chatResponse.getResult().getOutput();
historyMessages.add(output);
chatMemory.put(conversationId, historyMessages);
return chatClientResponse;
}
@Override
public int getOrder() {
return 0;
}
}
使用Advisor
java
package com.sangeng.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
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;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("chatMemory")
public class ZhiPuChatMemoryClientController {
private final ChatClient chatClient;
public ZhiPuChatMemoryClientController(ChatClient.Builder builder) {
// 设置默认的memoryAdvisor实现上下文
MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory
.builder()
.build();
MessageChatMemoryAdvisor memoryAdvisor = MessageChatMemoryAdvisor
.builder(messageWindowChatMemory)
.build();
this.chatClient = builder.
defaultAdvisors(memoryAdvisor)
.build();
}
// 系统提供的
@GetMapping("/MessageChatMemoryAdvisor")
public Flux<String> MessageChatMemoryAdvisor(@RequestParam(name = "query") String query,
@RequestParam(name = "conversationId") String conversationId) {
Flux<String> flux = chatClient.prompt()
.advisors(advisorSpec -> advisorSpec.param(MessageWindowChatMemory.CONVERSATION_ID, conversationId))
.user(query)
.stream()
.content();
return flux;
}
/* prompt Template */
@GetMapping("/PromptTemplate")
public Flux<String> PromptTemplate() {
// 1、用户提示词
PromptTemplate promptTemplate = new PromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题:{question}");
Message message = promptTemplate.createMessage(
Map.of("name", "小宇", "voice", "御姐", "question", "平凡的人指的被爱吗?"));
Prompt prompt = new Prompt(List.of(message));
return chatClient.prompt(prompt)
.stream()
.content();
}
@GetMapping("/SystemPromptTemplate")
public Flux<String> SystemPromptTemplate() {
// 2、系统提示词
PromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题");
Message sysMessage = systemPromptTemplate.createMessage(Map.of("name", "小宇", "voice", "御姐" ));
PromptTemplate userPromptTemplate = new PromptTemplate("请回答问题:{question}");
Message userMessage = userPromptTemplate.createMessage(Map.of("question", "平凡的人指的被爱吗?"));
// 组装prompt
Prompt prompt = new Prompt(List.of(sysMessage, userMessage));
return chatClient.prompt(prompt)
.stream()
.content();
}
@GetMapping("/SystemPromptTemplate2")
public Flux<String> SystemPromptTemplate2() {
// 另一种写法请求
PromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题");
Prompt sysPrompt = systemPromptTemplate.create(Map.of("name", "小宇", "voice", "御姐"));
PromptTemplate userPromptTemplate = new PromptTemplate("请回答问题:{question}");
Prompt userPrompt = userPromptTemplate.create(Map.of("question", "平凡的人指的被爱吗?"));
return chatClient.prompt(sysPrompt)
.stream()
.content();
}
}
2.5 Prompt Template
①使用原因 假设提示词为:你是一个有用的人工智能助手,名字是小白请用幽默的风格回答以下问题:推荐上海的三个景点 我们希望助手的名字,风格,还需要回答的问题都是可变的。如果我们没有没有PromptTemplate的情 况下可以需要写成这样的:
java
String name = "小王";
String voice = "幽默";
String userQuestion = "推荐上海的三个景点";
// 硬编码拼接提示词
String promptText = "你是一个有用的人工智能助手,名字是" + name + ",请用" + voice + "的
风格回答以下问题:" + userQuestion;
存在几个明显问题:
- **代码臃肿:**每次需要改变提示词结构或角色设定时,都必须修改代码并重新部署。
- **难以维护:**如果提示词逻辑变得复杂(例如需要加入系统指令、上下文历史等),字符串拼接会变 得非常混乱且容易出错。
- **缺乏复用性:**相同的提示词结构无法轻松应用于其他类似场景。
使用PromptTemplate就可以解决这些问题。
②是什么 在 Spring AI 的开发过程中,Prompt Template(提示词模板) 的核心价值在于它能够将静态的提示 词结构与动态的业务数据分离,从而提升代码的可维护性和复用性。
当你的应用需要与大型语言模型 (LLM)交互,且交互内容会根据用户输入或业务状态发生变化时,就是使用 Prompt Template 的典型 场景。
③如何使用
渲染用户提示词
java
@GetMapping("/PromptTemplate")
public Flux<String> PromptTemplate() {
// 1、用户提示词
PromptTemplate promptTemplate = new PromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题:{question}");
Message message = promptTemplate.createMessage(
Map.of("name", "小宇", "voice", "御姐", "question", "平凡的人指的被爱吗?"));
Prompt prompt = new Prompt(List.of(message));
return chatClient.prompt(prompt)
.stream()
@GetMapping("/SystemPromptTemplate")
public Flux<String> SystemPromptTemplate() {
// 2、系统提示词
PromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题");
Message sysMessage = systemPromptTemplate.createMessage(Map.of("name", "小宇", "voice", "御姐" ));
PromptTemplate userPromptTemplate = new PromptTemplate("请回答问题:{question}");
Message userMessage = userPromptTemplate.createMessage(Map.of("question", "平凡的人指的被爱吗?"));
// 组装prompt
Prompt prompt = new Prompt(List.of(sysMessage, userMessage));
return chatClient.prompt(prompt)
.stream()
.content();
}
@GetMapping("/SystemPromptTemplate2")
public Flux<String> SystemPromptTemplate2() {
// 另一种写法请求
PromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是一个有用的人工智能,名字为{name}请用{voice}的风格回答问题");
Prompt sysPrompt = systemPromptTemplate.create(Map.of("name", "小宇", "voice", "御姐"));
PromptTemplate userPromptTemplate = new PromptTemplate("请回答问题:{question}");
Prompt userPrompt = userPromptTemplate.create(Map.of("question", "平凡的人指的被爱吗?"));
return chatClient.prompt(sysPrompt)
.stream()
.content();
}
3、RAG
3.1 概述
我们去问AI一些问题的时候,如果这个知识是模型在训练时没有涉及到的,这个时候AI就会出现幻觉(看似合理但实则错误或虚构的信息)。
所以我们想要避免AI出现幻觉就需要让AI知道相关的知识。
检索增强生成(RAG)技术其核心价值在于,它以一种成本效益高且灵活的方式,为LLM连接了一个可实时更新的"外部知识库",从而显著提升了AI应用的可靠性、时效性和专业性。
3.2 什么是RAG
RAG(Retrieval-Augmented Generation) 检索增强生成。通过外部数据库检索出相关的知识,然后把问题和相关知识一起给到大模型来让模型生成回答。
3.3 RAG流程

3.4. 相关概念
| 概念 | 解释 |
|---|---|
| 向量 | 向量就像是一个有序的数字列表,或者说是多维空间中的一个点、一条有方向的线段。这些数字共同描述了对象在某一个"特征空间"中的位置。 |
| 为什么要把文本转换成向量 | 计算机无法直接理解人类语言,但极其擅长处理数字。一旦文本变成了向量(即高维空间中的点),我们就可以运用强大的数学工具来处理它们,计算相似度。(比如余弦相似度算法) |
| Embedding 嵌入 | 把文本->多维向量的过程叫做Embedding 嵌入。 |
| EmbeddingMode嵌入模型 | 嵌入模型可以把文本转换成多维向量 |
| 向量数据库 | 能够存储向量并且具有向量相似度检索相关能力的数据库 |
| 向量维度 | 向量的维度就是这个列表里数字的个数。例如,向量 [0.12, -0.87, 0.33,1.24]的维度是4; 较高维度通常能提供更丰富的语义信息和更强的区分能力,尤其利于处理复杂语义或需要高精度的场景。但这会增加计算量,占用更多内存和存储,也可能使计算速度变慢 |
地址(智谱)docs.bigmodel.cn/api-referen...
准备向量数据库
- 1、使用redis-stack作为向量数据库。
- 2、提供一个接口,接口被调用的时候可以把文本向量化然后存入向量数据库
- 3、另外再提供一个接口,这个接口能基于问题去向量数据库的中进行相似度搜索,查询出相似度较高的几个数据。
1、向量数据库安装 使用docker部署redis-stack【好像windows不能直接安装redis-stack???】
bash
docker run -d --name redis-stack
-p 6379:6379
-p 8001:8001 redis/redis-stack:latest
2、创建项目并添加如下依赖,主要是redis作为向量数据库的依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
3、添加相关配置
yaml
# 服务器配置
server:
port: 8080 # 应用服务监听端口,默认8080
spring:
# 应用基本信息配置
application:
name: sangeng-redis-rag # 应用名称
data:
redis:
host: 127.0.0.1
port: 6379
# Spring AI 配置
ai:
# 智谱AI大模型配置
zhipuai:
api-key: ZHIPU_KEY # 智谱API密钥
chat:
options:
model: glm-4.6 # 使用的聊天模型名称(GLM-4.6)
embedding:
options:
model: embedding-3 # 使用的嵌入模型名称(embedding-3)
dimensions: 256 # 嵌入向量的维度(256维)
# 向量存储配置
vectorstore:
redis:
initialize-schema: true # 启动时自动创建Redis向量索引结构(首次部署需开启)
prefix: sangeng_rag_prefix # Redis键名前缀,用于区分不同应用的向量数据
index: sangeng_rag_index # Redis向量索引名称
4、代码编写
conrtoller编写【支持导入和检索】
java
package com.sangeng.controller;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
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 java.util.List;
@RestController
@RequestMapping("/rag")
public class RagController {
// springAI提供的向量数据库操作方法
private final VectorStore vectorStore;
public RagController(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
/* 插入数据 */
@GetMapping("/importData")
public String importData(@RequestParam("data") String data) {
Document document = Document.builder()
.text(data)
.build();
vectorStore.add(List.of(document));
return "success";
}
/* 检索数据 */
@GetMapping("/search")
public List<Document> search(@RequestParam("query") String query) {
SearchRequest searchRequest = SearchRequest.builder()
.query(query)
.topK(3) //搜索后返回前几个
.similarityThreshold(0) // 阈值,低于该阈值的不返回
.build();
List<Document> documents = vectorStore.similaritySearch(searchRequest);
return documents;
}
}
3.5.咖啡店智能客服RAG实战
目标: 使用RetrievalAugmentationAdvisor把常见咖啡店相关的QA数据存入向量数据库。让用户问智能客服相 关问题的时候能够回答出来。 思路:
- 1、先把csv文件中每一个QA作为一个Document存入向量数据库
- 2、用户提问的时候先基于用户的问题去向量数据库中检索相关的知识。然后把让AI能够基于这些知识去回答。
代码编写 添加依赖
xml
<!-- 解析csv文件的依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.10.0</version>
</dependency>
<!-- 解析RAG依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-rag</artifactId>
</dependency>
contoller编写
java
package com.sangeng.controller;
import com.sangeng.tool.TimeTools;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.ClassPathResource;
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 java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/coffee")
public class CoffeeController {
private final VectorStore vectorStore;
private final ChatClient chatClient;
public CoffeeController(VectorStore vectorStore, ChatClient.Builder chatClientBuilder) {
this.vectorStore = vectorStore;
// 设置检索增强的advisor
// VectorStoreDocumentRetriever vectorStoreDocumentRetriever =
// new VectorStoreDocumentRetriever(vectorStore, 0.5, 2,null);
VectorStoreDocumentRetriever vectorStoreDocumentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.topK(2)
.similarityThreshold(0.5)
.build();
RetrievalAugmentationAdvisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(vectorStoreDocumentRetriever)
.build();
this.chatClient = chatClientBuilder
.defaultAdvisors(retrievalAugmentationAdvisor)
.defaultTools(new TimeTools()) //工具
.build();
}
/* 导入csv数据到向量数据库 */
@GetMapping("/importCsvData")
public String importCsvData() {
try {
// 读取classpath下的QA.csv文件
ClassPathResource resource = new ClassPathResource("QA.csv");
InputStreamReader reader = new InputStreamReader(resource.getInputStream());
// 使用Apache Commons CSV解析CSV文件
CSVParser csvParser = CSVFormat.DEFAULT
.builder()
.setHeader() // 第一行作为标题
.setSkipHeaderRecord(true) // 跳过标题行
.build()
.parse(reader);
List<Document> documents = new ArrayList<>();
// 遍历每一行记录
for (CSVRecord record : csvParser) {
// 获取问题和回答字段
String question = record.get("问题");
String answer = record.get("回答");
// 将问题和回答组合成文档内容
String content = "问题: " + question + "\n回答: " + answer;
// 创建Document对象
Document document = new Document(content);
// 添加到文档列表
documents.add(document);
}
// 关闭解析器
csvParser.close();
// 将文档存入向量数据库
vectorStore.add(documents);
return "成功导入 " + documents.size() + " 条记录到向量数据库";
} catch (IOException e) {
e.printStackTrace();
return "导入失败" + e.getMessage();
}
}
/* RAG回答接口,明确展示查询向量数据库的过程 */
@GetMapping("/rag-ask")
public String ragAskQuestion(@RequestParam("question") String question) {
// 先从向量数据库检索相关信息
// 这里会使用RetrievalAugmentationAdvisor自动检索相关文档
// 将问题和检索到的上下文一同发给ai模型生成回答
String content = chatClient.prompt()
.system("你是咖啡店的服务员,你需要回答用户的问题")
.user(question)
.call().content();
return content;
}
}
4、Tool Calling
1、概述 如果我们现在问我们前面的智能客服说:我现在下单一杯美式几点几分可以做好 ? 你会发现他没办法很好的回答问题。因为模型能力是有限的(比如不知道当前时间),如果想要让AI应用具备更强的功能,就可以使用tool calling。相当于是给模型提供一些工具,当模型判断需要使用工具的时候就会进行使用。 2、tool calling调用流程

- 其实就是把工具的name,description,input schema等信息也放入模型的请求中。
- 模型如果觉得需要调用工具就会返回相关的工具调用消息,这个时候SpringAI会基于这个消息去调用工具,得到一个工具调用的结果给到模型。
- 模型再综合这个调用结果返回最终的答案。
3、代码编写
定义一个工具类
java
package com.sangeng.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TimeTools {
@Tool(description = "通过时区id获取当前时间")
public String getTimeByZoneId(@ToolParam(description = "时区id,比如 Asia/ShangHai") String zoneId) {
ZoneId zone = ZoneId.of(zoneId);
ZonedDateTime now = ZonedDateTime.now(zone);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
传入工具调用
java
@GetMapping("/ask")
public String askQuestion(@RequestParam("question") String question) {
return chatClient.prompt()
.user(question)
.tools(new TimeTools())
.call()
.content();
}
逻辑探究

通过@Tool 和 @ToolParam的描述 ai生成变量传入后调用?