How ConversationManager Handles Conversation History and Maintains Context Across Interactions

The ConversationManager stitches together chronological message logs and semantically similar past exchanges to provide full conversational background for every LLM request, storing each turn as embedded MessageData records via the VectorStorageProvider interface.

In the heurist-network/heurist-agent-framework, the ConversationManager serves as the central orchestration component that handles conversation history through a pipeline of retrieval, augmentation, and persistence operations. Located in core/components/conversation_manager.py, it ensures that every new LLM call receives both the exact sequence of prior exchanges and relevant semantically similar dialogues from any chat session.

Three-Stage Context Retrieval Pipeline

The manager implements a three-stage pipeline that processes conversation history before each LLM inference. Each stage corresponds to a specific method in core/components/conversation_manager.py.

Retrieving Chronological History with get_conversation_context

When a new request arrives, the get_conversation_context(chat_id, limit) method queries the underlying message store for the most recent agent-response entries belonging to the same chat_id. The implementation fetches raw rows and sorts them by timestamp in descending order, then reverses the list to obtain strict chronological order. Each stored pair is formatted into a conversational string:


User: <original_query>
Assistant: <message>

This block is prefixed with a header indicating it represents the conversation history, providing the LLM with the exact sequence of prior turns for that specific session.

Augmenting with Semantic Similarity via get_similar_messages

For richer grounding beyond sequential logs, get_similar_messages(embedding, chat_id, threshold, limit) performs a vector similarity search. The method computes the cosine similarity between the current query's embedding and stored user-message embeddings, returning records where similarity meets or exceeds the threshold parameter (typically ≥ 0.85).

For each matching user message, the manager retrieves the corresponding agent-response and constructs a "Related previous conversations" block that includes the similar question, the past answer, and the similarity score. This section explicitly advises the model to provide a fresh perspective, preventing verbatim repetition while leveraging relevant context from any historical chat.

Persisting Turns Through store_interaction

After the LLM generates a response, store_interaction(message, response, chat_id, metadata) persists the complete turn. The implementation:

  1. Embeds the user message via get_embedding() and stores it as a MessageData record with message_type="user_message"
  2. Embeds the assistant reply and stores it as a MessageData record with message_type="agent_response", maintaining a reference to the original query and its embedding

Both records are written immediately to the message_store, making them available for subsequent context building operations.

Storage Architecture and the VectorStorageProvider Interface

The ConversationManager delegates storage operations to an implementation of the abstract VectorStorageProvider interface defined in core/embedding.py. This provider supplies two critical operations:

  • find_messages(...): Fetches raw rows filtered by message type and chat ID, used by get_conversation_context to build chronological logs
  • find_similar_messages(embedding, ...): Executes vector-similarity queries against stored embeddings, powering the semantic retrieval in get_similar_messages

The concrete PostgresVectorStorage class provides the production implementation, utilizing PostgreSQL with pgvector extension to store MessageData objects and perform efficient similarity searches.

Implementation Example

from core.components.conversation_manager import ConversationManager
from core.embedding import PostgresVectorStorage, PostgresConfig

# Initialize PostgreSQL storage backend

pg_cfg = PostgresConfig(
    host="localhost",
    port=5432,
    database="heurist",
    user="heurist_user",
    password="***",
    table_name="messages",
)
store = PostgresVectorStorage(pg_cfg)
store.initialize()

# Instantiate the conversation manager

conv_mgr = ConversationManager(message_store=store)

# Process a new user query

chat_id = "session-42"
user_msg = "How do I reset my API key?"

# 1. Retrieve chronological history

history_ctx = await conv_mgr.get_conversation_context(chat_id, limit=10)

# 2. Compute embedding and find similar messages

embedding = conv_mgr.get_embedding(user_msg)
similar_ctx = await conv_mgr.get_similar_messages(
    embedding, chat_id=chat_id, threshold=0.85, limit=3
)

# 3. Construct complete prompt for LLM

full_prompt = f"{history_ctx}{similar_ctx}\nUser: {user_msg}\nAssistant:"

# Submit full_prompt to LLM client...

# 4. Store the interaction after receiving response

assistant_reply = "You can reset your API key from the dashboard..."
metadata = {
    "source_interface": "web",
    "tool_call": None,
    "response_type": "text",
    "key_topics": ["api-key", "reset"],
}
await conv_mgr.store_interaction(user_msg, assistant_reply, chat_id, metadata)

The same ConversationManager instance safely handles multiple concurrent chats, with each operation isolating history by unique chat_id values.

Summary

  • Chronological retrieval: get_conversation_context queries message_store for recent agent responses by chat_id, sorting by timestamp to maintain exact conversational sequence
  • Semantic augmentation: get_similar_messages leverages vector embeddings and cosine similarity to surface relevant historical exchanges across all chats
  • Persistent storage: store_interaction embeds and saves both user queries and assistant responses as MessageData records immediately after generation
  • Storage abstraction: The manager depends on VectorStorageProvider (typically PostgresVectorStorage) for both exact filtering and similarity search operations
  • Session isolation: All history operations are scoped to specific chat_id parameters, enabling multi-tenancy without cross-contamination

Frequently Asked Questions

How does ConversationManager isolate different chat sessions?

The manager uses the chat_id parameter as a mandatory filter in get_conversation_context and get_similar_messages, ensuring that chronological history only pulls records matching the specific session identifier. While semantic similarity searches can optionally query across all chats, the primary conversation flow remains strictly partitioned by chat_id.

What is the role of embeddings in conversation history?

Embeddings enable semantic retrieval beyond exact keyword matching. When store_interaction saves a turn, both the user message and agent response are embedded using the get_embedding function. These vectors allow find_similar_messages to compute cosine similarity between the current query and historical messages, surfacing conceptually related conversations even when phrasing differs significantly.

Which storage backends are compatible with ConversationManager?

The manager requires any class implementing the VectorStorageProvider abstract interface from core/embedding.py. The reference implementation uses PostgresVectorStorage, which requires PostgreSQL with the pgvector extension. Custom implementations must provide find_messages() for filtered retrieval and find_similar_messages() for vector similarity search.

How does similarity-based context differ from chronological history?

Chronological history provides the exact sequence of prior exchanges within the same chat_id, ensuring continuity and turn-taking coherence. Similarity-based context retrieves semantically related exchanges from any chat session when their embeddings exceed the similarity threshold, offering relevant background knowledge without requiring the user to have had prior interactions on that specific topic in the current thread.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →