Skip to main content

Custom Memory Providers

BindAI allows applications to implement custom memory providers when the built-in providers do not meet their storage or retrieval requirements. A custom provider can store MemoryRecord objects in a database, external service, file-based system, proprietary storage engine, or another application-specific backend. The provider integrates with the existing Memory abstraction, allowing application code to continue using the same memory operations.

Why Create a Custom Memory Provider?

A custom provider is useful when an application needs to:
  • Integrate with an existing database
  • Use a proprietary storage system
  • Connect to an external service
  • Implement application-specific persistence
  • Add custom indexing or retrieval behavior
  • Integrate with existing infrastructure
  • Implement specialized memory policies
  • Use a storage backend not provided by BindAI
The application continues to interact with Memory rather than directly depending on the storage implementation.

Memory Architecture

The memory system follows this structure:
Memory provides the application-facing abstraction. The provider implements the storage and retrieval behavior behind that abstraction. This separation makes the underlying storage implementation replaceable.

MemoryProvider

Custom providers implement the MemoryProvider abstraction. The provider is responsible for the core memory operations:
These operations work with MemoryRecord objects and memory results according to the MemoryProvider contract. The storage implementation behind those operations is entirely provider-specific.

Provider Contract

A custom provider should implement the operations required by MemoryProvider. Conceptually:
The exact storage logic inside these methods is application-specific. For example:
  • set() might insert or update a database record.
  • get() might perform a primary-key lookup.
  • search() might execute a full-text or vector query.
  • delete() might remove a record from persistent storage.
  • exists() might check whether a record is present.
  • clear() might remove records belonging to a namespace.
A custom provider should preserve the expected semantics of the MemoryProvider interface.

Memory Records

Custom providers operate on MemoryRecord objects. A record contains a key and value together with optional memory metadata.
Records can also contain information such as:
  • Metadata
  • Importance
  • Access count
  • Expiration
  • Tags
  • Source
  • Relationships
  • Timestamps
  • Embeddings
  • Search score
A custom provider should preserve the information required by the application. The provider can map these fields to whatever representation its underlying storage uses.

Using a Custom Provider

Once the provider is implemented, it can be passed to Memory.
The rest of the application can continue using the standard Memory API. For example:
The application does not need to know how the provider stores the records.

Attaching Memory to an Agent

A Memory instance can be configured on an agent using Agent.builder().
This keeps the agent coupled to the Memory abstraction rather than to a particular storage backend. The same agent architecture can therefore work with a built-in provider or a custom provider.

Registering a Provider

BindAI includes MemoryRegistry for registering memory providers by name.
The provider can then be resolved through the registry:
Applications can also inspect registered providers:
This allows applications to discover providers dynamically or select providers by configuration.

Unregistering a Provider

A registered provider can be removed from the registry:
The registry can also be cleared:
Registry operations affect provider registration; they do not automatically remove data stored by an existing provider.

Storage Design

The storage implementation is controlled by the custom provider. For example:
Or:
Or:
The Memory API remains consistent in each case. This allows storage-specific concerns to remain isolated inside the provider.

Namespaces

Custom providers should respect memory namespaces. For example:
and:
These records use the same key but belong to different namespaces. A provider should therefore preserve namespace isolation when implementing:
  • get()
  • search()
  • delete()
  • exists()
  • clear()
For example, clear(namespace="user-123") should operate on the intended namespace rather than affecting unrelated records. Namespace isolation should also be combined with application-level authorization when multiple users or tenants share the same provider.

Search

Custom providers determine how memory search is implemented. The Memory API supports:
A simple provider might perform text-based matching. A more specialized provider could use:
  • Database indexes
  • Full-text search
  • Vector similarity
  • Embedding-based retrieval
  • Metadata filtering
  • Hybrid retrieval
  • Application-specific ranking
The retrieval strategy belongs to the provider. Applications should not assume that every provider implements search in the same way.

Metadata Filtering

Custom providers can use the metadata argument to implement provider-specific filtering behavior. For example:
A provider backed by a relational database might translate this into a metadata query. A vector database might use native metadata filters. Another provider might implement its own filtering strategy. A custom provider should document which metadata fields and filtering semantics it supports.

Embeddings and Vector Search

