实战 | 基于Spring AI Alibaba快速搭建企业级知识库RAG系统(附完整代码)
在大模型应用落地过程中,知识库RAG(检索增强生成)是解决“知识时效性”和“事实准确性”的核心方案。Spring AI Alibaba作为阿里云官方推出的AI开发框架,能让Java开发者以极低的成本快速构建RAG应用。本文将带大家从零开始,基于Spring AI Alibaba + 通义千问 + Qdrant向量库,实现一个完整的企业级知识库RAG系统。
一、核心技术栈说明
- 基础框架:JDK21 + Spring Boot 3.x + Spring AI 1.1.2
- AI能力:阿里云DashScope(通义千问qwen-max、文本嵌入text-embedding-v3、重排序gte-rerank-v2)
- 向量数据库:Qdrant(轻量级、高性能的向量存储)
- 核心组件:Spring AI Alibaba ReactAgent(智能体框架)
二、项目初始化与配置
2.1 依赖配置(pom.xml)
首先在pom.xml中引入核心依赖,通过BOM统一管理版本,避免依赖冲突:
<properties>
<java.version>21</java.version>
<spring-ai.version>1.1.2</spring-ai.version>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
<fastjson2.version>2.0.61</fastjson2.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.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>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-extensions-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba DashScope Starter(核心AI能力) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Spring AI Alibaba Agent Framework(ReactAgent智能体) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<!-- Qdrant向量库依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-qdrant</artifactId>
</dependency>
<!-- 简化开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
</dependencies>
2.2 核心配置(application.yml)
配置文件主要包含DashScope API密钥、Qdrant向量库连接信息、RAG相关参数:
server:
port: 8080
spring:
application:
name: yuncheng-ai-rag
ai:
dashscope:
# 替换为你的阿里云DashScope API Key(https://dashscope.console.aliyun.com/)
api-key: ${AI_DASHSCOPE_API_KEY:sk-your-api-key}
chat:
options:
model: qwen-max # 对话模型
embedding:
options:
model: text-embedding-v3 # 文本嵌入模型
vectorstore:
qdrant:
host: 127.0.0.1 # Qdrant服务地址
port: 6334 # Qdrant gRPC端口
api-key: test@qdrant # Qdrant API密钥
collection-name: yuncheng-knowledge-base # 向量集合名称
initialize-schema: true # 自动初始化集合
rag:
docs-path: classpath:data/docs/ # 知识库文档存放路径(MD文件)
qdrant-rest-url: http://127.0.0.1:6333 # Qdrant REST API端口
rerank:
model: gte-rerank-v2 # 重排序模型
top-k: 3 # 重排序后保留的顶级结果数
search:
top-k: 10 # 向量检索的顶级结果数
注意:需要将
api-key、host等信息替换为你自己的实际配置,DashScope API Key可在阿里云百炼控制台获取。
三、核心模块实现
3.1 项目结构概览
com.yuncheng.ai.rag
├── common/ # 通用组件(统一响应)
├── agent/ # AI智能体配置
├── controller/ # 接口控制器
├── service/ # 核心业务逻辑
│ ├── DocumentService.java # 文档加载与向量存储
│ ├── RagService.java # 检索与重排序
│ └── RerankService.java # 重排序实现
├── tool/ # 智能体工具
└── YunchengAiRagApplication.java # 启动类
3.2 文档加载与向量存储(DocumentService)
该模块负责加载本地MD文档、文本分块、清空旧数据、将分块后的文本存入Qdrant向量库(自动完成文本嵌入):
package com.yuncheng.ai.rag.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class DocumentService {
private static final Logger logger = LoggerFactory.getLogger(DocumentService.class);
@Value("${rag.qdrant-rest-url}")
private String qdrantRestUrl;
@Value("${spring.ai.vectorstore.qdrant.api-key}")
private String qdrantApiKey;
@Value("${spring.ai.vectorstore.qdrant.collection-name}")
private String collectionName;
@Value("${rag.docs-path}")
private String docsPath;
private final VectorStore vectorStore;
private final ResourcePatternResolver resourcePatternResolver;
private final RestTemplate restTemplate;
public DocumentService(VectorStore vectorStore,
ResourcePatternResolver resourcePatternResolver,
RestTemplate restTemplate) {
this.vectorStore = vectorStore;
this.resourcePatternResolver = resourcePatternResolver;
this.restTemplate = restTemplate;
}
/**
* 重新加载文档:清空旧数据 -> 加载 .md 文件 -> 分块 -> 存入向量库
*
* @return 存入向量库的文档块数量
*/
public int reloadDocuments() throws Exception {
// 1. 清空历史旧数据
clearOldData();
// 2. 加载 .md 文档
List<Document> documents = loadDocuments();
if (documents.isEmpty()) {
logger.warn("No documents found in {}", docsPath);
return 0;
}
// 3. 文本分块(避免单文档过长)
List<Document> chunks = splitDocuments(documents);
// 4. 存入向量库(内部会自动调用嵌入模型生成向量)
if (!chunks.isEmpty()) {
vectorStore.add(chunks);
logger.info("Successfully stored {} chunks into vector store", chunks.size());
}
return chunks.size();
}
/**
* 清空 Qdrant 集合中的所有数据
*/
private void clearOldData() {
try {
String url = qdrantRestUrl + "/collections/" + collectionName + "/points/delete";
HttpHeaders headers = new HttpHeaders();
headers.set("api-key", qdrantApiKey);
headers.setContentType(MediaType.APPLICATION_JSON);
// 空 filter 匹配所有数据
String body = "{\"filter\":{}}";
HttpEntity<String> entity = new HttpEntity<>(body, headers);
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
logger.info("Cleared old data from collection: {}", collectionName);
} catch (Exception e) {
logger.warn("Failed to clear old data (collection might not exist yet): {}", e.getMessage());
}
}
/**
* 从 classpath:data/docs/ 加载所有 .md 文件
*/
private List<Document> loadDocuments() throws Exception {
String pattern = docsPath.endsWith("/") ? docsPath + "*.md" : docsPath + "/*.md";
Resource[] resources = resourcePatternResolver.getResources(pattern);
List<Document> documents = new ArrayList<>();
for (Resource resource : resources) {
try (InputStream is = resource.getInputStream()) {
String content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
String filename = resource.getFilename();
if (content.isBlank()) {
logger.warn("Skipping empty document: {}", filename);
continue;
}
Map<String, Object> metadata = new HashMap<>();
metadata.put("source", filename); // 记录文档来源
documents.add(new Document(content, metadata));
logger.info("Loaded document: {} ({} chars)", filename, content.length());
} catch (Exception e) {
logger.error("Failed to load document: {}", resource.getFilename(), e);
}
}
logger.info("Loaded {} documents total", documents.size());
return documents;
}
/**
* 将文档按 token 进行分块(TokenTextSplitter默认按512token分块)
*/
private List<Document> splitDocuments(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter();
List<Document> chunks = splitter.apply(documents);
logger.info("Split {} documents into {} chunks", documents.size(), chunks.size());
return chunks;
}
}
3.3 检索与重排序(RagService + RerankService)
3.3.1 重排序服务(RerankService)
向量检索的结果可能存在“相关性偏差”,通过DashScope的重排序模型对结果二次筛选,提升回答准确性:
package com.yuncheng.ai.rag.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class RerankService {
private static final Logger logger = LoggerFactory.getLogger(RerankService.class);
private static final String RERANK_URL = "https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank";
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Value("${rag.rerank.model:gte-rerank-v2}")
private String rerankModel;
@Value("${rag.rerank.top-k:3}")
private int defaultTopK;
private final RestTemplate restTemplate;
public RerankService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* 使用 DashScope Reranker 对检索结果进行重排序,仅保留高相关片段
*
* @param query 用户查询
* @param documents 检索到的文档列表
* @param topN 返回的最大文档数
* @return 重排序后的文档列表
*/
@SuppressWarnings("unchecked")
public List<Document> rerank(String query, List<Document> documents, int topN) {
if (documents == null || documents.isEmpty()) {
return List.of();
}
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + apiKey);
List<String> docTexts = documents.stream()
.map(Document::getText)
.toList();
Map<String, Object> body = Map.of(
"model", rerankModel,
"input", Map.of(
"query", query,
"documents", docTexts
),
"parameters", Map.of(
"top_n", topN,
"return_documents", true
)
);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.exchange(
RERANK_URL, HttpMethod.POST, entity, Map.class);
Map<String, Object> responseBody = response.getBody();
if (responseBody == null) {
logger.warn("Rerank API returned null response, falling back to original order");
return fallback(documents, topN);
}
Map<String, Object> output = (Map<String, Object>) responseBody.get("output");
if (output == null) {
logger.warn("Rerank API response missing 'output', falling back");
return fallback(documents, topN);
}
List<Map<String, Object>> results = (List<Map<String, Object>>) output.get("results");
if (results == null || results.isEmpty()) {
logger.warn("Rerank API returned empty results, falling back");
return fallback(documents, topN);
}
List<Document> reranked = new ArrayList<>();
for (Map<String, Object> result : results) {
int index = ((Number) result.get("index")).intValue();
double score = ((Number) result.get("relevance_score")).doubleValue();
// 仅保留相关性分数大于阈值的文档(过滤低相关结果)
if (score > 0.1 && index < documents.size()) {
Document original = documents.get(index);
original.getMetadata().put("rerank_score", score); // 记录重排序分数
reranked.add(original);
logger.debug("Rerank result: index={}, score={}", index, score);
}
}
if (reranked.isEmpty()) {
logger.warn("No documents passed rerank threshold, falling back");
return fallback(documents, topN);
}
logger.info("Reranked {} documents, kept {} high-relevance results",
documents.size(), reranked.size());
return reranked;
} catch (Exception e) {
logger.error("Rerank API call failed, falling back to original order: {}", e.getMessage());
return fallback(documents, topN);
}
}
/**
* 使用默认 topK 进行重排序
*/
public List<Document> rerank(String query, List<Document> documents) {
return rerank(query, documents, defaultTopK);
}
/**
* 回退策略:返回前 topN 个原始文档(保证服务可用性)
*/
private List<Document> fallback(List<Document> documents, int topN) {
return documents.subList(0, Math.min(topN, documents.size()));
}
}
3.3.2 核心检索服务(RagService)
整合向量检索和重排序,返回格式化的知识库上下文,供智能体调用:
package com.yuncheng.ai.rag.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.stereotype.Service;
import java.util.List;
@Service
public class RagService {
private static final Logger logger = LoggerFactory.getLogger(RagService.class);
private static final String NO_RESULT_MESSAGE = "未找到相关信息";
@Value("${rag.search.top-k:10}")
private int searchTopK;
@Value("${rag.rerank.top-k:3}")
private int rerankTopK;
private final VectorStore vectorStore;
private final RerankService rerankService;
public RagService(VectorStore vectorStore, RerankService rerankService) {
this.vectorStore = vectorStore;
this.rerankService = rerankService;
}
/**
* 检索 + 重排序,返回格式化的知识上下文
*
* @param query 用户查询
* @return 格式化的知识库上下文,或 "未找到相关信息"
*/
public String searchAndRerank(String query) {
try {
// 1. 向量检索(从Qdrant获取TopK相似文档)
List<Document> searchResults = vectorStore.similaritySearch(
SearchRequest.builder()
.query(query)
.topK(searchTopK)
.build()
);
if (searchResults == null || searchResults.isEmpty()) {
logger.info("No search results found for query: {}", query);
return NO_RESULT_MESSAGE;
}
logger.info("Vector search returned {} results for query: {}", searchResults.size(), query);
// 2. 重排序,仅保留高相关片段
List<Document> rerankedResults = rerankService.rerank(query, searchResults, rerankTopK);
if (rerankedResults.isEmpty()) {
logger.info("No relevant results after reranking for query: {}", query);
return NO_RESULT_MESSAGE;
}
// 3. 格式化为上下文(带来源信息,方便追溯)
StringBuilder context = new StringBuilder();
for (int i = 0; i < rerankedResults.size(); i++) {
Document doc = rerankedResults.get(i);
String source = doc.getMetadata().getOrDefault("source", "unknown").toString();
context.append("--- 知识片段 ").append(i + 1)
.append(" (来源: ").append(source).append(") ---\n");
context.append(doc.getText()).append("\n\n");
}
logger.info("Returning {} reranked context snippets", rerankedResults.size());
return context.toString().trim();
} catch (Exception e) {
logger.error("Search and rerank failed for query: {}", query, e);
return NO_RESULT_MESSAGE;
}
}
}
3.4 知识库工具定义(KnowledgeBaseSearchTool)
定义供ReactAgent调用的工具类,封装检索逻辑:
package com.yuncheng.ai.rag.tool;
import com.yuncheng.ai.rag.service.RagService;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.tool.annotation.ToolParam;
import java.util.function.BiFunction;
/**
* 知识库搜索工具,供 ReactAgent 调用
* 执行:向量检索 -> 重排序 -> 返回高相关知识片段
*/
public class KnowledgeBaseSearchTool implements BiFunction<KnowledgeBaseSearchTool.Request, ToolContext, String> {
private final RagService ragService;
public KnowledgeBaseSearchTool(RagService ragService) {
this.ragService = ragService;
}
@Override
public String apply(Request request, ToolContext toolContext) {
try {
return ragService.searchAndRerank(request.query());
} catch (Exception e) {
return "知识库检索失败: " + e.getMessage();
}
}
/**
* 工具输入参数,LLM 会以 JSON 对象形式传入
*/
public record Request(
@ToolParam(description = "用户提出的问题或查询关键词") String query
) {}
}
3.5 ReactAgent智能体配置(ReactAgentConfig)
配置AI智能体,定义系统提示词(约束智能体行为),绑定知识库搜索工具:
package com.yuncheng.ai.rag.agent;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.yuncheng.ai.rag.service.RagService;
import com.yuncheng.ai.rag.tool.KnowledgeBaseSearchTool;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class ReactAgentConfig {
private static final String SYSTEM_PROMPT = """
你是一个智能知识库问答助手。当用户提问时,你必须使用 knowledge_base_search 工具从知识库中检索相关信息,然后基于检索结果回答问题。
严格规则:
1. 收到用户问题后,必须先调用 knowledge_base_search 工具获取相关信息。
2. 只能基于工具返回的知识库内容回答问题,禁止编造信息或使用你自身的知识。
3. 如果工具返回"未找到相关信息",你必须明确回复"无法回答"。
4. 回答时保持准确、简洁,可以适当引用知识片段的来源信息。
5. 不要在回答中暴露工具调用细节,直接给出最终答案。
""";
@Bean
@Primary
public ReactAgent ragAgent(ChatModel chatModel, RagService ragService) {
// 构建知识库搜索工具
ToolCallback knowledgeBaseTool = FunctionToolCallback
.builder("knowledge_base_search", new KnowledgeBaseSearchTool(ragService))
.description("从知识库中搜索与用户问题相关的信息。输入用户的问题或查询关键词,返回知识库中最相关的知识片段。当用户提出任何问题时,必须调用此工具。")
.inputType(KnowledgeBaseSearchTool.Request.class)
.build();
return ReactAgent.builder()
.name("rag_agent")
.model(chatModel) // 绑定通义千问模型
.systemPrompt(SYSTEM_PROMPT) // 约束智能体行为
.tools(knowledgeBaseTool) // 绑定知识库搜索工具
.saver(new MemorySaver()) // 内存级对话记忆
.build();
}
}
3.6 接口控制器
3.6.1 文档加载控制器(DocumentController)
提供手动触发文档重新加载的接口:
package com.yuncheng.ai.rag.controller;
import com.yuncheng.ai.rag.common.ToolResponse;
import com.yuncheng.ai.rag.service.DocumentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/rag/docs")
public class DocumentController {
private static final Logger logger = LoggerFactory.getLogger(DocumentController.class);
private final DocumentService documentService;
public DocumentController(DocumentService documentService) {
this.documentService = documentService;
}
/**
* 手动触发文档重新加载
* 清空历史旧数据后,从 classpath:data/docs/ 重新加载所有 .md 文件
*/
@GetMapping("/reload")
public ToolResponse<Integer> reloadDocuments() {
try {
logger.info("Received document reload request");
int chunkCount = documentService.reloadDocuments();
String msg = String.format("文档加载完成,共生成 %d 个知识片段", chunkCount);
logger.info(msg);
return ToolResponse.success(msg, chunkCount);
} catch (Exception e) {
logger.error("Document reload failed", e);
return new ToolResponse<>(false, "文档加载失败: " + e.getMessage(), null);
}
}
}
3.6.2 RAG问答控制器(RagController)
提供流式问答接口,返回基于知识库的回答:
package com.yuncheng.ai.rag.controller;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.alibaba.cloud.ai.graph.streaming.OutputType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.UUID;
@RestController
@RequestMapping("/api/rag")
public class RagController {
private static final Logger logger = LoggerFactory.getLogger(RagController.class);
private final ReactAgent ragAgent;
@Autowired
public RagController(ReactAgent ragAgent) {
this.ragAgent = ragAgent;
}
/**
* 流式问答接口
* ReactAgent 内部会自动调用 knowledge_base_search 工具检索知识库,
* 然后基于检索结果生成答案并流式返回。
*/
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message,
@RequestParam(required = false) String threadId)
throws GraphRunnerException {
final String tid = (threadId == null || threadId.isBlank())
? UUID.randomUUID().toString() : threadId;
logger.info("Received stream request: message={}, threadId={}", message, tid);
RunnableConfig config = RunnableConfig.builder()
.threadId(tid) // 对话线程ID,用于维护对话上下文
.build();
return ragAgent.stream(message, config)
.filter(StreamingOutput.class::isInstance)
.cast(StreamingOutput.class)
.filter(so -> so.getOutputType() == OutputType.AGENT_MODEL_STREAMING)
.map(StreamingOutput::message)
.filter(AssistantMessage.class::isInstance)
.cast(AssistantMessage.class)
.filter(am -> !am.hasToolCalls()) // 过滤工具调用信息,只返回最终回答
.map(AssistantMessage::getText)
.filter(text -> text != null && !text.isEmpty())
.concatWith(Flux.just("[DONE]")) // 流式结束标识
.onErrorResume(e -> {
logger.error("Stream error for threadId={}", tid, e);
return Flux.just("Error: " + e.getMessage(), "[DONE]");
});
}
}
3.7 通用响应封装(ToolResponse)
统一接口返回格式,提升前端对接体验:
package com.yuncheng.ai.rag.common;
import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 统一响应结果封装类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ToolResponse<T> {
/**
* 是否成功
*/
private boolean success;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
/**
* 成功响应 - 无数据
*
* @param message 响应消息
* @return 成功响应对象
*/
public static ToolResponse<Void> success(String message) {
return new ToolResponse<>(true, message, null);
}
/**
* 成功响应 - 有数据
*
* @param message 响应消息
* @param data 响应数据
* @param <T> 数据类型
* @return 成功响应对象
*/
public static <T> ToolResponse<T> success(String message, T data) {
return new ToolResponse<>(true, message, data);
}
/**
* 失败响应
*
* @param message 错误消息
* @return 失败响应对象
*/
public static ToolResponse<Void> error(String message) {
return new ToolResponse<>(false, message, null);
}
/**
* 结构化输出JSON字符串
*
* @return JSON字符串
*/
public String toJSONString() {
return JSON.toJSONString(this);
}
}
3.8 启动类
package com.yuncheng.ai.rag;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class YunchengAiRagApplication {
/**
* 注册RestTemplate Bean(用于调用Qdrant REST API和DashScope重排序接口)
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 应用程序入口点
*
* @param args 命令行参数
*/
public static void main(String[] args) {
SpringApplication.run(YunchengAiRagApplication.class, args);
}
}
四、运行与测试
4.1 准备工作
- 部署Qdrant:可以通过Docker快速部署(
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant)。 - 准备知识库文档:在
src/main/resources/data/docs/目录下放入MD格式的知识库文档。 - 配置API Key:替换
application.yml中的AI_DASHSCOPE_API_KEY为你的阿里云DashScope密钥。
4.2 启动项目
运行YunchengAiRagApplication,启动成功后,先调用文档加载接口:
# 加载知识库文档
curl http://localhost:8080/api/rag/docs/reload
返回示例:
{
"success": true,
"message": "文档加载完成,共生成 25 个知识片段",
"data": 25
}
4.3 测试流式问答
调用流式问答接口,测试知识库回答能力:
# 流式问答(替换为你的问题)
curl http://localhost:8080/api/rag/stream?message=Spring AI Alibaba如何集成Qdrant向量库
接口会流式返回基于知识库的回答,结束时返回[DONE]标识。
五、核心亮点
- 标准化框架:基于Spring AI Alibaba,符合Spring生态开发习惯,降低Java开发者学习成本。
- 双层筛选:向量检索 + 重排序,大幅提升回答准确性。
- 智能体约束:通过系统提示词严格约束智能体行为,避免“幻觉”。
- 流式响应:支持流式输出,提升用户体验。
- 高可用性:重排序服务提供回退策略,保证服务稳定性。
总结
本文基于Spring AI Alibaba实现了一套完整的企业级知识库RAG系统,涵盖了文档加载、文本分块、向量存储、检索重排序、智能体问答等核心环节。整套方案符合Spring生态开发规范,代码可直接落地使用,同时预留了丰富的扩展空间,满足不同企业的个性化需求。
对于Java开发者而言,Spring AI Alibaba极大降低了AI应用的开发门槛,让我们可以聚焦业务逻辑,快速实现大模型应用的落地。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)