example: add rag application example (#3743)

Signed-off-by: yuluo-yx <yuluo08290126@gmail.com>
pull/3755/head
YuLuo 10 months ago committed by GitHub
parent fe7a28e6ec
commit f0bb69b15b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -24,7 +24,7 @@
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -0,0 +1,22 @@
# Spring Cloud Alibaba AI Text Embedding
`TongYiController` 接受一个 HTTP GET 请求 `http://localhost:8080/ai/audio`
`controller` 将会调用 `TongYiService` 中的 `genAudio` 方法,完成服务请求得到响应。
有一个可选的 `text` 参数其默认值为“Spring Cloud Alibaba AI 框架!”。 请求响应来自 Alibaba TongYi Text Embedding 服务。
## 构建和运行
1. 修改配置文件 `application.yml` 中的 apikey 为有效的 apikey
2. 通过 IDE 或者 `./mvnw spring-boot:run` 运行应用程序。
## 访问接口
使用 curl 工具或者使用浏览器对接口发起请求:
```shell
$ curl http://localhost:8080/ai/textEmbedding
# Response:
为一组向量集合
```

@ -0,0 +1,126 @@
# Spring Cloud Alibaba AI RAG Example
This sample describes how to implement a RAG (Retrieval Augmented Generation) application using SCA AI and Spring AI RedisVector Store.
> RAG is a generative model based on retrieval, which combines retrieval and generation to produce more accurate and diverse texts.
> SCA AI: Spring Cloud Alibaba AI, adapting TongYi LLM big model through Spring AI API.
> Spring AI: The Spring AI project aims to simplify the development of applications that include artificial intelligence features and avoid unnecessary complexity.
> Spring AI RedisVector Store: Redis extends the core functionality of Redis OSS to allow Redis to be used as a vector database. Spring AI provides the RedisVector Store adapter.
> Project Code Address: [spring-cloud-ai-rag-example](https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example)
## 1. Environmental preparation
Use Docker Compose to deploy a Redis service to store vector data.
```yaml
version: '3.8'
services:
redis:
image: redis/redis-stack-server
container_name: redis
hostname: redis
ports:
- 6379:6379
```
Start with `docker compose up -d`, and then you can `docker ps | grep redis` check to see if the container is running properly.
## 2. Project dependency
> This project introduces `spring-cloud-alibaba-ai-starter` and `spring-ai-redis-spring-boot-starter` realizes RAG application.
You need to introduce the following dependencies in the POM. XML:
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-spring-boot-starter</artifactId>
<version>${spring.ai.version}</version>
</dependency>
```
## 3. Configuration
Configure the following information in the application. Yml:
```yaml
spring:
ai:
vectorstore:
redis:
index: peer
prefix: peer
```
## 4. Write the code
The `loader` classes are as follows:
```java
@Override
public void run(ApplicationArguments args) throws Exception {
Map<String, Object> indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex());
int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0"));
if (numDocs > 20000) {
logger.info("Embeddings already loaded. Skipping");
return;
}
Resource file = data;
if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) {
GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream());
file = new InputStreamResource(inputStream, "beers.json.gz");
}
logger.info("Creating Embeddings...");
JsonReader loader = new JsonReader(file, KEYS);
vectorStore.add(loader.get());
logger.info("Embeddings created.");
}
```
The `Service` classes are as follows:
```java
public Generation retrieve(String message) {
SearchRequest request = SearchRequest.query(message).withTopK(topK);
List<Document> docs = store.similaritySearch(request);
Message systemMessage = getSystemMessage(docs);
UserMessage userMessage = new UserMessage(message);
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = client.call(prompt);
return response.getResult();
}
private Message getSystemMessage(List<Document> similarDocuments) {
String documents = similarDocuments.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n"));
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt);
return systemPromptTemplate.createMessage(Map.of("documents", documents));
}
```
## 5. Run and verify
You can start the SprigBoot main class and then use a browser to access:
```shell
# request params is promptthe default valueWhat ber pairs well with smoked meats?"
http://localhost:8080/rag/chat
```
To experience the RAG application.

@ -0,0 +1,126 @@
# Spring Cloud Alibaba AI RAG Example
本示例介绍如何使用 SCA AI 和 Spring AI RedisVectorStore 实现 RAGRetrieval Augmented Generation应用。
> RAG 是一个基于检索的生成模型,它将检索和生成结合在一起,以生成更加准确和多样化的文本。
> SCA AI: Spring Cloud Alibaba AI, 通过 Spring AI API 适配 TongYi LLM 大模型。
> Spring AI: Spring AI项目旨在简化包含人工智能功能的应用程序的开发避免不必要的复杂性。
> Spring AI RedisVectorStore: Redis 扩展了 Redis OSS 的核心功能,允许将 Redis 用作矢量数据库Spring AI 提供了 RedisVectorStore 适配器。
> 项目代码地址:[spring-cloud-ai-rag-example](https://github.com/alibaba/spring-cloud-alibaba/tree/2023.x/spring-cloud-alibaba-examples/ai-example/spring-cloud-ai-rag-example)
## 1. 环境准备
使用 Docker Compose 部署一个 Redis 服务,用于存储向量数据。
```yaml
version: '3.8'
services:
redis:
image: redis/redis-stack-server
container_name: redis
hostname: redis
ports:
- 6379:6379
```
使用 `docker compose up -d` 启动,然后您可以通过 `docker ps | grep redis` 查看容器是否正常运行。
## 2. 项目依赖
> 本项目通过引入 `spring-cloud-alibaba-ai-starter``spring-ai-redis-spring-boot-starter` 实现 RAG 应用。
您需要在 pom.xml 中引入如下依赖:
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-spring-boot-starter</artifactId>
<version>${spring.ai.version}</version>
</dependency>
```
## 3. 配置
在 application.yml 中配置如下信息:
```yaml
spring:
ai:
vectorstore:
redis:
index: peer
prefix: peer
```
## 4. 编写代码
`loader` 类如下所示:
```java
@Override
public void run(ApplicationArguments args) throws Exception {
Map<String, Object> indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex());
int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0"));
if (numDocs > 20000) {
logger.info("Embeddings already loaded. Skipping");
return;
}
Resource file = data;
if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) {
GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream());
file = new InputStreamResource(inputStream, "beers.json.gz");
}
logger.info("Creating Embeddings...");
JsonReader loader = new JsonReader(file, KEYS);
vectorStore.add(loader.get());
logger.info("Embeddings created.");
}
```
`Service` 类如下所示:
```java
public Generation retrieve(String message) {
SearchRequest request = SearchRequest.query(message).withTopK(topK);
List<Document> docs = store.similaritySearch(request);
Message systemMessage = getSystemMessage(docs);
UserMessage userMessage = new UserMessage(message);
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = client.call(prompt);
return response.getResult();
}
private Message getSystemMessage(List<Document> similarDocuments) {
String documents = similarDocuments.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n"));
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt);
return systemPromptTemplate.createMessage(Map.of("documents", documents));
}
```
## 5. 运行并验证
您可以通过启动 SprigBoot 主类,之后使用浏览器访问:
```shell
# 参数为 prompt默认值为What ber pairs well with smoked meats?"
http://localhost:8080/rag/chat
```
来体验 RAG 应用。

@ -0,0 +1,9 @@
version: '3.8'
services:
redis:
image: redis/redis-stack-server
container_name: redis
hostname: redis
ports:
- 6379:6379

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2023-2024 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-ai-rag-example</artifactId>
<name>Spring Cloud Starter Alibaba AI RAG Example</name>
<description>Example build RAG Application By Spring Cloud Alibaba AI</description>
<packaging>jar</packaging>
<properties>
<spring.ai.version>0.8.1</spring.ai.version>
<redis.jedis.version>5.1.0</redis.jedis.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-spring-boot-starter</artifactId>
<version>${spring.ai.version}</version>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.jedis.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>

@ -0,0 +1,41 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.example.ai.rag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author yuluo
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/
@SpringBootApplication
public class RAGApplication {
private static final Logger logger = LoggerFactory.getLogger(RAGApplication.class);
public static void main(String[] args) {
SpringApplication.run(RAGApplication.class, args);
logger.info("RAGApplication started successfully.");
}
}

@ -0,0 +1,50 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.example.ai.rag.controller;
import com.alibaba.cloud.example.ai.rag.service.RAGService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
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;
/**
* @author yuluo
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/
@CrossOrigin
@RestController
@RequestMapping("/rag")
public class RAGController {
@Autowired
private RAGService ragService;
@GetMapping("/chat")
public String chatMessage(@RequestParam(value = "prompt",
defaultValue = "What ber pairs well with smoked meats?") String prompt) {
return ragService.retrieve(prompt)
.getOutput()
.getContent();
}
}

@ -0,0 +1,83 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.example.ai.rag.loader;
import java.util.Map;
import java.util.Objects;
import java.util.zip.GZIPInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
/**
* @author yuluo
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/
@Component
public class RAGDataLoader implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(RAGDataLoader.class);
private static final String[] KEYS = { "name", "abv", "ibu", "description" };
@Value("classpath:/data/beers.json.gz")
private Resource data;
private final RedisVectorStore vectorStore;
private final RedisVectorStoreProperties properties;
public RAGDataLoader(RedisVectorStore vectorStore, RedisVectorStoreProperties properties) {
this.vectorStore = vectorStore;
this.properties = properties;
}
@Override
public void run(ApplicationArguments args) throws Exception {
Map<String, Object> indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex());
int numDocs = Integer.parseInt((String) indexInfo.getOrDefault("num_docs", "0"));
if (numDocs > 20000) {
logger.info("Embeddings already loaded. Skipping");
return;
}
Resource file = data;
if (Objects.requireNonNull(data.getFilename()).endsWith(".gz")) {
GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream());
file = new InputStreamResource(inputStream, "beers.json.gz");
}
logger.info("Creating Embeddings...");
JsonReader loader = new JsonReader(file, KEYS);
vectorStore.add(loader.get());
logger.info("Embeddings created.");
}
}

@ -0,0 +1,85 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.example.ai.rag.service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.Generation;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
/**
* @author yuluo
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
*/
@Service
public class RAGService {
@Value("classpath:/prompts/system-qa.st")
private Resource systemBeerPrompt;
@Value("${topk:10}")
private int topK;
private final ChatClient client;
private final VectorStore store;
public RAGService(ChatClient client, VectorStore store) {
this.client = client;
this.store = store;
}
public Generation retrieve(String message) {
SearchRequest request = SearchRequest.query(message).withTopK(topK);
List<Document> docs = store.similaritySearch(request);
Message systemMessage = getSystemMessage(docs);
UserMessage userMessage = new UserMessage(message);
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = client.call(prompt);
return response.getResult();
}
private Message getSystemMessage(List<Document> similarDocuments) {
String documents = similarDocuments.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n"));
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemBeerPrompt);
return systemPromptTemplate.createMessage(Map.of("documents", documents));
}
}

@ -0,0 +1,28 @@
#
# Copyright 2023-2024 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
server:
port: 8081
spring:
application:
name: sca-ai-rag-example
ai:
vectorstore:
redis:
index: peer
prefix: peer

@ -0,0 +1,7 @@
You're assisting with questions about products in a beer catalog.
Use the information from the DOCUMENTS section to provide accurate answers.
The answer involves referring to the ABV or IBU of the beer, include the beer name in the response.
If unsure, simply state that you don't know.
DOCUMENTS:
{documents}

@ -52,7 +52,8 @@
<module>integrated-example/integrated-praise-consumer</module>
<module>integrated-example/integrated-common</module>
<module>integrated-example/integrated-frontend</module>
<module>spring-cloud-ai-example</module>
<module>ai-example/spring-cloud-ai-example</module>
<module>ai-example/spring-cloud-ai-rag-example</module>
</modules>

@ -64,7 +64,6 @@ import org.springframework.context.annotation.Bean;
TongYiImagesProperties.class,
TongYiAudioSpeechProperties.class,
TongYiConnectionProperties.class,
TongYiConnectionProperties.class,
TongYiTextEmbeddingProperties.class
})
public class TongYiAutoConfiguration {

Loading…
Cancel
Save