Spring AI Alibaba 学习
学习 大模型调用、图片生成、语音合成、工具调用、云RAG、Agent 的实践
Spring Ai Alibaba Chat
简单的流式大模型问答接口
🏷️模型配置信息
1spring:
2 ai:
3 dashscope:
4 api-key: "API_KEY" # api key
5 chat:
6 options:
7 model: "qwen-plus" # 模型名称: qwen-plus, deepseek-r1
⭐Api 接口代码
1@RestController
2@RequestMapping("/chat-memory")
3public class ChatMemoryController {
4
5 private final ChatClient chatClient;
6
7 public ChatMemoryController(ChatModel chatModel) {
8 this.chatClient = ChatClient.builder(chatModel)
9 .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
10 .defaultAdvisors()
11 .build();
12 }
13
14 /**
15 * 使用 Spring AI 提供的基于内存的 Chat memory方法
16 */
17 @GetMapping("/in-memory")
18 public Flux<String> chatWithMemory(@RequestParam("prompt") String prompt,
19 @RequestParam("chatId") String chatId,
20 HttpServletResponse response) {
21 response.setCharacterEncoding("UTF-8");
22
23 return chatClient.prompt(prompt)
24 .advisors(advisorSpec -> advisorSpec
25 .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) // 上下文记忆ID,隔离不同的聊天上下文
26 .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)) // 检索长度
27 .stream()
28 .content();
29 }
30
31}
多模型切换示例
1@RestController
2@RequestMapping("/multi-model-chat-client")
3public class multiModelChatClientController {
4
5 private final Set<String> modelList = Set.of(
6 "deepseek-r1",
7 "deepseek-v3",
8 "qwen-plus",
9 "qwen-max"
10 );
11
12 private final ChatClient chatClient;
13
14 public multiModelChatClientController(ChatModel chatModel) {
15 this.chatClient = ChatClient.builder(chatModel)
16 .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
17 .build();
18 }
19
20 @GetMapping("/chat")
21 public Flux<String> stream(
22 @RequestParam("prompt") String prompt,
23 @RequestParam("chatId") String chatId,
24 @RequestParam(value = "modelName", required = false) String modelName,
25 HttpServletResponse response
26 ) {
27 response.setCharacterEncoding("UTF-8");
28
29 if (!modelList.contains(modelName)) {
30 return Flux.just("model not exist!");
31 }
32
33 return chatClient.prompt(prompt)
34 .options(ChatOptions.builder() // 这里构建聊天选项,指定不同的模型
35 .withModel(modelName)
36 .build())
37 .advisors(advisorSpec -> advisorSpec // 这里构建顾问参数,指定不同的聊天id和上下文长度
38 .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
39 .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
40 .stream()
41 .content();
42 }
43}
Spring AI Alibaba Image Gen
调用文生图模型示例
1@RestController
2@RequestMapping("/image")
3public class ImageController {
4
5 private final ImageModel imageModel;
6
7 public ImageController(ImageModel imageModel) {
8 this.imageModel = imageModel;
9 }
10
11 @RequestMapping("/t2i/{prompt}")
12 public void image(@PathVariable("prompt") String prompt,
13 HttpServletResponse response) {
14 ImageOptions imageOptions = ImageOptionsBuilder.builder()
15 .model("wanx2.1-t2i-turbo")
16 .build();
17 ImageMessage imageMessage = new ImageMessage(prompt);
18 ImagePrompt imagePrompt = new ImagePrompt(imageMessage, imageOptions);
19 ImageResponse call = imageModel.call(imagePrompt); // 这里会进行阻塞
20 String imageUrl = call.getResult().getOutput().getUrl();
21
22 // 下载图片并写入response中
23 try {
24 URL url = URI.create(imageUrl).toURL();
25 InputStream in = url.openStream();
26
27 response.setHeader("Content-Type", MediaType.IMAGE_PNG_VALUE);
28 response.getOutputStream().write(in.readAllBytes());
29 response.getOutputStream().flush();
30 } catch (IOException e) {
31 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
32 }
33 }
34}
Spring AI Alibaba TTS
调用Text2Speech模型,文本生成语音 - 1.0.0-M5.1版本
1@RestController
2@RequestMapping("/tts")
3public class Text2SpeechController {
4
5 @Value("${spring.ai.dashscope.api-key}")
6 private String APIKEY;
7
8 @RequestMapping("/gen")
9 public String ttsGeneration(@RequestParam("prompt") String prompt,
10 HttpServletResponse response) {
11
12 SpeechSynthesisParam param = SpeechSynthesisParam.builder()
13 .text(prompt)
14 .apiKey(APIKEY)
15 .model("cosyvoice-v1")
16 .build();
17
18 SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer();
19
20 ByteBuffer audioBuffer = speechSynthesizer.call(param);
21
22 // 3. 设置响应头(关键步骤!)
23 response.setContentType("audio/mpeg"); // 根据实际音频格式调整
24 response.setHeader("Content-Disposition", "inline; filename=audio.mp3");
25
26 // 4. 将 ByteBuffer 写入响应流, try-with-resources 自动关闭资源 out 流
27 try (OutputStream out = response.getOutputStream()) {
28 byte[] audioBytes = new byte[audioBuffer.remaining()];
29 audioBuffer.get(audioBytes);
30 out.write(audioBytes);
31 out.flush();
32 }catch (IOException e) {
33 throw new RuntimeException(e);
34 }
35
36 return "finish";
37 }
38
39}
Spring AI Alibaba Function Call
提供工具给大模型调用的示例
1spring:
2 ai:
3 dashscope:
4 api-key: "APIKEY" # api key
5 chat:
6 options:
7 model: "qwen-plus" # 模型名称: qwen-plus, deepseek-r1
8 alibaba:
9 toolcalling: # func call 工具
10 time:
11 enabled: true
12 queryInfo:
13 enabled: true
14 weather:
15 enabled: true
注入 工具Bean对象
1@Configuration
2@ConditionalOnClass({QueryInfoService.class})
3@ConditionalOnProperty(prefix = "spring.ai.alibaba.toolcalling.queryInfo", name = "enabled", havingValue = "true")
4public class QueryInfoConfig {
5
6 @Bean(name = "getQueryInfoFunction")
7 @ConditionalOnMissingBean
8 @Description("Get the information of a LongWei.")
9 public QueryInfoService getQueryInfoFunction() {
10 return new QueryInfoService();
11 }
12
13}
工具实现
1public class QueryInfoService implements Function<QueryInfoService.Request, QueryInfoService.Response> {
2
3 @Override
4 public Response apply(Request request) {
5 String info = "LongWei is a graduate student and 23 years old.";
6 return new Response(info);
7 }
8
9 @JsonInclude(JsonInclude.Include.NON_NULL)
10 @JsonClassDescription("QueryInfoFunction Service API request")
11 public record Request(@JsonProperty(required = true, value = "stuName") @JsonPropertyDescription("Student Name, such as LongWei") String stuName) {}
12
13 @JsonClassDescription("QueryInfoFunction Service API response")
14 public record Response(@JsonProperty(required = true, value = "description") @JsonPropertyDescription("A description containing the LongWei information") String description) {
15 }
16
17}
⭐ 注入工具给ChatModel调用
1@RestController
2@RequestMapping("/func")
3public class FuncCallController {
4
5 private final ChatClient ChatClient;
6
7 public FuncCallController(ChatClient.Builder chatClientBuilder) {
8 this.dashScopeChatClient = chatClientBuilder.build();
9 }
10
11 /**
12 * 调用工具版 - function
13 */
14 @GetMapping("/chat-tool-function")
15 public Flux<String> chatTranslateFunction(@RequestParam(value = "query", defaultValue = "请告诉我现在北京时间几点了") String query) {
16 return chatClient.prompt(query)
17 .functions("getQueryInfoFunction")
18 .stream()
19 .content();
20 }
21
22}
Spring AI Alibaba Multi-Modal
调用多模态大模型,这里示例为图文大模型(支持图片+文本输入)
1@RestController
2@RequestMapping("/multi-modal")
3public class MultiModalController {
4
5 private final ChatClient chatClient;
6
7 private static final String DEFAULT_MODEL = "qwen-vl-max-latest";
8
9 private static final String TEXT_MODEL = "qwen-plus";
10
11 public MultiModalController(ChatModel chatModel) {
12 this.chatClient = ChatClient.builder(chatModel)
13 .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
14 .build();
15 }
16
17 @GetMapping("/image")
18 public Flux<String> multiModal(
19 @RequestParam("prompt") String prompt,
20 @RequestParam("chatId") String chatId,
21 @RequestParam(value = "imageURL", required = false) String imageURL,
22 HttpServletResponse response
23 ) throws Exception {
24 response.setCharacterEncoding("UTF-8");
25
26 boolean flag = imageURL != null && !imageURL.isEmpty();
27
28 UserMessage userMessage;
29 if (flag) {
30 List<Media> media = List.of(new Media(MimeTypeUtils.IMAGE_PNG, new URI(imageURL).toURL()));
31 userMessage = new UserMessage(prompt, media);
32 userMessage.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE);
33 } else {
34 userMessage = new UserMessage(prompt);
35 }
36
37 return chatClient.prompt(new Prompt(
38 userMessage,
39 DashScopeChatOptions.builder()
40 .withModel(flag ? DEFAULT_MODEL : TEXT_MODEL)
41 .withMultiModel(flag)
42 .build()
43 ))
44 .advisors(advisorSpec -> advisorSpec
45 .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
46 .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
47 .stream().content();
48 }
49
50}
Spring AI Alibaba Cloud Rag
使用阿里提供的云知识库,进行Rag检索增强
步骤:用户Prompt => 检索知识库 => 嵌入知识片段到提示词 => 调用大模型
1@RestController
2@RequestMapping("/rag-chat")
3public class CloudRagController {
4
5 @Resource
6 private ICloudRagService cloudRagService;
7
8 @GetMapping("/bailian/knowledge/importDocument")
9 public void importDocument() {
10 cloudRagService.importDocuments();
11 }
12
13 @GetMapping("/bailian/knowledge/generate")
14 public Flux<String> generate(@RequestParam(value = "message",
15 defaultValue = "你好,请问你的知识库文档主要是关于什么内容的?") String message,
16 HttpServletResponse response) {
17 response.setCharacterEncoding("UTF-8");
18 return cloudRagService.retrieve(message).map(x -> x.getResult().getOutput().getText());
19 }
20}
1@Slf4j
2@Service
3public class CloudRagService implements ICloudRagService {
4
5 private static final String indexName = "Rag测试"; // 知识库名称
6
7 @Value("classpath:juc.pdf")
8 private Resource ragResourceFile;
9
10 private static final String retrievalSystemTemplate = """
11 Context information is below.
12 ---------------------
13 {question_answer_context}
14 ---------------------
15 Given the context and provided history information and not prior knowledge,
16 reply to the user comment. If the answer is not in the context, inform
17 the user that you can't answer the question.
18 """;
19
20 private final ChatClient chatClient;
21
22 private final DashScopeApi dashscopeApi;
23
24 public CloudRagService(ChatClient.Builder builder, DashScopeApi dashscopeApi) {
25 DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
26 DashScopeDocumentRetrieverOptions.builder().withIndexName(indexName).build());
27 // 注入检索增强配置
28 this.dashscopeApi = dashscopeApi;
29 this.chatClient = builder
30 .defaultAdvisors(new DocumentRetrievalAdvisor(retriever, retrievalSystemTemplate))
31 .build();
32 }
33
34 // api导入信息到知识库
35 @Override
36 public void importDocuments() {
37 String path = saveToTempFile(ragResourceFile);
38
39 DashScopeDocumentCloudReader reader = new DashScopeDocumentCloudReader(path, dashscopeApi, null);
40 List<Document> documentList = reader.get();
41 log.info("{} documents loaded and split", documentList.size());
42
43 VectorStore vectorStore = new DashScopeCloudStore(dashscopeApi, new DashScopeStoreOptions(indexName));
44 vectorStore.add(documentList);
45 log.info("{} documents added to dashscope cloud vector store", documentList.size());
46 }
47
48 private String saveToTempFile(Resource springAiResource) {
49 try {
50 File tempFile = File.createTempFile("juc", ".pdf");
51 tempFile.deleteOnExit();
52
53 try (InputStream inputStream = springAiResource.getInputStream();
54 FileOutputStream outputStream = new FileOutputStream(tempFile)) {
55 byte[] buffer = new byte[4096];
56 int bytesRead;
57 while ((bytesRead = inputStream.read(buffer)) != -1) {
58 outputStream.write(buffer, 0, bytesRead);
59 }
60 }
61
62 return tempFile.getAbsolutePath();
63 }
64 catch (IOException e) {
65 throw new RuntimeException(e);
66 }
67 }
68
69 @Override
70 public Flux<ChatResponse> retrieve(String message) {
71 return chatClient.prompt().user(message).stream().chatResponse();
72 }
73}
Spring Ai alibaba Agent
这里调用阿里百炼平台搭建的 Agent智能体对象(需要在百炼平台预设好对应的Agent)
1spring:
2 ai:
3 dashscope:
4 api-key: "API_KEY"
5 workspace-id: "WORKSPACE_ID"
6 chat:
7 options:
8 model: "MODEL_NAME"
9 agent:
10 app-id: "APP_ID"
1@Slf4j
2@RestController
3@RequestMapping("/agent")
4public class TestAgentController {
5
6 private String APP_ID = "APP_ID";
7
8 private DashScopeAgent agent;
9
10 public TestAgentController(DashScopeAgentApi dashScopeAgentApi) {
11 this.agent = new DashScopeAgent(dashScopeAgentApi);
12 }
13
14 @GetMapping("/bailian/agent/stream")
15 public Flux<String> stream(@RequestParam(value = "prompt") String prompt,
16 HttpServletResponse response) {
17 response.setCharacterEncoding("UTF-8");
18
19 return agent.stream(new Prompt(prompt, DashScopeAgentOptions.builder().withAppId(APP_ID).build())).map(resp -> {
20 AssistantMessage app_output = resp.getResult().getOutput();
21 return app_output.getContent();
22 });
23 }
24}