Octopus_Procedures Table
The procedural memory table — named multi-step plans that agents have learned and stored. Procedural memory enables agents to execute complex repeatable tasks without re-deriving the plan every time.
What Is Procedural Memory?
When an Octopus agent successfully executes a complex multi-step task — such as processing an invoice, onboarding a new employee, or running a diagnostic — the resolved plan can be saved as a procedure. On subsequent requests for the same task, the agent retrieves the stored procedure from Octopus_Procedures and executes its steps directly, bypassing the reasoning phase that deriving the plan would require.
This is analogous to a human expert who, having solved a complex problem once, can repeat the solution efficiently without rethinking from first principles.
Column Reference
| Column | Type | Constraints | Description |
|---|---|---|---|
ProcedureId | uniqueidentifier | PK, NOT NULL | GUID primary key. |
AgentId | uniqueidentifier | FK, NOT NULL | The agent that owns this procedure. Agents can only retrieve their own procedures. |
TenantId | int | NOT NULL, IX | Multi-tenancy discriminator. |
Name | nvarchar(300) | NOT NULL, UNIQUE(TenantId, AgentId, Name) | Human-readable, semantic name for the procedure. Used by the agent to match incoming requests against stored procedures. |
Steps | nvarchar(max) | NOT NULL | JSON array of step objects defining the procedure. See Steps JSON schema below. |
ActivatedAt | datetime2 | NULL | UTC timestamp of the last time this procedure was retrieved and executed. Used by the retention job to identify stale procedures. |
CreatedAt | datetime2 | NOT NULL | UTC creation timestamp. |
Steps JSON Schema
Each procedure is a JSON array of step objects. The framework supports several step types:
[
{
"stepId": 1,
"type": "sql-query",
"description": "Fetch the invoice record",
"params": {
"datasourceId": "main-db",
"query": "SELECT * FROM Invoices WHERE InvoiceId = @invoiceId AND TenantId = @tenantId",
"outputVar": "invoice"
}
},
{
"stepId": 2,
"type": "condition",
"description": "Check invoice status",
"params": {
"expression": "invoice.Status == 'Pending'",
"onTrue": 3,
"onFalse": 6
}
},
{
"stepId": 3,
"type": "workflow-trigger",
"description": "Trigger the invoice approval workflow",
"params": {
"workflowId": "invoice-approval-v2",
"inputs": { "invoiceId": "@invoiceId" }
}
},
{
"stepId": 4,
"type": "agent-message",
"description": "Notify user of outcome",
"params": {
"message": "Invoice {{invoiceId}} has been submitted for approval."
}
}
]
Step Types
| Type | Description | Key Params |
|---|---|---|
sql-query | Execute a parameterized SQL SELECT and store the result in an output variable | datasourceId, query, outputVar |
sql-update | Execute a parameterized INSERT/UPDATE/DELETE | datasourceId, command |
workflow-trigger | Fire a Flow Studio workflow with inputs | workflowId, inputs |
agent-message | Send a message to the user in the conversation | message (template string) |
condition | Branch execution based on an expression result | expression, onTrue (stepId), onFalse (stepId) |
tool-call | Call a registered plugin tool directly | pluginId, toolName, arguments |
loop | Repeat a sub-procedure for each item in a collection | collection, stepIds |
How Agents Retrieve Procedures
When the agent receives a user request, it performs a two-stage lookup:
Exact Name Match
The agent extracts the likely task name from the user message using keyword extraction and queries Octopus_Procedures WHERE Name = @extractedName AND AgentId = @agentId AND TenantId = @tenantId.
Semantic Match (Fallback)
If no exact match is found, procedure names are embedded and compared semantically against the user's request using the vector store. The closest matching procedure above the confidence threshold is used.
Fresh Derivation (Final Fallback)
If no procedure matches with sufficient confidence, the agent derives the plan fresh from the LLM and optionally saves the derived plan as a new procedure for future use.
Common Queries
-- All procedures for an agent
SELECT ProcedureId, Name, ActivatedAt, CreatedAt
FROM Octopus_Procedures
WHERE TenantId = @tenantId
AND AgentId = @agentId
ORDER BY Name;
-- Stale procedures (not used in 90 days)
SELECT ProcedureId, Name, ActivatedAt
FROM Octopus_Procedures
WHERE TenantId = @tenantId
AND (ActivatedAt IS NULL OR ActivatedAt < DATEADD(DAY, -90, GETUTCDATE()))
ORDER BY ActivatedAt;
-- Retrieve a specific procedure by name for execution
SELECT ProcedureId, Name, Steps
FROM Octopus_Procedures
WHERE TenantId = @tenantId
AND AgentId = @agentId
AND Name = @procedureName;
A well-populated Octopus_Procedures table represents accumulated organizational knowledge. Agents that have solved common problems store those solutions here, progressively becoming faster and more deterministic for routine tasks. Backing up and exporting procedure data as part of your DR strategy ensures this knowledge is not lost.