Spring AI Alibaba 10分钟快速入门
Spring AI Alibaba 快速入门指南
本文案例源码笔记见:https://gitee.com/zhengqingya/java-workspace
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: 888servlet:encoding:enabled: truecharset: utf-8force: truespring:ai:dashscope:api-key: ${AI_DASHSCOPE_API_KEY}# Ollama配置(可选)ollama:base-url: http://localhost:11434chat: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格式内容""";@Overridepublic 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();}@Overridepublic ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {return chatClientResponse;}@Overridepublic 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.Driverurl: jdbc:mysql://localhost:3306/spring-ai-alibaba-demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: rootpassword: root
代码示例
@RestController
@RequestMapping("/chat-memory")
@Tag(name = "对话记忆")
public class ChatMemoryController {private final ChatClient chatClient;// 注入 JdbcTemplate, ChatClientpublic ChatMemoryController(JdbcTemplate jdbcTemplate, DashScopeChatModel dashScopeChatModel) {// 构造 ChatMemoryRepository 和 ChatMemoryChatMemoryRepository 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;@Autowiredprivate 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;@Autowiredprivate 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: offai:mcp:server:stdio: truename: my-weather-serverversion: 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: 10088spring:main:banner-mode: offai:mcp:server:name: my-sse-serverversion: 1.0.0type: ASYNCsse-endpoint: /ssesse-message-endpoint: /mcpcapabilities:tool: trueresource: trueprompt: truecompletion: 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-v4dimensions: 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: truelog-prompt: true# Chat config itemschat:client:observations:log-prompt: truelog-completion: trueinclude-error-logging: true# tools config itemstools:observability:include-content: truehttp:client:read-timeout: 60smanagement:endpoints:web:exposure:include: "*"endpoint:health:show-details: alwaystracing:sampling:probability: 1.0zipkin: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();}@Overridepublic 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. 将结果放入MapMap<String, Object> resultMap = new HashMap<>();resultMap.put("expander_content", queryVariants);return resultMap;}
}
配置状态图
@Configuration
public class SimpleGraphConfiguration {@SneakyThrows@Beanpublic 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,并编译成CompiledGraphpublic 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());}
}
学习资源
技术选型
- Spring AI Alibaba
- LangChain4j
教学视频
| 已阅 | 来源 |
|---|---|
| √(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哦!
