LangChain ReAct智能体实战:给会话历史添加ID,实现多会话隔离
在LangChain构建智能体的过程中,多轮对话的上下文管理是提升用户体验的关键。很多开发者在实现ReAct智能体时,会遇到一个常见问题:无法区分不同用户或不同会话的历史记录,导致对话上下文混乱,智能体“记混”对话内容。今天就结合实际开发场景,分享如何给LangChain ReAct智能体的会话历史添加唯一ID,实现会话隔离,让智能体能够精准记住每个会话的上下文,同时无需手动管理历史消息,大幅提升开发效率。一、核心痛点:为什么需要给会话历史加ID?
默认情况下,LangChain的会话历史存储是全局的,如果多个用户同时使用智能体,或者同一用户开启多个会话,所有的对话记录会混在一起。比如用户A和用户B分别与智能体对话,智能体可能会把A的提问和B的回复混淆,导致回答逻辑错乱。更关键的是,在实际部署场景中,我们需要对不同的会话进行区分、追溯甚至持久化存储,而唯一的会话ID就是实现这一切的基础。它就像每个会话的“身份证”,让智能体能够精准识别不同的对话上下文,避免交叉干扰。二、核心实现思路:会话ID与RunnableWithMessageHistory的结合
实现会话历史加ID的核心,在于LangChain中的RunnableWithMessageHistory组件,它能够将智能体执行器与会话历史管理绑定,通过会话ID来关联不同的对话记录,无需手动拼接历史消息,就能实现上下文的自动记忆。整个实现逻辑可以概括为3步,清晰易懂,无需复杂的配置:1. 初始化会话历史存储容器
首先需要创建一个会话历史的存储载体,用于保存当前会话的所有消息(用户提问和智能体回复)。这个载体可以根据实际需求选择,比如本地内存存储(适合测试)、数据库存储(适合生产环境),核心作用是临时或持久化存储对话记录。我们可以借助LangChain提供的会话历史管理工具,快速创建一个轻量的存储容器,无需自行开发复杂的存储逻辑。2. 绑定智能体与会话历史,关联会话ID
这是最关键的一步:通过RunnableWithMessageHistory,将我们已经创建好的ReAct智能体执行器,与会话历史存储容器进行绑定。同时,我们需要定义一个关联规则——通过会话ID来获取对应的会话历史。这里的核心设计是:每个会话对应一个唯一的会话ID,当智能体被调用时,会通过这个ID找到对应的会话历史,将历史消息自动注入到提示词中,让智能体能够“记住”之前的对话内容。值得注意的是,提示词模板中必须预留会话历史的占位符,这是会话历史能够被成功注入的前提,确保智能体在思考和回答时,能够获取到完整的上下文信息。3. 调用智能体时,传入会话ID实现隔离
在测试或实际调用智能体时,我们只需要在调用参数中传入唯一的会话ID,智能体就会自动关联该ID对应的会话历史。无论同一用户多次提问,还是不同用户同时提问,只要会话ID不同,对话历史就会被分开存储和调用,实现完全隔离。而且无需手动添加历史消息,智能体会自动将每次的用户提问和自身回复,保存到对应会话ID的历史容器中,下次调用时直接复用,极大简化了开发流程。三、关键注意点:避免踩坑的3个细节
在实现过程中,有几个细节容易被忽略,直接影响会话ID的作用效果,这里特别提醒大家:
- 会话ID必须唯一:无论是用户ID+会话标识,还是随机生成的唯一字符串,确保每个会话的ID不重复,否则会出现会话历史混乱的问题。
- 提示词占位符不可缺失:必须在ReAct提示词模板中预留会话历史的占位符,否则智能体无法获取历史消息,即使绑定了会话ID也无法实现上下文记忆。
- 会话历史的存储选择:测试环境可以使用本地内存存储,生产环境建议使用数据库 或缓存(如Redis),避免服务重启后会话历史丢失,同时支持多实例部署时的会话共享。
四、实际效果:会话隔离与自动记忆的实现
通过上述方法实现后,我们可以得到两个核心效果,完全解决多会话上下文混乱的问题:1. 会话隔离:不同会话ID对应的对话历史完全独立,比如会话ID为“tiger”的对话,和会话ID为“cat”的对话,智能体不会混淆两者的上下文,各自的对话能够连贯进行。2. 自动记忆:无需手动管理历史消息,智能体每次调用后,会自动将当前对话内容保存到对应会话ID的历史容器中,下次提问时,能够直接复用之前的对话信息,实现自然流畅的多轮对话。五、总结与延伸
给LangChain ReAct智能体的会话历史添加ID,本质上是通过会话ID实现对话上下文的隔离与关联,核心依赖RunnableWithMessageHistory组件的封装能力,无需复杂的代码开发,就能快速实现多会话管理。这种实现方式不仅适用于ReAct智能体,也适用于LangChain中的其他智能体类型,是多用户、多会话场景下的必备技巧。在实际生产环境中,我们还可以基于会话ID,实现会话历史的持久化、查询、清理等扩展功能,进一步提升智能体的实用性和可维护性。如果大家在实现过程中遇到会话历史注入失败、会话ID关联异常等问题,欢迎在评论区交流探讨,一起完善LangChain智能体的开发实践~ 代码实现:- from langchain_openai import ChatOpenAI
- from langchain_core.tools import Tool
- from langchain.agents import create_react_agent # 改用 ReAct 智能体
- from langchain.agents import AgentExecutor
- from langchain_core.prompts import PromptTemplate # ReAct 用 PromptTemplate 而非 ChatPromptTemplate
- from langchain_core.messages import AIMessage, HumanMessage
- from langchain_community.chat_message_histories import ChatMessageHistory
- from langchain_core.runnables.history import RunnableWithMessageHistory
- # 1. 初始化 LLM
- DEEPSEEK_API_KEY = "123" # 替换为实际的 API Key
- llm = ChatOpenAI(
- api_key=DEEPSEEK_API_KEY,
- base_url="http://172.25.133.51:8085/v1",
- model="qwen3.5-27b-awq",
- temperature=0.3,
- max_tokens=1024,
- )
- # 2. 工具函数
- def huawei_mall_search(query: str) -> str:
- """华为商城搜索工具"""
- print(f"[DEBUG] 工具被调用!搜索关键词:{query}")
- search_results = {
- "众测活动": "华为商城众测活动是让用户体验新品并反馈意见的活动。目前有Mate 60系列众测,参与可赢取礼品。",
- "手机": "华为商城最新手机:Mate 60系列、P60系列、nova系列等。",
- "笔记本": "华为MateBook X Pro、MateBook D系列笔记本电脑。",
- "手表": "华为Watch 4、Watch GT系列智能手表。",
- "默认": "请在华为商城官网查看详细信息或联系客服。"
- }
- for keyword in search_results:
- if keyword in query:
- return f"华为商城搜索结果:{search_results[keyword]}"
- return search_results["默认"]
- # 3. 创建工具
- huawei_tool = Tool(
- name="huawei_mall_search",
- description="查询华为商城相关信息,包括产品、活动、政策等",
- func=huawei_mall_search,
- )
- tools = [huawei_tool]
- # 4. 定义 ReAct 提示词模板(关键修改!)
- react_prompt = PromptTemplate.from_template("""
- Answer the following questions as best you can. You have access to the following tools:
- {tools}
- Use the following format:
- Question: the input question you must answer
- Thought: you should always think about what to do
- Action: the action to take, should be one of [{tool_names}]
- Action Input: the input to the action
- Observation: the result of the action
- ... (this Thought/Action/Action Input/Observation can repeat N times)
- Thought: I now know the final answer
- Final Answer: the final answer to the original input question
- Begin!
- Previous conversation history:
- {chat_history}
- Question: {input}
- Thought: {agent_scratchpad}
- """)
- #上面提示词中{chat_history}是记录历史会话记录的不能少
- # 5. 创建 ReAct 智能体
- try:
- agent = create_react_agent(llm=llm, tools=tools, prompt=react_prompt)
- print("Agent 创建成功")
- except Exception as e:
- print(f"创建 Agent 失败: {e}")
- exit()
- # 6. 创建执行器(保持不变)
- agent_executor = AgentExecutor(
- agent=agent,
- tools=tools,
- verbose=True,
- max_iterations=3,
- handle_parsing_errors=True,
- return_intermediate_steps=True
- )
- # 7. 测试(保持不变)
- print("\n" + "=" * 60)
- print("测试 Agent 工具调用")
- print("=" * 60)
- message_history = ChatMessageHistory()
- agent_with_chat_history = RunnableWithMessageHistory(
- agent_executor,
- # 这是必需的,因为在大多数现实场景中,需要会话 ID
- # 这里并没有真正使用它,因为我们使用的是一个简单 ChatMessageHistory
- lambda session_id: message_history,
- input_messages_key="input",
- history_messages_key="chat_history",
- )
- # 这里我们为 chat_history 传递一个空的消息列表,因为它是聊天中的第一条消息
- chat1 = agent_with_chat_history.invoke(
- {"input": "你好,我是小老虎"},
- config={"configurable": {"session_id": "tiger"}},
- )
- print(chat1)
- print('---------------')
- print('char1最终回复:' + chat1['output'])
- #这里不用手动写了 也能记住
- #history = []+[HumanMessage(content=chat1['input']),AIMessage(content=chat1['output'])]
- chat2 = agent_with_chat_history.invoke(
- {"input": "你知道我的名字吗?"},
- config={"configurable": {"session_id": "tiger"}},
- )
- print(chat2)
- print('---------------')
- print('char2最终回复:' + chat2['output'])
- print("完整会话记录:")
- for msg in message_history.messages:
- print(f"{msg.type}: {msg.content}")
复制代码 结果输出:
Agent 创建成功
============================================================
测试 Agent 工具调用
============================================================
> Entering new AgentExecutor chain...
Parent run 88f54bd0-50e6-4018-9249-ef030d2704fc not found for run 50f303ca-5ec0-4bb7-994b-0570ef8de62f. Treating as a root run.
l.
Thought: 用户只是在打招呼自我介绍,没有提出需要查询华为商城信息的问题。这是一个简单的问候,不需要使用工具。
Final Answer: 你好,小老虎!很高兴认识你!有什么我可以帮助你的吗?比如查询华为商城的产品信息、优惠活动或者相关政策等。
> Finished chain.
{'input': '你好,我是小老虎', 'chat_history': [], 'output': '你好,小老虎!很高兴认识你!有什么我可以帮助你的吗?比如查询华为商城的产品信息、优惠活动或者相关政策等。', 'intermediate_steps': []}
---------------
char1最终回复:你好,小老虎!很高兴认识你!有什么我可以帮助你的吗?比如查询华为商城的产品信息、优惠活动或者相关政策等。
> Entering new AgentExecutor chain...
Parent run 3cb69184-c16f-4f6b-ad16-901cf2580a56 not found for run 0255b5f3-5efe-4925-af16-0f22aa2a2f51. Treating as a root run.
s.
Thought: 根据之前的对话历史,用户在第一条消息中自我介绍为"小老虎",我已经在回复中确认知道了这个名字。这个问题不需要使用华为商城搜索工具,可以直接从对话历史中回答。
Final Answer: 是的,我知道你的名字!你叫"小老虎"。很高兴认识你!有什么我可以帮助你的吗?
> Finished chain.
{'input': '你知道我的名字吗?', 'chat_history': [HumanMessage(content='你好,我是小老虎'), AIMessage(content='你好,小老虎!很高兴认识你!有什么我可以帮助你的吗?比如查询华为商城的产品信息、优惠活动或者相关政策等。')], 'output': '是的,我知道你的名字!你叫"小老虎"。很高兴认识你!有什么我可以帮助你的吗?', 'intermediate_steps': []}
---------------
char2最终回复:是的,我知道你的名字!你叫"小老虎"。很高兴认识你!有什么我可以帮助你的吗?
完整会话记录:
human: 你好,我是小老虎
ai: 你好,小老虎!很高兴认识你!有什么我可以帮助你的吗?比如查询华为商城的产品信息、优惠活动或者相关政策等。
human: 你知道我的名字吗?
ai: 是的,我知道你的名字!你叫"小老虎"。很高兴认识你!有什么我可以帮助你的吗?
更多学习资料尽在 老虎网盘资源
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |