大模型“幻觉“频现?RAG技术如何根治三大痛点,实现精准问答?
文章深入解析了RAG(检索增强生成)技术的核心原理与实现流程,指出大模型普遍存在的三大缺陷:幻觉现象、知识更新缓慢以及领域知识理解有限。RAG通过结合向量数据库、嵌入模型和大语言模型,实现从外部私有知识库检索信息并作为上下文输入大模型,有效解决上述问题。文章详细阐述了RAG的检索流程,包括文档编码、向量入库、查询编码、相似性搜索、获取相关文档、构造提示词以及生成最终回答等关键步骤,并结合实际应用场景(如企业知识库问答、法律条文解读等)展示了RAG的实用价值。此外,还探讨了RAG的高级阶段,包括预检索策略(查询增强、改写、扩展)和后检索策略(重排序、文档后置处理器),以进一步提升检索质量与效率。
RAG 诞生背景:大模型原生缺陷
LLM 存在 3 个无法自愈的问题,这是 RAG 技术的核心出发点:
1.LLM存在幻觉现象, 生成无事实依据、虚假编造的内容

- LLM知识更新缓慢, 预训练数据固定,无法同步新数据 / 私有数据

- LLM对领域知识的理解有限, 对企业内部文档、专业手册等私有知识完全未知

RAG的概念
·RAG(Retrieval Augmented Generation)顾名思义,先从外部私有知识库检索相关信息,再将检索结果作为上下文输入大模型,让模型基于真实事实生成回答。
·类比:你可以把这个过程想象成开卷考试。让LLM先翻书,再回答问题。
·价值: 根治大模型私有知识未知、知识过时、内容幻觉三大痛点。
·举例: 我想询问LLM有关于我公司内部的请假流程它肯定不知道,但是我把公司的员工手册先给它看,当我在询问的时候他就会知道
RAG的检索流程

这张图详细展示了检索增强生成(Retrieval Augmented Generation, RAG)的工作流程,结合了向量数据库、嵌入模型和大语言模型(LLM),以实现基于外部知识的智能问答。整个流程分为7个步骤,清晰地描绘了从文档预处理到最终响应生成的完整闭环。
整体结构说明
图表左侧标注“RAG”,表明这是RAG架构的核心流程图。右侧按数字顺序1~7展示数据流与处理阶段:
·上半部分:文档索引与向量化存储(离线/批处理阶段)
·下半部分:用户查询与实时响应生成(在线/推理阶段)
中间核心组件包括:
·Embedding model(嵌入模型)
·Vector database(向量数据库)
·DeepSeek LLM(大语言模型,此处以 DeepSeek 为例)
分步详解
① Encode —— 文档编码
“Additional documents” → “Embedding model”
系统将额外的非结构化文档(如PDF、TXT、网页等)输入嵌入模型,将其转换为高维向量表示(即“嵌入向量”)。这些向量捕捉了文本的语义信息,便于后续相似性匹配。
✅ 关键点:此步骤是ETL中的“Transform”环节,通常批量执行。

② Index —— 向量入库
“Embedding model” → “Vector database”
将上一步生成的向量存入向量数据库,并建立索引以便高效检索。向量数据库支持近似最近邻搜索(ANN),能快速找到与查询最相似的文档片段。
✅ 关键点:向量数据库是RAG中“检索”能力的核心基础设施。

③ Encode —— 查询编码
“Query” → “Embedding model”
当用户提出问题时(Query),同样通过同一个嵌入模型将其转化为向量。注意:这里必须使用与文档编码相同的模型,保证语义空间一致。
✅ 关键点:查询与文档需在相同向量空间中比较,才能准确匹配相似度。

④ Similarity search —— 相似性搜索
“Vector database” ←→ “Similar documents”
向量数据库接收查询向量后,执行相似性搜索(如余弦相似度或欧氏距离),返回Top-K个最相关的文档片段(Similar documents)。
✅ 关键点:这一步实现了“检索”功能,是RAG区别于纯生成式AI的关键。

⑤ Retrieve —— 获取相关文档
“Vector database” → “Similar documents” + “Query”
系统将检索到的相关文档片段与原始用户查询一起打包,准备送入大语言模型。此时,“Query + Context”构成完整的提示词(Prompt)。
✅ 关键点:上下文增强——让LLM不仅依赖训练知识,还能利用最新/专属数据作答。

