【LangChain】langchain.chains.history_aware_retriever.create_history_aware_retriever函数:创建结合对话历史进行语义检索链

create_history_aware_retriever 是 LangChain 库中的一个函数,位于 langchain.chains.history_aware_retriever 模块。它用于创建一个能够结合对话历史进行语义检索的链(chain),特别适合需要上下文感知的检索增强生成(RAG)场景。该函数通过语言模型(LLM)重构用户查询,结合历史对话上下文生成更精准的查询,从而从向量存储或其他检索器中获取相关文档。

以下是对 create_history_aware_retriever 函数的详细介绍,涵盖其定义、功能、参数、返回值、使用方式、应用场景、优化建议和注意事项。


1. 什么是 create_history_aware_retriever

create_history_aware_retriever 是一个工厂函数,用于构建一个上下文感知的检索链。它通过以下步骤实现:

  1. 结合对话历史:分析历史对话(通常是 List[BaseMessage] 格式),理解用户的当前意图。
  2. 重构查询:利用语言模型(LLM)将当前用户输入与历史上下文结合,生成更适合检索的查询。
  3. 执行检索:将重构后的查询传递给底层检索器(如向量存储检索器),获取相关文档。

该函数的主要目标是解决传统检索器在多轮对话中缺乏上下文理解的问题。例如,在对话中用户提到“它如何工作?”,没有历史上下文,检索器难以判断“它”指代什么。通过 create_history_aware_retriever,可以结合历史生成更明确的查询,如“量子计算如何工作?”,从而提高检索精度。

背景

  • 在 LangChain 中,检索增强生成(RAG)通常结合向量存储(如 Chroma、FAISS)和嵌入模型(如 OpenAIEmbeddings)进行文档检索。
  • 多轮对话场景(如聊天机器人)需要处理历史上下文,create_history_aware_retriever 是为此设计的工具。
  • 该函数在 LangChain 0.1.0 及以上版本中稳定支持,广泛应用于对话系统和 RAG 工作流。

简单比喻
可以将 create_history_aware_retriever 想象为一个“智能图书管理员”,不仅听懂你当前的提问,还能根据之前的对话记录为你重新组织问题,找到最相关的书籍(文档)。


2. 核心功能

create_history_aware_retriever 提供了以下主要功能:

  1. 上下文感知查询生成
    • 使用 LLM 根据历史对话和当前输入生成语义更丰富的查询。
    • 示例:将“它是什么?”重构为“量子计算是什么?”(基于历史)。
  2. 集成检索器
    • 将重构的查询传递给指定的检索器(如向量存储),返回相关文档。
    • 支持多种检索器,如 VectorStoreRetriever 或自定义检索器。
  3. 支持多轮对话
    • 处理历史消息列表(List[BaseMessage]),维护对话上下文。
    • 示例:支持多轮对话中的指代消解(如“它”或“那个”)。
  4. 灵活的提示模板
    • 允许用户自定义提示模板,控制查询重构的方式。
    • 示例:可以指定 LLM 如何结合历史和输入生成查询。
  5. 链式工作流
    • 返回一个 Runnable 对象,可与其他 LangChain 链(如 ConversationalRetrievalChain)组合。
    • 示例:与回答生成链结合,构建完整的 RAG 系统。
  6. 异步支持
    • 支持异步调用,适合高并发场景。

特点

  • 上下文感知:通过历史对话提升查询的语义准确性。
  • 模块化:与 LangChain 的 LLM、检索器、提示模板无缝集成。
  • 可定制:支持自定义提示和检索器,适应不同场景。
  • 高效:优化多轮对话的检索性能,减少无关文档。

3. 函数签名与参数

以下是 create_history_aware_retriever 的函数签名及参数说明:

from langchain.chains.history_aware_retriever import create_history_aware_retriever

def create_history_aware_retriever(
    llm: BaseLanguageModel,
    retriever: BaseRetriever,
    prompt: BasePromptTemplate | None = None
) -> Runnable

