Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt

Use this file to discover all available pages before exploring further.

Add persistent, per-user memory to a Semantic Kernel application as a single plugin. Both kernel functions (search_memory and store_memory) are auto-invokable, so the kernel can decide when to query or persist memories on its own.

Overview

This guide shows how to add Synap to a Semantic Kernel application to build kernels that:
  • Recall user-specific facts, preferences, and past conversations
  • Persist new information surfaced during a conversation
  • Auto-invoke memory functions when the model decides they are relevant
The Synap Semantic Kernel integration ships a single plugin class with two kernel functions.
ExportSK interfacePurpose
SynapPluginKernel pluginProvides search_memory and store_memory kernel functions

Setup

Install the package alongside Semantic Kernel:
pip install maximem-synap-semantic-kernel semantic-kernel
Configure your API key. Generate one from the Synap Dashboard.
.env
SYNAP_API_KEY=synap_your_key_here
OPENAI_API_KEY=your-openai-api-key
Initialize the SDK once at application startup:
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
See SDK Initialization for the full lifecycle and configuration options.

Basic integration

The smallest useful integration adds SynapPlugin to a kernel and invokes a prompt that references the plugin’s functions:
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from synap_semantic_kernel import SynapPlugin

kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(service_id="default"))

kernel.add_plugin(
    SynapPlugin(sdk=sdk, user_id="alice", customer_id="acme"),
    plugin_name="synap",
)

result = await kernel.invoke_prompt(
    "{{synap.search_memory query='project priorities'}} What are my top priorities?"
)
The scoping triple (user_id, optional customer_id) is bound when you construct the plugin — the kernel functions only ever see query, content, and other model-supplied parameters. This prevents prompts (or prompt injections) from spoofing scope. For automatic invocation (the kernel chooses when to call memory functions), enable auto function calling — see “Auto function calling” below.

Core concepts

SynapPlugin

SynapPlugin is a regular Semantic Kernel plugin class with @kernel_function annotated methods. Adding it to the kernel registers both functions under the plugin name you choose:
from synap_semantic_kernel import SynapPlugin

plugin = SynapPlugin(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",   # optional — required for B2B instances
)

kernel.add_plugin(plugin, plugin_name="synap")
After registration, the kernel can invoke synap.search_memory and synap.store_memory either through prompt templating, manual invocation, or auto function calling.

search_memory

Function signature exposed to the kernel:
search_memory(query: str, max_results: int = 5) -> str
Returns a formatted string of results, suitable for direct interpolation into prompt templates. Search failures degrade gracefully — the function returns an empty result string and logs an error so the prompt template renders without breaking.

store_memory

Function signature exposed to the kernel:
store_memory(content: str, memory_type: str = "fact") -> str
Returns "Memory stored successfully." on success. Store failures surface explicitly — the function raises SynapIntegrationError so the kernel (and caller) know if persistence failed.

Complete example: chat function with auto-invoked memory

The following kernel auto-invokes synap.search_memory and synap.store_memory whenever the model decides they are relevant. The application just adds messages to a ChatHistory and the kernel handles the rest:
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.contents import ChatHistory
from synap_semantic_kernel import SynapPlugin


def build_kernel(sdk, user_id: str, customer_id: str | None = None) -> Kernel:
    kernel = Kernel()
    kernel.add_service(OpenAIChatCompletion(service_id="default"))
    kernel.add_plugin(
        SynapPlugin(sdk=sdk, user_id=user_id, customer_id=customer_id),
        plugin_name="synap",
    )
    return kernel


async def chat(kernel: Kernel, history: ChatHistory, message: str) -> str:
    settings = kernel.get_prompt_execution_settings_from_service_id("default")
    settings.function_choice_behavior = FunctionChoiceBehavior.Auto()

    history.add_user_message(message)

    chat_service = kernel.get_service("default")
    response = await chat_service.get_chat_message_content(
        chat_history=history,
        settings=settings,
        kernel=kernel,
    )
    history.add_message(response)
    return str(response)


# Usage
kernel = build_kernel(sdk, user_id="alice", customer_id="acme")
history = ChatHistory(system_message=(
    "You are a personal assistant. Call synap.search_memory before answering "
    "questions about the user. Call synap.store_memory when they share a new fact."
))

await chat(kernel, history, "I just upgraded to the Pro plan.")
reply = await chat(kernel, history, "What plan am I on?")
# → "You're on the Pro plan."
Three things to notice in this pattern:
  1. FunctionChoiceBehavior.Auto() lets the model drive memory. No prompt-template plumbing — the model calls the right function when it’s the right time.
  2. Scope is per-kernel. build_kernel(...) constructs a fresh kernel per user so concurrent requests cannot bleed scope.
  3. The system message is the policy. Tell the model when to search and when to store; the plugin just enforces it.

Advanced patterns

Multi-tenant scoping

SynapPlugin accepts the standard scoping triple — user_id (required), optional customer_id, optional conversation_id. customer_id is required on B2B Synap instances and ignored on single-tenant ones. See Memory Scopes.
# User-scoped only
plugin = SynapPlugin(sdk=sdk, user_id="alice")

# Organization-scoped (user sees org-shared memories too)
plugin = SynapPlugin(sdk=sdk, user_id="alice", customer_id="acme-corp")
For multi-tenant services, build the plugin (and kernel) per request rather than sharing one across users.

Prompt-template invocation

Outside of auto function calling, you can interpolate plugin calls directly in prompt templates:
result = await kernel.invoke_prompt(
    "Context: {{synap.search_memory query='dietary preferences'}}\n"
    "Question: What should I order for dinner?"
)
This pattern is useful when you want deterministic recall (the function always runs) rather than letting the model decide.

Failure semantics

The integration follows the Synap-wide contract:
  • search_memory degrades gracefully — returns an empty result string and logs an error if Synap is unreachable.
  • store_memory surfaces failures — raises SynapIntegrationError so the kernel and caller know persistence failed.
This is by design: read failures shouldn’t break a user-facing answer, but silent write failures would let the memory drift away from reality.

Next steps

Microsoft Agent Framework

Context and history providers for MAF agents.

OpenAI Agents

Function tools for the OpenAI Agents SDK.

Context Fetch

The retrieval API behind search_memory — modes, scopes, and response shapes.

Memory Scopes

How user_id and customer_id interact across reads and writes.