阿里百炼Spring AI Alibaba

文章目录

  • 学习链接
  • 阿里百炼
    • 创建api-key
    • 查看api调用示例
    • 示例
      • pom.xml
      • AQuickStart
      • MultiChat
      • StreamChat
  • Spring AI Alibaba
    • 简单示例
      • pom.xml
      • application.yml
      • HelloworldController
      • DashScopeChatModelController
      • 图解spring AI的结构
    • deepseek
      • pom.xml
      • application.yml
      • DeepSeekChatClientController
      • DeepSeekChatModelController
    • dashscope文生图
      • pom.xml
      • application.yml
      • DashScopeImageController
    • dashscope语音
      • pom.xml
      • application.yml
      • TTSController
      • STTController

学习链接

阿里AI-Spring Cloud Alibaba AI:快速搭建自己的通义千问

让你快速入门Spring Cloud Alibaba AI

SpringCloud + Spring AI Alibaba 整合阿里云百炼大模型

Spring AI Alibaba官方文档
spring-ai-alibaba 官方github
spring-ai-alibaba-examples - spring ai alibaba示例代码

spring-cloud-alibaba-examples/spring-cloud-ai-example(忽略这个)

Spring Cloud Alibaba官网
Spring Boot 单体应用升级 Spring Cloud 微服务最佳实践

阿里百炼

阿里百炼官网

创建api-key

在这里插入图片描述

查看api调用示例

在这里插入图片描述
在这里插入图片描述
jdk版本使用17(如果使用jdk8,okhttp版本和gson版本不兼容,要么这个类找不到,要么那个方法没有。更换了版本也总会有其它地方的问题。)

示例

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- 用java8 总是 okhttp方法找不到,类找不到,有时这个版本能用,但另外的问题还存在,换了版本,方法又不存在。 --><parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>3.4.2</version></parent><groupId>org.example</groupId><artifactId>demo-alibailian</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.18.2</version><exclusions><!-- 排除slf4j-simple依赖 --><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions></dependency></dependencies></project>

AQuickStart

package com.zzhua;// dashscope SDK的版本 >= 2.18.2import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.zzhua.constant.Constants;import java.util.Arrays;/*
测试:
curl -X POST "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation" \
-H "Authorization: Bearer sk-~~~" \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-r1","input":{"messages":[{"role": "user","content": "你是谁?"}]},"parameters": {"result_format": "message"}
}'
*/
public class AQuickStart {public static GenerationResult callWithMessage() throws Exception {Generation gen = new Generation();Message userMsg = Message.builder().role(Role.USER.getValue()).content("你是谁?").build();GenerationParam param = GenerationParam.builder()// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx").apiKey(Constants.API_KEY).model("deepseek-r1").prompt("你是谁?").messages(Arrays.asList(userMsg))// 不可以设置为"text".resultFormat(GenerationParam.ResultFormat.MESSAGE).build();return gen.call(param);}public static void main(String[] args) {try {GenerationResult result = callWithMessage();System.out.println("思考过程:");System.out.println(result.getOutput().getChoices().get(0).getMessage().getReasoningContent());System.out.println("回复内容:");System.out.println(result.getOutput().getChoices().get(0).getMessage().getContent());} catch (Exception e) {// 使用日志框架记录异常信息System.err.println("An error occurred while calling the generation service: " + e.getMessage());}System.exit(0);}
}

MultiChat

package com.zzhua;// dashscope SDK的版本 >= 2.18.2import java.util.Arrays;
import java.lang.System;import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.JsonUtils;
import com.zzhua.constant.Constants;public class MultiChat {public static GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException {Generation gen = new Generation();Message userMsg1 = Message.builder().role(Role.USER.getValue()).content("你好").build();Message AssistantMsg = Message.builder().role(Role.ASSISTANT.getValue()).content("你好!很高兴见到你,有什么我可以帮忙的吗?").build();Message UserMsg2 = Message.builder().role(Role.USER.getValue()).content("你是谁").build();GenerationParam param = GenerationParam.builder()// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx").apiKey(Constants.API_KEY).model("deepseek-r1").messages(Arrays.asList(userMsg1, AssistantMsg, UserMsg2))// 不可以设置为"text".resultFormat(GenerationParam.ResultFormat.MESSAGE).build();return gen.call(param);}public static void main(String[] args) {try {GenerationResult result = callWithMessage();System.out.println("思考过程:");System.out.println(result.getOutput().getChoices().get(0).getMessage().getReasoningContent());System.out.println("回复内容:");System.out.println(result.getOutput().getChoices().get(0).getMessage().getContent());} catch (ApiException | NoApiKeyException | InputRequiredException e) {// 使用日志框架记录异常信息System.err.println("An error occurred while calling the generation service: " + e.getMessage());}System.exit(0);}
}

StreamChat

package com.zzhua;// dashscope SDK的版本 >= 2.18.2import java.util.Arrays;import com.zzhua.constant.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;import java.lang.System;public class StreamChat {private static final Logger logger = LoggerFactory.getLogger(StreamChat.class);private static StringBuilder reasoningContent = new StringBuilder();private static StringBuilder finalContent = new StringBuilder();private static boolean isFirstPrint = true;private static void handleGenerationResult(GenerationResult message) {String reasoning = message.getOutput().getChoices().get(0).getMessage().getReasoningContent();String content = message.getOutput().getChoices().get(0).getMessage().getContent();if (!reasoning.isEmpty()) {reasoningContent.append(reasoning);if (isFirstPrint) {System.out.println("====================思考过程====================");isFirstPrint = false;}System.out.print(reasoning);}if (!content.isEmpty()) {finalContent.append(content);if (!isFirstPrint) {System.out.println("\n====================完整回复====================");isFirstPrint = true;}System.out.print(content);}}private static GenerationParam buildGenerationParam(Message userMsg) {return GenerationParam.builder()// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx").apiKey(Constants.API_KEY).model("deepseek-r1").messages(Arrays.asList(userMsg))// 不可以设置为"text".resultFormat(GenerationParam.ResultFormat.MESSAGE).incrementalOutput(true).build();}public static void streamCallWithMessage(Generation gen, Message userMsg)throws NoApiKeyException, ApiException, InputRequiredException {GenerationParam param = buildGenerationParam(userMsg);Flowable<GenerationResult> result = gen.streamCall(param);result.blockingForEach(message -> handleGenerationResult(message));}public static void main(String[] args) {try {Generation gen = new Generation();Message userMsg = Message.builder().role(Role.USER.getValue()).content("你是谁?").build();streamCallWithMessage(gen, userMsg);
//             打印最终结果
//            if (reasoningContent.length() > 0) {
//                System.out.println("\n====================完整回复====================");
//                System.out.println(finalContent.toString());
//            }} catch (ApiException | NoApiKeyException | InputRequiredException e) {logger.error("An exception occurred: {}", e.getMessage());}System.exit(0);}
}

Spring AI Alibaba

1、注意:因为 Spring AI Alibaba 基于 Spring Boot 3.x 开发,因此本地 JDK 版本要求为 17 及以上。

2、spring-ai-alibaba-examples:此仓库中包含许多 Example 来介绍 Spring AI Alibaba 从基础到高级的各种用法和 AI 项目的最佳实践。 更详细的介绍介绍请参阅每个子项目中的 README.md 和 Spring AI Alibaba 官网。

3、 阿里云百炼平台 获取 API-KEY

4、由于 spring-ai 相关依赖包还没有发布到中央仓库,如出现 spring-ai-core 等相关依赖解析问题,请在您项目的 pom.xml 依赖中加入指定的仓库配置。

简单示例

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>3.4.2</version></parent><groupId>org.example</groupId><artifactId>demo-alibailian</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring-ai-alibaba-starter 传递依赖了dashscope-sdk-java, 但是可能不是需要的版本(不是最新的版本) --><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.18.2</version><exclusions><!-- 排除slf4j-simple依赖 --><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M5.1</version></dependency></dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories></project>

application.yml

spring:ai:dashscope:api-key: sk-~~~ # 阿里百炼的key

HelloworldController

@RestController
@RequestMapping("/helloworld")
public class HelloworldController {private static final String DEFAULT_PROMPT = "你是一个博学的智能聊天助手,请根据用户提问回答!";private final ChatClient dashScopeChatClient;// ChatClientAutoConfiguration中注册了scope为prototype的ChatClient.Builder,// 同时该ChatClient.Builder会依赖1个 ChatModel, 此ChatModel在DashScopeAutoConfiguration中定义了DashScopeChatModel这个bean,// 所以,当需要ChatClient.Builder时,就会触发多例bean的获取,从而会寻找ChatModel的实现bean,如果有不止1个ChatModel的bean定义了,那么就会报错public HelloworldController(ChatClient.Builder chatClientBuilder) {this.dashScopeChatClient = chatClientBuilder.defaultSystem(DEFAULT_PROMPT)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(DashScopeChatOptions.builder().withTopP(0.7).build()).build();}// 也可以如下这样创建,与上面是1个意思/*public HelloworldController(ChatModel chatModel) {// 构造时,可以设置 ChatClient 的参数// {@link org.springframework.ai.chat.client.ChatClient};this.dashScopeChatClient = ChatClient.builder(chatModel)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(DashScopeChatOptions.builder().withTopP(0.7).build()).build();}*/// http://localhost:8080/helloworld/simple/chat?query=你是谁/*** ChatClient 简单调用*/@GetMapping("/simple/chat")public String simpleChat(@RequestParam("query") String query) {// 1、DefaultChatClientRequestSpec 构造方法中添加的 CallAroundAdvisor 触发了请求的执行//    调用 chatModel.call(Prompt),即 DashScopeChatModel#call(Prompt)// 2、然后调用 DashScopeApi#chatCompletionEntity(ChatCompletionRequest)//    可以看到 最终默认调用的模型是 qwen-plus//    (是因为 DashScopeAutoConfiguration 定义了 DashScopeChatModel 这个bean,//      而 DashScopeChatModel 这个bean又引用了 DashScopeChatProperties 这个bean,//      而 DashScopeChatProperties 中直接初始化的 DashScopeChatOptions 使用了 qwen-plus。//      所以,如果需要改掉模型,方法就是 1. 通过spring.ai.datascope.chat.options.model设置 来改掉默认的模型//                                  2. 或者这样改 dashScopeChatClient.prompt(new Prompt(query, ChatOptions.builder().model("deepseek-r1").build())).call().content()// 3、注意几个类: DashScopeAutoConfiguration、//              DashScopeConnectionProperties(配置前缀是 spring.ai.datascope)、//              DashScopeChatProperties(配置前缀是 spring.ai.datascope.chat)// return dashScopeChatClient.prompt(query).call().content();return dashScopeChatClient.prompt(new Prompt(query, ChatOptions.builder().model("deepseek-r1").build())).call().content();}// http://localhost:8080/helloworld/stream/chat?query=你是谁/*** ChatClient 流式调用*/@GetMapping("/stream/chat")public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?") String query, HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return dashScopeChatClient.prompt(query).stream().content();}// http://127.0.0.1:8080/helloworld/advisor/chat/123?query=你好,我叫牧生,之后的会话中都带上我的名字// http://127.0.0.1:8080/helloworld/advisor/chat/123?query=我叫什么名字?/*** ChatClient 使用自定义的 Advisor 实现功能增强.* eg:* http://127.0.0.1:8080/helloworld/advisor/chat/123?query=你好,我叫牧生,之后的会话中都带上我的名字* 你好,牧生!很高兴认识你。在接下来的对话中,我会记得带上你的名字。有什么想聊的吗?* http://127.0.0.1:8080/helloworld/advisor/chat/123?query=我叫什么名字?* 你叫牧生呀。有什么事情想要分享或者讨论吗,牧生?*/@GetMapping("/advisor/chat/{id}")public Flux<String> advisorChat(HttpServletResponse response,@PathVariable(value = "id") String id,@RequestParam(value = "query") String query) {response.setCharacterEncoding("UTF-8");return this.dashScopeChatClient.prompt(query)// 因为前面使用Client.Builder构建Client时, 添加了MessageChatMemoryAdvisor切面// 所以, 这里可以添加切面参数.advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, id).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)).stream().content();}}

DashScopeChatModelController

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import jakarta.servlet.http.HttpServletResponse;
import reactor.core.publisher.Flux;import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/dashscope/chat-model")
public class DashScopeChatModelController {private static final String DEFAULT_PROMPT = "你好,介绍下你自己吧。";private final ChatModel dashScopeChatModel;public DashScopeChatModelController(ChatModel chatModel) {this.dashScopeChatModel = chatModel;}// http://localhost:8080/dashscope/chat-model/simple/chat/*** 最简单的使用方式,没有任何 LLMs 参数注入。* @return String types.*/@GetMapping("/simple/chat")public String simpleChat() {return dashScopeChatModel.call(new Prompt(DEFAULT_PROMPT, DashScopeChatOptions.builder()// .withModel("qwen2.5-vl-72b-instruct") // 这个会报错, 这应该是模型的问题.withModel("qwen-plus") // 这个正常回答: 你好!我是通义千问,阿里巴巴集团旗下...// .withModel("deepseek-r1")  // 这个正常回答: 您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1。如您有任...// .withModel("deepseek-r1-distill-qwen-1.5b")  // 这个正常回答: 我是DeepSeek-R1,一个由深度求索公司开发的智能助手,我....build())).getResult().getOutput().getContent();}// http://localhost:8080/dashscope/chat-model/stream/chat/*** Stream 流式调用。可以使大模型的输出信息实现打字机效果。* @return Flux<String> types.*/@GetMapping("/stream/chat")public Flux<String> streamChat(HttpServletResponse response) {// 避免返回乱码response.setCharacterEncoding("UTF-8");Flux<ChatResponse> stream = dashScopeChatModel.stream(new Prompt(DEFAULT_PROMPT, DashScopeChatOptions.builder()// .withModel("qwen-plus") // 这个正常回答: 你好!我是通义千问,阿里巴巴集团旗下...// .withModel("deepseek-r1")  // 这个正常回答: 您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手DeepSeek-R1。如您有任....withModel("deepseek-r1-distill-qwen-1.5b")  // /这个正常回答: 我是DeepSeek-R1,....build()));return stream.map(resp -> resp.getResult().getOutput().getContent());}// http://localhost:8080/dashscope/chat-model/custom/chat/*** 使用编程方式自定义 LLMs ChatOptions 参数, {@link com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions}* 优先级高于在 application.yml 中配置的 LLMs 参数!*/@GetMapping("/custom/chat")public String customChat() {// 不指定模型, 将会使用默认的  spring.ai.dashscope.chat.options.model 配置的模型(默认是qwen-plus)DashScopeChatOptions customOptions = DashScopeChatOptions.builder().withTopP(0.7).withTopK(50).withTemperature(0.8).build();return dashScopeChatModel.call(new Prompt(DEFAULT_PROMPT, customOptions)).getResult().getOutput().getContent();}}

图解spring AI的结构

在这里插入图片描述

deepseek

这节示例是使用OpenAi对接deepseek,跟dashscope没关系(所以这个依赖可以不用引入),需要引入spring-ai-openai-spring-boot-starter。如果同时引入spring-ai-alibaba-starter就需要注意 注入的ChatModel的具体实现。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>3.4.2</version></parent><groupId>org.example</groupId><artifactId>demo-alibailian</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.18.2</version><exclusions>&lt;!&ndash; 排除slf4j-simple依赖 &ndash;&gt;<exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M5.1</version></dependency><!-- 引入spring-ai openai的启动依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>1.0.0-M5</version></dependency></dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories></project>

application.yml

spring:ai:# 百炼的配置dashscope:api-key: sk-~~~# 百炼的ApiKeychat:options:model: qwen-plus# 可兼容openai的配置openai:api-key: sk-~~~# deepseek的ApiKeybase-url: https://api.deepseek.comchat:options:model: deepseek-chatembedding:enabled: false

DeepSeekChatClientController

@RestController
public class DeepSeekChatClientController {private static final String DEFAULT_PROMPT = "你好,介绍下你自己!";private final ChatModel chatModel;private final ChatClient deepSeekChatClient;// 【注入的是 OpenAiChatModel, 而这里配置的是与openai兼容的deepseek。//  OpenAiAutoConfiguration 中定义了 OpenAiChatModel。//  ChatModel(这个由各个spring-ai模块包自己实现,并通过自动配置类提供出来)//  -> ChatClient.Builder(这个在 ClientAutoConfiguration 中定义为多例bean,它需要1个 ChatModel)//  -> ChatClient(使用 ChatClient.Builder 构建,其实现为 DefaultChatClient)】public DeepSeekChatClientController (OpenAiChatModel chatModel) {this.chatModel = chatModel;// 这里实际上就是在模拟 ClientAutoConfiguration 中定义 ChatClient.Builder多例bean 类似this.deepSeekChatClient = ChatClient.builder(chatModel).defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(OpenAiChatOptions.builder().temperature(0.7d).build()).build();}// http://localhost:8080/ai/customOptions/*** 使用自定义参数调用DeepSeek模型** @return ChatResponse 包含模型响应结果的封装对象* @apiNote 当前硬编码指定模型为deepseek-chat,温度参数0.7以平衡生成结果的创造性和稳定性*/@GetMapping(value = "/ai/customOptions")public ChatResponse testDeepSeekCustomOptions () {return this.deepSeekChatClient.prompt(DEFAULT_PROMPT).call().chatResponse();}// http://localhost:8080/ai/generate/*** 执行默认提示语的AI生成请求** @return Map 包含生成结果的键值对,格式为{ "generation": 响应内容 }*/@GetMapping("/ai/generate")public Map<String, Object> testEasyChat () {return Map.of("generation", this.deepSeekChatClient.prompt(DEFAULT_PROMPT).call().content());}// http://localhost:8080/ai/stream/*** 流式生成接口 - 支持实时获取生成过程的分块响应** @return Flux<ChatResponse> 响应式流对象,包含分块的模型响应数据* @see Flux 基于Project Reactor的响应式流对象*/@GetMapping("/ai/stream")public Flux<String> testDeepSeekGenerateWithStream (HttpServletResponse response) {response.setCharacterEncoding("UTF-8");// 1、DefaultChatClientRequestSpec 构造方法中添加的 StreamAroundAdvisor 触发了请求的执行//    调用 chatModel.stream(Prompt),即 chatModel#stream(Prompt),这里的chatModel就是OpenAiChatModel的实现// 2、所以具体怎么解析响应的 需要到 OpenAiChatModel#stream(Prompt) 方法中查看// 3、所以,其实就发现 chatClient还绕了一圈,然后再去调用chatModel,绕一圈的目的是为了增强功能return deepSeekChatClient.prompt(DEFAULT_PROMPT).stream().chatResponse().map(chatResponse -> chatResponse.getResult().getOutput().getContent());}}

DeepSeekChatModelController

@RestController
public class DeepSeekChatModelController {private static final String DEFAULT_PROMPT = "你好,介绍下你自己吧。";private final ChatModel deepSeekChatModel;// 注入的是 OpenAiChatModelpublic DeepSeekChatModelController (OpenAiChatModel chatModel) {this.deepSeekChatModel = chatModel;}// http://localhost:8080/simple/chat/*** 最简单的使用方式,没有任何 LLMs 参数注入。** @return String types.*/@GetMapping("/simple/chat")public String simpleChat () {return deepSeekChatModel.call(new Prompt(DEFAULT_PROMPT)).getResult().getOutput().getContent();}// http://localhost:8080/stream/chat/*** Stream 流式调用。可以使大模型的输出信息实现打字机效果。** @return Flux<String> types.*/@GetMapping("/stream/chat")public Flux<String> streamChat (HttpServletResponse response) {response.setCharacterEncoding("UTF-8");Flux<ChatResponse> stream = deepSeekChatModel.stream(new Prompt(DEFAULT_PROMPT));return stream.map(resp -> resp.getResult().getOutput().getContent());}// http://localhost:8080/custom/chat/*** 使用编程方式自定义 LLMs ChatOptions 参数, {OpenAIChatOption}* 优先级高于在 application.yml 中配置的 LLMs 参数!*/@GetMapping("/custom/chat")public String customChat () {// 指定的模型 将覆盖 chatModel 中配置的模型// 模型名优先级顺序 Prompt中的options > client配置的options > chatModel中的options(通过配置文件配置的)OpenAiChatOptions customOptions = OpenAiChatOptions.builder().model("deepseek-reasoner").temperature(0.8d).build();return deepSeekChatModel.call(new Prompt(DEFAULT_PROMPT, customOptions)).getResult().getOutput().getContent();}
}

dashscope文生图

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>3.4.2</version></parent><groupId>org.example</groupId><artifactId>demo-alibailian</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.18.2</version><exclusions><!-- 排除slf4j-simple依赖 --><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M5.1</version></dependency></dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories></project>

application.yml

spring:ai:# 百炼的配置dashscope:api-key: sk-~~~# 百炼的ApiKey

DashScopeImageController

可以看出来,spring ai alibaba其实就是实现了spring ai core中的接口

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;import jakarta.servlet.http.HttpServletResponse;import org.springframework.ai.image.ImageModel;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/example")
public class DashScopeImageController {private final ImageModel imageModel;private static final String DEFAULT_PROMPT = "为人工智能生成一张富有科技感的图片!";public DashScopeImageController(ImageModel imageModel) {this.imageModel = imageModel;}// http://localhost:8080/example/image?text=生成1张小怪兽打奥特曼的图片@GetMapping("/image")public void image(@RequestParam(value = "text", defaultValue = DEFAULT_PROMPT) String text, HttpServletResponse response) {ImageResponse imageResponse = imageModel.call(new ImagePrompt(text));String imageUrl = imageResponse.getResult().getOutput().getUrl();System.out.println(imageUrl);try {URL url = URI.create(imageUrl).toURL();InputStream in = url.openStream();response.setHeader("Content-Type", MediaType.IMAGE_PNG_VALUE);response.getOutputStream().write(in.readAllBytes());response.getOutputStream().flush();} catch (IOException e) {response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);}}}

dashscope语音

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-dependencies</artifactId><groupId>org.springframework.boot</groupId><version>3.4.2</version></parent><groupId>org.example</groupId><artifactId>demo-alibailian</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.18.2</version><exclusions><!-- 排除slf4j-simple依赖 --><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M5.1</version></dependency></dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories></project>

application.yml

spring:ai:# 百炼的配置dashscope:api-key: sk-~~~# 百炼的ApiKey

TTSController

@RestController
@RequestMapping("/ai/tts")
public class TTSController /*implements ApplicationRunner*/ {private final SpeechSynthesisModel speechSynthesisModel;private static final String TEXT = "白日依山尽,黄河入海流。";private static final String FILE_PATH = System.getProperty("user.dir") + "/file/";public TTSController(SpeechSynthesisModel speechSynthesisModel) {this.speechSynthesisModel = speechSynthesisModel;}// http://localhost:8080/ai/tts?text=白日依山尽,黄河入海流。@GetMappingpublic void tts(@RequestParam(value = "text", defaultValue = TEXT) String text) throws IOException {SpeechSynthesisResponse response = speechSynthesisModel.call(new SpeechSynthesisPrompt(text));File file = new File(FILE_PATH + "/output.mp3");try (FileOutputStream fos = new FileOutputStream(file)) {ByteBuffer byteBuffer = response.getResult().getOutput().getAudio();fos.write(byteBuffer.array());}catch (IOException e) {throw new IOException(e.getMessage());}}@GetMapping("/stream")public void streamTTS(@RequestParam(value = "text", defaultValue = TEXT) String text) {Flux<SpeechSynthesisResponse> response = speechSynthesisModel.stream(new SpeechSynthesisPrompt(text));CountDownLatch latch = new CountDownLatch(1);File file = new File(FILE_PATH + "/output-stream.mp3");try (FileOutputStream fos = new FileOutputStream(file)) {response.doFinally(signal -> latch.countDown()).subscribe(synthesisResponse -> {ByteBuffer byteBuffer = synthesisResponse.getResult().getOutput().getAudio();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);try {fos.write(bytes);}catch (IOException e) {throw new RuntimeException(e);}});latch.await();}catch (IOException | InterruptedException e) {throw new RuntimeException(e);}}/*@Overridepublic void run(ApplicationArguments args) {File file = new File(FILE_PATH);if (!file.exists()) {file.mkdirs();}}@PreDestroypublic void destroy() throws IOException {FileUtils.deleteDirectory(new File(FILE_PATH));}
*/
}

STTController


@RestController
@RequestMapping("/ai/stt")
public class STTController {private final AudioTranscriptionModel transcriptionModel;private static final Logger log = LoggerFactory.getLogger(STTController.class);private static final String DEFAULT_MODEL_1 = "sensevoice-v1";private static final String DEFAULT_MODEL_2 = "paraformer-realtime-v2";private static final String DEFAULT_MODEL_3 = "paraformer-v2";private static final String AUDIO_RESOURCES_URL = "https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/hello_world_female2.wav";private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public STTController(AudioTranscriptionModel transcriptionModel) {this.transcriptionModel = transcriptionModel;}@GetMappingpublic String stt() throws MalformedURLException {AudioTranscriptionResponse response = transcriptionModel.call(new AudioTranscriptionPrompt(new UrlResource(AUDIO_RESOURCES_URL),DashScopeAudioTranscriptionOptions.builder().withModel(DEFAULT_MODEL_1).build()));return response.getResult().getOutput();}@GetMapping("/stream")public String streamSTT() {CountDownLatch latch = new CountDownLatch(1);StringBuilder stringBuilder = new StringBuilder();Flux<AudioTranscriptionResponse> response = transcriptionModel.stream(new AudioTranscriptionPrompt(new FileSystemResource("spring-ai-alibaba-audio-example/dashscope-audio/src/main/resources/stt/count.pcm"),DashScopeAudioTranscriptionOptions.builder().withModel(DEFAULT_MODEL_2).withSampleRate(16000).withFormat(DashScopeAudioTranscriptionOptions.AudioFormat.PCM).withDisfluencyRemovalEnabled(false).build()));response.doFinally(signal -> latch.countDown()).subscribe(resp -> stringBuilder.append(resp.getResult().getOutput()));try {latch.await();}catch (InterruptedException e) {throw new RuntimeException(e);}return stringBuilder.toString();}@GetMapping("/async")public String asyncSTT() {StringBuilder stringBuilder = new StringBuilder();CountDownLatch latch = new CountDownLatch(1);try {AudioTranscriptionResponse submitResponse = transcriptionModel.asyncCall(new AudioTranscriptionPrompt(new UrlResource(AUDIO_RESOURCES_URL),DashScopeAudioTranscriptionOptions.builder().withModel(DEFAULT_MODEL_3).build()));DashScopeAudioTranscriptionApi.Response.Output submitOutput = Objects.requireNonNull(submitResponse.getMetadata().get("output"));String taskId = submitOutput.taskId();scheduler.scheduleAtFixedRate(() -> checkTaskStatus(taskId, stringBuilder, latch), 0, 1, TimeUnit.SECONDS);latch.await();}catch (MalformedURLException e) {throw new DashScopeException("Error in URL format: " + e.getMessage());}catch (InterruptedException e) {Thread.currentThread().interrupt();throw new DashScopeException("Thread was interrupted: " + e.getMessage());}finally {scheduler.shutdown();}return stringBuilder.toString();}private void checkTaskStatus(String taskId, StringBuilder stringBuilder, CountDownLatch latch) {try {AudioTranscriptionResponse fetchResponse = transcriptionModel.fetch(taskId);DashScopeAudioTranscriptionApi.Response.Output fetchOutput =Objects.requireNonNull(fetchResponse.getMetadata().get("output"));DashScopeAudioTranscriptionApi.TaskStatus taskStatus = fetchOutput.taskStatus();if (taskStatus.equals(DashScopeAudioTranscriptionApi.TaskStatus.SUCCEEDED)) {stringBuilder.append(fetchResponse.getResult().getOutput());latch.countDown();}else if (taskStatus.equals(DashScopeAudioTranscriptionApi.TaskStatus.FAILED)) {log.warn("Transcription failed.");latch.countDown();}}catch (Exception e) {latch.countDown();throw new RuntimeException("Error occurred while checking task status: " + e.getMessage());}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/34228.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【模拟算法】

目录 替换所有的问号 提莫攻击 Z 字形变换 外观数列 数青蛙&#xff08;较难&#xff09; 模拟算法&#xff1a;比葫芦画瓢。思路较简单&#xff0c;考察代码能力。 1. 模拟算法流程&#xff0c;一定要在演草纸上过一遍流程 2. 把流程转化为代码 替换所有的问号 1576. 替…

【Linux】进程(1)进程概念和进程状态

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 目录 前言 一、什么是进程 二、task_struct的内容 三、Linux下进程基本操作 四、父进程和子进程 1. 用fork函数创建子进程 五、进程状态 1. 三种重…

配置blender的python环境

在blender的脚本出输入&#xff1a; import sys print(sys.executable) 2. 通过上述命令我们得到blener的python版本&#xff0c;下面我们在conda配置一个同样版本的python环境。 conda create -n blenderpy python3.11.9找到blender安装路径下的python文件夹&#xff0c;将它…

【bug日记】 编译错误

在我使用vscode的时候&#xff0c;我想用一个头文件和两个cpp文件&#xff0c;头文件是用来声明一个类的&#xff0c;一个cpp是用来类的成员函数&#xff0c;一个cpp是主函数 但是我写完编译发现会弹出找不到这个类成员函数这个cpp文件&#xff0c;爆出这样的错误 提示我找不到…

SQLAlchemy系列教程:批量插入数据

高效地批量插入数据对于应用程序的性能至关重要。SQLAlchemy为批处理操作提供了几种机制&#xff0c;可以最大限度地减少开销并加快数据库事务时间。在本指南中&#xff0c;我们将探讨如何使用SQLAlchemy执行批量插入&#xff0c;包括从基础技术到高级技术。 搭建环境 在开始之…

蓝桥杯十天冲刺-day1(getline读入空格)

getline读入带空格的字符串 解决cin或scanf无法读入空格的问题 作文标题 代码思路 主要通过这个代码体会getline函数可以输入空格的作用 用getline函数输入含空格的字符串&#xff0c;用length()函数记字符串长度 依次扫描不为空格的字符计数 #include<bits/stdc.h>…

使用py-ffmpeg批量合成视频的脚本

我有一个小米摄像头&#xff0c;用它录出来的视频全部都是3s一段3s一段的。其中有几个小时的视频我需要保存&#xff0c;当初直接把摄像头的卡文件导出来重命名掉了&#xff0c;那时候没有注意&#xff0c;之后想剪辑/发送给别人的时候发现疯了&#xff1a; 1.剪辑的话&#x…

el-table表格样式设置单元格样式方法 :cell-class-name

需求&#xff1a;是否匹配当天日期决定当天时间高亮显示 效果如图 页面代码 <el-tableref"manpowerTable":key"manpowerForUserHandle.tableKey"class"sysDictInfoTable":data"handle.manpowerTable.data"style"width: 100…

基于express+TS+mysql+sequelize的后端开发环境搭建

步骤一&#xff1a;初始化node环境 npm init -y 步骤二&#xff1a;安装 Express、TypeScript、以及相关类型的定义文件 npm install express npm install --save-dev typescript types/node types/express ts-node nodemon npm install body-parser npm install mysql2 npm in…

蓝耘MaaS平台:阿里QWQ应用拓展与调参实践

摘要&#xff1a;本文深入探讨了蓝耘MaaS平台与阿里QWQ模型的结合&#xff0c;从平台架构、模型特点到应用拓展和调参实践进行了全面分析。蓝耘平台凭借其强大的算力支持、弹性资源调度和全栈服务&#xff0c;为QWQ模型的高效部署提供了理想环境。通过细化语义描述、调整推理参…

2. qt写带有槽的登录界面(c++)

我们在1.Qt写简单的登录界面(c)_c qt 设计一个简单界面-CSDN博客中写了个简单的登录界面&#xff0c;但没有槽&#xff0c;在这里写一个带有槽的界面。 1.代码 代码目录如下&#xff1a; main.cpp的代码如下&#xff1a; #include "MainWindow.h" #include <Qt…

linux - 基础IO之操作与文件描述符全解析:从C语言到系统调用底层实现

目录 1.回顾c语言中所学的文件 2.提炼对文件的理解&#xff08;linux基础io第一阶段的学习&#xff09; a.在操作系统内部&#xff0c;一个进程和一个被打开的文件&#xff0c;他们到后面会变成两种对象之间的指针关系。 b.文件 属性 内容 c.在c语言中,以w的方式打开文件…

【A2DP】深入解读A2DP中通用访问配置文件(GAP)的互操作性要求

目录 一、模式支持要求 1.1 发现模式 1.2 连接模式 1.3 绑定模式 1.4 模式间依赖关系总结 1.5 注意事项 1.6 协议设计深层逻辑 二、安全机制&#xff08;Security Aspects&#xff09; 三、空闲模式操作&#xff08;Idle Mode Procedures&#xff09; 3.1 支持要求 …

python 入门教程 window 10 环境下安装pyenv

python的环境配置方法很多&#xff0c;由于python有两个大版本&#xff0c;很多时候需要切换某个固定的版本才能运行三方包&#xff0c;所以推荐使用pyenv 配置python 环境变量 pyenv 的安装 安装方法&#xff1a; Invoke-WebRequest -UseBasicParsing -Uri "https://r…

【fNIRS可视化学习1】基于NIRS-SPM进行光极可视化并计算通道坐标

一、前言 功能性近红外光谱(fNIRS)是一种无创的脑功能成像技术。在fNIRS研究中&#xff0c;光极的空间定位和通道坐标的计算至关重要。 1.光极可视化 光极可视化的重要性我就不赘述了&#xff0c;它可以直观检查probe设计的合理性&#xff0c;确认光极覆盖目标脑区&#xff0c…

Vue.js 中 class 和 style 绑定的全面解析

目录 引言 6.1 v-bind 指令 介绍 使用方法 6.2 绑定 HTML class 介绍 用法 6.3 绑定内联样式 介绍 用法 6.4 实战&#xff1a;制作消息提示框 介绍 用法 总结 引言 在Vue.js构建用户界面的宏伟蓝图里&#xff0c;样式的动态呈现与交互性的完美融合是吸引用户目光…

【红黑树】—— 我与C++的不解之缘(二十五)

前言 学习了avl树&#xff0c;现在来学习红黑树。 一、什么是红黑树 红黑树是一颗平衡二叉搜索树&#xff0c;它每一个节点增加了一个存储位表示节点的颜色&#xff0c;可以是红色或者黑色。 相比较于AVL树&#xff0c;红黑树也是一个自平衡二叉搜索树&#xff0c;但是它与AVL树…

SFT数据处理部分的思考

SFT数据及处理的业内共识 1&#xff0e;prompt的质量和多样性远重要于数据量级&#xff0c;微调一个 30 b 量级的base model只需要 10 w 量级的数据即可 参考&#xff1a;《LIMA&#xff1a;Less Is More for Alignment》 2&#xff0e;合成数据很重要&#xff01;一般需要通过…

Python(学习一)

做网站有成熟的框架像FLASK、DJANGO、TORNADO&#xff0c;写爬虫有好用到哭的REQUESTS&#xff0c;还有强大到没盆友的SCRAPY 随着NUMPY、SCIPY、MATLOTLIB等众多第三方模块的开发和完善&#xff0c;不仅支持py支持各种数学运算&#xff0c;还可以绘制高质量的2D和3D图像&…

ArcGIS Pro将有文字标注底图切换为无标注底图(在线地图图源)

今天介绍一下在ArcGIS Pro将有标注的地形底图换成无标注的底图。 大家在这项目底图时候会经常调用ArcGIS Pro自带的地形图&#xff0c;但是这个地形图自带是有注记的&#xff0c;如下图。 如何更改&#xff0c;才可以调用无文字注记的呢&#xff1f; 对于一个已经切好图的有注记…