参数

  1. llm: BaseLanguageModel

    • 类型langchain_core.language_models.BaseLanguageModel
    • 描述:用于生成重构查询的语言模型实例,如 ChatOpenAIChatAnthropic
    • 要求:必须支持生成文本(completion)或聊天消息(chat)。
    • 示例ChatOpenAI(model="gpt-4o", api_key="your-api-key")
  2. retriever: BaseRetriever

    • 类型langchain_core.retrievers.BaseRetriever
    • 描述:底层检索器,用于根据重构查询检索文档。常见实现包括 VectorStoreRetriever(基于向量存储)或 BM25Retriever(基于关键字)。
    • 要求:必须实现 get_relevant_documents 方法。
    • 示例VectorStoreRetriever(vectorstore=Chroma(...))
  3. prompt: BasePromptTemplate | None (可选,默认为 None)

    • 类型langchain_core.prompts.BasePromptTemplate
    • 描述:用于指导 LLM 重构查询的提示模板。如果为 None,使用默认提示模板。
    • 默认提示:通常要求 LLM 结合历史对话和当前输入生成一个独立的查询。
    • 示例
      from langchain_core.prompts import ChatPromptTemplate
      prompt = ChatPromptTemplate.from_messages([
          ("system", "根据以下对话历史和用户输入,生成一个清晰的检索查询:\n历史:{chat_history}\n输入:{input}"),
          ("human", "生成查询")
      ])
      

返回值

  • 类型Runnable
  • 描述:一个可运行的 LangChain 链对象,接受输入(包含 inputchat_history)并返回检索到的文档列表(List[Document])。
  • 输入格式
    {
        "input": str,  # 用户当前输入
        "chat_history": List[BaseMessage]  # 历史对话消息
    }
    
  • 输出格式List[Document],检索到的相关文档。

4. 核心工作原理

create_history_aware_retriever 的工作流程如下:

  1. 接收输入
    • 接受用户输入(input)和历史对话(chat_history)。
  2. 格式化提示
    • 使用指定的 prompt(或默认提示)将 chat_historyinput 格式化为 LLM 的输入。
  3. 生成重构查询
    • LLM 根据提示生成一个更适合检索的查询,通常是独立的、语义明确的字符串。
    • 示例:输入“它如何工作?”,历史提到“量子计算”,LLM 生成“量子计算如何工作?”。
  4. 执行检索
    • 将重构查询传递给 retriever,调用其 get_relevant_documents 方法。
  5. 返回文档
    • 返回检索到的文档列表(List[Document])。

内部结构

  • 该函数构造了一个 RunnableSequence,包含:
    • 一个 Runnable 用于格式化输入和调用 LLM。
    • 一个 Runnable 用于调用检索器。
  • 整个链是 Runnable 类型的,符合 LangChain 的链式工作流。

5. 使用方式与代码示例

以下是通过代码示例展示 create_history_aware_retriever 的使用方式,涵盖基本用法、与 RAG 链集成、自定义提示和异步调用等场景。

示例 1:基本用法

创建一个简单的历史感知检索链:

from langchain.chains.history_aware_retriever import create_history_aware_retriever
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_community.vectorstores import Chroma
from langchain_core.messages import HumanMessage, AIMessage

# 创建示例文档
docs = [
    Document(page_content="量子计算使用量子比特进行并行计算,速度远超经典计算。"),
    Document(page_content="经典计算依赖二进制比特,适用于大多数传统应用。")
]

# 初始化嵌入模型和向量存储
embeddings = OpenAIEmbeddings(api_key="your-api-key")
vectorstore = Chroma.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o", api_key="your-api-key")

# 创建默认提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "根据以下对话历史和用户输入,生成一个清晰的检索查询:\n历史:{chat_history}\n输入:{input}"),
    ("human", "生成查询")
])

# 创建历史感知检索链
history_aware_retriever = create_history_aware_retriever(llm, retriever, prompt)

# 模拟对话历史
chat_history = [
    HumanMessage(content="什么是量子计算?"),
    AIMessage(content="量子计算使用量子比特进行计算,具有并行处理能力。")
]

# 调用链
input_data = {"input": "它如何工作?", "chat_history": chat_history}
result = history_aware_retriever.invoke(input_data)
print([doc.page_content for doc in result])

输出

['量子计算使用量子比特进行并行计算,速度远超经典计算。']

说明

  • LLM 根据历史(提到量子计算)和输入(“它如何工作?”)生成查询“量子计算如何工作?”。
  • 检索器返回与量子计算相关的文档。

示例 2:与 RAG 链集成

history_aware_retriever 与回答生成链结合,构建完整的 RAG 系统:

from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_community.vectorstores import Chroma
from langchain_core.messages import HumanMessage, AIMessage

# 初始化嵌入模型和向量存储
docs = [
    Document(page_content="量子计算使用量子比特进行并行计算,速度远超经典计算。")
]
embeddings = OpenAIEmbeddings(api_key="your-api-key")
vectorstore = Chroma.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o", api_key="your-api-key")

# 创建历史感知检索链
prompt_history = ChatPromptTemplate.from_messages([
    ("system", "根据对话历史和用户输入,生成检索查询:\n历史:{chat_history}\n输入:{input}"),
    ("human", "生成查询")
])
history_aware_retriever = create_history_aware_retriever(llm, retriever, prompt_history)

# 创建回答生成链
prompt_answer = ChatPromptTemplate.from_messages([
    ("system", "根据以下文档回答问题:\n{context}\n问题:{input}"),
    ("human", "{input}")
])
qa_chain = create_stuff_documents_chain(llm, prompt_answer)

# 组合成 RAG 链
rag_chain = create_retrieval_chain(history_aware_retriever, qa_chain)

# 模拟对话历史
chat_history = [
    HumanMessage(content="什么是量子计算?"),
    AIMessage(content="量子计算使用量子比特进行计算。")
]

# 调用 RAG 链
input_data = {"input": "它如何工作?", "chat_history": chat_history}
result = rag_chain.invoke(input_data)
print(result["answer"])

输出

量子计算通过量子比特的叠加和纠缠进行并行计算,显著提升处理速度。

说明

  • 历史感知检索链生成查询并检索文档。
  • 回答生成链根据检索结果生成自然语言回答。

示例 3:自定义提示模板

使用自定义提示模板控制查询生成:

from langchain.chains.history_aware_retriever import create_history_aware_retriever
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_core.messages import HumanMessage, AIMessage
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# 初始化向量存储
docs = [
    Document(page_content="量子计算基于量子力学原理,利用量子比特。")
]
embeddings = OpenAIEmbeddings(api_key="your-api-key")
vectorstore = FAISS.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o", api_key="your-api-key")

# 自定义提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个检索专家。结合以下历史和输入,生成一个简洁的检索查询(不超过20字):\n历史:{chat_history}\n输入:{input}"),
    ("human", "生成查询")
])

# 创建检索链
history_aware_retriever = create_history_aware_retriever(llm, retriever, prompt)

# 模拟对话历史
chat_history = [
    HumanMessage(content="量子计算是什么?"),
    AIMessage(content="量子计算基于量子力学。")
]

# 调用链
input_data = {"input": "它的原理是什么?", "chat_history": chat_history}
result = history_aware_retriever.invoke(input_data)
print([doc.page_content for doc in result])

输出

['量子计算基于量子力学原理,利用量子比特。']

说明

  • 自定义提示要求查询简洁,生成“量子计算原理”并检索相关文档。

示例 4:异步调用

使用异步方法处理高并发:

import asyncio
from langchain.chains.history_aware_retriever import create_history_aware_retriever
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_community.vectorstores import Chroma
from langchain_core.messages import HumanMessage, AIMessage

# 初始化向量存储
docs = [
    Document(page_content="量子计算使用量子比特进行并行计算。")
]
embeddings = OpenAIEmbeddings(api_key="your-api-key")
vectorstore = Chroma.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o", api_key="your-api-key")

# 创建提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "根据历史和输入生成检索查询:\n历史:{chat_history}\n输入:{input}")
])

# 创建检索链
history_aware_retriever = create_history_aware_retriever(llm, retriever, prompt)

# 模拟对话历史
chat_history = [
    HumanMessage(content="量子计算是什么?"),
    AIMessage(content="量子计算使用量子比特。")
]

# 异步调用
async def main():
    input_data = {"input": "它如何工作?", "chat_history": chat_history}
    result = await history_aware_retriever.ainvoke(input_data)
    print([doc.page_content for doc in result])

asyncio.run(main())

输出

['量子计算使用量子比特进行并行计算。']

说明

  • 使用 ainvoke 异步调用,适合高并发场景。

6. 应用场景

create_history_aware_retriever 在以下场景中广泛应用:

  1. 对话式 RAG 系统
    • 为聊天机器人提供上下文感知的文档检索。
    • 示例:用户问“它如何工作?”,系统结合历史检索量子计算文档。
  2. 多轮对话助手
    • 处理指代消解(如“它”或“那个”),提高对话连贯性。
    • 示例:技术支持机器人根据历史回答后续问题。
  3. 知识库查询
    • 从企业知识库中检索相关文档,结合对话历史。
    • 示例:HR 机器人根据员工历史问题检索政策文档。
  4. 教育助手
    • 为学生提供基于课程内容的上下文感知答案。
    • 示例:学生问“这个公式怎么用?”,系统结合历史检索教材。
  5. 客户支持
    • 在支持系统中结合用户历史提供精准文档。
    • 示例:结合历史订单信息检索产品手册。
  6. 研究工具
    • 为研究人员提供基于文献的上下文感知检索。
    • 示例:结合研究主题历史检索相关论文。

