当前位置: 首页 > news >正文

Spring AI实现一个简单的问答系统

1. 引言

随着大语言模型(LLM)技术的不断发展,将AI能力集成到企业应用中变得越来越重要。Spring AI是Spring生态系统的最新成员,旨在简化AI服务与Spring应用的集成过程。

本文将详细介绍如何利用Spring AI构建一个简单的问答系统,帮助开发者快速入门AI应用开发。

2. 环境准备

2.1 项目依赖

首先,创建一个Spring Boot项目,并添加必要的依赖

<?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><groupId>org.example</groupId><artifactId>spring-ai-demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><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><repository><name>Central Portal Snapshots</name><id>central-portal-snapshots</id><url>https://central.sonatype.com/repository/maven-snapshots/</url><releases><enabled>false</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.4.2</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-core</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>21</source><target>21</target><encoding>utf-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.2.0</version><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

2.2 配置API密钥

application.yml中配置API密钥

在这里插入图片描述

server:port: 5555spring:ai:openai:api-key: sk-xxxxx # 需要替换为上图所示的硅基流动API密钥base-url: https://api.siliconflow.cn/embedding:options:model: BAAI/bge-m3chat:options:model: deepseek-ai/DeepSeek-V3

注意:为了安全起见,建议通过环境变量注入API密钥,而不是直接硬编码在配置文件中。

3. 核心代码实现

3.1 主应用类

