Octopus_Episodes Table
The episodic memory table — one row per conversation turn. This is the highest-volume table in the Octopus database, storing the actual message content, tool call records, and embedding references for every interaction.
What Is an Episode?
In Octopus terminology, an episode is a single unit of episodic memory — one conversational turn. When a user sends a message, that message is an episode. When the agent replies, that reply is another episode. When the agent makes a tool call, the tool call request and response are recorded as episodes.
This fine-grained recording enables the memory system to reconstruct any conversation in precise detail, perform semantic recall of specific past interactions, and provide an auditable log of every agent action.
Column Reference
| Column | Type | Constraints | Description |
|---|---|---|---|
EpisodeId | uniqueidentifier | PK, NOT NULL | GUID primary key. Generated using NEWSEQUENTIALID() for clustered index efficiency. |
SessionId | uniqueidentifier | FK, NOT NULL | References the parent conversation in Octopus_Conversations. |
TenantId | int | NOT NULL, IX | Multi-tenancy discriminator. Present on every episode for direct tenant-scoped queries without requiring a join to Conversations. |
Role | nvarchar(50) | NOT NULL | The speaker role for this episode: user, assistant, system, tool. |
Messages | varbinary(max) | NOT NULL | GZip-compressed JSON array of message content objects. Decompressed by the EF Core value converter on read. |
ToolCalls | varbinary(max) | NULL | GZip-compressed JSON of tool/function call records. Only populated for tool role episodes. |
EmbeddingRef | nvarchar(500) | NULL | ID in the vector store pointing to the embedding generated for this episode's content. Used for semantic recall lookup. |
Timestamp | datetime2 | NOT NULL | UTC time when this episode was created. Used for chronological ordering and retention windowing. |
TokenCount | int | NULL | Approximate token count of this episode's content. Used by the context window manager to stay within model limits. |
Messages JSON Structure
The Messages column (after decompression) contains a JSON array of message objects following the OpenAI/chat completion message format:
[
{
"role": "user",
"content": "What is the status of my order #ORD-8821?",
"name": null
}
]
For multi-part messages (e.g., text + image), the content field is an array of content parts:
[
{
"role": "user",
"content": [
{ "type": "text", "text": "Analyze this chart:" },
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
]
}
]
ToolCalls JSON Structure
Tool call episodes record both the function call request (from the model) and the function result (returned to the model):
{
"toolCallId": "call_abc123",
"functionName": "get_order_status",
"arguments": { "orderId": "ORD-8821" },
"result": {
"status": "shipped",
"carrier": "FedEx",
"trackingNumber": "1Z999AA1012345678",
"estimatedDelivery": "2026-05-27"
},
"durationMs": 142,
"isError": false
}
Role Values
| Role | Source | Has ToolCalls | Description |
|---|---|---|---|
user | Human | No | The human's message to the agent |
assistant | AI Model | Sometimes | The agent's response — may include tool call requests inline |
system | Framework | No | System-injected context (e.g., memory summaries injected mid-conversation) |
tool | Framework | Yes | The result returned from a tool/function call execution |
Embedding Reference and Semantic Recall
When an episode is stored, the Octopus memory system asynchronously generates a vector embedding of the episode content and stores it in the configured vector store (Qdrant or PGVector). The EmbeddingRef column records the vector store's internal ID for that embedding.
During semantic recall (when a new user message triggers a search for relevant past episodes), the flow is:
Query Embedding
The user's new message is embedded into a vector using the same model that embedded past episodes.
Vector Search
The vector store finds the top-K most semantically similar episode embeddings and returns their EmbeddingRef IDs.
SQL Lookup
The returned IDs are used to query Octopus_Episodes WHERE EmbeddingRef IN (@ids) AND TenantId = @tenantId to retrieve the full message text for injection into the context window.
Short episodes (under a minimum token threshold), system injections, and tool result episodes may not have embeddings generated. The memory system skips semantic recall for episodes without an EmbeddingRef. This is by design — embedding every token would waste vector store capacity on low-value data.
Volume Considerations
Octopus_Episodes is by far the highest-volume table. In a production multi-tenant deployment with 100 active agents each handling 50 conversations per day averaging 20 turns per conversation, you get approximately 100,000 new episode rows per day. At an average compressed size of 2KB per row, that is 200MB of new data daily before archival kicks in.
The MemoryRetentionJob runs nightly and moves episodes older than the configured TTL to an archive table, keeping the active table query-fast. See the Maintenance page for details.