GeniffyDocs
Changelog Log In Get a key

Strands Agents

Give an agent built with Strands Agents a memory of each of your users. One hook provider does it: before each request it puts what is known about the user that bears on their message into the system prompt, and after the agent has answered it saves the exchange.

Install

Terminal
pip install "strands-agents[anthropic]" geniffy

Set ANTHROPIC_API_KEY, and GENIFFY_API_KEY from API keys in the Geniffy app. Any model provider Strands supports works the same way, Amazon Bedrock included.

Remember each user

agent.py
from geniffy import AsyncGeniffy
from strands import Agent
from strands.hooks import AfterInvocationEvent, BeforeInvocationEvent, HookProvider, HookRegistry
from strands.models.anthropic import AnthropicModel

geniffy = AsyncGeniffy()                          # reads GENIFFY_API_KEY
INSTRUCTIONS = "You are a helpful assistant."


def last_user_text(messages) -> str:
    for m in reversed(messages or []):
        if m.get("role") == "user":
            return " ".join(b["text"] for b in m.get("content") or [] if "text" in b).strip()
    return ""


class GeniffyMemory(HookProvider):
    """What is known about one user before each request; the exchange, saved after it."""

    def __init__(self, user_id: str) -> None:
        self.mem = geniffy.space(f"user_{user_id}")
        self.asked = ""

    def register_hooks(self, registry: HookRegistry, **kwargs) -> None:
        registry.add_callback(BeforeInvocationEvent, self.recall)
        registry.add_callback(AfterInvocationEvent, self.remember)

    async def recall(self, event: BeforeInvocationEvent) -> None:
        self.asked = last_user_text(event.messages)
        known = await self.mem.context(self.asked) if self.asked else ""
        event.agent.system_prompt = f"{INSTRUCTIONS}\n\n<memory>\n{known}\n</memory>" if known else INSTRUCTIONS

    async def remember(self, event: AfterInvocationEvent) -> None:
        reply = str(event.result or "").strip()
        if self.asked and reply:
            await self.mem.memories.add(messages=[{"role": "user", "content": self.asked},
                                                  {"role": "assistant", "content": reply}])


def agent_for(user_id: str) -> Agent:
    """One agent per user: an agent keeps its conversation, so it is never shared between users."""
    model = AnthropicModel(model_id="claude-opus-5-5", max_tokens=1024)
    return Agent(model=model, system_prompt=INSTRUCTIONS, hooks=[GeniffyMemory(user_id)], callback_handler=None)


async def chat(user_id: str, message: str) -> str:
    result = await agent_for(user_id).invoke_async(message)
    return str(result)

Call it with the user from your own sign-in:

reply = await chat(user.id, "Who signs the Lumen renewal?")

The memory goes in on every request, rebuilt from what bears on that message, each line with where it came from. When nothing is known, the block says so in one sentence, so the agent says it doesn't know instead of guessing. In a running app, keep one agent per conversation rather than one per message, so it remembers the conversation as well; each user still has an agent of their own.

Last updated October 6, 2026