7. 优化建议

以下是使用 create_history_aware_retriever 时的优化建议,帮助提高效率和准确性:

(1) 提示模板优化

  • 清晰的提示:设计简洁、明确的提示模板,确保 LLM 生成准确的查询。
prompt = ChatPromptTemplate.from_messages([
    ("system", "结合历史和输入,生成一个不超过20字的检索查询:\n历史:{chat_history}\n输入:{input}"),
    ("human", "生成查询")
])
  • 限制输出长度:要求 LLM 生成短查询,减少 token 消耗。
  • 测试提示效果:在小数据集上测试提示,确保生成查询符合预期。

(2) 检索器优化

  • 高质量向量存储:使用高质量嵌入模型(如 OpenAIEmbeddingsSentenceTransformers)提高检索精度。
  • 调整检索参数:设置 retrievertop_kscore_threshold,控制返回文档数量。
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
  • 混合检索:结合关键字检索(如 BM25Retriever)和语义检索,增强覆盖率。

(3) LLM 优化

  • 选择高效模型:使用性能均衡的模型(如 gpt-4o-mini)降低成本。
  • 温度调整:设置较低的 temperature(如 0.3)确保查询生成稳定。
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
  • 缓存 LLM 输出:对于常见历史和输入组合,缓存重构查询。
from langchain_core.cache import InMemoryCache
llm.cache = InMemoryCache()

(4) 性能优化

  • 异步调用:使用 ainvokeastream 处理高并发。
  • 批量处理:同时处理多个输入,减少调用次数。
  • 预计算嵌入:为文档预生成嵌入,加速检索。

(5) 上下文管理

  • 限制历史长度:截断过长的 chat_history,避免 token 超限。
chat_history = chat_history[-10:]  # 保留最后10条消息
  • 总结历史:使用 LLM 总结长历史,压缩上下文。
from langchain.memory import ConversationSummaryMemory
memory = ConversationSummaryMemory(llm=llm)
summary = memory.predict_new_summary(chat_history, "")

(6) 监控与调试

  • 记录查询:使用回调记录 LLM 生成的查询。
from langchain_core.callbacks import BaseCallbackHandler
class QueryCallback(BaseCallbackHandler):
    def on_llm_end(self, output, **kwargs):
        print(f"生成查询:{output.generations[0][0].text}")
config = {"callbacks": [QueryCallback()]}
  • 结合 LangSmith:使用 LangSmith 分析链的性能。
from langsmith import Client
config = {"callbacks": [Client(api_key="your-langsmith-key")]}

8. 注意事项

以下是使用 create_history_aware_retriever 时需要注意的关键点:

  • LLM 兼容性
    • 确保 llm 支持聊天或生成任务,推荐使用 ChatOpenAIChatAnthropic
    • 示例:检查模型文档确认支持的输入格式。
  • 检索器质量
    • 检索效果依赖底层 retriever 的质量,需确保向量存储包含相关文档。
    • 示例:验证向量存储的嵌入是否覆盖目标领域。
  • Token 限制
    • 长历史或复杂提示可能超过 LLM 的 token 限制。
    • 示例:使用 ConversationSummaryMemory 压缩历史。
  • 提示设计
    • 不清晰的提示可能导致生成无关查询,需反复测试。
    • 示例:避免模糊指令,如“生成查询”,应明确要求“生成简洁查询”。
  • 安全性
    • 避免在 inputchat_history 中包含敏感信息。
    • 示例:验证输入,防止注入攻击。
if any(char in input_data["input"] for char in ["<", ">", "&"]):
    raise ValueError("输入包含不安全字符")
  • 版本兼容性
    • 确保 LangChain 版本 >= 0.1.0,推荐使用最新版本(如 0.2.17)。
    • 检查文档确认功能支持。
  • 成本管理
    • 使用外部 LLM(如 OpenAI)可能产生费用,需监控 API 调用。
    • 示例:使用 gpt-4o-mini 降低成本。

9. 与 LangChain 生态的结合

