# Haystack

> Give a Haystack pipeline or agent a memory of each of your users, with a component that recalls what is known before the model answers, or a recall tool the agent calls.

Give a Haystack pipeline or agent a memory of each of your users. In a pipeline, one small component puts
what is known about the user that bears on their message in front of the model; an agent can instead look
things up with a recall tool. Either way, each exchange is saved after the reply.

## Install

```bash
pip install haystack-ai geniffy
```

```bash
uv add haystack-ai geniffy
```

Set `OPENAI_API_KEY`, and `GENIFFY_API_KEY` from **API keys** in the Geniffy app. Any chat generator
Haystack supports works the same way.

## Remember each user

```python
from geniffy import Geniffy
from haystack import Pipeline, component
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage

geniffy = Geniffy()                               # reads GENIFFY_API_KEY


@component
class GeniffyMemory:
    """What is known about one user that bears on their message."""

    @component.output_types(memory=str)
    def run(self, user_id: str, query: str) -> dict:
        return {"memory": geniffy.space(f"user_{user_id}").context(query)}


pipe = Pipeline()
pipe.add_component("memory", GeniffyMemory())
pipe.add_component("prompt", ChatPromptBuilder(template=[
    ChatMessage.from_system("You are a helpful assistant.\n\n<memory>\n{{ memory }}\n</memory>"),
    ChatMessage.from_user("{{ query }}"),
]))
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-5.5"))
pipe.connect("memory.memory", "prompt.memory")
pipe.connect("prompt.prompt", "llm.messages")


def chat(user_id: str, message: str) -> str:
    out = pipe.run({"memory": {"user_id": user_id, "query": message}, "prompt": {"query": message}})
    reply = out["llm"]["replies"][0].text or ""
    geniffy.space(f"user_{user_id}").memories.add(messages=[{"role": "user", "content": message},
                                                            {"role": "assistant", "content": reply}])
    return reply
```

Call it with the user from your own sign-in:

```python
reply = chat(user.id, "Who signs the Lumen renewal?")
```

The model sees what is known about this user, each line with where it came from. When nothing is known, the
block says so in one sentence, so the model says it doesn't know instead of guessing.

## Let the agent look things up

To let an agent decide when to look something up, give it a `recall` tool. The tool reads the user from the
agent's state, so the model never sees or chooses whose memory it reads.

```python
from geniffy import Geniffy
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import Tool

geniffy = Geniffy()


def recall(query: str, user_id: str) -> str:
    return geniffy.space(f"user_{user_id}").context(query)


agent = Agent(
    chat_generator=OpenAIChatGenerator(model="gpt-5.5"),
    tools=[Tool(name="recall", description="Look up what is known about the user, with where it came from.",
                parameters={"type": "object", "properties": {"query": {"type": "string", "description": "What to look up"}},
                            "required": ["query"]},
                function=recall, inputs_from_state={"user_id": "user_id"})],
    system_prompt="You are a helpful assistant. Use recall before answering anything that depends on what the user said before.",
    state_schema={"user_id": {"type": str}},
)


def chat(user_id: str, message: str) -> str:
    result = agent.run(messages=[ChatMessage.from_user(message)], user_id=user_id)
    reply = result["last_message"].text or ""
    geniffy.space(f"user_{user_id}").memories.add(messages=[{"role": "user", "content": message},
                                                            {"role": "assistant", "content": reply}])
    return reply
```

Source: https://docs.geniffy.com/integrations/haystack
