讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

updream

一键生成高质量推广成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

剧云
剧云

中文短剧剧本制作平台

Langchain对话检索链中聊天历史与内存的深度解析与实践

星辰大大_7055

星辰大大_7055

发布时间:2025-10-15 09:34:20

|

224人浏览过

|

来源于php中文网

原创

Langchain对话检索链中聊天历史与内存的深度解析与实践

本文深入探讨了langchain中`conversationalretrievalchain`在配置提示模板和内存时,为何仍需显式传入`chat_history`的常见疑问。通过详细解析内存管理、提示模板构建及`get_chat_history`参数的作用,提供了一套完整的解决方案,旨在帮助开发者有效构建具备上下文感知能力的对话式检索应用。

在构建基于Langchain的对话式检索应用时,开发者常会遇到一个问题:即使已经为ConversationalRetrievalChain配置了内存(Memory),在调用链时仍然收到ValueError: Missing some input keys: {'chat_history'}.的错误。这通常是因为对chat_history在链中扮演的角色以及其与内存和提示模板的交互机制存在误解。本文将详细阐述这一机制,并提供一个完整的实现方案。

核心概念解析

要理解为何需要显式传入chat_history,我们首先要明确几个关键组件的作用:

  1. ConversationalRetrievalChain: 这是一个专门用于结合对话历史和文档检索来回答用户问题的链。它内部通常包含一个用于压缩历史问题的链(可选)、一个检索器(Retriever)以及一个用于结合检索结果和历史来生成答案的链。
  2. 内存(Memory): 如ConversationBufferMemory,它负责存储和管理整个对话的轮次,以便后续的对话能够获取历史上下文。memory_key参数指定了内存内容在传递给其他组件时所使用的键名。
  3. 提示模板(Prompt Template): 定义了大型语言模型(LLM)接收输入时的结构。一个典型的对话式检索提示模板会包含占位符,如{context}(检索到的相关文档)、{chat_history}(对话历史)和{question}(当前用户问题)。
  4. get_chat_history 参数: 这是ConversationalRetrievalChain的一个关键参数。它是一个函数,用于指定如何从链的输入字典中提取chat_history变量,以满足提示模板中{chat_history}占位符的需求。

问题的核心在于,尽管ConversationBufferMemory内部维护了对话历史,但ConversationalRetrievalChain在执行其内部的combine_docs_chain时,如果该链所使用的提示模板(例如,通过combine_docs_chain_kwargs={"prompt": qa_prompt}传入)明确要求{chat_history}作为一个输入变量,那么链就必须从其接收的输入字典中获取这个chat_history。get_chat_history参数正是为了告诉链如何完成这个提取过程。

环境准备:构建检索索引

在构建对话检索链之前,我们需要一个可供检索的知识库。这里以FAISS作为向量存储,并使用VertexAIEmbeddings进行文本嵌入。

import os
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import VertexAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter, Language

# 配置嵌入模型
EMBEDDING_QPM = 100
EMBEDDING_NUM_BATCH = 5
embeddings = VertexAIEmbeddings(
    requests_per_minute=EMBEDDING_QPM,
    num_instances_per_batch=EMBEDDING_NUM_BATCH,
    model_name="textembedding-gecko",
    max_output_tokens=512,
    temperature=0.1,
    top_p=0.8,
    top_k=40
)

# 文本分割器
text_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=2000, chunk_overlap=500
)

# 加载训练数据并创建文档
docs = []
training_data_path = "training/facts/" # 假设训练数据文件在此目录
trainingData = os.listdir(training_data_path)

for training_file in trainingData:
    with open(os.path.join(training_data_path, training_file), 'r', encoding='utf-8') as f:
        print(f"Add {f.name} to dataset")
        texts = text_splitter.create_documents([f.read()])
        docs.extend(texts)

# 从文档创建FAISS向量存储并保存到本地
store = FAISS.from_documents(docs, embeddings)
store.save_local("faiss_index")
print("FAISS index created and saved.")

构建对话检索链

接下来,我们将逐步构建ConversationalRetrievalChain,重点关注内存、提示模板和chat_history的处理。

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain_community.llms import VertexAI # 假设使用VertexAI作为LLM
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import VertexAIEmbeddings

# 假设LLM和embeddings已经初始化
# code_llm = VertexAI(...) # 初始化你的LLM
# embeddings = VertexAIEmbeddings(...) # 初始化你的embeddings

# 1. 加载FAISS索引并创建检索器
# 确保faiss_index目录和embeddings模型与创建索引时一致
store = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True) # 注意:如果索引来自不可信来源,此参数需谨慎
retriever = store.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 2},
)

# 2. 初始化对话内存
# memory_key='chat_history' 是关键,它定义了内存内容在提示中被引用的变量名
memory = ConversationBufferMemory(
    memory_key='chat_history', return_messages=True, output_key='answer'
)

# 3. 定义自定义提示模板
# 注意:提示模板中必须包含 {context}, {chat_history}, {question} 占位符
promptTemplate = """请根据提供的上下文和聊天历史回答用户问题。

上下文:
{context}

聊天历史:
{chat_history}

用户问题:
{question}
"""

messages = [
    SystemMessagePromptTemplate.from_template(promptTemplate),
    HumanMessagePromptTemplate.from_template("{question}") # 这里的{question}是实际的用户输入
]
qa_prompt = ChatPromptTemplate.from_messages(messages)

