一、概述
檢索增強生成(Retrieval-Augmented Generation,RAG)已成為大型語言模型(LLM)應用的重要架構,通過結合外部知識庫來增強模型的回答能力,特別是在處理專業領域知識、最新信息或企業私有數據時。本報告將系統梳理使用 Elasticsearch(ES)作為向量數據庫實現 RAG 系統的優缺點,與傳統向量數據庫及其他存儲解決方案的對比,以及基于 Deepseek V3 和 Qwen2.5 大模型的實現方案。
二、Elasticsearch 作為 RAG 向量數據庫的優缺點分析
1. 優點
低門檻的獨立技術棧
- 一站式解決方案:ES 能夠一站式完成向量生成、存儲、索引和檢索,通過配置即可實現大部分功能
- 成熟的生態系統:作為成熟的搜索引擎,擁有豐富的文檔、社區支持和工具集
- 簡化的部署和維護:相比需要部署多個組件的解決方案,ES 可以作為單一系統處理所有 RAG 相關任務
高性能和擴展性
- 分布式架構:支持百萬級 QPS 和千億級數據量
- 靈活的擴展能力:可以通過添加節點水平擴展,滿足不斷增長的數據需求
- 高可用性:內置的分片和復制機制確保系統的可靠性
混合檢索能力
- 文本與向量的結合:同時支持傳統的全文檢索和向量相似性搜索
- 提高檢索精度:混合檢索策略能夠顯著提升搜索結果的準確性和多樣性
- 處理短查詢優勢:對于短查詢,傳統關鍵詞搜索可以彌補純向量搜索的不足
豐富的數據處理能力
- 強大的文本分析:內置多種語言分析器,支持分詞、同義詞、停用詞等處理
- 結構化和非結構化數據支持:可以同時處理結構化字段和非結構化文本
- 聚合和分析功能:提供豐富的聚合功能,可用于數據分析和可視化
多種相似度計算算法支持
- 余弦相似度(Cosine Similarity):適用于文本語義搜索,不受向量長度影響
- 點積(Dot Product):適用于推薦系統,考慮向量長度和方向
- 歐幾里得距離(L2 Norm):適用于圖像特征、地理位置等場景
- 腳本評分(Script Score):支持自定義腳本實現更復雜的相似度計算
- 函數評分(Function Score):允許結合衰減函數、字段值等因素調整相似度分數
與大模型的無縫集成
- 簡化的集成流程:提供 API 和工具,便于與各種大模型集成
- 靈活的檢索配置:可以根據不同大模型的特點調整檢索策略
2. 不足
向量搜索性能限制
- 非專用架構:ES 的架構不是專為向量搜索設計的,在大規模向量搜索時性能可能不如專用向量數據庫
- 延遲問題:在大規模向量集上,搜索延遲通常為毫秒級,而專用向量數據庫可達微秒級
- 資源消耗較高:向量操作可能需要更多的內存和計算資源
向量功能相對有限
- 算法支持有限:支持的向量索引和搜索算法相對較少,主要是 HNSW
- 缺乏專業優化:缺少針對向量操作的專門優化,如 GPU 加速
- 向量維度限制:在處理超高維向量時可能存在效率問題
學習和配置復雜性
- 配置復雜:需要正確配置索引映射、分片策略等
- 調優難度:優化 ES 性能需要專業知識和經驗
- 維護成本:需要定期維護和監控集群狀態
存儲效率問題
- 存儲開銷:存儲向量數據可能需要更多空間
- 索引大小:包含向量的索引通常比純文本索引大得多
三、Elasticsearch 向量存儲與檢索原理
1. 向量數據存儲原理
Elasticsearch 使用?dense_vector?字段類型來存儲向量數據,其工作原理如下:
基本存儲結構
- 文檔結構:向量被存儲為文檔的一個字段,與其他字段(如文本、數字等)一起構成完整的文檔。
- 向量表示:向量以浮點數數組的形式存儲,每個數組元素對應向量的一個維度。
- 索引映射:通過索引映射定義向量字段的屬性,包括維度大小、索引方式和相似度計算方法。
存儲過程簡述
-
1.?向量生成:通過嵌入模型將文本、圖像等內容轉換為固定維度的向量。
-
2.?文檔創建:創建包含向量字段的文檔,將向量數據與其他元數據一起存儲。
-
3.?分片分配:ES 將文檔分配到不同的分片中,每個分片可以位于不同的節點上。
-
4.?磁盤存儲:向量數據最終以 Lucene 索引格式存儲在磁盤上,同時部分熱數據會緩存在內存中。
2. 向量檢索原理
Elasticsearch 主要使用 HNSW(Hierarchical Navigable Small World)算法進行向量檢索,這是一種近似最近鄰(ANN)搜索算法:
HNSW 算法簡介
- 多層圖結構:HNSW 構建一個多層的圖結構,頂層包含少量節點,底層包含所有節點。
- 導航原理:搜索從頂層開始,通過"貪心"策略快速找到大致方向,然后在下層進行更精細的搜索。
- 近似搜索:通過犧牲一定的精確度來換取顯著的性能提升。
檢索過程
-
1.?查詢向量生成:將用戶查詢轉換為向量表示。
-
2.?分片搜索:在每個相關分片上執行向量搜索。
-
3.?相似度計算:根據配置的相似度方法(余弦、點積或 L2 范數)計算查詢向量與索引向量的相似度。
-
4.?結果合并:將各分片的搜索結果合并,并按相似度排序。
-
5.?返回結果:返回最相似的文檔作為搜索結果。
性能優化參數
- ef_construction:構建索引時的精度參數,值越大索引質量越高但構建越慢。
- ef_search:搜索時的精度參數,值越大搜索結果越精確但速度越慢。
- m:每個節點的最大連接數,影響圖的連通性和搜索效率。
3. 多模態向量存儲支持
Elasticsearch 不僅可以存儲文本向量,還可以存儲來自圖片、音頻和視頻等多模態數據的向量表示:
圖像向量存儲
- 實現原理:使用圖像嵌入模型(如 ResNet、ViT 等)將圖像轉換為向量表示。
- 存儲方式:與文本向量相同,使用?dense_vector?字段存儲圖像向量。
- 應用場景:圖像相似度搜索、以圖搜圖、視覺內容檢索。
音頻向量存儲
- 實現原理:使用音頻嵌入模型(如 Wav2Vec、CLAP 等)將音頻轉換為向量表示。
- 存儲方式:同樣使用?dense_vector?字段,維度根據音頻嵌入模型而定。
- 應用場景:音樂推薦、語音搜索、音頻內容分類。
視頻向量存儲
- 實現原理:可以通過兩種方式處理:
- 將視頻分解為關鍵幀,然后使用圖像嵌入模型處理每個關鍵幀。
- 使用專門的視頻嵌入模型(如 Video Transformers)直接生成視頻向量。
- 存儲方式:可以存儲單個視頻的整體向量,或者存儲多個關鍵幀的向量序列。
- 應用場景:視頻內容搜索、相似視頻推薦、視頻分類。
多模態向量存儲的簡單示例
# 圖像向量存儲示例
from?PIL?import?Image
from?transformers?import?AutoFeatureExtractor, AutoModel
import?torch# 加載圖像嵌入模型
image_model = AutoModel.from_pretrained("openai/clip-vit-base-patch32")
feature_extractor = AutoFeatureExtractor.from_pretrained("openai/clip-vit-base-patch32")# 處理圖像
image = Image.open("example.jpg")
inputs = feature_extractor(images=image, return_tensors="pt")
with?torch.no_grad():image_features = image_model.get_image_features(**inputs)
image_vector = image_features[0].tolist()# 存儲到Elasticsearch
doc = {"title":?"示例圖片","description":?"這是一張示例圖片","image_path":?"example.jpg","image_vector": image_vector ?# 使用dense_vector字段存儲
}es.index(index="image-vectors", document=doc)
四、Elasticsearch 與其他向量存儲解決方案的對比
1. 各向量存儲解決方案綜合對比
特性 | Elasticsearch | PGVector (PostgreSQL) | Redis Vector | MongoDB Atlas | FAISS | Milvus | Pinecone | Qdrant | Chroma |
架構類型 | 分布式搜索引擎 | 關系型數據庫擴展 | 內存數據庫擴展 | 文檔數據庫擴展 | 庫/嵌入式 | 分布式向量數據庫 | 托管向量數據庫 | 向量數據庫 | 嵌入式向量數據庫 |
部署模式 | 自托管/云服務 | 自托管/云服務 | 自托管/云服務 | 自托管/云服務 | 嵌入式 | 自托管/云服務 | 僅云服務 | 自托管/云服務 | 嵌入式/自托管 |
向量索引算法 | HNSW | HNSW, IVF | HNSW | HNSW | HNSW, IVF, PQ 等多種 | HNSW, IVF, FLAT 等多種 | HNSW 變體 | HNSW | HNSW |
查詢性能 | 毫秒級 | 毫秒級 | 亞毫秒級 | 毫秒級 | 微秒-毫秒級 | 微秒-毫秒級 | 毫秒級 | 毫秒級 | 毫秒級 |
擴展性 | 優秀 | 有限 | 有限 | 優秀 | 有限 | 優秀 | 優秀 | 良好 | 有限 |
混合查詢 | 原生支持 | 支持 SQL+向量 | 有限支持 | 支持文檔+向量 | 不支持 | 支持 | 有限支持 | 支持 | 支持 |
元數據過濾 | 強大 | 強大(SQL) | 有限 | 強大(文檔查詢) | 有限 | 支持 | 支持 | 支持 | 支持 |
GPU 加速 | 不支持 | 不支持 | 不支持 | 不支持 | 支持 | 支持 | 內部支持 | 不支持 | 不支持 |
存儲容量 | TB-PB 級 | TB 級 | GB-TB 級 | TB-PB 級 | 受內存限制 | TB 級 | TB 級 | TB 級 | GB-TB 級 |
全文搜索 | 優秀 | 基礎 | 良好 | 基礎 | 不支持 | 有限 | 不支持 | 有限 | 有限 |
相似度算法 | 余弦、點積、L2 范數 | 余弦、L2 范數 | 余弦、L2 范數 | 余弦、點積、歐幾里得 | 多種(余弦、內積、L2 等) | 多種(余弦、內積、L2 等) | 余弦、點積 | 余弦、點積、歐幾里得 | 余弦 |
事務支持 | 有限 | 完整 ACID | 有限 | 文檔級事務 | 不支持 | 有限 | 不支持 | 不支持 | 不支持 |
易用性 | 中等 | 高(SQL 熟悉度) | 高 | 高(MongoDB 用戶) | 復雜 | 中等 | 簡單 | 簡單 | 簡單 |
維護成本 | 高 | 中等 | 低 | 中等 | 低(嵌入式) | 中 | 低(托管) | 中 | 低 |
社區支持 | 強大 | 強大 | 強大 | 強大 | 活躍 | 活躍 | 有限 | 活躍 | 活躍 |
成本 | 開源,自托管 | 開源,自托管 | 開源+商業版 | 開源+商業版 | 開源,免費 | 開源+商業版 | 商業版,按量付費 | 開源+商業版 | 開源,免費 |
適用場景 | 混合搜索 | 結構化數據+向量 | 高性能、低延遲 | 半結構化數據+向量 | 高性能向量搜索 | 大規模向量管理 | 簡單部署、高可用 | 中小規模應用 | 快速原型開發 |
2. 不同相似度計算方法對比
相似度方法 | 數學表達式 | 值域 | 適用場景 | 特點 | 支持的存儲系統 |
余弦相似度 | cos(θ) = A·B / (‖A‖·‖B‖) | [-1, 1] | 文本語義搜索 | 不受向量長度影響,只考慮方向 | 全部 |
點積 | A·B = ∑(A_i × B_i) | 無限制 | 推薦系統 | 考慮向量長度和方向,通常需要歸一化 | ES, MongoDB, FAISS, Milvus, Pinecone, Qdrant |
歐幾里得距離 | ‖A-B‖ = √(∑(A_i - B_i)2) | [0, ∞) | 圖像特征、地理位置 | 值越小表示越相似,需要轉換為相似度 | ES, PGVector, Redis, MongoDB, FAISS, Milvus, Qdrant |
曼哈頓距離 | ∑|A_i - B_i| | [0, ∞) | 網格空間、特征差異 | 計算簡單,對異常值不敏感 | FAISS, Milvus |
杰卡德相似度 | J(A,B) = |A∩B| / |A∪B| | [0, 1] | 集合比較、文檔相似度 | 適用于二進制特征或集合 | ES(腳本), PGVector(SQL) |
漢明距離 | H(A,B) = ∑(A_i ⊕ B_i) | [0, dim] | 二進制特征、錯誤檢測 | 計算二進制向量中不同位的數量 | FAISS |
3. 適用場景對比總結
解決方案 | 最適合場景 | 不適合場景 |
Elasticsearch | 需要混合搜索(文本+向量)、已有 ES 基礎設施、需要復雜文本處理 | 超大規模純向量搜索、極低延遲要求、GPU 加速需求 |
PGVector | 已有 PostgreSQL 基礎設施、需要事務支持、結構化數據+向量 | 超大規模向量集、分布式部署需求 |
Redis Vector | 超低延遲需求、緩存層向量搜索、臨時數據 | 持久化要求高、復雜查詢、大規模數據 |
MongoDB Atlas | 半結構化數據、文檔+向量混合、MongoDB 用戶 | 復雜文本分析、極高性能要求 |
FAISS | 純向量搜索、算法研究、GPU 加速、嵌入式應用 | 需要持久化、分布式部署、元數據過濾 |
Milvus | 大規模向量管理、需要豐富索引算法、混合查詢 | 簡單應用、資源受限環境 |
Pinecone | 快速部署、無運維需求、按需擴展 | 自托管需求、成本敏感、復雜查詢 |
Qdrant | 中小規模應用、需要良好元數據過濾、開源需求 | 超大規模、GPU 加速需求 |
Chroma | 快速原型開發、簡單應用、本地部署 | 企業級應用、大規模數據、高并發 |
五、基于 Elasticsearch 的 RAG 實現方案
1. 系統架構設計
基于 Elasticsearch 實現 RAG 系統的整體架構如下:
-
1.?數據處理層:負責文檔的收集、清洗、分塊和向量化
-
2.?存儲層:使用 Elasticsearch 存儲文本內容和向量表示
-
3.?檢索層:實現混合檢索策略,結合文本和向量搜索
-
4.?生成層:集成 Deepseek V3 或 Qwen2.5 大模型,基于檢索結果生成回答
-
5.?應用層:提供用戶界面和 API 接口
2. 實現步驟詳解
2.1 環境準備
首先,我們需要安裝必要的依賴:
# 安裝必要的庫
pip install elasticsearch langchain langchain-elasticsearch langchain-community langchain-openai
pip install deepseek-ai qwen-api
2.2 Elasticsearch 配置
設置 Elasticsearch 集群并創建適合 RAG 的索引,展示不同相似度計算方法:
from?elasticsearch?import?Elasticsearch# 連接到Elasticsearch
es = Elasticsearch(hosts=["http://localhost:9200"],basic_auth=("user",?"password"), ?# 如果有認證request_timeout=60
)# 創建索引映射,使用cosine相似度
cosine_index_mapping = {"mappings": {"properties": {"title": {"type":?"text",?"analyzer":?"standard"},"content": {"type":?"text",?"analyzer":?"standard"},"source": {"type":?"keyword"},"content_vector": {"type":?"dense_vector","dims":?1536, ?# 根據嵌入模型調整維度"index":?True,"similarity":?"cosine"??# 余弦相似度}}},"settings": {"number_of_shards":?2,"number_of_replicas":?1}
}# 創建使用余弦相似度的索引
es.indices.create(index="rag-knowledge-cosine", body=cosine_index_mapping)# 創建索引映射,使用dot_product相似度
dot_product_index_mapping = {"mappings": {"properties": {"title": {"type":?"text",?"analyzer":?"standard"},"content": {"type":?"text",?"analyzer":?"standard"},"source": {"type":?"keyword"},"content_vector": {"type":?"dense_vector","dims":?1536,"index":?True,"similarity":?"dot_product"??# 點積相似度}}},"settings": {"number_of_shards":?2,"number_of_replicas":?1}
}# 創建使用點積相似度的索引
es.indices.create(index="rag-knowledge-dot-product", body=dot_product_index_mapping)# 創建索引映射,使用l2_norm相似度
l2_norm_index_mapping = {"mappings": {"properties": {"title": {"type":?"text",?"analyzer":?"standard"},"content": {"type":?"text",?"analyzer":?"standard"},"source": {"type":?"keyword"},"content_vector": {"type":?"dense_vector","dims":?1536,"index":?True,"similarity":?"l2_norm"??# 歐幾里得距離(L2范數)}}},"settings": {"number_of_shards":?2,"number_of_replicas":?1}
}# 創建使用L2范數相似度的索引
es.indices.create(index="rag-knowledge-l2-norm", body=l2_norm_index_mapping)
2.3 文檔處理與索引
實現文檔的處理、分塊和向量化,并分別存儲到不同相似度計算方法的索引中:
import?os
from?langchain_community.document_loaders?import?DirectoryLoader, TextLoader
from?langchain.text_splitter?import?RecursiveCharacterTextSplitter
from?langchain_openai?import?OpenAIEmbeddings ?# 也可以使用其他嵌入模型# 加載文檔
loader = DirectoryLoader("./documents", glob="**/*.txt", loader_cls=TextLoader)
documents = loader.load()# 文檔分塊
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000,chunk_overlap=200,length_function=len,
)
chunks = text_splitter.split_documents(documents)# 初始化嵌入模型
embeddings = OpenAIEmbeddings() ?# 可替換為其他嵌入模型# 處理文檔并索引到不同的Elasticsearch索引
for?i, chunk?in?enumerate(chunks):# 生成向量嵌入vector = embeddings.embed_query(chunk.page_content)# 準備索引文檔doc = {"title":?f"Chunk?{i}?from?{chunk.metadata.get('source',?'unknown')}","content": chunk.page_content,"source": chunk.metadata.get("source",?"unknown"),"content_vector": vector}# 索引文檔到不同相似度計算方法的索引es.index(index="rag-knowledge-cosine", document=doc)# 對于dot_product,通常需要歸一化向量es.index(index="rag-knowledge-dot-product", document=doc)# 對于l2_norm,直接使用原始向量es.index(index="rag-knowledge-l2-norm", document=doc)# 刷新索引
es.indices.refresh(index="rag-knowledge-cosine")
es.indices.refresh(index="rag-knowledge-dot-product")
es.indices.refresh(index="rag-knowledge-l2-norm")
2.4 使用 LangChain 與 Elasticsearch 實現 RAG
下面是使用 LangChain 與 Elasticsearch 結合實現 RAG 的代碼,支持不同相似度計算方法:
from?langchain_elasticsearch?import?ElasticsearchStore
from?langchain.chains?import?RetrievalQA
from?langchain.prompts?import?PromptTemplate
from?langchain_core.runnables?import?RunnablePassthrough
from?langchain_core.output_parsers?import?StrOutputParser
from?langchain.schema?import?Document# 初始化不同相似度計算方法的Elasticsearch向量存儲
es_store_cosine = ElasticsearchStore(es_url="http://localhost:9200",index_name="rag-knowledge-cosine",embedding=embeddings,es_user="user", ?# 如果有認證es_password="password"??# 如果有認證
)es_store_dot_product = ElasticsearchStore(es_url="http://localhost:9200",index_name="rag-knowledge-dot-product",embedding=embeddings,es_user="user",es_password="password"
)es_store_l2_norm = ElasticsearchStore(es_url="http://localhost:9200",index_name="rag-knowledge-l2-norm",embedding=embeddings,es_user="user",es_password="password"
)# 創建不同相似度計算方法的檢索器
retriever_cosine = es_store_cosine.as_retriever(search_type="similarity",search_kwargs={"k":?5}
)retriever_dot_product = es_store_dot_product.as_retriever(search_type="similarity",search_kwargs={"k":?5}
)retriever_l2_norm = es_store_l2_norm.as_retriever(search_type="similarity",search_kwargs={"k":?5}
)# 創建多相似度混合檢索器
def?multi_similarity_retriever(query, top_k=5):# 從三種不同相似度方法獲取結果cosine_docs = retriever_cosine.get_relevant_documents(query)dot_product_docs = retriever_dot_product.get_relevant_documents(query)l2_norm_docs = retriever_l2_norm.get_relevant_documents(query)# 合并結果all_docs = cosine_docs + dot_product_docs + l2_norm_docs# 去重unique_docs =?list({doc.page_content: doc?for?doc?in?all_docs}.values())# 限制返回數量return?unique_docs[:top_k]# 定義提示模板
template =?"""
你是一個專業的AI助手。請基于以下提供的上下文信息,回答用戶的問題。
如果你無法從上下文中找到答案,請直接說"我無法從提供的信息中找到答案",不要編造信息。上下文信息:
{context}用戶問題: {question}回答:
"""prompt = PromptTemplate(template=template,input_variables=["context",?"question"]
)# 格式化文檔函數
def?format_docs(docs):return?"\n\n".join([doc.page_content?for?doc?in?docs])
2.5 集成 Deepseek V3 模型
from?langchain_deepseek?import?DeepseekChat# 初始化Deepseek模型
deepseek_llm = DeepseekChat(model_name="deepseek-ai/deepseek-v3",api_key="your_api_key",temperature=0.1
)# 構建RAG鏈(使用余弦相似度)
deepseek_rag_chain_cosine = ({"context": retriever_cosine | format_docs,?"question": RunnablePassthrough()}| prompt| deepseek_llm| StrOutputParser()
)# 構建RAG鏈(使用點積相似度)
deepseek_rag_chain_dot_product = ({"context": retriever_dot_product | format_docs,?"question": RunnablePassthrough()}| prompt| deepseek_llm| StrOutputParser()
)# 構建RAG鏈(使用L2范數相似度)
deepseek_rag_chain_l2_norm = ({"context": retriever_l2_norm | format_docs,?"question": RunnablePassthrough()}| prompt| deepseek_llm| StrOutputParser()
)# 構建RAG鏈(使用多相似度混合檢索)
def?answer_with_deepseek_multi_similarity(question):# 獲取多相似度混合檢索結果docs = multi_similarity_retriever(question)context = format_docs(docs)# 構建RAG鏈chain = ({"context": RunnablePassthrough(),?"question": RunnablePassthrough()}| prompt| deepseek_llm| StrOutputParser())# 生成回答return?chain.invoke({"context": context,?"question": question})# 示例
response = answer_with_deepseek_multi_similarity("什么是向量數據庫?")
print(response)
2.6 集成 Qwen2.5 模型
from?langchain_community.llms?import?QianfanLLMEndpoint# 初始化Qwen模型
qwen_llm = QianfanLLMEndpoint(model_name="qwen-2.5",api_key="your_api_key",secret_key="your_secret_key",temperature=0.1
)# 構建RAG鏈(使用余弦相似度)
qwen_rag_chain_cosine = ({"context": retriever_cosine | format_docs,?"question": RunnablePassthrough()}| prompt| qwen_llm| StrOutputParser()
)# 構建RAG鏈(使用點積相似度)
qwen_rag_chain_dot_product = ({"context": retriever_dot_product | format_docs,?"question": RunnablePassthrough()}| prompt| qwen_llm| StrOutputParser()
)# 構建RAG鏈(使用L2范數相似度)
qwen_rag_chain_l2_norm = ({"context": retriever_l2_norm | format_docs,?"question": RunnablePassthrough()}| prompt| qwen_llm| StrOutputParser()
)# 構建RAG鏈(使用多相似度混合檢索)
def?answer_with_qwen_multi_similarity(question):# 獲取多相似度混合檢索結果docs = multi_similarity_retriever(question)context = format_docs(docs)# 構建RAG鏈chain = ({"context": RunnablePassthrough(),?"question": RunnablePassthrough()}| prompt| qwen_llm| StrOutputParser())# 生成回答return?chain.invoke({"context": context,?"question": question})# 示例
response = answer_with_qwen_multi_similarity("Elasticsearch的主要優勢是什么?")
print(response)
3. 高級功能實現
3.1 實現混合搜索策略
def?enhanced_hybrid_search(query, es_client, index_name, embedding_model, similarity_type="cosine"):# 生成查詢向量query_vector = embedding_model.embed_query(query)# 根據不同相似度類型構建腳本if?similarity_type ==?"cosine":similarity_script =?"cosineSimilarity(params.query_vector, 'content_vector') + 1.0"elif?similarity_type ==?"dot_product":similarity_script =?"dotProduct(params.query_vector, 'content_vector')"elif?similarity_type ==?"l2_norm":# 對于L2范數,較小的值表示更相似,所以我們使用負值或倒數similarity_script =?"1 / (1 + l2Norm(params.query_vector, 'content_vector'))"else:raise?ValueError(f"不支持的相似度類型:?{similarity_type}")# 構建混合查詢hybrid_query = {"query": {"bool": {"should": [# 向量搜索部分{"script_score": {"query": {"match_all": {}},"script": {"source": similarity_script,"params": {"query_vector": query_vector}}}},# 文本搜索部分{"match": {"content": {"query": query,"boost":?0.5??# 調整文本搜索的權重}}}]}},"size":?5}# 執行搜索response = es_client.search(index=index_name, body=hybrid_query)# 處理結果results = []for?hit?in?response["hits"]["hits"]:results.append({"score": hit["_score"],"title": hit["_source"]["title"],"content": hit["_source"]["content"],"source": hit["_source"]["source"]})return?results
3.2 實現自適應相似度選擇
def?adaptive_similarity_search(query, es_client, embedding_model):# 分析查詢特征,決定使用哪種相似度方法query_length =?len(query.split())# 短查詢(1-3個詞)可能更適合關鍵詞搜索if?query_length <=?3:# 使用混合搜索,但給文本搜索更高權重results = enhanced_hybrid_search(query,?es_client,?"rag-knowledge-cosine",?embedding_model,similarity_type="cosine")# 中等長度查詢(4-10個詞)elif?query_length <=?10:# 使用余弦相似度,適合一般語義搜索results = enhanced_hybrid_search(query,?es_client,?"rag-knowledge-cosine",?embedding_model,similarity_type="cosine")# 長查詢(>10個詞)else:# 對于長查詢,點積可能更適合捕捉更多語義信息results = enhanced_hybrid_search(query,?es_client,?"rag-knowledge-dot-product",?embedding_model,similarity_type="dot_product")return?results
3.3 實現多步檢索策略
def?multi_step_retrieval(query, es_client, embedding_model):# 第一步:使用文本搜索獲取初步結果text_query = {"query": {"match": {"content": query}},"size":?20}text_response = es_client.search(index="rag-knowledge-cosine", body=text_query)# 提取文檔ID和內容candidate_docs = []for?hit?in?text_response["hits"]["hits"]:candidate_docs.append({"id": hit["_id"],"content": hit["_source"]["content"]})# 如果文本搜索沒有足夠結果,添加向量搜索if?len(candidate_docs) <?10:# 生成查詢向量query_vector = embedding_model.embed_query(query)# 向量搜索vector_query = {"query": {"script_score": {"query": {"match_all": {}},"script": {"source":?"cosineSimilarity(params.query_vector, 'content_vector') + 1.0","params": {"query_vector": query_vector}}}},"size":?20?-?len(candidate_docs)}vector_response = es_client.search(index="rag-knowledge-cosine", body=vector_query)# 添加到候選文檔for?hit?in?vector_response["hits"]["hits"]:# 避免重復if?not?any(doc["id"] == hit["_id"]?for?doc?in?candidate_docs):candidate_docs.append({"id": hit["_id"],"content": hit["_source"]["content"]})# 第二步:對候選文檔進行重新排序# 生成查詢向量query_vector = embedding_model.embed_query(query)# 為每個候選文檔計算相似度分數for?doc?in?candidate_docs:# 生成文檔向量doc_vector = embedding_model.embed_query(doc["content"])# 計算余弦相似度similarity = cosine_similarity([query_vector],[doc_vector])[0][0]doc["similarity_score"] = similarity# 按相似度排序ranked_docs =?sorted(candidate_docs, key=lambda?x: x["similarity_score"], reverse=True)# 返回前5個最相關文檔return?ranked_docs[:5]
3.4 實現查詢擴展和改寫
def?query_expansion(original_query, llm):# 使用大模型擴展查詢expansion_prompt =?f"""請基于以下原始查詢,生成3個不同的擴展查詢,以便更全面地搜索相關信息。每個擴展查詢應該保留原始查詢的核心意圖,但可以添加相關術語、同義詞或上下文。原始查詢:?{original_query}擴展查詢(每行一個):"""# 獲取擴展查詢response = llm.invoke(expansion_prompt)# 解析響應expanded_queries = [q.strip()?for?q?in?response.split("\n")?if?q.strip()]# 過濾掉可能的空行或無關行expanded_queries = [q?for?q?in?expanded_queries?if?len(q) >?5]# 確保至少有一個查詢(原始查詢)if?not?expanded_queries:expanded_queries = [original_query]else:# 添加原始查詢expanded_queries.append(original_query)return?expanded_queriesdef?multi_query_retrieval(original_query, es_client, embedding_model, llm):# 擴展查詢expanded_queries = query_expansion(original_query, llm)# 對每個擴展查詢執行搜索all_results = []for?query?in?expanded_queries:results = enhanced_hybrid_search(query,?es_client,?"rag-knowledge-cosine",?embedding_model)all_results.extend(results)# 去重unique_results = []seen_contents =?set()for?result?in?all_results:if?result["content"]?not?in?seen_contents:unique_results.append(result)seen_contents.add(result["content"])# 重新排序(可以基于原始查詢的相似度)query_vector = embedding_model.embed_query(original_query)# 為每個結果計算與原始查詢的相似度for?result?in?unique_results:# 假設我們可以從ES獲取內容的向量content_vector_query = {"query": {"term": {"_id": result["id"]}},"_source": ["content_vector"]}vector_response = es_client.search(index="rag-knowledge-cosine", body=content_vector_query)content_vector = vector_response["hits"]["hits"][0]["_source"]["content_vector"]# 計算相似度similarity = cosine_similarity([query_vector],[content_vector])[0][0]result["final_score"] = similarity# 按最終分數排序ranked_results =?sorted(unique_results, key=lambda?x: x["final_score"], reverse=True)# 返回前5個最相關結果return?ranked_results[:5]
六、總結與結論
1. Elasticsearch 作為 RAG 向量數據庫的優勢總結
-
1.?全棧解決方案:Elasticsearch 提供了從數據索引到檢索的完整解決方案,無需集成多個系統。
-
2.?混合檢索能力:結合傳統文本搜索和向量搜索的能力是其最大優勢,能夠顯著提高檢索質量。
-
3.?成熟的生態系統:作為成熟的搜索引擎,擁有豐富的文檔、工具和社區支持。
-
4.?多模態支持:能夠存儲和檢索文本、圖像、音頻和視頻的向量表示,支持多模態 RAG 應用。
-
5.?靈活的相似度計算:支持多種相似度計算方法(余弦、點積、L2 范數),適應不同應用場景。
2. 實施建議
-
1.?選擇合適的相似度計算方法:
- 文本語義搜索:優先使用余弦相似度
- 推薦系統:考慮使用點積
- 圖像或地理位置搜索:考慮使用 L2 范數
-
2.?優化索引配置:
- 根據數據量和查詢頻率調整分片數量
- 為頻繁查詢的索引配置足夠的副本
- 合理設置 HNSW 參數,平衡搜索精度和性能
-
3.?實現混合檢索策略:
- 結合向量搜索和關鍵詞搜索
- 根據查詢特征動態調整搜索策略
- 考慮使用查詢擴展和多步檢索提高召回率
- 4.?性能優化:
- 對于大規模部署,考慮使用專用節點
- 監控和調整 JVM 堆大小
- 定期優化索引
3. 未來發展方向
-
1.?與專用向量數據庫的集成:
- 考慮 Elasticsearch 與專用向量數據庫(如 FAISS、Milvus)的混合架構
- 利用 ES 的文本處理和混合查詢能力,結合專用向量數據庫的高性能向量搜索
-
2.?知識圖譜增強:
- 將向量搜索與知識圖譜結合,實現 GraphRAG
- 利用實體關系增強檢索結果的相關性和可解釋性
-
3.?多模態 RAG 應用:
- 擴展到圖像、音頻和視頻內容的檢索增強生成
- 實現跨模態檢索,如以文搜圖、以圖搜文
- 4.?自適應檢索策略:
- 開發能夠根據查詢特征和上下文自動選擇最佳檢索策略的系統
- 利用強化學習優化檢索參數
4. 結論
Elasticsearch 作為 RAG 系統的向量數據庫具有顯著優勢,特別是在需要混合檢索、多模態支持和成熟生態系統的場景下。雖然在純向量搜索性能上可能不如專用向量數據庫,但其全面的功能和靈活性使其成為構建企業級 RAG 系統的有力選擇。
通過合理配置和優化,結合本報告提供的實現方案,可以充分發揮 Elasticsearch 在 RAG 系統中的潛力,為大模型應用提供高質量的知識檢索支持。
對于需要構建 RAG 系統的團隊,建議根據具體需求和現有技術棧選擇合適的向量存儲解決方案,Elasticsearch 特別適合已有 ES 基礎設施、需要混合檢索能力以及處理多模態數據的場景。
?
【大模型介紹電子書】
快速揭秘DeepSeek背后的AI工作原理
要獲取本書全文PDF內容,請在VX后臺留言:“AI大模型基礎” 或者 “大模型基礎” 就會獲得電子書的PDF。