create_history_aware_retriever 与 LangChain 生态中的其他组件紧密集成,以下是主要结合点:

  • 语言模型(LLMs)
    • 支持多种 LLM,如 ChatOpenAIChatAnthropic
    llm = ChatOpenAI(model="gpt-4o")
    
  • 检索器(Retrievers)
    • 兼容 VectorStoreRetrieverBM25Retriever 等。
    retriever = vectorstore.as_retriever()
    
  • 提示模板(Prompt Templates)
    • 使用 ChatPromptTemplate 自定义查询生成逻辑。
    prompt = ChatPromptTemplate.from_messages([...])
    
  • 链(Chains)
    • create_retrieval_chaincreate_stuff_documents_chain 组合。
    rag_chain = create_retrieval_chain(history_aware_retriever, qa_chain)
    
  • 记忆(Memory)
    • 结合 ConversationBufferMemoryConversationSummaryMemory 管理历史。
    from langchain.memory import ConversationBufferMemory
    memory = ConversationBufferMemory(return_messages=True)
    
  • 回调(Callbacks)
    • 使用 RunnableConfig 配置回调,监控执行。
    config = {"callbacks": [StdOutCallbackHandler()]}
    

10. 综合示例:完整 RAG 系统

以下是一个完整的示例,展示如何使用 create_history_aware_retriever 构建一个上下文感知的 RAG 系统:

from langchain.chains.history_aware_retriever import create_history_aware_retriever
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_community.vectorstores import Chroma
from langchain_core.messages import HumanMessage, AIMessage
from langchain.memory import ConversationBufferMemory

# 初始化向量存储
docs = [
    Document(page_content="量子计算使用量子比特进行并行计算,基于叠加和纠缠原理。"),
    Document(page_content="经典计算使用二进制比特,适合传统应用。")
]
embeddings = OpenAIEmbeddings(api_key="your-api-key")
vectorstore = Chroma.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o-mini", api_key="your-api-key", temperature=0.3)

# 创建历史感知检索提示
prompt_history = ChatPromptTemplate.from_messages([
    ("system", "根据对话历史和用户输入,生成一个简洁的检索查询(不超过20字):\n历史:{chat_history}\n输入:{input}"),
    ("human", "生成查询")
])

# 创建历史感知检索链
history_aware_retriever = create_history_aware_retriever(llm, retriever, prompt_history)

# 创建回答生成提示
prompt_answer = ChatPromptTemplate.from_messages([
    ("system", "根据以下文档回答问题,保持简洁:\n{context}\n问题:{input}"),
    ("human", "{input}")
])
qa_chain = create_stuff_documents_chain(llm, prompt_answer)

# 组合 RAG 链
rag_chain = create_retrieval_chain(history_aware_retriever, qa_chain)

# 初始化对话记忆
memory = ConversationBufferMemory(return_messages=True)

# 模拟多轮对话
def chat(query):
    chat_history = memory.chat_memory.messages
    result = rag_chain.invoke({"input": query, "chat_history": chat_history})
    memory.save_context({"input": query}, {"output": result["answer"]})
    return result["answer"]

# 第一轮对话
print(chat("什么是量子计算?"))
# 输出:量子计算使用量子比特进行并行计算,基于叠加和纠缠原理。

# 第二轮对话
print(chat("它如何工作?"))
# 输出:量子计算通过量子比特的叠加和纠缠实现并行计算。

说明

  • 第一轮对话直接检索量子计算定义。
  • 第二轮对话结合历史生成查询“量子计算如何工作?”,检索相关文档并生成回答。
  • 使用 ConversationBufferMemory 管理对话历史。

11. 学习资源

  • 官方文档:https://python.langchain.com/docs/modules/chains/history_aware_retriever/
  • API 参考:https://api.python.langchain.com/en/latest/chains/langchain.chains.history_aware_retriever.create_history_aware_retriever.html
  • GitHub 源码:https://github.com/langchain-ai/langchain/tree/master/libs/langchain/langchain/chains/history_aware_retriever
  • LangSmith:用于调试链执行(https://smith.langchain.com)
  • 社区教程:LangChain 官方博客、YouTube 视频

12. 总结

  • 定义create_history_aware_retriever 是 LangChain 中用于创建上下文感知检索链的函数,结合对话历史生成精准查询。
  • 核心参数
    • llm:语言模型,用于查询重构。
    • retriever:底层检索器,执行文档检索。
    • prompt:提示模板,控制查询生成(可选)。
  • 返回值Runnable 链,输入历史和查询,输出文档列表。
  • 功能:上下文感知查询生成、集成检索器、支持多轮对话、灵活提示、链式工作流、异步支持。
  • 应用场景:对话式 RAG、多轮对话助手、知识库查询、教育助手、客户支持、研究工具。
  • 优化建议:优化提示模板、检索器、LLM、性能、上下文管理、监控调试。
  • 注意事项:LLM 兼容性、检索器质量、token 限制、提示设计、安全性、版本兼容性、成本管理。

你可能感兴趣的:(LangChain,langchain,对话历史,语义检索,retriever)