# AutoGen

> Give an AutoGen agent a memory of each of your users through AutoGen's own Memory interface, with what is known put in its model context before each reply and each exchange saved after it.

Give an AutoGen agent a memory of each of your users. AutoGen agents take a list of memories, each with its
own way of filling the model context before a reply. Geniffy fits that interface: before each reply it puts
what is known about the user that bears on their message in front of the model, and after the run you save
the exchange.

## Install

```bash
pip install "autogen-agentchat" "autogen-ext[openai]" geniffy
```

```bash
uv add autogen-agentchat "autogen-ext[openai]" geniffy
```

Set `OPENAI_API_KEY`, and `GENIFFY_API_KEY` from **API keys** in the Geniffy app. Any model client AutoGen
supports works the same way.

## Remember each user

```python
from autogen_agentchat.agents import AssistantAgent
from autogen_core.memory import Memory, MemoryContent, MemoryMimeType, MemoryQueryResult, UpdateContextResult
from autogen_core.models import SystemMessage, UserMessage
from autogen_ext.models.openai import OpenAIChatCompletionClient
from geniffy import AsyncGeniffy

geniffy = AsyncGeniffy()                          # reads GENIFFY_API_KEY


class GeniffyMemory(Memory):
    """What is known about one user, put in the agent's model context before each reply."""

    def __init__(self, user_id: str) -> None:
        self.space = f"user_{user_id}"
        self.mem = geniffy.space(self.space)

    async def update_context(self, model_context) -> UpdateContextResult:
        messages = await model_context.get_messages()
        asked = next((m.content for m in reversed(messages)
                      if isinstance(m, UserMessage) and isinstance(m.content, str)), "")
        # this turn's memory replaces the last turn's, so a long chat never piles them up, and it goes first,
        # beside the agent's own system message, where every model client accepts one
        kept = [m for m in messages if not (isinstance(m, SystemMessage) and m.content.startswith("<memory>"))]
        known = await self.mem.context(asked) if asked else ""
        await model_context.clear()
        if known:
            await model_context.add_message(SystemMessage(content=f"<memory>\n{known}\n</memory>"))
        for m in kept:
            await model_context.add_message(m)
        found = [MemoryContent(content=known, mime_type=MemoryMimeType.TEXT)] if known else []
        return UpdateContextResult(memories=MemoryQueryResult(results=found))

    async def query(self, query, cancellation_token=None, **kwargs) -> MemoryQueryResult:
        text = query if isinstance(query, str) else str(query.content)
        return MemoryQueryResult(results=[MemoryContent(content=await self.mem.context(text),
                                                        mime_type=MemoryMimeType.TEXT)])

    async def add(self, content: MemoryContent, cancellation_token=None) -> None:
        await self.mem.memories.add(str(content.content))

    async def remember(self, message: str, reply: str) -> None:
        """Save one exchange; the user's words become facts about the user."""
        await self.mem.memories.add(messages=[{"role": "user", "content": message},
                                              {"role": "assistant", "content": reply}])

    async def clear(self) -> None:
        """Everything held for this user, gone: the call for a user who asks to be forgotten."""
        await geniffy.forget_space(self.space)

    async def close(self) -> None:
        pass


model = OpenAIChatCompletionClient(model="gpt-5")


async def chat(user_id: str, message: str) -> str:
    memory = GeniffyMemory(user_id)
    agent = AssistantAgent("assistant", model_client=model, memory=[memory],
                           system_message="You are a helpful assistant.")
    result = await agent.run(task=message)
    reply = str(result.messages[-1].content)
    await memory.remember(message, reply)
    return reply
```

Call it with the user from your own sign-in:

```python
reply = await chat(user.id, "Who signs the Lumen renewal?")
```

AutoGen calls `update_context` once per reply, so the memory is rebuilt from what bears on each 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. `add()` saves anything you hand it, such as a note, and `clear()` forgets
the user entirely; AutoGen never calls it by itself, so a reset only clears the conversation.

Source: https://docs.geniffy.com/integrations/autogen
