耄大厨——AI厨师智能体(2-RAG知识库)
文章目录
RAG知识库集成
RAG的基本概念
什么是RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索技术和Al内容生成的混合架构,
可以解决大模型的知识时效性限制和幻觉问题。
从技术角度看,R八G在大语言模型生成回答之前,会先从外部知识库中检索相关信息,然后将这些检索到的内容
作为额外上下文提供给模型,引导其生成更准确、更相关的回答。
RAG工作流程
1.文档收集与切割
文档收集:从各种来源(网页、PDF、数据库等)收集原始文档
文档预处理:清洗、标准化文本格式
文档切割:将长文档分割成适当大小的片段(俗称chunks)
- 基于固定大小(如512个token)
- 基于语义边界(如段落、章节)
- 基于递归分割策略(如递归字符n-gram切割)

2. 向量转换与存储
向量转换:使用Embedding模型将文本块转换为高维向量表示,可以捕获到文本的语义特征
向量存储:将生成的向量和对应文本存入向量数据库,支持高效的相似性搜索

3. 文档过滤与存储
查询处理:将用户问题也转换为向量表示
过滤机制:基于元数据、关键词或自定义规则进行过滤
相似度搜索:在向量数据库中查找与问题向量最相似的文档块,常用的相似度搜索算法有余弦相似度、欧氏距离
等
上下文组装:将检索到的多个文档块组装成连贯上下文

4.查询增强与关联
提示词组装:将检索到的相关文档与用户问题组合成增强提示词
上下文融合:大模型基于增强提示生成回答
源引用:在回答中添加信息来源引用
后处理:格式化、摘要或其他处理以优化最终输出

完整工作流程

SpringAI + 本地知识库
Spring Al框架为我们实现RAG提供了全流程的支持,参考Spring Al和Spring Al Alibaba的官方文档。
由于是第一个RAG程序,我们参考标准的RAG开发步骤并进行一定的简化,来实现基于本地知识库的AI厨师智能体的问答应用。
标准的RAG开发步骤:
- 文档收集和切割
- 向量转换和存储
- 切片过滤和检索
- 查询增强和关联
简化后的RAG开发步骤:
- 文档准备
- 文档读取
- 向量转换和存储
- 查询增强
1.文档准备
Anduin2017/HowToCook: 程序员在家做饭方法指南
Gar-b-age/CookLikeHOC: 🥢像老乡鸡🐔那样做饭
2. 文档读取
首先,我们要对自己准备好的知识库文档进行处理,然后保存到向量数据库中。这个过程俗称TL(抽取、转
换、加载),Spring Al提供了对ETL的支持,参考官方文档。
ETL的3大核心组件,按照顺序执行:
- DocumentReader:读取文档,得到文档列表
- DocumentTransformer:转换文档,得到处理后的文档列表
- DocumentWriter:将文档列表保存到存储中(可以是向量数据库,也可以是其他存储)

引入依赖
Spring Al提供了很多种DocumentReaders,用于加载不同类型的文件。

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
编写文档加载器
在根目录下新建rag包,编写文档加载器类CookerAppDocumentLoader,负责读取所有PDF文档并转
换为Document列表。代码如下:
/**
* 知识库文件加载
*
* @author: cheng fu
**/
@Component
@Slf4j
public class CookerAppDocumentLoader {
public final ResourcePatternResolver resourcePatternResolver;
CookerAppDocumentLoader(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
public List<Document> loadPDFs() {
List<Document> allDocuments = new ArrayList<>();
try {
Resource[] resources = resourcePatternResolver.getResources("classpath:document/*pdf");
for (Resource resource : resources) {
String filename = resource.getFilename();
PdfDocumentReaderConfig config = PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.build())
.withPagesPerDocument(1)
.build();
PagePdfDocumentReader reader = new PagePdfDocumentReader(resource, config);
allDocuments.addAll(reader.get());
}
} catch (IOException e) {
log.error("Document loader filed",e);
}
return allDocuments;
}
}
3. 向量转换与存储
为了实现方便,我们先使用Spring Al内置的、基于内存读写的向量数据库SimpleVectorStore来保存文档。
SimpleVectorStore实现了VectorStore接口,而VectorStore接口集成了DocumentWriter,.所以具备文档写入能
力。如图:
@Configuration
@Slf4j
public class CookerAppVectorStoreConfig {
@Resource
public CookerAppDocumentLoader cookerAppDocumentLoader;
@Bean
VectorStore cookerAppVectorStore(EmbeddingModel dashscopeEmbeddingModel) {
SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(dashscopeEmbeddingModel)
.build();
//加载文档
List<Document> documents = cookerAppDocumentLoader.loadPDFs();
simpleVectorStore.add(documents);
return simpleVectorStore;
}
}
4. 查询增强
Spring Al通过Advisor特性提供了开箱即用的RAG功能。主要是QuestionAnswerAdvisor问答拦截器和Retrieva
lAugmentationAdvisor检索增强拦截器,前者更简单易用、后者更灵活强大。
查询增强的原理其实很简单。向量数据库存储着A!模型本身不知道的数据,当用户问题发送给A!模型时,Quest
ionAnswerAdvisor会查询向量数据库,获取与用户问题相关的文档。然后从向量数据库返回的响应会被附加到用
户文本中,为AI模型提供上下文,帮助其生成回答。
此处我们就选用更简单易用的QuestionAnswerAdvisor问答拦截器,在CookerApp中新增和RAG知识库进行对话
的方法。代码如下:
public String doChatWithRag(String message,String chatId){
ChatResponse chatResponse = chatClient
.prompt()
.user(message)
.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID,chatId))
.advisors(QuestionAnswerAdvisor.builder(cookerAppVectorStore).build())
.call().chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}",content);
return content;
}