# 4. 初始化LLM
code_llm = VertexAI(
    model_name="gemini-pro", # 或者其他适合你的模型
    max_output_tokens=512,
    temperature=0.1,
    top_p=0.8,
    top_k=40
)

# 5. 构建ConversationalRetrievalChain
# get_chat_history=lambda h : h 是核心,它告诉链从输入字典中直接获取 'chat_history'
# combine_docs_chain_kwargs={"prompt": qa_prompt} 将我们自定义的提示模板注入到文档组合链中
qa_chain = ConversationalRetrievalChain.from_llm(
    llm=code_llm,
    retriever=retriever,
    memory=memory,
    get_chat_history=lambda h: h,
    combine_docs_chain_kwargs={"prompt": qa_prompt}
)

# 6. 维护外部聊天历史并调用链
# 外部维护的history列表用于满足 get_chat_history 的要求
history = []

def chat_with_bot(question: str):
    global history # 声明使用全局的history列表

    # 调用链时,显式传入 'question' 和 'chat_history'
    # 'chat_history' 会通过 get_chat_history 传递给提示模板
    # 同时,ConversationBufferMemory 也会利用这些信息更新其内部状态
    response = qa_chain({"question": question, "chat_history": history})

    answer = response['answer']
    # 更新外部历史列表,用于下一次调用
    history.append((question, answer))
    return answer

# 示例对话
print(chat_with_bot("什么是FAISS?"))
print(chat_with_bot("它有什么作用?"))
print(chat_with_bot("如何使用Python加载它?"))

注意事项与最佳实践

  1. chat_history 的双重角色:

    • 作为链的输入: 当你的提示模板明确要求{chat_history}时,ConversationalRetrievalChain需要从其输入字典中获取这个变量。get_chat_history=lambda h: h指定了从输入字典中键为'chat_history'的值作为聊天历史。因此,你需要在每次调用链时,显式地将一个包含历史对话的列表作为chat_history传入。
    • 作为内存管理的一部分: ConversationBufferMemory通过memory_key='chat_history'来管理和格式化聊天历史。它会在链的内部处理过程中,将历史记录注入到提示模板中。这里的memory_key需要与提示模板中引用的历史变量名一致。 理解这一点至关重要:get_chat_history处理的是链的外部输入到提示模板的映射,而memory处理的是链的内部状态管理和历史的格式化。
  2. 提示模板中的占位符匹配: 确保你的自定义提示模板中包含{context}、{chat_history}和{question}这些占位符,并且这些占位符的名称与链的内部期望以及内存的memory_key相匹配。

  3. 外部历史记录的维护: 在本示例中,我们维护了一个外部的history列表。每次对话结束后,我们都会将最新的问答对追加到这个列表中,以便在下一次调用链时传入完整的历史。这是满足get_chat_history=lambda h: h要求所必需的。

  4. allow_dangerous_deserialization=True: 在加载FAISS索引时,如果索引是本地文件并且你信任其来源,可以使用allow_dangerous_deserialization=True。但在生产环境中或处理来自未知来源的索引时,请务必谨慎,并考虑更安全的加载方式。

总结

ConversationalRetrievalChain是一个功能强大的工具,但其配置,尤其是在处理对话历史时,需要对Langchain的内部机制有清晰的理解。通过正确配置ConversationBufferMemory的memory_key,自定义包含{chat_history}的提示模板,并关键性地设置get_chat_history=lambda h: h参数,同时在每次调用链时显式传入一个外部维护的chat_history列表,我们可以有效地解决ValueError: Missing some input keys: {'chat_history'}.的问题,并成功构建一个具备上下文感知能力的对话式检索系统。理解这些组件如何协同工作,是构建健壮和智能对话应用的关键。

热门AI工具

更多
二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

相关专题

更多
lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

1171

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

345

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

674

2026.01.05

C++ Lambda 表达式与函数式编程
C++ Lambda 表达式与函数式编程

深入讲解 C++ Lambda 表达式与函数式编程范式,涵盖 Lambda 基础语法与返回值推导、值捕获与引用捕获的区别与陷阱、初始化捕获(C++14 广义捕获)、泛型 Lambda(auto 参数)、mutable 关键字修改捕获变量、std::function 类型擦除与函数包装、std::bind 参数绑定(及其被 Lambda 替代的趋势)、Lambda 在 STL 算法(sort/transform/for_each/remo

849

2026.05.06

Java 函数式接口与 Lambda 进阶技巧
Java 函数式接口与 Lambda 进阶技巧

在 Java Stream 基础之上进一步深入函数式编程,涵盖 @FunctionalInterface 注解与自定义函数式接口设计、四种方法引用(静态/实例/对象/构造器)的使用场景、Lambda 中的 effectively final 变量捕获规则、函数组合(andThen/compose)与柯里化技巧、Comparator 链式比较器构建、CompletableFuture 异步函数式编排、Reactor / RxJava 响应

448

2026.05.08

点击input框没有光标怎么办
点击input框没有光标怎么办

点击input框没有光标的解决办法:1、确认输入框焦点;2、清除浏览器缓存;3、更新浏览器;4、使用JavaScript;5、检查硬件设备;6、检查输入框属性;7、调试JavaScript代码;8、检查页面其他元素;9、考虑浏览器兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

974

2023.11.24

海螺AI提示词大全
海螺AI提示词大全

精选海螺AI进阶隐藏玩法与高效提示词(Prompt)公式。手把手教你如何用海螺AI撰写自媒体爆款文案、网络小说续写、脚本创作,以及利用AI工具落地副业的实战案例。

213

2026.06.02

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

0

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn