如何为基于LangChain的AI聊天机器人添加对话记忆功能?
问题排查与解决方案
你的对话记忆失效核心有三个原因:一是默认的OpenAI工具Agent Prompt不包含记忆变量,二是你手动维护的历史和LangChain的Memory没关联,三是调用方式错误。下面是具体修复步骤:
1. 调整Prompt,加入对话记忆变量
你拉取的hwchase17/openai-tools-agent默认Prompt没有预留chat_history输入位,Agent根本不会读取记忆内容。需要修改Prompt模板,把对话历史加入进去:
# 替换原prompt拉取代码,自定义适配记忆的Prompt from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个可以调用数据库和货币转换工具的AI助手,根据用户问题和对话历史提供准确回答。你可以使用以下工具:\n{tools}\n\n工具使用格式参考:\n{{{{tool_call}}}}"), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ])
2. 正确配置ConversationSummaryMemory
需要指定memory_key和Prompt中的变量名一致(这里用chat_history),同时设置return_messages=True让Memory返回消息对象,适配ChatPromptTemplate:
# 替换原memory初始化代码 memory = ConversationSummaryMemory( llm=llm, memory_key="chat_history", return_messages=True, verbose=True # 可选,方便调试记忆生成情况 )
3. 移除手动维护的历史同步,让Memory自动管理
你之前手动把对话加到st.session_state.history里,但这和LangChain的Memory是两套独立的存储,完全没关联。直接让Memory处理历史,同时把Memory的内容同步到Streamlit的messages用于展示:
修改页面渲染部分:
# 替换原历史消息渲染代码,从Memory读取历史并同步到st.session_state.messages if "messages" not in st.session_state: st.session_state.messages = [] # 从Memory同步历史到Streamlit消息 if memory.chat_memory.messages: # 清空旧消息,避免重复渲染 st.session_state.messages = [] for msg in memory.chat_memory.messages: role = "user" if isinstance(msg, HumanMessage) else "assistant" st.session_state.messages.append({"role": role, "content": msg.content}) # 渲染消息 for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"])
4. 修正AgentExecutor调用方式
不需要手动传入history参数,因为AgentExecutor已经绑定了Memory,会自动把历史注入到Prompt中:
# 替换原invoke代码 full_response = agent_executor.invoke({ "input": question })["output"] # 这里不需要手动加历史到st.session_state.history,Memory已经自动记录了
完整修复后的代码
import streamlit as st from langchain_openai import ChatOpenAI from sqlalchemy import create_engine from configs import OAI_MODEL from langchain.chains.conversation.memory import ConversationSummaryMemory from langchain_community.utilities import SQLDatabase from langchain_experimental.sql import SQLDatabaseChain from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit from langchain.agents import create_openai_tools_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.messages import AIMessage, HumanMessage from langchain.tools import StructuredTool from langchain_community.callbacks.manager import get_openai_callback from langchain_experimental.tools.python.tool import PythonREPLTool from dotenv import find_dotenv, load_dotenv from custom_agents.exchange import get_exchange_conv_rate, get_latest_currences load_dotenv(find_dotenv(), override=True) # 初始化Streamlit会话状态 if "messages" not in st.session_state: st.session_state.messages = [] llm = ChatOpenAI( temperature=0.5, max_tokens=3000, model_name=OAI_MODEL, verbose=False ) # 配置对话记忆 memory = ConversationSummaryMemory( llm=llm, memory_key="chat_history", return_messages=True, verbose=True ) # 同步记忆到Streamlit消息 if memory.chat_memory.messages: st.session_state.messages = [] for msg in memory.chat_memory.messages: role = "user" if isinstance(msg, HumanMessage) else "assistant" st.session_state.messages.append({"role": role, "content": msg.content}) # 渲染历史消息 for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"]) # 自定义带记忆的Prompt prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个可以调用数据库和货币转换工具的AI助手,根据用户问题和对话历史提供准确回答。你可以使用以下工具:\n{tools}\n\n工具调用必须按照指定格式,完成思考后再输出最终回答。"), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]) # 初始化工具 tools = [ StructuredTool.from_function(get_exchange_conv_rate), StructuredTool.from_function(get_latest_currences), ] # 添加SQL数据库工具 db_path = "second_project/data.db" engine = create_engine(f"sqlite:///{db_path}") database = SQLDatabase(engine) sql_toolkit = SQLDatabaseToolkit(db=database, llm=llm) tools.extend(sql_toolkit.get_tools()) # 添加算术工具 arithmetic_tool = PythonREPLTool() tools.append(arithmetic_tool) # 创建Agent和Executor agent = create_openai_tools_agent(llm, tools, prompt) agent_executor = AgentExecutor( agent=agent, memory=memory, tools=tools, verbose=True, handle_parsing_errors=True # 可选,处理工具调用格式错误 ) # 处理用户输入 question = st.chat_input("Please, enter your message") if question: st.session_state.messages.append({"role": "user", "content": question}) with st.chat_message("user"): st.markdown(question) with st.chat_message("assistant"): message_placeholder = st.empty() with get_openai_callback() as cb: full_response = agent_executor.invoke({"input": question})["output"] message_placeholder.markdown(full_response) print(cb) st.session_state.messages.append({"role": "assistant", "content": full_response})
关键说明
- 自定义Prompt必须包含
chat_history和agent_scratchpad两个MessagesPlaceholder,前者用于注入对话历史,后者用于Agent的思考过程。 ConversationSummaryMemory会自动把对话历史压缩成摘要,避免Token超限,同时return_messages=True确保输出格式和ChatPromptTemplate兼容。- 不再需要手动维护
st.session_state.history,所有历史由LangChain的Memory统一管理,再同步到Streamlit的messages展示即可。
内容的提问来源于stack exchange,提问作者ShadowFly
相关产品推荐
相关产品推荐

