支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


RAG进阶技术!这10种方法你一定要知道。

发布日期:2025-04-09 05:33:29 浏览次数: 1678 作者:Halo咯咯
推荐语

掌握RAG技术,提升AI问答系统性能,应对复杂查询和多轮对话挑战。

核心内容:
1. RAG系统在复杂查询和多轮对话中面临的挑战
2. 基础RAG系统的架构和局限性
3. 10种高级RAG技术优化索引、检索和生成环节

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

在当今这个信息爆炸的时代,AI 系统已经深入到我们生活的方方面面,从医疗健康助手到教育辅导工具,再到企业知识管理机器人,AI 正在帮助我们更高效地获取和处理知识。但随着应用场景的复杂化,传统的 AI 系统面临着诸多挑战:如何生成真正相关的回答?如何理解复杂的多轮对话?如何避免自信地输出错误信息?这些问题在基于 RAG(Retrieval-Augmented Generation,检索增强生成)的系统中尤为突出。

RAG 结合了文档检索的强大能力与语言生成的流畅性,能够让系统基于上下文给出有根据的回答。然而,基础的 RAG 系统在处理复杂查询、多轮对话以及特定领域的专业知识时,常常会“掉链子”,出现“幻觉”(生成错误信息)或丢失上下文的情况。那么,我们该如何升级 RAG 系统,让它变得更智能、更可靠呢?今天,就让我们一起探索如何通过高级 RAG 技术,提升问答系统的性能!

基础 RAG 的局限性

先来看看基础 RAG 系统的架构:它的工作流程大致是这样的,首先将文档加载进来,通过各种分块技术将其拆分成小块,然后使用嵌入模型将这些小块转化为向量,存储到向量数据库中。当用户提出问题时,系统会在向量数据库中检索与问题相关的文档片段,再将这些片段与问题一起传递给语言模型,最终生成回答。

听起来是不是很简单?但正是这种简单性,导致了基础 RAG 系统的诸多问题:

  • 幻觉问题:模型可能会生成一些与原始文档毫不相关,甚至是错误的内容。在医学或法律等对准确性要求极高的领域,这可是致命的缺陷。
  • 缺乏领域专一性:基础 RAG 系统在处理特定领域的复杂话题时,往往会因为检索到不相关或不准确的信息而“翻车”。
  • 多轮对话困境:在多轮对话中,基础 RAG 系统很容易丢失上下文,导致回答支离破碎,无法满足用户的需求。

那么,我们该如何突破这些局限呢?这就需要引入高级 RAG 技术,对 RAG 系统的各个环节——索引、检索和生成——进行优化升级!

索引与分块:构建坚实基础

一个好的索引是 RAG 系统的核心。我们首先要考虑如何高效地导入、拆分和存储数据。接下来,就让我们看看几种先进的索引和分块方法。

1、HNSW:高效检索的利器

HNSW(Hierarchical Navigable Small Worlds,层次可导航小世界)算法是一种在大数据集中快速查找相似项的强大工具。它通过构建一个基于图的结构,能够高效地找到近似最近邻(ANN)。具体来说,它有以下几个关键特点:

  • 邻近图:HNSW 构建了一个图,图中的每个点都与附近的点相连,这使得搜索过程更加高效。
  • 层次结构:算法将点分层组织,顶层连接较远的点,底层连接较近的点,从而加快了搜索速度。
  • 贪婪路由:在搜索时,HNSW 从高层的某个点开始,逐步向下层移动,直到找到局部最小值,大大减少了查找相似项所需的时间。

实际应用中,我们可以通过设置参数(如每个节点的邻居数量、构建图时考虑的邻居数量等)来优化 HNSW 的性能。通过 HNSW,我们能够在海量数据中快速准确地找到与用户问题最相关的文档片段,为后续的回答生成提供坚实基础。

动手实践 HNSW

接下来,让我们通过代码来实现 HNSW 算法。这里我们使用的是 FAISS 库,它是一个高效的相似性搜索库,非常适合与 HNSW 结合使用。

import faiss
import numpy as np

# 设置 HNSW 参数
d = 128  # 向量的维度
M = 32   # 每个节点的邻居数量

# 初始化 HNSW 索引
index = faiss.IndexHNSWFlat(d, M)