⑥ Prompt —— 构造提示词
“Similar documents + Query” → “DeepSeek LLM”
将用户问题和检索到的相关文档片段组合成一个结构化的提示词(Prompt),发送给大语言模型(如DeepSeek)。例如:
请根据以下资料回答问题:
[相关资料片段1]
[相关资料片段2]
…
问题:{用户提问}
✅ 关键点:Prompt工程决定LLM能否有效利用检索结果,避免幻觉或无关输出。

⑦ Final response —— 生成最终回答
“DeepSeek LLM” → “Final response”
大语言模型基于提供的上下文和问题,生成自然流畅、准确有据的最终回答,并返回给用户。
✅ 关键点:LLM负责“生成”,而RAG框架确保其“有据可依”。

流程总结
RAG = 文档向量化存储 + 用户查询实时检索 + 上下文增强的大模型生成
它解决了传统LLM缺乏实时知识、易产生幻觉的问题,特别适用于企业知识库、客服系统、法律医疗等专业领域。

常见应用场景举例
·企业内部知识库问答机器人
·法律条文/合同条款智能解读
·医疗文献辅助诊断建议
·教育领域个性化学习助手
详见RAG论文:https://arxiv.org/pdf/2312.10997
向量
向量定义
向量(vector)又称矢量,在数学中也称为欧几里得向量、几何向量,是数学中最基本的概念之一。其定义如下:
向量是指具有大小(magnitude)和方向的量。
在RAG中用来做语义相似匹配的关键,它解决了“电脑怎么理解文字意思”的问题。通常,我们会用这些向量来做相似性搜索。比如,通过一维向量可以显示出词语或短语之间在意义上的相近程度。举个例子,“你好”、“hello”和“见到你很高兴”,可以用一维向量来表示它们意思上的接近度。
对于像小狗这样复杂的东西,我们不能只靠一个特点来找到相似的。得考虑好几个方面才行,比如毛色、体型、品种这些。然后把这些特点都变成向量的一部分,组合成一个多维向量。举个例子吧,一只棕色的小泰迪就可以用这样的多维向量表示:[棕色, 小型, 泰迪]。

要让搜索结果更准确,我们就得用到更多种类的数据,把这些数据放在一个更大的空间里。在这个多维度的空间中,找相似的东西会变得更难一些。这时候,我们需要用到一些方法,比如余弦相似度或者欧式距离,来衡量这些数据之间的相似程度。而向量数据库可以帮助我们完成这个任务。

文本向量化
通过向量模型可以把文本转换成向量。我们学到了一种新的模型,叫做“向量模型”,专门用来把文本变成向量。大语言模型不能直接把文本变成向量,所以需要单独用一个向量模型来完成这个任务。
现有向量模型厂商如下:
1.阿里百炼有大量向量模型

spring.ai.dashscope.embedding.options.model=text-embedding-v1
2.ollama有大量向量模型

spring.ai.ollama.embedding.model=qwen3-embedding
那么,我们用这种向量模型把一句话变成一个向量有什么用呢?其实非常有用,因为我们可以用这些向量来判断两句话有多相似。举个例子:如果你想找和秋田犬类似的狗,在向量数据库里,每种狗的特点都会被表示成一个多维向量。你会发现秋田犬的向量和柴犬的向量最接近,这样就能找到跟秋田犬相似的狗了。

·向量模型的本质目标,就是把语义相似的内容用“相近”的向量表示,把“不相关”内容尽量拉远。
·所以好的向量模型能够更好的识别语义, 进行向量化.
示例以阿里百炼平台向量模型
spring.ai.model.embedding=dashscope
spring.ai.dashscope.embedding.options.model=text-embedding-v1
com.alibaba.cloud.ai
spring-ai-alibaba-starter-dashscope
org.springframework.ai
spring-ai-advisors-vector-store
@Autowired
EmbeddingModel embeddingModel;
@GetMapping("/embedding")
public void embedding() {
float[] embedded = embeddingModel.embed("我叫柱子");
System.out.println(embedded.length);
System.out.println(Arrays.toString(embedded));
}

向量数据库
对于向量模型生成出来的向量,我们可以持久化到向量数据库,并且能利用向量数据库来计算两个向量之间的相似度,或者根据一个向量查找跟这个向量最相似的向量。

在SpringAi中,VectorStore 表示向量数据库,目前常见支持的向量数据库有
·Chroma Vector Store - The Chroma vector store.
·Elasticsearch Vector Store - The Elasticsearch vector store. 可以“以向量+关键词”方式做混合检索。深度优化更多针对文本,不是专门“向量搜索引擎”。向量存储和检索容量有限制,查询延迟高于 Milvus。
·Milvus Vector Store - The Milvus vector store.
·MongoDB Atlas Vector Store - The MongoDB Atlas vector store.
·Neo4j Vector Store - The Neo4j vector store.可以结合结构化图谱查询与向量检索, 大规模嵌入检索(如千万—亿级高维向量)性能明显落后于 Milvus
·Redis Vector Store - The Redis vector store. 低门槛实现小规模向量检索。对于高维大规模向量(如几百万到上亿条),性能和存储效率不如专用向量库。
·SimpleVectorStore - A simple implementation of persistent vector storage, good for educational purposes.
其中有我们熟悉的几个数据库都可以用来存储向量,比如Elasticsearch、MongoDb、Redis。
匹配检索
在这个示例中,我存储了某个PDF文件里面的内容到向量数据库中,然后通过"招标文件发售开始时间"这几个关键字在向量数据库中进行查询,以下为流程图

此次示例代码讲解
Milvus Vector StoreMilvus(国产团队)、文档友好、社区国内活跃、性能最佳、市场占用率大。 实战中使用的向量数据库
2022 年,Milvus 支持十亿级向量,2023 年,它以持续稳定的方式扩展到数百亿级,为 300 多家大型企业的大规模场景提供支持,包括 Salesforce、PayPal、Shopee、Airbnb、eBay、NVIDIA、IBM、AT&T、LINE、ROBLOX、Inflection 等。
详见 https://milvus.io/docs/zh 文档。
在spring ai使用
1、添加依赖
org.springframework.ai
spring-ai-starter-vector-store-milvus
2、配置milvus
milvus 配置
spring.ai.vectorstore.milvus.client.host=192.168.0.143
spring.ai.vectorstore.milvus.client.port=19530
#spring.ai.vectorstore.milvus.client.username=root
#spring.ai.vectorstore.milvus.client.password=milvus
spring.ai.vectorstore.milvus.databaseName=default
spring.ai.vectorstore.milvus.collectionName=ragtest
spring.ai.vectorstore.milvus.initializeSchema=true
spring.ai.vectorstore.milvus.embeddingDimension=1536
SearchRequest组件
可以利用SearchRequest设置检索请求:
·query 代表要检索的内容
·topK 设置检索结果的前N条
o通常我们查询所有结果查出来, 因为查询结果最终要发给大模型, 查询过多的结果会:
1.过多的token意味着更长延迟, 更多的费用, 并且过多上下文会超限;
2.研究表明过多的内容会降低 LLM 的召回性能
·similarityThreshold 设置相似度阈值, 可以通关设置分数限制召回内容相似度. 从而过滤掉废料。 (中文语料要适当降低分数),所以应遵循始终以“业务召回效果”为主,而不是追求网上常说的高分阈值。
public WriterController(EmbeddingModel embeddingModel, VectorStore vectorStore) {
logger.info(“WriterController --> start read pdf file by page”);
Resource resource = new DefaultResourceLoader().getResource(Constant.PDF_FILE_PATH);
PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件
//pagePdfDocumentReader.read();
this.documents = pagePdfDocumentReader.get().subList(0, 5); // query 5 pages
this.vectorStore = vectorStore;
this.embeddingModel = embeddingModel;
}
@GetMapping("/writeVector")
public void writeVector() {
logger.info("Writing vector...");
vectorStore.add(documents);
}
@GetMapping(“/search”)
public Listsearch(@RequestParam(“msg”) String msg) {
msg = “招标文件发售开始时间”;
logger.info(“start search data: {}”, msg);
float[] embed = embeddingModel.embed(msg);
logger.info(“embed: {}”, embed);
return vectorStore.similaritySearch(SearchRequest
.builder()
.query(msg)
.topK(2).similarityThreshold(0.5)
.build());
}
提取转换加载ETL
在之前,我们主要完成了数据检索阶段, 但是完整的RAG流程还需要有emedding阶段, 即:
提取(读取)、转换(分隔)和加载(写入)