A custom provider can support embeddings when semantic retrieval is required. A typical vector-oriented design looks like:
If a provider stores embeddings, it should preserve the relationship between a memory record and its vector representation. The embedding dimensions must also remain compatible with the selected vector storage system. Applications changing embedding models may need to re-embed existing records or use a compatible vector index.

Persistence

Whether memory survives application restarts depends on the custom provider’s storage implementation. An in-process provider may lose data when the process exits. A database-backed provider can persist records across restarts. For example:
Persistence is therefore a property of the provider and its underlying storage rather than the Memory abstraction itself.

Error Handling

Custom providers should handle storage failures consistently with the MemoryProvider contract. Potential failures include:
  • Database connection failures
  • Network errors
  • Serialization errors
  • Authentication failures
  • Unavailable storage
  • Invalid stored data
  • Backend timeouts
The provider should distinguish expected storage failures from successful operations and return the appropriate memory result state or raise exceptions according to the established provider contract. Applications should not silently treat failed storage operations as successful writes.

Resource Management

A custom provider may own external resources such as:
  • Database connections
  • Connection pools
  • HTTP clients
  • File handles
  • Vector-store clients
If the provider owns resources that require cleanup, it should support appropriate cleanup behavior and integrate correctly with the Memory lifecycle. Application code can close the memory instance when it is no longer needed:
It can also use Memory as a context manager:
Custom providers should ensure that their resources are released correctly when the memory instance is closed.

Testing a Custom Provider

A custom provider should be tested independently from model providers and agent execution. At minimum, verify:
  • Storing a record
  • Retrieving a record
  • Searching records
  • Checking record existence
  • Deleting a record
  • Clearing a namespace
  • Preserving namespaces
  • Handling missing records
  • Preserving relevant record metadata
  • Handling storage failures
  • Handling resource cleanup where applicable
A useful test sequence is:
The provider should behave consistently regardless of the underlying storage technology.

Provider Interchangeability

The important separation is:
Application code should use:
rather than directly accessing the custom storage system. This keeps the storage implementation replaceable. For example, application code can move from SQLite to PostgreSQL or from a built-in vector provider to a custom backend while keeping most memory access code unchanged.

Custom Providers and Workflows

Custom memory providers can also be used by workflows. A workflow can store information through Memory:
A later workflow step can retrieve it:
This allows custom storage systems to participate in longer-running application workflows without requiring workflow code to understand the underlying storage implementation.

Custom Providers and Agents

Custom providers can support agents in the same way as built-in providers. The architecture remains:
The agent does not need to directly access the storage backend. This is especially useful when an application already has an established persistence layer and wants agent memory to use that infrastructure.

Security Considerations

A custom memory provider may store user-specific or application-sensitive information. Providers should therefore consider:
  • Access control
  • Namespace isolation
  • Credential management
  • Encryption where appropriate
  • Secure network connections
  • Input validation
  • Logging of storage failures without exposing sensitive data
Credentials should not be hard-coded into provider source code. For example, avoid embedding database passwords or service API keys directly in a custom provider implementation. Prefer environment variables, application configuration, or a secure secret-management system. A provider is responsible for storage integration; application-level authorization should still determine which users or agents are allowed to access particular memory records.

Best Practices

  • Implement the MemoryProvider contract completely.
  • Preserve the MemoryRecord information required by the application.
  • Respect namespaces consistently across all operations.
  • Return appropriate memory results and handle failures predictably.
  • Keep storage-specific logic inside the provider.
  • Avoid coupling agents directly to storage systems.
  • Document provider-specific search behavior.
  • Document metadata filtering capabilities.
  • Preserve embedding compatibility when implementing vector search.
  • Test every provider operation.
  • Test namespace isolation.
  • Test missing records and storage failures.
  • Release external resources correctly.
  • Use MemoryRegistry when the provider needs to be discoverable by name.
  • Keep custom providers interchangeable with built-in providers where practical.
  • Keep credentials outside source code.
  • Apply appropriate authorization to stored memory.

Summary

Custom memory providers extend BindAI’s memory system without changing the application-facing Memory API. The core relationship is:
A custom provider implements the memory operations used by Memory:
Providers can also be registered with MemoryRegistry when named provider discovery is useful. Custom providers can support relational storage, external APIs, vector databases, proprietary systems, or other application-specific backends. This allows applications to integrate their own storage systems while keeping agent, workflow, and application code independent from storage-specific implementation details.