趋近智
构建一个能记住之前对话的简单聊天机器人,需要在实践中应用记忆组件。这个实操例子展示了如何将记忆对象整合到链中,以便在对话的多个轮次中保持状态。
首先,让我们准备环境。我们需要安装必要的库并配置API密钥。本例将使用OpenAI API,但其原理适用于LangChain支持的任何LLM。
# 确保已安装所需的库
# pip install langchain langchain-openai langchain-community python-dotenv
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 从.env文件加载环境变量
load_dotenv()
# 建议将您的API密钥设置为环境变量
# os.environ["OPENAI_API_KEY"] = "your_api_key_here"
在添加记忆之前,让我们快速说明我们正在解决的问题。如果没有记忆组件,每次对LLM的调用都是独立的。如果您提出一个后续问题,模型将无法了解您之前陈述的背景。
考虑以下对话:
模型在后续问题上失败了,因为第三轮对话的处理完全独立于第一轮。
为了解决这个问题,我们将使用LangChain的RunnableWithMessageHistory。这个工具将标准链(模型+提示)包装起来,并根据会话ID自动管理从历史存储读写数据。
我们首先定义模型和提示模板。提示必须包含一个消息历史的占位符,这样模型才能看到之前的轮次。
# 初始化LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)
# 定义提示模板
# 我们包含一个系统消息来设定行为,一个历史记录的占位符,
# 以及一个新用户输入的模板。
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个友好的助手,会记住对话中的细节。"),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}")
])
# 创建基本链
chain = prompt | llm
variable_name="chat_history"告知系统将过去的消息注入何处。
接下来,我们需要一种方式来存储对话数据。本例中,我们将使用一个简单的字典来存放InMemoryChatMessageHistory对象,以会话ID为键。在生产应用中,您可能会在这里使用Redis等数据库。
# 存储不同会话对话历史的字典
store = {}
def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
现在我们使用RunnableWithMessageHistory来包装我们的基本链。这个对象连接我们的链、历史工厂函数以及提示中使用的特定键。
# 创建对话可运行对象
conversation = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history"
)
我们指定input_messages_key="input"以匹配我们提示中的{input}变量,并指定history_messages_key="chat_history"以匹配MessagesPlaceholder。
现在,让我们测试聊天机器人。我们使用.invoke()方法,传入输入和一个包含session_id的config字典。这个ID让系统获取正确的历史记录。
首次交互: 我们提供一些初始信息。
# 带有特定会话ID的第一个用户输入
config = {"configurable": {"session_id": "user_123"}}
response = conversation.invoke(
{"input": "你好,我叫克拉拉。我正在构建一个聊天机器人。"},
config=config
)
print(response.content)
聊天机器人会提供友好的问候。InMemoryChatMessageHistory现在已将我们的输入和模型的响应存储在键user_123下。
第二次交互: 现在进行真正的测试。让我们问一个依赖于第一条消息上下文的后续问题。
# 使用相同会话ID的第二个用户输入
response = conversation.invoke(
{"input": "我叫什么名字?"},
config=config
)
print(response.content)
预期输出将类似于:
你的名字是克拉拉。
成功了。模型正确识别了名称,因为可运行对象获取了user_123的历史记录,将其插入到提示中,并将完整的背景信息发送给LLM。
每次交互的流程遵循清晰的循环模式,这确保了背景信息的保存和利用。
此图展示了单个对话轮次的生命周期。可运行对象从历史存储中读取数据,格式化提示,从LLM获取响应,然后将新的交互写回存储,为下一轮对话做好准备。
这个实践展示了构建有状态应用的基本机制。虽然将所有历史保存在内存中很简单,但对于短会话来说它很强大。对于需要非常长对话的应用,您可以采用策略来总结旧消息或截断历史记录,这可以通过修改历史记录获取或链步骤中的逻辑来处理。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造