langchain :记忆组件混淆概念澄清 & 创建Conversational ReAct后显示指定 记忆组件

发布于:2025-07-08 ⋅ 阅读:(54) ⋅ 点赞:(0)

Q:但是langchain 0.3的记忆组件有多种,比如ConversationBufferMemory,ConversationBufferWindowMemory, 那么create_react_agent用的是哪一种?或者是否可以显示的指定?

你提的这个问题非常精准,直击了 LangChain 模块化设计的核心!答案会让你对新版 LangChain 的设计有更深的理解。

核心答案:create_react_agent 本身不关心也不使用任何记忆组件。你可以,也必须**,在 AgentExecutor 中显式地指定你想要使用的记忆类型。**

这是一个非常重要的设计分离:

  • create_react_agent 的职责是创建 Agent 的核心决策逻辑。它只负责将 LLM、工具(Tools)和提示(Prompt)三者“粘合”起来,生成一个懂得如何“思考”和“行动”的 Runnable 对象。它本身是无状态的。
  • AgentExecutor 的职责是执行和管理 Agent 的运行循环。它负责调用 Agent、执行工具、处理错误,以及——正如你所问的——管理对话记忆

如何为你的 Agent 指定记忆类型?

你可以在创建 AgentExecutor 时,将任何你想要的记忆组件实例通过 memory 参数传递进去。AgentExecutor 会自动处理好后续的一切:

  1. 在每次调用 Agent 之前,从 memory 对象中加载历史对话。
  2. 将加载的历史对话填充到提示模板中名为 chat_history 的占位符里。
  3. 在 Agent 完成一次交互后,将这次新的输入和输出保存回 memory 对象中。
示例:明确使用 ConversationBufferWindowMemory

让我们用一个具体的例子,来创建一个只记住最近 2 轮对话的 Conversational ReAct Agent。

from langchain_openai import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain import hub
from langchain.agents import create_react_agent, AgentExecutor
from langchain.memory import ConversationBufferWindowMemory # 明确导入窗口记忆

# 1. 初始化 LLM 和工具
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
tools = [TavilySearchResults(max_results=1)]

# 2. 明确创建你想要的记忆实例
# 我们创建一个只保留最近 k=2 轮对话的窗口记忆
memory = ConversationBufferWindowMemory(
    k=2,
    memory_key="chat_history", # 必须和 prompt 中的占位符名称一致
    return_messages=True
)

# 3. 拉取一个支持对话的 prompt
# "hwchase17/react-chat" 模板内置了 `MessagesPlaceholder(variable_name="chat_history")`
prompt = hub.pull("hwchase17/react-chat")

# 4. 创建 Agent (这里不涉及 memory)
agent = create_react_agent(llm, tools, prompt)

# 5. 创建 AgentExecutor,并在这里“注入”我们选择的记忆组件
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,  # <--- 在这里显式指定!
    verbose=True
)

# --- 开始对话,观察窗口记忆的效果 ---

# 第 1 轮对话 (记忆中: 1)
print("--- Interaction 1 ---")
agent_executor.invoke({"input": "Hi, my name is Bob."})

# 第 2 轮对话 (记忆中: 1, 2)
print("\n--- Interaction 2 ---")
agent_executor.invoke({"input": "I live in New York."})

# 第 3 轮对话 (记忆中: 2, 3。第 1 轮被挤掉了)
print("\n--- Interaction 3 ---")
agent_executor.invoke({"input": "What is my name?"})

输出分析:

当你运行到第三次交互时,你会发现 Agent 已经不记得你的名字是 “Bob” 了。这是因为 ConversationBufferWindowMemory(k=2) 的作用,它只保留了最近的两次交互(“我住在纽约” 和 “我叫什么名字?”),而最早的 “我叫 Bob” 已经被“挤出”了记忆窗口。

如果你把 memory = ConversationBufferWindowMemory(k=2, ...) 换成 memory = ConversationBufferMemory(...),那么在第三轮交互时,Agent 就会轻松地回答出你的名字。

常用记忆组件总结

你可以根据需求,选择并实例化不同的记忆组件传入 AgentExecutor

记忆组件 工作方式 适用场景
ConversationBufferMemory 存储所有对话历史。 需要完整上下文的短对话,但小心过长的对话会超出模型的 token 限制。
ConversationBufferWindowMemory 只存储最近的 k 轮对话。 需要上下文但又想控制 token 数量的持续对话,是最常用的选项之一。
ConversationSummaryMemory 随着对话进行,不断将旧的对话内容用 LLM 进行总结。 非常长的对话,既要保留早期信息又要节省 token 的场景。
ConversationSummaryBufferMemory 结合了 Buffer 和 Summary,保留最近的几轮对话原文,并把更早的对话进行总结。 对近期对话细节和长期对话主线都有要求的复杂场景。

结论: LangChain 的新设计赋予了你完全的控制权。create_react_agent 只负责“思考逻辑”,而 AgentExecutor 负责“执行和记忆”,你可以像插拔 U 盘一样,轻松地为你的 Agent 配置任何类型的记忆模块。

Q:为什么在创建create_react_agent制定了tool,然后在AgentExecutor又指定了tool?

这是一个非常棒的观察,也是一个能帮助你彻底理解 LangChain 新版模块化设计的关键问题!

简单来说,AgentAgentExecutor 是两个独立的角色,它们都需要知道 tools 的信息,但用途完全不同。

我们可以用一个 “经理”“工人” 的比喻来理解:

  • create_react_agent 创建的是 “经理 (Agent)”:经理负责决策。他需要一份工具清单(包含每个工具的名称和功能描述),这样他才能在接到任务时,聪明地决定“哦,这个任务应该让‘搜索工人’去做”。他只下达指令,自己不动手。
  • AgentExecutor 是 “负责执行的工头”:工头负责执行。他手下管理着所有真正的工人(工具对象)。当他收到经理的指令(例如“使用‘搜索工具’去查‘LangChain CEO’”),他需要根据指令中的工具名称,找到那个真正会干活的“搜索工人”对象,然后命令他去工作。

现在,我们把这个比喻对应到代码中:

1. create_react_agent(..., tools, ...) 的职责 (经理做决策)

create_react_agent 需要 tools 列表,是为了生成给 LLM 看的提示 (Prompt)

它会遍历你传入的 tools 列表,提取出每个工具的 name (名称) 和 description (功能描述),然后将它们格式化成一段文本,插入到提示模板的 {tools}{tool_names} 占位符中。

例如,提示模板中的这部分:

You have access to the following tools:
{tools}

Use the following format:
...
Action: the action to take, should be one of [{tool_names}]
...

会被 create_react_agent 填充成:

You have access to the following tools:
TavilySearchResults: A search engine optimized for comprehensive, accurate, and trusted results.

Use the following format:
...
Action: the action to take, should be one of [TavilySearchResults]
...

所以,传给 create_react_agenttools,是为了让 LLM (经理) 知道自己有哪些工具可用,以及它们分别能干什么,从而做出正确的决策。

2. AgentExecutor(..., tools, ...) 的职责 (工头去执行)

AgentExecutor 需要 tools 列表,是为了在接收到 LLM 的决策后,能真正地执行工具

当 LLM (经理) 输出它的决策后,例如:

Thought: I need to find the CEO of LangChain.
Action: TavilySearchResults
Action Input: "Who is the CEO of LangChain?"

AgentExecutor (工头) 会解析这段文本。它看到 Action: TavilySearchResults,就会在自己持有的 tools 列表(一个包含真实工具对象的 Python 列表)中,查找那个 name 属性为 “TavilySearchResults” 的工具对象。找到后,它会调用这个对象的 .invoke("Who is the CEO of LangChain?") 方法来执行搜索。

所以,传给 AgentExecutortools,是为了在 LLM 做出选择后,能够找到并运行那个具体的、可执行的工具对象。

总结

组件 角色 为什么需要 tools
create_react_agent 决策者 (经理) 为了“看”:读取工具的名称和描述,生成提示,让 LLM 知道有哪些选项可选。
AgentExecutor 执行者 (工头) 为了“用”:根据 LLM 输出的工具名称,查找并调用实际的工具对象来完成任务。

这种设计上的分离,正是 LangChain 模块化和灵活性的体现。它将“决策逻辑”和“执行逻辑”解耦,使得你可以独立地修改和配置它们,例如,你可以用同一个 AgentExecutor 来运行不同逻辑的 Agent,反之亦然。


网站公告

今日签到

点亮在社区的每一天
去签到