Skip to content
Notifications
Clear all

How do I share chat memory between different LangChain agents reliably?

1 Posts
1 Users
0 Reactions
3 Views
(@llm_eval_curious_42)
Estimable Member
Joined: 4 months ago
Posts: 57
Topic starter   [#1066]

Having extensively benchmarked various LangChain agent architectures for multi-turn conversational tasks, I've identified memory sharing as a critical, often unstable, component in complex workflows. The core challenge is that while individual agents (e.g., a `ReAct` agent, a `SQL` agent) can be instantiated with their own `ConversationBufferMemory`, creating a coherent, persistent dialogue history across a sequential or hierarchical chain of agents is non-trivial. The naive approach of passing the memory object to each agent constructor often leads to context contamination, key collisions, or outright failures when the agents use different prompt templates or input/output keys.

My experiments point to three primary strategies for reliable inter-agent memory sharing, each with distinct trade-offs in terms of consistency, flexibility, and complexity.

**1. Centralized Memory with a Custom Chain or Supervisor**
This is the most robust pattern. You create a single memory instance and a custom `Chain` or `RunnableSequence` that orchestrates the agents, explicitly managing the memory's input and output. This avoids the internal memory handling of individual agents.

```python
from langchain.agents import AgentExecutor, create_react_agent
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough

# Central memory instance
shared_memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)

# Define your agents (tool_executor, llm, etc.) here...
# agent1 = AgentExecutor(...)
# agent2 = AgentExecutor(...)

# Custom orchestration chain
def route_input(data):
# Logic to decide which agent to call based on input
if "database" in data["input"].lower():
return agent1
else:
return agent2

orchestrator = (
RunnablePassthrough.assign(
chat_history=lambda x: shared_memory.load_memory_variables({})["chat_history"]
)
| route_input
)
# You then invoke the orchestrator and save context back to shared_memory manually.
```

**2. Memory-Backed Chat Message History with a Defined Schema**
Utilize a single `ChatMessageHistory` instance (e.g., `RedisChatMessageHistory`) that multiple agents can access. The key is to strictly standardize the prompt templates across all agents to use the same memory key and placeholder.

```python
from langchain.memory import ConversationBufferMemory
from langchain_community.chat_message_histories import RedisChatMessageHistory

# Shared message history backend
redis_history = RedisChatMessageHistory(
session_id="shared_session",
url="redis://localhost:6379"
)

# Create memory objects for each agent, all pointing to the same history
shared_memory_base = ConversationBufferMemory(
chat_memory=redis_history,
memory_key="history",
return_messages=True
)

# Agent prompts MUST include a MessagesPlaceholder for the key "history"
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are an assistant..."),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
```

**3. Custom Agent Output Parsing and Memory Injection**
For maximum control, bypass the agent's internal memory mechanism. Run agents without memory, but parse their output and append it to a central memory store as structured messages (Human/AI). This requires more plumbing but eliminates side effects.

**Pitfalls Observed in Benchmarking:**
* **Key Mismatches:** Agent `A` uses `"chat_history"` while Agent `B` expects `"memory"`. This causes silent context drops.
* **Prompt Template Incompatibility:** Not all agent factories support the `MessagesPlaceholder` seamlessly. The `create_react_agent` function is more amenable than some legacy `initialize_agent` methods.
* **State Mutation Risks:** Directly passing the same `ConversationBufferMemory` object to two `AgentExecutor` instances can lead to race conditions during concurrent operations or unexpected context clearing.
* **Tool Interaction Conflicts:** Some agents may modify the memory buffer when using tools, writing tool observations into the chat history if not carefully configured.

My preliminary results suggest Strategy 1 (Centralized Orchestration) offers the highest reliability for deterministic workflows, as it decouples memory management from agent execution. Strategy 2 is effective for simpler, homogenous agent collections. I am currently designing a benchmark to quantitatively measure context retention fidelity across these patterns using a suite of multi-agent, multi-turn query tasks.


Prompt engineering is engineering


   
Quote