Spring AI Alibaba 快速入门指南
1. 环境准备
1.1 API Key 配置
访问 阿里云百炼平台 获取API Key,并在Windows环境变量中配置:
AI_DASHSCOPE_API_KEY=your_api_key_here
配置完成后重启电脑使环境变量生效,可通过命令行验证:
shell
echo %AI_DASHSCOPE_API_KEY%
1.2 Ollama本地模型安装(可选)
Ollama是一个用于在本地运行大型语言模型(LLM)的工具和平台。
Windows安装步骤:
-
下载安装包:https://github.com/ollama/ollama/releases/download/v0.12.3/OllamaSetup.exe
-
配置环境变量(可选):
OLLAMA_MODELS=D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama\models -
自定义安装路径:
shellOllamaSetup.exe /DIR="D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama" -
验证安装:
shellollama --version
常用Ollama命令:
shell
# 启动 Ollama 服务(默认端口:11434)
ollama serve
# 列出已下载的模型
ollama list
# 下载指定模型
ollama pull <model_name>
# 运行指定模型
ollama run <model_name>
# 停止运行中的模型
ollama stop <model_name>
# 删除已下载的模型
ollama rm <model_name>
# 查看当前运行的模型实例
ollama ps
2. 项目依赖配置
2.1 Maven依赖
xml
<properties>
<!-- Spring Boot -->
<spring-boot.version>3.4.0</spring-boot.version>
<!-- Spring AI -->
<spring-ai.version>1.0.0</spring-ai.version>
<!-- Spring AI Alibaba -->
<spring-ai-alibaba.version>1.0.0.3</spring-ai-alibaba.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>
<!-- 核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Ollama本地模型支持(可选) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.0.0</version>
</dependency>
2.2 基础配置
application.yml 配置
yaml
server:
port: 888
servlet:
encoding:
enabled: true
charset: utf-8
force: true
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
# Ollama配置(可选)
ollama:
base-url: http://localhost:11434
chat:
model: deepseek-r1 # qwen3:8b | deepseek-r1
3. 基础使用
3.1 简单对话示例
java
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.model.ChatModel;
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;
@RequiredArgsConstructor
@RestController
public class HelloWorldController {
private final ChatModel chatModel;
/**
* 简单调用
* http://localhost:888/simple/chat?msg=你是谁?
*/
@GetMapping("/simple/chat")
public String simpleChat(@RequestParam String msg) {
return chatModel.call(msg);
}
/**
* 流式调用
* http://localhost:888/stream/chat?msg=你是谁?
*/
@GetMapping("/stream/chat")
public Flux<String> streamChat(@RequestParam String msg) {
return chatModel.stream(msg);
}
}
3.2 ChatClient使用
ChatClient是更高层次的抽象,提供了简化的聊天接口和流式处理能力。
java
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient;
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 ChatClientController {
private final ChatClient chatClient;
public ChatClientController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 简单调用
* http://localhost:888/chat-client/simple/chat?msg=你是谁?
*/
@GetMapping("/chat-client/simple/chat")
public String simpleChat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
/**
* 流式调用
* http://localhost:888/chat-client/stream/chat?msg=你是谁?
*/
@GetMapping("/chat-client/stream/chat")
public Flux<String> streamChat(@RequestParam String msg) {
return chatClient.prompt().user(msg).stream().content();
}
}
3.3 多模型动态配置
java
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
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.ollama.OllamaChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;
@RestController
@RequestMapping("/mutli-platform-model")
@Tag(name = "多模型动态配置")
public class MutliPlatformModelController {
private Map<String, ChatModel> STRATEGY = Maps.newHashMap();
public MutliPlatformModelController(DashScopeChatModel dashScopeChatModel,
OllamaChatModel ollamaChatModel) {
STRATEGY.put("dashscope", dashScopeChatModel);
STRATEGY.put("ollama", ollamaChatModel);
}
/**
* http://localhost:888/mutli-platform-model/stream/chat?platform=dashscope&model=qwen3-max&temperature=0.8&msg=你是谁?
* http://localhost:888/mutli-platform-model/stream/chat?platform=ollama&model=deepseek-r1&temperature=0.8&msg=你是谁?
*/
@Operation(summary = "动态切换模型")
@GetMapping("/stream/chat")
public Flux<String> streamChat(@ModelAttribute MutliPlatformModelOptions options) {
ChatModel chatModel = STRATEGY.get(options.platform());
ChatClient chatClient = ChatClient.builder(chatModel)
// 模型配置参数
.defaultOptions(
ChatOptions.builder()
.model(options.model())
.temperature(options.temperature())
.build()
).build();
return chatClient.prompt().user(options.msg()).stream().content();
}
}
4. 提示词(Prompt)使用
4.1 四大角色
java
public enum MessageType {
USER("user"), // 用户发起的消息或请求
ASSISTANT("assistant"), // AI助手生成的回复或响应
SYSTEM("system"), // 系统级别的指令或信息
TOOL("tool"); // 工具调用产生的消息
}
4.2 提示词模板
java
@RestController
@RequestMapping("/prompt")
@Tag(name = "提示词")
public class PromptController {
private ChatClient chatClient;
private ChatModel chatModel;
@Value("classpath:/prompt-template/tell-joke.txt")
private org.springframework.core.io.Resource promptTemplateRes;
public PromptController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
chatModel = dashScopeChatModel;
}
private static final String DEFAULT_PROMPT = """
你是一位资深Java架构师,专注于企业级Java后端开发。
请严格按照以下规则回答:
1. 只回答Java及相关技术栈的问题;
2. 提供准确、专业的技术解答;
3. 对于非Java后端相关问题,请礼貌说明超出了专业范围。
""";
/**
* http://localhost:888/prompt/chat-client?msg=今天吃什么?
*/
@GetMapping("/chat-client")
public String chatClient(@RequestParam String msg) {
return chatClient.prompt()
.system(DEFAULT_PROMPT)
.user(msg)
.call().content();
}
/**
* 提示词模板使用
* http://localhost:888/prompt/prompt-template?topic=无聊
*/
@GetMapping("/prompt-template")
public Flux<String> promptTemplate(@RequestParam String topic) {
// 创建提示词模板
PromptTemplate promptTemplate = new PromptTemplate("给我讲一个有关于{topic}的笑话");
// 创建 Prompt 实例
Prompt prompt = promptTemplate.create(Map.of("topic", topic));
// 流式返回结果
return chatModel.stream(prompt).map(chatResponse -> chatResponse.getResult().getOutput().getText());
}
}
5. Advisor对话拦截
Advisor主要用于拦截ChatClient的对话请求和响应,在对话过程中添加通用处理逻辑。
5.1 日志记录
java
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor对话拦截--日志记录")
public class AdvisorLogController {
private ChatClient chatClient;
public AdvisorLogController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
/**
* http://localhost:888/advisor/log?msg=你好
*/
@GetMapping("/log")
public Flux<String> log(@RequestParam String msg) {
return chatClient.prompt()
.user(msg)
.stream().content();
}
}
5.2 敏感词拦截
java
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor对话拦截--敏感词拦截")
public class AdvisorSafeGuardController {
private ChatClient chatClient;
public AdvisorSafeGuardController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
new SafeGuardAdvisor(
Lists.newArrayList("敏感词", "WC"),
"由于包含敏感内容,我无法对此进行回复。我们可以重新表述或讨论其他话题吗?",
1)
)
.build();
}
/**
* http://localhost:888/advisor/safe-guard?msg=你好
* http://localhost:888/advisor/safe-guard?msg=WC
*/
@GetMapping("/safe-guard")
public Flux<String> safeGuard(@RequestParam String msg) {
return chatClient.prompt()
.user(msg)
.stream().content();
}
}
5.3 自定义拦截器
java
/**
* <p> 自定义Advisor拦截器 -- 实现重写提示词 </p>
*/
public class RewritePromptAdvisor implements BaseAdvisor {
private static final String REWRITE_TEXT = """
让我们仔细分析这个问题:
问题: {input_msg}
分析步骤:
1. 问题理解: 这个问题在问什么?
2. 关键信息: 有哪些重要的细节需要注意?
3. 回答构建: 基于以上分析,给出准确详细的回答:
回答要求:
- 使用通俗易懂的语言
- 结合实际情况举例说明
- 如涉及技术内容,提供具体的操作步骤
- 返回html格式内容
""";
@Override
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
String contents = chatClientRequest.prompt().getContents();
String inputMsg = PromptTemplate.builder().template(REWRITE_TEXT).build()
.render(Map.of("input_msg", contents));
return chatClientRequest.mutate()
.prompt(Prompt.builder().content(inputMsg).build())
.build();
}
@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
return chatClientResponse;
}
@Override
public int getOrder() {
return 0;
}
}
6. 对话记忆(Chat Memory)
用于维护对话上下文状态。
6.1 MySQL存储示例
依赖配置
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
<version>1.0.0.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
配置文件
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-ai-alibaba-demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: root
代码示例
java
@RestController
@RequestMapping("/chat-memory")
@Tag(name = "对话记忆")
public class ChatMemoryController {
private final ChatClient chatClient;
// 注入 JdbcTemplate, ChatClient
public ChatMemoryController(JdbcTemplate jdbcTemplate, DashScopeChatModel dashScopeChatModel) {
// 构造 ChatMemoryRepository 和 ChatMemory
ChatMemoryRepository chatMemoryRepository = MysqlChatMemoryRepository.mysqlBuilder()
.jdbcTemplate(jdbcTemplate)
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10) // 消息存储条数
.build();
this.chatClient = ChatClient.builder(dashScopeChatModel)
// 增加聊天记忆能力
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
// 实现 Logger 的 Advisor
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
/**
* 使用自定义的 Advisor 增加聊天记忆能力
* eg:
* http://127.0.0.1:888/chat-memory/chat/123?msg=你好,我叫郑清,之后的会话中都带上我的名字
* http://127.0.0.1:888/chat-memory/chat/123?msg=我叫什么名字?
* http://127.0.0.1:888/chat-memory/chat/111?msg=我叫什么名字?
*/
@GetMapping("/chat/{id}")
public Flux<String> advisorChat(HttpServletResponse response, @PathVariable String id, @RequestParam String msg) {
response.setCharacterEncoding("UTF-8");
return this.chatClient.prompt(msg)
.advisors(
a -> a
.param(ChatMemory.CONVERSATION_ID, id) // 多用户记忆隔离
).stream().content();
}
}
7. 结构化输出
将模型的输出转换为特定的数据结构。
java
// 定义数据结构
public record StructuredVO(String category, String question, String answer) {
}
@RestController
@RequestMapping("/structured-output")
@Tag(name = "结构化输出")
public class StructuredOutputController {
private ChatClient chatClient;
public StructuredOutputController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* http://localhost:888/structured-output/test
* {"category":"售后服务","question":"这件商品的质量不好,我可以申请退货吗?","answer":"如果商品存在质量问题,通常可以申请退货。请查看商家的退换货政策或联系客服确认具体流程。"}
*/
@GetMapping("/test")
public StructuredVO structuredOutput() {
return chatClient.prompt()
.system("""
对问题分类并简要总结,并给出答案
""")
.user("这件商品的质量不好,我可以申请退货吗?")
.call().entity(StructuredVO.class);
}
}
8. 工具调用(Tool Calling)
允许AI模型调用外部工具和函数来执行特定任务。
8.1 定义工具
方法工具
java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Service
public class TimeTools {
@Tool(description = "获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
public String getCurrentTime(@ToolParam(description = "时间格式") String format) {
log.info("获取当前时间格式:{}", format);
return DateUtil.now();
}
}
函数工具
java
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.function.Function;
@Slf4j
@Service(value = "getWeather")
public class WeatherFunction implements Function<String, String> {
@Override
@Description("获取天气")
public String apply(@JsonPropertyDescription("城市") String city) {
log.info("获取{}天气", city);
return city + "天气:晴";
}
}
8.2 注册工具 & 调用
java
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用")
public class ToolCallingController {
private ChatClient chatClient;
@Autowired
private TimeTools timeTools;
public ToolCallingController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 法1:方法工具
* http://localhost:888/tool-calling/time?msg=现在时间?
*/
@GetMapping("/time")
public Flux<String> time(@RequestParam String msg) {
return chatClient.prompt().user(msg)
.tools(timeTools)
.stream().content();
}
/**
* 法2:函数工具
* http://localhost:888/tool-calling/weather?msg=成都天气?
*/
@GetMapping("/weather")
public Flux<String> weather(@RequestParam String msg) {
return chatClient.prompt().user(msg)
.toolNames(new String[]{"getWeather"})
.stream().content();
}
}
8.3 动态注册工具
java
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用-动态注册")
public class ToolCallingDynamicController {
private ChatClient chatClient;
@Autowired
private TimeTools timeTools;
public ToolCallingDynamicController(DashScopeChatModel dashScopeChatModel,
TimeTools timeTools) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 法一:方法工具
* http://localhost:888/tool-calling/dynamic/1?msg=现在时间?
* http://localhost:888/tool-calling/dynamic/2?msg=现在时间?
*/
@GetMapping("/dynamic/{userId}")
public Flux<String> dynamic(@PathVariable Long userId, @RequestParam String msg) {
return chatClient.prompt().user(msg)
// 动态注册工具
.toolCallbacks(getToolCallbacks(userId))
.stream().content();
}
private List<ToolCallback> getToolCallbacks(Long userId) {
List<ToolCallback> toolCallbacks = Lists.newArrayList();
if (userId != 1) {
return toolCallbacks;
}
Method method = ReflectionUtils.findMethod(TimeTools.class, "getCurrentTime", String.class);
if (method == null) {
throw new RuntimeException("Method not found");
}
String inputSchema = JsonSchemaGenerator.generateForMethodInput(method);
ToolCallback timeToolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.builder()
.name("getCurrentTime")
.description("获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
.inputSchema(inputSchema)
.build())
.toolMethod(method)
.toolObject(timeTools)
.build();
toolCallbacks.add(timeToolCallback);
return toolCallbacks;
}
/**
* 法二:函数工具
* http://localhost:888/tool-calling/dynamic2/1?msg=成都天气?
* http://localhost:888/tool-calling/dynamic2/2?msg=成都天气?
*/
@GetMapping("/dynamic2/{userId}")
public Flux<String> dynamic2(@PathVariable Long userId, @RequestParam String msg) {
String[] toolNames = userId == 1 ? new String[]{"getWeather"} : new String[0];
return chatClient.prompt().user(msg)
// 动态注册工具
.toolNames(toolNames)
.stream().content();
}
}
9. MCP(Model Context Protocol)
MCP是一种模型上下文协议,主要用于AI模型调用外部工具和函数。
9.1 传输方式
- STDIO(标准输入输出):通过标准输入输出流进行数据传输
- SSE(Server-Sent Events):基于HTTP的单向服务器推送技术
- Streamable HTTP:基于HTTP协议的流式传输
9.2 自定义MCP服务 - STDIO
服务端实现
java
// pom.xml依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
// application.yml配置
spring:
main:
web-application-type: none # 设置为非 Web 应用类型
banner-mode: off
ai:
mcp:
server:
stdio: true
name: my-weather-server
version: 1.0.0
// 实现MCP工具
@Service
public class MyWeatherService {
@Tool(description = "获取天气")
public String getWeather(@ToolParam(description = "城市") String city) {
return city + "天气:雨";
}
}
// 注册MCP工具
@Bean
public ToolCallbackProvider weatherTools(MyWeatherService myWeatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(myWeatherService)
.build();
}
客户端接入
yaml
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:/mcp/mcp-servers-config.json
json
{
"mcpServers": {
"my-weather-server": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=",
"-jar",
"D:\\path\\to\\your\\mcp-server.jar"
]
}
}
}
9.3 自定义MCP服务 - SSE
服务端实现
java
// application.yml配置
server:
port: 10088
spring:
main:
banner-mode: off
ai:
mcp:
server:
name: my-sse-server
version: 1.0.0
type: ASYNC
sse-endpoint: /sse
sse-message-endpoint: /mcp
capabilities:
tool: true
resource: true
prompt: true
completion: true
客户端接入
yaml
spring:
ai:
mcp:
client:
sse:
connections:
my-sse-server:
url: http://localhost:10088
10. RAG(Retrieval-Augmented Generation)
RAG是一种结合检索和生成的技术架构,通过引入"外接知识库"让模型在回答问题前先查阅权威资料。
10.1 嵌入模型 (Embedding Model)
yaml
spring:
ai:
dashscope:
embedding:
options:
model: text-embedding-v4
dimensions: 64
java
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
private EmbeddingModel embeddingModel;
public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
embeddingModel = dashScopeEmbeddingModel;
}
/**
* http://localhost:888/rag/embedding?msg=你好
*/
@GetMapping("/embedding")
public Object embedding(@RequestParam String msg) {
EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(msg));
return Map.of("embedding", embeddingResponse);
}
}
10.2 向量数据库
使用SimpleVectorStore轻量级向量数据库:
java
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
private VectorStore vectorStore;
public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
}
/**
* http://localhost:888/rag/vectorStore?topK=3&threshold=0.1&msg=有哪些开源的向量数据库?
*/
@GetMapping("/vectorStore")
public Object vectorStore(@RequestParam String msg, @RequestParam int topK, @RequestParam double threshold) {
// 存储向量
vectorStore.add(Lists.newArrayList(
Document.builder().text("LangChain是一个用于开发由语言模型驱动的应用程序的框架。").build(),
Document.builder().text("Milvus是一款开源向量数据库,专为大规模向量相似性搜索而设计。").build()
));
// 相似性搜索
return vectorStore.similaritySearch(SearchRequest.builder()
.query(msg)
.topK(topK)
.similarityThreshold(threshold)
.build());
}
}
10.3 ELT (Extract, Load, Transform)
文本读取器
java
@Value("classpath:rag/pet.txt")
private Resource textRes;
/**
* 文本读取器
* http://localhost:888/rag/elt/text
*/
@GetMapping("/text")
public Object text() {
// 1. 创建文本读取器
TextReader textReader = new TextReader(textRes);
// 2. 读取文档内容
List<Document> documents = textReader.read();
// 3. 返回结果
return documents;
}
Markdown读取器
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>
java
@Value("classpath:rag/iphone.md")
private Resource MdRes;
/**
* markdown读取器
* http://localhost:888/rag/elt/markdown
*/
@GetMapping("/markdown")
public Object markdown() {
// 1. 创建Markdown文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes, MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.withHorizontalRuleCreateDocument(false)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.build());
// 2. 读取Markdown文档内容
List<Document> documents = markdownReader.read();
// 3. 返回文档列表
return documents;
}
10.4 文档分割器
TokenTextSplitter是RAG应用中关键的文档预处理工具。
java
/**
* 文本读取器
* http://localhost:888/rag/elt/split/text
*/
@GetMapping("/text")
public Object text() {
// 1. 创建文本读取器
TextReader textReader = new TextReader(textRes);
// 2. 读取文档内容
List<Document> documents = textReader.read();
// 3. 文档分割处理
List<Document> splitDocs = new TokenTextSplitter(800, 350, 5, 10000, true).split(documents);
// 4. 返回结果
return splitDocs;
}
10.5 自定义文档分割器
java
/**
* 学校规章制度专用文档分割器 - 针对规章制度结构优化
* 支持按"第.*条"分割,保留规章制度的结构完整性
*/
public class RegulationDocumentSplitter extends TextSplitter {
// 实现细节...
public static Builder builder() {
return new Builder();
}
// ... 其他方法实现
}
10.6 文档转换器
关键字提取
java
@GetMapping("/keyword")
public Object keyword() {
// 1. 创建Markdown文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes, MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 关键字提取
KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
List<Document> enricherDocs = enricher.apply(documents);
return enricherDocs;
}
摘要生成
java
@GetMapping("/summary")
public Object summary() {
// 1. 创建Markdown文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes, MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 摘要生成
SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel,
List.of(SummaryMetadataEnricher.SummaryType.CURRENT, SummaryMetadataEnricher.SummaryType.NEXT),
"""
请为以下文档内容生成摘要:
{context_str}
要求:
1. 摘要长度不超过100字
2. 突出关键信息
3. 保持原意不变
""",
MetadataMode.ALL);
List<Document> enricherDocs = enricher.apply(documents);
return enricherDocs;
}
元数据过滤
java
@GetMapping("/metadata-filter")
public Object metadataFilter() {
// 1. 创建Markdown文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes, MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 关键字提取
KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
List<Document> enricherDocs = enricher.apply(documents);
// 4. 存储向量
vectorStore.add(enricherDocs);
// 5. 相似性搜索
List<Document> result = vectorStore.similaritySearch(SearchRequest.builder()
.query("手机处理器")
.topK(3)
// 基于元数据的过滤条件
.filterExpression(new FilterExpressionBuilder().eq("filename", "phone.md").build())
.build());
// 6. 返回结果
return result;
}
10.7 检索增强生成
java
@RestController
@RequestMapping("/rag/retrieval-augmentation")
@Tag(name = "RAG-检索增强生成")
public class RagRetrievalAugmentationAdvisorController {
@Value("classpath:rag/iphone.md")
private Resource MdRes;
private ChatModel chatModel;
private ChatClient chatClient;
private VectorStore vectorStore;
public RagRetrievalAugmentationAdvisorController(DashScopeChatModel dashScopeChatModel,
DashScopeEmbeddingModel dashScopeEmbeddingModel) {
chatModel = dashScopeChatModel;
chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
}
/**
* RetrievalAugmentationAdvisor 检索增强生成
* http://localhost:888/rag/retrieval-augmentation/chat?msg=下雨天,我不想出门,你能不能帮我了解下手机店里的iphone18价格?
*/
@GetMapping("/chat")
public Flux<String> chat(@RequestParam String msg) {
// 1. 创建Markdown文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes, MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
vectorStore.add(documents);
// 3. 检索增强生成
Advisor advisor = RetrievalAugmentationAdvisor.builder()
// documentRetriever:文档检索器
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.5)
.topK(3)
.build())
// queryAugmenter:查询增强器
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(false)
.emptyContextPromptTemplate(PromptTemplate.builder().template("用户查询位于知识库之外。礼貌地告知用户您无法回答。").build())
.build())
// QueryTransformer:查询转换器
.queryTransformers(
// 查询重写
RewriteQueryTransformer.builder()
.chatClientBuilder(ChatClient.builder(chatModel))
.targetSearchSystem("知识库")
.build(),
// 翻译转换器
TranslationQueryTransformer.builder()
.chatClientBuilder(ChatClient.builder(chatModel))
.targetLanguage("中文")
.build()
)
.build();
return chatClient.prompt().user(msg).advisors(advisor).stream().content();
}
}
10.8 重排序
java
@RestController
@RequestMapping("/rag/rerank")
@Tag(name = "重排序")
public class RerankController {
private RerankModel rerankModel;
private ChatClient chatClient;
private VectorStore vectorStore;
public RerankController(DashScopeChatModel dashScopeChatModel,
DashScopeEmbeddingModel dashScopeEmbeddingModel,
DashScopeRerankModel dashScopeRerankModel) {
chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
rerankModel = dashScopeRerankModel;
}
/**
* http://localhost:888/rag/rerank/chat?msg=iphone20价格
*/
@GetMapping("/chat")
public Flux<String> chat(@RequestParam String msg) {
vectorStore.add(Lists.newArrayList(
Document.builder().text("iPhone18 基础价格: 5999元, 税率: 13%, 运费: 0元").build(),
Document.builder().text("iPhone20 基础价格: 12999元, 税率: 13%, 运费: 50元").build(),
Document.builder().text("iPhone10 基础价格: 1299元, 税率: 13%, 运费: 0元").build()
));
// 重排序
RetrievalRerankAdvisor rerankAdvisor = new RetrievalRerankAdvisor(vectorStore, rerankModel,
SearchRequest.builder().topK(10).build());
return chatClient.prompt().user(msg)
.advisors(rerankAdvisor)
.stream().content();
}
}
10.9 幻觉评估
java
@RestController
@RequestMapping("/rag/fact-check")
@Tag(name = "RAG-幻觉评估")
public class RagFactCheckController {
private ChatModel chatModel;
public RagFactCheckController(DashScopeChatModel dashScopeChatModel) {
chatModel = dashScopeChatModel;
}
@GetMapping("/test")
public Object test() {
// 准备评估数据
ArrayList<Document> documents = Lists.newArrayList(
Document.builder().text("iPhone18 基础价格: 5999元, 税率: 13%, 运费: 0元").build(),
Document.builder().text("iPhone20 基础价格: 12999元, 税率: 13%, 运费: 50元").build(),
Document.builder().text("iPhone10 基础价格: 1299元, 税率: 13%, 运费: 0元").build()
);
// AI回答
String aiRes = "iPhone20 6000元";
// 执行评估
FactCheckingEvaluator evaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));
EvaluationRequest evaluationRequest = new EvaluationRequest(documents, aiRes);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
return evaluationResponse; // {"pass":false,"score":0.0,"feedback":"","metadata":{}}
}
}
11. AI Agent
AI Agent是一种能够自主感知环境、做出决策并执行动作以实现特定目标的智能实体。
11.1 三种人机协同模式
| 模式名称 | 核心关系 (人类 : AI) | 特点与工作方式 | 典型场景 |
|---|---|---|---|
| 嵌入模式 (Embedded) | 我做你看 | 将AI能力作为功能模块嵌入现有产品 | 智能客服中的自动回复、推荐系统 |
| 副驾驶模式 (Copilot) | 我们一起做 | AI作为实时协作的伙伴,提供建议和辅助 | GitHub Copilot代码补全 |
| 智能体模式 (Agent) | 你做我看 | AI拥有高度自主权,可独立规划、决策、执行复杂任务 | 自动交易系统、智能助手 |
11.2 AI Agent的五大工作模式
- 提示链模式:将复杂任务拆解成一系列顺序执行的子任务
- 路由模式:根据输入内容特点,动态选择最合适的处理路径
- 并行化模式:同时启动多个Agent处理独立的子任务
- 协调者-工作者模式:模拟指挥体系,由协调者负责任务分解和调度
- 评估者-优化者模式:引入自我反思和优化的闭环
12. 可观测性
软件的可观测性是指通过系统输出(如日志、指标、跟踪等)来推断其内部状态的能力。
12.1 Zipkin部署
参考:https://gitee.com/zhengqingya/docker-compose
12.2 Java项目配置
依赖配置
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-tool-calling-weather</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>1.5.0-M2</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>3.4.3</version>
</dependency>
配置文件
yaml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
observations:
log-completion: true
log-prompt: true
# Chat config items
chat:
client:
observations:
log-prompt: true
log-completion: true
include-error-logging: true
# tools config items
tools:
observability:
include-content: true
http:
client:
read-timeout: 60s
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
13. Spring AI Alibaba Graph
Spring AI Alibaba Graph是一个强大且直观的框架,能让你像搭积木一样,通过编排节点和流程来构建复杂的AI应用。
13.1 快速入门
依赖配置
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
创建自定义节点
java
public class ExpanderNode implements NodeAction {
private final ChatClient chatClient;
public ExpanderNode(DashScopeChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 1. 从全局状态中获取输入
String query = state.value("query", "");
// 2. 构建提示词并调用大模型
String promptTemplate = "请为以下问题生成3个不同角度的变体问题:{query}";
String result = chatClient.prompt()
.user(u -> u.text(promptTemplate).param("query", query))
.call()
.content();
// 3. 处理结果
List<String> queryVariants = Arrays.asList(result.split("\n"));
// 4. 将结果放入Map
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("expander_content", queryVariants);
return resultMap;
}
}
配置状态图
java
@Configuration
public class SimpleGraphConfiguration {
@SneakyThrows
@Bean
public StateGraph simpleGraph(DashScopeChatModel chatModel) {
// 全局变量的替换策略
KeyStrategyFactory keyStrategyFactory = () -> {
HashMap<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("query", new ReplaceStrategy());
strategies.put("expander_content", new ReplaceStrategy());
return strategies;
};
// 构建状态图
StateGraph stateGraph = new StateGraph("问题扩展工作流", keyStrategyFactory)
// 添加节点
.addNode("expander", AsyncNodeAction.node_async(new ExpanderNode(chatModel)))
// 添加边
.addEdge(StateGraph.START, "expander")
.addEdge("expander", StateGraph.END);
return stateGraph;
}
}
编译并运行工作流
java
@RestController
@RequestMapping("/graph")
@Tag(name = "Graph")
public class SimpleGraphController {
private final CompiledGraph compiledGraph;
// 注入定义好的StateGraph,并编译成CompiledGraph
public SimpleGraphController(@Qualifier("simpleGraph") StateGraph stateGraph) throws GraphStateException {
this.compiledGraph = stateGraph.compile();
}
/**
* http://localhost:888/graph/expand?query=什么是人工智能
*/
@GetMapping("/expand")
public Map<String, Object> expandQuery(@RequestParam String query) throws GraphRunnerException {
// 设置初始状态
Map<String, Object> initialState = Map.of("query", query);
// 执行工作流
Optional<OverAllState> result = compiledGraph.invoke(initialState);
// 从最终状态中获取结果
return result.map(OverAllState::data).orElse(Map.of());
}
}
学习资源
技术选型
教学视频
| 已阅 | 来源 |
|---|---|
| √(1天看完-推荐看) | Spring AI 1.0全套教程(入门+实战+原理&源码;大模型+tools+mcp+Agent全流程落地) |
| √(1h看完-推荐看) | LangChain4j实战教程 |
| √(1天看完) | Spring AI Alibaba入门到进阶实战 |
| √(1h看完) | Coze(扣子)智能体教程 |
| √(1天看完,不建议看,个人觉得讲的太啰嗦) | 尚硅谷Spring AI Alibaba教程,2025最新springai从基础到实战 |
文档资源
tips: 书写本项目案例代码的时候,个人建议根据
Spring AI和Spring AI Alibaba2个官方文档以及官方demo来,避免踩坑~
- Spring AI Alibaba 官方文档
- Spring AI
- ReLE中文大模型能力评测
- MCP Server 工具与插件
- DeepSeek 提示库
- Spring AI 中的 DocumentTransformer 与 RAG 深度解析
- Spring AI 系列博客文章
今日励志语句
每一次代码提交都是进步的足迹,每一行注释都是智慧的积累。编程路上没有捷径,只有不断的学习和实践。遇到bug不要气馁,它们是成长路上的垫脚石。保持好奇心,拥抱变化,让每一行代码都闪耀着创新的光芒。今天也要元气满满地coding哦!