RAG重排序2026技术

RAG重排序2026技术:让最相关的信息浮出水面

引言 在RAG系统中,向量检索负责从海量文档中快速召回候选结果,但召回结果的排序不一定准确。重排序(Reranking)作为第二步,对候选结果进行更精确的排序,将最相关的文档排在最前面。 2026年,重排序已经成为RAG系统的标配组件。一个好的重排序模型可以将RAG的准确率提升20-40%。本文将深入探讨重排序技术。 一、为什么需要重排序 1.1 向量检索的局限 向量检索使用双塔模型(Bi-encoder):查询和文档分别编码,通过向量相似度排序。这种方式速度快,但精度有限——因为查询和文档的交互发生在最后的相似度计算阶段。 1.2 重排序的优势 重排序使用交叉编码器(Cross-encoder):将查询和文档拼接在一起输入模型,模型可以充分理解查询和文档的关系。精度更高,但速度更慢。 向量检索: encode(query) · encode(doc) → similarity score (快,精度中等) 重排序: encode([query, doc]) → relevance score (慢,精度高) 1.3 两阶段架构 查询 → 向量检索(召回Top-100)→ 重排序(精排Top-10)→ 生成 向量检索负责"快而全",重排序负责"准而精"。 二、重排序模型 2.1 交叉编码器 class CrossEncoderReranker: def __init__(self, model_name="cross-encoder/ms-marco-MiniLM-L-12-v2"): self.model = load_model(model_name) async def rerank(self, query, documents, top_k=10): # 1. 构建query-document对 pairs = [(query, doc.text) for doc in documents] # 2. 模型打分 scores = self.model.predict(pairs) # 3. 排序 ranked = sorted(zip(documents, scores), key=lambda x: -x[1]) return [doc for doc, score in ranked[:top_k]] 2.2 LLM重排序 class LLMReranker: def __init__(self, llm): self.llm = llm async def rerank(self, query, documents, top_k=10): # 方法1: 逐个打分 scores = [] for doc in documents: score = await self.score_document(query, doc) scores.append(score) # 方法2: 两两比较(更准确但更慢) # ranked = await self.pairwise_rank(query, documents) # 排序 ranked = sorted(zip(documents, scores), key=lambda x: -x[1]) return [doc for doc, score in ranked[:top_k]] async def score_document(self, query, document): """使用LLM对query-document对打分""" prompt = f""" 查询: {query} 文档: {document.text[:500]} 请评估文档与查询的相关性: 0 - 完全不相关 1 - 弱相关 2 - 部分相关 3 - 相关 4 - 高度相关 只输出数字。 """ result = await self.llm.call(prompt) return int(result.strip()) async def pairwise_rank(self, query, documents): """两两比较排序""" # 使用类似冒泡排序的方法 for i in range(len(documents)): for j in range(i+1, len(documents)): comparison = await self.compare(query, documents[i], documents[j]) if comparison == "B is better": documents[i], documents[j] = documents[j], documents[i] return documents 2.3 ColBERT ColBERT是一种延迟交互模型,介于双塔和交叉编码器之间: ...

2026-07-02 · 3 min · 623 words · 硅基 AGI 探索者
RAG分块策略对比

RAG分块策略对比:找到知识切分的最优解

引言 RAG系统的第一步是将文档分块(Chunking)。这个看似简单的步骤,实际上对RAG质量有巨大影响——分块太大,检索不精确;分块太小,丢失上下文。 2026年,分块策略已经从简单的"按字数切分"发展出多种智能策略。本文将系统对比这些策略。 一、为什么分块很重要 1.1 检索精度 文档: "Python是一种解释型语言。它由Guido van Rossum于1991年创建。Python强调代码可读性..." 分块A(太大大): 整个文档作为一个块 → 检索时可能返回很多不相关的内容 分块B(太小): "Python是一种解释型语言。" → 缺少上下文,不知道在说什么 分块C(合适): "Python是一种解释型语言。它由Guido van Rossum于1991年创建。" → 信息完整且精确 1.2 上下文完整性 好的分块应该保持语义完整性——一个块应该表达一个完整的意思。 1.3 检索效率 块的大小直接影响向量索引的大小和检索速度。 二、分块策略分类 2.1 固定大小分块 class FixedSizeChunker: def __init__(self, chunk_size=500, overlap=50): self.chunk_size = chunk_size # 字符数或token数 self.overlap = overlap def chunk(self, text): chunks = [] start = 0 while start < len(text): end = start + self.chunk_size chunk = text[start:end] chunks.append(chunk) start = end - self.overlap # 重叠部分 return chunks 优势:简单、快速、可预测。 劣势:可能在句子中间截断,破坏语义。 最佳实践:设置50-100 token的重叠,减少截断影响。 2.2 句子分块 class SentenceChunker: def __init__(self, max_sentences=5, max_tokens=500): self.max_sentences = max_sentences self.max_tokens = max_tokens def chunk(self, text): sentences = self.split_sentences(text) chunks = [] current_chunk = [] current_tokens = 0 for sentence in sentences: sent_tokens = self.count_tokens(sentence) if current_tokens + sent_tokens > self.max_tokens or \ len(current_chunk) >= self.max_sentences: if current_chunk: chunks.append(" ".join(current_chunk)) current_chunk = [sentence] current_tokens = sent_tokens else: current_chunk.append(sentence) current_tokens += sent_tokens if current_chunk: chunks.append(" ".join(current_chunk)) return chunks 优势:保持句子完整,语义更好。 劣势:块大小不均匀。 ...

2026-07-02 · 4 min · 767 words · 硅基 AGI 探索者
rag pipeline optimization

RAG 流水线优化全攻略:从检索到生成的极致调优

RAG 流水线全景 一个生产级 RAG 系统远不止"Embedding + 向量检索 + LLM 生成"这么简单。完整的优化链路: 用户查询 → 查询改写 → 多路召回 → 重排序 → 上下文压缩 → LLM 生成 → 流式输出 ↑ ↓ 缓存 引用标注 每个环节都有优化空间。下面逐一拆解。 1. 分块策略 分块决定了文档被切分成什么粒度的片段,直接影响检索精度。 固定长度分块 最简单的策略,按固定 token 数切分: from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=512, chunk_overlap=64, separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""] ) chunks = splitter.split_text(long_document) chunk_overlap 很关键:64-128 的重叠可以避免句子被截断导致语义丢失。但重叠太大会增加存储和检索冗余。 语义分块 按语义完整性切分,而非固定长度: from langchain.text_splitter import SemanticChunker from langchain.embeddings import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5") splitter = SemanticChunker( embeddings, breakpoint_threshold_type="percentile", # 或 "standard_deviation" breakpoint_threshold_amount=95 ) chunks = splitter.split_text(long_document) 语义分块在文档结构复杂时效果更好,但计算成本高(每句话都要算 Embedding)。 结构感知分块 利用文档结构(Markdown 标题、HTML 标签)分块: ...

2026-06-24 · 4 min · 685 words · 硅基 AGI 探索者
鲁ICP备2026018361号