RAG鱼皮视频:RAG 知识库基础

This commit is contained in:
huangge1199 2025-05-24 10:48:50 +08:00
parent 9ee7dcde3b
commit 6dfba8de70
13 changed files with 375 additions and 2 deletions

View File

@ -93,12 +93,19 @@
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
<!-- 日志解析 -->
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
<version>4.38.0</version>
</dependency>
<!-- markdown文章读取器 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
<version>1.0.0-M6</version>
</dependency>
</dependencies>
<repositories>

View File

@ -0,0 +1,11 @@
package com.huangge1199.aiagent.Service;
/**
* RagService
*
* @author huangge1199
* @since 2025/5/24 9:21:24
*/
public interface RagService {
String localDoc(String question);
}

View File

@ -0,0 +1,35 @@
package com.huangge1199.aiagent.Service.impl;
import com.huangge1199.aiagent.Service.RagService;
import com.huangge1199.aiagent.config.MyLoggerAdvisor;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;
/**
* RagServiceImpl
*
* @author huangge1199
* @since 2025/5/24 9:21:38
*/
@Service
public class RagServiceImpl implements RagService {
@Resource
private ChatClient chatClient;
@Resource
private VectorStore vectorStore;
@Override
public String localDoc(String question) {
return chatClient.prompt()
.user(question)
.advisors(new MyLoggerAdvisor())
.advisors(new QuestionAnswerAdvisor(vectorStore))
.call()
.content();
}
}

View File

@ -0,0 +1,66 @@
package com.huangge1199.aiagent.config;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import org.springframework.ai.chat.client.advisor.api.AdvisedRequest;
import org.springframework.ai.chat.client.advisor.api.AdvisedResponse;
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain;
import org.springframework.ai.chat.client.advisor.api.StreamAroundAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAroundAdvisorChain;
import org.springframework.ai.chat.model.MessageAggregator;
/**
* MyLoggerAdvisor
* 自定义日志 Advisor
* 打印 info 级别日志只输出单次用户提示词和 AI 回复的文本
*
* @author huangge1199
* @since 2025/5/24 9:12:54
*/
@Slf4j
public class MyLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
@Override
public String getName() {
return this.getClass().getSimpleName();
}
@Override
public int getOrder() {
return 0;
}
private AdvisedRequest before(AdvisedRequest request) {
log.info("AI Request: {}", request.userText());
return request;
}
private void observeAfter(AdvisedResponse advisedResponse) {
log.info("AI Response: {}", advisedResponse.response().getResult().getOutput().getText());
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
advisedRequest = before(advisedRequest);
AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
observeAfter(advisedResponse);
return advisedResponse;
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
advisedRequest = before(advisedRequest);
Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);
return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
}
}

View File

@ -0,0 +1,35 @@
package com.huangge1199.aiagent.controller;
import com.huangge1199.aiagent.Service.RagService;
import com.huangge1199.aiagent.common.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* RagController
*
* @author huangge1199
* @since 2025/5/24 9:14:30
*/
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG相关")
public class RagController {
@Resource
private RagService ragService;
@PostMapping("/localDoc")
@Operation(summary = "本地知识库")
public R<String> localDoc(@RequestBody String question) {
String res = ragService.localDoc(question);
return R.ok(res);
}
}

View File

