检索增强生成(RAG)技术的深度调优指南
检索增强生成(RAG)技术优化与实践 RAG将信息检索与生成模型结合,解决大模型知识局限问题。本文从原理到实践探讨RAG优化的关键环节: 知识库构建:需统一格式、高质量内容,采用动态语义分块技术(如BERT计算相似度)替代传统固定分块,并合理选择向量数据库(FAISS、Pinecone等)。 意图识别与查询改写:使用BERT分类模型识别用户意图,结合对话历史上下文,通过LLM将模糊查询改写为明确形
检索增强生成(Retrieval-Augmented Generation, RAG) 是一种结合信息检索与生成式模型的技术,旨在提升AI回答的准确性和时效性 。在实际应用中,RAG系统常常面临"答非所问"、"幻觉"等问题,这主要源于知识库构建不当、检索策略不优化以及意图识别不准确。本文将从RAG的基本原理出发,深入探讨其各个关键环节的调优逻辑和最佳实践,包括知识库构建、检索优化、意图识别、query改写重写、混合检索、查结构化数据、rerank模型以及后处理等技术细节。
一、RAG的基本原理与框架
RAG的核心思想是将大语言模型的语言生成能力与外部知识库相结合。其工作流程可分为三步:首先根据用户查询从知识库中检索相关文档或片段;然后将检索到的信息与查询语境整合;最后模型基于整合信息生成回答 。这种技术有效解决了大模型预训练数据存在限制、缺乏实时知识或垂直专业知识的问题。
在RAG系统中,知识库的构建是基础且关键的环节。一个高质量的知识库应具备以下特点:文档格式统一、内容质量高、语义清晰、元数据丰富以及跨系统整合。具体来说,应将知识库中的文档统一转换为结构化的格式(如Markdown、JSON或纯文本),并设置规范化的命名规则(如"[部门]-[年份]-[主题].md") 。同时,为每份文档添加元数据(如关键词、创建日期、适用场景),帮助RAG精准匹配查询 。
分块策略是知识库构建中的核心技术。传统方法通常采用固定大小的分块,但这会导致上下文被截断和语义不完整的问题。更优的方案是结合语义理解模型进行动态分块,例如使用BERT或GTE模型计算句子嵌入并设置相似度阈值 。以下是一个基于BERT的动态分块代码示例:
from transformers import BertTokenizer, BertModel
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
def dynamic_chunking(text, max_chunk_size=128, similarity_threshold=0.7):
# 将文本分割为句子
sentences = text.split('. ')
chunks = []
current_chunk = []
# 计算每个句子的嵌入向量
with torch.no_grad():
sentence_embeddings = []
for sent in sentences:
inputs = tokenizer(sent, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
sentence_embeddings.append(outputs.last_hidden_state.mean(dim=1).numpy())
# 动态分块
for i, sent in enumerate(sentences):
if i == 0:
current_chunk.append(sent)
continue
# 计算当前句子与当前块的平均相似度
current_avg_emb = np.mean([sentence_embeddings[j] for j in range(len(current_chunk))], axis=0)
similarity = cosine_similarity([current_avg_emb], [sentence_embeddings[i]])[0][0]
if similarity >= similarity_threshold and len(' '.join(current_chunk)) + len(sent) <= max_chunk_size:
current_chunk.append(sent)
else:
chunks.append(' '.join(current_chunk))
current_chunk = [sent]
if current_chunk:
chunks.append(' '.join(current_chunk))
return chunks
向量数据库选型也是知识库构建的重要环节。目前主流的向量数据库包括FAISS、Pinecone、pgVector等,它们各有优缺点:
向量数据库 | 优势 | 劣势 | 适用场景 |
---|---|---|---|
FAISS | 速度快、适合本地部署 | 扩展性有限、不适合大规模数据 | 研发环境、小规模应用 |
Pinecone | 高扩展性、适合云场景 | 成本较高、配置复杂 | 大规模生产环境 |
pgVector | 与PostgreSQL无缝集成、可靠性高 | 向量检索性能不如专用数据库 | 需要关系型数据与向量数据结合的场景 |
二、意图识别与Query改写重写
意图识别是RAG系统中的关键环节,它能帮助系统理解用户查询的真实需求,从而选择更合适的检索策略和改写方法。意图识别通常采用文本分类模型,如BERT结合前馈神经网络(FFN),对用户查询进行分类 。以下是一个基于BERT的意图识别模型代码框架:
from transformers import BertTokenizer, BertForSequenceClassification
import torch
import numpy as np
# 加载预训练模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=5)
def intent_recognition(query):
# 对查询进行编码
inputs = tokenizer(query, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 计算分类概率
probabilities = torch.softmax(outputs.logits, dim=1).numpy()[0]
intent_labels = ['fact_query', 'summary', 'inference', 'command', 'others']
# 返回意图标签和概率
return {label: prob for label, prob in zip(intent_labels, probabilities)}
意图识别后,需要根据不同的意图类型对原始Query进行改写重写。Query改写的主要目的是将模糊、不明确或不完整的查询转换为更适合检索的形式 ,例如:
# Query改写提示模板
query_rewrite_template = """You are an AI assistant tasked with reformulating user queries to improve retrieval in a RAG system.
Given the original query and the intent of the query, rewrite it to be more specific, detailed, and likely to retrieve relevant information.
Original query: {original_query}
Intent: {intent}
Rewritten query:"""
# 使用LLM进行Query改写
def rewrite_query(original_query, intent):
rewritten_query = get_llm_response(
query_rewrite_template.format(
original_query=original_query,
intent=intent
)
)
return rewritten_query
在实际应用中,上下文感知的Query改写尤为重要。例如,当用户在对话过程中间询问"昨天的会议怎么样?“,系统需要结合对话历史(如"9月14日预算会议”)将其改写为更详细的版本:“9月14日预算会议的主要决策是什么?” 。在LangChain中,可以通过Memory
组件实现:
from langchain的记忆 import ConversationBufferWindowMemory
from langchain.链式 import LLMChain
from langchain.提示 import PromptTemplate
from langchain社区.模型 import ChatOpenAI
# 创建记忆组件,保存最近的对话历史
memory = ConversationBufferWindowMemory(k=3)
# Query改写提示模板
rewrite_prompt = PromptTemplate(
inputVariables=["query", "chat_history"],
template="""
You are an AI assistant that helps reformulate user queries for better retrieval in a RAG system.
Given the original query and the conversation history, rewrite the query to be more specific and detailed.
Original query: {query}
Conversation history: {chat_history}
Rewritten query:"""
)
# 创建Query改写链
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rewrite_chain = LLMChain llm=llm, prompt=rewrite_prompt, memory=memory
意图识别和Query改写是RAG调优的第一步,它们直接影响后续检索的质量和效率。通过这些技术,系统能够更好地理解用户需求,生成更准确的检索结果。
三、混合检索策略与结构化数据检索
混合检索是RAG系统中提高检索准确性的关键技术。传统的向量检索在处理精确匹配和短文本查询时存在局限性 ,而关键词检索则在处理语义相似性时表现不佳。因此,结合两种检索方法的优势,形成混合检索策略,能够显著提高RAG系统的性能。
在Elasticsearch中,可以同时利用向量检索(kNN)和BM25关键词检索,然后通过加权融合的方式合并结果:
from elasticsearch import Elasticsearch
import numpy as np
es = Elasticsearch()
# 定义混合检索函数
def hybrid_search(query, alpha=0.5):
# 1. BM25关键词检索
bm25_result = es.search(
index="your_index",
body={
"query": {
"match": {
"content": query
}
},
"size": 10
}
)
# 2. 向量检索
# 假设已经将query向量化
vector = get_embedding(query)
knn_result = es.search(
index="your_index",
body={
"query": {
"knn": {
"field": "vector_field",
"k": 10,
"num_candidates": 100,
"query_vector": vector
}
}
}
)
# 3. 结果合并与加权排序
combined_results = bm25_result["hits"]["hits"] + knn_result["hits"]["hits"]
# 计算混合得分
for doc in combined_results:
bm25_score = doc "_score"
knn_score = doc "_source" ["knn_score"]
doc ["hybrid_score"] = alpha * bm25_score + (1 - alpha) * knn_score
# 去重并排序
unique_results = []
seen_ids = set()
for doc in combined_results:
if doc "_id" not in seen_ids:
seen_ids.add(doc "_id")
unique_results.append(doc)
# 根据混合得分排序
sorted_results = sorted(unique_results, key=lambda x: x["hybrid_score"], reverse=True)
return sorted_results[:10]
混合检索的权重调整是优化的关键。根据实验数据,不同场景下的最优alpha值有所不同:
场景类型 | 推荐alpha值 | 说明 |
---|---|---|
事实查询 | 0.7-0.8 | 更重视向量检索的语义理解 |
指令执行 | 0.3-0.4 | 更重视关键词检索的精确匹配 |
复杂推理 | 0.6-0.7 | 平衡语义理解和关键词匹配 |
除了混合检索外,结构化数据检索也是RAG系统的重要组成部分。结构化数据(如数据库、表格)通常包含精确的信息,如订单ID、产品价格等。将结构化数据与非结构化文本共同纳入RAG系统,能够提供更全面的答案。
在LlamaIndex中,可以使用HybridRetriever
实现混合检索:
from llama_index import VectorStoreIndex, SimpleDirectoryReader
from llama_index社区.检索器 import HybridRetriever
from llama_index社区.检索器.向量检索器 import VectorStoreRetriever
from llama_index社区.检索器 bm25 检索器 import BM25Retriever
# 加载文档并构建索引
documents = SimpleDirectoryReader('data').load_data()
vectorstore_index = VectorStoreIndex.fromDocuments(documents)
# 创建向量检索器和BM25检索器
vector_retriever = VectorStoreRetriever(vectorstore_index=vectorstore_index)
bm25_retriever = BM25Retriever(vectorstore_index=vectorstore_index)
# 创建混合检索器
hybrid_retriever = HybridRetriever(
vectorstore_retriever=vector_retriever,
bm25_retriever=bm25_retriever
)
# 执行混合检索
results = hybrid_retriever.invoke("查询订单号12345的配送状态")
对于结构化数据,可以将其转换为文本块并添加到知识库中。例如,将数据库记录转换为"订单ID: 12345, 配送状态: 已发货, 发货时间: 2025-09-20"这样的格式。然后,通过在检索时设置适当的参数(如body_search
),可以提高结构化数据的检索准确率。
from langchain社区.向量存储器 import Cassandra
from langchain_openai import OpenAIEmbeddings
# 创建向量存储并添加结构化数据
embeddings = OpenAIEmbeddings()
vectorstore = Cassandra(
embedding=embeddings,
table_name="orders",
body_index_options=[STANDARD_ANALYZER]
)
# 将数据库记录转换为文本块
def convert_database_record_to_text(record):
return f"订单ID: {record['order_id']}, 客户名称: {record['customer_name']}, 配送状态: {record['status']}, 发货时间: {record['ship_time']}"
# 添加结构化数据
database_records = get_database_records() # 从数据库获取记录
text_chunks = [convert_database_record_to_text(record) for record in database_records]
vectorstore.add_texts(text_chunks)
四、Rerank模型与后处理优化
Rerank模型是RAG系统中提高检索结果质量的关键技术。它通过对初步检索结果进行重新排序,筛选出最相关的文档,从而提高最终生成回答的质量 。目前主流的Rerank模型包括基于传统信息检索的BM25算法、基于交叉编码器的神经排序模型以及基于强化学习的动态排序模型。
交叉编码器是当前最流行的Rerank模型之一,它能够直接计算Query-Document对的匹配得分 。在LangChain中,可以使用CrossEncoderReranker
实现:
from langchain.检索器 import ContextualCompressionRetriever
from langchain.检索器.文档压缩器 import CrossEncoderReranker
from langchain社区.交叉编码器 import HuggingFaceCrossEncoder
# 加载交叉编码器模型
model = HuggingFaceCrossEncoder("BAAI/bge-reranker-base")
# 创建Rerank压缩器
compressor = CrossEncoderReranker(model=model, top_n=3)
# 创建上下文压缩检索器
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=your_retriever
)
# 执行检索并压缩
compressedDocs = compression_retriever.invoke("What is the plan for the economy?")
除了交叉编码器外,基于强化学习的Rerank模型也是当前的研究热点。通过强化学习,模型可以学习到更符合人类偏好的排序策略。以下是使用Ray RLlib框架训练Rerank模型的代码框架:
import ray
from ray import tune
from ray.rllib代理商.ppo import PPOTrainer
from ray.rllib环境 import SimpleCorridor
# 定义自定义环境
class RerankEnv(SimpleCorridor):
def __init__(self, config):
# 定义状态空间和动作空间
# 状态空间可以是Query-Document对的特征向量
# 动作空间是文档的排序
super().__init__(config)
def reset(self):
# 重置环境,返回初始状态
return super().reset()
def step(self, action):
# 执行动作,返回新的状态、奖励、终止信号等
# 奖励可以根据文档与Query的相关性来设计
return super().step(action)
# 初始化Ray
ray.init()
# 配置PPO算法
config = {
"env": RerankEnv,
"env_config": {
"corridor_length": 5
},
"num_workers": 1,
"framework": "torch",
"lr": tune.grid_search([0.01, 0.001, 0.0001])
}
# 训练Rerank模型
trainer = PPOTrainer(config=config, env=RerankEnv)
for i in range(1000):
result = trainer.train()
if i % 100 == 0:
checkpoint = trainer.save()
print(f"Checkpoint saved at {checkpoint}")
# 评估模型
env = RerankEnv({"corridor_length": 5})
obs, info = env.reset()
while not terminated:
action = trainer.compute_action(obs)
obs, reward, terminated, truncated, info = env.step(action)
print(f"Action: {action}, Reward: {reward}")
在实际应用中,上下文压缩是后处理优化的重要环节。通过压缩检索结果,可以减少LLM需要处理的Token数量,提高生成效率 。以下是几种常用的上下文压缩方法:
# 1. LLMChainFilter:仅过滤无关文档
from langchain.检索器.文档压缩器 import LLMChainFilter
filter = LLMChainFilter.from_llm ChatOpenAI(model_name="gpt-4o-mini", temperature=0)
compression_retriever = ContextualCompressionRetriever(
base_compressor=filter,
base_retriever=your_retriever
)
# 2. LLMListwiseRerank:使用LLM对文档进行重排序
from langchain.检索器.文档压缩器 import LLMListwiseRerank
filter = LLMListwiseRerank.from_llm ChatOpenAI(model_name="gpt-4o-mini", temperature=0), top_n=1
compression_retriever = ContextualCompressionRetriever(
base_compressor=filter,
base_retriever=your_retriever
)
# 3. EmbeddingsFilter:基于嵌入相似度过滤文档
from langchain.检索器.文档压缩器 import EmbeddingsFilter
from langchain_openai import OpenAIEmbeddings
filter = EmbeddingsFilter(
embeddings=OpenAIEmbeddings(),
similarity_threshold=0.76
)
compression_retriever = ContextualCompressionRetriever(
base_compressor=filter,
base_retriever=your_retriever
)
结构化数据与RAG的结合需要特别注意。当检索到结构化数据时,可以使用以下方法提高其与Query的匹配度:
# 结构化数据的改写
def rewrite结构化_data_query(query, data_type):
prompt = f"你是一个专门处理{data_type}数据的专家。请根据以下查询,生成一个更适合检索的查询:{query}"
rewritten_query = get_llm_response(prompt)
return rewritten_query
# 结构化数据的优先检索
def优先检索结构化_data(query):
# 先检查是否包含结构化数据关键词
struct_keywords = ["订单", "价格", "数量", "ID", "时间"]
if any(keyword in query for keyword in struct_keywords):
# 如果是结构化数据查询,优先使用结构化数据检索
return rewrite结构化_data_query(query, "结构化")
else:
return query
五、意图识别与Query改写的深度优化
意图识别是RAG系统中理解用户需求的关键环节。多任务联合模型是当前意图识别的主流方法,它通过共享参数的方式同时处理意图识别和槽位填充任务,提高模型的性能 。以下是一个基于BERT的多任务联合模型代码框架:
from transformers import BertForTokenClassification, BertTokenizer
import torch
import numpy as np
# 加载预训练模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForTokenClassification.from_pretrained('bert-base-uncased', num_labels=5)
def joint_intent槽位填充实体识别(query):
# 对查询进行编码
inputs = tokenizer(query, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 获取意图和槽位信息
intent_scores = outputs.logits[0, 0] # [CLS]标记的意图得分
intent = np.argmax(intent_scores.numpy())
slot_scores = outputs.logits[0, 1:] # 其他标记的槽位得分
slots = np.argmax[slot_scores.numpy(), axis=1]
return intent, slots
意图识别后,上下文感知的Query改写需要考虑对话历史中的信息。在LangChain中,可以通过ConversationSummaryBufferMemory
实现:
from langchain.记忆 import ConversationSummaryBufferMemory
from langchain.链式 import LLMChain
from langchain.提示 import PromptTemplate
from langchain社区.模型 import ChatOpenAI
# 创建记忆组件,保存对话摘要
memory = ConversationSummaryBufferMemory(
llm=ChatOpenAI(model_name="gpt-3.5-turbo"),
max_token_limit=40
)
# Query改写提示模板
rewrite_prompt = PromptTemplate(
inputVariables=["query", "chat_history"],
template="""
你是一个AI助手,负责优化用户查询以提高RAG系统的检索效果。
给定原始查询和对话历史,生成一个更具体、详细的查询,以检索相关信息。
原始查询:{query}
对话历史:{chat_history}
改写后的查询:"""
)
# 创建Query改写链
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rewrite_chain = LLMChain llm=llm, prompt=rewrite_prompt, memory=memory
在实际应用中,Query改写的质量评估也很重要。可以通过以下方法评估改写效果:
def评估Query改写效果(original_query, rewritten_query, ground truth):
# 计算改写后的Query与原始Query的相似度
similarity = calculate_similarity(original_query, rewritten_query)
# 计算改写后的Query与地面真相的相似度
accuracy = calculate_similarity(rewritten_query, ground truth)
return similarity, accuracy
六、混合检索的高级策略
混合检索是RAG系统中提高检索准确性的高级策略。多级检索是混合检索的一种重要实现方式,它通过分层次检索来缩小范围,提高检索效率 。以下是多级检索的两种主要实现方式:
# 1. 总分层级索引:先检索摘要,再在相关组内检索文档块
def hierarchical_retrieval(query, top_k=5):
# 第一层检索:摘要匹配
abstract_results = vectorstore abstract.search(query, k=10)
relevantAbstracts = [result for result in abstract_results if result "_score" > 0.5]
# 第二层检索:在相关组内检索文档块
document_results = []
for abstract in relevantAbstracts:
document块 = vectorstore documents.search(f"related_to_{abstract '_id'}", k=5)
document_results.extend(document块)
# 去重并排序
unique_results = []
seen_ids = set()
for doc in document_results:
if doc "_id" not in seen_ids:
seen_ids.add(doc "_id")
unique_results.append(doc)
# 根据得分排序并返回前top_k
sorted_results = sorted(unique_results, key=lambda x: x "_score", reverse=True)
return sorted_results[:top_k]
# 2. 父子层级索引:先检索子块,再回溯父块获取完整上下文
def hierarchical_retrieval(query, top_k=5):
# 检索子块
child_results = vectorstore child.search(query, k=10)
# 回溯父块
parent_ids = set([child "_source" ["parent_id"] for child in child_results])
parent_results = vectorstore parent.search(parent_ids, k=len(parent_ids))
# 合并结果
combined_results = child_results + parent_results
# 去重并排序
unique_results = []
seen_ids = set()
for doc in combined_results:
if doc "_id" not in seen_ids:
seen_ids.add(doc "_id")
unique_results.append(doc)
# 根据得分排序并返回前top_k
sorted_results = sorted(unique_results, key=lambda x: x "_score", reverse=True)
return sorted_results[:top_k]
动态混合检索是另一种高级策略,它根据Query的特征动态调整检索策略 。例如,对于包含专有名词的短文本Query,可以增加关键词检索的权重;对于长文本或语义复杂的Query,可以增加向量检索的权重。
# 动态混合检索
def dynamic_hybrid_search(query):
# 分析Query特征
query_length = len(query.split())
keyword密度 = count_keywords(query) / query_length
# 动态调整权重
if query_length < 5 and keyword密度 > 0.8:
alpha = 0.3 # 更重视关键词检索
elif query_length > 15:
alpha = 0.8 # 更重视向量检索
else:
alpha = 0.5 # 平衡两种检索
# 执行混合检索
return hybrid_search(query, alpha=alpha)
在实际应用中,混合检索的评估指标也很重要。可以通过以下指标评估混合检索的效果:
def评估混合检索效果(queries, ground truths):
precision_at_k = []
recall_at_k = []
f1_at_k = []
for query, ground truth in zip(queries, ground truths):
results = hybrid_search(query)
relevant_results = [result for result in results if result "_id" in ground truth]
precision = len(relevant_results) / len(results)
recall = len(relevant_results) / len(ground truth)
f1 = 2 * (precision * recall) / (precision + recall)
precision_at_k.append(precision)
recall_at_k.append(recall)
f1_at_k.append(f1)
return {
"precision_at_k": np.mean(precision_at_k),
"recall_at_k": np.mean(recall_at_k),
"f1_at_k": np.mean(f1_at_k)
}
七、Rerank模型的训练与优化
Rerank模型的训练需要考虑多种因素,包括模型架构、训练数据、损失函数和评估指标等。对于基于交叉编码器的Rerank模型,通常使用二分类或回归任务进行训练。以下是使用Hugging Face库训练交叉编码器Rerank模型的代码框架:
from transformers import CrossEncoder, CrossEncoderModel, CrossEncoderTokenizer
import torch
from torch import nn
from torch.utils.data import DataLoader
from datasets import load_dataset
# 加载预训练模型和分词器
model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-12-v2")
tokenizer = CrossEncoderTokenizer.from_pretrained("cross-encoder/ms-marco-MiniLM-L-12-v2")
# 定义训练数据
train_dataset = load_dataset("marco", split="train")
trainLoader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 定义损失函数
criterion = nn.BCEWithLogitsLoss()
# 定义优化器
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
# 训练循环
for epoch in range(3):
for batch in trainLoader:
# 获取Query-Document对和标签
queries = batch["query"]
documents = batch["document"]
labels = batch["label"]
# 计算模型输出
inputs = tokenizer(queries, documents, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 计算损失
loss = criterion(outputs.logits, labels.float())
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch+1} completed, loss: {loss.item()}")
对于基于强化学习的Rerank模型,训练过程更为复杂。以下是使用Ray RLlib训练PPO算法的Rerank模型的代码框架:
import ray
from ray import tune
from ray.rllib代理商.ppo import PPOTrainer
from ray.rllib环境 import SimpleCorridor
# 定义自定义环境
class RerankEnv(SimpleCorridor):
def __init__(self, config):
# 定义状态空间和动作空间
# 状态空间可以是Query-Document对的特征向量
# 动作空间是文档的排序
super().__init__(config)
def reset(self):
# 重置环境,返回初始状态
return super().reset()
def step(self, action):
# 执行动作,返回新的状态、奖励、终止信号等
# 奖励可以根据文档与Query的相关性来设计
return super().step(action)
# 初始化Ray
ray.init()
# 配置PPO算法
config = {
"env": RerankEnv,
"env_config": {
"corridor_length": 5
},
"num_workers": 1,
"framework": "torch",
"lr": 1e-4,
"gamma": 0.99,
"clip_param": 0.2
}
# 训练Rerank模型
trainer = PPOTrainer(config=config, env=RerankEnv)
for i in range(1000):
result = trainer.train()
if i % 100 == 0:
checkpoint = trainer.save()
print(f"Checkpoint saved at {checkpoint}")
# 评估模型
env = RerankEnv({"corridor_length": 5})
obs, info = env.reset()
while not terminated:
action = trainer.compute_action(obs)
obs, reward, terminated, truncated, info = env.step(action)
print(f"Action: {action}, Reward: {reward}")
在实际应用中,Rerank模型的优化也很重要。可以通过以下方法优化Rerank模型:
- 特征工程:在输入中添加额外的特征,如文档长度、创建时间、来源可信度等
- 多任务学习:同时训练意图识别和文档重排序任务
- 在线学习:根据用户反馈动态调整模型参数
- 模型蒸馏:使用轻量级模型替代大型模型,提高推理速度
# 特征工程增强的Rerank模型
def enhanced_rerank_model(query, documents, additional_features):
# 将额外特征添加到输入中
inputs = tokenizer(
query,
[doc["text"] + " " + features for doc, features in zip(documents, additional_features)],
return_tensors="pt",
padding=True,
truncation=True
)
# 计算模型输出
outputs = model(**inputs)
# 返回排序结果
return sorted(zip(outputs.logits扁平化().numpy(), documents), key=lambda x: x[0], reverse=True)
八、结构化数据与RAG的整合
结构化数据与RAG的整合是提高RAG系统在特定领域(如金融、医疗、电商等)表现的关键。结构化数据通常包含精确的信息,如订单ID、产品价格、医疗诊断等,而RAG系统可以通过以下方法整合结构化数据:
- 文档与结构化数据混合检索:在检索阶段同时考虑文档和结构化数据
- 结构化数据改写:将结构化数据转换为自然语言描述
- 结构化数据优先检索:根据Query特征优先检索结构化数据
- 结构化数据与LLM的交互:通过SQL或API直接访问结构化数据,再与LLM结合
以下是文档与结构化数据混合检索的代码示例:
from langchain.检索器 import VectorStoreRetriever, BM25Retriever
from langchain.检索器.混合检索器 import HybridRetriever
# 创建向量检索器和BM25检索器
vector_retriever = VectorStoreRetriever(vectorstore=vectorstore)
bm25_retriever = BM25Retriever bm25_index=bm25_index
# 创建混合检索器
hybrid_retriever = HybridRetriever(
vectorstore_retriever=vector_retriever,
bm25_retriever=bm25_retriever,
alpha=0.5 # 向量和BM25的权重
)
# 执行混合检索
results = hybrid_retriever.invoke("查询订单号12345的配送状态")
对于结构化数据的改写,可以使用以下方法:
# 结构化数据改写
def rewrite结构化_data(data_type, data):
prompt = f"你是一个专门处理{data_type}数据的专家。请将以下数据转换为自然语言描述:{data}"
rewritten_data = get_llm_response(prompt)
return rewritten_data
# 结构化数据优先检索
def优先检索结构化_data(query):
# 检查是否包含结构化数据关键词
struct_keywords = ["订单", "价格", "数量", "ID", "时间"]
if any(keyword in query for keyword in struct_keywords):
# 如果是结构化数据查询,优先使用结构化数据检索
return rewrite结构化_data_query(query, "结构化")
else:
return query
在实际应用中,结构化数据与RAG的整合需要考虑多种因素,包括数据格式、检索效率和生成质量等。以下是几种常用的结构化数据整合方法:
# 1. 将结构化数据转换为文本块
def convert_database_record_to_text(record):
return f"订单ID: {record['order_id']}, 客户名称: {record['customer_name']}, 配送状态: {record['status']}, 发货时间: {record['ship_time']}"
# 2. 使用LLM直接访问结构化数据
from langchain.工具 import SQLDatabase, SQLDatabaseChain
# 创建SQL数据库工具
db = SQLDatabase.from_uri("postgresql://user:password@localhost:5432/mydatabase")
sql_chain = SQLDatabaseChain.from_llm ChatOpenAI(model_name="gpt-4"), db=db)
# 执行SQL查询
sql_response = sql_chain.invoke("SELECT status FROM orders WHERE order_id=12345")
# 将SQL结果转换为自然语言
prompt = f"根据以下订单信息,回答用户关于订单号12345配送状态的问题:{sql_response}"
answer = get_llm_response(prompt)
九、后处理优化与效果评估
后处理优化是RAG系统中提高生成质量的关键环节。通过优化后处理流程,可以减少LLM的"幻觉",提高回答的准确性和可靠性。以下是几种常用的后处理优化方法:
# 1. 明确提示LLM仅基于提供的上下文回答
def optimize_prompt(query, context):
prompt = f"你是一名智能客服。你的目标是提供准确的信息,并尽可能帮助提问者解决问题。你应保持友善,但不要过于啰嗦。请根据提供的上下文信息,在不考虑已有知识的情况下,回答相关查询:{query}\n\n上下文信息:{context}"
return prompt
# 2. 使用Few-shot方法指导LLM利用检索到的知识
def few shot_optimize_prompt(query, context):
prompt = f"以下是一些示例,展示了如何根据提供的上下文信息回答问题:\n\n示例1:\n查询:公司2025年战略规划\n上下文:公司2025年战略规划包括数字化转型、全球化布局和可持续发展\n回答:根据公司2025年战略规划,公司将在三个方面推进发展:数字化转型、全球化布局和可持续发展。\n\n示例2:\n查询:RAG技术的优缺点\n上下文:RAG技术能够提高回答准确性,但也会增加计算成本\n回答:RAG技术的主要优势是能够提高回答准确性,但其缺点是会增加计算成本。因此,在实际应用中需要权衡利弊,选择合适的实施方式。\n\n现在,请根据以下上下文信息回答用户的问题:{query}\n\n上下文信息:{context}"
return prompt
# 3. Self-RAG方法:检索评分和反思评分
def self_rag_optimize(query, context, rerank_model):
# 检索评分
retrieval_score = rerank_model涉入(query, context)
# 生成答案
answer = get_llm_response(query, context)
# 反思评分
reflection prompt = f"评估以下回答与查询的相关性:{answer}\n查询:{query}\n评估标准:1-非常相关,0-不相关"
reflection_score = rerank_model涉入(reflection prompt)
# 根据评分选择最佳答案
if retrieval_score > 0.8 and reflection_score > 0.7:
return answer
else:
return "我无法提供准确的信息,请您提供更多背景或尝试重新表述问题。"
在实际应用中,RAG系统的效果评估也很重要。可以通过以下指标评估RAG系统的效果:
def评估RAG系统效果(queries, ground truths, rerank_model):
precision_at_k = []
recall_at_k = []
f1_at_k = []
rerankimprovement = []
for query, ground truth in zip(queries, ground truths):
# 初始检索
initial_results = vectorstore abstract.search(query, k=10)
initial_relevant = [result for result in initial_results if result "_id" in ground truth]
# Rerank后检索
rerank_results = rerank_model涉入(query, initial_results)
rerank_relevant = [result for result in rerank_results if result "_id" in ground truth]
# 计算指标
precision = len(rerank_relevant) / len(rerank_results)
recall = len(rerank_relevant) / len(ground truth)
f1 = 2 * (precision * recall) / (precision + recall)
rerank_improvement = (len(rerank_relevant) - len(initial_relevant)) / len(initial_relevant)
precision_at_k.append(precision)
recall_at_k.append(recall)
f1_at_k.append(f1)
rerankimprovement.append(rerank_improvement)
return {
"precision_at_k": np.mean(precision_at_k),
"recall_at_k": np.mean(recall_at_k),
"f1_at_k": np.mean(f1_at_k),
"rerank_improvement": np.mean(rerankimprovement)
}
后处理优化还可以通过以下方法实现:
# 1. 上下文压缩
def compress_context(context, max_tokens=2000):
# 使用LLM对上下文进行压缩
prompt = f"请将以下上下文信息压缩到{max_tokens}个token以内,保留关键信息:{context}"
compressed_context = get_llm_response(prompt)
return compressed_context
# 2. 答案验证
def validate_answer(answer, query, context):
# 检查答案是否基于提供的上下文
prompt = f"验证以下回答是否基于提供的上下文:{answer}\n查询:{query}\n上下文:{context}"
validation_response = get_llm_response(prompt)
# 解析验证结果
if "是的" in validation_response:
return True
else:
return False
# 3. 答案融合
def fuse_answers(answers, query):
# 使用LLM融合多个答案
prompt = f"请根据以下多个回答,生成一个更准确、更全面的回答:{query}\n\n回答列表:{answers}"
fused_answer = get_llm_response(prompt)
return fused_answer
十、RAG系统的设计与实施建议
基于上述分析,以下是RAG系统的设计与实施建议:
知识库构建建议:
- 采用统一的文档格式(如Markdown)和命名规则,便于检索和管理
- 使用动态语义分块策略,避免固定分块导致的上下文截断问题
- 为文档添加丰富的元数据(如关键词、创建日期、适用场景),提高检索准确性
- 根据数据规模和场景需求选择合适的向量数据库(如FAISS、Pinecone或pgVector)
- 将结构化数据转换为文本块并添加到知识库中,提高特定领域的表现
检索优化建议:
- 根据Query特征动态调整混合检索的权重(alpha值)
- 实现多级检索策略(总分层级或父子层级),提高检索效率
- 结合意图识别结果选择适当的检索策略
- 对于包含专有名词的短文本Query,增加关键词检索的权重
- 对于长文本或语义复杂的Query,增加向量检索的权重
后处理优化建议:
- 明确提示LLM仅基于提供的上下文回答问题
- 使用Few-shot方法指导LLM利用检索到的知识
- 实现Self-RAG方法,通过检索评分和反思评分提高回答质量
- 对检索结果进行上下文压缩,减少LLM需要处理的Token数量
- 对生成的答案进行验证,确保其基于提供的上下文
意图识别与Query改写建议:
- 使用BERT等预训练模型进行意图识别
- 根据意图识别结果选择适当的Query改写策略
- 实现上下文感知的Query改写,考虑对话历史中的信息
- 对于意图不明确的Query,可以生成多个改写版本并行检索
- 对于复杂问题,可以将Query分解为多个子Query并分别检索
混合检索与结构化数据整合建议:
- 实现文档与结构化数据的混合检索,提高系统在特定领域的表现
- 根据Query特征动态选择优先检索结构化数据或文档
- 使用交叉编码器或强化学习模型进行文档重排序
- 对于结构化数据,可以使用SQL或API直接访问,再与LLM结合
- 实现结构化数据的改写,将其转换为自然语言描述
通过上述优化策略,RAG系统可以在保持大语言模型强大生成能力的同时,显著提高回答的准确性和可靠性,解决"幻觉"和"答非所问"等问题。
十一、未来发展趋势与挑战
随着大语言模型和检索技术的不断发展,RAG系统也将面临新的挑战和机遇。未来RAG技术的主要发展趋势包括:
- 知识图谱与RAG的深度融合:通过知识图谱捕捉实体之间的复杂关系和层次结构,提高RAG系统在复杂查询场景下的表现
- 多模态RAG系统:整合文本、图像、音频等多种模态的数据,提高系统在多模态场景下的表现
- 端到端RAG训练:将检索和生成过程统一进行训练,提高系统的整体性能
- 动态权重调整:根据Query特征和用户反馈动态调整检索策略的权重
- 轻量化RAG系统:通过模型蒸馏和量化等技术,提高系统的推理速度和部署灵活性
然而,RAG系统仍面临一些挑战,包括:
- 知识库更新:如何及时更新知识库,确保信息的准确性和时效性
- 长上下文处理:如何处理长对话历史和大规模上下文,避免信息过载
- 多语言支持:如何在多语言环境下构建和检索知识库
- 成本优化:如何在保证性能的同时,降低RAG系统的计算和存储成本
- 可解释性:如何提高RAG系统的可解释性,让用户理解回答的来源和依据
更多推荐
所有评论(0)