Portal Community

Schema at a Glance

The Octopus database schema is intentionally minimal. Four tables cover everything the framework needs: agent registry, session tracking, episodic memory, and procedural memory. The schema is managed via EF Core Code-First migrations, ensuring that deployments are reproducible and upgrades are tracked.

-- Entity Relationship Summary
Octopus_Agents (AgentId PK)
    └── Octopus_Conversations (AgentId FK)
            └── Octopus_Episodes (SessionId FK)
    └── Octopus_Procedures (AgentId FK)

Octopus_Agents

ColumnTypeNullableDescription
AgentIduniqueidentifierNoPrimary key — GUID assigned at agent registration
TenantIdintNoMulti-tenancy discriminator — required on all queries
Namenvarchar(200)NoHuman-readable agent name, unique per tenant
SystemPromptnvarchar(max)YesThe base system prompt injected at the start of every conversation
PluginRefsnvarchar(max)YesJSON array of plugin identifiers the agent has access to
Confignvarchar(max)YesJSON blob for model settings: temperature, maxTokens, modelId, etc.
VersionintNoSchema version of the agent definition — incremented on update
Statusnvarchar(50)NoActive, Inactive, Draft, Deprecated
CreatedAtdatetime2NoUTC creation timestamp
UpdatedAtdatetime2NoUTC last-modified timestamp

Octopus_Conversations

ColumnTypeNullableDescription
SessionIduniqueidentifierNoPrimary key — GUID created when a new chat session starts
AgentIduniqueidentifierNoFK → Octopus_Agents.AgentId
TenantIdintNoMulti-tenancy discriminator
UserIdnvarchar(200)YesIdentity of the user who initiated the conversation
StartedAtdatetime2NoUTC timestamp when the session was opened
EndedAtdatetime2YesUTC timestamp when the session was closed (null = still active)
Statusnvarchar(50)NoActive, Ended, TimedOut, Error
Metadatanvarchar(max)YesJSON blob for caller-supplied context (channel, referrer, custom tags)

Octopus_Episodes

ColumnTypeNullableDescription
EpisodeIduniqueidentifierNoPrimary key
SessionIduniqueidentifierNoFK → Octopus_Conversations.SessionId
TenantIdintNoMulti-tenancy discriminator
Rolenvarchar(50)Nouser, assistant, system, tool
Messagesvarbinary(max)NoCompressed JSON array of message objects in this episode
ToolCallsvarbinary(max)YesCompressed JSON of tool/function call records, if any
EmbeddingRefnvarchar(500)YesID pointing to the vector store entry for this episode
Timestampdatetime2NoUTC creation time of this episode turn
TokenCountintYesApproximate token count for this episode (for context window management)

Octopus_Procedures

ColumnTypeNullableDescription
ProcedureIduniqueidentifierNoPrimary key
AgentIduniqueidentifierNoFK → Octopus_Agents.AgentId — the agent this procedure belongs to
TenantIdintNoMulti-tenancy discriminator
Namenvarchar(300)NoUnique name for this procedure within the agent+tenant scope
Stepsnvarchar(max)NoJSON array of procedure steps with type, action, params
ActivatedAtdatetime2YesWhen this procedure was last activated/executed
CreatedAtdatetime2NoUTC creation timestamp

Indexes

The following indexes are created by the EF Core migrations to support common query patterns:

TableIndex ColumnsPurpose
Octopus_Agents(TenantId, Status)Filter active agents per tenant
Octopus_Agents(TenantId, Name) UNIQUEEnforce unique agent names within a tenant
Octopus_Conversations(TenantId, AgentId, Status)Find active sessions for an agent
Octopus_Conversations(TenantId, UserId)Look up all sessions for a user
Octopus_Episodes(TenantId, SessionId, Timestamp DESC)Retrieve episodes in reverse-chronological order (most recent first)
Octopus_Episodes(TenantId, EmbeddingRef)Retrieve SQL episode by vector store ID after semantic search
Octopus_Procedures(TenantId, AgentId, Name) UNIQUEEnforce unique procedure names per agent per tenant
EF Core Migrations

The schema is managed via EF Core Code-First migrations located at Octopus/Plugins/BuiltIn/SqlServerStorage/Migrations/. Never modify the schema manually. Apply changes by running dotnet ef migrations add <MigrationName> and dotnet ef database update.

JSON Column Conventions

Several columns store structured data as JSON. Here are the conventions followed throughout the schema:

-- Example: Query plugin refs using OPENJSON
SELECT a.AgentId, a.Name, p.value AS PluginRef
FROM Octopus_Agents a
CROSS APPLY OPENJSON(a.PluginRefs) AS p
WHERE a.TenantId = 42
  AND a.Status = 'Active';