Agenda:
1)介绍一下AI支持下的智能问答系统有哪些主要模块
2)一个可以跑起来的代码样例,说明怎么用Java构建这个AI智能问答系统
AI人工智能智能问答系统简介
智能问答系统是一种利用人工智能技术理解并回答用户提问的应用。该系统通过自然语言处理技术分析问题的含义,并在知识库中查找相关信息,以生成一个或多个可能的答案。这些答案基于对问题的理解及知识库中的数据生成。智能问答系统适用于多种场景,如在线客服、智能助手和教育辅导等,能够显著提升信息获取速度与用户体验。其关键组成部分包括:自然语言处理(NLP),用于解析问题;知识库,存储了丰富的结构化和非结构化信息;检索和匹配算法,负责从知识库中找出相关资料;答案生成机制,依据找到的信息形成回复;以及评估和反馈模块,不断优化答案质量。
我们可以看到,随着大语言模型的落地,问答系统完全可以使用大语言模型来做,里面的自然语言处理,答案生成部分,就可以用大模型来做,而知识库和检索匹配算法,则可以用RAG检索增强技术来做。
后面的例子,我们使用 spring ai alibaba + 通义千问Qwen api 来构建这个智能问答系统 , qwen有100万免费Token额度,可以快速实现需求。同时,因为qwen 也是个开源的模型,我们可以自己搭建模型来实现免费使用,
Spring AI Alibaba框架介绍
Spring AI Alibaba 是由Spring官方团队维护的一个框架,专为集成阿里云的AI服务设计。它提供了统一的接口,让开发者能够轻松接入多种生成式模型(如对话、文生图等),并通过简单的配置切换不同的AI实现,极大地简化了开发流程。此外,该框架还支持Java RAG(检索增强生成)功能,结合阿里云的最佳实践,使得在企业级应用中快速部署和利用强大的AI能力变得更为便捷。
通义千问大模型:在多基准测试中超越Llama 3 70B
通义千问大模型在多个基准测评中表现出色,超越了Llama 3 70B。它在MMLU、TheoremQA和GPQA等客观评测指标上取得了优异成绩,这些指标被广泛认为是评估语言模型能力的重要标准。此外,在Hugging Face开源大模型排行榜Open LLM Leaderboard上,通义千问大模型更是荣登榜首,展示了其卓越的性能和广泛的应用前景。
另外,在真人参与评测的arena里面,它不仅在思南平台 CompassArena 上仅次于国际知名的GPT和Claude系列,还在 Hugging Face的视觉模型竞技场 https://huggingface.co/spaces/lmarena-ai/chatbot-arena-leaderboard 中稳居中国首位。
检索增强生成:结合检索与生成模型提高文本准确性
检索增强生成(RAG)是一种结合了检索模型和生成模型的技术,旨在通过私有知识库的信息来提高文本生成的准确性。它解决了大模型在使用时常见的两个问题:一是模型可能产生不准确或虚构的答案,即“幻觉”现象;二是由于缺乏对企业自有数据的掌握,导致回答过于泛化而不精准。通过引入私有知识库作为信息源,RAG增强了生成内容的相关性和准确性。
基于检索增强技术的后端代码开发
要通过检索增强(RAG)的方式读取名为“智能问答的问题集.docs”的PDF文件,构建向量索引,并对外提供服务,按照我了解的信息中给出的信息和步骤,可以遵循以下详细流程。
前置要求
- 确保您的JDK版本为17或更高。
- Spring Boot版本应至少为3.3.x。
- 在阿里云上获取通义千问的API Key。这可以通过访问阿里云百炼页面并按照指示完成来实现。
- 配置环境变量
AI_DASHSCOPE_API_KEY
或者直接在项目的application.properties
里设置spring.ai.dashscope.api-key: ${AI_DASHSCOPE_API_KEY}
来指定API Key。
- 将Spring仓库添加到项目配置中,以便能够引用尚未发布到Maven中央仓库的依赖包。
- 添加必要的Spring AI Alibaba依赖至您的项目。
RAG服务的具体实现
1. 修改pom.xml
文件以添加所需依赖
确保包含对spring-ai-alibaba-starter
以及Spring Boot父级项目的正确引用。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M2</version>
</dependency>
...其他依赖...
</dependencies>
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<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>
2. 实现RAGService类
创建一个RagService
类用于处理文档读取、向量存储及查询逻辑。
public class RagService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
private final DashScopeApi dashscopeApi = new DashScopeApi("您的API_KEY");
DocumentRetriever retriever;
public RagService(ChatClient chatClient, EmbeddingModel embeddingModel) {
this.chatClient = chatClient;
vectorStore = new DashScopeCloudStore(dashscopeApi, new DashScopeStoreOptions("问题集"));
retriever = new DashScopeDocumentRetriever(dashscopeApi, DashScopeDocumentRetrieverOptions.builder().withIndexName("问题集").build());
}
public String buildIndex() {
String filePath = "路径/智能问答的问题集.docs";
DocumentReader reader = new DashScopeDocumentCloudReader(filePath, dashscopeApi, null);
List<Document> documentList = reader.get();
vectorStore.add(documentList);
return "索引构建成功";
}
public StreamResponseSpec queryWithDocumentRetrieval(String message) {
return chatClient.prompt().user(message).advisors(new DocumentRetrievalAdvisor(retriever, DEFAULT_USER_TEXT_ADVISE)).stream();
}
}
3. 创建控制器暴露接口
定义一个REST控制器以供外部调用。
@RestController
@RequestMapping("/ai")
public class RagController {
private final RagService ragService;
public RagController(RagService ragService) {
this.ragService = ragService;
}
@GetMapping("/steamChat")
public Flux<String> generate(@RequestParam("input") String message, HttpServletResponse httpResponse) {
StreamResponseSpec response = ragService.queryWithDocumentRetrieval(message);
httpResponse.setCharacterEncoding("UTF-8");
return response.content();
}
@GetMapping("/buildIndex")
public String buildIndex() {
return ragService.buildIndex();
}
}
此方案允许您先通过访问http://localhost:8080/ai/buildIndex
来初始化PDF文件的索引,之后便可通过http://localhost:8080/ai/steamChat?input=您的问题
进行查询。注意调整实际文件路径和API密钥等敏感信息。
增强前端代码以支持流式输出
在构建一个基于React的支持流式输出的前端项目时,首先需要确保后端接口能够返回flux<String>
类型的数据,并且支持CORS(跨源资源共享),以便前端能够顺利访问该接口。本示例将指导你如何创建这样一个简单的聊天应用,其中前端通过发送请求到指定的后端URL http://localhost:8080/ai/steamChat?input=...
来获取流式响应。
构建项目并填写代码
- 初始化React项目首先,你需要安装Node.js和npm(如果尚未安装)。接着使用以下命令行来创建一个新的React应用:
npx create-react-app frontend
cd frontend
npm install
- 编辑HTML模板修改位于
public/index.html
中的文件以适应你的项目需求。这里保持默认配置即可满足基本要求。
- 设置React入口点更新
src/index.js
为应用程序的主要渲染点:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
- 创建主组件编写
src/App.js
作为应用程序的核心布局容器:
import React from 'react';
import ChatComponent from './components/ChatComponent';
function App() {
return (
<div className="App">
<ChatComponent />
</div>
);
}
export default App;
- 实现聊天组件在
src/components/ChatComponent.js
中编写处理用户输入及显示消息逻辑的组件。此组件会向服务器发送GET请求并将接收到的流数据逐块添加至显示区域:
import React, { useState } from 'react';
function ChatComponent() {
const [input, setInput] = useState('');
const [messages, setMessages] = useState('');
const handleInputChange = (event) => {
setInput(event.target.value);
};
const handleSendMessage = async () => {
try {
const response = await fetch(`http://localhost:8080/ai/steamChat?input=${input}`);
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let done = false;
while (!done) {
const { value, done: readerDone } = await reader.read();
done = readerDone;
const chunk = decoder.decode(value, { stream: true });
setMessages((prevMessages) => prevMessages + chunk);
}
// 添加分隔符以区分不同消息
setMessages((prevMessages) => prevMessages + '\n\n---------------------\n\n');
} catch (error) {
console.error('Failed to fetch', error);
}
};
const handleClearMessages = () => {
setMessages('');
};
return (
<div>
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="Enter your message"
/>
<button onClick={handleSendMessage}>Send</button>
<button onClick={handleClearMessages}>Clear</button>
<div>
<h3>Messages:</h3>
<pre>{messages}</pre>
</div>
</div>
);
}
export default ChatComponent;
运行项目
完成以上步骤之后,可以通过下面的命令启动你的React开发服务器:
cd frontend
npm start
这将在本地环境运行你的React应用,并打开浏览器展示结果页面。每当用户点击“Send”按钮时,它会向指定的后端地址发起请求,并实时更新界面上的消息内容。请确保后端服务已经正确配置并且可访问。