@ -0,0 +1,54 @@
package com.huangge1199.aiagent.rag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.markdown.MarkdownDocumentReader;
import org.springframework.ai.reader.markdown.config.MarkdownDocumentReaderConfig;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* DocumentLoaderUtils
*
* @author huangge1199
* @since 2025/5/24 9:32:25
*/
@Component
@Slf4j
public class DocumentLoaderUtils {
private final ResourcePatternResolver resourcePatternResolver;
public DocumentLoaderUtils(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
public List<Document> loadMarkdowns() {
List<Document> allDocuments = new ArrayList<>();
try {
Resource[] resources = resourcePatternResolver.getResources("classpath:doc/*.md");
for (Resource resource : resources) {
String filename = resource.getFilename();
// 提取文档倒数第 3 和第 2 个字作为标签
String status = filename.substring(filename.length() - 6, filename.length() - 4);
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", filename)
.withAdditionalMetadata("status", status)
.build();
MarkdownDocumentReader markdownDocumentReader = new MarkdownDocumentReader(resource, config);
allDocuments.addAll(markdownDocumentReader.get());
}
} catch (IOException e) {
log.error("Markdown 文档加载失败", e);
}
return allDocuments;
}
}

View File

@ -0,0 +1,28 @@
package com.huangge1199.aiagent.rag;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.KeywordMetadataEnricher;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* MyKeywordEnricher
*
* @author huangge1199
* @since 2025/5/24 9:38:03
*/
@Component
public class MyKeywordEnricher {
@Resource
private ChatModel dashscopeChatModel;
public List<Document> enrichDocuments(List<Document> documents) {
KeywordMetadataEnricher keywordMetadataEnricher = new KeywordMetadataEnricher(dashscopeChatModel, 5);
return keywordMetadataEnricher.apply(documents);
}
}

View File

@ -0,0 +1,26 @@
package com.huangge1199.aiagent.rag;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* MyTokenTextSplitter
*
* @author huangge1199
* @since 2025/5/24 9:37:03
*/
@Configuration
public class MyTokenTextSplitter {
public List<Document> splitDocuments(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter();
return splitter.apply(documents);
}
public List<Document> splitCustomized(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter(200, 100, 10, 5000, true);
return splitter.apply(documents);
}
}

View File

@ -0,0 +1,51 @@
package com.huangge1199.aiagent.rag;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* RagConfig
*
* @author huangge1199
* @since 2025/5/24 9:26:40
*/
@Configuration
public class RagConfig {
@Resource
private DocumentLoaderUtils documentLoaderUtils;
@Resource
private MyTokenTextSplitter myTokenTextSplitter;
@Resource
private MyKeywordEnricher myKeywordEnricher;
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder.defaultSystem("你将作为一名恋爱大师,对于用户的问题作出解答")
.build();
}
@Bean
VectorStore vectorStore(@Qualifier("ollamaEmbeddingModel") EmbeddingModel embeddingModel) {
SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();
// 加载文档
List<Document> documentList = documentLoaderUtils.loadMarkdowns();
// 自主切分文档
List<Document> splitDocuments = myTokenTextSplitter.splitCustomized(documentList);
// 自动补充关键词元信息
List<Document> enrichedDocuments = myKeywordEnricher.enrichDocuments(documentList);
simpleVectorStore.add(enrichedDocuments);
return simpleVectorStore;
}
}

View File

@ -11,7 +11,7 @@ spring:
ollama:
base-url: http://192.168.188.2:11435
chat:
model: DeepSeek-R1-Distill-Qwen-1.5B:latest
model: gemma3:1b
server:
port: 8123
servlet:

View File

