更新 SpringAI/0_使用SpringAI接入AI模型.md

This commit is contained in:
8ga 2025-03-17 11:04:44 +08:00
parent 8bbefca044
commit 46f1cb0d4f

View File

@ -120,7 +120,7 @@ Spring AI 支持 spring boot 3.2.x 及更高版本对JDK的最低要求是JDK
</repositories>
```
以千问举例,引入阿里灵积DashScope平台的starter这官方适配Spring AI的依赖包版本和 Spring AI 的版本是一致的。
以千问举例引入阿里的starter这官方适配Spring AI的依赖包版本和 Spring AI 的版本是一致的。
```
<dependency>
@ -130,27 +130,36 @@ Spring AI 支持 spring boot 3.2.x 及更高版本对JDK的最低要求是JDK
</dependency>
```
申请灵积平台的 API Keyhttps://dashscope.console.aliyun.com/apiKey添加相关配置
申请 API Keyhttps://bailian.console.aliyun.com/?apiKey=1#/api-key添加相关配置
```yml
spring:
ai:
dash-scope: # 这是阿里的配置根路径
api-key: xxx
```
指定聊天模型
```yml
spring:
ai:
dash-scope:
api-key: xxx
chat:
options:
model: qwen-max # 使用的模型
model: qwen-max # 阿里云的文档中有提供模型名称
```
### 使用ChatClient发送消息示例
1. 引入ChatModel可以切换不同的AI模型
1. 引入ChatModel
```java
// 如果需要根据名称注入则可以指定为dashScopeChatModel
private final ChatModel chatModel;
```
2. 同步返回答案
2. 阻塞式传输
```java
@GetMapping("chat")
@ -163,7 +172,7 @@ public String chat(@RequestParam String prompt) {// 用户输入的prompt
}
```
3. 流式返回答案
3. 流式传输
```java
@GetMapping(value = "chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@ -189,14 +198,196 @@ ChatMermory仅仅是一个存储和获取历史对话消息的接口而Messag
```java
// 这里用InMemoryChatMemory做示例
private static final ChatMemory chatMemory = new InMemoryChatMemory();
// 从历史记录里取6条对话消息一起发送至模型的API。
// 历史消息也是算在这一次对话Token消耗的要关注Token膨胀的问题
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, sessionId, 6);
ChatClient.create(chatModel)
.prompt()
.user(prompt)
.advisors(messageChatMemoryAdvisor)
// 从历史记录里取6条对话消息一起发送至模型的API。
// 历史消息也是算在这一次对话Token消耗的要关注Token膨胀
.advisors(new MessageChatMemoryAdvisor(chatMemory, sessionId, 6))
.stream()
.content()
.map(chatResponse -> ServerSentEvent.builder(chatResponse).event("message").build());
```
### ETL
ETL的全称是Extract, Transform, Load即抽取、转换、加载。我们可以利用ETL框架 [Apache Tika](https://tika.apache.org/2.9.0/formats.html),将文档(.pdf .xlsx .docx .pptx .md .json等导入至向量数据库让AI模型能够从向量数据库中检索并生成答案。
**DocumentReader**负责读取文档:
- JsonReader读取JSON
- TextReader读取text文档
- PagePdfDocumentReader读取PDF
- TikaDocumentReader读取各种文件.pdf .xlsx .docx .pptx .md .json都支持
**DocumentTransformer**用于处理文档:
- TextSplitter文档切割成小块
- ContentFormatTransformer将文档转换成键值对
- SummaryMetadataEnricher使用大模型总结文档
- KeywordMetadataEnricher使用大模型提取文档关键词
**DocumentWriter**负责文档写入:
- VectorStore写入到向量数据库
- FileDocumentWriter写入到文件
<img width='80%' src='https://www.jarcheng.top/blog/assets/etl-pipeline-tlEpEE9G.jpg'>
引入相关依赖
```xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>
```
Document对象是ETL的核心它包含了文档的元数据和内容。
#### 读取
1. 从输入流读取
```java
// 适合前端上传 MultipartFile 的场景
Resource resource = new InputStreamResource(file.getInputStream());
List<Document> documents = new TikaDocumentReader(resource).read();
```
2. 从本地文件读取
```java
Resource resource = new FileSystemResource("D:\\xxx.pdf");
List<Document> documents = new TikaDocumentReader(resource).read();
```
3. 从URL读取
```java
Resource resource = new UrlResource("http://oss.com/xxx.pdf");
List<Document> documents = new TikaDocumentReader(resource).read();
```
#### 转换
内容转换:
- TokenTextSplitter 可以把内容切割成更小的块在RAG的时候可以提升响应速度、节省Token。
- ContentFormatTransformer 可以把元数据的内容变成字符串键值对。
元数据转换:
- SummaryMetadataEnricher 使用大模型总结文档,在元数据里增加一个**summary**字段。
- KeywordMetadataEnricher 使用大模型提取文档关键词,在元数据里面增加一个**keywords**字段。
```java
List<Document> documents = new TikaDocumentReader(resource).read();
// 这里示例用 TokenTextSplitter 分块
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
```
#### 存储
这里需要引入一个新的组件**向量数据库**VectorStore这是AI记忆的核心组件。前面提到的**ChatMemory**属于短期记忆的组件,一般只在聊天对话的上下文中生效。而**VectorStore**是持久化存储的也就是大家常说的AI知识库。
什么是向量?我这里贴一段通义千问的回答:
> 向量在向量数据库中指的是数学意义上的向量,即一维数组,它可以包含实数或复数。但在计算机科学和信息技术领域,特别是在机器学习、人工智能以及数据检索的上下文中,向量通常是指特征向量。这些向量用来表示数据点或对象的特征,每个元素代表一个特定的特征值。
例如,在文本处理中,文档可以用词频-逆文档频率TF-IDF向量或者词嵌入如Word2Vec或GloVe模型生成的向量来表示在图像识别中图像可以转换为一个描述其视觉特征的向量在推荐系统中用户偏好和物品属性也可以被编码成向量形式。
向量数据库就是专门设计用来存储、索引和查询这些高维度向量数据的数据库系统。它们优化了相似度搜索(比如通过计算向量之间的距离,如欧氏距离、余弦相似度等),使得能够快速找到与给定向量最接近的数据点。这种能力对于实现诸如图像搜索、语音识别、自然语言处理等任务非常有用。
还记得前面提到的**Embedding**嵌入模型这个组件就是SpringAI框架中用于把文档、音视频转换成向量的。向量化以后可以使用向量存储数据库进行存储下面用**RedisStack**来进行示例。
##### 引入redis相关依赖
```xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
```
##### 配置连接参数
```yml
spring:
data:
redis:
host: 地址
port: 端口
password: 密码
repositories:
enabled: false
```
如果项目本身也用到了redis做为缓存或者分布式锁可能会导致配置冲突可以排除RedisVectorStoreAutoConfiguration手动配置来规避。
```java
@Configuration
@EnableAutoConfiguration(exclude = {RedisVectorStoreAutoConfiguration.class})
@EnableConfigurationProperties({RedisVectorStoreProperties.class})
@AllArgsConstructor
public class RedisVectorConfig {
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel,
RedisVectorStoreProperties properties,
RedisConnectionDetails redisConnectionDetails) {
RedisVectorStore.RedisVectorStoreConfig config =
RedisVectorStore.RedisVectorStoreConfig.builder().withIndexName(properties.getIndex()).withPrefix(properties.getPrefix()).build();
return new RedisVectorStore(config, embeddingModel,
new JedisPooled(redisConnectionDetails.getStandalone().getHost(),
redisConnectionDetails.getStandalone().getPort()
, redisConnectionDetails.getUsername(),
redisConnectionDetails.getPassword()),
properties.isInitializeSchema());
}
}
```
##### 声明Embedding的模型
```yml
dash-scope:
embedding:
options:
model: text-embedding-v2
```
##### 使用示例
注入模型:
```java
private final EmbeddingModel embeddingModel;
```
向量化:
```
public void embedding() {
float[] embed = embeddingModel.embed("Hello World");
}
```
向量化存储文档
注入VectorStore组件
```java
private final VectorStore vectorStore;
```
**vectorStore.add**会自动调用embeddingModel完成向量化并存储
```java
List<Document> documents = new TikaDocumentReader(resource).read();
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
vectorStore.add(splitDocuments);
```