结果显示RAG能正常实现,接下来进行RAG的一些高级用法。
基于PGVector实现向量存储
PGVector是经典数据库PostgreSQL的扩展,为 PostgreSQL 提供了存储和检索高维向量数据的能力。
为什么选择它来实现向量存储呢?因为很多传统业务都会把数据存储在这种关系型数据库中,直接给原有的数据
库安装扩展就能实现向量相似度搜索、而不需要额外搞一套向量数据库,人力物力成本都很低,所以这种方案很
受企业青睐,也是目前实现RAG的主流方案之一。
首先我们准备PostgreSQL数据库,并为其添加扩展。有2种方式,第一种是在自己的本地或服务器安装,可以参
考下列文章实现:
1.引入依赖
参考Spring Al官方文档整合PGVector,先引入依赖,版本号可以在 Maven 中央仓库查找:
<!-- https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-vector-store-pgvector -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
编写配置,建立数据库连接:
spring:
datasource:
url: jdbc:postgresql://改为你的公网地址/yu_ai_agent
username: 改为你的用户名
password: 改为你的密码
ai:
vectorstore:
pgvector:
index-type: HNSW
dimensions: 1536
distance-type: COSINE_DISTANCE
max-document-batch-size: 10000 # Optional: Maximum number of documents per batch
2. 编写向量数据库配置类
@Configuration
public class PgVectorVectorStoreConfig {
@Resource
private CookerAppDocumentLoader cookerAppDocumentLoader;
@Bean
public VectorStore pgVectorVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel dashscopeEmbeddingModel) {
VectorStore vectorStore = PgVectorStore.builder(jdbcTemplate, dashscopeEmbeddingModel)
.dimensions(1536) // Optional: defaults to model dimensions or 1536
.distanceType(COSINE_DISTANCE) // Optional: defaults to COSINE_DISTANCE
.indexType(HNSW) // Optional: defaults to HNSW
.initializeSchema(true) // Optional: defaults to false
.schemaName("public") // Optional: defaults to "public"
.vectorTableName("vector_store") // Optional: defaults to "vector_store"
.maxDocumentBatchSize(10000) // Optional: defaults to 10000
.build();
// 加载文档
List<Document> documents = cookerAppDocumentLoader.loadPDFs();
vectorStore.add(documents);
return vectorStore;
}
}
3.启动类排除配置类
@SpringBootApplication(exclude = PgVectorStoreAutoConfiguration.class)
public class AiCookerApplication {
public static void main(String[] args) {
SpringApplication.run(AiCookerApplication.class, args);
}
}
4. 实现RAG
public String doChatWithRag(String message,String chatId){
ChatResponse chatResponse = chatClient
.prompt()
.user(message)
.advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID,chatId))
.advisors(QuestionAnswerAdvisor.builder(pgVectorVectorStore).build())
.call().chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}",content);
return content;
}
advisorSpec.param(ChatMemory.CONVERSATION_ID,chatId))
.advisors(QuestionAnswerAdvisor.builder(pgVectorVectorStore).build())
.call().chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}",content);
return content;
}
更多推荐
所有评论(0)