@ -0,0 +1,20 @@
# 恋爱常见问题和回答 - 单身篇
#### 如何提升自身魅力吸引潜在伴侣?
提升自身魅力需从多方面入手。外在形象上,保持良好的个人卫生,选择适合自己身材与风格的着装,定期锻炼塑造健康体魄。内在修养方面,培养广泛兴趣爱好,如阅读提升知识储备,学习乐器增添艺术气质。积极参加社交活动,锻炼沟通与表达能力,以自信、热情的态度展现自我。比如小李原本内向且不注重穿着,通过学习穿搭、参与户外运动并加入读书俱乐部,逐渐变得开朗自信,吸引了不少异性目光。
推荐课程:[《单身魅力提升全攻略》](https://www.codefather.cn),该课程从形象打造、气质培养到社交技巧提升,全方位助力单身人士散发独特魅力,吸引心仪对象。
#### 怎样在社交场合主动结识心仪异性?
在社交场合,首先要保持微笑,展现亲和力。主动寻找话题切入点,比如在画展上可从对某幅作品的看法聊起。真诚地表达自己对对方的兴趣,例如“我看你对这个话题很有见解,能和我多说说吗”。注意倾听对方回应,给予专注眼神与积极反馈,让交流顺畅进行。小王在一次行业交流会上,主动与一位女生就新技术应用展开讨论,认真倾听并适时发表看法,成功交换联系方式并后续发展出感情。
推荐课程:[《社交场合主动交友秘籍》](https://www.codefather.cn),课程通过大量实例与技巧讲解,教你在各类社交场合自信主动出击,结识优质异性。
#### 线上交友有哪些注意事项能提高脱单成功率?
线上交友时,务必完善真实且有吸引力的个人资料,照片选择清晰、展现个人特色的。聊天初期避免过于急切或油腻的话术,从兴趣爱好、日常趣事等轻松话题开启。保持聊天频率适中,不要过于频繁或长时间不回复。同时,注意保护个人隐私,在未深入了解对方前,不随意透露重要信息。小张通过精心打造线上资料,与潜在对象真诚交流,逐步筛选出合适人选,最终成功脱单。
推荐课程:[《线上交友高效脱单指南》](https://www.codefather.cn),课程全面解析线上交友流程,传授实用技巧与避雷方法,助你在虚拟世界找到真爱。
#### 如何克服单身时对恋爱的焦虑情绪?
正视自己的焦虑情绪,分析焦虑产生原因,是担心找不到合适对象,还是对恋爱过程恐惧等。丰富自己的生活,投入工作、发展兴趣爱好,让生活充实起来,转移对恋爱的过度关注。多与亲朋好友交流,分享感受获取支持。参加心理成长课程或阅读相关书籍,学习情绪管理方法。例如小赵通过培养摄影爱好,参加摄影活动结识新朋友,生活变得丰富多彩,对恋爱的焦虑也逐渐减轻。
推荐课程:[《战胜单身恋爱焦虑课程》](https://www.codefather.cn),课程提供专业心理疏导与实用应对策略,帮你摆脱焦虑,以轻松心态迎接爱情。
#### 如何判断相亲对象是否值得深入发展?
观察相亲对象的言行举止,是否尊重他人,比如对服务员的态度。交流中了解其价值观,包括对家庭、事业、生活的看法是否与你契合。关注对方的情绪稳定性,能否理性处理分歧。看其是否有明确的人生规划,对未来有清晰想法。像小钱相亲时,发现对方对生活积极向上,尊重自己的观点,且双方对未来家庭生活规划相似,于是决定深入发展。
推荐课程:[《相亲对象评估与恋爱决策》](https://www.codefather.cn),课程从多个维度教你精准评估相亲对象,做出正确恋爱决策,少走弯路。

View File

@ -0,0 +1,20 @@
# 恋爱常见问题和回答 - 已婚篇
#### 婚后如何平衡工作与家庭责任?
制定详细的日程表,合理分配工作与家庭时间,如工作日晚上预留两小时陪伴家人。与配偶共同协商家务分工,依据各自擅长领域安排,如一方擅长烹饪负责做饭,另一方擅长清洁负责打扫卫生。在工作中提高效率,减少不必要加班,重要家庭活动提前安排工作。例如老陈通过合理规划,既能在工作上取得成绩,又能照顾好家庭,家庭关系和睦。
推荐课程:[《婚后工作家庭平衡之道》](https://www.codefather.cn),课程从时间管理、责任分配等方面入手,助你轻松应对婚后工作与家庭的双重挑战,实现和谐生活。
#### 怎样维护婚后夫妻间的亲密关系?
定期安排二人世界,如每周一次看电影或共进晚餐。保持身体亲密接触,日常拥抱、亲吻。分享日常喜怒哀乐,深入交流内心想法。一起回忆美好过往,如旅行经历、恋爱趣事。为对方制造小惊喜,如纪念日礼物。像老张夫妇坚持每周约会,分享生活点滴,结婚多年仍甜蜜如初。
推荐课程:[《婚后亲密关系维护秘籍》](https://www.codefather.cn),课程提供多种维护亲密关系的方法与技巧,让婚后爱情持续升温,家庭幸福美满。
#### 婚后与伴侣家人产生矛盾,如何妥善解决?
保持冷静,不被情绪左右。先倾听伴侣家人想法,理解其立场。与伴侣坦诚沟通,共同分析矛盾根源。在沟通中尊重对方家人,用温和语气表达自己观点。寻求伴侣帮助,让其在中间协调。例如老王家婆媳产生矛盾,他积极倾听双方想法,与妻子共同解决,最终化解矛盾,家庭恢复和睦。
推荐课程:[《婚后家庭关系矛盾化解课程》](https://www.codefather.cn),课程传授处理家庭矛盾的有效策略,助你巧妙化解与伴侣家人的冲突,营造和谐家庭氛围。
#### 如何在婚后保持自我成长,不被家庭琐事磨灭个人追求?
设定个人成长目标,如学习新技能、考取证书。利用碎片化时间学习,如在通勤路上听有声书。与伴侣商量争取其支持,共同安排家庭事务,为个人成长留出时间。参加线上或线下学习小组,保持学习动力与热情。比如老孙婚后利用业余时间学习编程,获得伴侣支持,成功转行进入新领域,实现自我价值。
推荐课程:[《婚后个人成长与自我实现》](https://www.codefather.cn),课程指导你在婚后繁杂生活中,坚持自我成长,实现个人梦想,拥有精彩人生。
#### 婚后夫妻消费观念不同,如何协调理财规划?
坐下来开诚布公地交流各自消费观念与理财目标,了解对方想法。制定家庭预算,划分必要支出、储蓄、娱乐等板块。设定共同储蓄目标,如每年存下一定金额用于购房或旅行。对于大额消费共同商议决定。例如老李家夫妻通过沟通制定预算,合理规划消费与储蓄,家庭财务状况良好。
推荐课程:[《婚后理财规划与消费协调》](https://www.codefather.cn),课程从观念沟通到实际规划,教你协调夫妻理财差异,实现家庭财富稳健增长。

View File

@ -0,0 +1,20 @@
# 恋爱常见问题和回答 - 恋爱篇
#### 恋爱中如何有效处理双方的争吵?
争吵发生时,先冷静情绪,可暂时离开现场或做几次深呼吸。之后真诚倾听对方诉求,不打断、不急于反驳。表达自己想法时,使用“我觉得”“我感受”等句式,避免指责。共同寻找争吵根源,探讨解决方案,如约定以后遇到类似问题的沟通方式。比如小孙和女友争吵后,冷静下来倾听女友需求,表达自己感受,最终两人找到更好的沟通模式,感情升温。
推荐课程:[《恋爱争吵化解与沟通技巧》](https://www.codefather.cn),课程通过案例分析与实战演练,传授高效化解争吵、提升沟通质量的技巧,让恋爱更甜蜜。
#### 怎样给恋爱中的对方制造浪漫惊喜?
了解对方喜好是关键,若对方喜欢阅读,可精心挑选一本限量版书籍,附上手写情书。策划一场特别约会,比如在对方喜欢的海边看日出。日常也能制造小惊喜,如在对方下班时送上一杯热咖啡。或者为对方准备一场主题派对,布置成对方喜欢的风格。小李为女友精心策划了一场星空露营约会,让女友感动不已,感情愈发深厚。
推荐课程:[《恋爱浪漫惊喜制造宝典》](https://www.codefather.cn),课程涵盖各类浪漫惊喜创意与实施方法,帮你为爱人打造难忘瞬间,让爱情时刻充满新鲜感。
#### 恋爱中如何保持自我,避免过度依赖对方?
培养独立兴趣爱好,如绘画、健身,拥有属于自己的休闲时光。保持自己的社交圈子,定期与朋友相聚。在工作和学习上设定个人目标并努力追求。遇到问题先尝试自己解决,锻炼独立思考与应对能力。例如小周在恋爱中坚持自己的舞蹈爱好,与朋友保持密切联系,工作上积极进取,既享受恋爱甜蜜,又不失自我。
推荐课程:[《恋爱中保持自我与独立成长》](https://www.codefather.cn),课程引导你在恋爱中平衡亲密关系与个人发展,实现自我价值,让爱情更健康持久。
#### 如何在恋爱中与对方有效沟通未来规划?
选择合适时机,如在轻松的周末午后,心平气和开启话题。先分享自己对未来的设想,包括事业发展、家庭规划、生活愿景等,如“我希望未来几年能在事业上取得晋升,同时能多去旅行看看世界”。认真倾听对方想法,尊重差异,不强行要求一致。共同探讨如何协调双方规划,制定共同目标与阶段性计划。比如小吴和男友通过深入沟通,制定了先一起攒钱买房,再旅行的计划,感情更加稳固。
推荐课程:[《恋爱中未来规划沟通秘籍》](https://www.codefather.cn),课程教授沟通技巧与规划协调方法,助你和伴侣明确未来方向,携手走向幸福。
#### 恋爱中发现对方缺点,如何巧妙沟通让其改善?
选择恰当时间地点,避免在公共场合或对方忙碌时提出。以关心的口吻开场,如“我很在意你,发现一个小问题想和你聊聊”。具体描述缺点及带来的影响,例如“你最近总是熬夜打游戏,我担心会影响你的健康和我们相处时间”。提出建设性意见,如“我们可以一起制定一个作息时间表,互相监督”。小郑用这种方式与女友沟通熬夜问题,女友欣然接受并努力改正。
推荐课程:[《恋爱中缺点沟通与关系优化》](https://www.codefather.cn),课程讲解有效沟通策略,帮你巧妙指出对方缺点,促进双方共同成长,提升恋爱质量。