1、Spring AI alibaba介绍
Spring AI Alibaba是基于Spring AI框架构建的实现,专注于接入阿里云百炼系列的大模型服务。通过Spring AI Alibaba,开发者可以轻松地利用通义大模型进行文本生成、图像生成、语音合成等任务。其核心优势在于提供了统一的API接口,使得开发者能够以一致的方式调用不同类型的AI服务,并且只需修改配置即可切换不同的AI提供者,极大地简化了开发过程中的复杂度。此外,Spring AI Alibaba还支持本地部署及多种主流AI框架(如OpenAI和Ollama)的集成,进一步增强了其灵活性与适用性。
模型 Model
一句话说明:支持多种模型,包括多模态处理与图像识别。 输入输出举例:输入为文本或图片等,输出为相应的内容生成、分析结果。例如使用qwen-vl-max-latest模型进行图像内容识别。
提示 Prompt
一句话说明:提供给AI的初始信息或问题,用于引导其生成特定类型的响应。 输入输出举例:输入可以是一段话如"请描述这张照片",输出则是基于此提示对图片内容的解释。
提示词模板 Prompt Template
一句话说明:预设好的结构化提示模板,简化了复杂提示的设计过程。 输入输出举例:通过定义类似"我想知道消息id: {id}"这样的模板,并填充具体值(如{id}替换为实际ID),以产生针对特定场景的有效查询语句。
嵌入 Embedding
一句话说明:将数据转换成固定长度向量表示的方法,便于进行相似度计算和检索。 输入输出举例:一段文本作为输入,经过嵌入处理后得到一个数值向量作为输出,该向量可用于后续的相似性比较或其他机器学习任务中。
结构化输出 Structured Output
一句话说明:确保生成的内容符合预定格式,提高数据的一致性和可解析性。 输入输出举例:请求天气预报时,返回的数据不仅包含天气状况的文字描述,还包括温度、湿度等具体数值信息,这些都按照事先定义好的JSON格式组织起来。
检索增强生成 RAG
一句话说明:结合检索技术和生成模型,利用私有知识库来提升回答质量和准确性。 输入输出举例:用户询问公司财报中的某个具体数字,系统先从已存储的文档中检索相关信息,再结合这些上下文生成精准的回答。
函数调用 Function Calling
一句话说明:允许大语言模型在需要时执行开发者提供的功能,如调用外部API或执行特定代码。 输入输出举例:当询问某条消息的状态时,LLM不会直接回应,而是触发一个函数去查询数据库并返回状态详情。
向量存储 Vector Store
一句话说明:用于保存和管理大量高维向量数据的数据库,支持快速搜索相似项。 输入输出举例:上传一系列文档后将其转化为向量形式存入向量库,在需要时可通过关键词快速定位相关资料。
2、代码实现
前置条件
确保您的开发环境满足以下要求:
- JDK版本至少为17。
- Spring Boot版本为3.3.x或更高。
- 已经在阿里云上申请了通义千问服务的API Key,并且已经按照指示将其设置到了系统变量中。
2.1创建一个springboot工程,添加依赖
python
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
由于Spring AI的部分库尚未发布到Maven中央仓库,因此需要额外配置Spring自己的仓库地址至项目的pom.xml中
java
<repositories>
<repository>
<id>spring-snapshots</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
配置apikey

2.2创建一个控制器类用于接收用户的输入并通过聊天客户端向AI发送请求
java
private final ChatClient chatClient;
private final ImageModel imageModel;
private final ChatModel chatMduel;
public AIController(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory, ImageModel imageModel, ChatModel chatMduel) {
this.chatMduel = chatMduel;
this.chatClient = chatClientBuilder
.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory))
.defaultSystem("You are a helpful assistant.")
.build();
this.imageModel = imageModel;
}
@GetMapping("/chat")
public String chat(@RequestParam("message") String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
2.3流式响应
java
@GetMapping(value = "/stream",produces = "text/html;charset=UTF-8")
public Flux<String> streamChat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
Flux进行流式响应,
produces = "text/html;charset=UTF-8",解决响应乱码
2.4生成图片
java
@GetMapping("/image")
public ResponseEntity<String> generateImage(@RequestParam String message) {
try {
ImageOptions options = ImageOptionsBuilder.builder()
.width(1024)
.height(1024)
.model("wanx-v1") // 使用从阿里云获取到的具体模型名称
.N(1)
.build();
ImagePrompt prompt = new ImagePrompt(message, options);
ImageResponse response = imageModel.call(prompt);
List<ImageGeneration> results = response.getResults();
if (results.isEmpty()) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("No image generated.");
}
String imageUrl = results.get(0).getOutput().getUrl();
return ResponseEntity.ok(imageUrl);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
2.5图片识别
java
@Value("classpath:231.png")
private Resource imageResource;
private static final String DEFAULT_PROMPT = "这些是什么?";
private static final String DEFAULT_MODEL = "qwen-vl-max-latest";
@GetMapping("/imageRecognition")
public Flux<String> imageRecognition(@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt,
HttpServletResponse response) throws Exception {
response.setCharacterEncoding("UTF-8");
List<Media> mediaList = Collections.singletonList(new Media(MimeTypeUtils.IMAGE_PNG, imageResource));
UserMessage message = new UserMessage(prompt, mediaList);
message.getMetadata().put(DashScopeChatModel.MESSAGE_FORMAT, MessageFormat.IMAGE);
return chatMduel.stream(
new Prompt(message,
DashScopeChatOptions.builder()
.withModel(DEFAULT_MODEL)
.withMultiModel(true)
.build()))
.map(resp -> resp.getResult().getOutput().toString());
}
2.6Function Call
**大模型是怎么知道: **
1)当前有哪些接口可以调用的?
2)每个接口能干什么?
3)接口有什么入参出参的?
函数调用的实现依赖于开发者定义一个实现了java.util.function.Function接口的Java类,并通过注解描述其输入参数、输出结果及功能。
具体来说,使用@JsonProperty和@JsonPropertyDescription等Jackson注解来详细说明方法的参数与返回值。 Json property标记了函数的输入参数和输出参数,然后Function的Description则表达了这函数是干啥的。
java
@GetMapping(value ="/chatStream",produces = "text/html;charset=UTF-8")
public Flux<String> chatSteam(@RequestParam String input) {
PromptTemplate promptTemplate = new PromptTemplate("我想知道{company}的最新财务状况");
DashScopeChatOptions ops = DashScopeChatOptions.builder()
.withFunction("xueQiuFinanceFunction").build();
Map<String, Object> map = Map.of("company", input);
Prompt promp = promptTemplate.create(map, ops);
return chatClient.prompt(promp).stream().content();
}
当大模型判断需要调用方法时,去调用
withFunction配置的方法名称
java
package com.lijianxi.springai.springailearn;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import java.util.function.Function;
@Configuration
public class AppConfig {
@Bean
@Description("查询指定公司代码的财务信息")
public Function<XueQiuFinanceService.FinanceRequest, String> xueQiuFinanceFunction() {
return new XueQiuFinanceService();
}
}
java
package com.lijianxi.springai.springailearn;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import org.springframework.web.client.RestTemplate;
import java.util.function.Function;
public class XueQiuFinanceService implements Function<XueQiuFinanceService.FinanceRequest, String> {
@Override
public String apply(FinanceRequest request) {
RestTemplate restTemplate = new RestTemplate();
String url = "https://stock.xueqiu.com/v5/stock/finance/cn/income.json?symbol=" + request.getSymbol() + "&type=all&is_detail=true&count=1";
// String response = restTemplate.getForObject(url, String.class);
// 解析response并生成简单分析,这里仅做示意
return "解析后的数据及简要分析:" + "业务很好,继续保持";
}
public static class FinanceRequest {
@JsonProperty(required = true, value = "公司代码")
@JsonPropertyDescription("上市公司的股票代码")
private String symbol;
public FinanceRequest() {}
public FinanceRequest(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
}
}
实际来说就是三步,
第一步:定义一个类继承实现Functiion的applay方法,在这个方法里边调用工具。
第二步:Spring配置类中注册该函数
第三步:通过withFunction告诉大模型什么时候来调用该函数