创建Spring Boot应用的入口类:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.util.List;@SpringBootApplication
public class QaApplication {public static void main(String[] args) {SpringApplication.run(QaApplication.class, args);}@Beanpublic ChatClient chatClient(OpenAiChatModel model){return ChatClient.builder(model).build();}@Beanpublic VectorStore vectorStore(EmbeddingModel embeddingModel) {VectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();// 构建测试数据List<Document> documents =List.of(new Document("Hello Spring AI"),new Document("Hello Spring Boot"));// 添加到向量数据库vectorStore.add(documents);return vectorStore;}}

3.2 请求模型

创建一个简单的模型类来封装问题请求:

import lombok.Data;@Data
public class QuestionRequest {private String question;private String sessionId;
}

3.3 问答服务

实现问答核心服务:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class QaService {private final ChatClient chatClient;private final PromptTemplate promptTemplate;@Autowiredpublic QaService(ChatClient chatClient) {this.chatClient = chatClient;// 创建一个提示模板,指导AI如何回答问题this.promptTemplate = new PromptTemplate("""你是一个智能问答助手,请简洁、准确地回答用户的问题。如果你不知道答案,请直接说不知道,不要编造信息。用户问题: {question}回答:""");}public String getAnswer(String question) {// 准备模板参数Map<String, Object> parameters = new HashMap<>();parameters.put("question", question);// 创建提示Prompt prompt = promptTemplate.create(parameters);// 调用AI获取回答return chatClient.prompt(prompt).call().content();}
}

3.4 REST控制器

创建REST API接口,处理问题请求:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/api/qa")
public class QaController {private final QaService qaService;private final ConversationService conversationService;private final KnowledgeBaseQaService knowledgeBaseQaService;@Autowiredpublic QaController(QaService qaService,ConversationService conversationService,KnowledgeBaseQaService knowledgeBaseQaService) {this.qaService = qaService;this.conversationService = conversationService;this.knowledgeBaseQaService = knowledgeBaseQaService;}@PostMapping("/ask")public Map<String, String> askQuestion(@RequestBody QuestionRequest request) {String answer = qaService.getAnswer(request.getQuestion());Map<String, String> response = new HashMap<>();response.put("question", request.getQuestion());response.put("answer", answer);return response;}@PostMapping("/ask-session")public Map<String, String> askSession(@RequestBody QuestionRequest request) {String answer = conversationService.chat(request.getSessionId(),request.getQuestion());Map<String, String> response = new HashMap<>();response.put("question", request.getQuestion());response.put("answer", answer);return response;}@PostMapping("/ask-knowledge")public Map<String, String> askKnowledge(@RequestBody QuestionRequest request) {String answer = knowledgeBaseQaService.getAnswerWithKnowledgeBase(request.getQuestion());Map<String, String> response = new HashMap<>();response.put("question", request.getQuestion());response.put("answer", answer);return response;}}

3.5 简单HTML前端

src/main/resources/static目录下创建一个简单的HTML页面(qa.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AI问答系统</title><style>body {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;}.container {border: 1px solid #ddd;border-radius: 5px;padding: 20px;margin-top: 20px;}.question-form {margin-bottom: 20px;}#question {width: 100%;padding: 10px;margin-bottom: 10px;border: 1px solid #ddd;border-radius: 4px;}button {padding: 10px 15px;background-color: #4CAF50;color: white;border: none;border-radius: 4px;cursor: pointer;}button:hover {background-color: #45a049;}.answer {margin-top: 20px;padding: 15px;background-color: #f9f9f9;border-radius: 4px;white-space: pre-wrap;}.loading {color: #888;font-style: italic;display: none;}</style>
</head>
<body>
<h1>AI问答系统</h1><div class="container"><div class="question-form"><h2>请输入您的问题</h2><textarea id="question" rows="4" placeholder="例如:什么是Spring AI?"></textarea><button id="ask-button">提问</button><p class="loading" id="loading">AI正在思考中,请稍候...</p></div><div class="answer" id="answer-container" style="display:none;"><h2>回答</h2><div id="answer-text"></div></div>
</div><script>document.getElementById('ask-button').addEventListener('click', async function() {const question = document.getElementById('question').value.trim();if (!question) {alert('请输入问题');return;}// 显示加载状态document.getElementById('loading').style.display = 'block';document.getElementById('answer-container').style.display = 'none';try {// 普通模式   /api/qa/ask// 会话模式   /api/qa/ask-session// 知识库模式 /api/qa/ask-knowledgeconst response = await fetch('/api/qa/ask', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ question: question, sessionId: '12345' })});if (!response.ok) {throw new Error('服务器错误');}const data = await response.json();// 显示回答document.getElementById('answer-text').textContent = data.answer;document.getElementById('answer-container').style.display = 'block';} catch (error) {console.error('Error:', error);document.getElementById('answer-text').textContent = '发生错误: ' + error.message;document.getElementById('answer-container').style.display = 'block';} finally {// 隐藏加载状态document.getElementById('loading').style.display = 'none';}});
</script>
</body>
</html>

4. 运行与测试

完成上述代码后,运行Spring Boot应用:

mvn spring-boot:run

或者使用IDE直接运行QaApplication类。

启动后,访问http://localhost:5555/qa.html,即可使用问答系统。在文本框中输入问题,点击"提问"按钮后,系统会将问题发送给AI,并展示回答结果。

在这里插入图片描述

5. 功能扩展

这个基础的问答系统可以通过以下方式进行扩展

5.1 添加对话历史

改进服务,支持多轮对话

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@Service
public class ConversationService {private final ChatClient chatClient;// TODO 此处仅为简单模拟,实际应为数据库或其他存储方式private final Map<String, List<Message>> conversations = new ConcurrentHashMap<>();@Autowiredpublic ConversationService(ChatClient chatClient) {this.chatClient = chatClient;}public String chat(String sessionId, String userMessage) {// 获取或创建会话历史List<Message> messages = conversations.computeIfAbsent(sessionId, k -> new ArrayList<>());// 添加用户消息messages.add(new UserMessage(userMessage));// 创建带有历史上下文的提示Prompt prompt = new Prompt(messages);// 调用AIString response = chatClient.prompt(prompt).call().content();// 保存AI回复messages.add(new AssistantMessage(response));// 管理会话长度,避免超出Token限制if (messages.size() > 10) {messages = messages.subList(messages.size() - 10, messages.size());conversations.put(sessionId, messages);}return response;}public void clearConversation(String sessionId) {conversations.remove(sessionId);}
}

5.2 添加知识库集成

使用向量存储和检索增强生成(RAG)

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.Embedding;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class KnowledgeBaseQaService {private final ChatClient chatClient;private final VectorStore vectorStore;@Autowiredpublic KnowledgeBaseQaService(ChatClient chatClient, VectorStore vectorStore) {this.chatClient = chatClient;this.vectorStore = vectorStore;}public String getAnswerWithKnowledgeBase(String question) {// 在知识库中搜索相关文档List<Document> relevantDocs = vectorStore.similaritySearch(question);// 构建上下文StringBuilder context = new StringBuilder();for (Document doc : relevantDocs) {context.append(doc.getText()).append("\n\n");}// 创建提示模板PromptTemplate promptTemplate = new PromptTemplate("""你是一个智能问答助手。请根据以下提供的信息回答用户问题。如果无法从提供的信息中找到答案,请基于你的知识谨慎回答,并明确指出这是你的一般性了解。参考信息:{context}用户问题: {question}回答:""");// 准备参数Map<String, Object> parameters = new HashMap<>();parameters.put("context", context.toString());parameters.put("question", question);// 创建提示并调用AIPrompt prompt = promptTemplate.create(parameters);return chatClient.prompt(prompt).call().content();}
}

6. 总结

本文详细介绍了如何使用Spring AI创建一个简单的问答系统。通过Spring AI提供的抽象层,我们能够轻松地集成大语言模型,无需深入了解底层API细节。这种方式可以让开发者专注于业务逻辑,同时保持了Spring生态系统的一致性。

相关文章:

  • Java程序员从0学AI(二)
  • C# 使用 OpenCV 基础
  • spark调度系统核心组件SparkContext、DAGSchedul、TaskScheduler介绍
  • 【Fifty Project - D29】
  • 容器环境渗透测试工具(docker渗透测试工具、kubernetes)
  • 在CuPy中使用多节点多GPU环境
  • 2025年医美行业报告60+份汇总解读 | 附 PDF 下载
  • Golang的代码注释规范与实践
  • 【笔试强训day37】
  • 从 0 到 1 选对 AI 自动化平台,深度对比三大AI自动化平台:n8n vs Dify vs Coze(附选型指南与实战案例)
  • 简易的Java制作的c4爆炸倒计时程序
  • 时源芯微|接口滤波与防护电路的设计
  • 【课堂笔记】核方法和Mercer定理
  • 打造高效数据处理利器:用Python实现Excel文件智能合并工具
  • Java EE进阶1:导读
  • 科技赋能·长效治理|无忧树建筑修缮渗漏水长效治理交流会圆满举行!
  • Spring Boot 使用 jasypt配置明文密码加密
  • Canvas SVG BpmnJS编辑器中Canvas与SVG职能详解
  • 《经济日报》深度聚焦|珈和科技携手万果博览荟共筑智慧农业新示范高地 全链赋能蒲江茶果产业数字化转型升级
  • 01-通过纯js理解数据驱动图表概念
  • 渝昆高铁云南段路基土石方工程已完成97%,桥隧施工完成超90%
  • 欧盟宣布解除对叙利亚的经济制裁
  • 昆明市委:今年起连续三年,每年在全市集中开展警示教育
  • 遇见东方:18世纪俄罗斯宫殿中的“中国风”
  • 习近平:推进中国式现代化要继续把制造业搞好
  • 巴基斯坦外长访华是否与印巴局势有关?外交部:此访体现巴方高度重视中巴关系