Skip to main content

Conversation Model

Conversations are the primary interface between users and agents on the XpressAI Platform. They are not just chat logs --- they are structured entities with participant management, dispatch rules, deduplication, and read-state tracking. Understanding how conversations work is important for building agents that behave correctly in group settings, for debugging message delivery, and for reasoning about what an agent "sees" when it responds.

Conversation Types

The platform supports three types of conversations, each with different dispatch semantics.

TypeParticipantsAgent behavior
GroupMultiple users + multiple agentsOnly mentioned agents respond (via @agent)
DM (Direct Message)Two usersNo agent involvement
Agent DMOne user + one agentAgent always responds to every message

The conversation type determines how the platform decides which agents to activate when a message is posted.

Conversation Keys

Every conversation has a conversation key that determines its scope and uniqueness.

Key patternScopeExample use case
globalProject-wideA shared team channel
per_roomTied to a specific room/contextA conversation scoped to a particular feature, topic, or UI context (a "room" is any named scope that groups related conversations -- for example, a task detail page or a specific channel)
direct_messageBetween two specific participantsPrivate user-to-user or user-to-agent chat

The conversation key, combined with the project ID and participant IDs, uniquely identifies a conversation. This means the same two users in the same project always return to the same DM thread rather than creating new ones.

Message Dispatch Flow

When a user posts a message, the platform does not simply broadcast it. It follows a structured dispatch process.

Mentions and Group Conversations

In group conversations with multiple agents, the platform uses @mentions to determine which agents respond. This prevents every agent from replying to every message, which would be chaotic.

  • @toby can you research this? --- only the agent named "toby" receives the message for processing.
  • Hey team, good morning! --- no agents are dispatched because none are mentioned.

In Agent DM conversations, mentions are not required. The agent is the only other participant, so it always responds.

Design Rationale

The mention-based dispatch for group conversations was a deliberate choice. Early versions dispatched to all agent participants on every message, which led to agents talking over each other and generating confusing, expensive multi-agent responses to casual messages. Mentions give users explicit control over which agent engages.

Deduplication

Messages can be re-delivered to an agent's queue due to the NACK/requeue mechanism in the Agent Messaging System. The deduplication layer prevents duplicate processing.

Before dispatching a message to an agent:

  1. The platform checks the deduplication table for this message ID + agent combination.
  2. If an entry exists, the message has already been dispatched and is skipped.
  3. If no entry exists, the dispatch proceeds and the entry is recorded.

This is especially important for preventing duplicate side effects. Without deduplication, a redelivered message could cause an agent to send the same email twice or create duplicate tasks.

note

There is also execution-level deduplication in the messaging system. The dispatch-level deduplication described here prevents the same message from being sent to an agent's queue twice, while execution-level deduplication prevents duplicate tool executions within the NACK/requeue loop. See Agent Messaging System for details.

History Window

Agents have limited context. When the platform builds the prompt for an agent, it includes only the last N messages from the conversation. The default window size is 50 messages.

This means:

  • In a long conversation, the agent does not see the first messages.
  • Critical context from early in a conversation may need to be restated or stored in the agent's memory.
  • The history window is a trade-off between context richness and token cost.

Older messages are not lost --- they remain in the database and can be accessed through the agent's mid-term memory (Vecto semantic search). But they are not included in the direct LLM prompt. See Agent Messaging System for more details on how the history window is managed.

Why 50?

The default of 50 messages balances context quality with cost. Most conversations have their most relevant context in the recent messages. For long-running conversations, the agent's memory system fills the gap by surfacing semantically relevant older messages.

Read State

The platform tracks read state per user per conversation. This powers:

  • Unread message counts in the sidebar.
  • "New messages" indicators when returning to a conversation.
  • The "mark as read" action when a user views a conversation.

Read state is updated when a user opens a conversation or scrolls to the bottom. It is stored as a timestamp of the last-read message, which makes the unread count calculation a simple query for messages after that timestamp.

Typing Indicators

When an agent is processing a message (calling the LLM, executing tools), the platform broadcasts typing indicators to the conversation participants via SSE (Server-Sent Events). This gives users visual feedback that the agent is working on a response, similar to the "typing..." indicator in consumer messaging apps.

Typing indicators are ephemeral --- they are not persisted to the database. They exist only in the SSE stream and disappear when the agent's response arrives or the connection is closed.

How It All Connects

The conversation model sits at the intersection of several platform systems: