LangChain与Llama-Index联动:解锁多重检索RAG新技能‌

LangChain与Llama-Index联动:解锁多重检索RAG新技能‌

在RAG(Retrieval-Augmented Generation)架构中,如何同时利用多种检索方式,提升生成质量和检索覆盖面,是一个前沿话题。LangChain 作为引领 LLM 应用开发的框架,提供了丰富的链式调用和检索器适配;而 Llama-Index(又名 GPT Index)则专注于构建灵活、高效的索引结构,支持多种检索后端。本文将带你从原理到实战,讲解如何将 LangChain 与 Llama-Index 联动,打造一个“多重检索”RAG 系统,并配以代码示例Mermaid 流程图详细说明,帮助你快速上手。


目录

  1. 背景与目标
  2. 核心组件概览

    1. LangChain 简介
    2. Llama-Index 简介
  3. 多重检索RAG架构

    1. 架构原理
    2. Mermaid 流程图
  4. 环境准备与依赖安装
  5. 构建数据源与索引

    1. 文本数据准备
    2. 向量索引与全文检索索引
  6. LangChain 中的检索器配置

    1. 基于向量的检索器
    2. 基于关键词或全文的检索器
  7. Llama-Index 中的索引构建与查询

    1. 节点索引(GPTSimpleVectorIndex)
    2. 树形索引(GPTTreeIndex)
    3. 结合全文搜索引擎(如 ElasticSearch)
  8. 多重检索管道示例

    1. 设计思路
    2. 代码示例:整合 LangChain + Llama-Index
  9. 完整流程演示

    1. 初始化 LLM 与工具
    2. 构建检索—生成链(Retrieval Chain)
    3. 执行查询并解析结果
  10. 调优与注意事项

    1. 检索器权重与融合策略
    2. 索引更新与数据刷新
    3. 并行检索与性能优化
    4. 对话上下文与缓存
  11. 小结

1. 背景与目标

在大规模文本库中,仅依赖单一的向量检索或全文检索,往往难以同时兼顾召回率与精确度。多重检索(Multi-Retrieval)通过将多种检索策略(如向量近邻检索+关键词匹配+实体索引等)组合起来,可以在兼顾语义召回的同时,也保证对准确性或时效性要求较高的场景表现更好。

  • 场景示例

    • 技术文档库:向量检索可召回相关度高的章节,全文检索可精准匹配关键代码片段。
    • 常见问答库:向量检索能处理自然语言模糊查询,全文检索能保证针对“特定术语/编号”的定位。
    • 知识库搜人:向量检索基于简介文本检索,全文检索则命中具体姓名或 ID。

目标

  1. 使用 Llama-Index 构建多种索引(例如向量索引与树形索引、全文索引)。
  2. LangChain 中同时引入这些检索器,通过融合策略获取多路检索结果。
  3. 将多路检索结果统一传给 LLM,提升 RAG 生成的准确度与丰富度。

2. 核心组件概览

2.1 LangChain 简介

LangChain 是目前最活跃的 LLM 应用框架之一,具备以下特点:

  • 链式调用(Chain):将检索、LLM 生成、后处理等步骤用“链”串联起来。
  • 丰富的检索器适配:内置对 OpenAI、Milvus、Weaviate、Chroma 等向量库的支持,也能对接自定义检索接口。
  • 工具流程(Tool):可以把检索、问答、计算等功能做成“工具”,由 LLM 调度。
  • Prompt 管理与记忆模块:支持将历史对话、检索结果等信息拼接到 Prompt 中。
简言之,LangChain 为我们提供了一个“可组合、可扩展”的 RAG 架构蓝图。

2.2 Llama-Index 简介

Llama-Index(又名 GPT Index)侧重于灵活索引结构的构建,并分离了“索引”与“查询”两大核心。其特色包括:

  • 索引多样性:支持 GPTSimpleVectorIndex(向量索引)、GPTTreeIndex(树形索引)、GPTKeywordTableIndex(关键词索引)、GPTListIndex(列表索引)等。
  • 抽象数据流:从原始文档 → 文本分割 → 索引构建 → 查询调用,每一步都对用户开放定制。
  • 与向量数据库集成:可以将向量索引结果同步到 Milvus/ElasticSearch/FAISS 等。
  • 可选全文检索插件:可以结合 ElasticSearch、Weaviate 等外部全文检索引擎。
Llama-Index 的定位是“让文档索引与查询变得一行代码可调用”,非常适合做复杂索引结构的快速搭建与实验。

3. 多重检索RAG架构

3.1 架构原理

我们要实现的多重检索RAG,大致包含以下步骤:

  1. 文档预处理:将文档切分成适合 Llama-Index 的 Document 单元。
  2. 构建多种索引

    • 向量索引:利用 Llama-Index 的 GPTSimpleVectorIndex,并存储到向量数据库(如 Milvus)。
    • 关键词或树形索引:用 GPTKeywordTableIndexGPTTreeIndex 建立一种“主题+节点”索引,支持按目录层级或关键词进行检索。
    • 全文检索(可选):结合 ElasticSearch,通过 Llama-Index 插件把每个 Chunk 同步到 ES。
  3. LangChain 检索器配置:在 LangChain 中,构造多个检索器对象:

    • 向量检索器(调用 Llama-Index 向量索引后的 query)。
    • 关键词检索器(调用 Llama-Index 关键词索引后的 query)。
    • 全文检索器(调用 ES API 或 Llama-Index ES 插件)。
  4. 融合策略:将多个检索器的 Top-K 结果进行去重、打分或简单合并,得到最终的上下文片段列表。
  5. LLM 生成:将融合后的上下文片段拼接到 Prompt 中,调用 LLM(如 OpenAI GPT-4)生成答案。

这样的好处在于:

  • 兼顾召回率与精确性:向量检索善于“语义召回”,关键词/全文检索擅长“精准定位”。
  • 多样化结果补充:不同检索器返回的结果类型互补,能丰富上下文。
  • 灵活可扩展:未来可继续增加新的检索器,比如基于知识图谱的检索。

3.2 Mermaid 流程图

flowchart TB
  subgraph 索引构建阶段
    A1[原始文档集合] --> A2[文本分割与清洗]
    A2 --> A3a[向量索引构建 (GPTSimpleVectorIndex)]
    A2 --> A3b[关键词/树形索引构建 (GPTKeywordTableIndex/GPTTreeIndex)]
    A2 --> A3c[同步到 ElasticSearch (可选)]
  end

  subgraph 多重检索阶段
    B1[用户输入 Query] --> B2a[LangChain 向量检索器] 
    B1 --> B2b[LangChain 关键词检索器]
    B1 --> B2c[LangChain 全文检索器 (ES)]
    B2a --> C[结果融合与去重]
    B2b --> C
    B2c --> C
    C --> D[拼接上下文 + Prompt 构建]
    D --> E[LLM 生成答案]
    E --> F[返回给用户]
  end
上图分为两个阶段:索引构建阶段(左侧)和多重检索阶段(右侧)。在实际系统运行时,索引构建通常是离线或定时任务,而多重检索阶段则是在线请求流程。

4. 环境准备与依赖安装

以下以 Python 3.9+ 为例,示范如何安装所需依赖。

# 1. 创建并激活虚拟环境(推荐)
python3 -m venv langchain_llamaenv
source langchain_llamaenv/bin/activate

# 2. 安装基础依赖
pip install --upgrade pip setuptools

# 3. 安装 LangChain
pip install langchain

# 4. 安装 Llama-Index(GPT Index)
pip install llama-index

# 5. 安装 OpenAI SDK(用于 LLM 调用)
pip install openai

# 6. 安装可选向量数据库依赖(以 Milvus 为例)
pip install pymilvus

# 7. 安装可选 ElasticSearch 客户端
pip install elasticsearch

# 8. 安装额外工具(可视化、数据处理)
pip install pandas numpy tqdm

# 9. 确保 API Key 环境变量已配置
export OPENAI_API_KEY="你的_OpenAI_Key"
如果你只想先在本地完成小规模实验,也可跳过 Milvus 或 ElasticSearch 部分,直接使用 Llama-Index 内置的 storage_context 存储向量索引。

5. 构建数据源与索引

5.1 文本数据准备

假设我们有一批技术文档,格式为多个 Markdown 文件或 TXT 文本,存放于 ./docs/ 目录。示例文件结构:

docs/
├─ doc1.md
├─ doc2.txt
├─ subfolder/
│   ├─ doc3.md
│   └─ ...

我们要将所有文本读入并做基本清洗,包括:

  • 去除空行、特殊符号
  • 按 “段落” 或 “固定字符数” 将文件切分成 text_chunks

下面给出一个简单的文本加载与切分函数示例:

import os
from typing import List
from llama_index import Document

def load_and_split_documents(data_dir: str, chunk_size: int = 1000, overlap: int = 200) -> List[Document]:
    """
    加载 data_dir 下的所有文本文件,按 chunk_size 切分,重叠 overlap 个字符。
    返回 Llama-Index 需要的 Document 列表。
    """
    documents = []
    for root, _, files in os.walk(data_dir):
        for file in files:
            if not file.endswith((".md", ".txt")):
                continue
            file_path = os.path.join(root, file)
            with open(file_path, "r", encoding="utf-8") as f:
                text = f.read()
            # 简单去除多余空白
            text = "\n".join([line.strip() for line in text.splitlines() if line.strip()])
            # 切分
            start = 0
            length = len(text)
            while start < length:
                end = start + chunk_size
                chunk_text = text[start:end]
                doc = Document(text=chunk_text, metadata={"source": file_path})
                documents.append(doc)
                start = end - overlap  # 保持 overlap 重叠
    return documents

# 示例调用
docs = load_and_split_documents("./docs", chunk_size=1000, overlap=200)
print(f"已生成 {len(docs)} 个文本块 (Documents).")
  • Document 对象:Llama-Index 中的基本单位,包含 textmetadata(如来源文件路径、文档 ID 等)字段。

5.2 向量索引与全文检索索引

5.2.1 构建向量索引(GPTSimpleVectorIndex)

向量索引可直接存储到本地的 storage_context 中,或同步到外部数据库。以下示例先使用本地简单索引,再演示如何将索引传给 Milvus。

from llama_index import SimpleDirectoryReader, GPTSimpleVectorIndex, StorageContext, load_index_from_storage

# 如果你已经生成好了 Document 列表 (docs),则直接用 Document 构建索引:
from llama_index import VectorStoreIndex
from llama_index.vector_stores import FAISSVectorStore

# 方法 A: 使用本地 FAISS 存储
def build_local_vector_index(documents):
    # 创建 FAISS 向量存储
    vector_store = FAISSVectorStore.from_documents(documents)
    # 用向量存储构建索引
    index = VectorStoreIndex(documents, vector_store=vector_store)
    index.storage_context.persist("./index_storage")
    return index

index = build_local_vector_index(docs)
print("向量索引已构建并保存到 ./index_storage")

# 方法 B: 将向量索引写入 Milvus(假设 Milvus 已启动)
from llama_index.vector_stores import MilvusVectorStore

def build_milvus_vector_index(documents, milvus_collection_name="langchain_collection"):
    # 初始化 Milvus 向量存储
    vector_store = MilvusVectorStore.from_documents(
        documents,
        collection_name=milvus_collection_name,
        index_args={"index_type": "IVF_FLAT", "metric_type": "IP", "params": {"nlist": 128}},
        connection_args={"host": "127.0.0.1", "port": "19530"},
    )
    # 构建索引
    index = VectorStoreIndex(documents, vector_store=vector_store)
    index.storage_context.persist("./index_storage_milvus")
    return index

milvus_index = build_milvus_vector_index(docs)
print("向量索引已构建并保存到 ./index_storage_milvus (Milvus).")
  • VectorStoreIndex:Llama-Index 2.0 中新引入的类,替换了老版本中的 GPTSimpleVectorIndex,但使用思路相同。
  • FAISSVectorStore:使用 FAISS 在本地进行向量索引。
  • MilvusVectorStore:将索引写入 Milvus,以便多实例或分布式部署。

5.2.2 构建关键词/树形索引(GPTKeywordTableIndex / GPTTreeIndex)

假设我们想在文档中按“章节标题”或“关键字表”做一种快速导航式检索,可以使用 GPTKeywordTableIndex

from llama_index import GPTKeywordTableIndex, LLMPredictor, PromptHelper, ServiceContext

# 1. 先定义一个简单的 LLM Predictor,供索引在构建时使用(可使用 OpenAI)
from openai import OpenAI
llm = OpenAI(model="gpt-3.5-turbo", temperature=0)

# 2. 构造服务上下文
prompt_helper = PromptHelper(
    max_input_size=4096,
    num_output=512,
    max_chunk_overlap=200
)
service_context = ServiceContext.from_defaults(
    llm_predictor=LLMPredictor(llm=llm),
    prompt_helper=prompt_helper
)

def build_keyword_index(documents):
    index = GPTKeywordTableIndex.from_documents(
        documents,
        service_context=service_context,
        index_structure_kwargs={"threshold": 1, "num_children": 3}
    )
    index.storage_context.persist("./keyword_index")
    return index

keyword_index = build_keyword_index(docs)
print("关键词索引已构建并保存到 ./keyword_index")
  • GPTKeywordTableIndex:自动生成“关键字→文档块”映射表,供后续按关键字快速检索。
  • GPTTreeIndex:则会将文档切分为树状层级索引,适合章节式分布的文档。构建用法类似。
小提示:关键词索引更适合“区分度较高的术语检索”;树形索引更适合“文档已划分章节”的场景。

5.2.3 构建全文检索索引(ElasticSearch)

如果你想在多重检索中加入“全文检索”的能力,可将文本同步到 ElasticSearch:

from llama_index import ElasticsearchReader, GPTListIndex

# 1. 首先需要确保 Elasticsearch 服务已启动 (默认端口 9200)
# 2. 使用 Reader 将本地 documents 导入 ES
def sync_to_elasticsearch(documents, index_name="langchain_es_index"):
    es_client = ElasticsearchReader(
        hosts=["http://127.0.0.1:9200"],
        index_name=index_name
    )
    # 将每个 Document 存到 ES,ESReader 会自动创建索引并写入
    for doc in documents:
        es_client.add_document(doc)
    return es_client

es_reader = sync_to_elasticsearch(docs)
print("全文检索索引已同步到 ElasticSearch (index: langchain_es_index)")
  • ElasticsearchReader.add_document 会将 Document.text 写入 ES 并生成倒排索引。
  • 后续可在 LangChain 中使用 ES 的 API 或 Llama-Index 的 ES 查询适配器完成检索。

6. LangChain 中的检索器配置

完成索引构建后,我们需要在 LangChain 中创建多个检索器(Retriever),并按需求组合。

from langchain.retrievers import VectorRetriever, ElasticSearchRetriever, BaseRetriever

# 1. 加载已持久化的 Llama-Index index
from llama_index import load_index_from_storage, StorageContext
# 加载向量索引
storage_context = StorageContext.from_defaults(persist_dir="./index_storage")
vector_index = load_index_from_storage(storage_context).as_retriever()
# 加载关键词索引
kw_storage = StorageContext.from_defaults(persist_dir="./keyword_index")
keyword_index = load_index_from_storage(kw_storage).as_retriever()

# 2. 构建 LangChain Retriever
# (1)向量检索器
vector_retriever = VectorRetriever(
    index=vector_index,
    embeddings_model="openai-ada"  # 如果使用 OpenAI 嵌入
)

# (2)关键词检索器(内部其实调用 keyword_index.query)
class LlamaKeywordRetriever(BaseRetriever):
    def __init__(self, llama_retriever):
        self.llama_retriever = llama_retriever

    async def get_relevant_documents(self, query: str):
        # llama_retriever.query 返回 Document 列表
        results = self.llama_retriever.retrieve(query, top_k=5)
        # 转换为 LangChain Document 类型
        from langchain.schema import Document as LCDocument
        return [LCDocument(page_content=doc.text, metadata=doc.metadata) for doc in results]

keyword_retriever = LlamaKeywordRetriever(keyword_index)

# (3)全文检索器(ElasticSearch)
class ESDocumentRetriever(BaseRetriever):
    def __init__(self, index_name="langchain_es_index", host="http://127.0.0.1:9200"):
        from elasticsearch import Elasticsearch
        self.es = Elasticsearch([host])
        self.index_name = index_name

    async def get_relevant_documents(self, query: str):
        body = {
            "query": {
                "multi_match": {
                    "query": query,
                    "fields": ["text"]
                }
            }
        }
        res = self.es.search(index=self.index_name, body=body, size=5)
        docs = []
        for hit in res["hits"]["hits"]:
            docs.append(
                Document(
                    text=hit["_source"]["text"],
                    metadata={"score": hit["_score"], "source": hit["_source"].get("source", "")}
                )
            )
        # 转换为 LangChain Document
        from langchain.schema import Document as LCDocument
        return [LCDocument(page_content=d.text, metadata=d.metadata) for d in docs]

es_retriever = ESDocumentRetriever()
  • VectorRetriever:LangChain 自带的向量检索器,可以接受一个 Llama-Index 返回的向量检索接口。
  • 自定义 LlamaKeywordRetriever:利用 Llama-Index 对关键词索引的 retrieve 方法,将结果包装成 LangChain 文档。
  • 自定义 ESDocumentRetriever:通过 ElasticSearch 原生 API 对 ES 索引做多字段检索,并返回 LangChain 格式的文档列表。
Tip:LangChain 所有检索器最终都需要实现 get_relevant_documents(query) 方法,输出一列表 LangChain Document 对象。

7. Llama-Index 中的索引构建与查询

虽然我们已经在第 5 节展示了索引构建示例,这里进一步补充几种常用的 Llama-Index 索引类型与查询方法。

7.1 节点索引(GPTSimpleVectorIndex)

注意:在 Llama-Index v0.6+ 中,GPTSimpleVectorIndex 被整合到 VectorStoreIndex 中。

构建完成后,进行查询很简洁:

# 加载向量索引
from llama_index import load_index_from_storage, StorageContext

storage_context = StorageContext.from_defaults(persist_dir="./index_storage")
vector_index = load_index_from_storage(storage_context)

# 查询
query_text = "如何在 Python 中使用多线程?"
response = vector_index.as_query_engine().query(query_text)
print("向量检索结果:", response.response)
  • as_query_engine():以默认的参数包装一个“查询引擎”,方便直接调用 query() 获得一个 Response 对象,包含 response(文本)和 source_nodes(对应文档块元信息)。

7.2 树形索引(GPTTreeIndex)

如果你想按层级结构检索文档,可以使用树形索引:

from llama_index import GPTTreeIndex

# 构建
tree_index = GPTTreeIndex.from_documents(
    docs,
    service_context=service_context,
    index_struct_kwargs={"num_children": 4}
)
tree_index.storage_context.persist("./tree_index")

# 查询
storage_context = StorageContext.from_defaults(persist_dir="./tree_index")
loaded_tree = load_index_from_storage(storage_context)

response = loaded_tree.as_query_engine().query("请列出所有关于日志记录的章节内容。")
print("树形检索结果:", response.response)
  • 树形索引会自动根据语义或层次将文档分成多级节点,查询时模型会逐级下钻以确定最相关的节点。

7.3 结合全文搜索引擎(如 ElasticSearch)

如果使用 ES 同步方式,Llama-Index 也能封装 ES 作为 QueryEngine

from llama_index import ElasticSearchReader, ElasticsearchQueryEngine

# 构建 ES 查询引擎
es_query_engine = ElasticsearchQueryEngine(
    es_reader=es_reader,  # 前面已经构建好的 ES Reader
    service_context=service_context
)

# 查询
result = es_query_engine.query("如何处理数据库死锁?")
print("全文检索结果:", result.response)
  • 通过 ElasticsearchQueryEngine,Llama-Index 会将 ES 返回的文本包装为 Response,并可选地在生成时结合 LLm 做进一步指引。

8. 多重检索管道示例

8.1 设计思路

我们希望在 LangChain 中同时调用以上三种检索器,形成一个多路并行检索 → 融合 → LLM 生成的完整管道。具体思路:

  1. 定义多个检索器vector_retrieverkeyword_retrieveres_retriever
  2. 并行调用:在收到用户 Query 后,同时调用三条检索管道,获取各自 Top-K 文档。
  3. 结果融合:对三路检索结果做去重和打分。简单示例:将相同文本块合并,将各自的相关度分数归一化后求平均。
  4. 拼接 Prompt:将融合后的前 N 条文档内容,按照优先级(一般向量检索优先级高)拼接到 LLM Prompt 中。
  5. 生成答案:调用 LLM 生成最终回答并返回。

下面给出一个完整的 LangChain + Llama-Index 多重检索管道示例

8.2 代码示例:整合 LangChain & Llama-Index

import asyncio
from typing import List, Dict, Any
from langchain import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.schema import Document as LCDocument

# 假设前面已经创建好以下检索器
# vector_retriever: VectorRetriever
# keyword_retriever: LlamaKeywordRetriever
# es_retriever: ESDocumentRetriever

# 1. 定义一个并行调用检索器的函数
async def multi_retrieve(query: str, top_k: int = 5) -> List[LCDocument]:
    """
    并行调用向量、关键词、全文检索器,返回融合后的前 top_k 文档列表。
    """
    # 并发执行
    results = await asyncio.gather(
        vector_retriever.get_relevant_documents(query),
        keyword_retriever.get_relevant_documents(query),
        es_retriever.get_relevant_documents(query),
    )
    # results = [list_vector_docs, list_keyword_docs, list_es_docs]
    all_docs = []
    seen_texts = set()

    # 简单去重与合并:按照来源优先级遍历
    for source_docs in results:
        for doc in source_docs:
            txt = doc.page_content.strip()
            if txt not in seen_texts:
                seen_texts.add(txt)
                all_docs.append(doc)
            if len(all_docs) >= top_k:
                break
        if len(all_docs) >= top_k:
            break
    return all_docs

# 2. 定义 Prompt 模板
prompt_template = """
你是一个知识问答助手。以下是从不同检索器获取到的上下文片段,请根据它们回答用户的问题。
=== 上下文开始 ===
{context}
=== 上下文结束 ===
问题:{question}
"""

prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

# 3. 初始化 LLMChain
llm = OpenAI(temperature=0.0, model_name="gpt-3.5-turbo")
chain = LLMChain(llm=llm, prompt=prompt)

# 4. 将检索与生成串联
async def run_multiretrieval_qa(query: str):
    # 多重检索
    docs = await multi_retrieve(query, top_k=5)
    # 将文档拼接成一个长上下文
    context = "\n\n".join([f"- 文档来源:{doc.metadata.get('source', 'unknown')}\n{doc.page_content}" for doc in docs])
    # 构造输入
    inputs = {"context": context, "question": query}
    # 调用 LLM
    response = chain.run(inputs)
    return response

# 示例运行
if __name__ == "__main__":
    query = "如何在 Python 中实现并发文件下载?"
    ans = asyncio.run(run_multiretrieval_qa(query))
    print("最终回答:\n", ans)

代码说明

  1. multi\_retrieve

    • 使用 asyncio.gather 并行调用三路检索器的 get_relevant_documents(query)
    • 结果分别为三条 Doc 列表,内部逐一合并、去重,并按顺序取前 top_k 条。
    • 简易去重策略:根据文档文本 page_content 是否重复剔除。
  2. PromptTemplate

    • 将多重检索得到的上下文片段拼接并传给 LLM,使用明确的标识区分不同来源。
  3. LLMChain

    • 调用 OpenAI LLM(如 GPT-3.5 或 GPT-4)生成答案。
    • 你可以自定义 Prompt 模板,以加入更多如“使用 Markdown 输出”或“回答要点列举”等要求。
  4. 异步运行

    • 使用 asyncio 并行加速检索,避免串行导致的延迟。
    • 最终使用 asyncio.run 在主程序中同步获取结果。
整个示例展现出如何把多路检索LLM 生成无缝集成在一起,实现一个端到端的“多重检索RAG”流水线。

9. 完整流程演示

为了让你更清晰地理解上述各组件如何串联,下面再一次以流程图形式重现“用户 Query → 多重检索 → 融合 → 生成 → 返回”全过程。

flowchart LR
  U[用户输入 Query] -->|async| R1[调用向量检索器]
  U -->|async| R2[调用关键词检索器]
  U -->|async| R3[调用全文检索器]

  subgraph 并行检索
    R1 --> MR[结果收集]
    R2 --> MR
    R3 --> MR
  end

  MR -->|去重&融合| Ctx[生成统一上下文块]

  Ctx -->|拼接 Prompt| PromptHai[构造 Prompt]
  PromptHai --> LLM[LLMChain 调用 OpenAI]

  LLM -->|返回答案| Ans[输出给用户]
  • 并行检索:向量、关键词、全文检索同时触发。
  • 结果收集与融合:在本地合并不同检索源的结果,去除重复文本,并可自定义更复杂的打分策略。
  • Prompt 拼接:将多个文档片段统一拼接到 Prompt 中,传给 LLM。
  • 生成与返回:LLM 给出最终答案输出。

10. 调优与注意事项

10.1 检索器权重与融合策略

  • 简单去重 vs 权重融合

    • 上述示例只做了按顺序去重与合并。若想保留每种检索器的“置信度”或“相关度分数”,可以给每个文档打分(如向量检索使用相似度得分,关键词检索使用出现次数,全文检索使用 ES 得分),然后归一化后做加权平均,再取 Top-K。
  • 动态权重

    • 可以根据 Query 类型动态调整权重,例如当 Query 包含专业术语时,降低向量检索权重;当 Query 简单常见时,优先全文检索。

10.2 索引更新与数据刷新

  • 增量更新

    • 如果文档库在运行过程中不断更新,需支持对向量索引与关键词索引的增量更新。Llama-Index 支持新 Document 追加:

      new_docs = load_and_split_documents("./new_docs")
      vector_index.insert_documents(new_docs)
      keyword_index.insert_documents(new_docs)
    • 对 ES 同步则可直接调用 es_retriever.add_document 写入新索引。
  • 定期重建

    • 当文档变化较大时,建议定期重建索引以保证检索质量。尤其是关键词索引和树形索引,可能在分层方式上发生重大变化时,需要全量重建。

10.3 并行检索与性能优化

  • 异步 vs 线程池

    • LangChain 的检索器方法是异步的(async get_relevant_documents),可以使用 asyncio 并发,也可将其包装到 ThreadPoolExecutor 在同步代码中并行调用。
  • 批量查询

    • 如果系统需要处理高并发请求,可考虑批量化查询或预热常见 Query,缓存 Top-K 结果,减少后端检索压力。
  • 检索器超时与降级

    • 对于 ES 等外部服务,需设置RPC/HTTP 超时时间。一旦检索器超时,可主动降级为“只使用本地向量检索”,保证系统可用性。

10.4 对话上下文与缓存

  • 对话历史纳入检索

    • 在对话型 RAG 场景中,可以将之前的用户问题和答案也记入上下文,然后再触发多重检索。LangChain 提供了 ConversationBufferMemory,可以将历史对话自动拼接到 Prompt 中。
  • 检索结果缓存

    • 对于相同的 Query,缓存 multi_retrieve(query) 的结果,避免重复调用多个检索器。可使用 Redis 或内存缓存进行缓存管理。

11. 小结

本文系统地介绍了如何将 LangChainLlama-Index 联动,打造一个“多重检索 RAG”系统。核心流程包括:

  1. 索引构建

    • 利用 Llama-Index 构建向量索引、关键词/树形索引,甚至同步到 ElasticSearch。
  2. LangChain 检索器配置

    • 将 Llama-Index 索引封装为 LangChain 可以调用的检索器:向量检索器、关键词检索器、全文检索器等。
  3. 多重检索与融合

    • 异步并行调用多路检索器,聚合去重后形成统一上下文。
  4. Prompt 拼接与 LLM 生成

    • 将融合后的上下文块传给 LLM(如 GPT-4),生成高质量、覆盖面更广的答案。

通过示例代码与 Mermaid 流程图,我们展示了从数据准备索引构建检索—生成的完整端到端流程。你可以根据实际业务需求,灵活调整:

  • 检索器的种类与权重
  • 索引刷新策略与增量更新方式
  • 对话上下文与缓存技术
  • 并行化与降级机制

多重检索能显著提升 RAG 系统在“召回率+精确度”上的平衡,适用于技术文档库、问答知识库、客户支持中心等场景。

评论已关闭

推荐阅读

DDPG 模型解析,附Pytorch完整代码
2024年11月24日
DQN 模型解析,附Pytorch完整代码
2024年11月24日
AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日