Spring AI Alibaba 10分钟快速入门
Spring AI Alibaba 快速入门:1. 环境准备(API Key配置、Ollama安装)2. 项目依赖配置3. 基础使用(简单对话、ChatClient使用)4. 提示词使用5. Advisor对话拦截6. 对话记忆7. 结构化输出8. 工具调用9. MCP协议10. RAG技术11. AI Agent12. 可观测性13. Spring AI Alibaba Graph14. 学习资
Spring AI Alibaba 快速入门指南
1. 环境准备
1.1 API Key 配置
访问 阿里云百炼平台 获取API Key,并在Windows环境变量中配置:
AI_DASHSCOPE_API_KEY=your_api_key_here
配置完成后重启电脑使环境变量生效,可通过命令行验证:
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 - 自定义安装路径:
OllamaSetup.exe /DIR="D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama" - 验证安装:
ollama --version
常用Ollama命令:
# 启动 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依赖
<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 配置
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 简单对话示例
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是更高层次的抽象,提供了简化的聊天接口和流式处理能力。
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 多模型动态配置
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 四大角色
public enum MessageType {
USER("user"), // 用户发起的消息或请求
ASSISTANT("assistant"), // AI助手生成的回复或响应
SYSTEM("system"), // 系统级别的指令或信息
TOOL("tool"); // 工具调用产生的消息
}
4.2 提示词模板
@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 日志记录
@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 敏感词拦截
@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 自定义拦截器
/**
* <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存储示例
依赖配置
<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>
配置文件
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
代码示例
@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. 结构化输出
将模型的输出转换为特定的数据结构。
// 定义数据结构
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 定义工具
方法工具
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();
}
}
函数工具
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 注册工具 & 调用
@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 动态注册工具
@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
服务端实现
// 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();
}
客户端接入
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:/mcp/mcp-servers-config.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
服务端实现
// 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
客户端接入
spring:
ai:
mcp:
client:
sse:
connections:
my-sse-server:
url: http://localhost:10088
10. RAG(Retrieval-Augmented Generation)
RAG是一种结合检索和生成的技术架构,通过引入"外接知识库"让模型在回答问题前先查阅权威资料。
10.1 嵌入模型 (Embedding Model)
spring:
ai:
dashscope:
embedding:
options:
model: text-embedding-v4
dimensions: 64
@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轻量级向量数据库:
@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)
文本读取器
@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读取器
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>
@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应用中关键的文档预处理工具。
/**
* 文本读取器
* 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 自定义文档分割器
/**
* 学校规章制度专用文档分割器 - 针对规章制度结构优化
* 支持按"第.*条"分割,保留规章制度的结构完整性
*/
public class RegulationDocumentSplitter extends TextSplitter {
// 实现细节...
public static Builder builder() {
return new Builder();
}
// ... 其他方法实现
}
10.6 文档转换器
关键字提取
@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;
}
摘要生成
@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;
}
元数据过滤
@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 检索增强生成
@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 重排序
@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 幻觉评估
@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项目配置
依赖配置
<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>
配置文件
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 快速入门
依赖配置
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
创建自定义节点
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;
}
}
配置状态图
@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;
}
}
编译并运行工作流
@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哦!
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐



所有评论(0)