# 设置 efConstruction 参数,控制构建索引时考虑的邻居数量
efConstruction = 200
index.hnsw.efConstruction = efConstruction

# 生成随机数据并添加到索引中
n = 10000  # 要索引的向量数量
xb = np.random.random((n, d)).astype('float32')
index.add(xb)  # 构建索引

# 设置 efSearch 参数,影响搜索过程
efSearch = 100
index.hnsw.efSearch = efSearch

# 执行搜索
nq = 5  # 查询向量的数量
xq = np.random.random((nq, d)).astype('float32')
k = 5   # 检索最近邻的数量
distances, indices = index.search(xq, k)

# 输出结果
print("查询向量:\n", xq)
print("\n最近邻索引:\n", indices)
print("\n最近邻距离:\n", distances)

通过上述代码,我们可以看到 HNSW 在处理大规模数据集时的高效性和准确性。它能够快速找到与查询向量最相似的文档片段,为后续的语言模型生成环节提供高质量的输入。

2、语义分块:让信息更有意义

传统的分块方法通常是基于固定大小来拆分文本,但这种方法可能会将一个完整的概念或信息拆得支离破碎。而语义分块则不同,它根据文本的含义来划分分块,每个分块都代表一个连贯的信息单元。具体操作是计算句子嵌入之间的余弦距离,如果两个句子在语义上相似(低于某个阈值),就把它们归为同一个分块。这种方法的优点是能够生成更有意义、更连贯的分块,从而提高检索的准确性。不过,它需要使用基于 BERT 等的编码器,计算成本相对较高。

动手实践语义分块

接下来,我们通过代码来实现语义分块。这里我们使用的是 LangChain 库中的 SemanticChunker 类,它利用 OpenAI 的嵌入模型来实现语义分块。

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings

# 初始化语义分块器
text_splitter = SemanticChunker(OpenAIEmbeddings())

# 将文档分割为语义相关的分块
docs = text_splitter.create_documents([document])
print(docs[0].page_content)

通过上述代码,我们可以看到语义分块能够根据文本的语义内容生成更有意义的分块,这对于后续的检索和生成环节非常有帮助。

3、基于语言模型的分块:精准捕捉文本结构

这种方法利用强大的语言模型(如拥有 70 亿参数的模型)来处理文本,将其拆分成一个个完整的语句,然后再将这些语句组合成分块,既保证了每个分块的完整性,又兼顾了上下文信息。虽然这种方法计算量较大,但它能够根据文本的具体内容灵活调整分块方式,生成高质量的分块,特别适合对文本结构要求较高的应用场景。

动手实践基于语言模型的分块

接下来,我们通过代码来实现基于语言模型的分块。这里我们使用的是 OpenAI 的 GPT-4o 模型,通过异步调用生成每个分块的上下文信息。

import asyncio
from langchain_openai import ChatOpenAI

async def generate_contexts(document, chunks):
    async def process_chunk(chunk):
        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role""system""content""Generate a brief context explaining how this chunk relates to the full document."},
                {"role""user""content"f"<document> \n{document} \n</document> \nHere is the chunk we want to situate within the whole document \n<chunk> \n{chunk} \n</chunk> \nPlease give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else."}
            ],
            temperature=0.3,
            max_tokens=100
        )
        context = response.choices[0].message.content
        return f"{context} {chunk}"
    
    # 并行处理所有分块
    contextual_chunks = await asyncio.gather(
        *[process_chunk(chunk) for chunk in chunks]
    )
    return contextual_chunks

通过上述代码,我们可以看到基于语言模型的分块能够生成高质量的分块,并为每个分块生成上下文信息,这对于后续的检索和生成环节非常有帮助。

4、利用元数据:为检索增添更多上下文

元数据可以为文档提供额外的上下文信息,比如日期、患者年龄、既往病史等。在检索时,通过过滤这些元数据,我们可以排除无关的信息,让检索结果更加精准。例如,在医疗领域,如果查询与儿童相关的内容,就可以直接过滤掉 18 岁以上患者的记录。在索引时,将元数据与文本一起存储,能够大大提高检索的效率和相关性。

动手实践元数据的使用

接下来,我们通过代码来实现元数据的使用。这里我们使用的是 LangChain 库中的 Document 类,它允许我们在文档中存储元数据。

from langchain_core.documents import Document

# 创建带有元数据的文档
doc = Document(
    page_content="This is a sample document.",
    metadata={"id""doc1""source""https://example.com"}
)

# 打印文档内容和元数据
print(doc.page_content)
print(doc.metadata)

通过上述代码,我们可以看到元数据能够为文档提供更多的上下文信息,这对于后续的检索和生成环节非常有帮助。

检索:精准定位关键信息

检索是 RAG 系统中的关键环节,它决定了我们能否从海量数据中找到与用户问题真正相关的文档。接下来,让我们看看几种提升检索性能的技术。

5、混合搜索:语义与关键词的完美结合

混合搜索将向量搜索(语义搜索)和关键词搜索结合起来,充分发挥两者的优点。在一些领域,如 AI 技术,很多术语都是特定的关键词,比如算法名称、技术术语等。单独使用向量搜索可能会遗漏这些重要信息,而关键词搜索则可以确保这些关键术语被纳入考虑范围。通过同时运行这两种搜索方式,并根据权重系统合并和排序结果,我们可以得到一个更全面、更精准的检索结果列表。

动手实践混合搜索

接下来,我们通过代码来实现混合搜索。这里我们使用的是 LangChain 库中的 WeaviateHybridSearchRetriever 类,它结合了 Weaviate 向量数据库的向量搜索和关键词搜索能力。

from langchain_community.retrievers import WeaviateHybridSearchRetriever

# 初始化混合搜索检索器
retriever = WeaviateHybridSearchRetriever(
    client=client,
    index_name="LangChain",
    text_key="text",
    attributes=[],
    create_schema_if_missing=True,
)

# 执行混合搜索
results = retriever.invoke("the ethical implications of AI")
print(results)

通过上述代码,我们可以看到混合搜索能够结合向量搜索和关键词搜索的优点,生成更全面、更精准的检索结果。

6、查询重写:让问题更“友好”

人类提出的问题往往并不是最适合数据库或语言模型理解的形式。通过使用语言模型重写查询,可以显著提升检索的效果。比如,将“AI 代理是什么,为什么它们是 2025 年的下一个大事件”重写为“AI 代理 大事件 2025”,这样更符合数据库的检索逻辑。此外,还可以通过重写提示词来优化与语言模型的交互,提高结果的质量和准确性。

动手实践查询重写

接下来,我们通过代码来实现查询重写。这里我们使用的是 LangChain 库中的 ChatOpenAI 类,它允许我们利用 OpenAI 的语言模型来重写查询。

from langchain_openai import ChatOpenAI

# 初始化语言模型
chatgpt = ChatOpenAI(model_name="gpt-4o", temperature=0)

# 重写查询
query = "what are AI agents and why they are the next big thing in 2025"
rewritten_query = chatgpt.invoke(query)
print(rewritten_query)

通过上述代码,我们可以看到查询重写能够将人类的问题转化为更适合数据库和语言模型理解的形式,从而提高检索的效果。

7、多查询检索:从不同角度挖掘信息

由于查询的措辞稍有不同,检索结果可能会大相径庭。多查询检索器利用大型语言模型(LLM)根据用户输入生成多个不同角度的查询,然后对每个查询分别检索相关文档,最后将所有查询的结果汇总,从而提供更广泛的相关文档集合。这种方法无需进行大量的手动调整,就能提高找到有用信息的概率。

动手实践多查询检索

接下来,我们通过代码来实现多查询检索。这里我们使用的是 LangChain 库中的 MultiQueryRetriever 类,它允许我们利用 OpenAI 的语言模型生成多个查询,并从 Chroma 向量数据库中检索相关文档。

from langchain.retrievers.multi_query import MultiQueryRetriever

# 初始化多查询检索器
mq_retriever = MultiQueryRetriever.from_llm(
    retriever=similarity_retriever3, llm=chatgpt,
    include_original=True
)

# 执行多查询检索
query = "what is the capital of India?"
docs = mq_retriever.invoke(query)
print(docs)

通过上述代码,我们可以看到多查询检索能够从多个角度生成查询,并从向量数据库中检索相关文档,从而提高找到有用信息的概率。

生成:打造高质量回答

最后,我们来到了 RAG 系统的生成环节。这一环节的目标是为语言模型提供尽可能与问题相关的上下文,避免无关信息引发“幻觉”。以下是一些提升生成质量的技巧。

8、自动裁剪:去除无关信息

自动裁剪技术可以过滤掉从数据库中检索到的与问题无关的信息,防止语言模型被误导。具体操作是,在检索时,根据相似度分数找到一个显著下降的临界点,排除分数低于该临界点的对象,从而确保传递给语言模型的信息都是最相关的。

动手实践自动裁剪

接下来,我们通过代码来实现自动裁剪。这里我们使用的是 LangChain 库中的 PineconeVectorStore 类,它允许我们利用 Pinecone 向量数据库进行相似性搜索,并根据相似度分数过滤信息。

from langchain_pinecone import PineconeVectorStore
from langchain_openai import OpenAIEmbeddings

# 初始化向量存储
vectorstore = PineconeVectorStore.from_documents(
    docs, index_name="sample", embedding=OpenAIEmbeddings()
)

# 执行相似性搜索并获取相似度分数
docs, scores = vectorstore.similarity_search_with_score("dinosaur")
for doc, score in zip(docs, scores):
    doc.metadata["score"] = score
print(docs)

通过上述代码,我们可以看到自动裁剪能够根据相似度分数过滤掉无关信息,从而提高传递给语言模型的信息质量。

9、重新排序:让重要信息优先

重新排序技术使用更高级的模型(通常是交叉编码器)重新评估和排序最初检索到的对象。它会考虑查询和每个对象之间的配对相似度,重新确定相关性,并将最相关的文档放在最前面。这样,语言模型接收到的数据质量更高,生成的回答也更精准。

动手实践重新排序

接下来,我们通过代码来实现重新排序。这里我们使用的是 LangChain 库中的 FlashrankRerank 类,它允许我们利用高级模型重新评估和排序检索到的文档。

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import FlashrankRerank

# 初始化重新排序器
compressor = FlashrankRerank()
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor, base_retriever=retriever
)

# 执行重新排序
query = "What did the president say about Ketanji Jackson Brown"
compressed_docs = compression_retriever.invoke(query)
print([doc.metadata["id"for doc in compressed_docs])
print(compressed_docs)

通过上述代码,我们可以看到重新排序能够根据查询和文档之间的相似度重新排序,从而提高传递给语言模型的信息质量。

10、微调语言模型:让模型更懂你的领域

对预训练的语言模型进行微调,可以显著提升检索性能。在特定领域(如医学)中,可以选择在相关数据上预训练的模型(如 MedCPT 系列),并收集自己的数据,创建正负样本对进行微调,让模型学会领域内的特定关系。经过微调的模型在特定领域的检索和生成任务中表现更出色。

动手实践微调语言模型

接下来,我们通过代码来实现微调语言模型。这里我们使用的是 LangChain 库中的 ChatOpenAI 类,它允许我们利用 OpenAI 的语言模型进行微调。

from langchain_openai import ChatOpenAI

# 初始化语言模型
chatgpt = ChatOpenAI(model_name="gpt-4o", temperature=0)

# 微调语言模型
# 这里需要提供自己的数据集进行微调
# 例如,使用医学领域的数据集进行微调
# fine_tuned_model = chatgpt.fine_tune(dataset)

通过上述代码,我们可以看到微调语言模型能够显著提升模型在特定领域的性能,从而提高生成回答的质量。

高级 RAG 技术:让 AI 回答更靠谱

通过上述一系列高级 RAG 技术,我们可以对 RAG 系统的各个环节——索引、检索和生成——进行优化升级,从而提升系统的整体性能。无论是医疗健康助手、教育辅导工具,还是企业知识管理机器人,这些技术都能让 AI 系统在处理复杂信息需求时更加得心应手,生成更准确、更可靠、更符合上下文的回答。

总之,随着应用场景的不断复杂化,AI 系统需要不断进化。高级 RAG 技术为我们提供了一种有效的途径,让我们能够打造出更智能、更强大的问答系统,让 AI 真正成为我们获取知识、解决问题的得力助手!


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询