Document Loaders 文档读取器

spring ai提供了以下文档阅读器
·JSON
·文本
·HTML(JSoup)
·Markdown
·PDF页面
·PDF段落
·Tika(DOCX、PPTX、HTML……)
alibaba ai也提供了很多阅读器
https://github.com/alibaba/spring-ai-alibaba/tree/main/community/document-parsers
·document-parser-apache-pdfbox:用于解析 PDF 格式文档。
·document-parser-bshtml:用于解析基于 BSHTML 格式的文档。
·document-parser-pdf-tables:专门用于从 PDF 文档中提取表格数据。
·document-parser-bibtex:用于解析 BibTeX 格式的参考文献数据。
·document-parser-markdown:用于解析 Markdown 格式的文档。
·document-parser-tika:一个多功能文档解析器,支持多种文档格式。

org.springframework.ai
spring-ai-jsoup-document-reader
org.springframework.ai
spring-ai-markdown-document-reader
org.springframework.ai
spring-ai-pdf-document-reader
org.springframework.ai
spring-ai-tika-document-reader
读取Text
@GetMapping(“/text”)
public ListreadText() {
logger.info(“start read text file”);
Resource resource = new DefaultResourceLoader().getResource(Constant.TEXT_FILE_PATH);
TextReader textReader = new TextReader(resource); // 适用于文本数据
return textReader.read();
}
读取markdown
@GetMapping(“/markdown”)
public ListreadMarkdown() {
logger.info(“start read markdown file”);
MarkdownDocumentReader markdownDocumentReader = new MarkdownDocumentReader(Constant.MARKDOWN_FILE_PATH); // 只可以传markdown格式文件
return markdownDocumentReader.read();
}
读取pdf
·PagePdfDocumentReader一页1个document
·ParagraphPdfDocumentReader 按pdf目录分成一个个document
@GetMapping("/pdf-page")
public ListreadPdfPage() {
logger.info("start read pdf file by page");
Resource resource = new DefaultResourceLoader().getResource(Constant.PDF\_FILE\_PATH);
PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件
return pagePdfDocumentReader.read();
}
@GetMapping("/pdf-paragraph")
public ListreadPdfParagraph() {
logger.info("start read pdf file by paragraph");
Resource resource = new DefaultResourceLoader().getResource(Constant.PDF\_FILE\_PATH);
ParagraphPdfDocumentReader paragraphPdfDocumentReader = new ParagraphPdfDocumentReader(resource); // 有目录的pdf文件
return paragraphPdfDocumentReader.read();
}
DocumentSplitter 文档拆分器(转换器)

