# LangChain

> Give a LangChain agent a memory of each of your users, with two small pieces of middleware, or with a recall tool the agent calls.

Give a LangChain agent a memory of each of your users. Two pieces of middleware put what is known in front of
the model and save each exchange when the agent is done; or a recall tool lets the agent look things up
itself. Both read the user from the agent's runtime context, so one agent serves every user.

## Install

```bash
pip install langchain langchain-anthropic geniffy
```

```bash
uv add langchain langchain-anthropic geniffy
```

Set `ANTHROPIC_API_KEY`, and `GENIFFY_API_KEY` from **API keys** in the Geniffy app. Any chat model
LangChain supports works the same way.

## Remember each user

```python
from dataclasses import dataclass

from geniffy import Geniffy
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import ModelRequest, after_agent, dynamic_prompt
from langgraph.runtime import Runtime

geniffy = Geniffy()                      # reads GENIFFY_API_KEY


@dataclass
class User:
    id: str


@dynamic_prompt
def with_memory(request: ModelRequest) -> str:
    mem = geniffy.space(f"user_{request.runtime.context.id}")
    asked = next(m for m in reversed(request.messages) if m.type == "human")
    context = mem.context(asked.text)    # what is known that bears on the message
    return f"You are a helpful assistant.\n\n<memory>\n{context}\n</memory>"


@after_agent
def save_exchange(state: AgentState, runtime: Runtime[User]) -> None:
    asked = next(m for m in reversed(state["messages"]) if m.type == "human")
    answer = state["messages"][-1]
    geniffy.space(f"user_{runtime.context.id}").memories.add(messages=[
        {"role": "user", "content": asked.text},
        {"role": "assistant", "content": answer.text},
    ])


agent = create_agent(
    "anthropic:claude-opus-5-5",
    middleware=[with_memory, save_exchange],
    context_schema=User,
)
```

Pass the user with each call, from your own sign-in:

```python
result = agent.invoke(
    {"messages": [{"role": "user", "content": "Who signs the Lumen renewal?"}]},
    context=User(id=user.id),
)
print(result["messages"][-1].text)
```

`with_memory` runs before each call to the model, so 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. `save_exchange` runs once, when the agent has its answer.

## Let the agent look things up

To let the agent decide when to look something up, give it a `recall` tool. The tool reads the user from the
same runtime context, so the model never sees or chooses whose memory it reads.

```python
from dataclasses import dataclass

from geniffy import Geniffy
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import after_agent
from langchain.tools import ToolRuntime, tool
from langgraph.runtime import Runtime

geniffy = Geniffy()
INSTRUCTIONS = ("You are a helpful assistant. Use recall before answering anything "
                "that depends on what the user said before.")


@dataclass
class User:
    id: str


@tool
def recall(query: str, runtime: ToolRuntime[User]) -> str:
    """Look up what is known about the user, with where it came from."""
    return geniffy.space(f"user_{runtime.context.id}").context(query)


@after_agent
def save_exchange(state: AgentState, runtime: Runtime[User]) -> None:
    asked = next(m for m in reversed(state["messages"]) if m.type == "human")
    answer = state["messages"][-1]
    geniffy.space(f"user_{runtime.context.id}").memories.add(messages=[
        {"role": "user", "content": asked.text},
        {"role": "assistant", "content": answer.text},
    ])


agent = create_agent(
    "anthropic:claude-opus-5-5",
    tools=[recall],
    system_prompt=INSTRUCTIONS,
    middleware=[save_exchange],
    context_schema=User,
)
```

The tool answers with `context()`, so the agent reads the same lines, with their sources, that the system
prompt would hold, and the same sentence when nothing is known.

## Memory across conversations

A LangChain checkpointer keeps one conversation going. Geniffy is what the agent knows about the user across
all of them: what they said last month, in another app, or in a file you added. Use both.

Source: https://docs.geniffy.com/integrations/langchain
