Go Library
Membrane can run inside your Go process. The embedded facade exposes the same core model as the daemon: graph-aware capture, trust-gated graph retrieval, revision, reinforcement, and metrics.
Installation
go get github.com/BennettSchwartz/membrane
Create An Instance
cfg := membrane.DefaultConfig()
cfg.DBPath = "my-agent.db"
m, err := membrane.New(cfg)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
if err := m.Start(ctx); err != nil {
log.Fatal(err)
}
defer m.Stop()
Note
Always call m.Stop() to shut down schedulers and close the database.
Capture Memory
Use CaptureMemory for events, tool output, observations, working state, and agent turns. The response contains the primary record plus any linked records or graph edges.
capture, err := m.CaptureMemory(ctx, ingestion.CaptureMemoryRequest{
Source: "auth-agent",
SourceKind: "tool_output",
Content: map[string]any{
"tool_name": "go test",
"args": map[string]any{"packages": []string{"./pkg/auth"}},
"result": map[string]any{"exit_code": 0, "stdout": "ok ./pkg/auth"},
},
Context: map[string]any{"thread_id": "session-001"},
ReasonToRemember: "Successful auth package verification",
Summary: "Auth package tests passed",
Tags: []string{"auth", "tests"},
Scope: "project-auth",
Sensitivity: schema.SensitivityLow,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(capture.PrimaryRecord.ID)
Common Source Kinds
| Source kind | Use for |
|---|---|
event | User messages, agent replies, errors, status changes |
tool_output | Tool calls and structured results |
observation | Subject-predicate-object facts |
working_state | In-flight task state |
agent_turn | Generic agent turn capture |
Observation Example
_, err := m.CaptureMemory(ctx, ingestion.CaptureMemoryRequest{
Source: "auth-agent",
SourceKind: "observation",
Content: map[string]any{
"subject": "user",
"predicate": "prefers_language",
"object": "Go",
},
ReasonToRemember: "Remember user language preference",
Summary: "User prefers Go",
Tags: []string{"preference"},
Sensitivity: schema.SensitivityLow,
})
Working State Example
_, err := m.CaptureMemory(ctx, ingestion.CaptureMemoryRequest{
Source: "auth-agent",
SourceKind: "working_state",
Content: map[string]any{
"thread_id": "session-001",
"state": "executing",
"next_actions": []string{"run tests", "review output"},
"context_summary": "Refactoring auth middleware",
},
Summary: "Auth refactor in progress",
Tags: []string{"auth", "working"},
Sensitivity: schema.SensitivityLow,
})
Record Outcome
RecordOutcome updates an existing episodic record with success, failure, or partial outcome state.
updated, err := m.RecordOutcome(ctx, ingestion.IngestOutcomeRequest{
Source: "auth-agent",
TargetRecordID: capture.PrimaryRecord.ID,
OutcomeStatus: schema.OutcomeStatusSuccess,
})
Retrieve Graph Context
RetrieveGraph returns ranked root records and a bounded graph neighborhood. Every retrieval request needs a TrustContext.
graph, err := m.RetrieveGraph(ctx, &retrieval.RetrieveGraphRequest{
TaskDescriptor: "debug auth retry failures",
Trust: retrieval.NewTrustContext(
schema.SensitivityMedium,
true,
"auth-agent",
[]string{"project-auth"},
),
MemoryTypes: []schema.MemoryType{
schema.MemoryTypeEntity,
schema.MemoryTypeSemantic,
schema.MemoryTypeCompetence,
schema.MemoryTypeEpisodic,
},
RootLimit: 8,
NodeLimit: 20,
EdgeLimit: 80,
MaxHops: 1,
MinSalience: 0.2,
})
if err != nil {
log.Fatal(err)
}
for _, node := range graph.Nodes {
fmt.Printf("Found: %s (type=%s, root=%t, hop=%d)\n",
node.Record.ID,
node.Record.Type,
node.Root,
node.Hop,
)
}
Fetch a single record by ID:
record, err := m.RetrieveByID(ctx, capture.PrimaryRecord.ID, retrieval.NewTrustContext(
schema.SensitivityHigh,
true,
"auth-agent",
[]string{"project-auth"},
))
Revision Operations
Revision operations update durable knowledge with audit entries.
newRec := &schema.MemoryRecord{ /* ... */ }
superseded, err := m.Supersede(ctx, oldRecordID, newRec, "agent", "updated fact")
forkedRec := &schema.MemoryRecord{ /* ... */ }
forked, err := m.Fork(ctx, sourceID, forkedRec, "agent", "different in staging")
err = m.Retract(ctx, recordID, "agent", "no longer accurate")
mergedRec := &schema.MemoryRecord{ /* ... */ }
merged, err := m.Merge(ctx, []string{id1, id2}, mergedRec, "agent", "deduplicated")
err = m.Contest(ctx, recordID, conflictingRecordID, "agent", "conflicting evidence")
Reinforce And Penalize
err := m.Reinforce(ctx, recordID, "agent", "used successfully")
err = m.Penalize(ctx, recordID, 0.2, "agent", "not useful for this task")
Metrics
snap, err := m.GetMetrics(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total records: %d\n", snap.TotalRecords)
fmt.Printf("Retrieval usefulness: %.2f\n", snap.RetrievalUsefulness)
Complete Example
package main
import (
"context"
"fmt"
"log"
"github.com/BennettSchwartz/membrane/pkg/ingestion"
"github.com/BennettSchwartz/membrane/pkg/membrane"
"github.com/BennettSchwartz/membrane/pkg/retrieval"
"github.com/BennettSchwartz/membrane/pkg/schema"
)
func main() {
cfg := membrane.DefaultConfig()
cfg.DBPath = "my-agent.db"
m, err := membrane.New(cfg)
if err != nil {
log.Fatal(err)
}
defer m.Stop()
ctx := context.Background()
if err := m.Start(ctx); err != nil {
log.Fatal(err)
}
capture, err := m.CaptureMemory(ctx, ingestion.CaptureMemoryRequest{
Source: "auth-agent",
SourceKind: "event",
Content: map[string]any{
"ref": "thread-1:turn-7",
"text": "Refactored auth middleware and verified package tests",
},
ReasonToRemember: "Keep auth refactor context",
Summary: "Refactored auth middleware",
Tags: []string{"auth"},
Sensitivity: schema.SensitivityLow,
})
if err != nil {
log.Fatal(err)
}
graph, err := m.RetrieveGraph(ctx, &retrieval.RetrieveGraphRequest{
TaskDescriptor: "debug auth",
Trust: retrieval.NewTrustContext(
schema.SensitivityMedium,
true,
"auth-agent",
nil,
),
MemoryTypes: []schema.MemoryType{
schema.MemoryTypeEntity,
schema.MemoryTypeSemantic,
schema.MemoryTypeEpisodic,
},
RootLimit: 5,
NodeLimit: 20,
MaxHops: 1,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Captured %s and retrieved %d graph nodes\n",
capture.PrimaryRecord.ID,
len(graph.Nodes),
)
}