由于文本读取过来后, 还需要分成一段一段的片段(分块chunk), 分块是为了更好地拆分语义单元,这样在后面可以更精确地进行语义相似性检索,也可以避免LLM的Token限制。
SpringAi就提供了一个文档拆分器:
·TextSplitter 抽象类
·TokenTextSplitter 按token分隔
TokenTextSplitter
1.chunkSize (默认值: 800)
o每个文本块的目标大小,以token为单位
2.minChunkSizeChars (默认值: 350) 建议小一点
o如果块超过最小块字符数( 按照块的最后. ! ? \n 符号截取)
o如果块没超过最小块字符数,不会按照符号截取(保留原块)。
3.minChunkLengthToEmbed (默认值: 5)
o丢弃短于此长度的文本块(如果去掉\r\n, 只剩5个有效文本, 那就丢掉)
4.maxNumChunks (默认值: 10000)
o最多能分多少个块, 超过了就不管了
5.keepSeparator (默认值: true)
o是否在块中保留分隔符、换行符 \r\n
分块经验
过细分块的潜在问题
1.语义割裂: 破坏上下文连贯性,影响模型理解。
2.计算成本增加:分块过细会导致向量嵌入和检索次数增多,增加时间和算力开销。
3.信息冗余与干扰:碎片化的文本块可能引入无关内容,干扰检索结果的质量,降低生成答案的准确性。
分块过大的弊端
1.信息丢失风险:过大的文本块可能超出嵌入模型的输入限制,导致关键信息未被有效编码。
2.检索精度下降:大块内容可能包含多主题混合,与用户查询的相关性降低,影响模型反馈效果。
| 场景 | 分块策略 | 参数参考 |
| 微博/短文本 | 句子级分块,保留完整语义 | 每块100-200字符 |
| 学术论文 | 段落级分块,叠加10%重叠 | 每块300-500字符 |
| 法律合同 | 条款级分块,严格按条款分隔 | 每块200-400字符 |
| 长篇小说 | 章节级分块,过长段落递归拆分为段落 | 每块500-1000字符 |
在实际应用中,不应过度依赖基于文本主题的分割策略。由于实战数据体量庞大且缺乏规律,难以确保每个切片(Chunk)都能完整覆盖单一主题,即便引入人工干预也收效甚微。因此,工程实践中通常需根据数据特性灵活选择分割器,主流方案是采用按 Token 数量截断的策略。毕竟不存在完美的分割方法,更优的解法往往是结合‘固定 Token 长度’为基础,辅以人工规则或大模型智能分割的混合模式。
分块五种策略
以下是 RAG 的五种分块策略:


1)固定大小分块
生成块的最直观和直接的方法是根据预定义的字符、单词或标记数量将文本分成统一的段。

由于直接分割会破坏语义流,因此建议在两个连续的块之间保持一些重叠(上图蓝色部分)。
这很容易实现。而且,由于所有块的大小相同,它简化了批处理。
但有一个大问题。这通常会打断句子(或想法)。因此,重要的信息很可能会分散到不同的块之间。

2)语义分块
这个想法很简单。

·根据句子、段落或主题部分等有意义的单位对文档进行细分。
·接下来,为每个片段创建嵌入。
·假设我从第一个片段及其嵌入开始。
o如果第一个段的嵌入与第二个段的嵌入具有较高的余弦相似度,则这两个段形成一个块。
o这种情况一直持续到余弦相似度显著下降。
o一旦发生这种情况,我们就开始新的部分并重复。
输出可能如下所示:

与固定大小的块不同,这保持了语言的自然流畅并保留了完整的想法。
由于每个块都更加丰富,它提高了检索准确性,进而使 LLM 产生更加连贯和相关的响应。
一个小问题是,它依赖于一个阈值来确定余弦相似度是否显著下降,而这个阈值在不同文档之间可能会有所不同。

3)递归分块

首先,根据固有分隔符(如段落或章节)进行分块。
接下来,如果每个块的大小超出了预定义的块大小限制,则将其拆分成更小的块。但是,如果块符合块大小限制,则不再进行进一步拆分。
输出可能如下所示:

如上图:
·首先,我们定义两个块(紫色的两个段落)。
·接下来,第 1 段被进一步分成更小的块。
与固定大小的块不同,这种方法还保持了语言的自然流畅性并保留了完整的想法。
然而,在实施和计算复杂性方面存在一些额外的开销。

4)基于文档结构的分块
这是另一种直观的方法。

它利用文档的固有结构(如标题、章节或段落)来定义块边界。
这样,它就通过与文档的逻辑部分对齐来保持结构完整性。
输出可能如下所示:

也就是说,这种方法假设文档具有清晰的结构,但事实可能并非如此。
此外,块的长度可能会有所不同,可能会超出模型令牌的限制。您可以尝试使用递归拆分进行合并。

5)基于LLM的分块

既然每种方法都有优点和缺点,为什么不使用 LLM 来创建块呢?
可以提示 LLM 生成语义上孤立且有意义的块。
显然,这种方法将确保较高的语义准确性,因为 LLM 可以理解超越简单启发式方法(用于上述四种方法)的上下文和含义。
唯一的问题是,它是这里讨论的所有五种技术中计算要求最高的分块技术。
此外,由于 LLM 通常具有有限的上下文窗口,因此需要注意这一点。


Document Writers文档写入
文本向量化

向量化存储之前在“文本向量化”介绍了, 就是通过向量模型库进行向量化
依然通过Qwen向量模型进行向量化: 将第分割的chunk进行向量化
@Autowired
EmbeddingModel embeddingModel;
@GetMapping("/embedding")
public void embedding() {
float[] embedded = embeddingModel.embed("我叫柱子");
System.out.println(embedded.length);
System.out.println(Arrays.toString(embedded));
}
存储向量

我们通过向量数据库存储document, 可以省略向量化这一步, 向量数据库会在底层自动完成向量化
@GetMapping(“/writeVector”)
public void writeVector() {
logger.info(“Writing vector…”);
vectorStore.add(documents);
}
向量数据库检索
需要先将文本进行向量化, 然后去向量数据库查询,
// 相似性查询
Listdocuments = vectorStore.similaritySearch(SearchRequest
.builder()
.query(msg)
.topK(2).similarityThreshold(0.4)
.build());
// 输出
System.out.println(documents);
完整代码:
public WriterController(EmbeddingModel embeddingModel, VectorStore vectorStore, EmbeddingModel embeddingModel1) {
logger.info(“WriterController --> start read pdf file by page”);
Resource resource = new DefaultResourceLoader().getResource(Constant.PDF_FILE_PATH);
PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件
this.documents = pagePdfDocumentReader.get().subList(0, 5); // query 5 pages
this.vectorStore = vectorStore;
this.embeddingModel = embeddingModel1;
}
@GetMapping("/writeVector")
public void writeVector() {
logger.info("Writing vector...");
vectorStore.add(documents);
}
@GetMapping("/search")
public Listsearch(@RequestParam("msg") String msg) {
msg = "招标文件发售开始时间";
logger.info("start search data: {}", msg);
float[] embed = embeddingModel.embed(msg);
logger.info("embed: {}", embed);
return vectorStore.similaritySearch(SearchRequest
.builder()
.query(msg)
.topK(2).similarityThreshold(0.4)
.build());
}
与模型对话
在Spring AI中,结合 ChatClient 可以直接将检索和Advisor整合在一起,这边用QuestionAnswerAdvisor

// 对话代理
ChatClient chatClient;
// 向量数据库
VectorStore vectorStore;
public AiRagController(ChatModel chatModel, ChatMemory chatMemory,
VectorStore vectorStore, MilvusClient milvusClient) {
this.chatClient = ChatClient.builder(chatModel)
// 默认系统提示词
.defaultSystem("""
你是太和知识库系统的对话助手,请以乐于助人的方式进行对话,
今天的日期:{current\_data},
我的用户ID是{user\_id}
""")
.defaultAdvisors(
PromptChatMemoryAdvisor.builder(chatMemory).build(),
SimpleLoggerAdvisor.builder().build() // 日志拦截
)
.build();
this.vectorStore = vectorStore;
}
@GetMapping("/rag")
public Fluxrag(
@RequestParam(value = "question", defaultValue = "招标文件每套售价多少人民币?") String question, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
Long userId = 100000000l;
String currentDate = LocalDate.now().toString();
Fluxcontent = chatClient.prompt()
.system(a -> a.param("current\_data", currentDate))
.system(a -> a.param("user\_id", userId))
.user(question)
.advisors(a -> a.param(ChatMemory.CONVERSATION\_ID, userId))
// 简单快速 rag QuestionAnswerAdvisor实现思路
.advisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(
SearchRequest.builder()
.query(question)
.similarityThreshold(0.4d).topK(2)
.build()
)
.build())
.stream()
.content();
return content;
}
RAG高级阶段

基于朴素RAG,高级RAG主要通过预检索策略和后检索策略来提升检索质量。
详见RAG论文:https://arxiv.org/abs/2407.21059
Pre-Retrieval(预检索)
增强和转换用户输入,使其更有效地执行检索任务,解决格式不正确的查询、query 语义不清晰、或不受支持的语言等。
1.QueryAugmenter 查询增强:使用附加的上下文数据信息增强用户 query,提供大模型回答问题时的必要上下文信息;
2.QueryTransformer查询改写:因为用户的输入通常是片面的,关键信息较少,不便于大模型理解和回答问题。因此需要使用 prompt 调优手段或者大模型改写用户 query;
3.QueryExpander查询扩展:将用户 query 扩展为多个语义不同的变体以获得不同视角,有助于检索额外的上下文信息并增加找到相关结果的机会。
代码实现
com.alibaba.cloud.ai
spring-ai-alibaba-rag
co.elastic.clients
elasticsearch-java
org.elasticsearch.client
elasticsearch-rest-client
org.springframework.ai
spring-ai-starter-vector-store-elasticsearch
// 查询增强
ContextualQueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
// 用于增强输入查询的组件,提供额外的数据,用于为大型语言模型提供必要的上下文以回答用户查询。
.allowEmptyContext(false) // 允许空上下文,避免在没有上下文时生成空查询
.emptyContextPromptTemplate(PromptTemplate.builder().template(“没有相关数据查询”).build())
.build();
var compressionQueryTransformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build(); // 查询压缩 --使用大型语言模型将对话历史和后续查询压缩为捕获对话本质的独立查询
var rewriteQueryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.targetSearchSystem(“vector store”)
.build();// 重写用户查询
var translationQueryTransformer = TranslationQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.targetLanguage(“english”)
.build();// 查询翻译 --使用大型语言模型翻译用户查询为目标语言
// Query Transformation 查询改写
ListqueryTransformers = List.of(
rewriteQueryTransformer
,compressionQueryTransformer
,translationQueryTransformer
);
// Query Expansion 查询扩展
MultiQueryExpander multiQueryExpander = MultiQueryExpander.builder()
.numberOfQueries(2)
.chatClientBuilder(chatClient.mutate())
.build(); // 查询扩展 --使用大型语言模型扩展用户查询,生成多个相关查询
Post-Retrieval(后检索)
对于由问题检索得到的一系列上下文,后检索策略关注如何优化它们与查询问题的集成。这一过程主要包括重新排序。重新排列检索到的信息,将最相关的内容予以定位标记。直接将所有相关文档输入到大型语言模型(LLMs)可能导致信息过载,为了缓解这一点,后检索工作集中选择必要的信息,强调关键部分,并限制了了相应的上下文长度。
Document Post-Processing 后置处理器:使用附加的上下文数据信息增强用户 query,提供大模型回答问题时的必要上下文信息;
示例介绍下重排模型
专业化模型:重排序模型(如 qwen3-vl-rerank)专门针对文档相关性评估进行训练,能够更准确地计算查询与文档的语义匹配度。
分数阈值过滤:通过设置最小分数阈值,可以过滤掉低质量的文档,确保只有高相关性的内容被保留。在实现中可以看到这个过滤逻辑:
动态参数调整:支持根据实际效果动态调整 topN 等参数,优化最终返回的文档数量和质量。
示例组件用com.alibaba.cloud.ai.rag.postretrieval.DashScopeRerankPostProcessor重排模型
rerank 配置
spring.ai.dashscope.rerank.options.model=qwen3-vl-rerank
// rerank 重排序
DashScopeRerankPostProcessor dashScopeRerankPostProcessor = DashScopeRerankPostProcessor.builder()
.rerankModel(rerankModel)
.rerankOptions(dashScopeRerankProperties.getOptions())
.build();
重排前后效果

总结后高级RAG流程图如下

整体示例代码
@GetMapping(“/advancedRag”)
public FluxadvancedRag(HttpServletResponse response
, @RequestParam(value = “question”, defaultValue = “开标时间和地点分别是?”) String question) {
response.setCharacterEncoding(“UTF-8”);
// 查询增强
ContextualQueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
// 用于增强输入查询的组件,提供额外的数据,用于为大型语言模型提供必要的上下文以回答用户查询。
.allowEmptyContext(false) // 允许空上下文,避免在没有上下文时生成空查询
.emptyContextPromptTemplate(PromptTemplate.builder().template("没有相关数据查询").build())
.build();
var compressionQueryTransformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build(); // 查询压缩 --使用大型语言模型将对话历史和后续查询压缩为捕获对话本质的独立查询
var rewriteQueryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.targetSearchSystem("vector store")
.build(); // 重写用户查询
var translationQueryTransformer = TranslationQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.targetLanguage("english")
.build(); // 查询翻译 --使用大型语言模型翻译用户查询为目标语言
// Query Transformation 查询改写
ListqueryTransformers = List.of(
rewriteQueryTransformer
,compressionQueryTransformer
,translationQueryTransformer
);
// Query Expansion 查询扩展
MultiQueryExpander multiQueryExpander = MultiQueryExpander.builder()
.numberOfQueries(2)
.chatClientBuilder(chatClient.mutate())
.build(); // 查询扩展 --使用大型语言模型扩展用户查询,生成多个相关查询
// rerank 重排序
DashScopeRerankPostProcessor dashScopeRerankPostProcessor = DashScopeRerankPostProcessor.builder()
.rerankModel(rerankModel)
.rerankOptions(dashScopeRerankProperties.getOptions())
.build();
// 创建RAG流程,基于模块化架构设计
Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
.queryTransformers(queryTransformers)
.queryExpander(multiQueryExpander)
.queryAugmenter(queryAugmenter)
.documentRetriever(VectorStoreDocumentRetriever.builder() // Retrieval 检索器,--从向量数据库中检索与查询最相关的文档
.similarityThreshold(0.40).topK(2)
.vectorStore(vectorStore)
.build())
.documentJoiner(new ConcatenationDocumentJoiner()) // 将从多个 query 和从多个数据源检索到的 Document 合并为一个 Document 集合;
.documentPostProcessors(dashScopeRerankPostProcessor) // 文档后处理器,用于对检索到的文档进行索引,方便后续的检索和使用。
.build();
String answer = chatClient.prompt().system(a -> a.param("current\_data", LocalDate.now().toString())).system(a -> a.param("user\_id", 100000000L))
.advisors(retrievalAugmentationAdvisor)
.user(question)
.call()
.content();
Fluxcontent = Flux.just(answer);
return content;
}
最近两年大模型发展很迅速,在理论研究方面得到很大的拓展,基础模型的能力也取得重大突破,大模型现在正在积极探索落地的方向,如果与各行各业结合起来是未来落地的一个重大研究方向
大模型应用工程师年包50w+属于中等水平,如果想要入门大模型,那现在正是最佳时机
2025年Agent的元年,2026年将会百花齐放,相应的应用将覆盖文本,视频,语音,图像等全模态
如果你对AI大模型入门感兴趣,那么你需要的话可以点击这里大模型重磅福利:入门进阶全套104G学习资源包免费分享!
扫描下方csdn官方合作二维码获取哦!

给大家推荐一个大模型应用学习路线
这个学习路线的具体内容如下:
第一节:提示词工程
提示词是用于与AI模型沟通交流的,这一部分主要介绍基本概念和相应的实践,高级的提示词工程来实现模型最佳效果,以现实案例为基础进行案例讲解,在企业中除了微调之外,最喜欢的就是用提示词工程技术来实现模型性能的提升

第二节:检索增强生成(RAG)
可能大家经常会看见RAG这个名词,这个就是将向量数据库与大模型结合的技术,通过外部知识来增强改进提升大模型的回答结果,这一部分主要介绍RAG架构与组件,从零开始搭建RAG系统,生成部署RAG,性能优化等

第三节:微调
预训练之后的模型想要在具体任务上进行适配,那就需要通过微调来提升模型的性能,能满足定制化的需求,这一部分主要介绍微调的基础,模型适配技术,最佳实践的案例,以及资源优化等内容

第四节:模型部署
想要把预训练或者微调之后的模型应用于生产实践,那就需要部署,模型部署分为云端部署和本地部署,部署的过程中需要考虑硬件支持,服务器性能,以及对性能进行优化,使用过程中的监控维护等

第五节:人工智能系统和项目
这一部分主要介绍自主人工智能系统,包括代理框架,决策框架,多智能体系统,以及实际应用,然后通过实践项目应用前面学习到的知识,包括端到端的实现,行业相关情景等

学完上面的大模型应用技术,就可以去做一些开源的项目,大模型领域现在非常注重项目的落地,后续可以学习一些Agent框架等内容
上面的资料做了一些整理,有需要的同学可以下方添加二维码获取(仅供学习使用)

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)