spring-ai-alibaba官方 Playground 示例

发布于:2025-07-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

1、Spring AI Alibaba 官方社区开发了一个包含完整 “前端UI+后端实现” 的智能体 Playground 示例,示例使用 Spring AI Alibaba 开发,可以体验聊天机器人、多轮对话、图片生成、多模态、工具调用、MCP集成、RAG知识库等所有框架核心能力。

2、界面

3、准备工作 jdk17  ,

阿里云百炼api-key 对应环境变量 AI_DASHSCOPE_API_KEY

百度云翻译 ak 对应环境变量 BAIDU_TRANSLATE_APP_ID

百度云翻译sk对应环境变量 BAIDU_TRANSLATE_SECRET_KEY

百度云地图ak对应环境变量  BAIDU_MAP_API_KEY

阿里云信息查询服务iqs-search api-key对应环境变量 IQS_SEARCH_API_KEY

4、pom文件内容

<?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>
  
	<artifactId>spring-ai-alibaba-playground</artifactId>
	<groupId>com.alibaba.cloud.ai</groupId>
	<version>0.0.1</version>

	<description>Spring AI Alibaba Playground</description>
	<name>Spring AI Alibaba Playground</name>

	<properties>
		<spring-boot.version>3.4.0</spring-boot.version>
		<spring-ai.version>1.0.0</spring-ai.version>
		<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>

        <!-- Node and npm -->
        <node.version>v20.12.2</node.version>
        <npm.version>10.9.2</npm.version>
        <!-- For playground frontend resource. dist has been placed in the resource directory,
        if you have the problem of secondary development or local front-end project failure to start,
        please set this development to true, and run the "mvn clean package" to repackage the front-end project.
        If you don't modify this configuration, you can compile the frontend and
        copy the frontend resources by executing "mvn clean package -Dnpm.build.skip=false". -->
        <npm.build.skip>true</npm.build.skip>

		<zipkin.version>3.4.3</zipkin.version>
		<micrometr.version>1.5.0-M2</micrometr.version>
		<jackson.version>2.15.0</jackson.version>
		<hibernate.version>6.6.9.Final</hibernate.version>
		<sqlite-jdbc.version>3.49.1.0</sqlite-jdbc.version>
		<knife4j.version>4.6.0</knife4j.version>
		<javacv-platform.version>1.5.9</javacv-platform.version>

		<maven.compiler.source>17</maven.compiler.source>
		<maven.compiler.target>17</maven.compiler.target>
		<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.5.18</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>1.5.18</version>
		</dependency>
		<!-- Spring Boot Starter -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<artifactId>logback-classic</artifactId>
					<groupId>ch.qos.logback</groupId>
				</exclusion>
				<exclusion>
					<artifactId>logback-core</artifactId>
					<groupId>ch.qos.logback</groupId>
				</exclusion>
			</exclusions>
		</dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- Spring AI -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-autoconfigure-mcp-client</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-markdown-document-reader</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-advisors-vector-store</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <!-- Spring AI Alibaba -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-store-analyticdb</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>

        <!-- Swagger API Docs -->
        <dependency>
            <groupId>com.github.xingfudeshi</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>

        <!--DB -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>${sqlite-jdbc.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-community-dialects</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- Observable -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-brave</artifactId>
            <version>${micrometr.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.zipkin.reporter2</groupId>
            <artifactId>zipkin-reporter-brave</artifactId>
            <version>${zipkin.version}</version>
        </dependency>

        <!-- Others -->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>${javacv-platform.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.4.0-jre</version>
        </dependency>

    </dependencies>

	<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>com.alibaba.cloud.ai</groupId>
				<artifactId>spring-ai-alibaba-bom</artifactId>
				<version>${spring-ai-alibaba.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>
		</dependencies>
	</dependencyManagement>

	<build>
		<finalName>app</finalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${spring-boot.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-deploy-plugin</artifactId>
				<version>${maven-deploy-plugin.version}</version>
			</plugin>

            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <version>1.11.0</version>
                <executions>
                    <execution>
                        <id>install node and pnpm</id>
                        <goals>
                            <goal>install-node-and-npm</goal>
                        </goals>
                        <configuration>
                            <skip>${npm.build.skip}</skip>
                            <nodeVersion>${node.version}</nodeVersion>
                            <npmVersion>${npm.version}</npmVersion>
                            <workingDirectory>ui</workingDirectory>
                        </configuration>
                    </execution>

                    <execution>
                        <id>install pnpm</id>
                        <goals>
                            <goal>npm</goal>
                        </goals>
                        <configuration>
                            <skip>${npm.build.skip}</skip>
                            <arguments>install -g pnpm</arguments>
                            <workingDirectory>ui</workingDirectory>
                        </configuration>
                    </execution>

                    <execution>
                        <id>pnpm install</id>
                        <goals>
                            <goal>npm</goal>
                        </goals>
                        <configuration>
                            <skip>${npm.build.skip}</skip>
                            <arguments>exec -- pnpm install</arguments>
                            <workingDirectory>ui</workingDirectory>
                        </configuration>
                    </execution>

                    <execution>
                        <id>pnpm build</id>
                        <goals>
                            <goal>npm</goal>
                        </goals>
                        <phase>generate-resources</phase>
                        <configuration>
                            <skip>${npm.build.skip}</skip>
                            <arguments>exec -- pnpm build</arguments>
                            <workingDirectory>ui</workingDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.2.0</version>
                <executions>

					<!-- Copy frontend dist resource to static dir -->
                    <execution>
                        <id>copy-react-build</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <skip>${npm.build.skip}</skip>
                            <outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>ui/dist</directory>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>

					<!-- Add new execution to copy mcp-lib directory -->
					<execution>
						<id>copy-mcp-lib</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>copy-resources</goal>
						</goals>
						<configuration>
							<outputDirectory>${project.build.directory}/mcp-libs</outputDirectory>
							<resources>
								<resource>
									<directory>${project.basedir}/mcp-libs</directory>
									<filtering>false</filtering>
								</resource>
							</resources>
						</configuration>
					</execution>

					<!-- Add new execution to copy db directory -->
					<execution>
						<id>copy-db</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>copy-resources</goal>
						</goals>
						<configuration>
							<outputDirectory>${project.build.directory}/db</outputDirectory>
							<resources>
								<resource>
									<directory>${project.basedir}/db</directory>
									<filtering>false</filtering>
								</resource>
							</resources>
						</configuration>
					</execution>
				</executions>
			</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>aliyunmaven</id>
			<name>aliyun</name>
			<url>https://maven.aliyun.com/repository/public</url>
		</repository>
	</repositories>

</project>

 5、rag向量库代码解析

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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
 *
 *     http://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.ai.application.config.rag;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SimpleVectorStoreConfiguration {

	@Bean
	CommandLineRunner ingestTermOfServiceToVectorStore(VectorStoreDelegate vectorStoreDelegate) {
		//
		return args -> {
			String type = System.getenv("VECTOR_STORE_TYPE");
			//通过环境变量 VECTOR_STORE_TYPE 判断使用analyticdb向量库还是使用基于内存的向量库
			VectorStoreInitializer initializer = new VectorStoreInitializer();
			//初始化数据到向量库
			initializer.init(vectorStoreDelegate.getVectorStore(type));
		};
	}

	/**
	 * 提供基于内存的向量存储(SimpleVectorStore)
	 * <p>
	 * 依赖 EmbeddingModel(自动注入,Alibaba 的嵌入模型)
	 * @param embeddingModel
	 * @return
	 */
	@Bean
	public VectorStore simpleVectorStore(EmbeddingModel embeddingModel) {
		// 向量存储的简单实现,非常适合用于教育目的
		return SimpleVectorStore.builder(embeddingModel).build();
	}

	@Bean
	public VectorStoreDelegate vectorStoreDelegate(
			@Qualifier("simpleVectorStore") VectorStore simpleVectorStore,
			@Qualifier("analyticdbVectorStore") @Autowired(required = false) VectorStore analyticdbVectorStore
	) {
		//注入基于内存的向量库和 analyticdb
		return new VectorStoreDelegate(simpleVectorStore, analyticdbVectorStore);
	}

}

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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
 *
 *     http://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.ai.application.config.rag;

import java.util.Objects;

import org.springframework.ai.vectorstore.VectorStore;

public class VectorStoreDelegate {

	private VectorStore simpleVectorStore;

	private VectorStore analyticdbVectorStore;

	public VectorStoreDelegate(VectorStore simpleVectorStore, VectorStore analyticdbVectorStore) {
		this.simpleVectorStore = simpleVectorStore;
		this.analyticdbVectorStore = analyticdbVectorStore;
	}

	public VectorStore getVectorStore(String vectorStoreType) {

		if (Objects.equals(vectorStoreType, "analyticdb") && analyticdbVectorStore != null) {
			return analyticdbVectorStore;
		}

		return simpleVectorStore;
	}
}

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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
 *
 *     http://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.ai.application.config.rag;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.ai.document.Document;
import org.springframework.ai.reader.markdown.MarkdownDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;

/**
 * @author yuluo
 * @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
 */
public class VectorStoreInitializer {

	private final Logger logger = LoggerFactory.getLogger(VectorStoreInitializer.class);

	public void init(VectorStore vectorStore) throws Exception {
		//初始化数据至向量库
		//读取markdown文件
		List<MarkdownDocumentReader> markdownDocumentReaderList = loadMarkdownDocuments();

		int size = 0;
		if (markdownDocumentReaderList.isEmpty()) {
			logger.warn("No markdown documents found in the directory.");
			return;
		}

		logger.debug("Start to load markdown documents into vector store......");
		for (MarkdownDocumentReader markdownDocumentReader : markdownDocumentReaderList) {
//			TokenTextSplitter是TextSplitter它使用 CL100K_BASE 编码根据Tokens计数将文本拆分为块
//
//			defaultChunkSize:每个文本块的目标大小(以 tokens 为单位)(默认值:800)。
//
//			minChunkSizeChars:每个文本块的最小大小(以字符为单位)(默认值:350)。
//
//			minChunkLengthToEmbed:要包含的 chunk 的最小长度(默认值:5)。
//
//			maxNumChunks:从文本生成的最大块数(默认值:10000)。
//
//			keepSeparator:是否在块中保留分隔符(如换行符)(默认:true)。

			List<Document> documents = new TokenTextSplitter(2000, 1024, 10, 10000, true).transform(markdownDocumentReader.get());
			size += documents.size();

			// 拆分 documents 列表为最大 20 个元素的子列表  也就是每次最多向向量库中添加20条数据
			for (int i = 0; i < documents.size(); i += 20) {
				int end = Math.min(i + 20, documents.size());
				List<Document> subList = documents.subList(i, end);
				vectorStore.add(subList);
			}
		}
		logger.debug("Load markdown documents into vector store successfully. Load {} documents.", size);
	}

	private List<MarkdownDocumentReader> loadMarkdownDocuments() throws IOException, URISyntaxException {
		List<MarkdownDocumentReader> readers;

		// 首先检查jar包当前运行目录是否存在markdown文件
		Path currentDirPath = Paths.get(System.getProperty("user.dir"), "rag", "markdown");

		if (Files.exists(currentDirPath) && Files.isDirectory(currentDirPath)) {
			logger.debug("Found markdown directory in current running directory: {}", currentDirPath);

			try (Stream<Path> paths = Files.walk(currentDirPath)) {
				List<Path> markdownFiles = paths.filter(Files::isRegularFile)
						.filter(path -> path.toString().endsWith(".md"))
						.collect(Collectors.toList());

				if (!markdownFiles.isEmpty()) {
					logger.debug("Loading {} markdown files from current directory", markdownFiles.size());
					readers = markdownFiles.stream()
							.map(path -> {
								String filePath = path.toAbsolutePath().toString();
								return new MarkdownDocumentReader("file:" + filePath);
							})
							.collect(Collectors.toList());
					return readers;
				} else {
					logger.debug("No markdown files found in current directory, falling back to resources");
				}
			}
		} else {
			logger.debug("Markdown directory not found in current directory, falling back to resources");
		}

		// 如果当前运行目录没有找到,则从resources目录加载
		Path markdownDir = Paths.get(getClass().getClassLoader().getResource("rag/markdown").toURI());
		logger.debug("Loading markdown files from resources directory: {}", markdownDir);

		try (Stream<Path> paths = Files.walk(markdownDir)) {
			readers = paths.filter(Files::isRegularFile)
					.filter(path -> path.toString().endsWith(".md"))
					.map(path -> {
						String fileName = path.getFileName().toString();
						String classpathPath = "classpath:rag/markdown/" + fileName;
						return new MarkdownDocumentReader(classpathPath);
					})
					.collect(Collectors.toList());
		}

		return readers;
	}

}

6、源代码地址

https://github.com/springaialibaba/spring-ai-alibaba-examples