【RAG】【retrievers08】基于Together.ai长上下文嵌入的混合检索
案例目标
本案例展示如何使用Together.ai的长上下文嵌入模型实现混合检索系统,结合块级别和文档级别的相似度计算,提高RAG系统的检索质量。通过将文档嵌入与块嵌入相结合,利用文档级别的语义信息辅助块级别的检索,从而获得更准确的检索结果。
技术栈与核心依赖
llama-index-embeddings-together
llama-index-llms-openai
llama-index-readers-web
llama-index-readers-file
llama-index-core
BeautifulSoup4
llama-index-vector-stores-chroma
openai
环境配置
# 安装必要的依赖
pip install llama-index-embeddings-together llama-index-llms-openai
pip install llama-index-readers-web llama-index-readers-file
pip install llama-index-core beautifulsoup4
pip install llama-index-vector-stores-chroma openai# 设置API密钥
import os
os.environ["TOGETHER_API_KEY"] = "your_together_api_key"
os.environ["OPENAI_API_KEY"] = "your_openai_api_key"
案例实现
1. 数据准备
步骤 1
使用BeautifulSoup爬取LlamaIndex文档,获取120个链接的内容:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
def get_all_links(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
links = []
for link in soup.find_all('a'):
href = link.get('href')
if href and href.startswith('/') and not href.startswith('//'):
full_url = urljoin(url, href)
links.append(full_url)
return links
步骤 2
使用AsyncHtmlLoader和Html2TextTransformer处理网页内容:
from llama_index.readers.web import AsyncHtmlLoader
from llama_index.core.node_parser import HTML2TextTransformer
loader = AsyncHtmlLoader(urls[:120])
docs = await loader.aload_data()
transformer = HTML2TextTransformer()
transformed_docs = transformer.transform_documents(docs)
2. 混合检索器实现
步骤 3
初始化Together嵌入模型:
from llama_index.embeddings.together import TogetherEmbedding
embed_model = TogetherEmbedding(
model_name="togethercomputer/m2-bert-80M-8k-retrieval",
api_key=os.environ["TOGETHER_API_KEY"]
)
步骤 4
创建自定义混合检索器类:
class HybridRetriever(BaseRetriever):
def __init__(
self,
vector_retriever,
doc_retriever,
alpha=0.5, # 平衡块和文档相似度的权重
):
self.vector_retriever = vector_retriever
self.doc_retriever = doc_retriever
self.alpha = alpha
super().__init__()
def _retrieve(self, query_bundle: QueryBundle):
# 获取块级别的检索结果
vector_nodes = self.vector_retriever.retrieve(query_bundle)
# 获取文档级别的检索结果
doc_nodes = self.doc_retriever.retrieve(query_bundle)
# 创建文档ID到文档节点的映射
doc_id_to_node = {node.node_id: node for node in doc_nodes}
# 计算混合分数
hybrid_nodes = []
for node in vector_nodes:
# 获取节点所属文档的ID
doc_id = node.metadata.get("doc_id")
if doc_id and doc_id in doc_id_to_node:
# 获取文档级别的相似度
doc_node = doc_id_to_node[doc_id]
doc_similarity = doc_node.score
# 计算混合分数
node_score = node.score
hybrid_score = (1 - self.alpha) * node_score + self.alpha * doc_similarity
# 更新节点分数
node.score = hybrid_score
hybrid_nodes.append(node)
# 按混合分数排序
hybrid_nodes.sort(key=lambda x: x.score, reverse=True)
return hybrid_nodes
3. 构建检索系统
步骤 5
创建文档级别的向量存储:
from llama_index.core import Document, VectorStoreIndex, StorageContext
from llama_index.core.node_parser import SentenceSplitter
# 创建文档对象
documents = [Document(text=doc.text, metadata={"doc_id": str(i)}) for i, doc in enumerate(transformed_docs)]
# 创建文档级别的节点解析器(不分割文档)
doc_parser = SentenceSplitter(chunk_size=1000000) # 设置一个很大的值,确保不分割
# 创建文档级别的索引
doc_index = VectorStoreIndex.from_documents(documents, embed_model=embed_model, transformations=[doc_parser])
doc_retriever = doc_index.as_retriever(similarity_top_k=10)
步骤 6
创建块级别的向量存储:
# 创建块级别的节点解析器
chunk_parser = SentenceSplitter(chunk_size=512)
# 创建块级别的索引
chunk_index = VectorStoreIndex.from_documents(documents, embed_model=embed_model, transformations=[chunk_parser])
vector_retriever = chunk_index.as_retriever(similarity_top_k=10)
步骤 7
创建混合检索器:
# 创建混合检索器
hybrid_retriever = HybridRetriever(
vector_retriever=vector_retriever,
doc_retriever=doc_retriever,
alpha=0.5 # 平衡块和文档相似度的权重
)
4. 查询与评估
步骤 8
执行查询并比较结果:
from llama_index.core.query_engine import RetrieverQueryEngine
# 创建查询引擎
query_engine = RetrieverQueryEngine(hybrid_retriever)
base_query_engine = chunk_index.as_query_engine(similarity_top_k=10)
# 执行查询
query_str = "What is the LLM interface in LlamaIndex?"
response = query_engine.query(query_str)
base_response = base_query_engine.query(query_str)
# 打印结果
print("混合检索结果:")
print(str(response))
print("\n基础检索结果:")
print(str(base_response))
案例效果
混合检索系统相比仅使用块级别检索的基线系统,在检索质量和答案相关性方面有明显提升。通过结合文档级别的语义信息,混合检索器能够更好地理解查询意图,并返回更相关的文档块。
混合检索器的优势
- 更准确的排序:文档级别的相似度信息帮助调整块级别的检索结果排序
- 上下文感知:考虑整个文档的语义,而不仅仅是单个块的局部信息
- 减少噪声:过滤掉来自不相关文档的高分块
- 灵活性:通过alpha参数可以灵活调整块级别和文档级别相似度的权重
案例实现思路
本案例的核心思路是通过结合两种不同粒度的检索策略来提高检索质量:
- 双粒度索引:同时构建文档级别和块级别的向量索引,分别捕获整体文档语义和局部文本细节
- 相似度融合:通过加权平均的方式融合两种粒度的相似度分数,平衡局部和全局信息
- 动态权重调整:通过alpha参数可以灵活调整两种相似度的权重,适应不同场景的需求
- 长上下文嵌入:利用Together.ai的长上下文嵌入模型,更好地处理长文档的语义表示
这种混合检索方法特别适用于处理长文档集合,其中单个文档包含多个相关但不连续的主题。通过考虑文档级别的语义,可以避免检索到来自不相关文档的高分块,从而提高整体检索质量。
扩展建议
- 多级混合检索:扩展到更多粒度级别,如段落级别、句子级别等
- 动态权重调整:根据查询类型或内容动态调整alpha参数
- 重排序集成:在混合检索结果基础上应用重排序模型进一步优化结果
- 查询扩展:结合查询扩展技术提高检索覆盖率
- 多模态混合:扩展到多模态内容,结合文本、图像等多种信息的检索
- 自适应检索:根据文档特征自动选择最适合的检索策略组合
总结
基于Together.ai长上下文嵌入的混合检索系统通过结合块级别和文档级别的检索策略,有效提高了RAG系统的检索质量。这种方法特别适用于处理长文档集合,能够同时考虑局部文本细节和整体文档语义,从而返回更相关的检索结果。
混合检索的核心优势在于它能够克服单一检索策略的局限性,通过多粒度信息的融合获得更全面的语义理解。随着长上下文嵌入模型的发展,这种混合检索方法将在处理复杂文档集合和多样化查询需求方面发挥越来越重要的作用。
