本文的目的是在5分钟内能把智能问答系统的原理和实践讲明白
代码可执行,复制粘贴即可,可以快速跑起来。
智能问答系统简介
智能问答系统是一种人工智能应用,它能够理解用户提出的问题,并通过自然语言处理技术来分析和理解问题的含义。随后,系统会在其知识库中搜索相关信息,以生成一个或多个可能的答案。这些答案基于系统对问题的理解和知识库中的信息生成。
该系统的关键组成部分包括:
自然语言处理(NLP):这是系统的核心部分,负责理解用户的输入。NLP 涉及分词、词性标注、句法分析和语义理解等多个步骤,确保系统准确地解析用户意图。
知识库:存储了大量的结构化和非结构化数据,为系统提供解答问题所需的知识基础。这可以是事实数据库、规则集合或更复杂的概念框架。
检索与匹配算法:利用高效的搜索策略,在庞大的知识库中快速找到与用户查询最相关的信息片段。此过程可能涉及关键词匹配、向量空间模型或其他高级技术。
答案生成:结合从知识库中获取的数据,以及上下文信息,构建出易于理解且符合逻辑的回答。在某些情况下,还可能需要进行推理才能得出正确结论。
评估与反馈机制:持续监测生成答案的质量,并允许根据用户反馈调整模型参数或更新知识库内容,从而不断提高系统的性能和服务水平。
通过上述组件协同工作,智能问答系统能够在多种场景下发挥作用,比如在线客服支持、虚拟个人助理、教育培训领域等,极大地提升了用户体验和工作效率。
我们可以看到,随着大语言模型的落地,问答系统完全可以使用大语言模型来做,里面的自然语言处理,答案生成部分,就可以用大模型来做,而知识库和检索匹配算法,则可以用RAG检索增强技术来做。
后面的例子,我们使用 spring ai alibaba + 通义千问Qwen api 来构建这个智能问答系统 , qwen有100万免费Token额度,可以快速实现需求。同时,因为qwen 也是个开源的模型,我们可以自己搭建模型来实现免费使用,
Spring AI Alibaba框架介绍
Spring AI Alibaba 是由Spring官方团队维护的一个针对AI工程的应用框架,专为Java开发者设计。它旨在通过标准化不同AI提供者(如OpenAI、Azure、阿里云等)的接口实现,让开发者能够“写一次代码,支持所有模型”,极大减少了迁移和开发的工作量。
此外,它还特别集成了阿里云百炼平台上的大模型服务,包括对话生成、文生图、文生语音等功能,并提供了检索增强生成(RAG)能力以利用私有知识库提高响应质量。这不仅体现了Spring生态系统的设计原则,也融入了阿里云在大规模AI应用中的最佳实践。
这套系统也是开源的,免费用。
通义千问大模型在多项基准测试中超越Llama 3 70B,获广泛认可
通义千问大模型在MMLU、TheoremQA、GPQA等基准测评中表现出色,超越了Llama 3 70B。这些评测指标被Claude和OpenAI广泛认可,能够客观地衡量模型的能力。此外,通义千问还在Hugging Face开源大模型排行榜Open LLM Leaderboard上荣登榜首,进一步证明了其卓越的性能。这一成就彰显了通义千问在处理复杂问题、理解和生成自然语言方面的强大能力,为用户提供了更加准确和可靠的帮助。
另外,在真人参与评测的arena里面,它不仅在思南平台 CompassArena 上仅次于国际知名的GPT和Claude系列,还在 Hugging Face的视觉模型竞技场 https://huggingface.co/spaces/lmarena-ai/chatbot-arena-leaderboard 中稳居中国首位。
检索增强生成(RAG)技术介绍:利用私有知识库提高LLM回复准确性
检索增强生成 (RAG) 是一种结合了检索模型和生成模型的技术,通过从私有知识库中检索相关信息来辅助大型语言模型生成更准确、具体的文本回复。它主要解决了使用大模型时的两个问题:一是减少因缺乏最新或特定信息而产生的“幻觉”现象;二是让模型能够利用企业特有的私有数据进行回答,从而提高回复的精准度与实用性。通过将企业的私有知识库与强大的生成能力相结合,RAG技术确保了输出内容既丰富又贴合实际情况。
检索增强后端代码开发:
为了实现通过检索增强 (RAG) 的方式读取名为“智能问答的问题集.docs”的PDF文件,构建向量索引,并对外提供服务,您需要按照以下步骤操作。这包括环境设置、依赖添加、API密钥配置、代码编写等环节。下面是详细的执行步骤:
1. 硯境准备
JDK版本:确保您的开发环境使用的是 JDK 17 或更高版本。
Spring Boot版本:项目需基于 Spring Boot 3.3.x 版本或以上。
2. 阿里云账号及API密钥申请
访问阿里云百炼页面,登录账号后开通“百炼大模型推理”服务。成功开通后,创建一个新的 API KEY 并妥善保存,用于后续的开发配置中。
3. 配置API KEY
在本地环境变量中配置刚刚获得的API KEY:
export AI_DASHSCOPE_API_KEY=这里替换为实际的API Key值
同时,在
application.properties
文件内增加如下配置行来注入API KEY:
spring.ai.dashscope.api-key: ${AI_DASHSCOPE_API_KEY}
4. 添加仓库与依赖
在项目的pom.xml文件中添加必要的仓库和依赖项,以便能够引用到
spring-ai-alibaba-starter
等相关组件。
<repositories>
<!-- 忽略其他repository定义 -->
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<!-- 更多repository省略... -->
</repositories>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M2.1</version>
</dependency>
<!-- 其他依赖 -->
</dependencies>
5. 编写RAG服务相关代码
RagService类
此部分实现了对指定PDF文档的处理、向量存储以及基于文档内容的查询功能。
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 "SUCCESS";
}
public StreamResponseSpec queryWithDocumentRetrieval(String message) {
return chatClient.prompt().user(message)
.advisors(new DocumentRetrivalAdvisor(retriever, DEFAULT_USER_TEXT_ADVISE)).stream();
}
}
RagController类
负责接收客户端请求并调用相应的服务方法。
@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 input, HttpServletResponse response) {
StreamResponseSpec chatResponse = ragService.queryWithDocumentRetrieval(input);
response.setCharacterEncoding("UTF-8");
return chatResponse.content();
}
@GetMapping("/buildIndex")
public String buildIndex() {
return ragService.buildIndex();
}
}
解释
上述步骤首先设置了正确的开发环境并获取了必要的认证信息(API KEY)。然后,通过添加适当的Maven仓库和依赖项使得项目可以引用所需的Spring AI Alibaba组件。接着,我们定义了一个
RagService
类来处理PDF文件的数据提取与索引建立工作,以及一个简单的控制器
RagController
用来暴露RESTful接口供外部调用。最后,用户可以通过先访问
/buildIndex
端点初始化索引,再利用
/steamChat?input=...
进行实际的聊天式查询。
增强检索的前端代码实现
在你需要构建的项目中,将基于React创建一个简单的前端应用,该应用通过调用后端接口(URL:
http://localhost:8080/ai/steamChat?input=...
)来实现流式数据接收,并显示给用户。此过程包括创建新的React应用程序、配置文件结构以及编写必要的组件代码以支持流输出。
构建项目并填写代码
首先,按照以下步骤创建一个新的React应用并安装所需的依赖:
npx create-react-app frontend
cd frontend
npm install
然后,在你的项目中定义几个关键文件及其内容如下:
public/index.html
确保HTML文档的基础结构正确无误:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stream Chat App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
src/index.js
这是React应用的入口点:
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
这里实现了核心功能——与服务器通信并处理流式响应:
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}`);
if (!response.ok) throw new Error("Network response was not ok");
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;
运行项目
完成以上配置之后,你可以通过执行以下命令启动前端开发服务器:
cd frontend
npm start
这段代码和配置使得你的React应用能够向指定的后端服务发送请求,并且以流的形式读取返回的数据,实时地将其展示给用户。每当有新的字符到达时,页面上的消息区域就会自动更新。此外,还提供了清除消息的功能以便于测试或实际使用中的需求。
版权归原作者 沈询-阿里 所有, 如有侵权,请联系我们删除。