第5节:RAG知识库上传,解析和验证
AI Agent 拖拉拽:AI Agent 拖拉拽
本节:第5节:RAG知识库上传,解析和验证
下一节:待更新
以大模型向量存储的方式,提交本地文件到知识库。并在 AI 对话中增强检索知识库符合 AI 对话内容的资料,合并提交问题。
技术方案
以 Spring AI 提供的向量模型处理框架,将上传文件以 TikaDocumentReader 方式进行解析,再通过 TokenTextSplitter 拆分文件。完成这些操作后,在遍历文档添加标记。标记的作用是为了可以区分不同的知识库内容。完成这些动作后,把这些拆解并打标的文件存储到 postgresql 向量库中。
方案流程
文件上传与解析
-
文件上传 : 用户通过前端界面或 API 上传文件,文件可以是多种格式(如 MD、TXT、SQL 等)。
-
文件解析 : 使用 TikaDocumentReader 对上传的文件进行解析,提取出文本内容。TikaDocumentReader 能够处理多种文件格式,并提取出结构化的文本数据。
文本拆分
-
文本拆分 : 使用 TokenTextSplitter 将解析后的文本内容拆分为更小的片段。拆分策略可以根据需求进行调整,例如按句子、段落或固定长度的 token 进行拆分。
-
拆分后的文本片段 : 每个文本片段将作为后续处理和存储的基本单元。
文本标记
-
标记添加 : 在遍历拆分后的文本片段时,为每个片段添加标记。标记的作用是区分不同的知识库内容,例如通过标记标识文件的来源、类别或其他元数据信息。
-
标记格式 : 标记可以是简单的字符串标签,也可以是结构化的 JSON 数据,具体格式根据业务需求确定。
向量化与存储
-
向量化 : 使用 Spring AI 提供的向量模型将标记后的文本片段转换为向量表示。向量化过程将文本内容映射到高维向量空间,便于后续的相似性搜索和检索。
-
存储到PostgreSQL向量库 : 将向量化后的文本片段及其标记存储到 PostgreSQL 向量库中。PostgreSQL 提供了高效的向量索引和搜索功能,能够支持大规模的文本数据存储和检索。
应用场景
-
知识库管理 : 适用于需要管理多个知识库内容的场景,通过标记区分不同来源的知识库。
-
文档检索 : 支持基于内容的文档检索,通过向量化实现高效的相似性搜索。
-
智能问答 : 适用于构建智能问答系统,通过向量化存储和检索实现快速的知识匹配。
-
代码评审 :结合代码库,评审具体的代码块,会更加准确。
-
编程开发 :可用于编程开发中需求理解、代码查询、流程图等。
功能实现
引入组件
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
文件配置
yaml
server:
port: 8090
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://172.27.213.102:5432/ai-chat-bot
username: postgres
password: postgres
type: com.zaxxer.hikari.HikariDataSource
ai:
ollama:
base-url: http://127.0.0.1:11434
embedding:
options:
num-batch: 768
model: nomic-embed-text
# Redis
redis:
sdk:
config:
host: 172.27.213.102
port: 6378
pool-size: 10
min-idle-size: 5
idle-timeout: 30000
connect-timeout: 5000
retry-attempts: 3
retry-interval: 1000
ping-interval: 60000
keep-alive: true
logging:
level:
root: info
config: classpath:logback-spring.xml
向量存储
Spring AI 提供了简单 SimpleVectorStore 实现类,把文档信息缓存到内存中。以及 PgVectorStore 实现类,把文档存储到向量库。
java
@Configuration
public class RAGEmbeddingConfig {
@Bean
public TokenTextSplitter tokenTextSplitter() {
return new TokenTextSplitter();
}
@Bean
public OllamaApi ollamaApi(@Value("${spring.ai.ollama.base-url}") String baseUrl) {
return new OllamaApi(baseUrl);
}
@Bean
public SimpleVectorStore simpleVectorStore( OllamaApi ollamaApi) {
OllamaEmbeddingClient ollamaEmbeddingClient = new OllamaEmbeddingClient(ollamaApi);
ollamaEmbeddingClient.withDefaultOptions(OllamaOptions.create().withModel("nomic-embed-text"));
return new SimpleVectorStore(ollamaEmbeddingClient);
}
@Bean
public PgVectorStore pgVectorStore(OllamaApi ollamaApi, JdbcTemplate jdbcTemplate) {
OllamaEmbeddingClient embeddingClient = new OllamaEmbeddingClient(ollamaApi);
embeddingClient.withDefaultOptions(OllamaOptions.create().withModel("nomic-embed-text"));
return new PgVectorStore(jdbcTemplate, embeddingClient);
}
}
-
向量库的指定,也可以通过实例化的时候通过 withModel 指定。
-
SimpleVectorStore,把知识库缓存到内存。
-
PgVectorStore,把知识库通过 JdbcTemplate 也就是 pgsql 操作,存储到向量库。
-
TokenTextSplitter,用于切割文本的操作。
上传知识库
java
@Test
public void chat() {
String message = "你好王大锤那年人?";
String systemPrompt = """
Use the information from the DOCUMENTS section to provide accurate answers but act as if you knew this information innately.
If unsure, simply state that you don't know.
Another thing you need to note is that your reply must be in Chinese!
DOCUMENTS:
{documents}
""";
simpleVectorStore.accept(loadKnowledgeDocuments());
SearchRequest request = SearchRequest.query(message).withTopK(5);
List<Document> documents = simpleVectorStore.similaritySearch(request);
String documentsCollectors = documents.stream()
.map(Document::getContent)
.collect(Collectors.joining());
Message ragMessage = new SystemPromptTemplate(systemPrompt)
.createMessage(Map.of("documents", documentsCollectors));
ArrayList<Message> messages = new ArrayList<>();
messages.add(new UserMessage(message));
messages.add(ragMessage);
ChatResponse chatResponse = ollamaChatClient.call(
new Prompt(messages, OllamaOptions.create().withModel("deepseek-r1:1.5b"))
);
log.info("test result: {}", JSON.toJSONString(chatResponse));
}
-
在有知识库的加持下,我们提问的信息,就可以从知识库通过向量方式检索出匹配的信息,之后一起作为问题提问。
-
那么,你可以想象下,如果你想做个代码需求的扩展,但你不想梳理你要修改的代码范围,就可以通过知识库检索,来辅助你完成需求的处理。