ConversationMemory
ConversationMemory automatically preserves and appends conversation history across successive node invocations.
By default, an agent_node is stateless: each invocation is isolated. Attaching ConversationMemory causes previous conversational turns to be remembered and prepended to new user inputs automatically.
import railtracks as rt
from railtracks.prebuilt.middleware import ConversationMemory
# ConversationMemory is node-level: it preserves and appends conversation
# history across repeated invocations automatically.
memory = ConversationMemory()
ChatAgent = rt.agent_node(
name="chat-demo",
llm=rt.llm.OpenAILLM("gpt-6-luna"),
middleware=[memory],
)
flow = rt.Flow("ChatFlow", entry_point=ChatAgent)
# flow.invoke("What is your name?")
# flow.invoke("What did I just ask?") -> Agent remembers Turn 1!
How It Works
- On the first turn, the user message is sent to the model normally, and the resulting message history is cached in the active session context (
rt.context) undermemory.context_key. - By default, each
ConversationMemoryinstance generates a unique, isolated session context key (e.g.conversation_history_a1b2c3d4). This ensures multiple agents running in the same flow each have their own independent conversation memory by default. - On subsequent turns, the prior message history is retrieved from the session context, the incoming user input is appended, and the accumulated history is passed to the agent.
- The session context variable is automatically updated after every turn, accumulating multi-turn conversation context.
Multi-Agent Isolation & Configuration
- Automatic Per-Instance Isolation: Each
ConversationMemory()instance has its own unique session context key by default. Two agents in the same flow maintain completely independent memory stores with zero extra configuration. - Custom Context Key: Pass an explicit
context_keyto assign a known session variable name: - Shared Memory Between Agents: If you want multiple agents to share a common conversation history, pass the same explicit
context_keyor instance: - Seeding Prior History: To start a conversation from existing turns, give the instance an explicit
context_keyand set that key in the flow or session context. A default instance's generated key is not addressable from outside, so seeding always needs an explicit key: - Context Inspection After Flow Completion:
flow.invoke()andflow.ainvoke()return only the flow's final result. To inspect context after an invocation finishes, useflow.connect(), which returns aFlowConnection: - Avoid Passing History Manually: Do not pass prior
MessageHistoryas user input whenConversationMemoryis attached, as the middleware automatically accumulates and prepends history across turns. - Max Messages: Pass
max_messages=10to prune history to the most recent N messages and avoid exceeding model context windows. BothNone(the default) and0mean no limit; a negative value raisesValueError. - One Conversation Per Instance: A memory instance models a single sequential conversation. Invoking the same instance concurrently (for example
asyncio.gatherover one agent) makes both turns read the same prior history, so the later write wins and the other turn is lost. Give each concurrent branch its ownConversationMemory, or, if the branches must share one history, putLockoutermost so the read and write are serialized: - Clearing Memory: Call
memory.clear()to wipe the stored history from both the instance and the active session context. Called after a run has finished there is no active context left to reach, so the instance copy is dropped but aFlowConnectionstill open on that run keeps reading the pre-clear value throughconn.context.