Octopus
SK Memory Integration
When BridgeToOctopusStore is enabled, SK memory calls are routed through the Octopus ISemanticMemoryStore, so both SK and Octopus share a single vector store (Qdrant or PGVector) without duplication.
The Memory Bridge
By default, SK uses its own memory abstraction (ISemanticTextMemory). The bridge replaces SK's default memory implementation with an adapter that delegates to Octopus's ISemanticMemoryStore:
// OctopusSKMemoryAdapter — implements SK's ISemanticTextMemory
// delegating all calls to Octopus ISemanticMemoryStore
public class OctopusSKMemoryAdapter : ISemanticTextMemory
{
private readonly ISemanticMemoryStore _octopusStore;
private readonly IEmbeddingProvider _embeddingProvider;
private readonly string _collectionPrefix;
public OctopusSKMemoryAdapter(
ISemanticMemoryStore octopusStore,
IEmbeddingProvider embeddingProvider,
IOptions<SemanticKernelPluginConfig> config)
{
_octopusStore = octopusStore;
_embeddingProvider = embeddingProvider;
_collectionPrefix = config.Value.Memory.CollectionPrefix;
}
public async Task<string> SaveInformationAsync(
string collection, string text, string id,
string? description = null, string? additionalMetadata = null,
CancellationToken cancellationToken = default)
{
var embedding = await _embeddingProvider.EmbedAsync(text, cancellationToken);
var prefixedCollection = $"{_collectionPrefix}{collection}";
await _octopusStore.UpsertAsync(prefixedCollection, new SemanticPoint
{
Id = id,
Embedding = embedding,
Payload = new { text, description, additionalMetadata }
}, cancellationToken);
return id;
}
public async Task<MemoryQueryResult?> GetAsync(
string collection, string key, bool withEmbedding = false,
CancellationToken cancellationToken = default)
{
var prefixedCollection = $"{_collectionPrefix}{collection}";
var point = await _octopusStore.GetByIdAsync(prefixedCollection, key, cancellationToken);
return point is null ? null : MapToSKResult(point);
}
public async IAsyncEnumerable<MemoryQueryResult> SearchAsync(
string collection, string query, int limit = 1, double minRelevanceScore = 0.7,
bool withEmbeddings = false,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var queryEmbedding = await _embeddingProvider.EmbedAsync(query, cancellationToken);
var prefixedCollection = $"{_collectionPrefix}{collection}";
var results = await _octopusStore.SearchAsync(
prefixedCollection, queryEmbedding, limit, (float)minRelevanceScore, cancellationToken);
foreach (var result in results)
yield return MapToSKResult(result);
}
}
Collection Naming with Prefix
SK memory calls use the CollectionPrefix to namespace SK collections within the shared vector store, preventing collisions with Octopus-managed collections:
| SK collection name | Actual Qdrant/PGVector collection |
|---|---|
hr_policies | sk_hr_policies |
product_docs | sk_product_docs |
tenant_abc_knowledge | sk_tenant_abc_knowledge |
Bridge Configuration
{
"SemanticKernelPlugin": {
"Memory": {
"BridgeToOctopusStore": true,
"CollectionPrefix": "sk_"
}
}
}
When BridgeToOctopusStore is false, SK uses its own separate memory store. This is useful when SK needs a completely isolated vector store independent of Octopus semantic memory.
Single embedding model. With the bridge enabled, SK memory and Octopus semantic memory both use the same
IEmbeddingProvider. This ensures vectors are compatible for cross-system searches, and avoids running two separate embedding models.